diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..bdb0cab --- /dev/null +++ b/.gitattributes @@ -0,0 +1,17 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cd2946a --- /dev/null +++ b/.gitignore @@ -0,0 +1,47 @@ +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= + +.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..658f479 --- /dev/null +++ b/COPYING @@ -0,0 +1,22 @@ +Copyright (c) 2009-2013 Bitcoin Developers +Copyright (c) 2011-2013 Litecoin Developers +Copyright (c) 2013-2014 Dogecoin Developers +Copyright (c) 2014-2014 CryptoLottoToken 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 +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/CryptoLottoToken-qt.pro b/CryptoLottoToken-qt.pro new file mode 100644 index 0000000..a2be445 --- /dev/null +++ b/CryptoLottoToken-qt.pro @@ -0,0 +1,471 @@ +TEMPLATE = app +TARGET = CryptoLottoToken-qt +macx:TARGET = "CryptoLottoToken-Qt" +VERSION = 1.0.0.1 +INCLUDEPATH += src src/json src/qt +QT += network +DEFINES += QT_GUI BOOST_THREAD_USE_LIB BOOST_SPIRIT_THREADSAFE USE_IPV6 BOOST_SPIRIT_THREADSAFE BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN __NO_SYSTEM_INCLUDES +DEFINES += STATIC +DEFINES += QT_STATIC_BUILD +CONFIG += no_include_pwd +CONFIG += thread +CONFIG += static + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets +lessThan(QT_MAJOR_VERSION, 5): CONFIG += static +QMAKE_CXXFLAGS = -fpermissive + +greaterThan(QT_MAJOR_VERSION, 4) { + QT += widgets + DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 +} + +win32 { +windows:LIBS += -lshlwapi +LIBS += $$join(BOOST_LIB_PATH,,-L,) $$join(BDB_LIB_PATH,,-L,) $$join(OPENSSL_LIB_PATH,,-L,) $$join(QRENCODE_LIB_PATH,,-L,) +LIBS += -lssl -lcrypto -ldb_cxx$$BDB_LIB_SUFFIX +windows:LIBS += -lws2_32 -lole32 -loleaut32 -luuid -lgdi32 +LIBS += -lboost_system-mgw49-mt-s-1_55 -lboost_filesystem-mgw49-mt-s-1_55 -lboost_program_options-mgw49-mt-s-1_55 -lboost_thread-mgw49-mt-s-1_55 +BOOST_LIB_SUFFIX=-mgw49-mt-s-1_55 +BOOST_INCLUDE_PATH=C:/deps/boost_1_55_0 +BOOST_LIB_PATH=C:/deps/boost_1_55_0/stage/lib +BDB_INCLUDE_PATH=C:/deps/db-4.8.30.NC/build_unix +BDB_LIB_PATH=C:/deps/db-4.8.30.NC/build_unix +OPENSSL_INCLUDE_PATH=C:/deps/openssl-1.0.1l/include +OPENSSL_LIB_PATH=C:/deps/openssl-1.0.1l +MINIUPNPC_INCLUDE_PATH=C:/deps/ +MINIUPNPC_LIB_PATH=C:/deps/miniupnpc +LIBPNG_INCLUDE_PATH=C:/deps/libpng-1.6.16 +LIBPNG_LIB_PATH=C:/deps/libpng-1.6.16/.libs +QRENCODE_INCLUDE_PATH=C:/deps/qrencode-3.4.4 +QRENCODE_LIB_PATH=C:/deps/qrencode-3.4.4/.libs +} + +macx:HEADERS += src/qt/macdockiconhandler.h src/qt/macnotificationhandler.h +macx:OBJECTIVE_SOURCES += src/qt/macdockiconhandler.mm src/qt/macnotificationhandler.mm + +# for boost 1.37, add -mt to the boost libraries +# use: qmake BOOST_LIB_SUFFIX=-mt +# for boost thread win32 with _win32 sufix +# use: BOOST_THREAD_LIB_SUFFIX=_win32-... +# or when linking against a specific BerkelyDB version: BDB_LIB_SUFFIX=-4.8 + +# Dependency library locations can be customized with: +# BOOST_INCLUDE_PATH, BOOST_LIB_PATH, BDB_INCLUDE_PATH, +# BDB_LIB_PATH, OPENSSL_INCLUDE_PATH and OPENSSL_LIB_PATH respectively + +OBJECTS_DIR = build +MOC_DIR = build +UI_DIR = build + +# use: qmake "RELEASE=1" +contains(RELEASE, 1) { + # Mac: compile for maximum compatibility (10.5, 32-bit) + macx:QMAKE_CXXFLAGS += -mmacosx-version-min=10.5 -arch i386 -isysroot /Developer/SDKs/MacOSX10.5.sdk + macx:QMAKE_CFLAGS += -mmacosx-version-min=10.5 -arch i386 -isysroot /Developer/SDKs/MacOSX10.5.sdk + macx:QMAKE_OBJECTIVE_CFLAGS += -mmacosx-version-min=10.5 -arch i386 -isysroot /Developer/SDKs/MacOSX10.5.sdk + + !win32:!macx { + # Linux: static link and extra security (see: https://wiki.debian.org/Hardening) + LIBS += -Wl,-Bstatic -Wl,-z,relro -Wl,-z,now + } +} + +!win32 { + # for extra security against potential buffer overflows: enable GCCs Stack Smashing Protection + QMAKE_CXXFLAGS *= -fstack-protector-all + QMAKE_LFLAGS *= -fstack-protector-all + # Exclude on Windows cross compile with MinGW 4.2.x, as it will result in a non-working executable! + # This can be enabled for Windows, when we switch to MinGW >= 4.4.x. +} +# for extra security (see: https://wiki.debian.org/Hardening): this flag is GCC compiler-specific +QMAKE_CXXFLAGS *= -D_FORTIFY_SOURCE=2 +# for extra security on Windows: enable ASLR and DEP via GCC linker flags +win32:QMAKE_LFLAGS *= -Wl,--dynamicbase -Wl,--nxcompat +# on win32: enable GCC large address aware linker flag +win32:QMAKE_LFLAGS *= -Wl,--large-address-aware -static +win32:QMAKE_LFLAGS += -static-libgcc -static-libstdc++ +lessThan(QT_MAJOR_VERSION, 5): win32: QMAKE_LFLAGS *= -static + +# use: qmake "USE_QRCODE=1" +# libqrencode (http://fukuchi.org/works/qrencode/index.en.html) must be installed for support +contains(USE_QRCODE, 1) { + message(Building with QRCode support) + DEFINES += USE_QRCODE + LIBS += -lqrencode +} + +# use: qmake "USE_UPNP=1" ( enabled by default; default) +# or: qmake "USE_UPNP=0" (disabled by default) +# or: qmake "USE_UPNP=-" (not supported) +# miniupnpc (http://miniupnp.free.fr/files/) must be installed for support +contains(USE_UPNP, -) { + message(Building without UPNP support) +} else { + message(Building with UPNP support) + count(USE_UPNP, 0) { + USE_UPNP=1 + } + DEFINES += USE_UPNP=$$USE_UPNP STATICLIB + INCLUDEPATH += $$MINIUPNPC_INCLUDE_PATH + LIBS += $$join(MINIUPNPC_LIB_PATH,,-L,) -lminiupnpc + win32:LIBS += -liphlpapi +} + +# use: qmake "USE_DBUS=1" +contains(USE_DBUS, 1) { + message(Building with DBUS (Freedesktop notifications) support) + DEFINES += USE_DBUS + QT += dbus +} + +# use: qmake "USE_IPV6=1" ( enabled by default; default) +# or: qmake "USE_IPV6=0" (disabled by default) +# or: qmake "USE_IPV6=-" (not supported) +contains(USE_IPV6, -) { + message(Building without IPv6 support) +} else { + count(USE_IPV6, 0) { + USE_IPV6=1 + } + DEFINES += USE_IPV6=$$USE_IPV6 +} + +contains(BITCOIN_NEED_QT_PLUGINS, 1) { + DEFINES += BITCOIN_NEED_QT_PLUGINS + QTPLUGIN += qcncodecs qjpcodecs qtwcodecs qkrcodecs qtaccessiblewidgets +} + +INCLUDEPATH += src/leveldb/include src/leveldb/helpers +LIBS += $$PWD/src/leveldb/libleveldb.a $$PWD/src/leveldb/libmemenv.a +!win32 { + # we use QMAKE_CXXFLAGS_RELEASE even without RELEASE=1 because we use RELEASE to indicate linking preferences not -O preferences + genleveldb.commands = cd $$PWD/src/leveldb && CC=$$QMAKE_CC CXX=$$QMAKE_CXX $(MAKE) OPT=\"$$QMAKE_CXXFLAGS $$QMAKE_CXXFLAGS_RELEASE\" libleveldb.a libmemenv.a +} else { + # make an educated guess about what the ranlib command is called + isEmpty(QMAKE_RANLIB) { + QMAKE_RANLIB = $$replace(QMAKE_STRIP, strip, ranlib) + } + LIBS += -lshlwapi + # genleveldb.commands = cd $$PWD/src/leveldb && CC=$$QMAKE_CC CXX=$$QMAKE_CXX TARGET_OS=OS_WINDOWS_CROSSCOMPILE $(MAKE) OPT=\"$$QMAKE_CXXFLAGS $$QMAKE_CXXFLAGS_RELEASE\" libleveldb.a libmemenv.a && $$QMAKE_RANLIB $$PWD/src/leveldb/libleveldb.a && $$QMAKE_RANLIB $$PWD/src/leveldb/libmemenv.a +} +genleveldb.target = $$PWD/src/leveldb/libleveldb.a +genleveldb.depends = FORCE +PRE_TARGETDEPS += $$PWD/src/leveldb/libleveldb.a +QMAKE_EXTRA_TARGETS += genleveldb +# Gross ugly hack that depends on qmake internals, unfortunately there is no other way to do it. +QMAKE_CLEAN += $$PWD/src/leveldb/libleveldb.a; cd $$PWD/src/leveldb ; $(MAKE) clean + +# regenerate src/build.h +!win32|contains(USE_BUILD_INFO, 1) { + genbuild.depends = FORCE + genbuild.commands = cd $$PWD; /bin/sh share/genbuild.sh $$OUT_PWD/build/build.h + genbuild.target = $$OUT_PWD/build/build.h + PRE_TARGETDEPS += $$OUT_PWD/build/build.h + QMAKE_EXTRA_TARGETS += genbuild + DEFINES += HAVE_BUILD_INFO +} + +QMAKE_CXXFLAGS_WARN_ON = -fdiagnostics-show-option -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter -Wstack-protector + +# Input +DEPENDPATH += src src/json src/qt +HEADERS += src/qt/bitcoingui.h \ + src/qt/transactiontablemodel.h \ + src/qt/addresstablemodel.h \ + src/qt/optionsdialog.h \ + src/qt/sendcoinsdialog.h \ + src/qt/coincontroldialog.h \ + src/qt/coincontroltreewidget.h \ + src/qt/addressbookpage.h \ + src/qt/signverifymessagedialog.h \ + src/qt/aboutdialog.h \ + src/qt/editaddressdialog.h \ + src/qt/bitcoinaddressvalidator.h \ + src/alert.h \ + src/addrman.h \ + src/base58.h \ + src/bignum.h \ + src/checkpoints.h \ + src/coincontrol.h \ + src/compat.h \ + src/sync.h \ + src/util.h \ + src/hash.h \ + src/uint256.h \ + src/serialize.h \ + src/main.h \ + src/net.h \ + src/key.h \ + src/db.h \ + src/walletdb.h \ + src/script.h \ + src/init.h \ + src/irc.h \ + src/bloom.h \ + src/mruset.h \ + src/checkqueue.h \ + src/json/json_spirit_writer_template.h \ + src/json/json_spirit_writer.h \ + src/json/json_spirit_value.h \ + src/json/json_spirit_utils.h \ + src/json/json_spirit_stream_reader.h \ + src/json/json_spirit_reader_template.h \ + src/json/json_spirit_reader.h \ + src/json/json_spirit_error_position.h \ + src/json/json_spirit.h \ + src/qt/clientmodel.h \ + src/qt/guiutil.h \ + src/qt/transactionrecord.h \ + src/qt/guiconstants.h \ + src/qt/optionsmodel.h \ + src/qt/monitoreddatamapper.h \ + src/qt/transactiondesc.h \ + src/qt/transactiondescdialog.h \ + src/qt/bitcoinamountfield.h \ + src/wallet.h \ + src/keystore.h \ + src/qt/transactionfilterproxy.h \ + src/qt/transactionview.h \ + src/qt/walletmodel.h \ + src/qt/walletview.h \ + src/qt/walletstack.h \ + src/qt/walletframe.h \ + src/bitcoinrpc.h \ + src/qt/overviewpage.h \ + src/qt/csvmodelwriter.h \ + src/crypter.h \ + src/qt/sendcoinsentry.h \ + src/qt/qvalidatedlineedit.h \ + src/qt/bitcoinunits.h \ + src/qt/qvaluecombobox.h \ + src/qt/askpassphrasedialog.h \ + src/protocol.h \ + src/qt/notificator.h \ + src/qt/paymentserver.h \ + src/allocators.h \ + src/ui_interface.h \ + src/qt/rpcconsole.h \ + src/version.h \ + src/netbase.h \ + src/clientversion.h \ + src/txdb.h \ + src/leveldb.h \ + src/threadsafety.h \ + src/limitedmap.h \ + src/qt/macnotificationhandler.h \ + src/qt/splashscreen.h + +SOURCES += src/qt/bitcoin.cpp \ + src/qt/bitcoingui.cpp \ + src/qt/transactiontablemodel.cpp \ + src/qt/addresstablemodel.cpp \ + src/qt/optionsdialog.cpp \ + src/qt/sendcoinsdialog.cpp \ + src/qt/coincontroldialog.cpp \ + src/qt/coincontroltreewidget.cpp \ + src/qt/addressbookpage.cpp \ + src/qt/signverifymessagedialog.cpp \ + src/qt/aboutdialog.cpp \ + src/qt/editaddressdialog.cpp \ + src/qt/bitcoinaddressvalidator.cpp \ + src/alert.cpp \ + src/version.cpp \ + src/sync.cpp \ + src/util.cpp \ + src/hash.cpp \ + src/netbase.cpp \ + src/key.cpp \ + src/script.cpp \ + src/main.cpp \ + src/init.cpp \ + src/irc.cpp \ + src/net.cpp \ + src/bloom.cpp \ + src/checkpoints.cpp \ + src/addrman.cpp \ + src/db.cpp \ + src/walletdb.cpp \ + src/qt/clientmodel.cpp \ + src/qt/guiutil.cpp \ + src/qt/transactionrecord.cpp \ + src/qt/optionsmodel.cpp \ + src/qt/monitoreddatamapper.cpp \ + src/qt/transactiondesc.cpp \ + src/qt/transactiondescdialog.cpp \ + src/qt/bitcoinstrings.cpp \ + src/qt/bitcoinamountfield.cpp \ + src/wallet.cpp \ + src/keystore.cpp \ + src/qt/transactionfilterproxy.cpp \ + src/qt/transactionview.cpp \ + src/qt/walletmodel.cpp \ + src/qt/walletview.cpp \ + src/qt/walletstack.cpp \ + src/qt/walletframe.cpp \ + src/bitcoinrpc.cpp \ + src/rpcdump.cpp \ + src/rpcnet.cpp \ + src/rpcmining.cpp \ + src/rpcwallet.cpp \ + src/rpcblockchain.cpp \ + src/rpcrawtransaction.cpp \ + src/qt/overviewpage.cpp \ + src/qt/csvmodelwriter.cpp \ + src/crypter.cpp \ + src/qt/sendcoinsentry.cpp \ + src/qt/qvalidatedlineedit.cpp \ + src/qt/bitcoinunits.cpp \ + src/qt/qvaluecombobox.cpp \ + src/qt/askpassphrasedialog.cpp \ + src/protocol.cpp \ + src/qt/notificator.cpp \ + src/qt/paymentserver.cpp \ + src/qt/rpcconsole.cpp \ + src/noui.cpp \ + src/leveldb.cpp \ + src/txdb.cpp \ + src/qt/splashscreen.cpp + +RESOURCES += src/qt/bitcoin.qrc + +FORMS += src/qt/forms/sendcoinsdialog.ui \ + src/qt/forms/coincontroldialog.ui \ + src/qt/forms/addressbookpage.ui \ + src/qt/forms/signverifymessagedialog.ui \ + src/qt/forms/aboutdialog.ui \ + src/qt/forms/editaddressdialog.ui \ + src/qt/forms/transactiondescdialog.ui \ + src/qt/forms/overviewpage.ui \ + src/qt/forms/sendcoinsentry.ui \ + src/qt/forms/askpassphrasedialog.ui \ + src/qt/forms/rpcconsole.ui \ + src/qt/forms/optionsdialog.ui + +contains(USE_QRCODE, 1) { +HEADERS += src/qt/qrcodedialog.h +SOURCES += src/qt/qrcodedialog.cpp +FORMS += src/qt/forms/qrcodedialog.ui +} + +contains(BITCOIN_QT_TEST, 1) { +SOURCES += src/qt/test/test_main.cpp \ + src/qt/test/uritests.cpp +HEADERS += src/qt/test/uritests.h +DEPENDPATH += src/qt/test +QT += testlib +TARGET = bitcoin-qt_test +DEFINES += BITCOIN_QT_TEST + macx: CONFIG -= app_bundle +} + +CODECFORTR = UTF-8 + +# for lrelease/lupdate +# also add new translations to src/qt/bitcoin.qrc under translations/ +TRANSLATIONS = $$files(src/qt/locale/bitcoin_*.ts) + +isEmpty(QMAKE_LRELEASE) { + win32:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]\\lrelease.exe + else:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease +} +isEmpty(QM_DIR):QM_DIR = $$PWD/src/qt/locale +# automatically build translations, so they can be included in resource file +TSQM.name = lrelease ${QMAKE_FILE_IN} +TSQM.input = TRANSLATIONS +TSQM.output = $$QM_DIR/${QMAKE_FILE_BASE}.qm +TSQM.commands = $$QMAKE_LRELEASE ${QMAKE_FILE_IN} -qm ${QMAKE_FILE_OUT} +TSQM.CONFIG = no_link +QMAKE_EXTRA_COMPILERS += TSQM + +# "Other files" to show in Qt Creator +OTHER_FILES += README.md \ + doc/*.rst \ + doc/*.txt \ + doc/*.md \ + src/bitcoind.cpp \ + src/qt/res/bitcoin-qt.rc \ + src/test/*.cpp \ + src/test/*.h \ + src/qt/test/*.cpp \ + src/qt/test/*.h + +# platform specific defaults, if not overridden on command line +isEmpty(BOOST_LIB_SUFFIX) { + macx:BOOST_LIB_SUFFIX = -mt + win32:BOOST_LIB_SUFFIX = -mgw49-mt-s-1_55 +} + +isEmpty(BOOST_THREAD_LIB_SUFFIX) { + BOOST_THREAD_LIB_SUFFIX = $$BOOST_LIB_SUFFIX +} + +isEmpty(BDB_LIB_PATH) { + macx:BDB_LIB_PATH = /opt/local/lib/db48 +} + +isEmpty(BDB_LIB_SUFFIX) { + macx:BDB_LIB_SUFFIX = -4.8 +} + +isEmpty(BDB_INCLUDE_PATH) { + macx:BDB_INCLUDE_PATH = /opt/local/include/db48 +} + +isEmpty(BOOST_LIB_PATH) { + macx:BOOST_LIB_PATH = /opt/local/lib +} + +isEmpty(BOOST_INCLUDE_PATH) { + macx:BOOST_INCLUDE_PATH = /opt/local/include +} + +win32:DEFINES += WIN32 +win32:RC_FILE = src/qt/res/bitcoin-qt.rc + +win32:!contains(MINGW_THREAD_BUGFIX, 0) { + # At least qmake's win32-g++-cross profile is missing the -lmingwthrd + # thread-safety flag. GCC has -mthreads to enable this, but it doesn't + # work with static linking. -lmingwthrd must come BEFORE -lmingw, so + # it is prepended to QMAKE_LIBS_QT_ENTRY. + # It can be turned off with MINGW_THREAD_BUGFIX=0, just in case it causes + # any problems on some untested qmake profile now or in the future. + DEFINES += _MT + QMAKE_LIBS_QT_ENTRY = -lmingwthrd $$QMAKE_LIBS_QT_ENTRY +} + +!win32:!macx { + DEFINES += LINUX + LIBS += -lrt + # _FILE_OFFSET_BITS=64 lets 32-bit fopen transparently support large files. + DEFINES += _FILE_OFFSET_BITS=64 +} + +macx:HEADERS += src/qt/macdockiconhandler.h +macx:OBJECTIVE_SOURCES += src/qt/macdockiconhandler.mm +macx:LIBS += -framework Foundation -framework ApplicationServices -framework AppKit +macx:DEFINES += MAC_OSX MSG_NOSIGNAL=0 +macx:ICON = src/qt/res/icons/bitcoin.icns +macx:QMAKE_CFLAGS_THREAD += -pthread +macx:QMAKE_LFLAGS_THREAD += -pthread +macx:QMAKE_CXXFLAGS_THREAD += -pthread +macx:QMAKE_INFO_PLIST = share/qt/Info.plist + +# Set libraries and includes at end, to use platform-defined defaults if not overridden +INCLUDEPATH += $$BOOST_INCLUDE_PATH $$BDB_INCLUDE_PATH $$OPENSSL_INCLUDE_PATH $$QRENCODE_INCLUDE_PATH +LIBS += $$join(BOOST_LIB_PATH,,-L,) $$join(BDB_LIB_PATH,,-L,) $$join(OPENSSL_LIB_PATH,,-L,) $$join(QRENCODE_LIB_PATH,,-L,) +LIBS += -lssl -lcrypto -ldb_cxx$$BDB_LIB_SUFFIX +# -lgdi32 has to happen after -lcrypto (see #681) +win32:LIBS += -lws2_32 -lshlwapi -lmswsock -lole32 -loleaut32 -luuid -lgdi32 +LIBS += -lboost_system$$BOOST_LIB_SUFFIX -lboost_filesystem$$BOOST_LIB_SUFFIX -lboost_program_options$$BOOST_LIB_SUFFIX -lboost_thread$$BOOST_THREAD_LIB_SUFFIX +win32:LIBS += -lboost_chrono$$BOOST_LIB_SUFFIX +macx:LIBS += -lboost_chrono$$BOOST_LIB_SUFFIX + +contains(RELEASE, 1) { + !win32:!macx { + # Linux: turn dynamic linking back on for c/c++ runtime libraries + LIBS += -Wl,-Bdynamic + } +} + +system($$QMAKE_LRELEASE -silent $$TRANSLATIONS) diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..856f605 --- /dev/null +++ b/INSTALL @@ -0,0 +1,9 @@ +Building CryptoLottoToken + +See doc/readme-qt.rst for instructions on building CryptoLottoToken-Qt, +the intended-for-end-users, nice-graphical-interface, reference +implementation of CryptoLottoToken. + +See doc/build-*.txt for instructions on building CryptoLottoTokend, +the intended-for-services, no-graphical-interface, reference +implementation of CryptoLottoToken. diff --git a/README b/README new file mode 100644 index 0000000..e013893 --- /dev/null +++ b/README @@ -0,0 +1,4 @@ +CryptoLottoToken + +RPC port 9154 +PORT 9157 diff --git a/contrib/bitcoind.bash-completion b/contrib/bitcoind.bash-completion new file mode 100644 index 0000000..dd6c1ce --- /dev/null +++ b/contrib/bitcoind.bash-completion @@ -0,0 +1,115 @@ +# bash programmable completion for bitcoind(1) +# Copyright (c) 2012 Christian von Roques +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +have bitcoind && { + +# call $bitcoind for RPC +_bitcoin_rpc() { + # determine already specified args necessary for RPC + local rpcargs=() + for i in ${COMP_LINE}; do + case "$i" in + -conf=*|-proxy*|-rpc*) + rpcargs=( "${rpcargs[@]}" "$i" ) + ;; + esac + done + $bitcoind "${rpcargs[@]}" "$@" +} + +# Add bitcoin accounts to COMPREPLY +_bitcoin_accounts() { + local accounts + accounts=$(_bitcoin_rpc listaccounts | awk '/".*"/ { a=$1; gsub(/"/, "", a); print a}') + COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W "$accounts" -- "$cur" ) ) +} + +_bitcoind() { + local cur prev words=() cword + local bitcoind + + # save and use original argument to invoke bitcoind + # bitcoind might not be in $PATH + bitcoind="$1" + + COMPREPLY=() + _get_comp_words_by_ref -n = cur prev words cword + + if ((cword > 2)); then + case ${words[cword-2]} in + listreceivedbyaccount|listreceivedbyaddress) + COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) + return 0 + ;; + move|setaccount) + _bitcoin_accounts + return 0 + ;; + esac + fi + + case "$prev" in + backupwallet) + _filedir + return 0 + ;; + setgenerate) + COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) + return 0 + ;; + getaccountaddress|getaddressesbyaccount|getbalance|getnewaddress|getreceivedbyaccount|listtransactions|move|sendfrom|sendmany) + _bitcoin_accounts + return 0 + ;; + esac + + case "$cur" in + -conf=*|-pid=*|-rpcsslcertificatechainfile=*|-rpcsslprivatekeyfile=*) + cur="${cur#*=}" + _filedir + return 0 + ;; + -datadir=*) + cur="${cur#*=}" + _filedir -d + return 0 + ;; + -*=*) # prevent nonsense completions + return 0 + ;; + *) + local helpopts commands + + # only parse --help if senseful + if [[ -z "$cur" || "$cur" =~ ^- ]]; then + helpopts=$($bitcoind --help 2>&1 | awk '$1 ~ /^-/ { sub(/=.*/, "="); print $1 }' ) + fi + + # only parse help if senseful + if [[ -z "$cur" || "$cur" =~ ^[a-z] ]]; then + commands=$(_bitcoin_rpc help 2>/dev/null | awk '{ print $1; }') + fi + + COMPREPLY=( $( compgen -W "$helpopts $commands" -- "$cur" ) ) + + # Prevent space if an argument is desired + if [[ $COMPREPLY == *= ]]; then + compopt -o nospace + fi + return 0 + ;; + esac +} + +complete -F _bitcoind bitcoind +} + +# Local variables: +# mode: shell-script +# sh-basic-offset: 4 +# sh-indent-comment: t +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et filetype=sh diff --git a/contrib/bitrpc/bitrpc.py b/contrib/bitrpc/bitrpc.py new file mode 100644 index 0000000..70758a2 --- /dev/null +++ b/contrib/bitrpc/bitrpc.py @@ -0,0 +1,324 @@ +from jsonrpc import ServiceProxy +import sys +import string + +# ===== BEGIN USER SETTINGS ===== +# if you do not set these you will be prompted for a password for every command +rpcuser = "" +rpcpass = "" +# ====== END USER SETTINGS ====== + + +if rpcpass == "": + access = ServiceProxy("http://127.0.0.1:9332") +else: + access = ServiceProxy("http://"+rpcuser+":"+rpcpass+"@127.0.0.1:9332") +cmd = sys.argv[1].lower() + +if cmd == "backupwallet": + try: + path = raw_input("Enter destination path/filename: ") + print access.backupwallet(path) + except: + print "\n---An error occurred---\n" + +elif cmd == "getaccount": + try: + addr = raw_input("Enter a Bitcoin address: ") + print access.getaccount(addr) + except: + print "\n---An error occurred---\n" + +elif cmd == "getaccountaddress": + try: + acct = raw_input("Enter an account name: ") + print access.getaccountaddress(acct) + except: + print "\n---An error occurred---\n" + +elif cmd == "getaddressesbyaccount": + try: + acct = raw_input("Enter an account name: ") + print access.getaddressesbyaccount(acct) + except: + print "\n---An error occurred---\n" + +elif cmd == "getbalance": + try: + acct = raw_input("Enter an account (optional): ") + mc = raw_input("Minimum confirmations (optional): ") + try: + print access.getbalance(acct, mc) + except: + print access.getbalance() + except: + print "\n---An error occurred---\n" + +elif cmd == "getblockbycount": + try: + height = raw_input("Height: ") + print access.getblockbycount(height) + except: + print "\n---An error occurred---\n" + +elif cmd == "getblockcount": + try: + print access.getblockcount() + except: + print "\n---An error occurred---\n" + +elif cmd == "getblocknumber": + try: + print access.getblocknumber() + except: + print "\n---An error occurred---\n" + +elif cmd == "getconnectioncount": + try: + print access.getconnectioncount() + except: + print "\n---An error occurred---\n" + +elif cmd == "getdifficulty": + try: + print access.getdifficulty() + except: + print "\n---An error occurred---\n" + +elif cmd == "getgenerate": + try: + print access.getgenerate() + except: + print "\n---An error occurred---\n" + +elif cmd == "gethashespersec": + try: + print access.gethashespersec() + except: + print "\n---An error occurred---\n" + +elif cmd == "getinfo": + try: + print access.getinfo() + except: + print "\n---An error occurred---\n" + +elif cmd == "getnewaddress": + try: + acct = raw_input("Enter an account name: ") + try: + print access.getnewaddress(acct) + except: + print access.getnewaddress() + except: + print "\n---An error occurred---\n" + +elif cmd == "getreceivedbyaccount": + try: + acct = raw_input("Enter an account (optional): ") + mc = raw_input("Minimum confirmations (optional): ") + try: + print access.getreceivedbyaccount(acct, mc) + except: + print access.getreceivedbyaccount() + except: + print "\n---An error occurred---\n" + +elif cmd == "getreceivedbyaddress": + try: + addr = raw_input("Enter a Bitcoin address (optional): ") + mc = raw_input("Minimum confirmations (optional): ") + try: + print access.getreceivedbyaddress(addr, mc) + except: + print access.getreceivedbyaddress() + except: + print "\n---An error occurred---\n" + +elif cmd == "gettransaction": + try: + txid = raw_input("Enter a transaction ID: ") + print access.gettransaction(txid) + except: + print "\n---An error occurred---\n" + +elif cmd == "getwork": + try: + data = raw_input("Data (optional): ") + try: + print access.gettransaction(data) + except: + print access.gettransaction() + except: + print "\n---An error occurred---\n" + +elif cmd == "help": + try: + cmd = raw_input("Command (optional): ") + try: + print access.help(cmd) + except: + print access.help() + except: + print "\n---An error occurred---\n" + +elif cmd == "listaccounts": + try: + mc = raw_input("Minimum confirmations (optional): ") + try: + print access.listaccounts(mc) + except: + print access.listaccounts() + except: + print "\n---An error occurred---\n" + +elif cmd == "listreceivedbyaccount": + try: + mc = raw_input("Minimum confirmations (optional): ") + incemp = raw_input("Include empty? (true/false, optional): ") + try: + print access.listreceivedbyaccount(mc, incemp) + except: + print access.listreceivedbyaccount() + except: + print "\n---An error occurred---\n" + +elif cmd == "listreceivedbyaddress": + try: + mc = raw_input("Minimum confirmations (optional): ") + incemp = raw_input("Include empty? (true/false, optional): ") + try: + print access.listreceivedbyaddress(mc, incemp) + except: + print access.listreceivedbyaddress() + except: + print "\n---An error occurred---\n" + +elif cmd == "listtransactions": + try: + acct = raw_input("Account (optional): ") + count = raw_input("Number of transactions (optional): ") + frm = raw_input("Skip (optional):") + try: + print access.listtransactions(acct, count, frm) + except: + print access.listtransactions() + except: + print "\n---An error occurred---\n" + +elif cmd == "move": + try: + frm = raw_input("From: ") + to = raw_input("To: ") + amt = raw_input("Amount:") + mc = raw_input("Minimum confirmations (optional): ") + comment = raw_input("Comment (optional): ") + try: + print access.move(frm, to, amt, mc, comment) + except: + print access.move(frm, to, amt) + except: + print "\n---An error occurred---\n" + +elif cmd == "sendfrom": + try: + frm = raw_input("From: ") + to = raw_input("To: ") + amt = raw_input("Amount:") + mc = raw_input("Minimum confirmations (optional): ") + comment = raw_input("Comment (optional): ") + commentto = raw_input("Comment-to (optional): ") + try: + print access.sendfrom(frm, to, amt, mc, comment, commentto) + except: + print access.sendfrom(frm, to, amt) + except: + print "\n---An error occurred---\n" + +elif cmd == "sendmany": + try: + frm = raw_input("From: ") + to = raw_input("To (in format address1:amount1,address2:amount2,...): ") + mc = raw_input("Minimum confirmations (optional): ") + comment = raw_input("Comment (optional): ") + try: + print access.sendmany(frm,to,mc,comment) + except: + print access.sendmany(frm,to) + except: + print "\n---An error occurred---\n" + +elif cmd == "sendtoaddress": + try: + to = raw_input("To (in format address1:amount1,address2:amount2,...): ") + amt = raw_input("Amount:") + comment = raw_input("Comment (optional): ") + commentto = raw_input("Comment-to (optional): ") + try: + print access.sendtoaddress(to,amt,comment,commentto) + except: + print access.sendtoaddress(to,amt) + except: + print "\n---An error occurred---\n" + +elif cmd == "setaccount": + try: + addr = raw_input("Address: ") + acct = raw_input("Account:") + print access.setaccount(addr,acct) + except: + print "\n---An error occurred---\n" + +elif cmd == "setgenerate": + try: + gen= raw_input("Generate? (true/false): ") + cpus = raw_input("Max processors/cores (-1 for unlimited, optional):") + try: + print access.setgenerate(gen, cpus) + except: + print access.setgenerate(gen) + except: + print "\n---An error occurred---\n" + +elif cmd == "settxfee": + try: + amt = raw_input("Amount:") + print access.settxfee(amt) + except: + print "\n---An error occurred---\n" + +elif cmd == "stop": + try: + print access.stop() + except: + print "\n---An error occurred---\n" + +elif cmd == "validateaddress": + try: + addr = raw_input("Address: ") + print access.validateaddress(addr) + except: + print "\n---An error occurred---\n" + +elif cmd == "walletpassphrase": + try: + pwd = raw_input("Enter wallet passphrase: ") + access.walletpassphrase(pwd, 60) + print "\n---Wallet unlocked---\n" + except: + print "\n---An error occurred---\n" + +elif cmd == "walletpassphrasechange": + try: + pwd = raw_input("Enter old wallet passphrase: ") + pwd2 = raw_input("Enter new wallet passphrase: ") + access.walletpassphrasechange(pwd, pwd2) + print + print "\n---Passphrase changed---\n" + except: + print + print "\n---An error occurred---\n" + print + +else: + print "Command not found or not supported" diff --git a/contrib/debian/README b/contrib/debian/README new file mode 100644 index 0000000..1cb9b75 --- /dev/null +++ b/contrib/debian/README @@ -0,0 +1,20 @@ +This directory contains files used to package bitcoind/bitcoin-qt +for Debian-based Linux systems. + +If you compile bitcoind/bitcoin-qt yourself, there are some +useful files here: + +bitcoin: URI support +-------------------- + +bitcoin-qt.desktop (Gnome / Open Desktop) +To install: + sudo desktop-file-install bitcoin-qt.desktop + sudo update-desktop-database + +If you build yourself, you will either need to modify the paths in +the .desktop file or copy or symlink your bitcoin-qt binary to /usr/bin +and the ../../share/pixmaps/bitcoin128.png to /usr/share/pixmaps + +bitcoin-qt.protocol (KDE) + diff --git a/contrib/debian/bitcoin-qt.desktop b/contrib/debian/bitcoin-qt.desktop new file mode 100644 index 0000000..b2a2cef --- /dev/null +++ b/contrib/debian/bitcoin-qt.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Bitcoin +Comment=Bitcoin P2P Cryptocurrency +Comment[fr]=Bitcoin, monnaie virtuelle cryptographique pair à pair +Comment[tr]=Bitcoin, eşten eşe kriptografik sanal para birimi +Exec=/usr/bin/bitcoin-qt %u +Terminal=false +Type=Application +Icon=/usr/share/pixmaps/bitcoin128.png +MimeType=x-scheme-handler/bitcoin; +Categories=Office; diff --git a/contrib/debian/bitcoin-qt.install b/contrib/debian/bitcoin-qt.install new file mode 100644 index 0000000..59cacb0 --- /dev/null +++ b/contrib/debian/bitcoin-qt.install @@ -0,0 +1,6 @@ +bitcoin-qt usr/bin +share/pixmaps/bitcoin32.xpm usr/share/pixmaps +share/pixmaps/bitcoin16.xpm usr/share/pixmaps +share/pixmaps/bitcoin128.png usr/share/pixmaps +debian/bitcoin-qt.desktop usr/share/applications +debian/bitcoin-qt.protocol usr/share/kde4/services/ diff --git a/contrib/debian/bitcoin-qt.lintian-overrides b/contrib/debian/bitcoin-qt.lintian-overrides new file mode 100644 index 0000000..7fb230e --- /dev/null +++ b/contrib/debian/bitcoin-qt.lintian-overrides @@ -0,0 +1,2 @@ +# Linked code is Expat - only Debian packaging is GPL-2+ +bitcoin-qt: possible-gpl-code-linked-with-openssl diff --git a/contrib/debian/bitcoin-qt.protocol b/contrib/debian/bitcoin-qt.protocol new file mode 100644 index 0000000..014588d --- /dev/null +++ b/contrib/debian/bitcoin-qt.protocol @@ -0,0 +1,11 @@ +[Protocol] +exec=bitcoin-qt '%u' +protocol=bitcoin +input=none +output=none +helper=true +listing= +reading=false +writing=false +makedir=false +deleting=false diff --git a/contrib/debian/bitcoind.bash-completion b/contrib/debian/bitcoind.bash-completion new file mode 100644 index 0000000..0f84707 --- /dev/null +++ b/contrib/debian/bitcoind.bash-completion @@ -0,0 +1 @@ +contrib/bitcoind.bash-completion bitcoind diff --git a/contrib/debian/bitcoind.examples b/contrib/debian/bitcoind.examples new file mode 100644 index 0000000..4ded67d --- /dev/null +++ b/contrib/debian/bitcoind.examples @@ -0,0 +1 @@ +debian/examples/bitcoin.conf diff --git a/contrib/debian/bitcoind.install b/contrib/debian/bitcoind.install new file mode 100644 index 0000000..7bf7460 --- /dev/null +++ b/contrib/debian/bitcoind.install @@ -0,0 +1 @@ +src/bitcoind usr/bin diff --git a/contrib/debian/bitcoind.lintian-overrides b/contrib/debian/bitcoind.lintian-overrides new file mode 100644 index 0000000..3f9f140 --- /dev/null +++ b/contrib/debian/bitcoind.lintian-overrides @@ -0,0 +1,2 @@ +# Linked code is Expat - only Debian packaging is GPL-2+ +bitcoind: possible-gpl-code-linked-with-openssl diff --git a/contrib/debian/bitcoind.manpages b/contrib/debian/bitcoind.manpages new file mode 100644 index 0000000..3e4ca63 --- /dev/null +++ b/contrib/debian/bitcoind.manpages @@ -0,0 +1,2 @@ +debian/manpages/bitcoind.1 +debian/manpages/bitcoin.conf.5 diff --git a/contrib/debian/changelog b/contrib/debian/changelog new file mode 100644 index 0000000..e600e46 --- /dev/null +++ b/contrib/debian/changelog @@ -0,0 +1,367 @@ +bitcoin (0.8.1-natty3) natty; urgency=low + + * New pixmaps + + -- Jonas Schnelli Mon, 13 May 2013 16:14:00 +0100 + +bitcoin (0.8.1-natty2) natty; urgency=low + + * Remove dumb broken launcher script + + -- Matt Corallo Sun, 24 Mar 2013 20:01:00 -0400 + +bitcoin (0.8.1-natty1) natty; urgency=low + + * New upstream release. + + -- Matt Corallo Tue, 19 Mar 2013 13:03:00 -0400 + +bitcoin (0.8.0-natty1) natty; urgency=low + + * New upstream release. + + -- Matt Corallo Sat, 23 Feb 2013 16:01:00 -0500 + +bitcoin (0.7.2-natty1) natty; urgency=low + + * New upstream release. + + -- Matt Corallo Sat, 15 Dec 2012 10:59:00 -0400 + +bitcoin (0.7.1-natty1) natty; urgency=low + + * New upstream release. + + -- Matt Corallo Wed, 24 Oct 2012 15:06:00 -0400 + +bitcoin (0.7.0-natty1) natty; urgency=low + + * New upstream release. + + -- Matt Corallo Mon, 17 Sep 2012 13:45:00 +0200 + +bitcoin (0.6.3-natty1) natty; urgency=low + + * New upstream release. + + -- Matt Corallo Mon, 25 Jun 2012 23:47:00 +0200 + +bitcoin (0.6.2-natty1) natty; urgency=low + + * Update package description and launch scripts. + + -- Matt Corallo Sat, 2 Jun 2012 16:41:00 +0200 + +bitcoin (0.6.2-natty0) natty; urgency=low + + * New upstream release. + + -- Matt Corallo Tue, 8 May 2012 16:27:00 -0500 + +bitcoin (0.6.1-natty0) natty; urgency=low + + * New upstream release. + + -- Matt Corallo Sun, 6 May 2012 20:09:00 -0500 + +bitcoin (0.6.0-natty0) natty; urgency=low + + * New upstream release. + * Add GNOME/KDE support for bitcoin-qt's bitcoin: URI support. + Thanks to luke-jr for the KDE .protocol file. + + -- Matt Corallo Sat, 31 Mar 2012 15:35:00 -0500 + +bitcoin (0.5.3-natty1) natty; urgency=low + + * Mark for upload to PPA. + + -- Matt Corallo Wed, 14 Mar 2012 23:06:00 -0400 + +bitcoin (0.5.3-natty0) natty; urgency=low + + * New upstream release. + + -- Luke Dashjr Tue, 10 Jan 2012 15:57:00 -0500 + +bitcoin (0.5.2-natty1) natty; urgency=low + + * Remove mentions on anonymity in package descriptions and manpage. + These should never have been there, bitcoin isnt anonymous without + a ton of work that virtually no users will ever be willing and + capable of doing + + -- Matt Corallo Sat, 7 Jan 2012 13:37:00 -0500 + +bitcoin (0.5.2-natty0) natty; urgency=low + + * New upstream release. + + -- Luke Dashjr Fri, 16 Dec 2011 17:57:00 -0500 + +bitcoin (0.5.1-natty0) natty; urgency=low + + * New upstream release. + + -- Matt Corallo Fri, 16 Dec 2011 13:27:00 -0500 + +bitcoin (0.5.0-natty0) natty; urgency=low + + * New upstream release. + + -- Matt Corallo Mon, 21 Nov 2011 11:32:00 -0500 + +bitcoin (0.5.0~rc7-natty0) natty; urgency=low + + * New upstream release candidate. + + -- Matt Corallo Sun, 20 Nov 2011 17:08:00 -0500 + +bitcoin (0.5.0~rc3-natty0) natty; urgency=low + + * New upstream release candidate. + * Don't set rpcpassword for bitcoin-qt. + + -- Matt Corallo Tue, 8 Nov 2011 11:56:00 -0400 + +bitcoin (0.5.0~rc1-natty1) natty; urgency=low + + * Add test_bitcoin to build test + * Fix clean + * Remove uneccessary build-dependancies + + -- Matt Corallo Wed, 26 Oct 2011 14:37:18 -0400 + +bitcoin (0.5.0~rc1-natty0) natty; urgency=low + + * Mark for natty + * Fix broken build + * Fix copyright listing + * Remove bitcoin: URL handler until bitcoin actually has support for it (Oops) + + -- Matt Corallo Wed, 26 Oct 2011 14:37:18 -0400 + +bitcoin (0.5.0~rc1-2) experimental; urgency=low + + * Add bitcoin-qt + + -- Matt Corallo Tue, 25 Oct 2011 15:24:18 -0400 + +bitcoin (0.5.0~rc1-1) experimental; urgency=low + + * New upstream prerelease. + * Add Github as alternate upstream source in watch file. + * Stop build-depending on libcrypto++-dev, and drop patch 1000: + Upstream no longer use crypto++. + * Drop patch 1003: Upstream builds dynamic by default now. + * Update copyright file: Drop notes on longer included sources. + + -- Jonas Smedegaard Fri, 14 Oct 2011 00:16:18 +0200 + +bitcoin (0.4.0-1) unstable; urgency=low + + * New upstream release. + * Stop repackaging source tarballs: No DFSG-violating stripping left. + * Update copyright file: + + Add Github URL to Source. + * Drop dpkg-source local-options hint: Declared options are default + since dpkg-source 1.16.1. + + Add irc URL to Upstream-Contact. + + Add comment on Bitcoin Developers to catch-all Files section. + + Add Files sections for newly readded src/cryptopp/* (new custom + BSD-like license), and newly added doc/build-osx.txt and + src/makefile.osx (Expat). + * Bump debhelper compatibility level to 7. + * Suppress binary icns and gpg files. + * Enable regression tests: + + Build-depend on libboost-test-dev. + + Extend patch 1003 to also dynamically link test binary. + + Build and invoke test binary unless tests are disabled. + * Tighten build-dependency on cdbs: Recent version needed to support + debhelper 7. + * Relax build-depend unversioned on debhelper: needed version + satisfied even in oldstable. + * Stop suppress optional build-dependencies: Satisfied in stable. + Build-depend on devscripts (enabling copyright-check). + + -- Jonas Smedegaard Wed, 05 Oct 2011 01:48:53 +0200 + +bitcoin (0.3.24~dfsg-1) unstable; urgency=low + + * New upstream release. + + [ Jonas Smedegaard ] + * Improve various usage hints: + + Explicitly mention in long description that bitcoind contains + daemon and command-line interface. + + Extend README.Debian with section on lack of GUI, and add primary + headline. + + Avoid installing upstream README: contains no parts relevant for + Debian usage. + Thanks to richard for suggestions (see bug#629443). + * Favor final releases over prereleases in rules and watch file. + Thanks to Jan Dittberner. + * Track -src (not -linux) tarballs in rules and watch file. + Thanks to Jan Dittberner. + * Drop patches 1004 and 1005 (integrated upstream) and simplify + CXXFLAGS in rules file. + * Stop stripping no longer included source-less binaries from upstream + tarballs. + + [ Jan Dittberner ] + * refresh debian/patches/1000_use_system_crypto++.patch + + -- Jonas Smedegaard Tue, 19 Jul 2011 15:08:54 +0200 + +bitcoin (0.3.21~dfsg-2) unstable; urgency=low + + * Enable UPNP support: + + Drop patch 1006. + + Build-depend on libminiupnpc-dev. + Thanks to Matt Corallo. + + -- Jonas Smedegaard Sat, 28 May 2011 15:52:44 +0200 + +bitcoin (0.3.21~dfsg-1) unstable; urgency=low + + * New upstream release. + * Refresh patches. + * Drop patch 1002: no longer needed, as upstream use pkgconfig now. + * Add patch 1006 to really unset USE_UPNP as aparently intended. + * Adjust cleanup rule to preserve .gitignore files. + * Update copyright file: + + Bump format to draft 174 of DEP-5. + + Shorten comments. + * Bump policy compliance to standards-version 3.9.2. + * Shorten Vcs-Browser paragraph in control file. + * Fix mention daemon (not CLI tools) in short description. + * Stop conflicting with or replace bitcoin-cli: Only transitional, no + longer needed. + * Link against unversioned berkeleydb. Update NEWS and README.Debian + accordingly (and improve wording while at it). + Closes: Bug#621425. Thanks to Ondřej Surý. + * This release also implicitly updates linkage against libcrypto++, + which closes: bug#626953, #627024. + * Disable linkage against not yet Debian packaged MiniUPnP. + * Silence seemingly harmless noise about unused variables. + + -- Jonas Smedegaard Tue, 17 May 2011 15:31:24 +0200 + +bitcoin (0.3.20.2~dfsg-2) unstable; urgency=medium + + * Fix have wrapper script execute real binary (not loop executing + itself). + Closes: bug#617290. Thanks to Philippe Gauthier and Etienne Laurin. + * Set urgency=medium as the only (user-exposed) binary is useless + without this fix and has been for some time. + + -- Jonas Smedegaard Wed, 16 Mar 2011 09:11:06 +0100 + +bitcoin (0.3.20.2~dfsg-1) unstable; urgency=low + + * New upstream release. + * Fix provide and replace former package name bitcoin-cli. + Closes: bug#618439. Thanks to Shane Wegner. + + -- Jonas Smedegaard Tue, 15 Mar 2011 11:41:43 +0100 + +bitcoin (0.3.20.01~dfsg-1) unstable; urgency=low + + * New upstream release. + + [ Micah Anderson ] + * Add myself as uploader. + + [ Jonas Smedegaard ] + * Add wrapper for bitcoind to ease initial startup. + * Update patches: + + Drop patch 2002: Applied upstream. + + Add patch 1005 to add phtread linker option. + Closes: bug#615619. Thanks to Shane Wegner. + + Refresh patches. + * Extend copyright years in rules file header. + * Rewrite copyright file using draft svn166 of DEP5 format. + * Rename binary package to bitcoind (from bincoin-cli). + Closes: bug#614025. Thanks to Luke-Jr. + + -- Jonas Smedegaard Tue, 01 Mar 2011 15:55:04 +0100 + +bitcoin (0.3.19~dfsg-6) unstable; urgency=low + + * Fix override agressive optimizations. + * Fix tighten build-dependencies to really fit backporting to Lenny: + + Add fallback build-dependency on libdb4.6++-dev. + + Tighten unversioned Boost build-dependencies to recent versions, + To force use of versioned Boost when backporting to Lenny. + ...needs more love, though: actual build fails. + + -- Jonas Smedegaard Mon, 17 Jan 2011 19:48:35 +0100 + +bitcoin (0.3.19~dfsg-5) unstable; urgency=low + + * Fix lower Boost fallback-build-dependencies to 1.35, really + available in Lenny. + * Correct comment in rules file regarding reason for versioned Boost + fallback-build-dependency. + * Add patch 2002 adding -mt decoration to Boost flags, to ease + backporting to Lenny. + * Respect DEB_BUILD_OPTIONS, and suppress arch-specific optimizations: + + Add patch 1004 to allow overriding optimization flags. + + Set optimization flags conditionally at build time. + + Drop patch 2002 unconditionally suppressing arch-optimizations. + + -- Jonas Smedegaard Mon, 17 Jan 2011 16:04:48 +0100 + +bitcoin (0.3.19~dfsg-4) unstable; urgency=low + + [ Micah Anderson ] + * Provide example bitcoin.conf. + * Add bitcoind(1) and bitcoin.conf(5) man pages. + + [ Jonas Smedegaard ] + * Ease backporting: + + Suppress optional build-dependencies. + + Add fallback build-dependencies on the most recent Boost libs + available in Lenny (where unversioned Boost libs are missing). + * Add Micah as copyright holder for manpages, licensed as GPL-3+. + * Bump copyright format to Subversion candidate draft 162 of DEP5. + + -- Jonas Smedegaard Mon, 17 Jan 2011 14:00:48 +0100 + +bitcoin (0.3.19~dfsg-3) unstable; urgency=low + + * Document in copyright file files excluded from repackaged source. + * Update copyright file: + + Bump DEP5 format hint to Subversion draft rev. 153. + + Consistently wrap at 72 chars. + + Refer to GPL-2 file (not GPL symlink). + * Link against Berkeley DB 4.8 (not 4.7): + + Build-depend on libdb4.8++-dev (and on on libdb4.7++-dev). + + Suggest libdb4.8-util and db4.7-util. + + Add README.Debian note on (untested) upgrade routine. + + Add NEWS entry on changed db version, referring to README.Debian. + + -- Jonas Smedegaard Fri, 07 Jan 2011 22:50:57 +0100 + +bitcoin (0.3.19~dfsg-2) unstable; urgency=low + + * Adjust build options to use optimized miner only for amd64. Fixes + FTBFS on i386 (and other archs, if compiling anywhere else at all). + * Avoid static linking. + * Adjust patch 2001 to avoid only arch-specific optimizations (keep + -O3). + * Extend long description to mention disk consumption and initial use + of IRC. + All of above changes thanks to Helmuth Grohne. + * Add lintian override regarding OpenSSL and GPL: Linked code is Expat + - only Debian packaging is GPL-2+. + + -- Jonas Smedegaard Wed, 29 Dec 2010 00:27:54 +0100 + +bitcoin (0.3.19~dfsg-1) unstable; urgency=low + + [ Jonas Smedegaard ] + * Initial release. + Closes: bug#578157. + + -- Jonas Smedegaard Tue, 28 Dec 2010 15:49:22 +0100 diff --git a/contrib/debian/compat b/contrib/debian/compat new file mode 100644 index 0000000..7f8f011 --- /dev/null +++ b/contrib/debian/compat @@ -0,0 +1 @@ +7 diff --git a/contrib/debian/control b/contrib/debian/control new file mode 100644 index 0000000..dd167ef --- /dev/null +++ b/contrib/debian/control @@ -0,0 +1,55 @@ +Source: bitcoin +Section: utils +Priority: optional +Maintainer: Jonas Smedegaard +Uploaders: Micah Anderson +Build-Depends: debhelper, + devscripts, + bash-completion, + libboost-system-dev (>> 1.35) | libboost-system1.35-dev, + libdb4.8++-dev, + libssl-dev, + pkg-config, + libminiupnpc8-dev, + libboost-filesystem-dev (>> 1.35) | libboost-filesystem1.35-dev, + libboost-program-options-dev (>> 1.35) | libboost-program-options1.35-dev, + libboost-thread-dev (>> 1.35) | libboost-thread1.35-dev, + libboost-test-dev (>> 1.35) | libboost-test1.35-dev, + qt4-qmake, + libqt4-dev, + libqrencode-dev +Standards-Version: 3.9.2 +Homepage: http://www.bitcoin.org/ +Vcs-Git: git://github.com/bitcoin/bitcoin.git +Vcs-Browser: http://github.com/bitcoin/bitcoin + +Package: bitcoind +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: peer-to-peer network based digital currency - daemon + Bitcoin is a free open source peer-to-peer electronic cash system that + is completely decentralized, without the need for a central server or + trusted parties. Users hold the crypto keys to their own money and + transact directly with each other, with the help of a P2P network to + check for double-spending. + . + Full transaction history is stored locally at each client. This + requires 2+ GB of space, slowly growing. + . + This package provides bitcoind, a combined daemon and CLI tool to + interact with the daemon. + +Package: bitcoin-qt +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: peer-to-peer network based digital currency - Qt GUI + Bitcoin is a free open source peer-to-peer electronic cash system that + is completely decentralized, without the need for a central server or + trusted parties. Users hold the crypto keys to their own money and + transact directly with each other, with the help of a P2P network to + check for double-spending. + . + Full transaction history is stored locally at each client. This + requires 2+ GB of space, slowly growing. + . + This package provides Bitcoin-Qt, a GUI for Bitcoin based on Qt. diff --git a/contrib/debian/copyright b/contrib/debian/copyright new file mode 100644 index 0000000..9f7f0f3 --- /dev/null +++ b/contrib/debian/copyright @@ -0,0 +1,166 @@ +Format: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?rev=174 +Upstream-Name: Bitcoin +Upstream-Contact: Satoshi Nakamoto + irc://#bitcoin@freenode.net +Source: http://sourceforge.net/projects/CryptoLottoToken/files/ + https://github.com/bitcoin/bitcoin + +Files: * +Copyright: 2009-2012, Bitcoin Developers +License: Expat +Comment: The Bitcoin Developers encompasses the current developers listed on bitcoin.org, + as well as the numerous contributors to the project. + +Files: src/json/* +Copyright: 2007-2009, John W. Wilkinson +License: Expat + +Files: src/strlcpy.h +Copyright: 1998, Todd C. Miller +License: ISC + +Files: debian/* +Copyright: 2010-2011, Jonas Smedegaard + 2011, Matt Corallo +License: GPL-2+ + +Files: debian/manpages/* +Copyright: Micah Anderson +License: GPL-3+ + +Files: src/qt/res/icons/clock*.png, src/qt/res/icons/tx*.png, + src/qt/res/src/*.svg +Copyright: Wladimir van der Laan +License: Expat + +Files: src/qt/res/icons/address-book.png, src/qt/res/icons/export.png, + src/qt/res/icons/history.png, src/qt/res/icons/key.png, + src/qt/res/icons/lock_*.png, src/qt/res/icons/overview.png, + src/qt/res/icons/receive.png, src/qt/res/icons/send.png, + src/qt/res/icons/synced.png, src/qt/res/icons/filesave.png +Copyright: David Vignoni (david@icon-king.com) + ICON KING - www.icon-king.com +License: LGPL +Comment: NUVOLA ICON THEME for KDE 3.x + Original icons: kaddressbook, klipper_dock, view-list-text, + key-password, encrypted/decrypted, go-home, go-down, + go-next, dialog-ok + Site: http://www.icon-king.com/projects/nuvola/ + +Files: src/qt/res/icons/connect*.png +Copyright: schollidesign +License: GPL-3+ +Comment: Icon Pack: Human-O2 + Site: http://findicons.com/icon/93743/blocks_gnome_netstatus_0 + +Files: src/qt/res/icons/transaction*.png +Copyright: md2k7 +License: Expat +Comment: Site: https://bitcointalk.org/index.php?topic=15276.0 + +Files: src/qt/res/icons/configure.png, src/qt/res/icons/quit.png, + src/qt/res/icons/editcopy.png, src/qt/res/icons/editpaste.png, + src/qt/res/icons/add.png, src/qt/res/icons/edit.png, + src/qt/res/icons/remove.png +Copyright: http://www.everaldo.com +License: LGPL +Comment: Icon Pack: Crystal SVG + +Files: src/qt/res/icons/bitcoin.png, src/qt/res/icons/toolbar.png +Copyright: Bitboy (optimized for 16x16 by Wladimir van der Laan) +License: PUB-DOM +Comment: Site: https://bitcointalk.org/?topic=1756.0 + +Files: scripts/img/reload.xcf, src/qt/res/movies/update_spinner.mng +Copyright: Everaldo (Everaldo Coelho) +License: GPL-3+ +Comment: Icon Pack: Kids + Site: http://findicons.com/icon/17102/reload?id=17102 + +Files: src/qt/res/images/splash2.jpg +License: PUB-DOM +Copyright: Crobbo (forum) +Comment: Site: https://bitcointalk.org/index.php?topic=32273.0 + + +License: Expat + 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. + +License: ISC + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + . + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR + BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES + OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + SOFTWARE. + +License: GPL-2+ + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + . + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. +Comment: + On Debian systems the GNU General Public License (GPL) version 2 is + located in '/usr/share/common-licenses/GPL-2'. + . + You should have received a copy of the GNU General Public License along + with this program. If not, see . + +License: GPL-3+ + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU General Public License, Version 3 or any + later version published by the Free Software Foundation. +Comment: + On Debian systems the GNU General Public License (GPL) version 3 is + located in '/usr/share/common-licenses/GPL-3'. + . + You should have received a copy of the GNU General Public License along + with this program. If not, see . + +License: LGPL + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + . + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +Comment: + On Debian systems the GNU Lesser General Public License (LGPL) is + located in '/usr/share/common-licenses/LGPL'. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +License: PUB-DOM + This work is in the public domain. diff --git a/contrib/debian/examples/bitcoin.conf b/contrib/debian/examples/bitcoin.conf new file mode 100644 index 0000000..3a670e4 --- /dev/null +++ b/contrib/debian/examples/bitcoin.conf @@ -0,0 +1,84 @@ +# bitcoin.conf configuration file. Lines beginning with # are comments. + + +# Network-related settings: + +# Run on the test network instead of the real bitcoin network. +#testnet=1 + +# Connect via a socks4 proxy +#proxy=127.0.0.1:9050 + +# Use as many addnode= settings as you like to connect to specific peers +#addnode=69.164.218.197 +#addnode=10.0.0.2:9333 + +# ... or use as many connect= settings as you like to connect ONLY +# to specific peers: +#connect=69.164.218.197 +#connect=10.0.0.1:9333 + +# Maximum number of inbound+outbound connections. +#maxconnections= + + +# JSON-RPC options (for controlling a running Bitcoin/bitcoind process) + +# server=1 tells Bitcoin to accept JSON-RPC commands. +#server=1 + +# You must set rpcuser and rpcpassword to secure the JSON-RPC api +#rpcuser=Ulysseys +#rpcpassword=YourSuperGreatPasswordNumber_385593 + +# By default, only RPC connections from localhost are allowed. Specify +# as many rpcallowip= settings as you like to allow connections from +# other hosts (and you may use * as a wildcard character): +#rpcallowip=10.1.1.34 +#rpcallowip=192.168.1.* + +# Listen for RPC connections on this TCP port: +rpcport=9332 + +# You can use Bitcoin or bitcoind to send commands to Bitcoin/bitcoind +# running on another host using this option: +rpcconnect=127.0.0.1 + +# Use Secure Sockets Layer (also known as TLS or HTTPS) to communicate +# with Bitcoin -server or bitcoind +#rpcssl=1 + +# OpenSSL settings used when rpcssl=1 +rpcsslciphers=TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH +rpcsslcertificatechainfile=server.cert +rpcsslprivatekeyfile=server.pem + + +# Miscellaneous options + +# Set gen=1 to attempt to generate bitcoins +gen=0 + +# Use SSE instructions to try to generate bitcoins faster. +#4way=1 + +# Pre-generate this many public/private key pairs, so wallet backups will be valid for +# both prior transactions and several dozen future transactions. +keypool=100 + +# Pay an optional transaction fee every time you send bitcoins. Transactions with fees +# are more likely than free transactions to be included in generated blocks, so may +# be validated sooner. +paytxfee=0.00 + +# Allow direct connections for the 'pay via IP address' feature. +#allowreceivebyip=1 + + +# User interface options + +# Start Bitcoin minimized +#min=1 + +# Minimize to the system tray +#minimizetotray=1 diff --git a/contrib/debian/gbp.conf b/contrib/debian/gbp.conf new file mode 100644 index 0000000..a7281f9 --- /dev/null +++ b/contrib/debian/gbp.conf @@ -0,0 +1,5 @@ +# Configuration file for git-buildpackage and friends + +[DEFAULT] +pristine-tar = True +sign-tags = True diff --git a/contrib/debian/manpages/bitcoin-qt.1 b/contrib/debian/manpages/bitcoin-qt.1 new file mode 100644 index 0000000..421853a --- /dev/null +++ b/contrib/debian/manpages/bitcoin-qt.1 @@ -0,0 +1,206 @@ +.TH BITCOIN-QT "1" "April 2013" "bitcoin-qt 1" +.SH NAME +bitcoin-qt \- peer-to-peer network based digital currency +.SH DESCRIPTION +.SS "Usage:" +.IP +bitcoin\-qt [command\-line options] +.SH OPTIONS +.TP +\-? +This help message +.TP +\fB\-conf=\fR +Specify configuration file (default: bitcoin.conf) +.TP +\fB\-pid=\fR +Specify pid file (default: bitcoind.pid) +.TP +\fB\-gen\fR +Generate coins +.TP +\fB\-gen\fR=\fI0\fR +Don't generate coins +.TP +\fB\-datadir=\fR +Specify data directory +.TP +\fB\-dbcache=\fR +Set database cache size in megabytes (default: 25) +.TP +\fB\-timeout=\fR +Specify connection timeout in milliseconds (default: 5000) +.TP +\fB\-proxy=\fR +Connect through socks proxy +.TP +\fB\-socks=\fR +Select the version of socks proxy to use (4\-5, default: 5) +.TP +\fB\-tor=\fR +Use proxy to reach tor hidden services (default: same as \fB\-proxy\fR) +.TP +\fB\-dns\fR +Allow DNS lookups for \fB\-addnode\fR, \fB\-seednode\fR and \fB\-connect\fR +.TP +\fB\-port=\fR +Listen for connections on (default: 9333 or testnet: 19333) +.TP +\fB\-maxconnections=\fR +Maintain at most connections to peers (default: 125) +.TP +\fB\-addnode=\fR +Add a node to connect to and attempt to keep the connection open +.TP +\fB\-connect=\fR +Connect only to the specified node(s) +.TP +\fB\-seednode=\fR +Connect to a node to retrieve peer addresses, and disconnect +.TP +\fB\-externalip=\fR +Specify your own public address +.TP +\fB\-onlynet=\fR +Only connect to nodes in network (IPv4, IPv6 or Tor) +.TP +\fB\-discover\fR +Discover own IP address (default: 1 when listening and no \fB\-externalip\fR) +.TP +\fB\-checkpoints\fR +Only accept block chain matching built\-in checkpoints (default: 1) +.TP +\fB\-listen\fR +Accept connections from outside (default: 1 if no \fB\-proxy\fR or \fB\-connect\fR) +.TP +\fB\-bind=\fR +Bind to given address and always listen on it. Use [host]:port notation for IPv6 +.TP +\fB\-dnsseed\fR +Find peers using DNS lookup (default: 1 unless \fB\-connect\fR) +.TP +\fB\-banscore=\fR +Threshold for disconnecting misbehaving peers (default: 100) +.TP +\fB\-bantime=\fR +Number of seconds to keep misbehaving peers from reconnecting (default: 86400) +.TP +\fB\-maxreceivebuffer=\fR +Maximum per\-connection receive buffer, *1000 bytes (default: 5000) +.TP +\fB\-maxsendbuffer=\fR +Maximum per\-connection send buffer, *1000 bytes (default: 1000) +.TP +\fB\-upnp\fR +Use UPnP to map the listening port (default: 1 when listening) +.TP +\fB\-paytxfee=\fR +Fee per KB to add to transactions you send +.TP +\fB\-server\fR +Accept command line and JSON\-RPC commands +.TP +\fB\-testnet\fR +Use the test network +.TP +\fB\-debug\fR +Output extra debugging information. Implies all other \fB\-debug\fR* options +.TP +\fB\-debugnet\fR +Output extra network debugging information +.TP +\fB\-logtimestamps\fR +Prepend debug output with timestamp +.TP +\fB\-shrinkdebugfile\fR +Shrink debug.log file on client startup (default: 1 when no \fB\-debug\fR) +.TP +\fB\-printtoconsole\fR +Send trace/debug info to console instead of debug.log file +.TP +\fB\-rpcuser=\fR +Username for JSON\-RPC connections +.TP +\fB\-rpcpassword=\fR +Password for JSON\-RPC connections +.TP +\fB\-rpcport=\fR +Listen for JSON\-RPC connections on (default: 9332 or testnet: 19332) +.TP +\fB\-rpcallowip=\fR +Allow JSON\-RPC connections from specified IP address +.TP +\fB\-rpcthreads=\fR +Set the number of threads to service RPC calls (default: 4) +.TP +\fB\-blocknotify=\fR +Execute command when the best block changes (%s in cmd is replaced by block hash) +.TP +\fB\-walletnotify=\fR +Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) +.TP +\fB\-alertnotify=\fR +Execute command when a relevant alert is received (%s in cmd is replaced by message) +.TP +\fB\-upgradewallet\fR +Upgrade wallet to latest format +.TP +\fB\-keypool=\fR +Set key pool size to (default: 100) +.TP +\fB\-rescan\fR +Rescan the block chain for missing wallet transactions +.TP +\fB\-salvagewallet\fR +Attempt to recover private keys from a corrupt wallet.dat +.TP +\fB\-checkblocks=\fR +How many blocks to check at startup (default: 288, 0 = all) +.TP +\fB\-checklevel=\fR +How thorough the block verification is (0\-4, default: 3) +.TP +\fB\-txindex\fR +Maintain a full transaction index (default: 0) +.TP +\fB\-loadblock=\fR +Imports blocks from external blk000??.dat file +.TP +\fB\-reindex\fR +Rebuild block chain index from current blk000??.dat files +.TP +\fB\-par=\fR +Set the number of script verification threads (1\-16, 0=auto, default: 0) +.SS "Block creation options:" +.TP +\fB\-blockminsize=\fR +Set minimum block size in bytes (default: 0) +.TP +\fB\-blockmaxsize=\fR +Set maximum block size in bytes (default: 250000) +.HP +\fB\-blockprioritysize=\fR Set maximum size of high\-priority/low\-fee transactions in bytes (default: 27000) +.PP +SSL options: (see the Bitcoin Wiki for SSL setup instructions) +.TP +\fB\-rpcssl\fR +Use OpenSSL (https) for JSON\-RPC connections +.TP +\fB\-rpcsslcertificatechainfile=\fR +Server certificate file (default: server.cert) +.TP +\fB\-rpcsslprivatekeyfile=\fR +Server private key (default: server.pem) +.TP +\fB\-rpcsslciphers=\fR +Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) +.SS "UI options:" +.TP +\fB\-lang=\fR +Set language, for example "de_DE" (default: system locale) +.TP +\fB\-min\fR +Start minimized +.TP +\fB\-splash\fR +Show splash screen on startup (default: 1) diff --git a/contrib/debian/manpages/bitcoin.conf.5 b/contrib/debian/manpages/bitcoin.conf.5 new file mode 100644 index 0000000..a95285f --- /dev/null +++ b/contrib/debian/manpages/bitcoin.conf.5 @@ -0,0 +1,92 @@ +.TH BITCOIN.CONF "5" "January 2011" "bitcoin.conf 3.19" +.SH NAME +bitcoin.conf \- bitcoin configuration file +.SH SYNOPSIS +All command-line options (except for '\-datadir' and '\-conf') may be specified in a configuration file, and all configuration file options may also be specified on the command line. Command-line options override values set in the configuration file. +.TP +The configuration file is a list of 'setting=value' pairs, one per line, with optional comments starting with the '#' character. +.TP +The configuration file is not automatically created; you can create it using your favorite plain-text editor. By default, bitcoind(1) will look for a file named bitcoin.conf(5) in the bitcoin data directory, but both the data directory and the configuration file path may be changed using the '\-datadir' and '\-conf' command-line arguments. +.SH LOCATION +bitcoin.conf should be located in $HOME/.bitcoin +.SH NETWORK-RELATED SETTINGS +.TP +.TP +\fBtestnet=\fR[\fI'1'\fR|\fI'0'\fR] +Enable or disable run on the test network instead of the real *bitcoin* network. +.TP +\fBproxy=\fR\fI'127.0.0.1:9050'\fR +Connect via a socks4 proxy. +.TP +\fBaddnode=\fR\fI'10.0.0.2:9333'\fR +Use as many *addnode=* settings as you like to connect to specific peers. +.TP +\fBconnect=\fR\fI'10.0.0.1:9333'\fR +Use as many *connect=* settings as you like to connect ONLY to specific peers. +.TP +\fRmaxconnections=\fR\fI'value'\fR +Maximum number of inbound+outbound connections. +.SH JSON-RPC OPTIONS +.TP +\fBserver=\fR[\fI'1'\fR|\fI'0'\fR] +Tells *bitcoin* to accept or not accept JSON-RPC commands. +.TP +\fBrpcuser=\fR\fI'username'\fR +You must set *rpcuser* to secure the JSON-RPC api. +.TP +\fBrpcpassword=\fR\fI'password'\fR +You must set *rpcpassword* to secure the JSON-RPC api. +.TP +\fBrpctimeout=\fR\fI'30'\fR +How many seconds *bitcoin* will wait for a complete RPC HTTP request, after the HTTP connection is established. +.TP +\fBrpcallowip=\fR\fI'192.168.1.*'\fR +By default, only RPC connections from localhost are allowed. Specify as many *rpcallowip=* settings as you like to allow connections from other hosts (and you may use * as a wildcard character). +.TP +\fBrpcport=\fR\fI'9332'\fR +Listen for RPC connections on this TCP port. +.TP +\fBrpcconnect=\fR\fI'127.0.0.1'\fR +You can use *bitcoin* or *bitcoind(1)* to send commands to *bitcoin*/*bitcoind(1)* running on another host using this option. +.TP +\fBrpcssl=\fR\fI'1'\fR +Use Secure Sockets Layer (also known as TLS or HTTPS) to communicate with *bitcoin* '\-server' or *bitcoind(1)*. Example of OpenSSL settings used when *rpcssl*='1': +.TP +\fB\-rpcsslciphers=\fR +Acceptable ciphers (default: TLSv1+HIGH:\:!SSLv2:\:!aNULL:\:!eNULL:\:!AH:\:!3DES:\:@STRENGTH) +.TP +\fBrpcsslcertificatechainfile=\fR\fI'server.cert'\fR +.TP +\fBrpcsslprivatekeyfile=\fR\fI'server.pem'\fR +.TP +.SH MISCELLANEOUS OPTIONS +.TP +\fBgen=\fR[\fI'0'\fR|\fI'1'\fR] +Enable or disable attempt to generate bitcoins. +.TP +\fB4way=\fR[\fI'0'\fR|\fI'1'\fR] +Enable or disable use SSE instructions to try to generate bitcoins faster. +.TP +\fBkeypool=\fR\fI'100'\fR +Pre-generate this many public/private key pairs, so wallet backups will be valid for both prior transactions and several dozen future transactions. +.TP +\fBpaytxfee=\fR\fI'0.00'\fR +Pay an optional transaction fee every time you send bitcoins. Transactions with fees are more likely than free transactions to be included in generated blocks, so may be validated sooner. +.TP +\fBallowreceivebyip=\fR\fI'1'\fR +Allow direct connections for the 'pay via IP address' feature. +.TP +.SH USER INTERFACE OPTIONS +.TP +\fBmin=\fR[\fI'0'\fR|\fI'1'\fR] +Enable or disable start bitcoind minimized. +.TP +\fBminimizetotray=\fR[\fI'0'\fR|\fI'1'\fR] +Enable or disable minimize to the system tray. +.SH "SEE ALSO" +bitcoind(1) +.SH AUTHOR +This manual page was written by Micah Anderson for the Debian system (but may be used by others). Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 3 or any later version published by the Free Software Foundation. + +On Debian systems, the complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL. + diff --git a/contrib/debian/manpages/bitcoind.1 b/contrib/debian/manpages/bitcoind.1 new file mode 100644 index 0000000..0c191b6 --- /dev/null +++ b/contrib/debian/manpages/bitcoind.1 @@ -0,0 +1,209 @@ +.TH BITCOIND "1" "January 2011" "bitcoind 3.19" +.SH NAME +bitcoind \- peer-to-peer network based digital currency +.SH SYNOPSIS +bitcoin [options] [params] +.TP +bitcoin [options] help \- Get help for a command +.SH DESCRIPTION +This manual page documents the bitcoind program. Bitcoin is a peer-to-peer digital currency. Peer-to-peer (P2P) means that there is no central authority to issue new money or keep track of transactions. Instead, these tasks are managed collectively by the nodes of the network. Advantages: + +Bitcoins can be sent easily through the Internet, without having to trust middlemen. Transactions are designed to be irreversible. Be safe from instability caused by fractional reserve banking and central banks. The limited inflation of the Bitcoin system’s money supply is distributed evenly (by CPU power) throughout the network, not monopolized by banks. + +.SH OPTIONS +.TP +\fB\-conf=\fR +Specify configuration file (default: bitcoin.conf) +.TP +\fB\-gen\fR +Generate coins +.TP +\fB\-gen\fR=\fI0\fR +Don't generate coins +.TP +\fB\-min\fR +Start minimized +.TP +\fB\-datadir=\fR +Specify data directory +.TP +\fB\-proxy=\fR +Connect through socks4 proxy +.TP +\fB\-addnode=\fR +Add a node to connect to +.TP +\fB\-connect=\fR +Connect only to the specified node +.TP +\fB\-paytxfee=\fR +Fee per KB to add to transactions you send +.TP +\fB\-server\fR +Accept command line and JSON\-RPC commands +.TP +\fB\-daemon\fR +Run in the background as a daemon and accept commands +.TP +\fB\-testnet\fR +Use the test network +.TP +\fB\-rpcuser=\fR +Username for JSON\-RPC connections +.TP +\fB\-rpcpassword=\fR +Password for JSON\-RPC connections +.TP +\fB\-rpcport=\fR +Listen for JSON\-RPC connections on +.TP +\fB\-rpcallowip=\fR +Allow JSON\-RPC connections from specified IP address +.TP +\fB\-rpcconnect=\fR +Send commands to node running on +.PP +SSL options: (see the Bitcoin Wiki for SSL setup instructions) +.TP +\fB\-rpcssl\fR=\fI1\fR +Use OpenSSL (https) for JSON\-RPC connections +.TP +\fB\-rpcsslcertificatchainfile=\fR +Server certificate file (default: server.cert) +.TP +\fB\-rpcsslprivatekeyfile=\fR +Server private key (default: server.pem) +.TP +\fB\-rpcsslciphers=\fR +Acceptable ciphers (default: TLSv1+HIGH:\:!SSLv2:\:!aNULL:\:!eNULL:\:!AH:\:!3DES:\:@STRENGTH) +.TP +\-? +This help message +.SH COMMANDS +.TP +\fBbackupwallet 'destination'\fR +Safely copies *wallet.dat* to 'destination', which can be a directory or a path with filename. +.TP +\fBgetaccount 'bitcoinaddress'\fR +Returns the account associated with the given address. +.TP +\fBsetaccount 'bitcoinaddress' ['account']\fR +Sets the ['account'] associated with the given address. ['account'] may be omitted to remove an address from ['account']. +.TP +\fBgetaccountaddress 'account'\fR +Returns a new bitcoin address for 'account'. +.TP +\fBgetaddressesbyaccount 'account'\fR +Returns the list of addresses associated with the given 'account'. +.TP +\fBgetbalance 'account'\fR +Returns the server's available balance, or the balance for 'account'. +.TP +\fBgetblockcount\fR +Returns the number of blocks in the longest block chain. +.TP +\fBgetblocknumber\fR +Returns the block number of the latest block in the longest block chain. +.TP +\fBgetconnectioncount\fR +Returns the number of connections to other nodes. +.TP +\fBgetdifficulty\fR +Returns the proof-of-work difficulty as a multiple of the minimum difficulty. +.TP +\fBgetgenerate\fR +Returns boolean true if server is trying to generate bitcoins, false otherwise. +.TP +\fBsetgenerate 'generate' ['genproclimit']\fR +Generation is limited to ['genproclimit'] processors, \-1 is unlimited. +.TP +\fBgethashespersec\fR +Returns a recent hashes per second performance measurement while generating. +.TP +\fBgetinfo\fR +Returns an object containing server information. +.TP +\fBgetnewaddress 'account'\fR +Returns a new bitcoin address for receiving payments. If 'account' is specified (recommended), it is added to the address book so payments received with the address will be credited to 'account'. +.TP +\fBgetreceivedbyaccount 'account' ['minconf=1']\fR +Returns the total amount received by addresses associated with 'account' in transactions with at least ['minconf'] confirmations. +.TP +\fBgetreceivedbyaddress 'bitcoinaddress' ['minconf=1']\fR +Returns the total amount received by 'bitcoinaddress' in transactions with at least ['minconf'] confirmations. +.TP +\fBgettransaction 'txid'\fR +Returns information about a specific transaction, given hexadecimal transaction ID. +.TP +\fBgetwork 'data'\fR +If 'data' is specified, tries to solve the block and returns true if it was successful. If 'data' is not specified, returns formatted hash 'data' to work on: + + "midstate" : precomputed hash state after hashing the first half of the data. + "data" : block data. + "hash1" : formatted hash buffer for second hash. + "target" : little endian hash target. +.TP +\fBhelp 'command'\fR +List commands, or get help for a command. +.TP +\fBlistaccounts ['minconf=1']\fR +List accounts and their current balances. + *note: requires bitcoin 0.3.20 or later. +.TP +\fBlistreceivedbyaccount ['minconf=1'] ['includeempty=false']\fR +['minconf'] is the minimum number of confirmations before payments are included. ['includeempty'] whether to include addresses that haven't received any payments. Returns an array of objects containing: + + "account" : the account of the receiving address. + "amount" : total amount received by the address. + "confirmations" : number of confirmations of the most recent transaction included. +.TP +\fBlistreceivedbyaddress ['minconf=1'] ['includeempty=false']\fR +['minconf'] is the minimum number of confirmations before payments are included. ['includeempty'] whether to include addresses that haven't received any payments. Returns an array of objects containing: + + "address" : receiving address. + "account" : the account of the receiving address. + "amount" : total amount received by the address. + "confirmations" : number of confirmations of the most recent transaction included. +.TP +\fBlisttransactions 'account' ['count=10']\fR +Returns a list of the last ['count'] transactions for 'account' \- for all accounts if 'account' is not specified or is "*". Each entry in the list may contain: + + "category" : will be generate, send, receive, or move. + "amount" : amount of transaction. + "fee" : Fee (if any) paid (only for send transactions). + "confirmations" : number of confirmations (only for generate/send/receive). + "txid" : transaction ID (only for generate/send/receive). + "otheraccount" : account funds were moved to or from (only for move). + "message" : message associated with transaction (only for send). + "to" : message-to associated with transaction (only for send). + + *note: requires bitcoin 0.3.20 or later. +.TP +\fBmove <'fromaccount'> <'toaccount'> <'amount'> ['minconf=1'] ['comment']\fR +Moves funds between accounts. +.TP +\fBsendfrom* <'account'> <'bitcoinaddress'> <'amount'> ['minconf=1'] ['comment'] ['comment-to']\fR +Sends amount from account's balance to 'bitcoinaddress'. This method will fail if there is less than amount bitcoins with ['minconf'] confirmations in the account's balance (unless account is the empty-string-named default account; it behaves like the *sendtoaddress* method). Returns transaction ID on success. +.TP +\fBsendtoaddress 'bitcoinaddress' 'amount' ['comment'] ['comment-to']\fR +Sends amount from the server's available balance to 'bitcoinaddress'. amount is a real and is rounded to the nearest 0.01. Returns transaction id on success. +.TP +\fBstop\fR +Stops the bitcoin server. +.TP +\fBvalidateaddress 'bitcoinaddress'\fR +Checks that 'bitcoinaddress' looks like a proper bitcoin address. Returns an object containing: + + "isvalid" : true or false. + "ismine" : true if the address is in the server's wallet. + "address" : bitcoinaddress. + + *note: ismine and address are only returned if the address is valid. + +.SH "SEE ALSO" +bitcoin.conf(5) +.SH AUTHOR +This manual page was written by Micah Anderson for the Debian system (but may be used by others). Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 3 or any later version published by the Free Software Foundation. + +On Debian systems, the complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL. + diff --git a/contrib/debian/patches/README b/contrib/debian/patches/README new file mode 100644 index 0000000..80c1584 --- /dev/null +++ b/contrib/debian/patches/README @@ -0,0 +1,3 @@ +0xxx: Grabbed from upstream development. +1xxx: Possibly relevant for upstream adoption. +2xxx: Only relevant for official Debian release. diff --git a/contrib/debian/patches/series b/contrib/debian/patches/series new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/contrib/debian/patches/series @@ -0,0 +1 @@ + diff --git a/contrib/debian/rules b/contrib/debian/rules new file mode 100644 index 0000000..98bb2bb --- /dev/null +++ b/contrib/debian/rules @@ -0,0 +1,33 @@ +#!/usr/bin/make -f +# -*- mode: makefile; coding: utf-8 -*- + +#DEB_MAKE_CHECK_TARGET = test_bitcoin +#build/bitcoind:: +# $(if $(filter nocheck,$(DEB_BUILD_OPTIONS)),,src/test_bitcoin) + +DEB_INSTALL_EXAMPLES_bitcoind += debian/examples/* +DEB_INSTALL_MANPAGES_bitcoind += debian/manpages/* + +%: + dh --with bash-completion $@ + +override_dh_auto_build: + cd src; $(MAKE) -f makefile.unix bitcoind + $(MAKE) + +override_dh_auto_clean: + if [ -f Makefile ]; then $(MAKE) clean; else rm -rf build/; rm -f bitcoin-qt; fi + cd src; $(MAKE) -f makefile.unix clean + +override_dh_auto_configure: + qmake bitcoin-qt.pro USE_QRCODE=1 + +override_dh_auto_test: + cd src; $(MAKE) -f makefile.unix test_bitcoin + src/test_bitcoin + +# Ensure wrapper is set executable +binary-post-install/bitcoind: + chmod +x $(cdbs_curdestdir)usr/bin/bitcoind +binary-post-install/bitcoin-qt: + chmod +x $(cdbs_curdestdir)usr/bin/bitcoin-qt diff --git a/contrib/debian/source/format b/contrib/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/contrib/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/contrib/debian/watch b/contrib/debian/watch new file mode 100644 index 0000000..c96d2f8 --- /dev/null +++ b/contrib/debian/watch @@ -0,0 +1,7 @@ +# Run the "uscan" command to check for upstream updates and more. +version=3 +# use qa.debian.org redirector; see man uscan +opts=uversionmangle=s/(\d)(alpha|beta|rc)/$1~$2/;s/\-src//,dversionmangle=s/~dfsg\d*// \ + http://sf.net/bitcoin/bitcoin-(\d.*)-linux\.tar\.gz debian +opts=uversionmangle=s/(\d)(alpha|beta|rc)/$1~$2/,dversionmangle=s/~dfsg\d*// \ + http://githubredir.debian.net/github/bitcoin/bitcoin v(.*).tar.gz diff --git a/contrib/gitian-descriptors/README b/contrib/gitian-descriptors/README new file mode 100644 index 0000000..46c7668 --- /dev/null +++ b/contrib/gitian-descriptors/README @@ -0,0 +1,86 @@ +Gavin's notes on getting gitian builds up and running using KVM: + +These instructions distilled from: + https://help.ubuntu.com/community/KVM/Installation +... see there for complete details. + +You need the right hardware: you need a 64-bit-capable CPU with hardware virtualization support (Intel VT-x or AMD-V). Not all modern CPUs support hardware virtualization. + +You probably need to enable hardware virtualization in your machine's BIOS. + +You need to be running a recent version of 64-bit-Ubuntu, and you need to install several prerequisites: + sudo apt-get install ruby apache2 git apt-cacher-ng python-vm-builder qemu-kvm + +Sanity checks: + sudo service apt-cacher-ng status # Should return apt-cacher-ng is running + ls -l /dev/kvm # Should show a /dev/kvm device + + +Once you've got the right hardware and software: + + git clone git://github.com/bitcoin/bitcoin.git + git clone git://github.com/devrandom/gitian-builder.git + mkdir gitian-builder/inputs + cd gitian-builder/inputs + # Inputs for Linux and Win32: + wget -O miniupnpc-1.6.tar.gz 'http://miniupnp.tuxfamily.org/files/download.php?file=miniupnpc-1.6.tar.gz' + wget 'http://fukuchi.org/works/qrencode/qrencode-3.2.0.tar.bz2' + # Inputs for Win32: (Linux has packages for these) + wget 'https://downloads.sourceforge.net/project/boost/boost/1.50.0/boost_1_50_0.tar.bz2' + wget 'http://www.openssl.org/source/openssl-1.0.1c.tar.gz' + wget 'http://download.oracle.com/berkeley-db/db-4.8.30.NC.tar.gz' + wget 'https://downloads.sourceforge.net/project/libpng/zlib/1.2.6/zlib-1.2.6.tar.gz' + wget 'https://downloads.sourceforge.net/project/libpng/libpng15/older-releases/1.5.9/libpng-1.5.9.tar.gz' + wget 'http://releases.qt-project.org/qt4/source/qt-everywhere-opensource-src-4.8.3.tar.gz' + cd ../.. + + cd gitian-builder + bin/make-base-vm --arch i386 + bin/make-base-vm --arch amd64 + cd .. + + # Build Linux release: + cd bitcoin + git pull + cd ../gitian-builder + git pull + ./bin/gbuild --commit bitcoin=HEAD ../bitcoin/contrib/gitian-descriptors/gitian.yml + + # Build Win32 dependencies: (only needs to be done once, or when dependency versions change) + ./bin/gbuild --commit bitcoin=HEAD ../bitcoin/contrib/gitian-descriptors/boost-win32.yml + ./bin/gbuild --commit bitcoin=HEAD ../bitcoin/contrib/gitian-descriptors/deps-win32.yml + ./bin/gbuild --commit bitcoin=HEAD ../bitcoin/contrib/gitian-descriptors/qt-win32.yml + + # Build Win32 release: + ./bin/gbuild --commit bitcoin=HEAD ../bitcoin/contrib/gitian-descriptors/gitian-win32.yml + +--------------------- + +gitian-builder now also supports building using LXC. See + https://help.ubuntu.com/12.04/serverguide/lxc.html +... for how to get LXC up and running under Ubuntu. + +If your main machine is a 64-bit Mac or PC with a few gigabytes of memory +and at least 10 gigabytes of free disk space, you can gitian-build using +LXC running inside a virtual machine. + +Here's a description of Gavin's setup on OSX 10.6: + +1. Download and install VirtualBox from https://www.virtualbox.org/ + +2. Download the 64-bit Ubuntu Desktop 12.04 LTS .iso CD image from + http://www.ubuntu.com/ + +3. Run VirtualBox and create a new virtual machine, using the + Ubuntu .iso (see the VirtualBox documentation for details). + Create it with at least 2 gigabytes of memory and a disk + that is at least 20 gigabytes big. + +4. Inside the running Ubuntu desktop, install: + sudo apt-get install debootstrap lxc ruby apache2 git apt-cacher-ng python-vm-builder + +5. Still inside Ubuntu, tell gitian-builder to use LXC, then follow the "Once you've got the right + hardware and software" instructions above: + export USE_LXC=1 + git clone git://github.com/bitcoin/bitcoin.git + ... etc diff --git a/contrib/gitian-descriptors/boost-win32.yml b/contrib/gitian-descriptors/boost-win32.yml new file mode 100644 index 0000000..b421cbe --- /dev/null +++ b/contrib/gitian-descriptors/boost-win32.yml @@ -0,0 +1,66 @@ +--- +name: "boost" +suites: +- "precise" +architectures: +- "amd64" +packages: +- "mingw-w64" +- "g++-mingw-w64" +- "faketime" +- "zip" +reference_datetime: "2011-01-30 00:00:00" +remotes: [] +files: +- "boost_1_54_0.tar.bz2" +- "boost-mingw-gas-cross-compile-2013-03-03.patch" +script: | + # Defines + INSTALLPREFIX="$OUTDIR/staging/boost" + HOST=i686-w64-mingw32 + # Input Integrity Check + echo "047e927de336af106a24bceba30069980c191529fd76b8dff8eb9a328b48ae1d boost_1_54_0.tar.bz2" | shasum -c + echo "d2b7f6a1d7051faef3c9cf41a92fa3671d905ef1e1da920d07651a43299f6268 boost-mingw-gas-cross-compile-2013-03-03.patch" | shasum -c + + mkdir -p "$INSTALLPREFIX" + tar xjf boost_1_54_0.tar.bz2 + cd boost_1_54_0 + GCCVERSION=$($HOST-g++ -E -dM $(mktemp --suffix=.h) | grep __VERSION__ | cut -d ' ' -f 3 | cut -d '"' -f 2) + echo "using gcc : $GCCVERSION : $HOST-g++ + : + $HOST-windres + $HOST-ar + -frandom-seed=boost1 + $HOST-ranlib + ;" > user-config.jam + ./bootstrap.sh --without-icu + + # Workaround: Upstream boost dev refuses to include patch that would allow Free Software cross-compile toolchain to work + # This patch was authored by the Fedora package developer and ships in Fedora's mingw32-boost. + # Please obtain the exact patch that matches the above sha256sum from one of the following mirrors. + # + # Read History: https://svn.boost.org/trac/boost/ticket/7262 + # History Mirror: http://rose.makesad.us/~paulproteus/mirrors/7262%20Boost.Context%20fails%20to%20build%20using%20MinGW.html + # + # Patch: https://svn.boost.org/trac/boost/raw-attachment/ticket/7262/boost-mingw.patch + # Patch Mirror: http://wtogami.fedorapeople.org/boost-mingw-gas-cross-compile-2013-03-03.patch + # Patch Mirror: http://mindstalk.net/host/boost-mingw-gas-cross-compile-2013-03-03.patch + # Patch Mirror: http://rose.makesad.us/~paulproteus/mirrors/boost-mingw-gas-cross-compile-2013-03-03.patch + patch -p0 < ../boost-mingw-gas-cross-compile-2013-03-03.patch + + # Bug Workaround: boost-1.54.0 broke the ability to disable zlib + # https://svn.boost.org/trac/boost/ticket/9156 + sed -i 's^\[ ac.check-library /zlib//zlib : /zlib//zlib^^' libs/iostreams/build/Jamfile.v2 + sed -i 's^zlib.cpp gzip.cpp \]^^' libs/iostreams/build/Jamfile.v2 + + # http://statmt.org/~s0565741/software/boost_1_52_0/libs/context/doc/html/context/requirements.html + # Note: Might need these options in the future for 64bit builds. + # "Please note that address-model=64 must be given to bjam command line on 64bit Windows for 64bit build; otherwise 32bit code will be generated." + # "For cross-compiling the lib you must specify certain additional properties at bjam command line: target-os, abi, binary-format, architecture and address-model." + ./bjam toolset=gcc binary-format=pe target-os=windows threadapi=win32 threading=multi variant=release link=static --user-config=user-config.jam --without-mpi --without-python -sNO_BZIP2=1 -sNO_ZLIB=1 --layout=tagged --build-type=complete --prefix="$INSTALLPREFIX" $MAKEOPTS install + + cd "$INSTALLPREFIX" + export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 + export FAKETIME=$REFERENCE_DATETIME + zip -r boost-win32-1.54.0-gitian-r6.zip * + cp boost-win32-1.54.0-gitian-r6.zip $OUTDIR diff --git a/contrib/gitian-descriptors/deps-win32.yml b/contrib/gitian-descriptors/deps-win32.yml new file mode 100644 index 0000000..7ad00fc --- /dev/null +++ b/contrib/gitian-descriptors/deps-win32.yml @@ -0,0 +1,93 @@ +--- +name: "bitcoin-deps" +suites: +- "precise" +architectures: +- "amd64" +packages: +- "mingw-w64" +- "g++-mingw-w64" +- "git-core" +- "zip" +- "faketime" +- "psmisc" +reference_datetime: "2011-01-30 00:00:00" +remotes: [] +files: +- "openssl-1.0.1c.tar.gz" +- "db-4.8.30.NC.tar.gz" +- "miniupnpc-1.6.tar.gz" +- "zlib-1.2.6.tar.gz" +- "libpng-1.5.9.tar.gz" +- "qrencode-3.2.0.tar.bz2" +script: | + # + export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 + export FAKETIME=$REFERENCE_DATETIME + export TZ=UTC + export INSTALLPREFIX=$OUTDIR/staging/deps + export HOST=i686-w64-mingw32 + # + mkdir -p $INSTALLPREFIX + + tar xzf openssl-1.0.1c.tar.gz + cd openssl-1.0.1c + ./Configure --cross-compile-prefix=$HOST- mingw --openssldir=$INSTALLPREFIX + make + make install_sw + cd .. + # + tar xzf db-4.8.30.NC.tar.gz + cd db-4.8.30.NC/build_unix + ../dist/configure --prefix=$INSTALLPREFIX --enable-mingw --enable-cxx --host=$HOST --disable-shared + make $MAKEOPTS library_build + make install_lib install_include + cd ../.. + # + tar xzf miniupnpc-1.6.tar.gz + cd miniupnpc-1.6 + echo " + --- miniupnpc-1.6/Makefile.mingw.orig 2013-09-29 18:52:51.014087958 -1000 + +++ miniupnpc-1.6/Makefile.mingw 2013-09-29 19:09:29.663318691 -1000 + @@ -67,8 +67,8 @@ + + wingenminiupnpcstrings.o: wingenminiupnpcstrings.c + + -miniupnpcstrings.h: miniupnpcstrings.h.in wingenminiupnpcstrings + - wingenminiupnpcstrings \$< \$@ + +miniupnpcstrings.h: miniupnpcstrings.h.in + + sed -e 's|OS/version|MSWindows/5.1.2600|' -e 's|MINIUPNPC_VERSION_STRING \"version\"|MINIUPNPC_VERSION_STRING \"VERSIONHERE\"|' \$< > \$@ + + minixml.o: minixml.c minixml.h miniupnpcstrings.h + + " | sed "s/VERSIONHERE/$(cat VERSION)/" | patch -p1 + mkdir -p dll + make -f Makefile.mingw CC=$HOST-gcc AR=$HOST-ar libminiupnpc.a + install -d $INSTALLPREFIX/include/miniupnpc + install *.h $INSTALLPREFIX/include/miniupnpc + install libminiupnpc.a $INSTALLPREFIX/lib + cd .. + # + tar xzf zlib-1.2.6.tar.gz + cd zlib-1.2.6 + CROSS_PREFIX=$HOST- ./configure --prefix=$INSTALLPREFIX --static + make + make install + cd .. + # + tar xzf libpng-1.5.9.tar.gz + cd libpng-1.5.9 + CFLAGS="-I$INSTALLPREFIX/include" LDFLAGS="-L$INSTALLPREFIX/lib" ./configure --disable-shared --prefix=$INSTALLPREFIX --host=$HOST + make $MAKEOPTS + make install + cd .. + # + tar xjf qrencode-3.2.0.tar.bz2 + cd qrencode-3.2.0 + png_CFLAGS="-I$INSTALLPREFIX/include" png_LIBS="-L$INSTALLPREFIX/lib -lpng" ./configure --prefix=$INSTALLPREFIX --host=$HOST + make + make install + cd .. + # + cd $INSTALLPREFIX + zip -r $OUTDIR/bitcoin-deps-win32-gitian-r9.zip include lib diff --git a/contrib/gitian-descriptors/gitian-win32.yml b/contrib/gitian-descriptors/gitian-win32.yml new file mode 100644 index 0000000..29a0fe4 --- /dev/null +++ b/contrib/gitian-descriptors/gitian-win32.yml @@ -0,0 +1,65 @@ +--- +name: "CryptoLottoToken" +suites: +- "precise" +architectures: +- "amd64" +packages: +- "mingw-w64" +- "g++-mingw-w64" +- "git-core" +- "unzip" +- "nsis" +- "faketime" +reference_datetime: "2011-01-30 00:00:00" +remotes: +- "url": "https://github.com/CryptoLottoToken-project/CryptoLottoToken.git" + "dir": "CryptoLottoToken" +files: +- "qt-win32-4.8.3-gitian-r4.zip" +- "boost-win32-1.54.0-gitian-r6.zip" +- "bitcoin-deps-win32-gitian-r9.zip" +script: | + # + STAGING=$HOME/staging + HOST=i686-w64-mingw32 + # + mkdir -p $STAGING + cd $STAGING + unzip ../build/qt-win32-4.8.3-gitian-r4.zip + unzip ../build/boost-win32-1.54.0-gitian-r6.zip + unzip ../build/bitcoin-deps-win32-gitian-r9.zip + cd $HOME/build/ + # + cd CryptoLottoToken + export PATH=$STAGING/host/bin:$PATH + mkdir -p $OUTDIR/src + git archive HEAD | tar -x -C $OUTDIR/src + cp $OUTDIR/src/doc/README_windows.txt $OUTDIR/readme.txt + cp $OUTDIR/src/COPYING $OUTDIR/COPYING.txt + export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 + export FAKETIME=$REFERENCE_DATETIME + export TZ=UTC + ln -s $STAGING $HOME/qt + $HOME/staging/host/bin/qmake -spec unsupported/win32-g++-cross MINIUPNPC_LIB_PATH=$STAGING MINIUPNPC_INCLUDE_PATH=$STAGING BDB_LIB_PATH=$STAGING BDB_INCLUDE_PATH=$STAGING BOOST_LIB_PATH=$STAGING BOOST_INCLUDE_PATH=$STAGING BOOST_LIB_SUFFIX=-mt-s BOOST_THREAD_LIB_SUFFIX=_win32-mt-s OPENSSL_LIB_PATH=$STAGING OPENSSL_INCLUDE_PATH=$STAGING QRENCODE_LIB_PATH=$STAGING QRENCODE_INCLUDE_PATH=$STAGING USE_QRCODE=1 INCLUDEPATH=$STAGING DEFINES=BOOST_THREAD_USE_LIB BITCOIN_NEED_QT_PLUGINS=1 QMAKE_LRELEASE=lrelease QMAKE_CXXFLAGS=-frandom-seed=CryptoLottoToken USE_BUILD_INFO=1 USE_SSE2=1 + make $MAKEOPTS + $HOST-strip release/CryptoLottoToken-qt.exe + cp release/CryptoLottoToken-qt.exe $OUTDIR/ + # + cd src + export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 + export FAKETIME=$REFERENCE_DATETIME + export TZ=UTC + make -f makefile.linux-mingw $MAKEOPTS DEPSDIR=$STAGING CryptoLottoTokend.exe USE_UPNP=0 DEBUGFLAGS="-frandom-seed=CryptoLottoToken" USE_SSE2=1 + $HOST-strip CryptoLottoTokend.exe + mkdir $OUTDIR/daemon + cp CryptoLottoTokend.exe $OUTDIR/daemon + cd .. + mkdir nsis + git archive HEAD | tar -x -C nsis + cd nsis/src + mkdir ../release + cp ../../release/* ../release/ + cp ../../src/*.exe . + makensis ../share/setup.nsi + cp ../share/CryptoLottoToken-*-win32-setup.exe $OUTDIR/ diff --git a/contrib/gitian-descriptors/gitian.yml b/contrib/gitian-descriptors/gitian.yml new file mode 100644 index 0000000..2e173f1 --- /dev/null +++ b/contrib/gitian-descriptors/gitian.yml @@ -0,0 +1,55 @@ +--- +name: "CryptoLottoToken" +suites: +- "lucid" +architectures: +- "i386" +- "amd64" +packages: +- "libdb4.8++-dev" +- "qt4-qmake" +- "libqt4-dev" +- "libboost-system-dev" +- "libboost-filesystem-dev" +- "libboost-program-options-dev" +- "libboost-thread-dev" +- "libssl-dev" +- "git-core" +- "unzip" +- "pkg-config" +- "libpng12-dev" +reference_datetime: "2011-01-30 00:00:00" +remotes: +- "url": "https://github.com/CryptoLottoToken-project/CryptoLottoToken.git" + "dir": "CryptoLottoToken" +files: +- "miniupnpc-1.6.tar.gz" +- "qrencode-3.2.0.tar.bz2" +script: | + INSTDIR="$HOME/install" + export LIBRARY_PATH="$INSTDIR/lib" + # + tar xzf miniupnpc-1.6.tar.gz + cd miniupnpc-1.6 + INSTALLPREFIX=$INSTDIR make $MAKEOPTS install + cd .. + # + tar xjf qrencode-3.2.0.tar.bz2 + cd qrencode-3.2.0 + ./configure --prefix=$INSTDIR --enable-static --disable-shared + make $MAKEOPTS install + cd .. + # + cd CryptoLottoToken + mkdir -p $OUTDIR/src + git archive HEAD | tar -x -C $OUTDIR/src + cp $OUTDIR/src/doc/README.md $OUTDIR + cp $OUTDIR/src/COPYING $OUTDIR + cd src + make -f makefile.unix STATIC=1 OPENSSL_INCLUDE_PATH="$INSTDIR/include" OPENSSL_LIB_PATH="$INSTDIR/lib" $MAKEOPTS CryptoLottoTokend USE_UPNP=0 DEBUGFLAGS= USE_SSE2=1 + mkdir -p $OUTDIR/bin/$GBUILD_BITS + install -s CryptoLottoTokend $OUTDIR/bin/$GBUILD_BITS + cd .. + qmake INCLUDEPATH="$INSTDIR/include" LIBS="-L$INSTDIR/lib" RELEASE=1 USE_QRCODE=1 USE_SSE2=1 + make $MAKEOPTS + install CryptoLottoToken-qt $OUTDIR/bin/$GBUILD_BITS diff --git a/contrib/gitian-descriptors/qt-win32.yml b/contrib/gitian-descriptors/qt-win32.yml new file mode 100644 index 0000000..1fc6f86 --- /dev/null +++ b/contrib/gitian-descriptors/qt-win32.yml @@ -0,0 +1,63 @@ +--- +name: "qt" +suites: +- "precise" +architectures: +- "amd64" +packages: +- "mingw-w64" +- "g++-mingw-w64" +- "zip" +- "unzip" +- "faketime" +- "unzip" +reference_datetime: "2011-01-30 00:00:00" +remotes: [] +files: +- "qt-everywhere-opensource-src-4.8.3.tar.gz" +- "bitcoin-deps-win32-gitian-r9.zip" +script: | + # + HOST=i686-w64-mingw32 + INSTDIR="$HOME/qt/" + # + mkdir $INSTDIR + mkdir -p $INSTDIR/host/bin + # + # Need mingw-compiled openssl from bitcoin-deps: + unzip bitcoin-deps-win32-gitian-r9.zip + DEPSDIR=`pwd` + # + tar xzf qt-everywhere-opensource-src-4.8.3.tar.gz + cd qt-everywhere-opensource-src-4.8.3 + sed 's/$TODAY/2011-01-30/' -i configure + sed "s/i686-pc-mingw32-/$HOST-/" -i mkspecs/unsupported/win32-g++-cross/qmake.conf + sed --posix "s|QMAKE_CFLAGS\t\t= -pipe|QMAKE_CFLAGS\t\t= -pipe -isystem /usr/$HOST/include/ -frandom-seed=qtbuild|" -i mkspecs/unsupported/win32-g++-cross/qmake.conf + sed 's/QMAKE_CXXFLAGS_EXCEPTIONS_ON = -fexceptions -mthreads/QMAKE_CXXFLAGS_EXCEPTIONS_ON = -fexceptions/' -i mkspecs/unsupported/win32-g++-cross/qmake.conf + sed 's/QMAKE_LFLAGS_EXCEPTIONS_ON = -mthreads/QMAKE_LFLAGS_EXCEPTIONS_ON = -lmingwthrd/' -i mkspecs/unsupported/win32-g++-cross/qmake.conf + sed --posix "s/QMAKE_MOC\t\t= $HOST-moc/QMAKE_MOC\t\t= moc/" -i mkspecs/unsupported/win32-g++-cross/qmake.conf + sed --posix "s/QMAKE_RCC\t\t= $HOST-rcc/QMAKE_RCC\t\t= rcc/" -i mkspecs/unsupported/win32-g++-cross/qmake.conf + sed --posix "s/QMAKE_UIC\t\t= $HOST-uic/QMAKE_UIC\t\t= uic/" -i mkspecs/unsupported/win32-g++-cross/qmake.conf + # ar adds timestamps to every object file included in the static library + # providing -D as ar argument is supposed to solve it, but doesn't work as qmake strips off the arguments and adds -M to pass a script... + # which somehow cannot be combined with other flags. + # use faketime only for ar, as it confuses make/qmake into hanging sometimes + sed --posix "s|QMAKE_LIB\t\t= $HOST-ar -ru|QMAKE_LIB\t\t= $HOME/ar -Dr|" -i mkspecs/unsupported/win32-g++-cross/qmake.conf + echo '#!/bin/bash' > $HOME/ar + echo 'export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1' >> $HOME/ar + echo "$HOST-ar \"\$@\"" >> $HOME/ar + chmod +x $HOME/ar + #export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 + export FAKETIME=$REFERENCE_DATETIME + export TZ=UTC + # Compile static libraries, and use statically linked openssl (-openssl-linked): + OPENSSL_LIBS="-L$DEPSDIR/lib -lssl -lcrypto -lgdi32" ./configure -prefix $INSTDIR -bindir $INSTDIR/host/bin -I $DEPSDIR/include -confirm-license -release -opensource -static -no-qt3support -xplatform unsupported/win32-g++-cross -no-multimedia -no-audio-backend -no-phonon -no-phonon-backend -no-declarative -no-script -no-scripttools -no-javascript-jit -no-webkit -no-svg -no-xmlpatterns -no-sql-sqlite -no-nis -no-cups -no-iconv -no-dbus -no-gif -no-libtiff -no-opengl -nomake examples -nomake demos -nomake docs -no-feature-style-plastique -no-feature-style-cleanlooks -no-feature-style-motif -no-feature-style-cde -no-feature-style-windowsce -no-feature-style-windowsmobile -no-feature-style-s60 -openssl-linked + find . -name *.prl | xargs -l sed 's|/\.||' -i + find . -name *.prl | xargs -l sed 's|/$||' -i + make $MAKEOPTS install + cd $INSTDIR + find . -name *.prl | xargs -l sed 's|/$||' -i + + # as zip stores file timestamps, use faketime to intercept stat calls to set dates for all files to reference date + export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 + zip -r $OUTDIR/qt-win32-4.8.3-gitian-r4.zip * diff --git a/contrib/gitian-downloader/aspect-key.pgp b/contrib/gitian-downloader/aspect-key.pgp new file mode 100644 index 0000000..3fe3902 --- /dev/null +++ b/contrib/gitian-downloader/aspect-key.pgp @@ -0,0 +1,20 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.13 (GNU/Linux) + +mQENBFHHt8wBCADfLkCS9624TrJ4pbFf5Cg88p0Sp7qofcPGjPa6K1Gs9cLfPHLu +EX17YvS9zxdmdlogVDGQ3d82O2OrjDCr26yioZteHsK1oPdzgwQJ5tAGxlv75UiW +BvhaDKXVfX+rdb+wnK3YMhynNQbG6pxQ0Q1Qujh6Xw0b1wbvg2FNEwpfHmL1ZoYd +ba0w6eRmREBMrk50lp8pmDxWjc7+7SdKPxqWsPpWOJTaMBVQNSaVr7ePoCOKwFNI +7CQiqMlGJUG1Zb7CnEkbiwNSwEi0VQkz8Ir8gBFzCTEgt2K1EGO8yo0Mq0hNTRdW +bC14pamlbu/+nx+LAefSJ9sMx7n/uzTjI1uTABEBAAG0LUFudG9uIFllbWVseWFu +b3YgPGFudG9uLnllbWVseWFub3ZAZ21haWwuY29tPokBOQQTAQIAIwUCUce3zAIb +DwcLCQgHAwIBBhUIAgkKCwQWAgMBAh4BAheAAAoJEKFZZWb4e+YxUwIIAM7DoCnv +lJXX5+z9My0RBIpd99if1udXBVtVxPgaF7lDDIaKY2cBktyXBp/R/G8arYRc9zIK +e+0bY+POpTrfMoTioqbiwoxPwhkXu39KwvPfSWY1UBQHTcMpFibMuFjRu6YrAEZ2 +ZH+CrLcmn6rB66uVm3OPE55sKdAL9C+TGIKU/GIRgQzI3S9/DT8E/F4Oifx/V9sh +V2flIXU2QBH4ZOP0G+OTp6QXZRD9TBfmrYRMU+yL5sYMalCjwz79ZZua50CQI5hN +5pEBvHxZ4GziWc5V40GSv1RnM/mGgrdmBlzej34OLjfEac2PCrZy8+FnQf04Lhvn +cRQpj+5V0a6RIfKIRgQQEQIABgUCUciczAAKCRBr3f6OVKKs8dqgAJ94zRvs99HM +pItL1r8v8vS5lXZrHgCfXy5V7AFtL8XvZlPC0uQqdyWKzHo= +=Bp2a +-----END PGP PUBLIC KEY BLOCK----- diff --git a/contrib/gitian-downloader/bluematt-key.pgp b/contrib/gitian-downloader/bluematt-key.pgp new file mode 100644 index 0000000..fb6d9eb Binary files /dev/null and b/contrib/gitian-downloader/bluematt-key.pgp differ diff --git a/contrib/gitian-downloader/coblee-key.pgp b/contrib/gitian-downloader/coblee-key.pgp new file mode 100644 index 0000000..28497f5 --- /dev/null +++ b/contrib/gitian-downloader/coblee-key.pgp @@ -0,0 +1,75 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.13 (GNU/Linux) + +mQINBE3zxrgBEAClsDj56PD9OheUjteWspOstSGxARKo05jeyK3wSM+dT7GIysxE +/0vyI7JS4GdYtFFIGbaPQRPgE+fs7BuSjQ5THHpOzna0lpxzE9lEmR1n2IntUpSL +7u2818Bu8HI5Tjjpw5VbCWjnbPfi2CfIoOgHUngRHy0cDBE86Ty/OmI7+2CwV0Ns ++Neo4NDJrMDmlOHr96+HZst33JKyeVJ+u8GzT0m3ilupGi/8UDDm0IDe61ZxAFqt +9NQyIi76rp8rD3yP/yH2PimmSjnFNiIhTAG6dEz97XN3pvzEkgBbPtd2c857SNb1 +zBGrORWBeIDbvHKGVfMPL6mt0MRTkGDlI3kf0dNooxNHZrtJNWRe3KeZpSo2fJbl +vYojpSqfZmGch+IJbC2KAqowfIzBuEVnyifoFQgS7gi7jBUHVYLamdUSY7cHRgCW +Qn/xj9liagk8QKS5L13q8rPDi466M84m/hp6HYMuSA9mhqDdp/JHA03lxHYOMSkf +QM5sZMeUGF9sEi/xaexV/KC9tzqhtmHHjn7GS5bwj/FrESFpCPaZAWCXzIuacmJ+ +lYAxf4uiBcC0qe4mU9AffmhYnNj4CCtW9CzpMP+gI/s0rdvgWuRgaE1LBTzdYFU2 +aVi61d8QY4PLiAsaMxm8gS/fwTnMSktLYhI4erOFa95+MVn18fVtxavgpQARAQAB +tCJDaGFybGVzIExlZSA8Y2hvY29ib0BhbHVtLm1pdC5lZHU+iQI4BBMBAgAiBQJN +88a4AhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRCCisH5TvJgU4KLD/4m +gbHmYlLqckM/wBg++pyf0ccnJqT05KiBD5NA0tNyGbcy6Ea+ppACsoFbr6zsSkhF +2HvKms2bZQrkZh7eCd8BxsQfJ1IiW1cZBa9v395ce0Xb7yyqHd1OhXokyhEAVgFo +lI1UP2eerkc/FGUGE5k3XPmMlpv1K54GGMozJNc7pScBR48e/cNQo0eizEKzZJH1 +d1fqrBVDyXMOEtkJyuPemCJgMmQ0heODCX1HxmoyuSmqtLSkjJOziCNGy1bbXpdp +G/imbRVQNVJCURDsztu17HHBMhJeN/xkGn830s3Eb6lQFkSMMtgwNqHbZfGflOSH +bfxvbFsQeqotlwED+Rzbsb+Xoua6I0SANPQVhf7gtlL7cWDOGTMsbbhhpSOI36/F +Qs51vItjBrWD+uYB8H5Cv6vkUvxVvn/70pD7pwyHwF5iHGfqvqCnEay0N5IMEuw2 +VgpCBpcZgWuAFGtmMpEmB/N5D9BBRUiWpI45aQTs9SCIGOR4dAJg1M5pmdy+GIyD +VcTO9o2reqAbEmBc0uq3olZfVV81JvABmQ7KX3QKI0UaQGFCUqeL/f84jrNiaO7S +ye0pTZQ6pomD4H94882kTyEtSR9YbsvP77/+bBv/gNUDd93Nn22IYSe0Hrg6eSAJ +GZR05xghFRlEtGfuDy8kEckWd6CjLdG2Vk+noKmLJIhGBBARAgAGBQJRoHvPAAoJ +EGvd/o5Uoqzxq18AnRksV8DKJTRm9vBFVxUn1x7YJPb/AJ46ujshfyV2/CRqri1W +Y61SIJNfLYkEHAQQAQIABgUCUdDbRQAKCRCxF563NH3BDc7eIACUgvlym0n1cvNk +z5itI02sycg1+RPDWsYIoMKrLzsGaTxnTn0zuu1V+jDAQT6Zhb9Uvf296KTGxnQS +NX22ViM6df7rqjkPzVlSSIuHN2GqmqXHdM76M8aqtyn246yDlI9ayVQbDLrNI8L0 +3Wx8Tdnotiu40dNLFuMX8fq8nyLTCkTVAVuns3i250gbp//ctIIbNeKQYItvBe4t +Zj+gwPJdHxX8THuiskJrAs3x2+WXuzZ/Tbpva7HqMITuSKFus5zQWO4RY+O2OpNl +wcRirtPdLy7dubzDtYNOshKi7szpwexpsMAQovaMf513K3eSWg5c3Gne8vpD0QKo +1iksjjyBFnuL0jrULlQN/K71nV0AUS03PyQdlzTubxGhK5xajiyLu9pjpIo5dq9V +sNP2LHSHGPrvvsOUh6Df/dkVnkxfpJY87RHSvqjz25OwE5KVMIWGnLvmJ9vj1Vri +dWLFhrwx/IZRNTiYJNJSbaSnljGk3n8SBe17+olIlYI1szevaJxst+7/HbOKNK7A +5Ggsi6JB4jnOiCNJ6dPIuOQHP8NCD0AL19y0SSnC1q6q75RAXw4fWzBgX5HK7psY +tsuvZw4qCsaXyCCBh7Qql8Io1m+Ugsxu1BifsI5s/I63SR/MEh23i3Scmtocmm3K +rkU5tgwXuFP1fZ9OqGsmYvlWN6q0ItdeG+9kUKs3zCGiaHqkJGvdVbtMwyx3KMkF +QqbyOkdaSff8Rkz1GDt0PTEVt4JMH8F2aAEG5WgjWwIMJlD2Knp73UfSa6jqbhlU +6M9mmGKIxbAQ3GUyfzvFE6VFPDS4B09uy0J0HCd1K0nvnKxQaX0ixEsHrpiG5IvH +teUn+DjqgVE7aBb28vYfsZB8yFl5SUVb7/7qqByeWzRmAQFJoA4pVu6kRGQG4QZb +YfE4aGLq88p4qnyJQEM7KIGBeRA6iQmTPvTTM0HgxQigUJvGrnr+SYKEXAF/ObkX +DsJunYcaNKh/JqczIg1YXc3raF+3yVcCZSDOiaipNQ1BGNKf/ko7hBM7mQJKR9Fy +VnYFzi2zAu/0FrJQWjLS7fRYlB60yl3/bWj4RxfaqXVIco0eLayVAWetsafBYC7M +ZMSIN9xzNoZQNwmTNWqVF0jRzSMonOgdTwj7R9GY/OU68a9VlXT9/s+YyUOqqOT1 +HYWrcIueaDPSTRkhEyOstg+K7m+ykG86y1GvqhWrZTqzDbPpEEoFVpLz5crQFHWK +Rs8G77h7YanhdI3+2rKrDLnolMOkgFFPdckD3aHwXTlpr9uuZ4hIhTJEBE+wFcOi +Ps4mjwdRS/9noOo4OiwTqPtu3hF6hal4q4afhTbCNajRV14Po69Sjhhz+axUtVoj +kD4LNltquQINBE3zxrgBEAColGLJYSaI+eJEQTEkpz9S4+7Tf/qoBo5mPO2jOOP7 +7ZwLLpYsMkMOtDWhiyMer6uLf49XrLJltCG4CNMbUUWIMhDEDA+iXhFOU1PljRBv +zJfNZI3sWs/42/ieA32ldSjEuVwGPRa9V5DSzasLunhiYl5E9AZOjpjjqhM8HO7c +TSShExlZcKgHf6313SKm/qJeFeC24BRu4aaZRKiEB9w6XBP55vu139QmCt2EUXYK +YPAgtOUnzUDqW+V2Z6oiDFHzFu0/9DtQyZdW3anVxudeogs6xy+GNbMNFG9lu+c6 +6qvhc/GLCr9CoHWHvB5T4hyfgKy0knO+j5AWoEnQe7+adQ/6uGGyBtxzP5DotbBS +B7OB5jDyIrB2uroHnAp7IONc0E83afxyYAxWws00C2qsG5htjP1dpSPpgcHSGvf2 +BWTpz8453mXNrY644RXyGsZiXOJOwfa/TtoeK1SA78Pj2LkQ13ES7Y6GX2zR8cqK +lAwFmXKMk76X8J6hb9h4rYgSzHkJQO9TjCq705eF66K2Z76ZC9A5QE19Gea7Tx5b +/GkpvYtOt/gx6P/Png6z8A1oGGGLWevSUxHLTrBZTnjmeHQG5Nx+po5zuM8BxRxF +GLn2NI8UxgdpKV0cZOX+p/WVSUoC+DNyl5fyxBKMTgeqYAqYNh7UDyruX5E+tlYF +7wARAQABiQIfBBgBAgAJBQJN88a4AhsMAAoJEIKKwflO8mBT6cIP/RFn8rgRK1Ib +JsBl3DJPx7yHmavp52t2vN+4ZHqkhnpzbEuFT2nWSetUQJ5J9LHoSkHoHn1yGFEQ +ctlvNqR35xX3PKHrCYv6XNHkUUj3pCcQPP7aenxqzyB9tNgyz8DqbnXNbqELhPvs +60ffmsEXgq2dNO+/wbpPW1+Yd6F5Q2X1g1dlJufFpFV0RTQFaCviESMV+lTgFAON +EpDK3wnByLukABLVKVrzq/xGly6OqeP4Rh9TvSYi/x3iqv+2wn5XZ5aGTIqWHVFb +FfwqCO4US4KdVXodubegED529TfwMzgDBfADC7HNtrgEBBs9mLT5ypv9xvyJxoMN +f1ATfKucRrRDbtSwZk3sh2LgZgXb/AAC9sZi9E6OEhXaWpx+tZNFon2vfzIcpYQz +C6rFezRGAOP2asTVqdmrtJDClHttydASNP5AKzLoc+hAmNdpMxaTnAwANVKtJggM +oS+YyINISiCFNDli+qKkl2VYXa6NK9GZGiwOVrIAFCpt1F/R+/aY+gPO9Cu+ZuJY +/XKSkUqrupqrXgc1KAtkfLEc3JuirG9rbJWDLR0kStjiczm4zW3CTzS4UwF3a5vP +lMyftAvOF+/895E8HzrO3lpT6/SwUDPUTEmv3ktcq4L0ZiX3uBiBnagbi4RREILf +Vk4vo5kBKxymtUB8mUtVL19rR0zwINuZ +=f8X6 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/contrib/gitian-downloader/devrandom-key.pgp b/contrib/gitian-downloader/devrandom-key.pgp new file mode 100644 index 0000000..7189812 Binary files /dev/null and b/contrib/gitian-downloader/devrandom-key.pgp differ diff --git a/contrib/gitian-downloader/face-key.pgp b/contrib/gitian-downloader/face-key.pgp new file mode 100644 index 0000000..aa05163 --- /dev/null +++ b/contrib/gitian-downloader/face-key.pgp @@ -0,0 +1,54 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.13 (GNU/Linux) + +mQENBFG9XmEBCACTioQ0fYcqjty1XBsPB+RXP6BIsMSuPWyEIoa/lMHr1i+7fpXN +CMtMoTRvu0+BzKvZbP1CoqkE+nqBZC4TybjlLJS+EOJRkgqm1PL+lB3B3EgLg+9h +TGf5cUqHL9qxLuVhQqjMXIJJn7N9dPPsg7TDQwnUxBGUVpKGyiEdRqL3nvubK67q +ysrwo9Ene7KVkz2O6hDX22cXJZVohA0QiNwqjG6mrIfyVEF5UyocAM7B5qHhG6f2 +FFc5+HykTVta6L10vsEXy3dO24Kmc8Uv+0iMCPqXzskQozGW0KrexFB7dkBCkhsf +XFjsZBJKIpQ3jtqUMBYka6fDxALE4eMRTvWhABEBAAG0H1JhbWEgTWNJbnRvc2gg +PHJhbWFAbXl1dGlsLmNvbT6JATgEEwECACIFAlG9XmECGwMGCwkIBwMCBhUIAgkK +CwQWAgMBAh4BAheAAAoJEGV+sBZSFnDAxZ4H/RyAOdEVu+KL6HwpjPsL94Yb0Mq1 +SkcHlvagzXRcMvycqqNlX1p3pOY8UNjErx2XMjgDdXMM4TC4QjmK1U+6nencRBQl +Qi6y2XuJXfJ4oFuSTYkhRw1gAivTcyak6BwJIbNkLBGRnlEWcUOSDLdnteSLGqgI +PbhBpBJsjtDH/Zr3wNH6K5ULvD9BPnb5fSRz9chaH1rreb0j9QhmryUAg7ijhltU +au1KTBWToMWAWH+POD/F3Xtxs1IbLZL2O8/D+2PowPQgMnD49wCdeRKdwNiNAypz +5o64eO6roI4xyDXAA5w/KhaKR6JCkmAbNZISuH1FIxnDEs5XFH7s4swgkxCIRgQQ +EQIABgUCUcAwTgAKCRBr3f6OVKKs8VKCAJ0YbK5dkE0AL9zfhy7Ekf/1Hj0urQCa +AujbjDb4z5HMc0+SUiik8j62/ZmJBBwEEAECAAYFAlHP/GAACgkQsReetzR9wQ0X +4iAAha3mmABoOwMkhE7oWv7J5Q5svSBPOr8QKBOBqdWpk3VeBlykYhcbadGFknS+ +6bi52lSulYTzbwoBjljw16EaHr+xPVBUjcya6boADQv+nOWyI+PHihJg64JdIPe6 +T/PM0J2vDcwziXjhQCRHtPAYLVnipwUbm9MB2gRGc6RFawFx2/Yb3AD/d6C13b2w +hgr7fx65vHi2aT3i29lIRZvwugQHjoACtmz/9poVLL0xSfgZQc2su/JnHch1Zg7u +KxVGb/celesZyfcd/0/SOaK8O1YLE2Um0gYrcG05L9TLhyvIP/MoL6jYLNz9n8Z8 +QPx1JDHgR3GbDKuifdOhlYdhmYEo2Ow673/QhnMDnNU14XQZx8lNBfbaZOymamIc +OtuGI04hDkJjVmuF73R1Fhx6xzv4i5KVSOouTUbthT7/RcZlYqtt56pPHv5WGapw +5udQVTEm0juC3BVNbM5qsYpjqYAZ7BQPAF0uYBO/Wtinssxwm+pS79/qX2E8Je0T +a0qlp9alfR1Y8yTtxjMXTYXrHre9ozSTE76m8wj9YCQOyiWi1Zs8RoUacLsRx5n3 +KStbru/2/DhKaAfZpvZuHzXOAbDhokzFXeCpUc40WeGvbWvyL/bbWggWYAGr1dve +UvBARFnbhAwpcsMWu2Egp1xtoLrTvcpAemZCBsPM6m8x5DWwOWaG3kiQqB5ZLn9D +RLQJejEkh/KkAIl5GUJV3JQe2dQI3YCAYwVr8MxnD7zxdqtUNNxOcxP1/NHTNUta +CJiuW08wsrS3wTpNHM2N4kc8z1Ky2b5S4h0Ytrgk20xKw5qQthpX7z7sY/HAfjJa +g+UemdWT0rdaZ4gum9uJ2SV9LWrzZ8f0MUZl9kwwtPXWdWlr4/V+F3lImBT3CmN4 +PHFF8CuU4I0/a42Ito4LJzUO1qRufpRJf16wNWbe3Fyh9wsVHjJd+lS2GsQ9jNme +c2a5G7cW4BdvB6UnPp4ofVe6v8j6xl2C/6Ev4/26vm712FaPEACWLBCSAAky8h1o +nIGY4LOL5cMrQqlO25AhwojpXjY0SuBzZrSg2x0qB8q7VCjIiWcEYLrp9AG395na +f3FA1gQ62W6oSBu6tk+HRcuiDjFTHvGAjU+SWEjrYyiUambt5KtDzRcJW53Agcyd +Y/Op1tqme/qzS/aedo0xaYFscoH2mWk55/bZPL6V7rQgSUsH+dUnxr3kJTGZXSeA +3SRy52KdKOh4zkolQH/Y+NOmIoo+C7/kU/wWkFrOOZmJ9h2yzctOlgYG+8bdLmMk +xCnWqyBpfUfgGh3NsyidnrxYvdJpGEwEuaEoT1to9IVpIW83B2CDyzMFPuxXkkmS +GDuDxUQHZ2ilTH41UdQJvpoCbrkBDQRRvV5hAQgA2lx2K9AzXA+hMLcfQ4eHbUNE +DsQsGNjt3wnjCwY2CmquzBhSCfHNnaPRV3i1qJP0GIBNHbsVD/BWMLVpSsoMYuXv +G5YW6TReeIdYLGQsuOY+B2KWp9Q+ZsEyITSd3SVWSNrcJTYyx6pdp+/RtkezbMLc +kGRzGq4024+1YcFQxYBVAHNmnzh/qbOveCeehxfqVip0GO/eVsMAsB63TcTjSX/0 +lEbeAR88awvgkGiV9sD82Tb+Dskbal24ty1IGQlINK/iM81uEpbWfTgwN4UUlvNx +My+3R0nR0T0n90keSCKp1ijOZIGXl/6UJsdEyUrEx61FClp5cGhDqh4O+E75dwAR +AQABiQEfBBgBAgAJBQJRvV5hAhsMAAoJEGV+sBZSFnDAKSEH/27wC4uqv3A4d+cs +o330BzLRGpjYkIXkLtkSgoRABVPOs++gQc/UxLNhuWhA8PTh0mPU7rw1gE/MyiEu +DeFc+aUDT0Z/I+iKJTrDmS46+hmR29j3xfKvE7B5N7MaWME2k4m8zFt2Uts52asm +R+k8bOgAXIyHRTBPCQAg3yikA6I3zwF3q7gytCLlVR6syXNXCDXtjLmUPj9x10YD +X+Epb6tmJpTGGSkrg6CTWBOrhQhSukcd6pJgFE6JfR515uJHoYdiNqdfEf5IY5x7 +ai/zq4vwshLWeN9/nLUQeKUlsVfJHzJzUKV73s/zk6R4ZvMl1gNcTdW9Lv/EWSZ0 +e0T7DjY= +=KQ8d +-----END PGP PUBLIC KEY BLOCK----- diff --git a/contrib/gitian-downloader/gavinandresen-key.pgp b/contrib/gitian-downloader/gavinandresen-key.pgp new file mode 100644 index 0000000..f81f44e Binary files /dev/null and b/contrib/gitian-downloader/gavinandresen-key.pgp differ diff --git a/contrib/gitian-downloader/laanwj-key.pgp b/contrib/gitian-downloader/laanwj-key.pgp new file mode 100644 index 0000000..5592951 --- /dev/null +++ b/contrib/gitian-downloader/laanwj-key.pgp @@ -0,0 +1,28 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: SKS 1.1.0 + +mQENBE5UtMEBCADOUz2i9l/D8xYINCmfUDnxi+DXvX5LmZ39ZdvsoE+ugO0SRRGdIHEFO2is +0xezX50wXu9aneb+tEqM0BuiLo6VxaXpxrkxHpr6c4jf37SkE/H0qsi/txEUp7337y3+4HMG +lUjiuh802I72p1qusjsKBnmnnR0rwNouTcoDmGUDh7jpKCtzFv+2TR2dRthJn7vmmjq3+bG6 +PYfqoFY1yHrAGT1lrDBULZsQ/NBLI2+J4oo2LYv3GCq8GNnzrovqvTvui50VSROhLrOe58o2 +shE+sjQShAy5wYkPt1R1fQnpfx+5vf+TPnkxVwRb3h5GhCp0YL8XC/BXsd5vM4KlVH2rABEB +AAG0K1dsYWRpbWlyIEouIHZhbiBkZXIgTGFhbiA8bGFhbndqQGdtYWlsLmNvbT6JATgEEwEC +ACIFAk5UtMECGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEHSBCwEjRsmmy6YIAK09 +buNXyYQrJBsX16sXxEhx5QPKyF3uHJDFJv66SdnpvIkNoznsaPiRJkbTANop93FZmaGa6wVn +zGDiz7jPA8Dpxx5aAYPhIT+zPJAdXWM3wJ/Gio9besRNzniai8Lwi5MZ9R/5yFGBobm6/AcN +4sUoqA3NSV2U3I29R0Vwlzo8GVtmyi9ENSi6Oo7AcXNTRt69cxW4nAHkB+amwwDJlcAb31ex +bogYXPhScwqQZixRr+JBkKxBjkTXXnQypT4KI5SegYwQVYfyiZmDP7UHKe/u6pSKKbVphLg8 +xLB5spcXse8/a2+onrbNlw6y8TXiJ++Z54PE7zztWTXf2huakeG5AQ0ETlS0wQEIAMNO3OkP +xoPRKWzBLcI7JRITAW+HNaLTq3uN2+4WxA57DEjbL9EDoAv+7wTkDAL40f0T+xiu6GJcLFjw +GJZu/tYu7+mErHjrdo+K4suCQt7w5EXCBvOLjhW4tyYMzNx8hP+oqzOW9iEC+6VV91+DYeqt +EkJuyVXOI4vzBlTw8uGow8aMMsCq8XVvKUZFTPsjGl197Q5B3A+ZOFCR8xqiqdPjuz6MglVV +oFdDNu3EZn8zkGsQlovXoE9ndVeVzx/XMNmsxFaMYsReUs253RIf1FEfgExID0fg2OnyLCjS +2iFW1RgajS+/saIkKl+N1iuMzJA7wMAM0plhRueOG0MtZSsAEQEAAYkBHwQYAQIACQUCTlS0 +wQIbDAAKCRB0gQsBI0bJpmsDB/4waenn2CvSHXyomykfpwf5lMte1V5LvH3z5R2LY+1NopRv +LSz3iC39x69XWiTbhywDfgafnGPW4pWBOff2/bu5/A6z1Hnan1vyrRRD/hx1uMJ7S6q+bIvZ +iVIg1p0jH6tdIIhwX3cydhdRZHo7e9oSMgOUWsr6Ar59NRo9CENwGPE4U61HXfOnxWdrFWoA +XdwZczBeLxmUy6Vo6sKqv+gE4bqrtAM0sY/MsQ9cU95x+52ox/sq44lQMwd3ZBYUP7B1qbHI +hZSZuch6MLi5scLPeau0ZvCaljiaMeivP5+x0gWPRs0kI+9sZxInbqvrsJ6oOBJM3xYGhtn1 +zZ7qmZR7 +=si/k +-----END PGP PUBLIC KEY BLOCK----- diff --git a/contrib/gitian-downloader/linux-download-config b/contrib/gitian-downloader/linux-download-config new file mode 100644 index 0000000..1d38b60 --- /dev/null +++ b/contrib/gitian-downloader/linux-download-config @@ -0,0 +1,58 @@ +--- +name: bitcoin +urls: +- http://bitcoin.org/bitcoin-latest-linux-gitian.zip +rss: +- url: http://sourceforge.net/api/file/index/project-id/244765/mtime/desc/limit/100/rss + xpath: //item/link/text() + pattern: bitcoin-\d+.\d+.\d+-linux-gitian.zip +signers: + 0A82509767C7D4A5D14DA2301AE1D35043E08E54: + weight: 40 + name: BlueMatt + key: bluematt + BF6273FAEF7CC0BA1F562E50989F6B3048A116B5: + weight: 40 + name: Devrandom + key: devrandom + E463A93F5F3117EEDE6C7316BD02942421F4889F: + weight: 40 + name: Luke-Jr + key: luke-jr + D762373D24904A3E42F33B08B9A408E71DAAC974: + weight: 40 + name: "Pieter Wuille" + key: sipa + 77E72E69DA7EE0A148C06B21B34821D4944DE5F7: + weight: 40 + name: tcatm + key: tcatm + 01CDF4627A3B88AAE4A571C87588242FBE38D3A8: + weight: 40 + name: "Gavin Andresen" + key: gavinandresen + 71A3B16735405025D447E8F274810B012346C9A6: + weight: 40 + name: "Wladimir J. van der Laan" + key: laanwj + AEC1884398647C47413C1C3FB1179EB7347DC10D: + weight: 40 + name: "Warren Togami" + key: wtogami + E084FE305BDF0C476F779792657EB016521670C0: + weight: 40 + name: "Rama McIntosh" + key: face + 1A2511E978239E491A096D0A828AC1F94EF26053: + weight: 40 + name: "Charles Lee" + key: coblee + EFDFCBD38FFF68B49160C7D3A1596566F87BE631: + weight: 40 + name: "Anton Yemelyanov" + key: aspect + 59CAF0E96F23F53747945FD4FE3348877809386C: + weight: 40 + name: "Adrian Gallagher" + key: thrasher +minimum_weight: 120 diff --git a/contrib/gitian-downloader/luke-jr-key.pgp b/contrib/gitian-downloader/luke-jr-key.pgp new file mode 100644 index 0000000..275b041 Binary files /dev/null and b/contrib/gitian-downloader/luke-jr-key.pgp differ diff --git a/contrib/gitian-downloader/sipa-key.pgp b/contrib/gitian-downloader/sipa-key.pgp new file mode 100644 index 0000000..ffa09bb Binary files /dev/null and b/contrib/gitian-downloader/sipa-key.pgp differ diff --git a/contrib/gitian-downloader/tcatm-key.pgp b/contrib/gitian-downloader/tcatm-key.pgp new file mode 100644 index 0000000..baaec76 Binary files /dev/null and b/contrib/gitian-downloader/tcatm-key.pgp differ diff --git a/contrib/gitian-downloader/thrasher-key.pgp b/contrib/gitian-downloader/thrasher-key.pgp new file mode 100644 index 0000000..14da2c7 --- /dev/null +++ b/contrib/gitian-downloader/thrasher-key.pgp @@ -0,0 +1,30 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.13 (GNU/Linux) + +mQENBFHBpq4BCAClUHF5fvpm1V0dxM1QKenkqeOl7w0EJ2MSZ26nzzH22yVOvwED +5h/7/Lb+o6QyPf/89uEPsPi4paPzgkDPT+CoZAkjKyzWy2YW/m2wHWoXWw1xSJql +qxFogmrq3ZHbjnxYOjAA4KsGpIijbLUAxOaAl5dkOCDEFl0KiKZzrXJNnYlbFef0 +fqj10QVW+o5uV9wYH6UMoc2x4yVucpLyJJVy25Qz33dqcG+nYdsT+jAPVG2Fcig/ +WlHZ2fQFloH3mThOa6PIHbym1YzjzLRLXH/oobE9RASpdwbsivVTUfq49B7BecKC +uwPRCWnv5es+dfRZrPsoipckB3ZNLQIy618TABEBAAG0MUFkcmlhbiBHYWxsYWdo +ZXIgPHRocmFzaGVyQGFkZGljdGlvbnNvZnR3YXJlLmNvbT6JATgEEwECACIFAlHB +pq4CGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEP4zSId4CThs/5AH/3jY +S6hF3P2IuXfmsO8WGxcZNHx+aTwDcAdpAr4PQ9aZWXbPwBD9cDxbqJkFA604cZfT +2YrL0sRrNXyz9auFTaSxM6nWsGuI60yd2+R+DfRo7irAsJgCG1Tzx5XYOuQXlhqh +Spq4tnv5lNvJczBlijC2G7uX7bUKvPN+AqmqWMuCcS7Pezrb5d0x5JJKQhw4g+ch +Qj8bmD57kFELYq4yXVx7Wraitgn8l+pzRBTVEwAyoyNoeLiGACx8IehFx/P67LFC +tmmCaaN9U7XGX77frhzEgphK9w1rvxdd8Va1F8agAdykcztxyG5tNn1HwwGk+xA5 +482nlDKQv6U6thi44Z65AQ0EUcGmrgEIAOGydbByhDehHAFYIRTEkxnd3LGxFR+S +hmyPMCobSCgbYS6SEq9Y1+X9zcvm5dB6lnTglqV3XIznl13RTAIwLwIdLCks4KE/ +smhGHMn4/gxddQOSJg+jdSBsIwhFnfU0y5ZOYtXXpkmaUZaMq2cBkgka17nqTsd4 +DPYZasErFc/Jlqllwlr4uynLJ1I9FZ2LA9Xzx3thIHByNFXdjxKPD0sT910i3h9A +TJ4Q7sJJ/Ir7okOwrGzGVAWQvMaGj85Fq7XJNLCSd3bXaXAskYlUryJijQAWjhvq +R9mcVZz4TLjI5TyGXatYqE8B/euovYsD8HoRDgVAtsQDimkuS8Xx0R8AEQEAAYkB +HwQYAQIACQUCUcGmrgIbDAAKCRD+M0iHeAk4bHGwB/96uN7K1MVO8dKQeq2avhrH +QZCczGXB/0gRhWNj6njBJMdsfOtPypSqLWuCCN107TRJkig+77lQ8JFhRGo+5QNt +76fQL9a/VFbm1gTsAy3uL4hasHTUIrY7Uq1nDX6poHd25wXWdEBbtiwAoCjp/gid +o69WS5lsga0S2e/IySx6Tel1pUO1hYUhUzSZYVFUjM/ncPJih+VMT/3+kB4iY/Sc +eNTx85gJSnucL+mXDuZTvxXui5tt4zGxSp+POHXBDduZliyxzKr5FTPGXw493DiM +3KggSieIDL6x3BWZR2U97w0iDbGWxS5mMJt+6FNCBJmeK2ooFRT+IJ6zeoXM0z6s +=hamx +-----END PGP PUBLIC KEY BLOCK----- diff --git a/contrib/gitian-downloader/win32-download-config b/contrib/gitian-downloader/win32-download-config new file mode 100644 index 0000000..f7b5c84 --- /dev/null +++ b/contrib/gitian-downloader/win32-download-config @@ -0,0 +1,58 @@ +--- +name: bitcoin +urls: +- http://bitcoin.org/bitcoin-latest-win32-gitian.zip +rss: +- url: http://sourceforge.net/api/file/index/project-id/244765/mtime/desc/limit/100/rss + xpath: //item/link/text() + pattern: bitcoin-\d+.\d+.\d+-win32-gitian.zip +signers: + 0A82509767C7D4A5D14DA2301AE1D35043E08E54: + weight: 40 + name: BlueMatt + key: bluematt + BF6273FAEF7CC0BA1F562E50989F6B3048A116B5: + weight: 40 + name: Devrandom + key: devrandom + E463A93F5F3117EEDE6C7316BD02942421F4889F: + weight: 40 + name: Luke-Jr + key: luke-jr + D762373D24904A3E42F33B08B9A408E71DAAC974: + weight: 40 + name: "Pieter Wuille" + key: sipa + 77E72E69DA7EE0A148C06B21B34821D4944DE5F7: + weight: 40 + name: tcatm + key: tcatm + 01CDF4627A3B88AAE4A571C87588242FBE38D3A8: + weight: 40 + name: "Gavin Andresen" + key: gavinandresen + 71A3B16735405025D447E8F274810B012346C9A6: + weight: 40 + name: "Wladimir J. van der Laan" + key: laanwj + AEC1884398647C47413C1C3FB1179EB7347DC10D: + weight: 40 + name: "Warren Togami" + key: wtogami + E084FE305BDF0C476F779792657EB016521670C0: + weight: 40 + name: "Rama McIntosh" + key: face + 1A2511E978239E491A096D0A828AC1F94EF26053: + weight: 40 + name: "Charles Lee" + key: coblee + EFDFCBD38FFF68B49160C7D3A1596566F87BE631: + weight: 40 + name: "Anton Yemelyanov" + key: aspect + 59CAF0E96F23F53747945FD4FE3348877809386C: + weight: 40 + name: "Adrian Gallagher" + key: thrasher +minimum_weight: 120 diff --git a/contrib/gitian-downloader/wtogami-key.pgp b/contrib/gitian-downloader/wtogami-key.pgp new file mode 100644 index 0000000..e0f6c4c --- /dev/null +++ b/contrib/gitian-downloader/wtogami-key.pgp @@ -0,0 +1,131 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.13 (GNU/Linux) + +mQQNBFHOzpUBIADYwJ1vC5npnYCthOtiSna/siS6tdol0OXc82QRgK4Q2YeFCkpN +Fw/T5YK34BLVGWDHPoafG2+r1nXIuMZnJIiGw6QVOL2sP9f7PrMmzck5KJPHD14Y +GRd9BPkhmt3dXzOCjhig7jI6hKEYayfJNUNs9nlZEvl4QWIBMmk+IyqQz3f1HMfl +/GkFDShBYF8Ny7Ktlx7AaXymajm4DCrTkbj5V2ZDqJgyQM549EoPSwXBQYrEjye3 +g2viC8rUFRFWFjdnx7jFEb1uhx71YGuqiLxKihUW9pbSNK2cLweFazHSVmh+B/pz +fxHfUn+ijLSIAnprTmc/rq89un/iiPt0O/mspcCZ6hE5pFIyX+SC+9PrGz+bFSmw +PkMOZzG489G8k4t/uZsit6helkl0emg6JiXLTmS/oTuT7B9Z9/MeEhOXFcxUb0fr +2aZkEmH5d1oxSBis3D5nylmNJXOUSCpJAZ8E5Sr/5FbF9IPR+NSzosVacqCx5Dxj +vJ7HpZKn6pJfmwrghVXQv04NRTcxbHNmwd98cofBtWX8yBO8M2M+jZrU+BVDUbb/ +A1oAyIbUUswBP768Oh11bELhCly774VwBqTojm2yodLGSyysx4zoa6qL7myfor0m +a+K29y8WH9XGmKGMdUOg+q9z+ODky9aToGvEo2eVhKIlJsk0aFAGy/8awy6qRIIj +UqLMq6XoFcYlE7SmnFUDDDPlBK/NkFFqySpFhKNRyt69Ea9kYXOxDnf/EnBwHn8m +PiFQpeZqgnmhyj8Nk1SSQBgUi07NyXdQ/WIYpWmqqqfHRVQgSE9C1920T1zg/E97 +n5yYjI/gQQwq9wikkJmog6Ny7MSiwIU4LYV0pTUdI4//EJMId2FH8YEUfvG5ds+F +H/o/D4CAJ86KjspizfH8jEjhn0Rm/OtrxLz1rwA1gtF//P3TYNWw5qruL4stP3Rx +9Gve8Bm7oCBU73UT2ZJomEsWE3oqXinLRl3YCsjGDg/d3ySD6i0/BBROLIeXkh3M +M1CNCqREDGLA0vxQi1o7Zi7ZA4gWPSzvi/8KtSzY1iAQODxWUmOICRP7KQODWJmt +roTqhKgZ39wlR6eqkO8ZfAvRYsjvkL+EZFbbKbHxVJLhKchd2qHS+/Q3ov4SFzWY +/cE0ChOPDM587Jkps2bynKQAzQ6810FXmJc0ztrPeD3PEbuyY4KNJV8HGViRDJXi +wvs8eqfvTDGDPl4aLYVCKO9VqZ2OJvqhRhh71LQ2xRrX1LGnYLnUGCMuEQYKvMcI +TSssM/VAfeWAPJDklD0lVNJ7d9Z5ugvJHFc01SaaB47Aod2SPWp5DeiY4A8dcy2w +7f4Wx6FcdP1RXqaRZKCapBooN04vsvGllCshABEBAAG0KFdhcnJlbiBUb2dhbWkg +KDIwMTMpIDx3dG9nYW1pQGdtYWlsLmNvbT6JBDgEEwECACIFAlHOzpUCGwMGCwkI +BwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJELEXnrc0fcENY4Ef/23L9iC/39ekJ8Is +1IZdCoDD7/DgVaZqydDcy/ha9uaDFY4MQ0h9RZYo1axVBth/Yxzh1XnvitW8HFKn +DXn5wJI++KWpdLMUsTrc2iWsjAGgicmN5bkQvfTnRwn2pF17EUUEhZ8YyE3qMSVD +rDBECLAswT4Oiq9r9yw3VCFsRaxz5bhk9AAzWjam4H7mAfaEAOUvuX221v+KGSDM +UsGAAe+GjMPL8KnGgEbISlSUF1Ubcw3EChcqjf3BID2gMLkAnGAoxlCZSYievytg +71mcHyIf9yF861QrGcrCh6/objtRdt4IDUVwo9wapunRmYCdZux4ApD0Hit8nAsm +QtxftSK6FWBTOCIRoOQTjwE8qj9GYTIbUFppX66Dzh00td5NKkWz0PVze7YSk2hC +KCVBYyUYHgkQYVlYLZw7dBrXSXv7ph95vc93RDS031cU7tPOrthqnMmhtg1WAwzH +xc2v3az9Gsw1RyxBAOVpkB0AFODiEiVg46xqmxaBPXfQOg/buZA2l4gK4U/pVUZH +72lle2CbBw6FoSx40Y3GYZWB2uEdXBTNLlhX7q2Jvo8WdeTxEv5ACZsjI7K/wrzt +nmvCHefOmVf4tefkXy1MyEvBt2+Ek9bHmHDL1BSk/JdJzJtam2uaP5pGum/PwIUW +KBatmHKZUKwgOIml9btB413C4zSK3GQmC5Y/+TxYybACIdxTDqPSczVZ5Q+jSywX +shdOoLXDRyrYhT2sHjZ1W29B8ebokqwousF77EA94sqfQvDDnmFpvfq9+m0WYtOh +PFF/yxOtlbPJYX7mnC8+dUgobSA4AR5Yrclt+levgivIyNuBwzevHRDMreMZKl2J +uiOT8tkuu66fAwEltIowjjV7TBRfij4QLXl/zfFo8jKU8efL3xluXoRn7g+E5FZ3 +19KTF/DWMcttfeTUYVnv0QTnstb1RGnVj7w8JMy90mKdMQFpl7IzHd2n6LrhEw1V +1AaPF7EcQBOlvsvlZdIFQrFyhKozKoGi3wRrl/bNdebxjIjPzfN9GgbiufFjz2d7 +DMR9GFXfUMVxLncaqBBy1X7MV17ZF7K4uw6DET4fRoecb4N5mJVUxvYq4iZApnNP +npgGdmlcyPD6o3ynx/vkw78m13Gfgw8i2OaUY7xBdOyNVEvkJZBLaC2hw+TKLaZa +v0RExtAO0i0QO4Y1eo78Pl9jOpz0wkJ4KG0270l1Jza4IyaIhYRDWagWOfOp/cXU +cvKKiuJhLOsX1Bapz+O2Aor9+EwWRdPd3BzE2ABdmKHPwrKobNp75wrCpQ5mZifn +DSTJRMPQQJV3wGfB2sP0NE47U8w5CCmVK8gEuqYr6wBl/CCq5tjiRc63VM+to5V4 +tVNTCJWIRgQQEQIABgUCUc7PqwAKCRBr3f6OVKKs8cYAAKCFCLJ5wc+iAVCFRevh +xTcJct0fiQCePHpY37CIeP8s9BH8GqCDftUqh8SIRgQQEQIABgUCUc7YwAAKCRDd +f+mrhdawLOVxAJ9Tjud26LtbM2mWcPj2eT7dhqgZrQCdGyMwMMVzp40lsCK44PrV ++mpFO7KJAhwEEAECAAYFAlHO0BkACgkQw35HI5aSdvXfLw//c2zZxXg4bI2W7gkB +ZQJIOWnmPZfhrXQNeFuetyGoWTm4ZWxW362AdDGiQSGNNkXqeBPOitKOkRyZP/Z3 +h1vwkLkwdFZyWXK00BzYBKfjThWV1BAnArQLewSiLlE7qSnsPEY6FW0PNv711cbL +lXSUP1/lW25Nx7L76GAF6sHreoIdglE8YH5y310JuFnqPa0uaJG+qDo8Mb+WkyLy +Q2A3Atws1tIB9vHsq2FCt9ACyAEA3AqtHR4uMFmIWpUYy77fJAZdzLZTWf0X5XYw +XILNPOl/I0iZrq3LYQAvJfIwjWAC/lm6uTLlvkIJHKyhcIT+RocjMV7bY9ezrC5i +Cag3gaOZ7USMt0h59KdmBaHHNa32n3PSHg9XWljqoWMRjuaRdcA7ofK0BHDJbHWE +cldKXC09laWOXbyNmJsfug/23vNE7fS/cAKSIgEWszEwHJCahB2i/HqOQF0DUGpq +3s5oIXs2xIuN0yT6yIIiQnTU/FkWDDu4D1OZNrDW6QG3cde0PRak/0fr4Kv4iB3E +CAzlsRBlWKNu/eE4QBx6cbvLqjriijhGAF+8Y1zvRKNKPr96hSsETfVytuKDTp6F +u7PAarrSATGXI92Hy3ThAZla0VOYUyeWPktqUMDNq90tIBZbwKpOMMqvJmZfgdOU +4ldDq1f5+2WhAt1aTL1GJVCuYcCJAhwEEAECAAYFAlHO3MQACgkQnSOpPExjO3Gi +jxAAsD+luooqqoz3A28ZxwfCDV+ovazQ4Bw6hVU0zKKZIz/2H4jwmLtLSHtucCRM +xRksZmnqf1p2nn+BKBXDInx9vI9HziMu7fWkzhuovAIf9+X/l6EYV1kQx0bIM1qU +BxXWPgGdrgSZZHl9Qff/BOBnrI8NJmVBDzOh3BSs0BrSR7aFbkSNbjk/JcP0JEyk +j6wDKQsop/Ca5AboLL0uQPgTvhxCu4VROKjhu7o3s7G3xlxTpimwYklDQuYFaGKj +ZNIGFq2orfIMBnj7ZEQVXzhWltlHcgPVP5TDfgd4pVUbyUB6ras7odJWWIHnUFmj +1l5bGidIwRXGFusE4iR8pR528LG2KxNDNQYipsKRY9m+wH+N7gbSgK8DxmocvieV +vcILFS5VrPLbEO2oC13NMljmvua3ovDB0CEh9rybaH+/oA+VDS2L3pkgATTju+Vx +6+mVdlvnrA4mJ5BoLHzrleKybS4ZkbtVBh1KOYmo95NgVifRvpVPB6hKzwqcjYFV +fVYBxTryTBRyd9MLsqpPKnGLBENTFvKDxRCK3iioNyVhXdS0z/UyF1C2hwNTpnjY +pGCu+Es3SILJg2TvQcwLM0OoYBA1bcONm2XbkTrdCpTOtQcSewQSkijREunx14iu +pvNSWeNmbjQU7gNYhvwcBgh90tWgNCfqTtSa5xSe46tmv0SJAhwEEAECAAYFAlHQ +1hgACgkQZwn/QC8Dr2hT/g/+OFUYPXfWo0+ILdxyTGP/v2mSw/X3dBCEYUqefWxD +umcwnksey+thEGFBlxbwpyOfAoTzZLUupaG6BacVgRUvv8bTne4v2H1d22aBXyjC +HMtQPhupn/giamu8q8hCPFrDp6inIAeFuz1GmQaH6xWO5eYBuYXQtxlvZLWBsuMT +74en4e3vjczxGmJu/nvM9ugcYsexA/zcN6SRGr7t2pV4ZElPzPBRyAzhYqhP1YlB +Rydz60OjgcWYEoJKWhJOfmFJ3ZoNGAz4TGoBkDIq4olCF0/cxqrtHN+ZnEOLwiZ7 +4ZX90avcjEFtM+Wb5dBHNpni4ISoHcVI1X0ye6tuAOOt7RywbET/0oIW5iSNMgJ0 +X4XYgOIQ2+a8yjGBjo9I57k0vp1mL6Ji/eaa0dlppcCGnzvSHss+O0qO212pg5Yk +GGfjX1y1ZeSP3ca9C2XyOGIVw2d2Iu7OyqAv/N81xt6ZgG3qixQC0nmgOmn7Kh2B +20W12KpLxKS8RQdHawGau3MBGKeqbfK6/eAzm22yD4/yJAoW4hKgm84z3FbKUN8w +ulYMK9hS2c4egpoDAOJ/QZLLXFWiyi7/sHZz69G2AweWCjOJh28Otg0cUHoLo7jw +oO/L0rCsOQMbUuIumYXBPHNnDwv1xfv2lT8tVzf6GksFJBAw0DybxOMTaOg45Lhz +jGS5BA0EUc7OlQEgAN6t+BV705uoCsdHtQBq/HKGGD5tBiOzy7Wd4nF/c6EWzET4 +QUnmw6bDnqjxrk9MWniPDf1O9MvuB4qIY6g9kEjZ+VSQpWUZpZ5bMXCNHrfh9J2Q +6oLWqDmpeZv2OI0O9wxT62QaFei2qBtimSnBudLSCnvmU3S0h1PflmJsbj+tVcko +w2yOh2bjH1jkVAODHvEbxqyD6fiZhbfUVbPC49SBmXv8Gv0UywNSkP+iqJdwZAb0 +XtjRx4WjZCkTwJAnbM4CJ63+5Hd83BtWZAZbGAh76XY/cSkDirXtXC+2LNUmP5W2 +QY+ur5Bvz8LHaqJMXLAtePdkv5kpd+jXBrZieXUtqovxZaQTinl7C3L2TZd/ivxD +F3Rko9BFDuXXcdZrxBY5b3146IvSPp1y0WmHRxhAPb+RuiHQMt8K92nOhPyvtWXB +mWz0GnW9L6+CW4LKSPRSnE057hyxYNP/DcDd+fWFH+MmhU9noqHfJXSaLVzdI5PI +L8N44AndPIojnlxrxRs7Ik/nW6cTV9H3agg+24yyTdFkACbfIS6wWXOHeHuBzmO6 +VI7pXOZJ9vZT7zI7M/hVci0R3putsGqgRfByRWWQ2DNeyrwUHexZNR/NYz1uhvA6 +dBfKcuAwqxbdSrW/BxJ+iJWdkgYGCV67VLlO6S9sO33HgOanpPr5R9V1KsFVh4dN +j6BjZ4ALE5FPNW+iONnuXvtZbN2cBlBzMDeFC9oZoYCs1Pkmk8xUY2sAXPUt1R0G +D/miIb7ig1N52j9P6vv6fPs1ghmc/hGkhaXyjS54B5T33V6M9g+yba9mIgi8ZxZa +G+4rlFFKA4HS7wYYRJoqMvnc/qBYvoWLaPu3Xq6AXrJyuAaN+e3L8++cWbYHBXF9 +qt+Q2RFL0FNiYUQuwkiaerysnm1a0H7ZtJ4zjl4ZgA1Ej7QcylTIbgFW3L7FnyMH +/5weLLN2wdjAtzjhRPYJLbV6V/gFbbpCpr+caDUaxSNizQuhhzVI5UrJegaHCCrx +DCiwWRFYzN5pqhtgzcaImK76DmPIk+Yrsum5KJZQeGfzKxvF0YnwxU0bxFzcDZJD +X2oCJn828Aw2j0nIlVlrrao0JMkvTBeZehO/11U68M2vKGEqrsQOb/BTXyLCeZwn +UGow1WvYfRxEZTrhhiYw94EH06gbqmKG1xsuV4LDI5z63/6ACcQW3orMbMymJCky +4HiNVZ7SNeGoYe380CJCwv6GN1opKTAWp84cr2KzhAzONGqNWNpUhznAXlI+GzCc +D2H330L1atMqZHjgpEfrkowvJ7WBM5KFKDfylaTKhYvfZcTOZs5OmRZSW3U54wRD +RMP0d2+k3vRililNhHIErHbjhYFc6zubVbBhvUMAEQEAAYkEHwQYAQIACQUCUc7O +lQIbDAAKCRCxF563NH3BDSX2IACugAdZqX+o/+pTkSrj+NEAcP0ZMci8w5nm/yOP +VlGyY6PXGuQKcBtvz3LWtIDdddMc/bD/zmZPwSzTx1MMOWc+gjR0azXe2RrdMHYk +8pb4X4Op2Nkasoc/8hNsRKaU24WUAQMqrRREIVBEOuHGl1A52Lj+aFB04rRHrkMl +AqjB5bwArPorIBdM417EEl4hjEZ9BpQxbUgBhTgGTZuc1u9PsKz1YvQ79YJIRmSH +n72Zaf35zY55eOQeoVBzGmFPq+/UFqtRNWA7jmRhHvMz/yR33B/RSxyTJuPb79zi +2mIZOrViG3X/UNL4qtOc1cKXQBi+FjHAMlGrCc+D5lnyOhEvqoEuvQic7V6C8Pvk +9q+jngn2Gs4pdJO8FOnwaC5xp/ZNE0v7x/KtAHyBA6iKcaepgoRQPSt1ONiHyfh1 +iGgJn+Y6IHx4YDYKEY0UIzHhCfWUl8XZWcf4wLGEbGztkRbkCFqrsja5IeaO7umB +i6C4f95uSGjV7SiIMJOE8xo/m2g4VCnnmk7U996JwtBMKREMMqa3ABK4trfBL3Kq +P6I6ZTlA/C5svkVUVwWOMZau9kLDsxv8keGrFteZtfYa1KPAROFwNuBU82UW0KtX +QQbZoBKt1o3LhqEu+hXU3iKocYWSbBThH8u6vPNgSnW2Qcv3gcUU3jGmYeHrGiUO +SuEWxwlKUxCxBNfmz1FGswlwve1LsS3RTz/XB/L6Ubhq5L7FevrXz8152kuMqnpy +m93sXkL1eJVo07hH+otcRnMzy4vUar9z/N12t3hfTffx29PBKUCc2PKPVpLfJX2i +hieHk23fhLnptjc3lm9S+bHO3rqEWHqgNgNp9bpuwiLRsIy6qTtmC8jxXkGXvQrS ++2Hv6+jRfDcqEAK3vqi1XL7Td81KRjnheBtsKpjS2PFatK3uTo6v1oRWJCdRCxg1 +HT6a9KvZ+DNKcxlQISKAOLX72qpziaDl4CpBdQy4Zg2pr9oYkLdlfkaDK/OH4J3M +wJiVf/uNPPd+yy6xZXK0SPZHf+mf5Yt+Sim93hIbdS9AMdvHKB5n3DR27H+/okPj +w3J9z85hxgP5KspizQR6t77AWddPRy/l3BBZeb+HiaeKGBJeSNWXpkPXHkdjLW8U +QStzFR8r15FWJTmamIknjJ3XNbytMCpu8cj2ZVZdyjPcHEBL3WbNYYtauSuYmyUO +yXBaecM/KoTdvHiERU/mMuf7f1ftftCHehZoNaP+BeIbIud9IHIdrSQBCW+RC1Y1 +8opDLMtnIOX3OnyCN38ELYcuNLMJxBqnQgi7MVDVcT1+BN/+lFQtG44+rPUkK+T1 +Jk1/tIJqcyc1BfY6uFHFXWWnqQnjl0XpZo+/bMDxTVy8yND2 +=icdI +-----END PGP PUBLIC KEY BLOCK----- diff --git a/contrib/macdeploy/LICENSE b/contrib/macdeploy/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/contrib/macdeploy/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/contrib/macdeploy/background.png b/contrib/macdeploy/background.png new file mode 100644 index 0000000..045fc24 Binary files /dev/null and b/contrib/macdeploy/background.png differ diff --git a/contrib/macdeploy/background.psd b/contrib/macdeploy/background.psd new file mode 100644 index 0000000..5889676 Binary files /dev/null and b/contrib/macdeploy/background.psd differ diff --git a/contrib/macdeploy/fancy.plist b/contrib/macdeploy/fancy.plist new file mode 100644 index 0000000..c0058d3 --- /dev/null +++ b/contrib/macdeploy/fancy.plist @@ -0,0 +1,32 @@ + + + + + window_bounds + + 300 + 300 + 800 + 620 + + background_picture + background.png + icon_size + 96 + applications_symlink + + items_position + + Applications + + 370 + 156 + + CryptoLottoToken-Qt.app + + 128 + 156 + + + + diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus new file mode 100644 index 0000000..2220a0a --- /dev/null +++ b/contrib/macdeploy/macdeployqtplus @@ -0,0 +1,808 @@ +#!/usr/bin/env python + +# +# Copyright (C) 2011 Patrick "p2k" Schneider +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import subprocess, sys, re, os, shutil, stat, os.path +from string import Template +from time import sleep +from argparse import ArgumentParser + +# This is ported from the original macdeployqt with modifications + +class FrameworkInfo(object): + def __init__(self): + self.frameworkDirectory = "" + self.frameworkName = "" + self.frameworkPath = "" + self.binaryDirectory = "" + self.binaryName = "" + self.binaryPath = "" + self.version = "" + self.installName = "" + self.deployedInstallName = "" + self.sourceFilePath = "" + self.destinationDirectory = "" + self.sourceResourcesDirectory = "" + self.destinationResourcesDirectory = "" + + def __eq__(self, other): + if self.__class__ == other.__class__: + return self.__dict__ == other.__dict__ + else: + return False + + def __str__(self): + return """ Framework name: %s + Framework directory: %s + Framework path: %s + Binary name: %s + Binary directory: %s + Binary path: %s + Version: %s + Install name: %s + Deployed install name: %s + Source file Path: %s + Deployed Directory (relative to bundle): %s +""" % (self.frameworkName, + self.frameworkDirectory, + self.frameworkPath, + self.binaryName, + self.binaryDirectory, + self.binaryPath, + self.version, + self.installName, + self.deployedInstallName, + self.sourceFilePath, + self.destinationDirectory) + + def isDylib(self): + return self.frameworkName.endswith(".dylib") + + def isQtFramework(self): + if self.isDylib(): + return self.frameworkName.startswith("libQt") + else: + return self.frameworkName.startswith("Qt") + + reOLine = re.compile(r'^(.+) \(compatibility version [0-9.]+, current version [0-9.]+\)$') + bundleFrameworkDirectory = "Contents/Frameworks" + bundleBinaryDirectory = "Contents/MacOS" + + @classmethod + def fromOtoolLibraryLine(cls, line): + # Note: line must be trimmed + if line == "": + return None + + # Don't deploy system libraries (exception for libQtuitools and libQtlucene). + if line.startswith("/System/Library/") or line.startswith("@executable_path") or (line.startswith("/usr/lib/") and "libQt" not in line): + return None + + m = cls.reOLine.match(line) + if m is None: + raise RuntimeError("otool line could not be parsed: " + line) + + path = m.group(1) + + info = cls() + info.sourceFilePath = path + info.installName = path + + if path.endswith(".dylib"): + dirname, filename = os.path.split(path) + info.frameworkName = filename + info.frameworkDirectory = dirname + info.frameworkPath = path + + info.binaryDirectory = dirname + info.binaryName = filename + info.binaryPath = path + info.version = "-" + + info.installName = path + info.deployedInstallName = "@executable_path/../Frameworks/" + info.binaryName + info.sourceFilePath = path + info.destinationDirectory = cls.bundleFrameworkDirectory + else: + parts = path.split("/") + i = 0 + # Search for the .framework directory + for part in parts: + if part.endswith(".framework"): + break + i += 1 + if i == len(parts): + raise RuntimeError("Could not find .framework or .dylib in otool line: " + line) + + info.frameworkName = parts[i] + info.frameworkDirectory = "/".join(parts[:i]) + info.frameworkPath = os.path.join(info.frameworkDirectory, info.frameworkName) + + info.binaryName = parts[i+3] + info.binaryDirectory = "/".join(parts[i+1:i+3]) + info.binaryPath = os.path.join(info.binaryDirectory, info.binaryName) + info.version = parts[i+2] + + info.deployedInstallName = "@executable_path/../Frameworks/" + os.path.join(info.frameworkName, info.binaryPath) + info.destinationDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, info.binaryDirectory) + + info.sourceResourcesDirectory = os.path.join(info.frameworkPath, "Resources") + info.destinationResourcesDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Resources") + + return info + +class ApplicationBundleInfo(object): + def __init__(self, path): + self.path = path + appName = os.path.splitext(os.path.basename(path))[0] + 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) + self.resourcesPath = os.path.join(path, "Contents", "Resources") + self.pluginPath = os.path.join(path, "Contents", "PlugIns") + +class DeploymentInfo(object): + def __init__(self): + self.qtPath = None + self.pluginPath = None + self.deployedFrameworks = [] + + def detectQtPath(self, frameworkDirectory): + parentDir = os.path.dirname(frameworkDirectory) + if os.path.exists(os.path.join(parentDir, "translations")): + # Classic layout, e.g. "/usr/local/Trolltech/Qt-4.x.x" + self.qtPath = parentDir + elif os.path.exists(os.path.join(parentDir, "share", "qt4", "translations")): + # MacPorts layout, e.g. "/opt/local/share/qt4" + self.qtPath = os.path.join(parentDir, "share", "qt4") + elif os.path.exists(os.path.join(os.path.dirname(parentDir), "share", "qt4", "translations")): + # Newer Macports layout + self.qtPath = os.path.join(os.path.dirname(parentDir), "share", "qt4") + else: + self.qtPath = os.getenv("QTDIR", None) + + if self.qtPath is not None: + pluginPath = os.path.join(self.qtPath, "plugins") + if os.path.exists(pluginPath): + self.pluginPath = pluginPath + + def usesFramework(self, name): + nameDot = "%s." % name + libNameDot = "lib%s." % name + for framework in self.deployedFrameworks: + if framework.endswith(".framework"): + if framework.startswith(nameDot): + return True + elif framework.endswith(".dylib"): + if framework.startswith(libNameDot): + return True + return False + +def getFrameworks(binaryPath, verbose): + if verbose >= 3: + print "Inspecting with otool: " + binaryPath + otool = subprocess.Popen(["otool", "-L", binaryPath], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + o_stdout, o_stderr = otool.communicate() + if otool.returncode != 0: + if verbose >= 1: + sys.stderr.write(o_stderr) + sys.stderr.flush() + raise RuntimeError("otool failed with return code %d" % otool.returncode) + + otoolLines = o_stdout.split("\n") + otoolLines.pop(0) # First line is the inspected binary + if ".framework" in binaryPath or binaryPath.endswith(".dylib"): + otoolLines.pop(0) # Frameworks and dylibs list themselves as a dependency. + + libraries = [] + for line in otoolLines: + info = FrameworkInfo.fromOtoolLibraryLine(line.strip()) + if info is not None: + if verbose >= 3: + print "Found framework:" + print info + libraries.append(info) + + return libraries + +def runInstallNameTool(action, *args): + subprocess.check_call(["install_name_tool", "-"+action] + list(args)) + +def changeInstallName(oldName, newName, binaryPath, verbose): + if verbose >= 3: + print "Using install_name_tool:" + print " in", binaryPath + print " change reference", oldName + print " to", newName + runInstallNameTool("change", oldName, newName, binaryPath) + +def changeIdentification(id, binaryPath, verbose): + if verbose >= 3: + print "Using install_name_tool:" + print " change identification in", binaryPath + print " to", id + runInstallNameTool("id", id, binaryPath) + +def runStrip(binaryPath, verbose): + if verbose >= 3: + print "Using strip:" + print " stripped", binaryPath + subprocess.check_call(["strip", "-x", binaryPath]) + +def copyFramework(framework, path, verbose): + if framework.sourceFilePath.startswith("Qt"): + #standard place for Nokia Qt installer's frameworks + fromPath = "/Library/Frameworks/" + framework.sourceFilePath + else: + fromPath = framework.sourceFilePath + toDir = os.path.join(path, framework.destinationDirectory) + toPath = os.path.join(toDir, framework.binaryName) + + if not os.path.exists(fromPath): + raise RuntimeError("No file at " + fromPath) + + if os.path.exists(toPath): + return None # Already there + + if not os.path.exists(toDir): + os.makedirs(toDir) + + shutil.copy2(fromPath, toPath) + if verbose >= 3: + print "Copied:", fromPath + print " to:", toPath + + permissions = os.stat(toPath) + if not permissions.st_mode & stat.S_IWRITE: + os.chmod(toPath, permissions.st_mode | stat.S_IWRITE) + + if not framework.isDylib(): # Copy resources for real frameworks + fromResourcesDir = framework.sourceResourcesDirectory + if os.path.exists(fromResourcesDir): + toResourcesDir = os.path.join(path, framework.destinationResourcesDirectory) + shutil.copytree(fromResourcesDir, toResourcesDir) + if verbose >= 3: + print "Copied resources:", fromResourcesDir + print " to:", toResourcesDir + elif framework.frameworkName.startswith("libQtGui"): # Copy qt_menu.nib (applies to non-framework layout) + qtMenuNibSourcePath = os.path.join(framework.frameworkDirectory, "Resources", "qt_menu.nib") + qtMenuNibDestinationPath = os.path.join(path, "Contents", "Resources", "qt_menu.nib") + if os.path.exists(qtMenuNibSourcePath) and not os.path.exists(qtMenuNibDestinationPath): + shutil.copytree(qtMenuNibSourcePath, qtMenuNibDestinationPath) + if verbose >= 3: + print "Copied for libQtGui:", qtMenuNibSourcePath + print " to:", qtMenuNibDestinationPath + + return toPath + +def deployFrameworks(frameworks, bundlePath, binaryPath, strip, verbose, deploymentInfo=None): + if deploymentInfo is None: + deploymentInfo = DeploymentInfo() + + while len(frameworks) > 0: + framework = frameworks.pop(0) + deploymentInfo.deployedFrameworks.append(framework.frameworkName) + + if verbose >= 2: + print "Processing", framework.frameworkName, "..." + + # Get the Qt path from one of the Qt frameworks + if deploymentInfo.qtPath is None and framework.isQtFramework(): + deploymentInfo.detectQtPath(framework.frameworkDirectory) + + if framework.installName.startswith("@executable_path"): + if verbose >= 2: + print framework.frameworkName, "already deployed, skipping." + continue + + # install_name_tool the new id into the binary + changeInstallName(framework.installName, framework.deployedInstallName, binaryPath, verbose) + + # Copy farmework to app bundle. + deployedBinaryPath = copyFramework(framework, bundlePath, verbose) + # Skip the rest if already was deployed. + if deployedBinaryPath is None: + continue + + if strip: + runStrip(deployedBinaryPath, verbose) + + # install_name_tool it a new id. + changeIdentification(framework.deployedInstallName, deployedBinaryPath, verbose) + # Check for framework dependencies + dependencies = getFrameworks(deployedBinaryPath, verbose) + + for dependency in dependencies: + changeInstallName(dependency.installName, dependency.deployedInstallName, deployedBinaryPath, verbose) + + # Deploy framework if necessary. + if dependency.frameworkName not in deploymentInfo.deployedFrameworks and dependency not in frameworks: + frameworks.append(dependency) + + return deploymentInfo + +def deployFrameworksForAppBundle(applicationBundle, strip, verbose): + frameworks = getFrameworks(applicationBundle.binaryPath, verbose) + if len(frameworks) == 0 and verbose >= 1: + print "Warning: Could not find any external frameworks to deploy in %s." % (applicationBundle.path) + return DeploymentInfo() + else: + return deployFrameworks(frameworks, applicationBundle.path, applicationBundle.binaryPath, strip, verbose) + +def deployPlugins(appBundleInfo, deploymentInfo, strip, verbose): + # Lookup available plugins, exclude unneeded + plugins = [] + for dirpath, dirnames, filenames in os.walk(deploymentInfo.pluginPath): + pluginDirectory = os.path.relpath(dirpath, deploymentInfo.pluginPath) + if pluginDirectory == "designer": + # Skip designer plugins + continue + elif pluginDirectory == "phonon" or pluginDirectory == "phonon_backend": + # Deploy the phonon plugins only if phonon is in use + if not deploymentInfo.usesFramework("phonon"): + continue + elif pluginDirectory == "sqldrivers": + # Deploy the sql plugins only if QtSql is in use + if not deploymentInfo.usesFramework("QtSql"): + continue + elif pluginDirectory == "script": + # Deploy the script plugins only if QtScript is in use + if not deploymentInfo.usesFramework("QtScript"): + continue + elif pluginDirectory == "qmltooling": + # Deploy the qml plugins only if QtDeclarative is in use + if not deploymentInfo.usesFramework("QtDeclarative"): + continue + elif pluginDirectory == "bearer": + # Deploy the bearer plugins only if QtNetwork is in use + if not deploymentInfo.usesFramework("QtNetwork"): + continue + + for pluginName in filenames: + pluginPath = os.path.join(pluginDirectory, pluginName) + if pluginName.endswith("_debug.dylib"): + # Skip debug plugins + continue + elif pluginPath == "imageformats/libqsvg.dylib" or pluginPath == "iconengines/libqsvgicon.dylib": + # Deploy the svg plugins only if QtSvg is in use + if not deploymentInfo.usesFramework("QtSvg"): + continue + elif pluginPath == "accessible/libqtaccessiblecompatwidgets.dylib": + # Deploy accessibility for Qt3Support only if the Qt3Support is in use + if not deploymentInfo.usesFramework("Qt3Support"): + continue + elif pluginPath == "graphicssystems/libqglgraphicssystem.dylib": + # Deploy the opengl graphicssystem plugin only if QtOpenGL is in use + if not deploymentInfo.usesFramework("QtOpenGL"): + continue + + plugins.append((pluginDirectory, pluginName)) + + for pluginDirectory, pluginName in plugins: + if verbose >= 2: + print "Processing plugin", os.path.join(pluginDirectory, pluginName), "..." + + sourcePath = os.path.join(deploymentInfo.pluginPath, pluginDirectory, pluginName) + destinationDirectory = os.path.join(appBundleInfo.pluginPath, pluginDirectory) + if not os.path.exists(destinationDirectory): + os.makedirs(destinationDirectory) + + destinationPath = os.path.join(destinationDirectory, pluginName) + shutil.copy2(sourcePath, destinationPath) + if verbose >= 3: + print "Copied:", sourcePath + print " to:", destinationPath + + if strip: + runStrip(destinationPath, verbose) + + dependencies = getFrameworks(destinationPath, verbose) + + for dependency in dependencies: + changeInstallName(dependency.installName, dependency.deployedInstallName, destinationPath, verbose) + + # Deploy framework if necessary. + if dependency.frameworkName not in deploymentInfo.deployedFrameworks: + deployFrameworks([dependency], appBundleInfo.path, destinationPath, strip, verbose, deploymentInfo) + +qt_conf="""[Paths] +translations=Resources +plugins=PlugIns +""" + +ap = ArgumentParser(description="""Improved version of macdeployqt. + +Outputs a ready-to-deploy app in a folder "dist" and optionally wraps it in a .dmg file. +Note, that the "dist" folder will be deleted before deploying on each run. + +Optionally, Qt translation files (.qm) and additional resources can be added to the bundle. + +Also optionally signs the .app bundle; set the CODESIGNARGS environment variable to pass arguments +to the codesign tool. +E.g. CODESIGNARGS='--sign "Developer ID Application: ..." --keychain /encrypted/foo.keychain'""") + +ap.add_argument("app_bundle", nargs=1, metavar="app-bundle", help="application bundle to be deployed") +ap.add_argument("-verbose", type=int, nargs=1, default=[1], metavar="<0-3>", help="0 = no output, 1 = error/warning (default), 2 = normal, 3 = debug") +ap.add_argument("-no-plugins", dest="plugins", action="store_false", default=True, help="skip plugin deployment") +ap.add_argument("-no-strip", dest="strip", action="store_false", default=True, help="don't run 'strip' on the binaries") +ap.add_argument("-sign", dest="sign", action="store_true", default=False, help="sign .app bundle with codesign tool") +ap.add_argument("-dmg", nargs="?", const="", metavar="basename", help="create a .dmg disk image; if basename is not specified, a camel-cased version of the app name is used") +ap.add_argument("-fancy", nargs=1, metavar="plist", default=[], help="make a fancy looking disk image using the given plist file with instructions; requires -dmg to work") +ap.add_argument("-add-qt-tr", nargs=1, metavar="languages", default=[], help="add Qt translation files to the bundle's ressources; the language list must be separated with commas, not with whitespace") +ap.add_argument("-add-resources", nargs="+", metavar="path", default=[], help="list of additional files or folders to be copied into the bundle's resources; must be the last argument") + +config = ap.parse_args() + +verbose = config.verbose[0] + +# ------------------------------------------------ + +app_bundle = config.app_bundle[0] + +if not os.path.exists(app_bundle): + if verbose >= 1: + sys.stderr.write("Error: Could not find app bundle \"%s\"\n" % (app_bundle)) + sys.exit(1) + +app_bundle_name = os.path.splitext(os.path.basename(app_bundle))[0] + +# ------------------------------------------------ + +for p in config.add_resources: + if verbose >= 3: + print "Checking for \"%s\"..." % p + if not os.path.exists(p): + if verbose >= 1: + sys.stderr.write("Error: Could not find additional resource file \"%s\"\n" % (p)) + sys.exit(1) + +# ------------------------------------------------ + +if len(config.fancy) == 1: + if verbose >= 3: + print "Fancy: Importing plistlib..." + try: + import plistlib + except ImportError: + if verbose >= 1: + sys.stderr.write("Error: Could not import plistlib which is required for fancy disk images.\n") + sys.exit(1) + + if verbose >= 3: + print "Fancy: Importing appscript..." + try: + import appscript + except ImportError: + if verbose >= 1: + sys.stderr.write("Error: Could not import appscript which is required for fancy disk images.\n") + sys.stderr.write("Please install it e.g. with \"sudo easy_install appscript\".\n") + sys.exit(1) + + p = config.fancy[0] + if verbose >= 3: + print "Fancy: Loading \"%s\"..." % p + if not os.path.exists(p): + if verbose >= 1: + sys.stderr.write("Error: Could not find fancy disk image plist at \"%s\"\n" % (p)) + sys.exit(1) + + try: + fancy = plistlib.readPlist(p) + except: + if verbose >= 1: + sys.stderr.write("Error: Could not parse fancy disk image plist at \"%s\"\n" % (p)) + sys.exit(1) + + try: + assert not fancy.has_key("window_bounds") or (isinstance(fancy["window_bounds"], list) and len(fancy["window_bounds"]) == 4) + assert not fancy.has_key("background_picture") or isinstance(fancy["background_picture"], str) + assert not fancy.has_key("icon_size") or isinstance(fancy["icon_size"], int) + assert not fancy.has_key("applications_symlink") or isinstance(fancy["applications_symlink"], bool) + if fancy.has_key("items_position"): + assert isinstance(fancy["items_position"], dict) + for key, value in fancy["items_position"].iteritems(): + assert isinstance(value, list) and len(value) == 2 and isinstance(value[0], int) and isinstance(value[1], int) + except: + if verbose >= 1: + sys.stderr.write("Error: Bad format of fancy disk image plist at \"%s\"\n" % (p)) + sys.exit(1) + + if fancy.has_key("background_picture"): + bp = fancy["background_picture"] + if verbose >= 3: + print "Fancy: Resolving background picture \"%s\"..." % bp + if not os.path.exists(bp): + bp = os.path.join(os.path.dirname(p), bp) + if not os.path.exists(bp): + if verbose >= 1: + sys.stderr.write("Error: Could not find background picture at \"%s\" or \"%s\"\n" % (fancy["background_picture"], bp)) + sys.exit(1) + else: + fancy["background_picture"] = bp +else: + fancy = None + +# ------------------------------------------------ + +if os.path.exists("dist"): + if verbose >= 2: + print "+ Removing old dist folder +" + + shutil.rmtree("dist") + +# ------------------------------------------------ + +target = os.path.join("dist", app_bundle) + +if verbose >= 2: + print "+ Copying source bundle +" +if verbose >= 3: + print app_bundle, "->", target + +os.mkdir("dist") +shutil.copytree(app_bundle, target) + +applicationBundle = ApplicationBundleInfo(target) + +# ------------------------------------------------ + +if verbose >= 2: + print "+ Deploying frameworks +" + +try: + deploymentInfo = deployFrameworksForAppBundle(applicationBundle, config.strip, verbose) + if deploymentInfo.qtPath is None: + deploymentInfo.qtPath = os.getenv("QTDIR", None) + if deploymentInfo.qtPath is None: + if verbose >= 1: + sys.stderr.write("Warning: Could not detect Qt's path, skipping plugin deployment!\n") + config.plugins = False +except RuntimeError as e: + if verbose >= 1: + sys.stderr.write("Error: %s\n" % str(e)) + sys.exit(ret) + +# ------------------------------------------------ + +if config.plugins: + if verbose >= 2: + print "+ Deploying plugins +" + + try: + deployPlugins(applicationBundle, deploymentInfo, config.strip, verbose) + except RuntimeError as e: + if verbose >= 1: + sys.stderr.write("Error: %s\n" % str(e)) + sys.exit(ret) + +# ------------------------------------------------ + +if len(config.add_qt_tr) == 0: + add_qt_tr = [] +else: + qt_tr_dir = os.path.join(deploymentInfo.qtPath, "translations") + add_qt_tr = ["qt_%s.qm" % lng for lng in config.add_qt_tr[0].split(",")] + for lng_file in add_qt_tr: + p = os.path.join(qt_tr_dir, lng_file) + if verbose >= 3: + print "Checking for \"%s\"..." % p + if not os.path.exists(p): + if verbose >= 1: + sys.stderr.write("Error: Could not find Qt translation file \"%s\"\n" % (lng_file)) + sys.exit(1) + +# ------------------------------------------------ + +if verbose >= 2: + print "+ Installing qt.conf +" + +f = open(os.path.join(applicationBundle.resourcesPath, "qt.conf"), "wb") +f.write(qt_conf) +f.close() + +# ------------------------------------------------ + +if len(add_qt_tr) > 0 and verbose >= 2: + print "+ Adding Qt translations +" + +for lng_file in add_qt_tr: + if verbose >= 3: + print os.path.join(qt_tr_dir, lng_file), "->", os.path.join(applicationBundle.resourcesPath, lng_file) + shutil.copy2(os.path.join(qt_tr_dir, lng_file), os.path.join(applicationBundle.resourcesPath, lng_file)) + +# ------------------------------------------------ + +if len(config.add_resources) > 0 and verbose >= 2: + print "+ Adding additional resources +" + +for p in config.add_resources: + t = os.path.join(applicationBundle.resourcesPath, os.path.basename(p)) + if verbose >= 3: + print p, "->", t + if os.path.isdir(p): + shutil.copytree(p, t) + else: + shutil.copy2(p, t) + +# ------------------------------------------------ + +if config.sign and 'CODESIGNARGS' not in os.environ: + print "You must set the CODESIGNARGS environment variable. Skipping signing." +elif config.sign: + if verbose >= 1: + print "Code-signing app bundle %s"%(target,) + subprocess.check_call("codesign --force %s %s"%(os.environ['CODESIGNARGS'], target), shell=True) + +# ------------------------------------------------ + +if config.dmg is not None: + def runHDIUtil(verb, image_basename, **kwargs): + hdiutil_args = ["hdiutil", verb, image_basename + ".dmg"] + if kwargs.has_key("capture_stdout"): + del kwargs["capture_stdout"] + run = subprocess.check_output + else: + if verbose < 2: + hdiutil_args.append("-quiet") + elif verbose >= 3: + hdiutil_args.append("-verbose") + run = subprocess.check_call + + for key, value in kwargs.iteritems(): + hdiutil_args.append("-" + key) + if not value is True: + hdiutil_args.append(str(value)) + + return run(hdiutil_args) + + if verbose >= 2: + if fancy is None: + print "+ Creating .dmg disk image +" + else: + print "+ Preparing .dmg disk image +" + + if config.dmg != "": + dmg_name = config.dmg + else: + spl = app_bundle_name.split(" ") + dmg_name = spl[0] + "".join(p.capitalize() for p in spl[1:]) + + if fancy is None: + try: + runHDIUtil("create", dmg_name, srcfolder="dist", format="UDBZ", volname=app_bundle_name, ov=True) + except subprocess.CalledProcessError as e: + sys.exit(e.returncode) + else: + if verbose >= 3: + print "Determining size of \"dist\"..." + size = 0 + for path, dirs, files in os.walk("dist"): + for file in files: + size += os.path.getsize(os.path.join(path, file)) + size += int(size * 0.1) + + 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) + except subprocess.CalledProcessError as e: + sys.exit(e.returncode) + + if verbose >= 3: + print "Attaching temp image..." + try: + output = runHDIUtil("attach", dmg_name + ".temp", readwrite=True, noverify=True, noautoopen=True, capture_stdout=True) + except subprocess.CalledProcessError as e: + sys.exit(e.returncode) + + m = re.search("/Volumes/(.+$)", output) + disk_root = m.group(0) + disk_name = m.group(1) + + if verbose >= 2: + print "+ Applying fancy settings +" + + if fancy.has_key("background_picture"): + bg_path = os.path.join(disk_root, os.path.basename(fancy["background_picture"])) + if verbose >= 3: + print fancy["background_picture"], "->", bg_path + shutil.copy2(fancy["background_picture"], bg_path) + else: + bg_path = None + + if fancy.get("applications_symlink", False): + os.symlink("/Applications", os.path.join(disk_root, "Applications")) + + # The Python appscript package broke with OSX 10.8 and isn't being fixed. + # So we now build up an AppleScript string and use the osascript command + # to make the .dmg file pretty: + appscript = Template( """ + on run argv + tell application "Finder" + tell disk "$disk" + open + set current view of container window to icon view + set toolbar visible of container window to false + set statusbar visible of container window to false + set the bounds of container window to {$window_bounds} + set theViewOptions to the icon view options of container window + set arrangement of theViewOptions to not arranged + set icon size of theViewOptions to $icon_size + $background_commands + $items_positions + close -- close/reopen works around a bug... + open + update without registering applications + delay 5 + eject + end tell + end tell + end run + """) + + itemscript = Template('set position of item "${item}" of container window to {${position}}') + items_positions = [] + if fancy.has_key("items_position"): + for name, position in fancy["items_position"].iteritems(): + params = { "item" : name, "position" : ",".join([str(p) for p in position]) } + items_positions.append(itemscript.substitute(params)) + + params = { + "disk" : "CryptoLottoToken-Qt", + "window_bounds" : "300,300,800,620", + "icon_size" : "96", + "background_commands" : "", + "items_positions" : "\n ".join(items_positions) + } + if fancy.has_key("window_bounds"): + params["window.bounds"] = ",".join([str(p) for p in fancy["window_bounds"]]) + if fancy.has_key("icon_size"): + params["icon_size"] = str(fancy["icon_size"]) + if bg_path is not None: + # Set background file, then call SetFile to make it invisible. + # (note: making it invisible first makes set background picture fail) + bgscript = Template("""set background picture of theViewOptions to file "$bgpic" + do shell script "SetFile -a V /Volumes/$disk/$bgpic" """) + params["background_commands"] = bgscript.substitute({"bgpic" : os.path.basename(bg_path), "disk" : params["disk"]}) + + s = appscript.substitute(params) + if verbose >= 2: + print("Running AppleScript:") + print(s) + + p = subprocess.Popen(['osascript', '-'], stdin=subprocess.PIPE) + p.communicate(input=s) + if p.returncode: + print("Error running osascript.") + + if verbose >= 2: + print "+ Finalizing .dmg disk image +" + + try: + runHDIUtil("convert", dmg_name + ".temp", format="UDBZ", o=dmg_name + ".dmg", ov=True) + except subprocess.CalledProcessError as e: + sys.exit(e.returncode) + + os.unlink(dmg_name + ".temp.dmg") + +# ------------------------------------------------ + +if verbose >= 2: + print "+ Done +" + +sys.exit(0) diff --git a/contrib/macdeploy/notes.txt b/contrib/macdeploy/notes.txt new file mode 100644 index 0000000..3d74901 --- /dev/null +++ b/contrib/macdeploy/notes.txt @@ -0,0 +1,26 @@ + +macdeployqtplus works best on OS X Lion, for Snow Leopard you'd need to install +Python 2.7 and make it your default Python installation. + +You will need the appscript package for the fancy disk image creation to work. +Install it by invoking "sudo easy_install appscript". + +This script should be invoked in the target directory like this: +$source_dir/contrib/macdeploy/macdeployqtplus Bitcoin-Qt.app -add-qt-tr da,de,es,hu,ru,uk,zh_CN,zh_TW -dmg -fancy $source_dir/contrib/macdeploy/fancy.plist -verbose 2 + +During the process, the disk image window will pop up briefly where the fancy +settings are applied. This is normal, please do not interfere. + +You can also set up Qt Creator for invoking the script. For this, go to the +"Projects" tab on the left side, switch to "Run Settings" above and add a +deploy configuration. Next add a deploy step choosing "Custom Process Step". +Fill in the following. + +Enable custom process step: [x] +Command: %{sourceDir}/contrib/macdeploy/macdeployqtplus +Working directory: %{buildDir} +Command arguments: Bitcoin-Qt.app -add-qt-tr da,de,es,hu,ru,uk,zh_CN,zh_TW -dmg -fancy %{sourceDir}/contrib/macdeploy/fancy.plist -verbose 2 + +After that you can start the deployment process through the menu with +Build -> Deploy Project "bitcoin-qt" + diff --git a/contrib/pyminer/README b/contrib/pyminer/README new file mode 100644 index 0000000..d159657 --- /dev/null +++ b/contrib/pyminer/README @@ -0,0 +1,6 @@ + +This is a 'getwork' CPU mining client for bitcoin. + +It is pure-python, and therefore very, very slow. The purpose is to +provide a reference implementation of a miner, for study. + diff --git a/contrib/pyminer/example-config.cfg b/contrib/pyminer/example-config.cfg new file mode 100644 index 0000000..757ca03 --- /dev/null +++ b/contrib/pyminer/example-config.cfg @@ -0,0 +1,32 @@ + +# +# RPC login details +# +host=127.0.0.1 +port=9332 + +rpcuser=myusername +rpcpass=mypass + + +# +# mining details +# + +threads=4 + +# periodic rate for requesting new work, if solution not found +scantime=60 + + +# +# misc. +# + +# not really used right now +logdir=/tmp/pyminer + +# set to 1, to enable hashmeter output +hashmeter=0 + + diff --git a/contrib/pyminer/pyminer.py b/contrib/pyminer/pyminer.py new file mode 100644 index 0000000..36d06a8 --- /dev/null +++ b/contrib/pyminer/pyminer.py @@ -0,0 +1,252 @@ +#!/usr/bin/python +# +# Copyright (c) 2011 The Bitcoin developers +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# + +import time +import json +import pprint +import hashlib +import struct +import re +import base64 +import httplib +import sys +from multiprocessing import Process + +ERR_SLEEP = 15 +MAX_NONCE = 1000000L + +settings = {} +pp = pprint.PrettyPrinter(indent=4) + +class BitcoinRPC: + OBJID = 1 + + def __init__(self, host, port, username, password): + authpair = "%s:%s" % (username, password) + self.authhdr = "Basic %s" % (base64.b64encode(authpair)) + self.conn = httplib.HTTPConnection(host, port, False, 30) + def rpc(self, method, params=None): + self.OBJID += 1 + obj = { 'version' : '1.1', + 'method' : method, + 'id' : self.OBJID } + if params is None: + obj['params'] = [] + else: + obj['params'] = params + self.conn.request('POST', '/', json.dumps(obj), + { 'Authorization' : self.authhdr, + 'Content-type' : 'application/json' }) + + resp = self.conn.getresponse() + if resp is None: + print "JSON-RPC: no response" + return None + + body = resp.read() + resp_obj = json.loads(body) + if resp_obj is None: + print "JSON-RPC: cannot JSON-decode body" + return None + if 'error' in resp_obj and resp_obj['error'] != None: + return resp_obj['error'] + if 'result' not in resp_obj: + print "JSON-RPC: no result in object" + return None + + return resp_obj['result'] + def getblockcount(self): + return self.rpc('getblockcount') + def getwork(self, data=None): + return self.rpc('getwork', data) + +def uint32(x): + return x & 0xffffffffL + +def bytereverse(x): + return uint32(( ((x) << 24) | (((x) << 8) & 0x00ff0000) | + (((x) >> 8) & 0x0000ff00) | ((x) >> 24) )) + +def bufreverse(in_buf): + out_words = [] + for i in range(0, len(in_buf), 4): + word = struct.unpack('@I', in_buf[i:i+4])[0] + out_words.append(struct.pack('@I', bytereverse(word))) + return ''.join(out_words) + +def wordreverse(in_buf): + out_words = [] + for i in range(0, len(in_buf), 4): + out_words.append(in_buf[i:i+4]) + out_words.reverse() + return ''.join(out_words) + +class Miner: + def __init__(self, id): + self.id = id + self.max_nonce = MAX_NONCE + + def work(self, datastr, targetstr): + # decode work data hex string to binary + static_data = datastr.decode('hex') + static_data = bufreverse(static_data) + + # the first 76b of 80b do not change + blk_hdr = static_data[:76] + + # decode 256-bit target value + targetbin = targetstr.decode('hex') + targetbin = targetbin[::-1] # byte-swap and dword-swap + targetbin_str = targetbin.encode('hex') + target = long(targetbin_str, 16) + + # pre-hash first 76b of block header + static_hash = hashlib.sha256() + static_hash.update(blk_hdr) + + for nonce in xrange(self.max_nonce): + + # encode 32-bit nonce value + nonce_bin = struct.pack(" Upstream RPC result:", result + + def iterate(self, rpc): + work = rpc.getwork() + if work is None: + time.sleep(ERR_SLEEP) + return + if 'data' not in work or 'target' not in work: + time.sleep(ERR_SLEEP) + return + + time_start = time.time() + + (hashes_done, nonce_bin) = self.work(work['data'], + work['target']) + + time_end = time.time() + time_diff = time_end - time_start + + self.max_nonce = long( + (hashes_done * settings['scantime']) / time_diff) + if self.max_nonce > 0xfffffffaL: + self.max_nonce = 0xfffffffaL + + if settings['hashmeter']: + print "HashMeter(%d): %d hashes, %.2f Khash/sec" % ( + self.id, hashes_done, + (hashes_done / 1000.0) / time_diff) + + if nonce_bin is not None: + self.submit_work(rpc, work['data'], nonce_bin) + + def loop(self): + rpc = BitcoinRPC(settings['host'], settings['port'], + settings['rpcuser'], settings['rpcpass']) + if rpc is None: + return + + while True: + self.iterate(rpc) + +def miner_thread(id): + miner = Miner(id) + miner.loop() + +if __name__ == '__main__': + if len(sys.argv) != 2: + print "Usage: pyminer.py CONFIG-FILE" + sys.exit(1) + + f = open(sys.argv[1]) + for line in f: + # skip comment lines + m = re.search('^\s*#', line) + if m: + continue + + # parse key=value lines + m = re.search('^(\w+)\s*=\s*(\S.*)$', line) + if m is None: + continue + settings[m.group(1)] = m.group(2) + f.close() + + if 'host' not in settings: + settings['host'] = '127.0.0.1' + if 'port' not in settings: + settings['port'] = 9332 + if 'threads' not in settings: + settings['threads'] = 1 + if 'hashmeter' not in settings: + settings['hashmeter'] = 0 + if 'scantime' not in settings: + settings['scantime'] = 30L + if 'rpcuser' not in settings or 'rpcpass' not in settings: + print "Missing username and/or password in cfg file" + sys.exit(1) + + settings['port'] = int(settings['port']) + settings['threads'] = int(settings['threads']) + settings['hashmeter'] = int(settings['hashmeter']) + settings['scantime'] = long(settings['scantime']) + + thr_list = [] + for thr_id in range(settings['threads']): + p = Process(target=miner_thread, args=(thr_id,)) + p.start() + thr_list.append(p) + time.sleep(1) # stagger threads + + print settings['threads'], "mining threads started" + + print time.asctime(), "Miner Starts - %s:%s" % (settings['host'], settings['port']) + try: + for thr_proc in thr_list: + thr_proc.join() + except KeyboardInterrupt: + pass + print time.asctime(), "Miner Stops - %s:%s" % (settings['host'], settings['port']) + diff --git a/contrib/qt_translations.py b/contrib/qt_translations.py new file mode 100644 index 0000000..fd8a8b7 --- /dev/null +++ b/contrib/qt_translations.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +# Helpful little script that spits out a comma-separated list of +# language codes for Qt icons that should be included +# in binary bitcoin distributions + +import glob +import os +import re +import sys + +if len(sys.argv) != 3: + sys.exit("Usage: %s $QTDIR/translations $BITCOINDIR/src/qt/locale"%sys.argv[0]) + +d1 = sys.argv[1] +d2 = sys.argv[2] + +l1 = set([ re.search(r'qt_(.*).qm', f).group(1) for f in glob.glob(os.path.join(d1, 'qt_*.qm')) ]) +l2 = set([ re.search(r'bitcoin_(.*).qm', f).group(1) for f in glob.glob(os.path.join(d2, 'bitcoin_*.qm')) ]) + +print ",".join(sorted(l1.intersection(l2))) + diff --git a/contrib/seeds/README b/contrib/seeds/README new file mode 100644 index 0000000..97e3e1e --- /dev/null +++ b/contrib/seeds/README @@ -0,0 +1,9 @@ +Utility to generate the pnSeed[] array that is compiled into the client +(see src/net.cpp). + +The 600 seeds compiled into the 0.8 release were created from sipa's DNS seed data, like this: + +curl -s http://bitcoin.sipa.be/seeds.txt | head -1000 | makeseeds.py + +The input to makeseeds.py is assumed to be approximately sorted from most-reliable to least-reliable, +with IP:port first on each line (lines that don't match IPv4:port are ignored). diff --git a/contrib/seeds/makeseeds.py b/contrib/seeds/makeseeds.py new file mode 100644 index 0000000..a0f6410 --- /dev/null +++ b/contrib/seeds/makeseeds.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# +# Generate pnSeed[] from Pieter's DNS seeder +# + +NSEEDS=600 + +import re +import sys +from subprocess import check_output + +def main(): + lines = sys.stdin.readlines() + + ips = [] + pattern = re.compile(r"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3}):9333") + for line in lines: + m = pattern.match(line) + if m is None: + continue + ip = 0 + for i in range(0,4): + ip = ip + (int(m.group(i+1)) << (8*(i))) + if ip == 0: + continue + ips.append(ip) + + for row in range(0, min(NSEEDS,len(ips)), 8): + print " " + ", ".join([ "0x%08x"%i for i in ips[row:row+8] ]) + "," + +if __name__ == '__main__': + main() diff --git a/contrib/spendfrom/README b/contrib/spendfrom/README new file mode 100644 index 0000000..8a087a0 --- /dev/null +++ b/contrib/spendfrom/README @@ -0,0 +1,32 @@ +Use the raw transactions API to send coins received on a particular +address (or addresses). + +Depends on jsonrpc + +Usage: + +spendfrom.py --from=FROMADDRESS1[,FROMADDRESS2] --to=TOADDRESS --amount=amount \ + --fee=fee --datadir=/path/to/.bitcoin --testnet --dry_run + +With no arguments, outputs a list of amounts associated with addresses. + +With arguments, sends coins received by the FROMADDRESS addresses to the TOADDRESS. + +You may explictly specify how much fee to pay (a fee more than 1% of the amount +will fail, though, to prevent bitcoin-losing accidents). Spendfrom may fail if +it thinks the transaction would never be confirmed (if the amount being sent is +too small, or if the transaction is too many bytes for the fee). + +If a change output needs to be created, the change will be sent to the last +FROMADDRESS (if you specify just one FROMADDRESS, change will go back to it). + +If --datadir is not specified, the default datadir is used. + +The --dry_run option will just create and sign the the transaction and print +the transaction data (as hexadecimal), instead of broadcasting it. + +If the transaction is created and broadcast successfully, a transaction id +is printed. + +If this was a tool for end-users and not programmers, it would have much friendlier +error-handling. diff --git a/contrib/spendfrom/setup.py b/contrib/spendfrom/setup.py new file mode 100644 index 0000000..01b9768 --- /dev/null +++ b/contrib/spendfrom/setup.py @@ -0,0 +1,9 @@ +from distutils.core import setup +setup(name='btcspendfrom', + version='1.0', + description='Command-line utility for bitcoin "coin control"', + author='Gavin Andresen', + author_email='gavin@bitcoinfoundation.org', + requires=['jsonrpc'], + scripts=['spendfrom.py'], + ) diff --git a/contrib/spendfrom/spendfrom.py b/contrib/spendfrom/spendfrom.py new file mode 100644 index 0000000..984505a --- /dev/null +++ b/contrib/spendfrom/spendfrom.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python +# +# Use the raw transactions API to spend bitcoins received on particular addresses, +# and send any change back to that same address. +# +# Example usage: +# spendfrom.py # Lists available funds +# spendfrom.py --from=ADDRESS --to=ADDRESS --amount=11.00 +# +# Assumes it will talk to a bitcoind or Bitcoin-Qt running +# on localhost. +# +# Depends on jsonrpc +# + +from decimal import * +import getpass +import math +import os +import os.path +import platform +import sys +import time +from jsonrpc import ServiceProxy, json + +BASE_FEE=Decimal("0.001") + +def check_json_precision(): + """Make sure json library being used does not lose precision converting BTC values""" + n = Decimal("20000000.00000003") + satoshis = int(json.loads(json.dumps(float(n)))*1.0e8) + if satoshis != 2000000000000003: + raise RuntimeError("JSON encode/decode loses precision") + +def determine_db_dir(): + """Return the default location of the bitcoin data directory""" + if platform.system() == "Darwin": + return os.path.expanduser("~/Library/Application Support/Bitcoin/") + elif platform.system() == "Windows": + return os.path.join(os.environ['APPDATA'], "Bitcoin") + return os.path.expanduser("~/.bitcoin") + +def read_bitcoin_config(dbdir): + """Read the bitcoin.conf file from dbdir, returns dictionary of settings""" + from ConfigParser import SafeConfigParser + + class FakeSecHead(object): + def __init__(self, fp): + self.fp = fp + self.sechead = '[all]\n' + def readline(self): + if self.sechead: + try: return self.sechead + finally: self.sechead = None + else: + s = self.fp.readline() + if s.find('#') != -1: + s = s[0:s.find('#')].strip() +"\n" + return s + + config_parser = SafeConfigParser() + config_parser.readfp(FakeSecHead(open(os.path.join(dbdir, "bitcoin.conf")))) + return dict(config_parser.items("all")) + +def connect_JSON(config): + """Connect to a bitcoin JSON-RPC server""" + testnet = config.get('testnet', '0') + testnet = (int(testnet) > 0) # 0/1 in config file, convert to True/False + if not 'rpcport' in config: + config['rpcport'] = 19332 if testnet else 9332 + connect = "http://%s:%s@127.0.0.1:%s"%(config['rpcuser'], config['rpcpassword'], config['rpcport']) + try: + result = ServiceProxy(connect) + # ServiceProxy is lazy-connect, so send an RPC command mostly to catch connection errors, + # but also make sure the bitcoind we're talking to is/isn't testnet: + if result.getmininginfo()['testnet'] != testnet: + sys.stderr.write("RPC server at "+connect+" testnet setting mismatch\n") + sys.exit(1) + return result + except: + sys.stderr.write("Error connecting to RPC server at "+connect+"\n") + sys.exit(1) + +def unlock_wallet(bitcoind): + info = bitcoind.getinfo() + if 'unlocked_until' not in info: + return True # wallet is not encrypted + t = int(info['unlocked_until']) + if t <= time.time(): + try: + passphrase = getpass.getpass("Wallet is locked; enter passphrase: ") + bitcoind.walletpassphrase(passphrase, 5) + except: + sys.stderr.write("Wrong passphrase\n") + + info = bitcoind.getinfo() + return int(info['unlocked_until']) > time.time() + +def list_available(bitcoind): + address_summary = dict() + + address_to_account = dict() + for info in bitcoind.listreceivedbyaddress(0): + address_to_account[info["address"]] = info["account"] + + unspent = bitcoind.listunspent(0) + for output in unspent: + # listunspent doesn't give addresses, so: + rawtx = bitcoind.getrawtransaction(output['txid'], 1) + vout = rawtx["vout"][output['vout']] + pk = vout["scriptPubKey"] + + # This code only deals with ordinary pay-to-bitcoin-address + # or pay-to-script-hash outputs right now; anything exotic is ignored. + if pk["type"] != "pubkeyhash" and pk["type"] != "scripthash": + continue + + address = pk["addresses"][0] + if address in address_summary: + address_summary[address]["total"] += vout["value"] + address_summary[address]["outputs"].append(output) + else: + address_summary[address] = { + "total" : vout["value"], + "outputs" : [output], + "account" : address_to_account.get(address, "") + } + + return address_summary + +def select_coins(needed, inputs): + # Feel free to improve this, this is good enough for my simple needs: + outputs = [] + have = Decimal("0.0") + n = 0 + while have < needed and n < len(inputs): + outputs.append({ "txid":inputs[n]["txid"], "vout":inputs[n]["vout"]}) + have += inputs[n]["amount"] + n += 1 + return (outputs, have-needed) + +def create_tx(bitcoind, fromaddresses, toaddress, amount, fee): + all_coins = list_available(bitcoind) + + total_available = Decimal("0.0") + needed = amount+fee + potential_inputs = [] + for addr in fromaddresses: + if addr not in all_coins: + continue + potential_inputs.extend(all_coins[addr]["outputs"]) + total_available += all_coins[addr]["total"] + + if total_available < needed: + sys.stderr.write("Error, only %f BTC available, need %f\n"%(total_available, needed)); + sys.exit(1) + + # + # Note: + # Python's json/jsonrpc modules have inconsistent support for Decimal numbers. + # Instead of wrestling with getting json.dumps() (used by jsonrpc) to encode + # Decimals, I'm casting amounts to float before sending them to bitcoind. + # + outputs = { toaddress : float(amount) } + (inputs, change_amount) = select_coins(needed, potential_inputs) + if change_amount > BASE_FEE: # don't bother with zero or tiny change + change_address = fromaddresses[-1] + if change_address in outputs: + outputs[change_address] += float(change_amount) + else: + outputs[change_address] = float(change_amount) + + rawtx = bitcoind.createrawtransaction(inputs, outputs) + signed_rawtx = bitcoind.signrawtransaction(rawtx) + if not signed_rawtx["complete"]: + sys.stderr.write("signrawtransaction failed\n") + sys.exit(1) + txdata = signed_rawtx["hex"] + + return txdata + +def compute_amount_in(bitcoind, txinfo): + result = Decimal("0.0") + for vin in txinfo['vin']: + in_info = bitcoind.getrawtransaction(vin['txid'], 1) + vout = in_info['vout'][vin['vout']] + result = result + vout['value'] + return result + +def compute_amount_out(txinfo): + result = Decimal("0.0") + for vout in txinfo['vout']: + result = result + vout['value'] + return result + +def sanity_test_fee(bitcoind, txdata_hex, max_fee): + class FeeError(RuntimeError): + pass + try: + txinfo = bitcoind.decoderawtransaction(txdata_hex) + total_in = compute_amount_in(bitcoind, txinfo) + total_out = compute_amount_out(txinfo) + if total_in-total_out > max_fee: + raise FeeError("Rejecting transaction, unreasonable fee of "+str(total_in-total_out)) + + tx_size = len(txdata_hex)/2 + kb = tx_size/1000 # integer division rounds down + if kb > 1 and fee < BASE_FEE: + raise FeeError("Rejecting no-fee transaction, larger than 1000 bytes") + if total_in < 0.01 and fee < BASE_FEE: + raise FeeError("Rejecting no-fee, tiny-amount transaction") + # Exercise for the reader: compute transaction priority, and + # warn if this is a very-low-priority transaction + + except FeeError as err: + sys.stderr.write((str(err)+"\n")) + sys.exit(1) + +def main(): + import optparse + + parser = optparse.OptionParser(usage="%prog [options]") + parser.add_option("--from", dest="fromaddresses", default=None, + help="addresses to get bitcoins from") + parser.add_option("--to", dest="to", default=None, + help="address to get send bitcoins to") + parser.add_option("--amount", dest="amount", default=None, + help="amount to send") + parser.add_option("--fee", dest="fee", default="0.0", + help="fee to include") + parser.add_option("--datadir", dest="datadir", default=determine_db_dir(), + help="location of bitcoin.conf file with RPC username/password (default: %default)") + parser.add_option("--testnet", dest="testnet", default=False, action="store_true", + help="Use the test network") + parser.add_option("--dry_run", dest="dry_run", default=False, action="store_true", + help="Don't broadcast the transaction, just create and print the transaction data") + + (options, args) = parser.parse_args() + + check_json_precision() + config = read_bitcoin_config(options.datadir) + if options.testnet: config['testnet'] = True + bitcoind = connect_JSON(config) + + if options.amount is None: + address_summary = list_available(bitcoind) + for address,info in address_summary.iteritems(): + n_transactions = len(info['outputs']) + if n_transactions > 1: + print("%s %.8f %s (%d transactions)"%(address, info['total'], info['account'], n_transactions)) + else: + print("%s %.8f %s"%(address, info['total'], info['account'])) + else: + fee = Decimal(options.fee) + amount = Decimal(options.amount) + while unlock_wallet(bitcoind) == False: + pass # Keep asking for passphrase until they get it right + txdata = create_tx(bitcoind, options.fromaddresses.split(","), options.to, amount, fee) + sanity_test_fee(bitcoind, txdata, amount*Decimal("0.01")) + if options.dry_run: + print(txdata) + else: + txid = bitcoind.sendrawtransaction(txdata) + print(txid) + +if __name__ == '__main__': + main() diff --git a/contrib/test-patches/README b/contrib/test-patches/README new file mode 100644 index 0000000..ed754ce --- /dev/null +++ b/contrib/test-patches/README @@ -0,0 +1,4 @@ +These patches are applied when the automated pull-tester +tests each pull and when master is tested using jenkins. +You can find more information about the tests run at +http://jenkins.bluematt.me/pull-tester/files/ diff --git a/contrib/testgen/README b/contrib/testgen/README new file mode 100644 index 0000000..02d6c4c --- /dev/null +++ b/contrib/testgen/README @@ -0,0 +1 @@ +Utilities to generate test vectors for the data-driven Bitcoin tests diff --git a/contrib/testgen/base58.py b/contrib/testgen/base58.py new file mode 100644 index 0000000..b716495 --- /dev/null +++ b/contrib/testgen/base58.py @@ -0,0 +1,104 @@ +''' +Bitcoin base58 encoding and decoding. + +Based on https://bitcointalk.org/index.php?topic=1026.0 (public domain) +''' +import hashlib + +# for compatibility with following code... +class SHA256: + new = hashlib.sha256 + +if str != bytes: + # Python 3.x + def ord(c): + return c + def chr(n): + return bytes( (n,) ) + +__b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' +__b58base = len(__b58chars) +b58chars = __b58chars + +def b58encode(v): + """ encode v, which is a string of bytes, to base58. + """ + long_value = 0 + for (i, c) in enumerate(v[::-1]): + long_value += (256**i) * ord(c) + + result = '' + while long_value >= __b58base: + div, mod = divmod(long_value, __b58base) + result = __b58chars[mod] + result + long_value = div + result = __b58chars[long_value] + result + + # Bitcoin does a little leading-zero-compression: + # leading 0-bytes in the input become leading-1s + nPad = 0 + for c in v: + if c == '\0': nPad += 1 + else: break + + return (__b58chars[0]*nPad) + result + +def b58decode(v, length = None): + """ decode v into a string of len bytes + """ + long_value = 0 + for (i, c) in enumerate(v[::-1]): + long_value += __b58chars.find(c) * (__b58base**i) + + result = bytes() + while long_value >= 256: + div, mod = divmod(long_value, 256) + result = chr(mod) + result + long_value = div + result = chr(long_value) + result + + nPad = 0 + for c in v: + if c == __b58chars[0]: nPad += 1 + else: break + + result = chr(0)*nPad + result + if length is not None and len(result) != length: + return None + + return result + +def checksum(v): + """Return 32-bit checksum based on SHA256""" + return SHA256.new(SHA256.new(v).digest()).digest()[0:4] + +def b58encode_chk(v): + """b58encode a string, with 32-bit checksum""" + return b58encode(v + checksum(v)) + +def b58decode_chk(v): + """decode a base58 string, check and remove checksum""" + result = b58decode(v) + if result is None: + return None + h3 = checksum(result[:-4]) + if result[-4:] == checksum(result[:-4]): + return result[:-4] + else: + return None + +def get_bcaddress_version(strAddress): + """ Returns None if strAddress is invalid. Otherwise returns integer version of address. """ + addr = b58decode_chk(strAddress) + if addr is None or len(addr)!=21: return None + version = addr[0] + return ord(version) + +if __name__ == '__main__': + # Test case (from http://gitorious.org/bitcoin/python-base58.git) + assert get_bcaddress_version('15VjRaDX9zpbA8LVnbrCAFzrVzN7ixHNsC') is 0 + _ohai = 'o hai'.encode('ascii') + _tmp = b58encode(_ohai) + assert _tmp == 'DYB3oMS' + assert b58decode(_tmp, 5) == _ohai + print("Tests passed") diff --git a/contrib/testgen/gen_base58_test_vectors.py b/contrib/testgen/gen_base58_test_vectors.py new file mode 100644 index 0000000..92dee87 --- /dev/null +++ b/contrib/testgen/gen_base58_test_vectors.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python +''' +Generate valid and invalid base58 address and private key test vectors. + +Usage: + gen_base58_test_vectors.py valid 50 > ../../src/test/data/base58_keys_valid.json + gen_base58_test_vectors.py invalid 50 > ../../src/test/data/base58_keys_invalid.json +''' +# 2012 Wladimir J. van der Laan +# Released under MIT License +import os +from itertools import islice +from base58 import b58encode, b58decode, b58encode_chk, b58decode_chk, b58chars +import random +from binascii import b2a_hex + +# key types +PUBKEY_ADDRESS = 48 +SCRIPT_ADDRESS = 5 +PUBKEY_ADDRESS_TEST = 111 +SCRIPT_ADDRESS_TEST = 196 +PRIVKEY = 176 +PRIVKEY_TEST = 239 + +metadata_keys = ['isPrivkey', 'isTestnet', 'addrType', 'isCompressed'] +# templates for valid sequences +templates = [ + # prefix, payload_size, suffix, metadata + # None = N/A + ((PUBKEY_ADDRESS,), 20, (), (False, False, 'pubkey', None)), + ((SCRIPT_ADDRESS,), 20, (), (False, False, 'script', None)), + ((PUBKEY_ADDRESS_TEST,), 20, (), (False, True, 'pubkey', None)), + ((SCRIPT_ADDRESS_TEST,), 20, (), (False, True, 'script', None)), + ((PRIVKEY,), 32, (), (True, False, None, False)), + ((PRIVKEY,), 32, (1,), (True, False, None, True)), + ((PRIVKEY_TEST,), 32, (), (True, True, None, False)), + ((PRIVKEY_TEST,), 32, (1,), (True, True, None, True)) +] + +def is_valid(v): + '''Check vector v for validity''' + result = b58decode_chk(v) + if result is None: + return False + valid = False + for template in templates: + prefix = str(bytearray(template[0])) + suffix = str(bytearray(template[2])) + if result.startswith(prefix) and result.endswith(suffix): + if (len(result) - len(prefix) - len(suffix)) == template[1]: + return True + return False + +def gen_valid_vectors(): + '''Generate valid test vectors''' + while True: + for template in templates: + prefix = str(bytearray(template[0])) + payload = os.urandom(template[1]) + suffix = str(bytearray(template[2])) + rv = b58encode_chk(prefix + payload + suffix) + assert is_valid(rv) + metadata = dict([(x,y) for (x,y) in zip(metadata_keys,template[3]) if y is not None]) + yield (rv, b2a_hex(payload), metadata) + +def gen_invalid_vector(template, corrupt_prefix, randomize_payload_size, corrupt_suffix): + '''Generate possibly invalid vector''' + if corrupt_prefix: + prefix = os.urandom(1) + else: + prefix = str(bytearray(template[0])) + + if randomize_payload_size: + payload = os.urandom(max(int(random.expovariate(0.5)), 50)) + else: + payload = os.urandom(template[1]) + + if corrupt_suffix: + suffix = os.urandom(len(template[2])) + else: + suffix = str(bytearray(template[2])) + + return b58encode_chk(prefix + payload + suffix) + +def randbool(p = 0.5): + '''Return True with P(p)''' + return random.random() < p + +def gen_invalid_vectors(): + '''Generate invalid test vectors''' + # start with some manual edge-cases + yield "", + yield "x", + while True: + # kinds of invalid vectors: + # invalid prefix + # invalid payload length + # invalid (randomized) suffix (add random data) + # corrupt checksum + for template in templates: + val = gen_invalid_vector(template, randbool(0.2), randbool(0.2), randbool(0.2)) + if random.randint(0,10)<1: # line corruption + if randbool(): # add random character to end + val += random.choice(b58chars) + else: # replace random character in the middle + n = random.randint(0, len(val)) + val = val[0:n] + random.choice(b58chars) + val[n+1:] + if not is_valid(val): + yield val, + +if __name__ == '__main__': + import sys, json + iters = {'valid':gen_valid_vectors, 'invalid':gen_invalid_vectors} + try: + uiter = iters[sys.argv[1]] + except IndexError: + uiter = gen_valid_vectors + try: + count = int(sys.argv[2]) + except IndexError: + count = 0 + + data = list(islice(uiter(), count)) + json.dump(data, sys.stdout, sort_keys=True, indent=4) + sys.stdout.write('\n') + diff --git a/contrib/tidy_datadir.sh b/contrib/tidy_datadir.sh new file mode 100644 index 0000000..5d6d826 --- /dev/null +++ b/contrib/tidy_datadir.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +if [ -d "$1" ]; then + cd "$1" +else + echo "Usage: $0 " >&2 + echo "Removes obsolete Bitcoin database files" >&2 + exit 1 +fi + +LEVEL=0 +if [ -f wallet.dat -a -f addr.dat -a -f blkindex.dat -a -f blk0001.dat ]; then LEVEL=1; fi +if [ -f wallet.dat -a -f peers.dat -a -f blkindex.dat -a -f blk0001.dat ]; then LEVEL=2; fi +if [ -f wallet.dat -a -f peers.dat -a -f coins/CURRENT -a -f blktree/CURRENT -a -f blocks/blk00000.dat ]; then LEVEL=3; fi +if [ -f wallet.dat -a -f peers.dat -a -f chainstate/CURRENT -a -f blocks/index/CURRENT -a -f blocks/blk00000.dat ]; then LEVEL=4; fi + +case $LEVEL in + 0) + echo "Error: no Bitcoin datadir detected." + exit 1 + ;; + 1) + echo "Detected old Bitcoin datadir (before 0.7)." + echo "Nothing to do." + exit 0 + ;; + 2) + echo "Detected Bitcoin 0.7 datadir." + ;; + 3) + echo "Detected Bitcoin pre-0.8 datadir." + ;; + 4) + echo "Detected Bitcoin 0.8 datadir." + ;; +esac + +FILES="" +DIRS="" + +if [ $LEVEL -ge 3 ]; then FILES=$(echo $FILES blk????.dat blkindex.dat); fi +if [ $LEVEL -ge 2 ]; then FILES=$(echo $FILES addr.dat); fi +if [ $LEVEL -ge 4 ]; then DIRS=$(echo $DIRS coins blktree); fi + +for FILE in $FILES; do + if [ -f $FILE ]; then + echo "Deleting: $FILE" + rm -f $FILE + fi +done + +for DIR in $DIRS; do + if [ -d $DIR ]; then + echo "Deleting: $DIR/" + rm -rf $DIR + fi +done + +echo "Done." diff --git a/contrib/wallettools/walletchangepass.py b/contrib/wallettools/walletchangepass.py new file mode 100644 index 0000000..e54f5d7 --- /dev/null +++ b/contrib/wallettools/walletchangepass.py @@ -0,0 +1,5 @@ +from jsonrpc import ServiceProxy +access = ServiceProxy("http://127.0.0.1:9332") +pwd = raw_input("Enter old wallet passphrase: ") +pwd2 = raw_input("Enter new wallet passphrase: ") +access.walletpassphrasechange(pwd, pwd2) diff --git a/contrib/wallettools/walletunlock.py b/contrib/wallettools/walletunlock.py new file mode 100644 index 0000000..e133af4 --- /dev/null +++ b/contrib/wallettools/walletunlock.py @@ -0,0 +1,4 @@ +from jsonrpc import ServiceProxy +access = ServiceProxy("http://127.0.0.1:9332") +pwd = raw_input("Enter wallet passphrase: ") +access.walletpassphrase(pwd, 60) diff --git a/doc/Doxyfile b/doc/Doxyfile new file mode 100644 index 0000000..4bf1e78 --- /dev/null +++ b/doc/Doxyfile @@ -0,0 +1,1752 @@ +# Doxyfile 1.7.4 + +# !!! Invoke doxygen from project root using: +# doxygen doc/Doxyfile + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = CryptoLottoToken + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 0.5.0 + +# 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 +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "P2P Digital Currency" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = doc/bitcoin_logo_doxygen.png + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc/doxygen + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = src + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py \ + *.f90 \ + *.f \ + *.for \ + *.vhd \ + *.vhdl + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is adviced to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the stylesheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the +# mathjax.org site, so you can quickly see the result without installing +# MathJax, but it is strongly recommended to install a local copy of MathJax +# before deployment. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will write a font called Helvetica to the output +# directory and reference it in all dot files that doxygen generates. +# When you want a differently looking font you can specify the font name +# using DOT_FONTNAME. You need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000..4a63ecd --- /dev/null +++ b/doc/README.md @@ -0,0 +1,47 @@ +CryptoLottoToken 0.8.x BETA +==================== + +Copyright (c) 2009-2013 Bitcoin Developers +Copyright (c) 2011-2013 CryptoLottoToken Developers + +Distributed under the MIT/X11 software license, see the accompanying +file COPYING or http://www.opensource.org/licenses/mit-license.php. +This product includes software developed by the OpenSSL Project for use in the [OpenSSL Toolkit](http://www.openssl.org/). This product includes +cryptographic software written by Eric Young ([eay@cryptsoft.com](mailto:eay@cryptsoft.com)), and UPnP software written by Thomas Bernard. + + +Intro +--------------------- +CryptoLottoToken is a free open source peer-to-peer electronic cash system that is +completely decentralized, without the need for a central server or trusted +parties. Users hold the crypto keys to their own money and transact directly +with each other, with the help of a P2P network to check for double-spending. + + +Setup +--------------------- +You need the Qt4 run-time libraries to run CryptoLottoToken-Qt. On Debian or Ubuntu: + `sudo apt-get install libqtgui4` + +Unpack the files into a directory and run: + +- bin/32/CryptoLottoToken-qt (GUI, 32-bit) +- bin/32/CryptoLottoTokend (headless, 32-bit) +- bin/64/CryptoLottoToken-qt (GUI, 64-bit) +- bin/64/CryptoLottoTokend (headless, 64-bit) + +See the documentation at the [CryptoLottoToken Wiki](http://CryptoLottoToken.info) +for help and more information. + + +Other Pages +--------------------- +- [Unix Build Notes](build-unix.md) +- [OSX Build Notes](build-osx.md) +- [Windows Build Notes](build-msw.md) +- [Coding Guidelines](coding.md) +- [Release Process](release-process.md) +- [Release Notes](release-notes.md) +- [Multiwallet Qt Development](multiwallet-qt.md) +- [Unit Tests](unit-tests.md) +- [Translation Process](translation_process.md) diff --git a/doc/README_windows.txt b/doc/README_windows.txt new file mode 100644 index 0000000..9e34445 --- /dev/null +++ b/doc/README_windows.txt @@ -0,0 +1,28 @@ +CryptoLottoToken 0.8.x BETA + +Copyright (c) 2009-2013 Bitcoin Developers +Copyright (c) 2011-2013 CryptoLottoToken Developers +Distributed under the MIT/X11 software license, see the accompanying +file COPYING or http://www.opensource.org/licenses/mit-license.php. +This product includes software developed by the OpenSSL Project for use in +the OpenSSL Toolkit (http://www.openssl.org/). This product includes +cryptographic software written by Eric Young (eay@cryptsoft.com). + + +Intro +----- +CryptoLottoToken is a free open source peer-to-peer electronic cash system that is +completely decentralized, without the need for a central server or trusted +parties. Users hold the crypto keys to their own money and transact directly +with each other, with the help of a P2P network to check for double-spending. + + +Setup +----- +Unpack the files into a directory and run CryptoLottoToken-qt.exe. + +CryptoLottoToken-Qt is the original CryptoLottoToken client and it builds the backbone of the network. +However, it downloads and stores the entire history of CryptoLottoToken transactions; +depending on the speed of your computer and network connection, the synchronization +process can take anywhere from a few hours to a day or more. + diff --git a/doc/Tor.txt b/doc/Tor.txt new file mode 100644 index 0000000..e17111e --- /dev/null +++ b/doc/Tor.txt @@ -0,0 +1,92 @@ +TOR SUPPORT IN BITCOIN +====================== + +It is possible to run CryptoLottoToken as a Tor hidden service, and connect to such services. + +The following directions assume you have a Tor proxy running on port 9050. Many distributions +default to having a SOCKS proxy listening on port 9050, but others may not. +In particular, the Tor Browser Bundle defaults to listening on a random port. See +https://www.torproject.org/docs/faq.html.en#TBBSocksPort for how to properly +configure Tor. + + +1. Run CryptoLottoToken behind a Tor proxy +--------------------------------- + +The first step is running CryptoLottoToken behind a Tor proxy. This will already make all +outgoing connections be anonimized, but more is possible. + +-socks=5 SOCKS5 supports connecting-to-hostname, which can be used instead + of doing a (leaking) local DNS lookup. SOCKS5 is the default, + but SOCKS4 does not support this. (SOCKS4a does, but isn't + implemented). + +-proxy=ip:port Set the proxy server. If SOCKS5 is selected (default), this proxy + server will be used to try to reach .onion addresses as well. + +-tor=ip:port Set the proxy server to use for tor hidden services. You do not + need to set this if it's the same as -proxy. You can use -notor + to explicitly disable access to hidden service. + +-listen When using -proxy, listening is disabled by default. If you want + to run a hidden service (see next section), you'll need to enable + it explicitly. + +-connect=X When behind a Tor proxy, you can specify .onion addresses instead +-addnode=X of IP addresses or hostnames in these parameters. It requires +-seednode=X SOCKS5. In Tor mode, such addresses can also be exchanged with + other P2P nodes. + +In a typical situation, this suffices to run behind a Tor proxy: + + ./CryptoLottoTokend -proxy=127.0.0.1:9050 + + +2. Run a CryptoLottoToken hidden server +------------------------------ + +If you configure your Tor system accordingly, it is possible to make your node also +reachable from the Tor network. Add these lines to your /etc/tor/torrc (or equivalent +config file): + + HiddenServiceDir /var/lib/tor/CryptoLottoToken-service/ + HiddenServicePort 9333 127.0.0.1:9333 + +The directory can be different of course, but (both) port numbers should be equal to +your CryptoLottoTokend's P2P listen port (9333 by default). + +-externalip=X You can tell CryptoLottoToken about its publicly reachable address using + this option, and this can be a .onion address. Given the above + configuration, you can find your onion address in + /var/lib/tor/CryptoLottoToken-service/hostname. Onion addresses are given + preference for your node to advertize itself with, for connections + coming from unroutable addresses (such as 127.0.0.1, where the + Tor proxy typically runs). + +-listen You'll need to enable listening for incoming connections, as this + is off by default behind a proxy. + +-discover When -externalip is specified, no attempt is made to discover local + IPv4 or IPv6 addresses. If you want to run a dual stack, reachable + from both Tor and IPv4 (or IPv6), you'll need to either pass your + other addresses using -externalip, or explicitly enable -discover. + Note that both addresses of a dual-stack system may be easily + linkable using traffic analysis. + +In a typical situation, where you're only reachable via Tor, this should suffice: + + ./CryptoLottoTokend -proxy=127.0.0.1:9050 -externalip=57qr3yd1nyntf5k.onion -listen + +(obviously, replace the Onion address with your own). If you don't care too much +about hiding your node, and want to be reachable on IPv4 as well, additionally +specify: + + ./CryptoLottoTokend ... -discover + +and open port 9333 on your firewall (or use -upnp). + +If you only want to use Tor to reach onion addresses, but not use it as a proxy +for normal IPv4/IPv6 communication, use: + + ./CryptoLottoTokend -tor=127.0.0.1:9050 -externalip=57qr3yd1nyntf5k.onion -discover + diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt new file mode 100644 index 0000000..2069c5d --- /dev/null +++ b/doc/assets-attribution.txt @@ -0,0 +1,58 @@ +Icon: src/qt/res/icons/clock*.png, src/qt/res/icons/tx*.png, + src/qt/res/src/clock_green.svg, src/qt/res/src/clock1.svg, + src/qt/res/src/clock2.svg, src/qt/res/src/clock3.svg, + src/qt/res/src/clock4.svg, src/qt/res/src/clock5.svg, + src/qt/res/src/inout.svg, src/qt/res/src/questionmark.svg +Designer: Wladimir van der Laan +License: MIT + +Icon: src/qt/res/icons/address-book.png, src/qt/res/icons/export.png, + src/qt/res/icons/history.png, src/qt/res/icons/key.png, + src/qt/res/icons/lock_*.png, src/qt/res/icons/overview.png, + src/qt/res/icons/receive.png, src/qt/res/icons/send.png, + src/qt/res/icons/synced.png, src/qt/res/icons/filesave.png +Icon Pack: NUVOLA ICON THEME for KDE 3.x +Designer: David Vignoni (david@icon-king.com) + ICON KING - www.icon-king.com +License: LGPL +Site: http://www.icon-king.com/projects/nuvola/ + +Icon: src/qt/res/icons/connect*.png +Icon Pack: Human-O2 +Designer: schollidesign +License: GNU/GPL +Site: http://findicons.com/icon/93743/blocks_gnome_netstatus_0 + +Icon: src/qt/res/icons/transaction*.png +Designer: md2k7 +Site: https://bitcointalk.org/index.php?topic=15276.0 +License: You are free to do with these icons as you wish, including selling, + copying, modifying etc. +License: MIT + +Icon: src/qt/res/icons/configure.png, src/qt/res/icons/quit.png, + src/qt/res/icons/editcopy.png, src/qt/res/icons/editpaste.png, + src/qt/res/icons/add.png, src/qt/res/icons/edit.png, + src/qt/res/icons/remove.png (edited) +Designer: http://www.everaldo.com +Icon Pack: Crystal SVG +License: LGPL + +Icon: scripts/img/reload.xcf (modified), src/qt/res/movies/update_spinner.mng +Icon Pack: Kids +Designer: Everaldo (Everaldo Coelho) +License: GNU/GPL +Site: http://findicons.com/icon/17102/reload?id=17102 + +Icon: src/qt/res/icons/debugwindow.png +Designer: Vignoni David +Site: http://www.oxygen-icons.org/ +License: Oxygen icon theme is dual licensed. You may copy it under the Creative Common Attribution-ShareAlike 3.0 License or the GNU Library General Public License. + +Icon: src/qt/res/icons/bitcoin.icns, src/qt/res/src/bitcoin.svg, + src/qt/res/src/bitcoin.ico, src/qt/res/src/bitcoin.png, + src/qt/res/src/bitcoin_testnet.png, docs/bitcoin_logo_doxygen.png, + src/qt/res/icons/toolbar.png, src/qt/res/icons/toolbar_testnet.png, + src/qt/res/images/splash.png, src/qt/res/images/splash_testnet.png +Designer: Jonas Schnelli (based on the original bitcoin logo from Bitboy) +License: MIT diff --git a/doc/bitcoin_logo_doxygen.png b/doc/bitcoin_logo_doxygen.png new file mode 100644 index 0000000..2d62a8d Binary files /dev/null and b/doc/bitcoin_logo_doxygen.png differ diff --git a/doc/build-msw.md b/doc/build-msw.md new file mode 100644 index 0000000..bb8af48 --- /dev/null +++ b/doc/build-msw.md @@ -0,0 +1,89 @@ +Copyright (c) 2009-2013 Bitcoin Developers +Distributed under the MIT/X11 software license, see the accompanying +file COPYING or http://www.opensource.org/licenses/mit-license.php. +This product includes software developed by the OpenSSL Project for use in the [OpenSSL Toolkit](http://www.openssl.org/). This product includes +cryptographic software written by Eric Young ([eay@cryptsoft.com](mailto:eay@cryptsoft.com)), and UPnP software written by Thomas Bernard. + + +See readme-qt.rst for instructions on building CryptoLottoToken-Qt, the +graphical user interface. + +WINDOWS BUILD NOTES +=================== + +Compilers Supported +------------------- +TODO: What works? +Note: releases are cross-compiled using mingw running on Linux. + + +Dependencies +------------ +Libraries you need to download separately and build: + + default path download +OpenSSL \openssl-1.0.1c-mgw http://www.openssl.org/source/ +Berkeley DB \db-4.8.30.NC-mgw http://www.oracle.com/technology/software/products/berkeley-db/index.html +Boost \boost-1.50.0-mgw http://www.boost.org/users/download/ +miniupnpc \miniupnpc-1.6-mgw http://miniupnp.tuxfamily.org/files/ + +Their licenses: + + OpenSSL Old BSD license with the problematic advertising requirement + Berkeley DB New BSD license with additional requirement that linked software must be free open source + Boost MIT-like license + miniupnpc New (3-clause) BSD license + +Versions used in this release: + + OpenSSL 1.0.1c + Berkeley DB 4.8.30.NC + Boost 1.50.0 + miniupnpc 1.6 + + +OpenSSL +------- +MSYS shell: + +un-tar sources with MSYS 'tar xfz' to avoid issue with symlinks (OpenSSL ticket 2377) +change 'MAKE' env. variable from 'C:\MinGW32\bin\mingw32-make.exe' to '/c/MinGW32/bin/mingw32-make.exe' + + cd /c/openssl-1.0.1c-mgw + ./config + make + +Berkeley DB +----------- +MSYS shell: + + cd /c/db-4.8.30.NC-mgw/build_unix + sh ../dist/configure --enable-mingw --enable-cxx + make + +Boost +----- +DOS prompt: + + downloaded boost jam 3.1.18 + cd \boost-1.50.0-mgw + bjam toolset=gcc --build-type=complete stage + +MiniUPnPc +--------- +UPnP support is optional, make with `USE_UPNP=` to disable it. + +MSYS shell: + + cd /c/miniupnpc-1.6-mgw + make -f Makefile.mingw + mkdir miniupnpc + cp *.h miniupnpc/ + +CryptoLottoToken +------- +DOS prompt: + + cd \CryptoLottoToken\src + mingw32-make -f makefile.mingw + strip CryptoLottoTokend.exe diff --git a/doc/build-osx.md b/doc/build-osx.md new file mode 100644 index 0000000..6364f8c --- /dev/null +++ b/doc/build-osx.md @@ -0,0 +1,210 @@ +Mac OS X CryptoLottoTokend build instructions +==================================== + +Authors +------- + +* Laszlo Hanyecz +* Douglas Huff +* Colin Dean +* Gavin Andresen + +License +------- + +Copyright (c) 2009-2012 Bitcoin Developers + +Distributed under the MIT/X11 software license, see the accompanying +file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in +the OpenSSL Toolkit (http://www.openssl.org/). + +This product includes cryptographic software written by +Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +Notes +----- + +See `doc/readme-qt.rst` for instructions on building CryptoLottoToken-Qt, the +graphical user interface. + +Tested on OS X 10.5 through 10.8 on Intel processors only. PPC is not +supported because it is big-endian. + +All of the commands should be executed in a Terminal application. The +built-in one is located in `/Applications/Utilities`. + +Preparation +----------- + +You need to install XCode with all the options checked so that the compiler +and everything is available in /usr not just /Developer. XCode should be +available on your OS X installation media, but if not, you can get the +current version from https://developer.apple.com/xcode/. If you install +Xcode 4.3 or later, you'll need to install its command line tools. This can +be done in `Xcode > Preferences > Downloads > Components` and generally must +be re-done or updated every time Xcode is updated. + +There's an assumption that you already have `git` installed, as well. If +not, it's the path of least resistance to install [Github for Mac](https://mac.github.com/) +(OS X 10.7+) or +[Git for OS X](https://code.google.com/p/git-osx-installer/). It is also +available via Homebrew or MacPorts. + +You will also need to install [Homebrew](http://mxcl.github.io/homebrew/) +or [MacPorts](https://www.macports.org/) in order to install library +dependencies. It's largely a religious decision which to choose, but, as of +December 2012, MacPorts is a little easier because you can just install the +dependencies immediately - no other work required. If you're unsure, read +the instructions through first in order to assess what you want to do. +Homebrew is a little more popular among those newer to OS X. + +The installation of the actual dependencies is covered in the Instructions +sections below. + +Instructions: MacPorts +---------------------- + +### Install dependencies + +Installing the dependencies using MacPorts is very straightforward. + + sudo port install boost db48@+no_java openssl miniupnpc + +### Building `CryptoLottoTokend` + +1. Clone the github tree to get the source code and go into the directory. + + git clone git@github.com:CryptoLottoToken-project/CryptoLottoToken.git CryptoLottoToken + cd CryptoLottoToken + +2. Build CryptoLottoTokend: + + cd src + make -f makefile.osx + +3. It is a good idea to build and run the unit tests, too: + + make -f makefile.osx test + +Instructions: HomeBrew +---------------------- + +#### If compiling on Maverick (10.9) + +You may find it easier to add the following steps to your process. Since QT 4.8 isn't supported on Maverick (and until I can rewrite some of this code to take advantage of QT 5.2, installing QT through homebrew will make your life easier. + + brew install qt + +Once you have QT installed, you might need to relink the new applications so that they appear in your Application folder, but this is unnecessary for compiling CryptoLottoToken. Now move on to installing the rest of the dependencies. + +#### Install dependencies using Homebrew + + brew install boost miniupnpc openssl berkeley-db4 + +Note: After you have installed the dependencies, you should check that the Brew installed version of OpenSSL is the one available for compilation. You can check this by typing + + openssl version + +into Terminal. You should see OpenSSL 1.0.1e 11 Feb 2013. + +If not, you can ensure that the Brew OpenSSL is correctly linked by running + + brew link openssl --force + +Rerunning "openssl version" should now return the correct version. + +### Building `CryptoLottoTokend` + +1. Clone the github tree to get the source code and go into the directory. + + git clone git@github.com:CryptoLottoToken-project/CryptoLottoToken.git CryptoLottoToken + cd CryptoLottoToken + +2. Modify source in order to pick up the `openssl` library. + + Edit `makefile.osx` to account for library location differences. There's a + diff in `contrib/homebrew/makefile.osx.patch` that shows what you need to + change, or you can just patch by doing + + patch -p1 < contrib/homebrew/makefile.osx.patch + +*Update*: The above #2 step has been rebuilt here. Now before you proceed to building CryptoLottoTokend it is coing to be necessary to edit both the makefile and the CryptoLottoToken-qt.pro file. You can find those edits in /contrib/homebrew/ + +What you doing is fixing the locations of openssl, boost, and berkeley-db4 to the correct locations that homebrew installs. + +3. Build CryptoLottoTokend: + + cd src + make -f makefile.osx + +4. It is a good idea to build and run the unit tests, too: + + make -f makefile.osx test + +Creating a release build +------------------------ + +A CryptoLottoTokend binary is not included in the CryptoLottoToken-Qt.app bundle. You can ignore +this section if you are building `CryptoLottoTokend` for your own use. + +If you are building `litecond` for others, your build machine should be set up +as follows for maximum compatibility: + +All dependencies should be compiled with these flags: + + -mmacosx-version-min=10.5 -arch i386 -isysroot /Developer/SDKs/MacOSX10.5.sdk + +For MacPorts, that means editing your macports.conf and setting +`macosx_deployment_target` and `build_arch`: + + macosx_deployment_target=10.5 + build_arch=i386 + +... and then uninstalling and re-installing, or simply rebuilding, all ports. + +As of December 2012, the `boost` port does not obey `macosx_deployment_target`. +Download `http://gavinandresen-bitcoin.s3.amazonaws.com/boost_macports_fix.zip` +for a fix. Some ports also seem to obey either `build_arch` or +`macosx_deployment_target`, but not both at the same time. For example, building +on an OS X 10.6 64-bit machine fails. Official release builds of CryptoLottoToken-Qt are +compiled on an OS X 10.6 32-bit machine to workaround that problem. + +Once dependencies are compiled, creating `CryptoLottoToken-Qt.app` is easy: + + make -f Makefile.osx RELEASE=1 + +QT Release +---------- + +First, run this command: + + qmake "USE_UPNP=1" + +Now you can run the command: + + make -f Makefile + +This will make the QT version of the wallet WITHOUT having to use QT Creator (since we also installed the QT components using homebrew earlier). + +Running +------- + +It's now available at `./CryptoLottoTokend`, provided that you are still in the `src` +directory. We have to first create the RPC configuration file, though. + +Run `./CryptoLottoTokend` to get the filename where it should be put, or just try these +commands: + + echo -e "rpcuser=CryptoLottoTokenrpc\nrpcpassword=$(xxd -l 16 -p /dev/urandom)" > "/Users/${USER}/Library/Application Support/CryptoLottoToken/CryptoLottoToken.conf" + chmod 600 "/Users/${USER}/Library/Application Support/CryptoLottoToken/CryptoLottoToken.conf" + +When next you run it, it will start downloading the blockchain, but it won't +output anything while it's doing this. This process may take several hours. + +Other commands: + + ./CryptoLottoTokend --help # for a list of command-line options. + ./CryptoLottoTokend -daemon # to start the CryptoLottoToken daemon. + ./CryptoLottoTokend help # When the daemon is running, to get a list of RPC commands diff --git a/doc/build-unix.md b/doc/build-unix.md new file mode 100644 index 0000000..80d100e --- /dev/null +++ b/doc/build-unix.md @@ -0,0 +1,154 @@ +Copyright (c) 2009-2013 Bitcoin Developers + +Distributed under the MIT/X11 software license, see the accompanying +file COPYING or http://www.opensource.org/licenses/mit-license.php. +This product includes software developed by the OpenSSL Project for use in the [OpenSSL Toolkit](http://www.openssl.org/). This product includes +cryptographic software written by Eric Young ([eay@cryptsoft.com](mailto:eay@cryptsoft.com)), and UPnP software written by Thomas Bernard. + +UNIX BUILD NOTES +==================== + +To Build +--------------------- + + cd src/ + make -f makefile.unix # Headless CryptoLottoToken + +See readme-qt.rst for instructions on building CryptoLottoToken-Qt, the graphical user interface. + +Dependencies +--------------------- + + Library Purpose Description + ------- ------- ----------- + libssl SSL Support Secure communications + libdb4.8 Berkeley DB Blockchain & wallet storage + libboost Boost C++ Library + miniupnpc UPnP Support Optional firewall-jumping support + +[miniupnpc](http://miniupnp.free.fr/) may be used for UPnP port mapping. It can be downloaded from [here]( +http://miniupnp.tuxfamily.org/files/). UPnP support is compiled in and +turned off by default. Set USE_UPNP to a different value to control this: + + USE_UPNP= No UPnP support miniupnp not required + USE_UPNP=0 (the default) UPnP support turned off by default at runtime + USE_UPNP=1 UPnP support turned on by default at runtime + +IPv6 support may be disabled by setting: + + USE_IPV6=0 Disable IPv6 support + +Licenses of statically linked libraries: + Berkeley DB New BSD license with additional requirement that linked + software must be free open source + Boost MIT-like license + miniupnpc New (3-clause) BSD license + +- Versions used in this release: +- GCC 4.3.3 +- OpenSSL 1.0.1c +- Berkeley DB 4.8.30.NC +- Boost 1.37 +- miniupnpc 1.6 + +Dependency Build Instructions: Ubuntu & Debian +---------------------------------------------- +Build requirements: + + sudo apt-get install build-essential + sudo apt-get install libssl-dev + +for Ubuntu 12.04: + + sudo apt-get install libboost-all-dev + + db4.8 packages are available [here](https://launchpad.net/~bitcoin/+archive/bitcoin). + + Ubuntu precise has packages for libdb5.1-dev and libdb5.1++-dev, + but using these will break binary wallet compatibility, and is not recommended. + +for other Ubuntu & Debian: + + sudo apt-get install libdb4.8-dev + sudo apt-get install libdb4.8++-dev + sudo apt-get install libboost1.37-dev + (If using Boost 1.37, append -mt to the boost libraries in the makefile) + +Optional: + + sudo apt-get install libminiupnpc-dev (see USE_UPNP compile flag) + + +Notes +----- +The release is built with GCC and then "strip bitcoind" to strip the debug +symbols, which reduces the executable size by about 90%. + + +miniupnpc +--------- + tar -xzvf miniupnpc-1.6.tar.gz + cd miniupnpc-1.6 + make + sudo su + make install + + +Berkeley DB +----------- +You need Berkeley DB 4.8. If you have to build Berkeley DB yourself: + + ../dist/configure --enable-cxx + make + + +Boost +----- +If you need to build Boost yourself: + + sudo su + ./bootstrap.sh + ./bjam install + + +Security +-------- +To help make your CryptoLottoToken installation more secure by making certain attacks impossible to +exploit even if a vulnerability is found, you can take the following measures: + +* Position Independent Executable + Build position independent code to take advantage of Address Space Layout Randomization + offered by some kernels. An attacker who is able to cause execution of code at an arbitrary + memory location is thwarted if he doesn't know where anything useful is located. + The stack and heap are randomly located by default but this allows the code section to be + randomly located as well. + + On an Amd64 processor where a library was not compiled with -fPIC, this will cause an error + such as: "relocation R_X86_64_32 against `......' can not be used when making a shared object;" + + To build with PIE, use: + make -f makefile.unix ... -e PIE=1 + + To test that you have built PIE executable, install scanelf, part of paxutils, and use: + + scanelf -e ./CryptoLottoToken + + The output should contain: + TYPE + ET_DYN + +* Non-executable Stack + If the stack is executable then trivial stack based buffer overflow exploits are possible if + vulnerable buffers are found. By default, bitcoin should be built with a non-executable stack + but if one of the libraries it uses asks for an executable stack or someone makes a mistake + and uses a compiler extension which requires an executable stack, it will silently build an + executable without the non-executable stack protection. + + To verify that the stack is non-executable after compiling use: + `scanelf -e ./CryptoLottoToken` + + the output should contain: + STK/REL/PTL + RW- R-- RW- + + The STK RW- means that the stack is readable and writeable but not executable. diff --git a/doc/coding.md b/doc/coding.md new file mode 100644 index 0000000..3581d7d --- /dev/null +++ b/doc/coding.md @@ -0,0 +1,94 @@ +Coding +==================== + +Please be consistent with the existing coding style. + +Block style: + + bool Function(char* psz, int n) + { + // Comment summarising what this section of code does + for (int i = 0; i < n; i++) + { + // When something fails, return early + if (!Something()) + return false; + ... + } + + // Success return is usually at the end + return true; + } + +- ANSI/Allman block style +- 4 space indenting, no tabs +- No extra spaces inside parenthesis; please don't do ( this ) +- No space after function names, one space after if, for and while + +Variable names begin with the type in lowercase, like nSomeVariable. +Please don't put the first word of the variable name in lowercase like +someVariable. + +Common types: + + n integer number: short, unsigned short, int, unsigned int, int64, uint64, sometimes char if used as a number + d double, float + f flag + hash uint256 + p pointer or array, one p for each level of indirection + psz pointer to null terminated string + str string object + v vector or similar list objects + map map or multimap + set set or multiset + bn CBigNum + +------------------------- +Locking/mutex usage notes + +The code is multi-threaded, and uses mutexes and the +LOCK/TRY_LOCK macros to protect data structures. + +Deadlocks due to inconsistent lock ordering (thread 1 locks cs_main +and then cs_wallet, while thread 2 locks them in the opposite order: +result, deadlock as each waits for the other to release its lock) are +a problem. Compile with -DDEBUG_LOCKORDER to get lock order +inconsistencies reported in the debug.log file. + +Re-architecting the core code so there are better-defined interfaces +between the various components is a goal, with any necessary locking +done by the components (e.g. see the self-contained CKeyStore class +and its cs_KeyStore lock for example). + +------- +Threads + +- StartNode : Starts other threads. + +- ThreadGetMyExternalIP : Determines outside-the-firewall IP address, sends addr message to connected peers when it determines it. + +- ThreadSocketHandler : Sends/Receives data from peers on port 8333. + +- ThreadMessageHandler : Higher-level message handling (sending and receiving). + +- ThreadOpenConnections : Initiates new connections to peers. + +- ThreadTopUpKeyPool : replenishes the keystore's keypool. + +- ThreadCleanWalletPassphrase : re-locks an encrypted wallet after user has unlocked it for a period of time. + +- SendingDialogStartTransfer : used by pay-via-ip-address code (obsolete) + +- ThreadDelayedRepaint : repaint the gui + +- ThreadFlushWalletDB : Close the wallet.dat file if it hasn't been used in 500ms. + +- ThreadRPCServer : Remote procedure call handler, listens on port 8332 for connections and services them. + +- ThreadBitcoinMiner : Generates bitcoins + +- ThreadMapPort : Universal plug-and-play startup/shutdown + +- Shutdown : Does an orderly shutdown of everything + +- ExitTimeout : Windows-only, sleeps 5 seconds then exits application diff --git a/doc/files.txt b/doc/files.txt new file mode 100644 index 0000000..5d4cdab --- /dev/null +++ b/doc/files.txt @@ -0,0 +1,19 @@ +Used in 0.8.0: +* wallet.dat: personal wallet (BDB) with keys and transactions +* peers.dat: peer IP address database (custom format); since 0.7.0 +* blocks/blk000??.dat: block data (custom, 128 MiB per file); since 0.8.0 +* blocks/rev000??.dat; block undo data (custom); since 0.8.0 (format changed since pre-0.8) +* blocks/index/*; block index (LevelDB); since 0.8.0 +* chainstate/*; block chain state database (LevelDB); since 0.8.0 +* database/*: BDB database environment; only used for wallet since 0.8.0 + +Only used in pre-0.8.0: +* blktree/*; block chain index (LevelDB); since pre-0.8, replaced by blocks/index/* in 0.8.0 +* coins/*; unspent transaction output database (LevelDB); since pre-0.8, replaced by chainstate/* in 0.8.0 + +Only used before 0.8.0: +* blkindex.dat: block chain index database (BDB); replaced by {chainstate/*,blocks/index/*,blocks/rev000??.dat} in 0.8.0 +* blk000?.dat: block data (custom, 2 GiB per file); replaced by blocks/blk000??.dat in 0.8.0 + +Only used before 0.7.0: +* addr.dat: peer IP address database (BDB); replaced by peers.dat in 0.7.0 diff --git a/doc/multiwallet-qt.md b/doc/multiwallet-qt.md new file mode 100644 index 0000000..8d69555 --- /dev/null +++ b/doc/multiwallet-qt.md @@ -0,0 +1,52 @@ +Multiwallet Qt Development and Integration Strategy +=================================================== + +In order to support loading of multiple wallets in bitcoin-qt, a few changes in the UI architecture will be needed. +Fortunately, only four of the files in the existing project are affected by this change. + +Three new classes have been implemented in three new .h/.cpp file pairs, with much of the functionality that was previously +implemented in the BitcoinGUI class moved over to these new classes. + +The two existing files most affected, by far, are bitcoingui.h and bitcoingui.cpp, as the BitcoinGUI class will require +some major retrofitting. + +Only requiring some minor changes is bitcoin.cpp. + +Finally, three new headers and source files will have to be added to bitcoin-qt.pro. + +Changes to class BitcoinGUI +--------------------------- +The principal change to the BitcoinGUI class concerns the QStackedWidget instance called centralWidget. +This widget owns five page views: overviewPage, transactionsPage, addressBookPage, receiveCoinsPage, and sendCoinsPage. + +A new class called *WalletView* inheriting from QStackedWidget has been written to handle all renderings and updates of +these page views. In addition to owning these five page views, a WalletView also has a pointer to a WalletModel instance. +This allows the construction of multiple WalletView objects, each rendering a distinct wallet. + +A second class called *WalletStack*, also inheriting from QStackedWidget, has been written to handle switching focus between +different loaded wallets. In its current implementation, as a QStackedWidget, only one wallet can be viewed at a time - +but this can be changed later. + +A third class called *WalletFrame* inheriting from QFrame has been written as a container for embedding all wallet-related +controls into BitcoinGUI. At present it just contains a WalletStack instance and does little more than passing on messages +from BitcoinGUI to the WalletStack, which in turn passes them to the individual WalletViews. It is a WalletFrame instance +that takes the place of what used to be centralWidget in BitcoinGUI. The purpose of this class is to allow future +refinements of the wallet controls with minimal need for further modifications to BitcoinGUI, thus greatly simplifying +merges while reducing the risk of breaking top-level stuff. + +Changes to bitcoin.cpp +---------------------- +bitcoin.cpp is the entry point into bitcoin-qt, and as such, will require some minor modifications to provide hooks for +multiple wallet support. Most importantly will be the way it instantiates WalletModels and passes them to the +singleton BitcoinGUI instance called window. Formerly, BitcoinGUI kept a pointer to a single instance of a WalletModel. +The initial change required is very simple: rather than calling `window.setWalletModel(&walletModel);` we perform the +following two steps: + + window.addWallet("~Default", &walletModel); + window.setCurrentWallet("~Default"); + +The string parameter is just an arbitrary name given to the default wallet. It's been prepended with a tilde to avoid name collisions in the future with additional wallets. + +The shutdown call `window.setWalletModel(0)` has also been removed. In its place is now: + +window.removeAllWallets(); diff --git a/doc/readme-qt.rst b/doc/readme-qt.rst new file mode 100644 index 0000000..c2d9101 --- /dev/null +++ b/doc/readme-qt.rst @@ -0,0 +1,163 @@ +CryptoLottoToken-Qt: Qt4 GUI for CryptoLottoToken +=============================== + +Build instructions +=================== + +Debian +------- + +First, make sure that the required packages for Qt4 development of your +distribution are installed, these are + +:: + +for Debian and Ubuntu <= 11.10 : + +:: + + apt-get install qt4-qmake libqt4-dev build-essential libboost-dev libboost-system-dev \ + libboost-filesystem-dev libboost-program-options-dev libboost-thread-dev \ + libssl-dev libdb4.8++-dev + +for Ubuntu >= 12.04 (please read the 'Berkely DB version warning' below): + +:: + + apt-get install qt4-qmake libqt4-dev build-essential libboost-dev libboost-system-dev \ + libboost-filesystem-dev libboost-program-options-dev libboost-thread-dev \ + libssl-dev libdb++-dev libminiupnpc-dev + +For Qt 5 you need the following, otherwise you get an error with lrelease when running qmake: + +:: + + apt-get install qt5-qmake libqt5gui5 libqt5core5 libqt5dbus5 qttools5-dev-tools + +then execute the following: + +:: + + qmake + make + +Alternatively, install `Qt Creator`_ and open the `CryptoLottoToken-qt.pro` file. + +An executable named `CryptoLottoToken-qt` will be built. + +.. _`Qt Creator`: http://qt-project.org/downloads/ + +Mac OS X +-------- + +- Download and install the `Qt Mac OS X SDK`_. It is recommended to also install Apple's Xcode with UNIX tools. + +- Download and install either `MacPorts`_ or `HomeBrew`_. + +- Execute the following commands in a terminal to get the dependencies using MacPorts: + +:: + + sudo port selfupdate + sudo port install boost db48 miniupnpc + +- Execute the following commands in a terminal to get the dependencies using HomeBrew: + +:: + + brew update + brew install boost miniupnpc openssl berkeley-db4 + +- If using HomeBrew, edit `CryptoLottoToken-qt.pro` to account for library location differences. There's a diff in `contrib/homebrew/bitcoin-qt-pro.patch` that shows what you need to change, or you can just patch by doing + + patch -p1 < contrib/homebrew/bitcoin.qt.pro.patch + +- Open the CryptoLottoToken-qt.pro file in Qt Creator and build as normal (cmd-B) + +.. _`Qt Mac OS X SDK`: http://qt-project.org/downloads/ +.. _`MacPorts`: http://www.macports.org/install.php +.. _`HomeBrew`: http://mxcl.github.io/homebrew/ + + +Build configuration options +============================ + +UPnP port forwarding +--------------------- + +To use UPnP for port forwarding behind a NAT router (recommended, as more connections overall allow for a faster and more stable CryptoLottoToken experience), pass the following argument to qmake: + +:: + + qmake "USE_UPNP=1" + +(in **Qt Creator**, you can find the setting for additional qmake arguments under "Projects" -> "Build Settings" -> "Build Steps", then click "Details" next to **qmake**) + +This requires miniupnpc for UPnP port mapping. It can be downloaded from +http://miniupnp.tuxfamily.org/files/. UPnP support is not compiled in by default. + +Set USE_UPNP to a different value to control this: + ++------------+--------------------------------------------------------------------------+ +| USE_UPNP=- | no UPnP support, miniupnpc not required; | ++------------+--------------------------------------------------------------------------+ +| USE_UPNP=0 | (the default) built with UPnP, support turned off by default at runtime; | ++------------+--------------------------------------------------------------------------+ +| USE_UPNP=1 | build with UPnP support turned on by default at runtime. | ++------------+--------------------------------------------------------------------------+ + +Notification support for recent (k)ubuntu versions +--------------------------------------------------- + +To see desktop notifications on (k)ubuntu versions starting from 10.04, enable usage of the +FreeDesktop notification interface through DBUS using the following qmake option: + +:: + + qmake "USE_DBUS=1" + +Generation of QR codes +----------------------- + +libqrencode may be used to generate QRCode images for payment requests. +It can be downloaded from http://fukuchi.org/works/qrencode/index.html.en, or installed via your package manager. Pass the USE_QRCODE +flag to qmake to control this: + ++--------------+--------------------------------------------------------------------------+ +| USE_QRCODE=0 | (the default) No QRCode support - libarcode not required | ++--------------+--------------------------------------------------------------------------+ +| USE_QRCODE=1 | QRCode support enabled | ++--------------+--------------------------------------------------------------------------+ + + +Berkely DB version warning +========================== + +A warning for people using the *static binary* version of CryptoLottoToken on a Linux/UNIX-ish system (tl;dr: **Berkely DB databases are not forward compatible**). + +The static binary version of CryptoLottoToken is linked against libdb4.8 (see also `this Debian issue`_). + +Now the nasty thing is that databases from 5.X are not compatible with 4.X. + +If the globally installed development package of Berkely DB installed on your system is 5.X, any source you +build yourself will be linked against that. The first time you run with a 5.X version the database will be upgraded, +and 4.X cannot open the new format. This means that you cannot go back to the old statically linked version without +significant hassle! + +.. _`this Debian issue`: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=621425 + +Ubuntu 11.10 warning +==================== + +Ubuntu 11.10 has a package called 'qt-at-spi' installed by default. At the time of writing, having that package +installed causes CryptoLottoToken-qt to crash intermittently. The issue has been reported as `launchpad bug 857790`_, but +isn't yet fixed. + +Until the bug is fixed, you can remove the qt-at-spi package to work around the problem, though this will presumably +disable screen reader functionality for Qt apps: + +:: + + sudo apt-get remove qt-at-spi + +.. _`launchpad bug 857790`: https://bugs.launchpad.net/ubuntu/+source/qt-at-spi/+bug/857790 diff --git a/doc/release-notes.md b/doc/release-notes.md new file mode 100644 index 0000000..f7efd33 --- /dev/null +++ b/doc/release-notes.md @@ -0,0 +1,76 @@ +0.8.6.1 changes +============= + +- Coin Control - experts only GUI selection of inputs before you send a transaction + +- Disable Wallet - reduces memory requirements, helpful for miner or relay nodes + +- 20x reduction in default mintxfee. + +- Up to 50% faster PoW validation, faster sync and reindexing. + +- Peers older than protocol version 70002 are disconnected. 0.8.3.7 is the oldest compatible client. + +- New RPC commands: getbestblockhash and verifychain + +- Improve fairness of the high priority transaction space per block + +- OSX block chain database corruption fixes + - Update leveldb to 1.13 + - Use fcntl with `F_FULLSYNC` instead of fsync on OSX + - Use native Darwin memory barriers + - Replace use of mmap in leveldb for improved reliability (only on OSX) + +- Fix nodes forwarding transactions with empty vins and getting banned + +- Network code performance and robustness improvements + +- Additional debug.log logging for diagnosis of network problems, log timestamps by default + +- Fix rare GUI crash on send + +0.8.5.1 changes +=============== + +Workaround negative version numbers serialization bug. + +Fix out-of-bounds check (CryptoLottoToken currently does not use this codepath, but we apply this +patch just to match Bitcoin 0.8.5.) + +0.8.4.1 changes +=============== + +CVE-2013-5700 Bloom: filter crash issue - CryptoLottoToken 0.8.3.7 disabled bloom by default so was +unaffected by this issue, but we include their patches anyway just in case folks want to +enable bloomfilter=1. + +CVE-2013-4165: RPC password timing guess vulnerability + +CVE-2013-4627: Better fix for the fill-memory-with-orphaned-tx attack + +Fix multi-block reorg transaction resurrection. + +Fix non-standard disconnected transactions causing mempool orphans. This bug could cause +nodes running with the -debug flag to crash, although it was lot less likely on CryptoLottoToken +as we disabled IsDust() in 0.8.3.x. + +Mac OSX: use 'FD_FULLSYNC' with LevelDB, which will (hopefully!) prevent the database +corruption issues have experienced on OSX. + +Add height parameter to getnetworkhashps. + +Fix Norwegian and Swedish translations. + +Minor efficiency improvement in block peer request handling. + + +0.8.3.7 changes +=============== + +Fix CVE-2013-4627 denial of service, a memory exhaustion attack that could crash low-memory nodes. + +Fix a regression that caused excessive writing of the peers.dat file. + +Add option for bloom filtering. + +Fix Hebrew translation. diff --git a/doc/release-process.md b/doc/release-process.md new file mode 100644 index 0000000..b77d066 --- /dev/null +++ b/doc/release-process.md @@ -0,0 +1,161 @@ +Release Process +==================== + +* * * + +###update (commit) version in sources + + + CryptoLottoToken-qt.pro + contrib/verifysfbinaries/verify.sh + doc/README* + share/setup.nsi + src/clientversion.h (change CLIENT_VERSION_IS_RELEASE to true) + +###tag version in git + + git tag -a v0.8.0 + +###write release notes. git shortlog helps a lot, for example: + + git shortlog --no-merges v0.7.2..v0.8.0 + +* * * + +##perform gitian builds + + From a directory containing the CryptoLottoToken source, gitian-builder and gitian.sigs + + export SIGNER=(your gitian key, ie bluematt, sipa, etc) + export VERSION=0.8.0 + cd ./gitian-builder + + Fetch and build inputs: (first time, or when dependency versions change) + + mkdir -p inputs; cd inputs/ + wget 'http://miniupnp.free.fr/files/download.php?file=miniupnpc-1.6.tar.gz' -O miniupnpc-1.6.tar.gz + wget 'http://www.openssl.org/source/openssl-1.0.1c.tar.gz' + wget 'http://download.oracle.com/berkeley-db/db-4.8.30.NC.tar.gz' + wget 'http://zlib.net/zlib-1.2.6.tar.gz' + wget 'ftp://ftp.simplesystems.org/pub/libpng/png/src/libpng-1.5.9.tar.gz' + wget 'http://fukuchi.org/works/qrencode/qrencode-3.2.0.tar.bz2' + wget 'http://downloads.sourceforge.net/project/boost/boost/1.50.0/boost_1_50_0.tar.bz2' + wget 'http://releases.qt-project.org/qt4/source/qt-everywhere-opensource-src-4.8.3.tar.gz' + cd .. + ./bin/gbuild ../CryptoLottoToken/contrib/gitian-descriptors/boost-win32.yml + mv build/out/boost-win32-1.50.0-gitian2.zip inputs/ + ./bin/gbuild ../CryptoLottoToken/contrib/gitian-descriptors/qt-win32.yml + mv build/out/qt-win32-4.8.3-gitian-r1.zip inputs/ + ./bin/gbuild ../CryptoLottoToken/contrib/gitian-descriptors/deps-win32.yml + mv build/out/CryptoLottoToken-deps-0.0.5.zip inputs/ + + Build CryptoLottoTokend and CryptoLottoToken-qt on Linux32, Linux64, and Win32: + + ./bin/gbuild --commit CryptoLottoToken=v${VERSION} ../CryptoLottoToken/contrib/gitian-descriptors/gitian.yml + ./bin/gsign --signer $SIGNER --release ${VERSION} --destination ../gitian.sigs/ ../CryptoLottoToken/contrib/gitian-descriptors/gitian.yml + pushd build/out + zip -r CryptoLottoToken-${VERSION}-linux-gitian.zip * + mv CryptoLottoToken-${VERSION}-linux-gitian.zip ../../ + popd + ./bin/gbuild --commit CryptoLottoToken=v${VERSION} ../CryptoLottoToken/contrib/gitian-descriptors/gitian-win32.yml + ./bin/gsign --signer $SIGNER --release ${VERSION}-win32 --destination ../gitian.sigs/ ../CryptoLottoToken/contrib/gitian-descriptors/gitian-win32.yml + pushd build/out + zip -r CryptoLottoToken-${VERSION}-win32-gitian.zip * + mv CryptoLottoToken-${VERSION}-win32-gitian.zip ../../ + popd + + Build output expected: + + 1. linux 32-bit and 64-bit binaries + source (CryptoLottoToken-${VERSION}-linux-gitian.zip) + 2. windows 32-bit binary, installer + source (CryptoLottoToken-${VERSION}-win32-gitian.zip) + 3. Gitian signatures (in gitian.sigs/${VERSION}[-win32]/(your gitian key)/ + +repackage gitian builds for release as stand-alone zip/tar/installer exe + +**Linux .tar.gz:** + + unzip CryptoLottoToken-${VERSION}-linux-gitian.zip -d CryptoLottoToken-${VERSION}-linux + tar czvf CryptoLottoToken-${VERSION}-linux.tar.gz CryptoLottoToken-${VERSION}-linux + rm -rf CryptoLottoToken-${VERSION}-linux + +**Windows .zip and setup.exe:** + + unzip CryptoLottoToken-${VERSION}-win32-gitian.zip -d CryptoLottoToken-${VERSION}-win32 + mv CryptoLottoToken-${VERSION}-win32/CryptoLottoToken-*-setup.exe . + zip -r CryptoLottoToken-${VERSION}-win32.zip bitcoin-${VERSION}-win32 + rm -rf CryptoLottoToken-${VERSION}-win32 + +**Perform Mac build:** + + OSX binaries are created by Gavin Andresen on a 32-bit, OSX 10.6 machine. + + qmake RELEASE=1 USE_UPNP=1 USE_QRCODE=1 CryptoLottoToken-qt.pro + make + export QTDIR=/opt/local/share/qt4 # needed to find translations/qt_*.qm files + T=$(contrib/qt_translations.py $QTDIR/translations src/qt/locale) + python2.7 share/qt/clean_mac_info_plist.py + python2.7 contrib/macdeploy/macdeployqtplus Bitcoin-Qt.app -add-qt-tr $T -dmg -fancy contrib/macdeploy/fancy.plist + + Build output expected: Bitcoin-Qt.dmg + +###Next steps: + +* Code-sign Windows -setup.exe (in a Windows virtual machine) and + OSX Bitcoin-Qt.app (Note: only Gavin has the code-signing keys currently) + +* upload builds to SourceForge + +* create SHA256SUMS for builds, and PGP-sign it + +* update CryptoLottoToken.org version + make sure all OS download links go to the right versions + +* update forum version + +* update wiki download links + +* update wiki changelog: [https://en.CryptoLottoToken.it/wiki/Changelog](https://en.bitcoin.it/wiki/Changelog) + +Commit your signature to gitian.sigs: + + pushd gitian.sigs + git add ${VERSION}/${SIGNER} + git add ${VERSION}-win32/${SIGNER} + git commit -a + git push # Assuming you can push to the gitian.sigs tree + popd + +------------------------------------------------------------------------- + +### After 3 or more people have gitian-built, repackage gitian-signed zips: + +From a directory containing CryptoLottoToken source, gitian.sigs and gitian zips + + export VERSION=0.5.1 + mkdir CryptoLottoToken-${VERSION}-linux-gitian + pushd CryptoLottoToken-${VERSION}-linux-gitian + unzip ../CryptoLottoToken-${VERSION}-linux-gitian.zip + mkdir gitian + cp ../CryptoLottoToken/contrib/gitian-downloader/*.pgp ./gitian/ + for signer in $(ls ../gitian.sigs/${VERSION}/); do + cp ../gitian.sigs/${VERSION}/${signer}/CryptoLottoToken-build.assert ./gitian/${signer}-build.assert + cp ../gitian.sigs/${VERSION}/${signer}/CryptoLottoToken-build.assert.sig ./gitian/${signer}-build.assert.sig + done + zip -r CryptoLottoToken-${VERSION}-linux-gitian.zip * + cp CryptoLottoToken-${VERSION}-linux-gitian.zip ../ + popd + mkdir CryptoLottoToken-${VERSION}-win32-gitian + pushd CryptoLottoToken-${VERSION}-win32-gitian + unzip ../CryptoLottoToken-${VERSION}-win32-gitian.zip + mkdir gitian + cp ../CryptoLottoToken/contrib/gitian-downloader/*.pgp ./gitian/ + for signer in $(ls ../gitian.sigs/${VERSION}-win32/); do + cp ../gitian.sigs/${VERSION}-win32/${signer}/CryptoLottoToken-build.assert ./gitian/${signer}-build.assert + cp ../gitian.sigs/${VERSION}-win32/${signer}/CryptoLottoToken-build.assert.sig ./gitian/${signer}-build.assert.sig + done + zip -r CryptoLottoToken-${VERSION}-win32-gitian.zip * + cp CryptoLottoToken-${VERSION}-win32-gitian.zip ../ + popd + +- Upload gitian zips to SourceForge +- Celebrate diff --git a/doc/translation_process.md b/doc/translation_process.md new file mode 100644 index 0000000..1724e95 --- /dev/null +++ b/doc/translation_process.md @@ -0,0 +1,105 @@ +Translations +============ + +The Qt GUI can be easily translated into other languages. Here's how we +handle those translations. + +Files and Folders +----------------- + +### bitcoin-qt.pro + +This file takes care of generating `.qm` files from `.ts` files. It is mostly +automated. + +### src/qt/bitcoin.qrc + +This file must be updated whenever a new translation is added. Please note that +files must end with `.qm`, not `.ts`. + + + locale/bitcoin_en.qm + ... + + +### src/qt/locale/ + +This directory contains all translations. Filenames must adhere to this format: + + bitcoin_xx_YY.ts or bitcoin_xx.ts + +#### bitcoin_en.ts (Source file) + +`src/qt/locale/bitcoin_en.ts` is treated in a special way. It is used as the +source for all other translations. Whenever a string in the code is changed +this file must be updated to reflect those changes. This can be accomplished +by running `lupdate` (included in the Qt SDK). Also, a custom script is used +to extract strings from the non-Qt parts. This script makes use of `gettext`, +so make sure that utility is installed (ie, `apt-get install gettext` on +Ubuntu/Debian): + + python share/qt/extract_strings_qt.py + lupdate bitcoin-qt.pro -no-obsolete -locations relative -ts src/qt/locale/bitcoin_en.ts + +##### Handling of plurals in the source file + +When new plurals are added to the source file, it's important to do the following steps: + +1. Open bitcoin_en.ts in Qt Linguist (also included in the Qt SDK) +2. Search for `%n`, which will take you to the parts in the translation that use plurals +3. Look for empty `English Translation (Singular)` and `English Translation (Plural)` fields +4. Add the appropriate strings for the singular and plural form of the base string +5. Mark the item as done (via the green arrow symbol in the toolbar) +6. Repeat from step 2. until all singular and plural forms are in the source file +7. Save the source file + +##### Creating the pull-request + +An updated source file should be merged to github and Transifex will pick it +up from there (can take some hours). Afterwards the new strings show up as "Remaining" +in Transifex and can be translated. + +To create the pull-request you have to do: + + git add src/qt/bitcoinstrings.cpp src/qt/locale/bitcoin_en.ts + git commit + +Syncing with Transifex +---------------------- + +We are using https://transifex.com as a frontend for translating the client. + +https://www.transifex.com/projects/p/bitcoin/resource/tx/ + +The "Transifex client" (see: http://help.transifex.com/features/client/) +will help with fetching new translations from Transifex. Use the following +config to be able to connect with the client: + +### .tx/config + + [main] + host = https://www.transifex.com + + [bitcoin.tx] + file_filter = src/qt/locale/bitcoin_.ts + source_file = src/qt/locale/bitcoin_en.ts + source_lang = en + +### .tx/config (for Windows) + + [main] + host = https://www.transifex.com + + [bitcoin.tx] + file_filter = src\qt\locale\bitcoin_.ts + source_file = src\qt\locale\bitcoin_en.ts + source_lang = en + +It is also possible to directly download new translations one by one from the Transifex website. + +### Fetching new translations + +1. `tx pull -a` +2. update `src/qt/bitcoin.qrc` manually or via + `ls src/qt/locale/*ts|xargs -n1 basename|sed 's/\(bitcoin_\(.*\)\).ts/locale/\1.qm<\/file>/'` +3. `git add` new translations from `src/qt/locale/` diff --git a/doc/unit-tests.md b/doc/unit-tests.md new file mode 100644 index 0000000..5b79e26 --- /dev/null +++ b/doc/unit-tests.md @@ -0,0 +1,35 @@ +Compiling/running CryptoLottoTokend unit tests +------------------------------------ + +CryptoLottoTokend unit tests are in the `src/test/` directory; they +use the Boost::Test unit-testing framework. + +To compile and run the tests: + + cd src + make -f makefile.unix test_CryptoLottoToken # Replace makefile.unix if you're not on unix + ./test_CryptoLottoToken # Runs the unit tests + +If all tests succeed the last line of output will be: +`*** No errors detected` + +To add more tests, add `BOOST_AUTO_TEST_CASE` functions to the existing +.cpp files in the test/ directory or add new .cpp files that +implement new BOOST_AUTO_TEST_SUITE sections (the makefiles are +set up to add test/*.cpp to test_CryptoLottoToken automatically). + + +Compiling/running CryptoLottoToken-Qt unit tests +--------------------------------------- + +Bitcoin-Qt unit tests are in the src/qt/test/ directory; they +use the Qt unit-testing framework. + +To compile and run the tests: + + qmake bitcoin-qt.pro BITCOIN_QT_TEST=1 + make + ./CryptoLottoToken-qt_test + +To add more tests, add them to the `src/qt/test/` directory, +the `src/qt/test/test_main.cpp` file, and bitcoin-qt.pro. diff --git a/share/certs/BitcoinFoundation_Apple_Cert.pem b/share/certs/BitcoinFoundation_Apple_Cert.pem new file mode 100644 index 0000000..beb0d70 --- /dev/null +++ b/share/certs/BitcoinFoundation_Apple_Cert.pem @@ -0,0 +1,37 @@ +Bag Attributes + friendlyName: Developer ID Application: BITCOIN FOUNDATION, INC., THE + localKeyID: 6B 9C 6C A8 A5 73 70 70 E2 57 A3 49 D8 62 FB 97 C7 A5 5D 5E +subject=/UID=PBV4GLS9J4/CN=Developer ID Application: BITCOIN FOUNDATION, INC., THE/OU=PBV4GLS9J4/O=BITCOIN FOUNDATION, INC., THE/C=US +issuer=/CN=Developer ID Certification Authority/OU=Apple Certification Authority/O=Apple Inc./C=US +-----BEGIN CERTIFICATE----- +MIIFhzCCBG+gAwIBAgIIJ0r1rumyfZAwDQYJKoZIhvcNAQELBQAweTEtMCsGA1UE +AwwkRGV2ZWxvcGVyIElEIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSYwJAYDVQQL +DB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUg +SW5jLjELMAkGA1UEBhMCVVMwHhcNMTMwMTEwMjIzOTAxWhcNMTgwMTExMjIzOTAx +WjCBqDEaMBgGCgmSJomT8ixkAQEMClBCVjRHTFM5SjQxQDA+BgNVBAMMN0RldmVs +b3BlciBJRCBBcHBsaWNhdGlvbjogQklUQ09JTiBGT1VOREFUSU9OLCBJTkMuLCBU +SEUxEzARBgNVBAsMClBCVjRHTFM5SjQxJjAkBgNVBAoMHUJJVENPSU4gRk9VTkRB +VElPTiwgSU5DLiwgVEhFMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBALTd5zURuZVoJviusr119aktXksenb9IN9vq6kBbq38vxEk7 +9wkKMES2XfBRh0HxcEizGzhMNy5OCXuTLMaNMihYdfwYSoBoR2foEU+6kjPUnyJ4 +dQBFLJZJr5/QeQmALmYHEgZ6lwXFD2lU8t92340zeJ4y5LZw5pcEHtH9IummYDut +OGCkCGXDcjL+5nHhNScJiXHhswM+62o6XXsQiP6EWbM1CsgrGTNLtaa0U/UvVDwE +79YKklSC5Bog2LD0jBcTuveI66mFzqu++L9X9u+ZArtebwCl7BPNQ+uboYy5uV2d +zf8lpNNZLfXCFjoLe9bLICKfZ7ub9V5aC8+GhckCAwEAAaOCAeEwggHdMD4GCCsG +AQUFBwEBBDIwMDAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuYXBwbGUuY29tL29j +c3AtZGV2aWQwMTAdBgNVHQ4EFgQUa5xsqKVzcHDiV6NJ2GL7l8elXV4wDAYDVR0T +AQH/BAIwADAfBgNVHSMEGDAWgBRXF+2iz9x8mKEQ4Py+hy0s8uMXVDCCAQ4GA1Ud +IASCAQUwggEBMIH+BgkqhkiG92NkBQEwgfAwKAYIKwYBBQUHAgEWHGh0dHA6Ly93 +d3cuYXBwbGUuY29tL2FwcGxlY2EwgcMGCCsGAQUFBwICMIG2DIGzUmVsaWFuY2Ug +b24gdGhpcyBjZXJ0aWZpY2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2NlcHRh +bmNlIG9mIHRoZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNv +bmRpdGlvbnMgb2YgdXNlLCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGNlcnRpZmlj +YXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wDgYDVR0PAQH/BAQDAgeAMBYGA1Ud +JQEB/wQMMAoGCCsGAQUFBwMDMBMGCiqGSIb3Y2QGAQ0BAf8EAgUAMA0GCSqGSIb3 +DQEBCwUAA4IBAQAfJ0BjID/1dS2aEeVyhAzPzCBjG8vm0gDf+/qfwRn3+yWeL9vS +nMdbilwM48IyQWTagjGGcojbsAd/vE4N7NhQyHInoCllNoeor1I5xx+blTaGRBK+ +dDhJbbdlGCjsLnH/BczGZi5fyEJds9lUIrp1hJidRcUKO76qb/9gc6qNZpl1vH5k +lDUuJYt7YhAs+L6rTXDyqcK9maeQr0gaOPsRRAQLLwiQCorPeMTUNsbVMdMwZYJs +R+PxiAnk+nyi7rfiFvPoASAYUuI6OzYL/Fa6QU4/gYyPgic944QYVkaQBnc0vEP1 +nXq6LGKwgVGcqJnkr/E2kui5gJoV5C3qll3e +-----END CERTIFICATE----- diff --git a/share/certs/BitcoinFoundation_Comodo_Cert.pem b/share/certs/BitcoinFoundation_Comodo_Cert.pem new file mode 100644 index 0000000..dc752d4 --- /dev/null +++ b/share/certs/BitcoinFoundation_Comodo_Cert.pem @@ -0,0 +1,37 @@ +Bag Attributes + friendlyName: The Bitcoin Foundation, Inc.'s COMODO CA Limited ID + localKeyID: 8C 94 64 E3 B5 B0 41 89 5B 89 B0 57 CC 74 B9 44 E5 B2 92 66 +subject=/C=US/postalCode=98104-1444/ST=WA/L=Seattle/street=Suite 300/street=71 Columbia St/O=The Bitcoin Foundation, Inc./CN=The Bitcoin Foundation, Inc. +issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO Code Signing CA 2 +-----BEGIN CERTIFICATE----- +MIIFeDCCBGCgAwIBAgIRAJVYMd+waOER7lUqtiz3M2IwDQYJKoZIhvcNAQEFBQAw +ezELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxITAfBgNV +BAMTGENPTU9ETyBDb2RlIFNpZ25pbmcgQ0EgMjAeFw0xMzAxMTYwMDAwMDBaFw0x +NDAxMTYyMzU5NTlaMIG8MQswCQYDVQQGEwJVUzETMBEGA1UEEQwKOTgxMDQtMTQ0 +NDELMAkGA1UECAwCV0ExEDAOBgNVBAcMB1NlYXR0bGUxEjAQBgNVBAkMCVN1aXRl +IDMwMDEXMBUGA1UECQwONzEgQ29sdW1iaWEgU3QxJTAjBgNVBAoMHFRoZSBCaXRj +b2luIEZvdW5kYXRpb24sIEluYy4xJTAjBgNVBAMMHFRoZSBCaXRjb2luIEZvdW5k +YXRpb24sIEluYy4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQChUwLD +u/hu5aFZ/n11B27awONaaDrmHm0pamiWHb01yL4JmTBtaLCrSftF8RhCscQ8jpI0 +UG1Cchmay0e3zH5o5XRs0H9C3x+SM5ozms0TWDmAYiB8aQEghsGovDk0D2nyTQeK +Q0xqyCh0m8ZPOnMnYrakHEmF6WvhLdJvI6Od4KIwbKxgN17cPFIfLVsZ7GrzmmbU +Gdi4wSQCHy5rxzvBxho8Qq/SfBl93uOMUrqOHjOUAPhNuTJG3t/MdhU8Zp24s29M +abHtYkT9W86hMjIiI8RTAR+WHKVglx9SB0cjDabXN8SZ3gME0+H++LyzlySHT8sI +ykepojZ7UBRgp9w3AgMBAAGjggGzMIIBrzAfBgNVHSMEGDAWgBQexbEsfYfaAmh8 +JbwMB4Q/ts/e8TAdBgNVHQ4EFgQUfPf+ZyDWl/4LH0Y5BuJTelkRd/EwDgYDVR0P +AQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEQYJ +YIZIAYb4QgEBBAQDAgQQMEYGA1UdIAQ/MD0wOwYMKwYBBAGyMQECAQMCMCswKQYI +KwYBBQUHAgEWHWh0dHBzOi8vc2VjdXJlLmNvbW9kby5uZXQvQ1BTMEEGA1UdHwQ6 +MDgwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0NPTU9ET0NvZGVTaWdu +aW5nQ0EyLmNybDByBggrBgEFBQcBAQRmMGQwPAYIKwYBBQUHMAKGMGh0dHA6Ly9j +cnQuY29tb2RvY2EuY29tL0NPTU9ET0NvZGVTaWduaW5nQ0EyLmNydDAkBggrBgEF +BQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMCgGA1UdEQQhMB+BHWxpbmRz +YXlAYml0Y29pbmZvdW5kYXRpb24ub3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAqibjo +D4HG5XSIIMCmYE5RgQBSEAJfI+EZERk1G9F83ZUWr0yNRZCw4O+RaM7xQhvJhEoD +G2kpk/q2bNOc71/VyZ6SrE1JRVUON41/Flhz4M6cP0BclTicXvh+efVwqZhIz+ws +UxF2hvC/1Xx6rqI7NYAlOYXk2MSUq3HREo+gWUPKM8em4MZZV/7XCH4QbsfxOl1J +xS6EOQmV8hfUN4KRXI5WfGUmedBxq7dM0RSJOSQl8fq2f+JjRLfjQwQucy7LDY+y +pRTsL2TdQV/DuDuI3s0NHRGznQNddoX5jqpXhSQFAAdgrhN1gGkWaaTPzr9IF2TG +qgr6PEp9tIYC+MbM +-----END CERTIFICATE----- diff --git a/share/certs/PrivateKeyNotes.md b/share/certs/PrivateKeyNotes.md new file mode 100644 index 0000000..da299d1 --- /dev/null +++ b/share/certs/PrivateKeyNotes.md @@ -0,0 +1,46 @@ +Code-signing private key notes +== + +The private keys for these certificates were generated on Gavin's main work machine, +following the certificate authoritys' recommendations for generating certificate +signing requests. + +For OSX, the private key was generated by Keychain.app on Gavin's main work machine. +The key and certificate is in a separate, passphrase-protected keychain file that is +unlocked to sign the Bitcoin-Qt.app bundle. + +For Windows, the private key was generated by Firefox running on Gavin's main work machine. +The key and certificate were exported into a separate, passphrase-protected PKCS#12 file, and +then deleted from Firefox's keystore. The exported file is used to sign the Windows setup.exe. + +Threat analysis +-- + +Gavin is a single point of failure. He could be coerced to divulge the secret signing keys, +allowing somebody to distribute a Bitcoin-Qt.app or bitcoin-qt-setup.exe with a valid +signature but containing a malicious binary. + +Or the machine Gavin uses to sign the binaries could be compromised, either remotely or +by breaking in to his office, allowing the attacker to get the private key files and then +install a keylogger to get the passphrase that protects them. + +Threat Mitigation +-- + +"Air gapping" the machine used to do the signing will not work, because the signing +process needs to access a timestamp server over the network. And it would not +prevent the "rubber hose cryptography" threat (coercing Gavin to sign a bad binary +or divulge the private keys). + +Windows binaries are reproducibly 'gitian-built', and the setup.exe file created +by the NSIS installer system is a 7zip archive, so you could check to make sure +that the bitcoin-qt.exe file inside the installer had not been tampered with. +However, an attacker could modify the installer's code, so when the setup.exe +was run it compromised users' systems. A volunteer to write an auditing tool +that checks the setup.exe for tampering, and checks the files in it against +the list of gitian signatures, is needed. + +The long-term solution is something like the 'gitian downloader' system, which +uses signatures from multiple developers to determine whether or not a binary +should be trusted. However, that just pushes the problem to "how will +non-technical users securely get the gitian downloader code to start?" diff --git a/share/genbuild.sh b/share/genbuild.sh new file mode 100644 index 0000000..d959877 --- /dev/null +++ b/share/genbuild.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +if [ $# -gt 0 ]; then + FILE="$1" + shift + if [ -f "$FILE" ]; then + INFO="$(head -n 1 "$FILE")" + fi +else + echo "Usage: $0 " + exit 1 +fi + +if [ -e "$(which git)" ]; then + # clean 'dirty' status of touched files that haven't been modified + git diff >/dev/null 2>/dev/null + + # get a string like "v0.6.0-66-g59887e8-dirty" + DESC="$(git describe --dirty 2>/dev/null)" + + # get a string like "2012-04-10 16:27:19 +0200" + TIME="$(git log -n 1 --format="%ci")" +fi + +if [ -n "$DESC" ]; then + NEWINFO="#define BUILD_DESC \"$DESC\"" +else + NEWINFO="// No build information available" +fi + +# only update build.h if necessary +if [ "$INFO" != "$NEWINFO" ]; then + echo "$NEWINFO" >"$FILE" + echo "#define BUILD_DATE \"$TIME\"" >>"$FILE" +fi diff --git a/share/pixmaps/addressbook16.bmp b/share/pixmaps/addressbook16.bmp new file mode 100644 index 0000000..c557691 Binary files /dev/null and b/share/pixmaps/addressbook16.bmp differ diff --git a/share/pixmaps/addressbook16mask.bmp b/share/pixmaps/addressbook16mask.bmp new file mode 100644 index 0000000..d3a478d Binary files /dev/null and b/share/pixmaps/addressbook16mask.bmp differ diff --git a/share/pixmaps/addressbook20.bmp b/share/pixmaps/addressbook20.bmp new file mode 100644 index 0000000..2b33b22 Binary files /dev/null and b/share/pixmaps/addressbook20.bmp differ diff --git a/share/pixmaps/addressbook20mask.bmp b/share/pixmaps/addressbook20mask.bmp new file mode 100644 index 0000000..56ce612 Binary files /dev/null and b/share/pixmaps/addressbook20mask.bmp differ diff --git a/share/pixmaps/bitcoin-bc.ico b/share/pixmaps/bitcoin-bc.ico new file mode 100644 index 0000000..83a01ea Binary files /dev/null and b/share/pixmaps/bitcoin-bc.ico differ diff --git a/share/pixmaps/bitcoin.ico b/share/pixmaps/bitcoin.ico new file mode 100644 index 0000000..3753370 Binary files /dev/null and b/share/pixmaps/bitcoin.ico differ diff --git a/share/pixmaps/bitcoin128.png b/share/pixmaps/bitcoin128.png new file mode 100644 index 0000000..e91daee Binary files /dev/null and b/share/pixmaps/bitcoin128.png differ diff --git a/share/pixmaps/bitcoin128.xpm b/share/pixmaps/bitcoin128.xpm new file mode 100644 index 0000000..c4593ff --- /dev/null +++ b/share/pixmaps/bitcoin128.xpm @@ -0,0 +1,1084 @@ +/* XPM */ +static char *graphic[] = { +/* width height num_colors chars_per_pixel */ +"128 128 949 2", +/* colors */ +" c None", +". c None", +"X c #444444", +"o c None", +"O c #666666", +"+ c None", +"@ c None", +"# c #888888", +"$ c None", +"% c None", +"& c None", +"* c #AAAAAA", +"= c #404040", +"- c #CCCCCC", +"; c None", +": c #686868", +"> c None", +", c #EEEEEE", +"< c #5D5D5D", +"1 c #595959", +"2 c #404040", +"3 c #4B4B4B", +"4 c None", +"5 c #313131", +"6 c #404040", +"7 c #707070", +"8 c None", +"9 c None", +"0 c None", +"q c #535353", +"w c #404040", +"e c #757575", +"r c #525252", +"t c #5A5A5A", +"y c #686868", +"u c #4B4B4B", +"i c #404040", +"p c #979797", +"a c #616161", +"s c None", +"d c #5A5A5A", +"f c #B9B9B9", +"g c #404040", +"h c #DBDBDB", +"j c #565656", +"k c None", +"l c None", +"z c #FDFDFD", +"x c #5A5A5A", +"c c #5A5A5A", +"v c #4B4B4B", +"b c #404040", +"n c #1E1E1E", +"m c #646464", +"M c #404040", +"N c #595959", +"B c #4F4F4F", +"V c #626262", +"C c #525252", +"Z c #4F4F4F", +"A c None", +"S c #848484", +"D c None", +"F c #4F4F4F", +"G c #525252", +"H c #A6A6A6", +"J c #595959", +"K c #4B4B4B", +"L c None", +"P c None", +"I c #C8C8C8", +"U c #707070", +"Y c #646464", +"T c None", +"R c #525252", +"E c #EAEAEA", +"W c #4B4B4B", +"Q c #707070", +"! c None", +"~ c None", +"^ c #595959", +"/ c None", +"( c None", +") c #2D2D2D", +"_ c #4F4F4F", +"` c #4F4F4F", +"' c None", +"] c #717171", +"[ c #3C3C3C", +"{ c None", +"} c #565656", +"| c #4B4B4B", +" . c None", +".. c #939393", +"X. c #474747", +"o. c #3C3C3C", +"O. c #525252", +"+. c #B5B5B5", +"@. c #3C3C3C", +"#. c #D7D7D7", +"$. c #3C3C3C", +"%. c #F9F9F9", +"&. c #565656", +"*. c #3C3C3C", +"=. c #555555", +"-. c None", +";. c #5D5D5D", +":. c #565656", +">. c #3C3C3C", +",. c #595959", +"<. c None", +"1. c None", +"2. c #474747", +"3. c #5E5E5E", +"4. c #565656", +"5. c #808080", +"6. c #4B4B4B", +"7. c #525252", +"8. c #A2A2A2", +"9. c #4B4B4B", +"0. c None", +"q. c #C4C4C4", +"w. c #5D5D5D", +"e. c None", +"r. c None", +"t. c #606060", +"y. c #E6E6E6", +"u. c None", +"i. c #4B4B4B", +"p. c None", +"a. c #595959", +"s. c None", +"d. c #4B4B4B", +"f. c None", +"g. c None", +"h. c #606060", +"j. c #292929", +"k. c #565656", +"l. c #4B4B4B", +"z. c #474747", +"x. c #595959", +"c. c #6D6D6D", +"v. c None", +"b. c #8F8F8F", +"n. c None", +"m. c None", +"M. c #B1B1B1", +"N. c #383838", +"B. c #D3D3D3", +"V. c None", +"C. c #383838", +"Z. c None", +"A. c #6C6C6C", +"S. c #F5F5F5", +"D. c #606060", +"F. c None", +"G. c #383838", +"H. c #565656", +"J. c None", +"K. c #383838", +"L. c #4E4E4E", +"P. c None", +"I. c #474747", +"U. c #5A5A5A", +"Y. c #4E4E4E", +"T. c #555555", +"R. c #565656", +"E. c #595959", +"W. c #7C7C7C", +"Q. c #9E9E9E", +"!. c #434343", +"~. c None", +"^. c None", +"/. c #595959", +"(. c #C0C0C0", +"). c #474747", +"_. c #555555", +"`. c #E2E2E2", +"'. c None", +"]. c #606060", +"[. c #5C5C5C", +"{. c #474747", +"}. c #525252", +"|. c #515151", +" X c #525252", +".X c #252525", +"XX c #474747", +"oX c #474747", +"OX c #5C5C5C", +"+X c #555555", +"@X c #5D5D5D", +"#X c #696969", +"$X c #8B8B8B", +"%X c None", +"&X c #595959", +"*X c None", +"=X c #525252", +"-X c #4E4E4E", +";X c #ADADAD", +":X c None", +">X c None", +",X c #CFCFCF", +"o c #3F3F3F", +",o c None", +"O c #585858", +",O c #606060", +"+ c #515151", +",+ c #5C5C5C", +"<+ c #424242", +"1+ c #333333", +"2+ c None", +"3+ c None", +"4+ c #515151", +"5+ c #5C5C5C", +"6+ c #555555", +"7+ c #6A6A6A", +"8+ c #777777", +"9+ c None", +"0+ c None", +"q+ c #494949", +"w+ c #999999", +"e+ c #545454", +"r+ c #BBBBBB", +"t+ c #424242", +"y+ c #585858", +"u+ c None", +"i+ c #5B5B5B", +"p+ c #DDDDDD", +"a+ c #3E3E3E", +"s+ c #424242", +"d+ c #515151", +"f+ c None", +"g+ c #FFFFFF", +"h+ c #3E3E3E", +"j+ c #505050", +"k+ c #6E6E6E", +"l+ c #585858", +"z+ c None", +"x+ c #515151", +"c+ c None", +"v+ c #4D4D4D", +"b+ c #202020", +"n+ c #424242", +"m+ c None", +"M+ c #424242", +"N+ c #515151", +"B+ c None", +"V+ c None", +"C+ c #646464", +"Z+ c #545454", +"A+ c #4D4D4D", +"S+ c None", +"D+ c #515151", +"F+ c #676767", +"G+ c None", +"H+ c #868686", +"J+ c None", +"K+ c #4D4D4D", +"L+ c #515151", +"P+ c #676767", +"I+ c #A8A8A8", +"U+ c None", +"Y+ c #CACACA", +"T+ c #494949", +"R+ c #ECECEC", +"E+ c #3E3E3E", +"W+ c None", +"Q+ c None", +"!+ c #494949", +"~+ c None", +"^+ c None", +"/+ c #3E3E3E", +"(+ c None", +")+ c #2F2F2F", +"_+ c #515151", +"`+ c None", +"'+ c None", +"]+ c #3E3E3E", +"[+ c None", +"{+ c #737373", +"}+ c #3E3E3E", +"|+ c #545454", +" @ c #585858", +".@ c #959595", +"X@ c None", +"o@ c #585858", +"O@ c None", +"+@ c #B7B7B7", +"@@ c None", +"#@ c #3E3E3E", +"$@ c #494949", +"%@ c #D9D9D9", +"&@ c None", +"*@ c #585858", +"=@ c #3E3E3E", +"-@ c #FBFBFB", +";@ c None", +":@ c #545454", +">@ c #585858", +",@ c #4D4D4D", +"<@ c None", +"1@ c None", +"2@ c #3E3E3E", +"3@ c #494949", +"4@ c #494949", +"5@ c #3E3E3E", +"6@ c #585858", +"7@ c #606060", +"8@ c None", +"9@ c #585858", +"0@ c #828282", +"q@ c None", +"w@ c None", +"e@ c #4D4D4D", +"r@ c #A4A4A4", +"t@ c #4D4D4D", +"y@ c None", +"u@ c #C6C6C6", +"i@ c #5B5B5B", +"p@ c #E8E8E8", +"a@ c #505050", +"s@ c #6E6E6E", +"d@ c None", +"f@ c None", +"g@ c #494949", +"h@ c #454545", +"j@ c None", +"k@ c #2B2B2B", +"l@ c #5B5B5B", +"z@ c #4D4D4D", +"x@ c #6F6F6F", +"c@ c None", +"v@ c #919191", +"b@ c #494949", +"n@ c #545454", +"m@ c #5B5B5B", +"M@ c #B3B3B3", +"N@ c None", +"B@ c #D5D5D5", +"V@ c None", +"C@ c None", +"Z@ c #3A3A3A", +"A@ c #585858", +"S@ c #545454", +"D@ c #3A3A3A", +"F@ c #F7F7F7", +"G@ c #575757", +"H@ c #3A3A3A", +"J@ c #575757", +"K@ c #494949", +"L@ c #181818", +"P@ c #3A3A3A", +"I@ c None", +"U@ c #454545", +"Y@ c #535353", +"T@ c #494949", +"R@ c #585858", +"E@ c #5C5C5C", +"W@ c #7E7E7E", +"Q@ c #505050", +"!@ c #A0A0A0", +"~@ c None", +"^@ c None", +"/@ c None", +"(@ c #5E5E5E", +")@ c #C2C2C2", +"_@ c #575757", +"`@ c #494949", +"'@ c #E4E4E4", +"]@ c None", +"[@ c None", +"{@ c #454545", +"}@ c #454545", +"|@ c #5E5E5E", +" # c None", +".# c #5F5F5F", +"X# c #272727", +"o# c #575757", +"O# c #494949", +"+# c None", +"@# c #6B6B6B", +"## c #454545", +"$# c #4C4C4C", +"%# c #8D8D8D", +"&# c #454545", +"*# c #575757", +"=# c None", +"-# c #363636", +";# c None", +":# c #AFAFAF", +"># c #454545", +",# c #535353", +"<# c #D1D1D1", +"1# c None", +"2# c #505050", +"3# c None", +"4# c None", +"5# c #F3F3F3", +"6# c None", +"7# c #5E5E5E", +"8# c #454545", +"9# c #363636", +"0# c None", +"q# c #363636", +"w# c None", +"e# c #5E5E5E", +"r# c #585858", +"t# c #575757", +"y# c #535353", +"u# c None", +"i# c #4C4C4C", +"p# c #454545", +"a# c #7A7A7A", +"s# c #454545", +"d# c #5A5A5A", +"f# c #4C4C4C", +"g# c #9C9C9C", +"h# c None", +"j# c None", +"k# c #BEBEBE", +"l# c #6A6A6A", +"z# c #E0E0E0", +"x# c #5E5E5E", +"c# c #454545", +"v# c #545454", +"b# c None", +"n# c None", +"m# c #5E5E5E", +"M# c #414141", +"N# c None", +"B# c #232323", +"V# c #454545", +"C# c #454545", +"Z# c None", +"A# c #5E5E5E", +"S# c #676767", +"D# c #535353", +"F# c #4C4C4C", +"G# c #505050", +"H# c #5E5E5E", +"J# c #898989", +"K# c None", +"L# c None", +"P# c #4C4C4C", +"I# c #ABABAB", +"U# c None", +"Y# c #CDCDCD", +"T# c None", +"R# c #5E5E5E", +"E# c #EFEFEF", +"W# c #4C4C4C", +"Q# c None", +"!# c None", +"~# c #4C4C4C", +"^# c #5E5E5E", +"/# c #505050", +"(# c #4C4C4C", +")# c #323232", +"_# c None", +"`# c #545454", +"'# c None", +"]# c #767676", +"[# c None", +"{# c #5E5E5E", +"}# c #989898", +"|# c #696969", +" $ c #5B5B5B", +".$ c None", +"X$ c #5B5B5B", +"o$ c #5A5A5A", +"O$ c #BABABA", +"+$ c #3D3D3D", +"@$ c #414141", +"#$ c None", +"$$ c #DCDCDC", +"%$ c None", +"&$ c None", +"*$ c #414141", +"=$ c #FEFEFE", +"-$ c #656565", +";$ c #414141", +":$ c None", +">$ c None", +",$ c #535353", +"<$ c None", +"1$ c #414141", +"2$ c None", +"3$ c #505050", +"4$ c None", +"5$ c #636363", +"6$ c #484848", +"7$ c #5A5A5A", +"8$ c #858585", +"9$ c None", +"0$ c None", +"q$ c #505050", +"w$ c #A7A7A7", +"e$ c #656565", +"r$ c #535353", +"t$ c #C9C9C9", +"y$ c #5A5A5A", +"u$ c #4F4F4F", +"i$ c #535353", +"p$ c #EBEBEB", +"a$ c #3D3D3D", +"s$ c #5E5E5E", +"d$ c #505050", +"f$ c #575757", +"g$ c None", +"h$ c #2E2E2E", +"j$ c None", +"k$ c #505050", +"l$ c None", +"z$ c None", +"x$ c #727272", +"c$ c #484848", +"v$ c #949494", +"b$ c None", +"n$ c None", +"m$ c #484848", +"M$ c #B6B6B6", +"N$ c #D8D8D8", +"B$ c #565656", +"V$ c #3D3D3D", +"C$ c #575757", +"Z$ c #5A5A5A", +"A$ c #3D3D3D", +"S$ c #FAFAFA", +"D$ c #565656", +"F$ c #3D3D3D", +"G$ c #4C4C4C", +"H$ c #3D3D3D", +"J$ c None", +"K$ c #575757", +"L$ c None", +"P$ c None", +"I$ c #5F5F5F", +"U$ c #5A5A5A", +"Y$ c #4C4C4C", +"T$ c None", +"R$ c #5B5B5B", +"E$ c #818181", +"W$ c #A3A3A3", +"Q$ c None", +"!$ c #616161", +"~$ c #C5C5C5", +"^$ c #4C4C4C", +"/$ c #575757", +"($ c #5A5A5A", +")$ c #E7E7E7", +"_$ c None", +"`$ c None", +"'$ c None", +"]$ c #616161", +"[$ c #4C4C4C", +"{$ c #444444", +"}$ c None", +"|$ c #616161", +" % c #4C4C4C", +".% c None", +"X% c None", +"o% c #4B4B4B", +"O% c #2A2A2A", +"+% c #4F4F4F", +"@% c None", +"#% c None", +"$% c #4C4C4C", +"%% c #6E6E6E", +"&% c #4B4B4B", +"*% c None", +"=% c None", +"-% c None", +";% c None", +":% c #909090", +">% c #5D5D5D", +",% c #6D6D6D", +"<% c #B2B2B2", +"1% c None", +"2% c #484848", +"3% c #D4D4D4", +"4% c #484848", +"5% c #393939", +"6% c None", +"7% c #F6F6F6", +"8% c #393939", +"9% c #575757", +"0% c #393939", +"q% c None", +"w% c #616161", +"e% c #393939", +"r% c None", +"t% c #444444", +"y% c #5D5D5D", +"u% c #484848", +"i% c #5B5B5B", +"p% c #4B4B4B", +"a% c #535353", +"s% c None", +"d% c #4F4F4F", +"f% c #7D7D7D", +"g% c #484848", +"h% c #9F9F9F", +"j% c #5A5A5A", +"k% c #C1C1C1", +"l% c #4B4B4B", +"z% c #E3E3E3", +"x% c #525252", +"c% c #535353", +"v% c #565656", +"b% c #5D5D5D", +"n% c #4F4F4F", +"m% c #444444", +"M% c #535353", +"N% c #484848", +"B% c #6C6C6C", +"V% c None", +"C% c #262626", +"Z% c None", +"A% c #444444", +"S% c #484848", +"D% c #565656", +"F% c #6A6A6A", +"G% c #565656", +"H% c #444444", +"J% c #8C8C8C", +"K% c None", +"L% c None", +"P% c #535353", +"I% c #444444", +"U% c #AEAEAE", +"Y% c #D0D0D0", +"T% c #4B4B4B", +"R% c None", +"E% c #353535", +"W% c #616161", +"Q% c None", +"!% c #4F4F4F", +"~% c None", +"^% c #F2F2F2", +"/% c None", +"(% c None", +")% c #353535", +"_% c #5D5D5D", +"`% c #535353", +"'% c #444444", +"]% c #616161", +"[% c #353535", +"{% c #444444", +"}% c None", +"|% c #5D5D5D", +" & c #575757", +".& c #404040", +"X& c #444444", +"o& c #535353", +"O& c #797979", +"+& c #696969", +"@& c None", +"#& c #565656", +"$& c #444444", +"%& c #9B9B9B", +"&& c #696969", +"*& c None", +"=& c #5A5A5A", +"-& c #BDBDBD", +";& c None", +":& c None", +">& c None", +",& c #DFDFDF", +"<& c #404040", +"1& c #444444", +"2& c #535353", +"3& c #4B4B4B", +"4& c None", +"5& c None", +"6& c #222222", +"7& c None", +"8& c #444444", +"9& c None", +"0& c #5D5D5D", +/* pixels */ +" ", +" V+/@. ' F./%:&L =# .z$v.HXCO6%-%1.. s V+ ", +" o G+iO4$.$n$% B%i+PoE.5+d &o4.}o}o9@c X$,+|oPokX^oaXCo'#>&r.8 ", +" o 9 ^+m+@+0 UOm@co!+2.s#g @.JoP@K.K.fOfOfOfOK.e%IoDo#@@$I.m$-+f$Z$(@j#.H$H$H$H$H$>.IoP@K.fOq#[%q#fOe%@.$&4@Q@so#%4&sXNXOO ", +" V+b#& !#_oG $@w H@fO[%fOP@H$1$X O#z@k$q 6+ &r#hOhOhOhOr#rX`#k$z@;OQOM+5@P@fO[%[%e%=@8#P#t#~ Uog.4OV+ ", +" V+MORo> v%q+2 0%[%q#e%5@C#l._+ &E@7@C+S#S#/O#XF%1Oc.1O1O1O1OF%#XS#O C+1oEohOq $%QOM P@fO[%K.A$pO!%^ NOw#jOV+ ", +" OOP y@t.d%H%D@q#[%P@1$S%_+hO7@ZX/O@##XS#F%] ] ] x$e W.0@# # E$a#e ] ] ] vo#XO /O/OZX1oU.q ;OM+Ioq#[%e%i g@r$U+;%jOOO ", +" OOYO+Ob%Y.WXfO0Xe%M S%`XE@5$#XF%/O1O%%W.J%v@w+* +@lX-&-&ZO)@q.q.k%ZO-&lXr++@I+[X:%$XO&%%@#S#/OC+3.`#;O1$P@[%q#*.##R X%Ko@@V+ ", +" o iOKo(Of#/+9#[%IoX ` i%ZX@#@#@#c.f%$XQ.+.ZOI - .+.+.+.+Y%Y%,XY%Y%Y%Y%Y%Y%,X.+Y#Y#- Y+~$lX<%%&J#cO1OF%/OO Eo_+oXH$q#[%Jo##i$no'#~% ", +" G+1@K#&%WX9#q#>.oX`#I$#X@#@#c.f%[X8O-&t$- .+<#Y%<#wO3%B@B@Zo#.#.N$N$N$#.#.#.Zo3%B.wO<#Y%,X.+Y#PXwor+|Ov@cO1O#XO 1orX;O>oq#[%$.z.Z+I@*%-O ", +" OOYO#+,.U@eO[%IoQOq 7@#XF%@#8+5O* k#t$Y#,XY%wO3%#.N$%@.o$$$$xXxXxX,&,&,&,&xX,&xXp+$$$$.o%@N$Zo3%B.<#,X.+- wor+6o# e #XS#5$ &;O5@q#q##@~#o$r%MOo ", +" -OV@>%5X[ q#e%X `X7@#XF%@#cOg#O$woY#,X<#wOZoN$%@$$p+,&z#FO`.'@ O O Oy.y.)$)$y.y.'@'@'@`.FOFO,&xX$$%@N$ZoB@wO,XY#PXq.Bo.@8+#XS#V rXoXIo[%e%I%*o%$1@8 ", +" jOV.y#.&9#q#1$` 3.#XF%1OW@h%-&Y+,XY%3%Zo%@h p+,&FOz%'@)$p@p@E p$R+YX, , , E#E#, YXYXR+R+p$yoyo)$'@z%FOz#p+$$%@Zo3%<#.+- I ofXa##XO 7@q X K.q#A$W y$C@qO ", +" OO$+9+l%mo[%>.S%hOS#1O@#W.`oZOPX,X<#B.N$h p+,&`.'@y.p@p$YX, %+1X^%tOS.7%FoF@F@F@F@F@Fo7%tOtO^%^%1XE#YXp$p@y.'@`.FOp+$$%@B@wO,XY#t$lX%&dX#XO Eo$%H$q#e%o0XH@4%VoKX]O ", +" iOu#a@do[%M k$V 1OF%5.8Ot$,XwOZo.oxXz%y.E YX1XtOFoS$#o-@%.^%)$#.k#`o# {+1oU.rX_+koko` k$ko$%;Ol.$%` `#7@{+J#6o'oh p$7%-@#o-@%.S.1XYXyoy.z%,&h #.3%,X- q.w$cOF%C+q M+q#K.8#T.q@qO ", +" ;#/ $#Soq#BX6+O @#%%MolX- <#B@%@p+FO OE YX1XS.%.-@#o%.tOy..+I## @#Eo &i%5$/Ox@x$e e e ]X{+x$voc.@#/O5$EorX` O#O#6+F%$XM.3%p$7%-@#o%.7%^%, yoy.`.xX.oB@<#Y#I +.[O@#O r#C#fOfOqXD#!#0# ", +" ^+QXT%8%fOX r##X@#x$!@~$.+<#Zoh ,&z%y.R+1XS.%.#o#oFo, .o+@J%/OU.3.F%]#/oS H+H+8$4o0@5./of%cOO&dX8+e {+x$voc.@#S#I$r#rX3.@#8+p k#z#5#-@#oS$7%1XYXp@'@z#h N$wO.+Y+ZOp x@S#i%S%K.q#<+&+(%qO ", +" G+wX#O5%q#X r#F%F%e H I .+B.#.$$FO OE YX^%Fo#o#oS$E#B@JXa#I$I$x@/o# %#5O%#$X[O# H+8$4o0@5./of%cOO&dX]#]Xx$] vo%%x@O&MoU%O$:#.@O&x@H+BoxXtO#o#oFotOE#E y.FOxXN$3%Y%PX)@BOvoS#E@S%K.fO{%)Oq@G+ ", +" 9 V%eoZ@fOX hOF%@#W.U%Y+,X3%%@xXz%)$R+1XS.%.-@S$1XZo6o]X3.#X5.%#v$.@..v@b.5OJ%J## H+8$4o0@5.W@W.cOO&dX]#]X] voe H+`or+Y+.+.+,XPXk%JX8$x@5.8OxX7%#o-@S.1XYXp@'@xX.oB@Y%- 'or@]XO EoO#K.fOX&_@;+J+ ", +" -OFX|.loq#X r#F%F%E$BoPXY%Zo.oxXz%yo, 5#Fo#o-@tOxX:#]#I$x@J#[XfXw+}#p v$Mob.J%J#GX8$4oE$5.W@W.cOa#dX]#]Xx$x$a#%#* lX~$wowoY+- Y%<#wO<#t$<%J%x@4oO$y.Fo#oFotOE#E Oz#$$#.wO- u@I#8+O EoO#K.K.kOU$Z#OO ", +" V+Z#D$a+)%BXr##X@#5. o- <#Zo$$z# OE E#S.S$#o%.E k%H+7@c.J%g#h%BOg#%&w+p .@:%J#E$cO8+e {+x$] x@%%c.1OF%/O#X{+GXw+w$JX* * ;XM.r+~$Y#wOB.3%B@3%Y#M@S %%v@PX1X#o-@7%%+p$y.FO$$#.wOY#woU%8+S#E@oXfOK.b@AO_$V+ ", +" +|@M#[%M rX#XF%cO+.- <#Zo.oFOy.p$%+7%-@#o7%,&6o#XZX8$BOW$8.`oh%BOg#fXp v$J%W.@#3.hOrX6+`#q `X_+_+k$k$r#ZXx$8+8+8+dXdXO&f%J#BOM$I Y%wO3%B@B@B@B@t$r@]#]X:#p@S$-@F@1Xp$y.FO$$#.wO- wo* ]XO i%X [%H@(#c+2X ", +" NX}%X.q#H$`XS#@#]X|OPX<#Zoh z#y.p$1XF@#o-@^%<#5O1oO&fXH w$H r@W$`oQ.g#%&p MoS F%` H$q#1+)#)#)#5 (X)#[%IoM M+M+M+1$BXBXBXX ;OE@/ow$k%.+wO3%B@B@#.#.#.B.ZO:%c.%&h 7%#oFo^%R+)$`.h Zo<#- u@r@x@O r#1$[%2@9O(+qO ", +" -Oc@p%-#P@$%C+1Ox$H t$Y%Zoh z#y.R+1XF@#o-@YXlX8+5$J#H I#* I+w$H r@8.h%BO%&}#:%W.hOK.k@) fofofok@fo) )+(X(X(X(X(X(X(X5 (X5 )#(XBXx@!@(.Y#wO3%3%B@#.#.N$%@.oY+5OrXO&wotO#oFo^%YX)$FOh #.<#- ~$Q.%%C+`XH$[%zX+XX@f+ ", +" V+&$vOmoq#oXI$1O%%h%t$,XB@h z#y.R+^%Fo#oS$E :#/Ox@w+;X:#U%|O* I+H r@8.!@Q.g#}#%#{+$%) /OO$k#k#k#k#ZOZOZOZOZOZOZOZOZOZOZOZOZOBo_+5@] r@)@- wOB.3%B@Zo%@#.- M.Moe i%C#C+O$%+#o%.5#YXy.FO$$Zo<#Y+'o.@#XV z@e%fOyO< &@o ", +" GOzoyX[%M+hO1OF%:%q..+B..oz# Op$1XFoz S$p@I+S#a#H <%<%M.:#;XI#JXw$6oW$`o!@BOp GXF%1$) ;Xz z -@#o#o#o#o#o#o#o#o#o#o#o#o#oz g+N$$%QOf%;X~$Y#<#wO3%B@<#-&!@0@] 1O1O@#I$oXE@M@, #oS$^%R+y.FO$$B@,Xt$k## #X3.oXq#IoAXR%^+ ", +" -O%X5Xq#>.`X/OF%/or+- wON$,& OE %+F@#oS$y.r@O E$|OM$+.M@<%8OU%|OJXI+H r@W$!@BO.@E$E@fO5@3%S$p+<#B.B.3%3%B@B@ZoZo#.#.N$%@.oYX=$M@e%`X[O+.I .+<#wO~$:#v@cOx$vox@%%c.1OF%1oO#hO:#, #oFo^%p$y.FO.o3%.+I M@dXZX &1$[%*$j J$OO ", +" 1#J@bofOO#V @#] U%- <##.p+z%yo%+7%#o%.)$r@S## 8Of o+@+.<%M.:#|O* JXw$6or@!@p [OdXhO1+V R+%+q.f r+r+lX-&k#ZO(.k%)@'oq.q.I p$%.J%q#I$[XlX- Y+O$BO8$8+]X{+{+] x@x@%%c.1OF%1o;O &M.E##oF@%+E '@,&%@B.- u@W$c.5$` P@K.K@h.,o ", +" b#N#t%[%M+U.1OF%fXwoY%Zo$$FOp@E#7%#o#op$JXS#J#+.lXr+f oM$M@M.8O;XI#JXI+`ofX[Xw+w+E$ &(X# %.)$k#lX-&k#ZO(.k%)@'oq.~$~$u@wo,XS.YX7@>.%%`o+.I+v@f%8+]#]#e {+x$x$vox@%%c.1O@##XV ;Oi% o5##o7%%+yo`.p+#.Y%Y+)@v@S#I$oXq#H$n%% G+ ", +" OOgXo%eOP@k$/O@#0@ZO.+3%.oFOy., tO-@-@YX8O#XH++.ZOk#lXO$f M$Bo<%M.:#* 8.fXfX`oI#I#g#dX;O0XM$-@h k#-&ZO(.k%)@'oq.~$u@u@woI I .o-@B.C#X 1OE$W@a#O&a#O&dX]#]#]X{+x$] vox@%%c.F%#X#X1oO#1ok%7%#o7%, p@FOh B@,XY++@cOO &>oq#1&x.7oV+ ", +" P KOE+q#QO7@1O] |OPXwON$xX'@p$5#%.#otOk#@#0@M$)@k%(.k#lXf +@M$M@U%8.g#g#6o|O:#U%JXv$c.5@C##.%.Y%ZO(.k%)@'oq.~$u@woI I t$Y+PX Oz JX5 H$hO%%O&cOcOa#O&dX8+]#e {+{+] vox@%%c.1OF%F%#XI$oXc.<#S$S$tOYX O,&%@wOY#u@8.c.V $%fOP@A+2OiO ", +" V++ i#9#>.q @#F%v@~$,XZop+z%yo%+Fo#oF@3%cOdX+.q.q.)@k%ZOlXO$+@U%r@fXh%6oI#;X;XU%;Xr@$X1o1+#XYX1Xt$)@'oq.q.~$u@woI t$Y+PXPX- Y%E#F@/o$OM+7@e W.W.W.cOO&O&8+]#e ]X{+x$vovox@%%c.@#F%#X/OE@C#8$z%-@%.^%E '@xX#.Y%Y+ZO# S#hOM [%s+G%]oV+ ", +" =%Woa$q#oX1o@#dX+@- wO%@FO)$YXtO-@S$`.v@c.|Ou@wo~$q.)@(.f ;X!@BO`oH JX* * |O;X;XI#h%5.`#h$5O%.p@~$q.~$u@woI I t$Y+PX- Y#Y#.+#.F@ O`#) l.S#dXf%f%W.cOa#O&8+]#]#e {+x$] vovo%%c.1O@##X/OS#E@U.H , z S.E#yo`.h 3%Y#I :#{+5$koe%e%v ]@`$ ", +" V+=oL.)%H$rXF%@#BOt$Y%#.p+'@p$^%%.#oYXJX#XQ.I Y+t$woq.f I#Q.BO8.6ow$I+JX* * |O|O|OI+p ]XoXq#O$-@p+~$u@woI I t$Y+PX- Y#.+,XY%Y%z##ot$K.0X`#x@cOW@W@W.cOa#O&dX8+]#e {+{+x$vovox@c.1O@#c.W.w+Bo}#volXF@-@5#R+y.,&N$<#PXq.v$/OU.M+[%t+a.(oo ", +" jO`+h+q#O#5$@#cO-&.+B.h FOp@E#F@#o7%~$] 5O~$Y#- q.+@6oQ.Q.`or@6oH H I+I+JXJXI#|OI#H b./O>.;O$$%.B.woI t$Y+Y+PX- Y#.+,XY%<#wOB.R+-@g#k@H$E@e f%W@W@f%W.cOa#O&dX]#]#]X{+x$] x@x@%%e [OI#I N$h PXJ#f%N$-@Fo1XE z%p+B@,Xt$Boe V koK.P@v+]@/@ ", +" V+L#u$E%H$6+@#@#g#Y+Y%#.xX'@R+tO-@#o`.$X8+r+Y#q.8O8.g#Q.`oW$W$r@r@6oH I+I+JX* * I#* `o8$E@)#vo%+1X.+t$Y+PX- Y#.+.+,XY%<#wOB.B.N$tOtO{+$OQOC+dXW@/o/of%W.cOa#O&dX8+]#]X{+x$x$x$f%}#+@Y%%@h h h N$O$]XQ.%+-@S.YXy.,&%@wOPXq.v@S#U.1$q#1&l@m+ ", +" s '.!.[%oX1o@#O&O$.+B.h FOp@%+Fo-@^%+.%%!@r+|OBO%&BOQ.!@!@8.W$r@r@6oH w$I+I+JXJXI#I+g#cO_+)+w+-@p@- PX- Y#.+,XY%<#<#B.B.B@B@Zo,&S$xXkoh$ko@#cO5.5./oW@f%W.cOa#dX8+]#e {+dXJ#6o'o<##.%@.o.o.o$$h B.Q.x$I S$%.1XE z%$$B@,XI 8Ox$1o$%fOH$ XuX8 ", +" g.QoD@e%`XF%F%}#I Y%#.p+'@R+tOS$%.h cO] }#fXw+g#BOQ.Q.h%!@`o8.W$W$r@H H I+I+JX* * 6o.@voX P@k%#o,&- .+.+,XY%<#wOB.3%3%Zo#.N$%@p@z (.1+fOr#] W@E$5./oW@f%f%cOa#dX]#]#5.v$Bot$B.B@B@#.N$N$%@.oh $$.oI /oJ%yo#oS., y.,&N$<#PX)@%#S# &5@q#`@e#MO ", +" f+c@iX[%M+3.1Oe +.Y#3%.oFOp@%+Fo-@^%|Oi%5.[XfXfXg#BOBOBOh%!@`o8.W$W$r@6oH w$I+I+JXJX8.J%C+K._+z%Fo#..+,XY%<#wOB.3%B@ZoZoN$%@%@$$1X%...O%1$1o]#5.0@E$5./of%f%a#dXcO[Or@(.Y#wOB.B.3%B@#.#.#.%@%@.o$$$$%@8Ox@-&%.%.1XE `.h B@.+wo|O%%7@oX[%;$y+'+ ", +" iO<.= q#$%S#1O[O~$,XZop+z%p$5#-@S$.o8+O v@p w+fX%&BOBOQ.Q.!@!@8.W$W$W$r@H H I+JXJXw$Q.4or#(XO&tO1XB.Y%<#wOB.3%B@Zo#.N$N$%@.oh z#F@1X#Xj.;O/OcOE$0@E$5.W@f%cO4ov$<%woY%<#Y%<#wOB.3%B@B@#.N$%@.o.o$$$$h ,X8$[Op@-@7%YX OxXN$<#Y+lX5.ZX_+e%P@K+w@4O ", +" _Oj+0OIo6+1O1OH PXwO%@z#y.E#7%#o^%:#i%/o[Xp }#fX%&BOBOQ.Q.h%!@`o8.8.W$r@6oH w$I+JXH [Xx$C#foH #op@wOB.3%3%B@Zo#.N$%@.oh $$p+p+)$#oN$S%5 q x@W@0@4o0@W@/oJ#W$O$Y+.+.+.+,XY%Y%wOwOB.B@B@#.#.N$.o.o$$$$h .o;Xx@)@%.%.1Xp@FOh B.Y#q.}##XU.M q#).J ,o ", +" 8 K%X.[%M+3.@#cOO$Y#3%$$z%E 1X%.S$p+f%V 5O[Xp }#w+fXg#BOBOQ.h%!@!@8.8.W$r@6oH w$JX8.[X/oI$Io5@Y+#o`.3%ZoZo#.N$N$%@h $$$$xX,&,&,&YX=$O$)+e%E@e E$0@5.0@..* k%Y+PX- - .+.+,X,XY%<#wOB.3%B@Zo#.#.%@.oh $$$$$$- /o:%p$#o5#p$'@xXZo.+wo8Ox$7@S%[%b {XJ$V+ ", +" qO%O6 fOl.O @#:%wo,XZoxX OYXtO#o7%-&I$dXv$p p }#w+fX%&BOBOQ.Q.h%!@8.8.W$r@6oW$BO[X..Mo8$7@[%U.E Fo$$ZoN$%@%@.oh $$p+xX,&z#FOFO'@S.%.5OO%M+5$8+W@GX%&Bo'ot$I t$Y+PX- Y#.+,XY%Y%Y%wOwO3%3%B@#.#.N$.o.o$$p+p+N$Q.1O.+-@F@, )$z#%@<#Y+k## ZXk$e%P@K { 8 ", +" ;@uo0OIo`#@#@#I#- <#%@FO)$E#Fo#op$v$E@# v$p p }#}#fX%&g#BOQ.Q.h%!@`o8.8.h%}#.@w+6ow$Q.5.`#(XS F@^%%@N$.o.o$$$$p+xX,&,&z#FO`.z%p@S$E#V O%S%O 4oh%Bo)@'ou@wowot$Y+PX- - Y#,X,XY%Y%<#wOB.3%B@Zo#.#.%@h h N$q.r@]Xl.W$S.S$1Xyo`.$$3%- ~$g#/OhO>ofOg%].P ", +" goi#[%1$E@c.8+f .+3%$$`.yo^%S$S$3%1OF%:%.@[Xp }#}#fXfX%&BOBOQ.h%!@h%fX[Xp W$|OM.:#JX}#]#S%)#<%z p$.o.o$$$$p+xX,&z#FO`.`.z%'@ O, z 3%1$)+r#4o!@I+|O<%O$(.~$woI Y+Y+- Y#Y#,X,XY%Y%<#wOwOB.B@ZoN$%@#.Y#<%v@]#F%C+oX]#z%-@5#p$'@xXZo.+wo8O{+3.C#[%;$+Xl$ ", +" 8 [@{$[%oX5$#X# u@,XZoxX OR+tO#o7%<%U.cOMo.@.@p p }#w+fX%&g#Q.BOfX[X[X!@* BoBo<%M.:#w$..1O5@C#wO#oy.h p+xX,&,&FOFO`.z%'@ Oy.)$p@5#=$M@k@P@7@dX/o/o8$Mor@BoZOu@I Y+Y+- - Y#.+.+,XY%Y%wOwOB.ZoZoB.(.`o4ovo@##X#X#X`#6+~$S$7%, y.z#N$Y%t$lXdXV l.q#F$} N@V+ ", +" r.y%]+fOz@/O@#p PXY%#.z#)$E#7%#oR+:%E@GXMov$.@p p }#}#fX%&fXp [X%&w$M@f oM$BoM@M.U%r@$X1o0X5$YXFo`.xX,&z#FO`.z%'@ Oy.y.)$p@yoR+FoFo8$mX5 QOk$` $%q O E$`o oq.wot$Y+Y+- - Y#.+,X,XY%<#3%3%t$:#v@a#x@1O1O1O@##X#X3.S%!@S.%.1Xp@`..owOPXk%5OO `XP@e%!Xq%8 ", +" 2XB$N.Io`##Xc.8O- wO.oFOyo1X%.#o$$x$/O%#Mov$v$[Xp }#}#.@.@p 6oM@r+lXr+O$ o+@+.M@M.;X!@0@6+5 [OFotOFOz#FO`.z%'@ Oy.y.p@yoyoE p$E##op$6+L@6&j.O%0X6+# I$1o%#:#)@u@I Y+Y+- - Y#,X.+,XwO- r+Q.S ]Xx@x@x@%%c.1O@##X#XZXoXcOE -@5#p$z%$$B.Y#u@h%ZXr#5@q#Yo|#9$ ", +" [+y+aO>oU.1O{+O$.+B.h `.p$5#-@%.~$I$e b.v@..v$[X[X.@.@8.U%lXk%k%k#lXO$f f +@M$BoM.I#%&dXO#1+M$z E#FO`.z%'@ Oy.)$p@p@E p$p$R+YX5#z PX5 6X-oP@voI#p+wo;O`XS |O(.u@I t$Y+PX- Y#,X.+)@I+%#O&{+x${+x$] x@%%c.1O@#F%#X/Ok$7@#.#oS.YX OxXB@.+wo;Xx$EoM+[%QO!oSX ", +" hXX+[%BXI$1OH+)@,XB@$$'@YXS.#oS.JXr#5.:%MoMo....BO|OlXq.~$)@k%(.ZO-&r+O$f +@M$+.M.JXv$c.5@O#B@#op$z%'@ Oy.)$p@yoE p$R+R+, E#%+F@z I+5 r#[X<#1X#o#oI+0X`#GXU%(.u@I t$Y+Y+- u@Bo%&5.8+]X]#e ]X{+x$] x@x@c.c.1O@##X#XhO$%lXS$F@, y.,&#.Y%t$r+a#3.S%q#b r & ", +" 7&h@[%S%C+/O5Ot$Y%#.xXy., 7%z , b.i%# :%:%p H o~$Y+t$wo~$'o)@k%ZO-&lXr+O$f M$+.8OH J%V 0X#XE#S$yo O)$p@yoE p$p$R+, E#E#%+1X5#-@#o,X'oE -@g+=$g+tOa#0X3.:%M@k%u@wot$t$-&6o[OW.8+dXdX8+]#e ]X]X{+x$vovo%%c.1O1OF%F%1ooXh%7%Fo%+p@z#N$<#Y+k#a#1oz@fO5@:@8@ ", +" -O:$>#fOl.S#S#v@- <#%@,&)$E#Foz OW.5$J%Q.M.'o.+.+- PXt$wou@q.'o)@(.k#lXr+f o+@M$8O`o0@6+)#:%%.7%)$p@yoE p$R+YX, E#%+1X1X5#5#7%=$g+z =$=$z #oz g+y. &P@1Ow++@'ou@)@:#[X0@a#cOcOcOa#O&dX]#]#e ]X{+x$] vox@%%c.1O@#F%ZXQO8$1XS$^%E FO.owOPXk%H+O k$e%>.R.HOOO ", +" iOGoXofO` F%%%h%- wO.oz#p@1X%.#oh 1O5.O$Y#wOwOY%,XY#- Y+I u@~$'o)@k%ZO-&lXO$f oM$:#g#8+O#K.k#z 1XyoE p$R+YX, E#%+1X^%5#tOS.7%S$=$=$=$#o#o#oz =$g+q.5@QOO&r@O$+.`oGXf%W.W@/of%W.cOa#O&O&8+]#e ]X{+x$] vovo%%c.1O@#F%/O;Ovoy.-@5#p$z%h B.- q.fXO `#Ioe%$oW+9 ", +" 1.PoSoe%q F%] M.- B..oFOyo^%S$-@PX%%I#B.B.B.<#Y%,X.+- Y+t$wo~$'o)@k%(.k#-&r+f o+.|O.@@#H$koh z E#p$R+YX, E#%+1X^%5#tOS.7%F@Fo-@#o#o#o#oz =$g+g+z !@)#q 5.[XJ%E$W.5.0@E$5./oW@W.W.a#a#O&8+]#]#e {+x$] vovo%%%%1O@#F%#Xk$5$.o-@tOp$'@$$3%Y#u@W$ZXr#H$fOYo:X;o ", +" 1.Y@5%IorX#Xx$O$Y#B.h `.E ^%-@%.r+]#f B.3%B.wO<#Y%.+- PXY+I ~$q.'ok%(.ZO-&r+f +@Bow$H+r#h$x$^%S$, YXE#%+%+^%^%5#tOS.7%F@Fo%.S$S$#oz z =$=$g+g+g+5#{+1+ko/O]X/oS 8$4o0@E$5./o/of%W.cOa#O&dX8+]#e ]X{+x$] x@x@%%c.1O@#voF%5$Y##oS.YX Op+B@.+wor@ZXi%M q#&O'XbX ", +" @@x%eOH$r#F%voO$.+3%h `.p$tO#oFo8O8+ZO3%B@B@B.<#,X,XY#PXY+I u@~$'o)@k%ZO-&O$ o<%r@GXO X )+fX-@Fo%+%+1X^%5#tOS.7%F@F@%.S$S$-@#oz =$=$g+g+g+=$^%B@%&M 1+`XvoE$8$H+8$S 4o0@E$5./oW@f%cOa#O&dXdX]#e e {+x$x$vox@%%%%O&..M.U%1O'o-@S.YX Op+B@,XwoH /OE@1$[%O#s@j@ ", +" TOy+K.5@U.1Ovor+.+B@$$z%p$S.#oF@JX8+)@B@ZoB@B.wOY%,X.+- Y+t$wou@q.)@(.k#O$8OQ.$XcOx@r#P@e%u@=$7%^%^%5#tOS.7%F@Fo%.S$-@#oz z =$g+g+g+=$7%xX|O] M h$0XO#C+a#S GXH+H+S 4o0@0@E$5.W@f%W.cOa#O&dX8+]#e {+{+] vo{+4o8.u@N$%@r+1Or+-@F@YX OxXZo,Xwo* c.3.M+[%S%rO}$ ", +" ~oo@K.>oE@c.{+lX.+B@$$'@R+S.#oS.W$dXq.B@#.Zo3%wO<#Y%,XY#PXY+I u@q.k%r+|O[X5.dXvoO q H$C%` z%z tO5#tOS.7%F@Fo%.S$-@#oz =$g+g+g+g+%. Of 5.l.)+foq#BX`XC+]#E$H+# GXH+8$S 4o0@E$5./of%f%cOa#O&dXdX]#e {+{+a#v@<%- %@$$$$#.O$%%<%%.F@, y.p+#.,XI U%{+7@BX[%S%&& # ", +" !O>@fO>oEoc.8+-&.+Zo$$'@R+tO-@tOBOO&~$ZoN$ZoB@B.<#Y%,XY#PXY+I u@-&* :%/o] #Xi%z@H$5 h$M+p F@#oS.S.7%F@Fo%.S$-@#oz z g+=$=$=$=$S.8.`X(Xj.1+M z@U.O ] W.S GX# J#GXH+8$S 4o0@E$5./oW@f%W.a#O&dX8+]#8+S Q.k#<#ZoN$%@h h 3%f ] |OF@F@, )$xX#.,XI M@8+7@X [%oX8oZ% ", +" ( 6@fOM Eo%%W.k#.+B@$$'@R+tO-@^%w+W.woZoN$#.Zo3%<#Y%,X.+- I lXI+[X[O/oc.rXM+0X)+>.ZX`oB@S.=$-@F@Fo%.%.S$-@#oz =$=$=$=$z #o#og+%@>ob+[%S%rX5$voO&5.8$J#[O[OJ#J## H+H+S 4o0@E$5./oW@f%W.cOa#8+W.%#|O'oY%wOB.B@ZoN$.o.owOf e I+F@F@, y.xX#.,XI M$O&3.X [%QO@X+O ", +" TXK$fOM Eoc.W.k#.+B@p+'@R+tO-@^%fXW.wo#.%@N$Zo3%wO<#<#I o8.p .@p J%] `XfOfO`#J%u@, #o=$z -@S$S$-@#o#oz =$=$=$=$=$#o#o-@S$#o=$r+) 1+` S#]#5.H+[OJ%%#%#J%$X[OJ## GXH+8$S 4o0@E$5./oW@a#a#E$w+M@u@Y#.+Y%Y%wO3%B@#..o%@<#f 8+JX7%F@, y.xX#.,XI M$O&3.X [%oX.#'$ ", +" LO*@fO>oE@c.]#lX.+B@$$'@R+tO#otOQ.a#woN$.o%@#.Zo<#u@U%%&..p %&g#[XS 5$>.M+I#z%%.g+=$#oS$S$S$-@#oz =$=$=$=$=$z z #o-@S$S$%.z S$$X$OBXO /o$X5O:%:%:%b.5O%#J%[OJ#J## H+H+S 4o4o0@/ocOf%$X8.f u@Y+Y+- .+,X,X<#B.B@#.%@N$wOf ]X;XFoF@, y.xX#.,XI <%8+7@X [%oX5od@ ", +" P.t K.5@U.@#] O$.+B@$$'@p$tO#o7%W$8+u@%@h %@wOlXH [Xv$[Xw+fX%&fX..W.r#1+x$^%=$#oS$%.%.%.S$#oz z g+g+=$=$z #o-@-@S$%.%.Fo%.z , C+fo_+]X$X......Mov@b.b.5O%#$X[OJ## H+H+8$4o5.f%0@..U%-&q.~$woI t$PXY#,X,XY%B.3%ZoN$%@3%f %%+.S$F@, y.xXZo,Xwo;Xx$3.BXq#S%+&IX ", +" 0+ROG.H$r##Xx@O$.+3%$$`.p$S.#oFoJXdXu@N$Y#M$BO..v$p }#}#}#fXfX}#5O{+z@)+!@#o#o%.%.S$-@#oz =$=$g+=$z z z #o-@S$%.%.FoF@7%%.=$3%>o[%E@/ov@.@.@..Mov@:%b.b.%#J%$XJ#J## 8$E$W@# Q.M.ZOk%k%)@q.u@wot$Y+- .+,XY%wOB.B@N$%@B@ o@#-&-@7%, Op+B@,XI JX1OE@1$[%;O,%ro ", +" ' Y@C.IorX#X] O$Y#3%h `.E 5#-@Fo<%]#;X* w+:%...@p p p }#}#fXw+[XJ#F%M+P@)@=$-@-@#oz =$=$g+g+z z #o#o-@-@S$%.FoF@F@7%7%S.%.z M.) >oS#8$..[X[Xv$..Mov@b.b.5OJ%J%J#H+E$0@b.6oM$r+-&k#ZOk%)@'o~$u@I Y+- Y#,XY%<#B.B@#.N$B@M$c.q.-@S.YX'@$$B@.+I H S#i%M q#l.U u. ", +" 1.PoZ@P@`#/Ox$ o- 3%.oFOyo^%S$S$(.5$W.b.v@..v$.@[Xp p }#}#w+w+v$S V P@q z%=$z =$=$g+g+g+=$z -@-@S$S$%.%.FoF@7%S.S.S.tOtO-@Fo4ok@;O] $X.@p [X.@..v@v@:%b.b.%#H+0@8$p * Bo o of r+-&k#(.k%)@q.u@wot$PX- .+,X<#wO3%B@ZoB@U%{+.+-@S.R+z%$$B@Y#u@r@C+r#5@fOi.k+*& ", +" 1.=.V$K.k$/O] ;XPX<#%@FOyo1X%.-@Y%7@x$:%MoMov$[X.@[Xp p }#}#w+..E$EofOdXtOg+g+=$7%$$yog+z -@S$S$%.%.FoF@F@7%S.tOtO5#^%tOz E i%)+6+a#b.[X}#p [Xv$MoMov@J%H+E$%#BO;XM@Bo+.+@ of r+lX-&ZOk%)@'o~$woI Y+PXY#,XY%<#B.3%ZoZoQ.8+h S$5#p$`..o3%Y#~$8.C+`#>.e%,@u+`$ ", +" r.y s+fO$%S#1Og#PXY%N$z#p@E#Fo#oxX@#F%5OMo..v$.@.@[Xp p }#}#}#v$E$3.5@I##o-@)$k#W@k$O$g+#oS$%.FoFoF@7%S.S.tO5#5#^%1X%+S.=$- IoK.7@4o..}#}#p [X.@Mo%#S GX:%8.|O8O8OM.M@BoM$ of O$r+-&k#(.)@'oq.u@I t$PXY#.+Y%<#3%<#k%`o1o8+yoS$^%E `.%@wO- 'o[XO k$e%Iod+T#-O ", +" J+D m%[%S%C+S#v@PXY%#.xXy., F@z )$W@5$$Xv@..v$v$.@.@[Xp }#}#p v$S C+i%t$B@w+hOfosO>.#.=$S$FoFoF@7%S.S.tO5#5#^%1X1XE#E#F@#ow$foBXF%[Op w+}#}#v$$XH+J#}#r@* I#|OU%8OM.<%Bo+.+@f f r+lXk#(.k%)@q.~$woI Y+- .+,XI 8O..]#ZXQOJ%^%Fo1Xp@FON$<#Y+(.4o5$$%fO5@A@<@V+ ", +" ooh@[%X 7@/O5Owo.+Zop+ OR+S.#o%+..hOH+v@Mo....v$.@[Xp p }#p .@$Xx$%%5.` fomX-oL@I$E#z F@7%S.S.tOtO5#5#^%1X%+E#E#, E#S$tOdXk@z@{+b.w+}#..J#H+5OfXW$H I+JX* I#|OU%8OM.<%Bo+.+@f f r+lXZO(.k%'oq.u@I PXt$f 8.4ovo/OS#3.QO6oF@F@E#y.,&#.Y%t$k#a#EoS%q#b RX[+ ", +" lO2#fOM i%@#0@(..+3%h z%p$5#-@Fo8OhOW@b.MoMo.....@[X[Xp }#}#[XMo4o@#rX$%;O1$)+B#b.%.#o7%S.tO5#5#^%1X1X%+E#, YXYXYXE##o O`X(XrXa#:%%#GX8$:%fX!@`o8.r@6oH w$JX* |O;XU%:#M.<%BoM$+@ of lX-&ZO(.'o~$ZO;X:%O&%%@##X#XS# &kok%S$S.YX Op+B@.+wo odXi%X [%8&e+& ", +" 3#&XG.Io6+F%] +@Y#B.%@FOyo1X%.-@- 1o] 5Ov@Mo.....@.@[Xp }#}#.@v@5Ob.b.[OcOI$H$1+r+g+%.tO5#^%^%1X%+%+E#, YXR+R+R+p$1Xz 'oq#q#rXx@8+5.J#:%..v$.@p p w+fX%&g#BOh%`o8.W$r@H w$I+* |O;X;X:#M.+.M$;XfX4o]Xx@%%c.@##X#XS#z@ZX$$S$5#p$'@h 3%Y#u@JXc.hO>oq#&OOo>$ ", +" ;#1 o.K.koS#%%I#PX<#N$z#)$E#7%#o`.]#5$$Xv@Mo....v$.@[X[X...@!@8OZOk# o|Ov$1OM k$$$=$S.^%1X1X%+E#E#, , YXR+p$p$E E S.#og#6&h$C#E@c.]Xe 8+8+dXO&a#cOcOW.f%/o5.5.E$0@S 8$8$H+# [O$X$XJ%%#[OS dX] vovovox@x@c.1OF%F%C+oXS , %.%+yo`..owOPXq.g#5$q Ioe%+o@&NX ", +" qO4 .6+@#{++.- <#%@FOp@%+Fo-@h ]X5$[OMoh%M@I <#B.wOY%,X.+- PXI 'o:#4o_+>ou@=$tO, , YXR+p$E E yoyop@p@)$y.y.%+=$YXJXg#Q.!@`o`o`o`o`o`o`o`o`o`o`o8.8.8.8.8.8.8.8.8.8.8.8.`o!@!@!@g#k$O%M+E@c.x$x$] vox@c.1OZXoX5.yoS$^%yo`.$$3%- u@I#x@r#>oq#{.[o.O ", +" _#~X[ fO$%ZX@#H Y+Y%#.,& OYXS.#oE#Q.3.}#)@wON$#.B@3%wOY%,X.+Y#PXI (.H 8+C#r#`.#o%+YXR+R+E E yoyop@)$)$y. O'@ OtO=$z -@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@-@S$S$S$S$S$S$S$S$S$S$S$-@-@ O`XO%QO7@x@x$x$x$] x@%%1Oi%$%:#FoFoE#)$z#.owOY+'o}#C+_+e%e%G$LXjO ", +" /@0$8#[%BXI$F%J#q.Y#3%$$z%E ^%S$%.wox$I+N$.o%@#.Zo3%B.<#Y%,XY#- I lXg#F%IoW@F@%.R+p$E E yoyop@)$)$y. O'@z%z%z%p$E#, , , YXYXYXYXR+p$R+p$p$E E E yoyop@p@)$)$y.y.y.y. O O O'@z%%+=$'oq#(XkoO ] {+{+x$] x@%%F%koO N$-@tOR+'@xX#.,XI O$5.7@O#q#M M%.rXF%]#BoPXwO%@z#)$E#F@#oy.5O0@- %@%@N$ZoB@3%wOY%,X,X- wo+@v@E@IoH #oS.E E p@p@p@)$y.y. O O'@z%`.FOFOFO,&,&xXxXp+$$$$h h %@%@N$N$#.ZoB@3%3%3%B.wOwO<#Y%Y%,X.+.+Y#Y#- p@z BOX#K.6+F%{+]X]X{+x$vox@V oXw+^%%.1Xyo`.h 3%- ~$I#%%hO1$q#POj%o+V+ ", +" &@6O$.fO$%S#@#h%wo,XZop+'@p$5#-@F@k#vo:#N$%@%@#.Zo3%wO<#Y%,X- u@M.H+` X Y%z %+p@p@)$y.y. O'@'@z%z%`.FOz#z#,&,&xXp+p+p+$$.o.o.o%@N$#.#.ZoZoB@3%B.wOwOwO<#Y%Y%,X.+.+Y#- - PX,X1X5#] C%M E@%%e e e {+x$voc.`XEo.+S$7%YXy.,&N$<#Y+)@MoC+_+e%e%Y$_%@@ ", +" `$[#A%[%BX3.F%0@k%Y#B..o`.p@%+Fo#o'@H+GX.+%@%@N$#.B@B.<#<#,X- 'oJXdXX 3.yo-@p$)$y.y. O'@z%z%`.`.FOz#,&xXxXxXp+p+$$h h .oN$N$N$#.ZoZoB@3%3%B.wOwO<#<#Y%,X,X.+.+Y#- PXPXY+t$B.Fo`.O#k@oX5$x$]#]#e {+x$voO S%..%+S$^%yoz%$$B@.+u@+@cOI$S%q#M c%@ -O ", +" OO'#+%K.P@q /O] ;XPXY%#.xX'@R+S.-@7%f x@8O#.N$N$#.Zo3%wO<#Y%- ZOh%F%P@GX%.F@)$y. O'@z%z%`.FOFOz#z#,&,&p+p+$$$$h .o.o%@N$#.#.ZoB@B@B@3%wOwOwO<#Y%Y%,X.+Y#Y#Y#- PXY+Y+Y+I wop+-@lX)#)#` #Xe 8+8+]#]X] voi%3.- -@7%, y.z#%@wOPXq.r@#X &5@q#N%w.s. ", +" `OA#i [%oXV #Xb.~$.+3%h FOyo%+Fo#o O[O5.wo#.N$#.ZoB@B.wO,XPXO$v@rXq#U%=$1Xz%'@z%`.`.FOFOz#,&xXxXxXp+$$h .o.o%@N$N$N$#.ZoZoB@3%B.B.wO<#<#<#Y%.+.+Y#Y#- PX- PXt$t$I I wowop@S$:%C%Io &%%8+dX8+]#cO5OMo#Xw+%+%.^%E z%$$B@,XI -&8$7@z@fO>.L+e$iO ", +" ]Oio^Xq#5@ &/Ox$M@PXY%#.xX OYXtO-@%.'ovoQ.wON$N$#.B@B.B.,Xk#w+5$[%5@B@#op$`.`.`.FOz#z#,&xXxXp+$$$$h .o%@%@N$N$#.ZoZoB@3%3%B.wOwO<#<#Y%,X.+.+Y#Y#- PXY+Y+Y+t$I wowou@~$PX1X, O X#M+I$x$dXa#8$g#-&wo..8+B@-@7%, y.z#%@wO- u@JX1OhOM+q#C#>@-.OO ", +" V+2++XF$fOl.C+#Xv$u@Y#B..o`.p@%+F@#op$w+x$r+#.N$N$#.wO(.h%W@1oQOk@U.p$S$)$`.`.`.FOz#z#,&xXp+$$$$h h .o.o%@%@N$#.#.ZoB@B@3%B.B.wOwOY%Y%,X,X.+Y#Y#- - PXPXY+t$t$I wou@u@wO%.N$M+) ;OZXO&5O|Owo%@.oM@x@|OS.%.1XE z%p+B@.+I (.J#1o` P@P@e@O+3+ ", +" ~%n#7O[%>o  M$Y+Y%N$xX Op$^%%.-@wOO&J#PXN$.+M$.@O&@#C+ &>oX## %.z 7%tOtOtOtOtOtO^%^%^%^%^%^%^%1X1X%+%+%+%+%+%+%+E#E#E#E#, , YXYXYXYXYXYXYXYXR+R+p$p$p$p$E E E E 1X#oM$)+0X &S M@<#xXxX$$u@/oGXz%-@tOYXy.,&%@<#PXu@;X%%hOBXq#M+&.>XG+ ", +" n.C *.fOO#5$#Xv$~$Y#B.%@FO)$YXtO#o5# o1O:%`oS vo#X#X#XV `XK.h$I#S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.7%S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.%+8$j.z@H+O$B@p+xX$$<#..]XY+Fo7%E#p@`.h B@.+I (.J%V ` e%e%6.R#0+ ", +" b#VX3Oq#5@6+S#{+8Ot$Y%Zop+`.p@E#Fo#oR+!@k$rXO /O#X#X/O7@z@0X) 5$]#e e e e e e e e e e e e e e e e ]Xe 8+8+8+8+8+8+8+8+]#8+8+8+8+8+]#]#]#]#]#8+8+]#]#e e ]#]#e ]#]#]#]#oXIo1O!@q.B@h p+#.w$x@+.S.Fo1XE '@xX#.<#PXq.I+c.hOBXq#M+S@/ G+ ", +" o ;+9o$.q#oX7@#X# (.- <#N$,&'@p$5#S$-@z#5.M+i%S##X#XO 3.koP@O%EO6&6&6&6&6&6&6&B#6&B#B#B#EOB#EOEOX#O%fofofofok@fok@k@k@O%O%j.j.j.j.j.$O$O$OX#X#X#X#C%X#X#C%.X.XX#O%) (XIorXE$;Xt$3%N$N$f ]Xw+R+-@tOR+y.z#.oB.Y#woO$0@7@z@K.P@t@{#&@o ", +" NXpo{@q#Ioq /Oc.W$wo.+B@h z#y., S.-@%.Y#/OX 3./O#XS#V rXoX5@P@e%P@e%P@P@IoIoIo>.H$>.H$5@X $%q 6+rX6+6+`#`X`X`X_+_+k$` ` kokoz@z@l.;O;O;OO#S%S%S%oXQOX BXoXkoq rXr#i%C+cOg#O$- B.3%ZO5.GXp+-@7%E#)$z#$$B@,Xt$)@w+/O &>o[%BXa%b$/@ ", +" V+s.,#}+[%X Eo/OcO oPX<##.p+`.E %+7%#oS.(.1ooX7@#X#XZX1ohO6+`#`#6+6+rX &r#r#hOhOU.1o%%/o# J%$X[O# H+H+8$4o0@E$5./oW@f%cOcOO&dX8+]#e ]X{+x$] x@c.#X#Xx@a#5.S H+# $X5Ov$W$M$~$- <#k#4o/o<#%.F@1XE '@p+N$wO- u@8O]XE@O#fO>.Z ^#L%o ", +" 4OioW#e%K.z@5$/O:%)@- <##.p+'@E %+F@#o5#O$I$S%7@/O/O/OO O ZXO /O#XF%@#1Oc.x$5.w+* +.+.Bo<%8O:#;XI#* I+w$H r@8.`oh%Q.g#%&w+}#[X.@v$Mo:%b.[O8$S %#}#Q.8.W$H I+I#U%8O+.r+'ot$PXlX4oO&Y+FoFo1Xp$ O,&%@B.Y#I lX# 1o_+>.q#XX*+:O8 ", +" +:+&#q#5@q O %%8.u@- wON$z# Op$1XF@#o%+M$EoO#7@/OF%@#1Oc.%%x@vo] ]XE$}#8Oq.Y+I u@q.)@(.k#-&r+f +@+.Bo<%M.:#;X|O* JXw$H 6o8.`oh%%&.@5Ob.p !@6ow$* ;X:#<%Bo+@O$-&(.'ou@woM$5.]#q.7%%.^%R+y.FO.o3%.+t$)@}#S# &M+[%M o&U#9$ ", +" J+X@,$A$[%X i%/O]#M.t$.+3%h z#y.R+^%Fo#oE# oV S%3.F%@#1Oc.%%vodX%#* 'o,XY%,X.+- Y+wou@q.)@(.k#lXO$f oM$Bo<%8O:#;XI#* I+w$r@Q...5O5Op g#`oW$6oI+I#U%8OM@M$ or+k#k%)@)@U%W.8+q.tOS$tOYXy.FO$$ZoY%Y+q.JXvoE@O#q#P@t@;.S+V+ ", +" NX+#T+K.K.;O7@#XH+-&Y+,XZoh FOy.YX5#%.#o^%k#S#oXU./O@#] E$BOlX<#N$#.3%wOY%,XY#- t$wo~$'ok%(.k#lXO$f oM$M@<%M.:#;XI#w$BOMoJ#5O..}#%&BO!@8.6oI+* ;X:#<%+. oO$lXk#lXr@e f%Y+7%%.5#, )$`.$$#.<#PX~$Bof%I$koP@q#QOC$/ /@ ", +" V+g.|%>#q#Ioko5$F%v@k%PX<##.$$FO)$YX^%Fo#oS.u@x$oXi%H+|OPXh p+$$%@N$B@B.<#Y%.+- PXI u@q.)@k%ZO-&r+O$f +@BoM@<%:#H }#$XGX[Ob.Mo.@p w+g#h%`or@w$JX|OU%M.Bo+@f f M@Mo@#S <#Fo%.tO, p@`.p+#.<#- u@O$J#5$q 5@[%;$G#%o`O ", +" OO6#dO2 [%H$q ZX1O}#)@- <##.h FOy.p$^%F@#oFoB@5O5$..Y+.op+p+$$.oN$Zo3%<#Y%.+Y#PXt$woq.)@k%(.k#lXO$f o+.8O8.Mo8$0@GX[O%#b.v@..[X}#%&BO!@W$H I+I#;X8O<%BoBoH W@/Ow+p+%.Fo5#, p@`.p+#.wOY#wolX:%O &M+[%5@3$0&~.V+ ", +" /@)X7.2@[%1$ &O %%Q.q.- <#Zoh z#y.p$1X7%-@S$y.I#] 8$M$B.h h .o%@ZoB@wOY%,XY#- t$wo~$'o)@k%k#-&r+ oU%Q.J#/oW@0@8$GXJ#J%5Ov@...@p fXg#h%8.r@H JX|OU%:#* ..c.vo+.R+-@Fo1XR+)$`.p+#.wOY#I ZO.@S#U.QOq#P@^$++@%-O ", +" 9$eX~#Doq#X i%S#x$w$wo- <#Zoh z#y.E E#S.S$#o%+(.5.] w+~$N$%@%@ZoB@B.<#,X.+- PXI u@q.'o)@(.O$|Ov$5.dXcOf%/oE$4oH+# [O%#:%Mov$p w+g#Q.`oW$6oI+* JXw+dXV 8$Y+S.S$7%^%R+y.`.p+N$wO.+wok%%&F%i%O#K.K.;Of$1%^+ ", +" V+SXXO3@e%fOQOi%S#e I#wo- <#Zoh z#'@yo, tO%.#o7%.oW$x$cOI#PXZoZoB@B.wOY%,XY#PXt$wou@'of r@$XdX]Xe 8+a#W./o5.0@8$GXJ#$X5Ov@Mo.@p fXBOh%8.W$r@fX5.5$] ;X`.%.%.7%E#p$ OFO$$#.wOY#I k%!@1Oi%;OP@fOC#H.m s% ", +" $ ~+/Xu%K.fOQOi%S#]#H q.PX<#B@.oxXz%p@R+1X7%-@z 1XY+b.%%5.w$~$<#B.wOY%.+.+Y#- ~$Bo}#f%voc.vo{+e 8+O&cOf%/o0@S H+# [O%#:%v@..[Xw+%&BOg#Moa#V S#}#B@S.#oF@5#, yo'@,&h ZowO- woZO%&1Oi%l.P@q#BX>+|$g$o ", +" OO<$G@gOK.fOoXi%S#] h%'oPXY%3%N$xX`. OE E#tOFo-@S$E k%J%%%O&BOf t$Y#,X- )@* [O{+/O/O@#%%vox$]X]#dXa#f%W@5.4oS GX[OJ%b.v@...@.@v@H+] I$S#..PX%+-@%.S.%+p$)$`.xX%@3%Y%- wo-&[X#Xi%;Oe%q#BX>+7#z+o ", +" OOpX_@gOK.fOQOU.O c.}#k%Y+.+wO#.h ,&'@p@YX%+S.%.#oS$yoq...vovoH+W$I#}#f%1OS#S#/O#XF%c.x@] x$e 8+O&W.f%/o0@S H+J#[OJ%%#[O0@]X1oE@c.fX.+E#-@S$7%1XYXyo OFO$$N$3%,XY+u@f b.O U.;Oe%q#BX7Xx#Z#8 ", +" s j$*#p#K.fOC#r#ZX@#v@-&t$- <#B@%@xX`. OE , ^%7%%.-@%.YXY%w$E$S#`XS%z@r#I$C+O /O#X@#c.x@vox$]X8+O&cOW@5.0@0@E$f%e #XEor#5$4oU%N$1X-@%.F@^%E#E y.z%,&.oZo<#Y#t$q.+.# ZXr#S%e%q#X 7X{O*X-O ", +" qOxoo#p#K.q#M+`#V #X8$M.woPX,XB.#.$$,&'@)$E YX^%7%%.#o-@S.z%~$`of%V l.QOQO;O` 6+U.3.1oZXS##XF%F%F%#XO 1oU. &6+r#@#S w$PXp@F@-@S$7%^%E#p$)$'@z#p+N$B.Y%- wo)@* f%V rXQOK.fOX 4+m#o` I$O ]X`ok%t$- Y%B@%@p+z#z%y.E , 1XtOFo-@#o-@S.E #.-&`oJ#]XC+rXko;OS%S%O#;Ol.z@ko6+7@@#8+J%H (..oR+F@-@-@FoS.^%E#p$)$'@z#p+.oZowOY#Y+wo-&}#x@I$`X1$q#K.oX`%D.S+-O ", +" 8 KX7$u *.[%P@;OU.C+1O5OM$~$PX,XwOZo%@$$z#z%y.yoR+E#5#S.FoS$-@z S$7%%+p@.oY#'olXBo;XI+I+;XM$-&q.Y%p+E 1XF@-@#oS$%.7%5#%+YXyo)$'@FOp+.oZowO,XPXwo'oM.# S#U.z@H$q#>.;OH.]$&@8 ", +" OOho>%8X;$[%K.C#`#1oS#O&!@k#t$- ,XB.Zo%@$$,&FO'@)$p$YXE#^%tOF@FoS$-@#o#o#o#o-@%.FoFoF@Fo%.-@-@#o-@-@-@%.F@tO^%%+YXp$p@ O`.z#p+.oZoB.Y%- I ~$r+%&e 1orXoXP@[%>o[$_X%oF.V+ ", +" OO& h#|+'%e%q#5@$%E@C+@#H+;Xq.Y+- ,XwOB@%@h p+FOz% O)$E R+, %+1X5#tO7%F@F@F@Fo%.%.%.FoF@F@7%S.tO5#^%%+, R+E p@y.z%FO,&$$%@Zo3%Y%Y#t$wo(.I+0@/Oi%k$1$q#fOBXN+9XZ.s%o ", +" V+P )X_.2%$.[%K.X q 3.ZXx$v@<%q.Y+- ,XwOB@#.%@$$xXFOz%'@y.p@yop$p$YX, E#%+%+%+%+%+%+%+E#YXYXR+p$E p@y. Oz%FOz#p+.oN$ZowO,XY#Y+u@k%U%J%c.I$6+oXP@[%IoS%P%H#xoiO ", +" /@w#[.~OcXe%q#>.oX6+I$O {+b.:#k%I PXY#Y%wO3%Zo%@$$p+xXz#FOz%'@'@y.)$)$)$)$)$)$)$)$)$y. O'@z%`.z#xXp+h %@N$B@B.Y%.+PXt$u@k#* [O%%7@rXl.5@q#fOM+0o|X-$;&-O ", +" OOHoe.D%| M fOq#H$O#6+I$O ] $X* lXu@I PXY#,X<#B.3%ZoN$.oh $$p+xXxX,&z#z#,&,&,&,&xXxXp+$$h .oN$#.B@B.wOY%.+- t$u@)@ o6oS @#I$ &$%M fOq#H$&OzO;.Q+`OV+ ", +" o b#f@!$7.pOH$q#q#H$O#6+3.C+1O5.}#8Ok#u@t$PX- .+,X<#B.B.B@Zo#.#.#.N$N$N$N$#.#.#.#.B@3%B.wO<#Y%.+Y#PXI woq.r+|O..a##X3.rX$%M fOq#IoQODX4X; A /@ ", +" V+,o)XN ^Oc#>.q#q#H$oX`Xi%V O ] H+g#U%-&'oI t$Y+PX- Y#,X,X,XY%<#<#<#<#<#Y%Y%,X.+Y#Y#- PXt$I u@k%O$I#}#5.c.V E@`#O#>oK.[%e%X F K$]%m.4#V+ ", +" 9 xO*Ox.CXSOH$q#q#P@BX$%6+E@V ZX] dX%#h%;Xr+(.)@~$woI t$Y+Y+t$t$Y+t$t$Y+t$I woq.k%ZOO$* g#J#]#%%1oEorXkoX >.q#q#IoX e@:.{#b$O@8 ", +" o qOsX~@i@}.`@1$e%[%fO>.BX$%`#U.3.5$5$x@a#a#8$}#8.6ow$* U%<%M$M$M.;XJXH r@8.[X4oa#dXc.7@I$U.q $%C#H$K.[%K.>oS%q$C$=+(+1#^. ", +" o 4O1@w@s$n@,@V#H$fO[%q#P@M oXz@q &U.E@V C+5$5$ZX@#] e dXdX]XvoF%ZX5$5$V 3.hO &q koS%1$IofO[%fOIoX %'Ox W%% *%4Oo ", +" 4OQ#Q%t.VO}O9.8&5@K.q#[%q#P@>oBXoXl.` `X6+rXr#U.U.U.U.U.hO &6+q k$$%S%X >oIofO[%q#fO>.BX@OD+/$toY Uo3+G+ ", +" 8 ' ;+L$H#=&=Xt@WO1$H$e%q#[%[%q#K.P@>.H$5@>oM M >o>o5@H$>.P@K.fOq#[%q#K.>.1$WOd.MXk.{ow%@ s.iOOO ", +" G+J.Ko{ qo>O @/#2o&OWOM+>oH$P@e%fOq#q#[%q#q#q#q#q#K.Io>.>oM+C#O#jo2&9%IO($: T$EX@@-O ", +" o 8 @@g.z$uO7+;.#&nXR@vXB _ koz@$%$%l.l.$%z@` k$d$x+v#R$ $DOa bOZ.B+k jOOOV+ ", +" o f+=%P$0.To^@UXU#7 aoA.l#P+,O=OF+nONoQ k+jXUX^@v.T >$NXf+o ", +" OO-O~%p.j@}$.%Z%'$'$f.! oOu.*&s -OOO ", +" " +}; diff --git a/share/pixmaps/bitcoin16.png b/share/pixmaps/bitcoin16.png new file mode 100644 index 0000000..540fab0 Binary files /dev/null and b/share/pixmaps/bitcoin16.png differ diff --git a/share/pixmaps/bitcoin16.xpm b/share/pixmaps/bitcoin16.xpm new file mode 100644 index 0000000..c103200 --- /dev/null +++ b/share/pixmaps/bitcoin16.xpm @@ -0,0 +1,167 @@ +/* XPM */ +static char *graphic[] = { +/* width height num_colors chars_per_pixel */ +"16 16 144 2", +/* colors */ +" c None", +". c #888888", +"X c #AAAAAA", +"o c #CCCCCC", +"O c #979797", +"+ c #B9B9B9", +"@ c #DBDBDB", +"# c None", +"$ c #848484", +"% c #A6A6A6", +"& c #C8C8C8", +"* c #8F8F8F", +"= c #747474", +"- c #939393", +"; c #B5B5B5", +": c None", +"> c #F9F9F9", +", c None", +"< c None", +"1 c #A2A2A2", +"2 c #C4C4C4", +"3 c #707070", +"4 c #707070", +"5 c #9E9E9E", +"6 c #8F8F8F", +"7 c #B1B1B1", +"8 c #D3D3D3", +"9 c #686868", +"0 c #686868", +"q c #6C6C6C", +"w c #C0C0C0", +"e c #9E9E9E", +"r c #C0C0C0", +"t c #9A9A9A", +"y c None", +"u c #5C5C5C", +"i c #8B8B8B", +"p c #ADADAD", +"a c #CFCFCF", +"s c #9A9A9A", +"d c #BCBCBC", +"f c None", +"g c #969696", +"h c #878787", +"j c #A9A9A9", +"k c #CBCBCB", +"l c #969696", +"z c #B8B8B8", +"x c #B8B8B8", +"c c #DADADA", +"v c #C7C7C7", +"b c #E9E9E9", +"n c #A1A1A1", +"m c #929292", +"M c None", +"N c #B4B4B4", +"B c None", +"V c None", +"C c #7F7F7F", +"Z c #A1A1A1", +"A c None", +"S c #8A8A8A", +"D c #C3C3C3", +"F c #8A8A8A", +"G c None", +"H c #9D9D9D", +"J c #8E8E8E", +"K c None", +"L c #B0B0B0", +"P c #5F5F5F", +"I c #5F5F5F", +"U c #BFBFBF", +"Y c #BFBFBF", +"T c None", +"R c #5B5B5B", +"E c #CECECE", +"W c #999999", +"Q c #DDDDDD", +"! c None", +"~ c #868686", +"^ c #A8A8A8", +"/ c #CACACA", +"( c #737373", +") c #959595", +"_ c #B7B7B7", +"` c #B7B7B7", +"' c None", +"] c #828282", +"[ c #A4A4A4", +"{ c #C6C6C6", +"} c #A0A0A0", +"| c #919191", +" . c None", +".. c #B3B3B3", +"X. c None", +"o. c #6A6A6A", +"O. c #A0A0A0", +"+. c #6E6E6E", +"@. c #C2C2C2", +"#. c #6E6E6E", +"$. c #898989", +"%. c None", +"&. c #8D8D8D", +"*. c #AFAFAF", +"=. c #D1D1D1", +"-. c #F3F3F3", +";. c #5E5E5E", +":. c #5E5E5E", +">. c #BEBEBE", +",. c #9C9C9C", +"<. c #858585", +"1. c #989898", +"2. c #898989", +"3. c #EFEFEF", +"4. c #989898", +"5. c #A7A7A7", +"6. c None", +"7. c #C9C9C9", +"8. c #EBEBEB", +"9. c #909090", +"0. c #949494", +"q. c #B6B6B6", +"w. c #B6B6B6", +"e. c #D8D8D8", +"r. c #818181", +"t. c #A3A3A3", +"y. c #C5C5C5", +"u. c #B2B2B2", +"i. c None", +"p. c #D4D4D4", +"a. c None", +"s. c None", +"d. c #7D7D7D", +"f. c #9F9F9F", +"g. c #C1C1C1", +"h. c #E3E3E3", +"j. c None", +"k. c #656565", +"l. c None", +"z. c #AEAEAE", +"x. c #656565", +"c. c #BDBDBD", +"v. c #9B9B9B", +"b. c #BDBDBD", +/* pixels */ +" G y A I o.0 :.< # G ", +" 6.V = n w / 7.>.5 4 X.6. ", +" 6.s.9.o 7.j | 6 ..8 / F K 6. ", +"G V * p.d 1 ~ | l s u.^ o F X.G ", +"' = o r z.t.e 7.@.] C C ; & #.f ", +", } & f.t.s N e.x $ 5.@.v E t X.", +"P U N ^ 7 ,.a b b.0.w.5.. O.z u ", +"9 / v { X Z 8.-.g.2.. r.&.p 2 x.", +"9 / ` 1 ) @ > Q h J 4.*.2 2 2 k.", +";.c.L 1 4.7.3.y.~ - ,.W i % q.R ", +"V H =.{ X p.h.c o 7.v - d.r g a.", +"# 3 7.u.( 0.t.t.e W v.s g.2 q %.", +"G : S k j ..+ [ | m 1 Y E <. .! ", +" M l.$.& 8 x ) O z.7.2 <.j.i. ", +" i.a.+.1.z D D _ g q .i. ", +" G T B u x.x.R a.T G " +}; diff --git a/share/pixmaps/bitcoin256.png b/share/pixmaps/bitcoin256.png new file mode 100644 index 0000000..0f772ed Binary files /dev/null and b/share/pixmaps/bitcoin256.png differ diff --git a/share/pixmaps/bitcoin256.xpm b/share/pixmaps/bitcoin256.xpm new file mode 100644 index 0000000..ac3a979 --- /dev/null +++ b/share/pixmaps/bitcoin256.xpm @@ -0,0 +1,1969 @@ +/* XPM */ +static char *graphic[] = { +/* width height num_colors chars_per_pixel */ +"256 256 1706 2", +/* colors */ +" c None", +". c #444444", +"X c None", +"o c None", +"O c #5E5E5E", +"+ c #656565", +"@ c #8E8E8E", +"# c #5D5D5D", +"$ c None", +"% c #666666", +"& c #565656", +"* c #7F7F7F", +"= c None", +"- c #5D5D5D", +"; c #888888", +": c #4F4F4F", +"> c None", +", c #AAAAAA", +"< c #777777", +"1 c None", +"2 c #707070", +"3 c None", +"4 c #535353", +"5 c None", +"6 c #CCCCCC", +"7 c #313131", +"8 c None", +"9 c None", +"0 c #787878", +"q c None", +"w c #535353", +"e c None", +"r c #EEEEEE", +"t c None", +"y c #616161", +"u c None", +"i c #7F7F7F", +"p c #5A5A5A", +"a c #0F0F0F", +"s c #4B4B4B", +"d c None", +"f c #8A8A8A", +"g c None", +"h c None", +"j c #313131", +"k c None", +"l c #535353", +"z c #696969", +"x c #616161", +"c c None", +"v c None", +"b c #535353", +"n c None", +"m c None", +"M c #5A5A5A", +"N c None", +"B c #5E5E5E", +"V c #757575", +"C c #4F4F4F", +"Z c None", +"A c None", +"S c #686868", +"D c None", +"F c None", +"G c #5D5D5D", +"H c #979797", +"J c #6D6D6D", +"K c None", +"L c #656565", +"P c #7F7F7F", +"I c None", +"U c #B9B9B9", +"Y c None", +"T c #7B7B7B", +"R c None", +"E c #5A5A5A", +"W c #DBDBDB", +"Q c None", +"! c None", +"~ c #4F4F4F", +"^ c #868686", +"/ c None", +"( c #616161", +") c None", +"_ c None", +"` c None", +"' c #FDFDFD", +"] c #616161", +"[ c #686868", +"{ c #525252", +"} c None", +"| c #7B7B7B", +" . c #5D5D5D", +".. c #565656", +"X. c None", +"o. c None", +"O. c None", +"+. c #1E1E1E", +"@. c None", +"#. c None", +"$. c None", +"%. c #404040", +"&. c #4F4F4F", +"*. c #404040", +"=. c None", +"-. c #626262", +";. c None", +":. c #4B4B4B", +">. c #777777", +",. c #626262", +"<. c #4F4F4F", +"1. c #686868", +"2. c None", +"3. c None", +"4. c #525252", +"5. c #5D5D5D", +"6. c None", +"7. c #646464", +"8. c #6D6D6D", +"9. c None", +"0. c #848484", +"q. c #4F4F4F", +"w. c #4B4B4B", +"e. c None", +"r. c None", +"t. c None", +"y. c None", +"u. c #A6A6A6", +"i. c None", +"p. c None", +"a. c #525252", +"s. c #C8C8C8", +"d. c None", +"f. c #474747", +"g. c #8A8A8A", +"h. c None", +"j. c #696969", +"k. c #6C6C6C", +"l. c #EAEAEA", +"z. c None", +"x. c #616161", +"c. c None", +"v. c #4F4F4F", +"b. c None", +"n. c None", +"m. c #565656", +"M. c #0B0B0B", +"N. c #656565", +"B. c #4F4F4F", +"V. c #646464", +"C. c #2D2D2D", +"Z. c #5E5E5E", +"A. c #4F4F4F", +"S. c #5A5A5A", +"D. c None", +"F. c #4F4F4F", +"G. c #707070", +"H. c None", +"J. c #5A5A5A", +"K. c None", +"L. c #717171", +"P. c None", +"I. c #646464", +"U. c #595959", +"Y. c None", +"T. c None", +"R. c None", +"E. c #939393", +"W. c None", +"Q. c None", +"!. c #616161", +"~. c None", +"^. c None", +"/. c #686868", +"(. c #B5B5B5", +"). c None", +"_. c #4A4A4A", +"`. c None", +"'. c None", +"]. c #646464", +"[. c #606060", +"{. c #D7D7D7", +"}. c None", +"|. c None", +" X c None", +".X c #5D5D5D", +"XX c #7B7B7B", +"oX c #565656", +"OX c #525252", +"+X c #F9F9F9", +"@X c None", +"#X c #656565", +"$X c None", +"%X c None", +"&X c None", +"*X c #525252", +"=X c #1A1A1A", +"-X c None", +";X c #555555", +":X c #5E5E5E", +">X c None", +",X c #3C3C3C", +"o c None", +",o c None", +"O c #525252", +",O c None", +"+ c #CBCBCB", +",+ c None", +"<+ c None", +"1+ c #525252", +"2+ c None", +"3+ c None", +"4+ c None", +"5+ c #5C5C5C", +"6+ c None", +"7+ c #EDEDED", +"8+ c #595959", +"9+ c #4E4E4E", +"0+ c #3F3F3F", +"q+ c None", +"w+ c #767676", +"e+ c #555555", +"r+ c None", +"t+ c #0E0E0E", +"y+ c #303030", +"u+ c None", +"i+ c None", +"p+ c #525252", +"a+ c None", +"s+ c None", +"d+ c None", +"f+ c #525252", +"g+ c #5D5D5D", +"h+ c None", +"j+ c None", +"k+ c #7E7E7E", +"l+ c None", +"z+ c None", +"x+ c None", +"c+ c #747474", +"v+ c #555555", +"b+ c #4A4A4A", +"n+ c #3F3F3F", +"m+ c #606060", +"M+ c #969696", +"N+ c #676767", +"B+ c None", +"V+ c #B8B8B8", +"C+ c None", +"Z+ c #606060", +"A+ c None", +"S+ c #4E4E4E", +"D+ c #DADADA", +"F+ c None", +"G+ c #4E4E4E", +"H+ c None", +"J+ c #555555", +"K+ c #515151", +"L+ c #5C5C5C", +"P+ c None", +"I+ c #FCFCFC", +"U+ c None", +"Y+ c None", +"T+ c None", +"R+ c #3F3F3F", +"E+ c #1D1D1D", +"W+ c #4E4E4E", +"Q+ c None", +"!+ c #3F3F3F", +"~+ c None", +"^+ c None", +"/+ c None", +"(+ c #676767", +")+ c #4E4E4E", +"_+ c #7A7A7A", +"`+ c #616161", +"'+ c #4E4E4E", +"]+ c #595959", +"[+ c #6B6B6B", +"{+ c #5D5D5D", +"}+ c None", +"|+ c #595959", +" @ c #838383", +".@ c #606060", +"X@ c None", +"o@ c #585858", +"O@ c None", +"+@ c None", +"@@ c #A5A5A5", +"#@ c None", +"$@ c None", +"%@ c None", +"&@ c #585858", +"*@ c None", +"=@ c #4E4E4E", +"-@ c #7A7A7A", +";@ c None", +":@ c #5C5C5C", +">@ c #898989", +",@ c #6C6C6C", +"<@ c #C7C7C7", +"1@ c None", +"2@ c #595959", +"3@ c #4E4E4E", +"4@ c #6C6C6C", +"5@ c #5D5D5D", +"6@ c #E9E9E9", +"7@ c None", +"8@ c None", +"9@ c None", +"0@ c None", +"q@ c None", +"w@ c #0A0A0A", +"e@ c #686868", +"r@ c None", +"t@ c None", +"y@ c None", +"u@ c #2C2C2C", +"i@ c #5D5D5D", +"p@ c None", +"a@ c #646464", +"s@ c #4E4E4E", +"d@ c None", +"f@ c #5C5C5C", +"g@ c None", +"h@ c #707070", +"j@ c #585858", +"k@ c None", +"l@ c None", +"z@ c #929292", +"x@ c #7A7A7A", +"c@ c None", +"v@ c None", +"b@ c #B4B4B4", +"n@ c #4A4A4A", +"m@ c None", +"M@ c #767676", +"N@ c None", +"B@ c None", +"V@ c #D6D6D6", +"C@ c None", +"Z@ c #7A7A7A", +"A@ c #676767", +"S@ c None", +"D@ c #767676", +"F@ c #595959", +"G@ c #F8F8F8", +"H@ c #545454", +"J@ c #5D5D5D", +"K@ c None", +"L@ c #636363", +"P@ c None", +"I@ c None", +"U@ c None", +"Y@ c None", +"T@ c #191919", +"R@ c None", +"E@ c None", +"W@ c #5D5D5D", +"Q@ c #3B3B3B", +"!@ c None", +"~@ c #606060", +"^@ c None", +"/@ c #4D4D4D", +"(@ c #727272", +")@ c None", +"_@ c None", +"`@ c #4A4A4A", +"'@ c None", +"]@ c #5D5D5D", +"[@ c None", +"{@ c None", +"}@ c #545454", +"|@ c #636363", +" # c None", +".# c #7F7F7F", +"X# c None", +"o# c #585858", +"O# c #4A4A4A", +"+# c None", +"@# c None", +"## c #464646", +"$# c #A1A1A1", +"%# c #4A4A4A", +"&# c #727272", +"*# c #636363", +"=# c None", +"-# c #5F5F5F", +";# c None", +":# c #C3C3C3", +"># c #5C5C5C", +",# c #737373", +"<# c #595959", +"1# c #606060", +"2# c #E5E5E5", +"3# c #4A4A4A", +"4# c #585858", +"5# c None", +"6# c #727272", +"7# c #5C5C5C", +"8# c None", +"9# c #6E6E6E", +"0# c #5F5F5F", +"q# c #636363", +"w# c #595959", +"e# c None", +"r# c #060606", +"t# c #A3A3A3", +"y# c #4A4A4A", +"u# c None", +"i# c #515151", +"p# c #282828", +"a# c #4A4A4A", +"s# c None", +"d# c #909090", +"f# c #5F5F5F", +"g# c None", +"h# c None", +"j# c None", +"k# c None", +"l# c #858585", +"z# c #555555", +"x# c None", +"c# c #6C6C6C", +"v# c #636363", +"b# c #595959", +"n# c #8E8E8E", +"m# c #5C5C5C", +"M# c None", +"N# c None", +"B# c #595959", +"V# c #464646", +"C# c #858585", +"Z# c None", +"A# c #B0B0B0", +"S# c None", +"D# c #5F5F5F", +"F# c #636363", +"G# c #D2D2D2", +"H# c None", +"J# c #595959", +"K# c None", +"L# c #545454", +"P# c #F4F4F4", +"I# c #515151", +"U# c #494949", +"Y# c None", +"T# c #595959", +"R# c #464646", +"E# c None", +"W# c None", +"Q# c #606060", +"!# c #464646", +"~# c #151515", +"^# c None", +"/# c None", +"(# c #515151", +")# c None", +"_# c #373737", +"`# c None", +"'# c #5C5C5C", +"]# c None", +"[# c #595959", +"{# c None", +"}# c #595959", +"|# c None", +" $ c #555555", +".$ c #5F5F5F", +"X$ c #585858", +"o$ c #606060", +"O$ c #636363", +"+$ c #7B7B7B", +"@$ c None", +"#$ c #4D4D4D", +"$$ c None", +"%$ c None", +"&$ c #464646", +"*$ c #5F5F5F", +"=$ c None", +"-$ c None", +";$ c None", +":$ c None", +">$ c #9D9D9D", +",$ c None", +"<$ c #727272", +"1$ c None", +"2$ c None", +"3$ c None", +"4$ c None", +"5$ c #BFBFBF", +"6$ c None", +"7$ c None", +"8$ c #494949", +"9$ c #636363", +"0$ c None", +"q$ c None", +"w$ c #585858", +"e$ c #646464", +"r$ c None", +"t$ c #E1E1E1", +"y$ c #4D4D4D", +"u$ c None", +"i$ c #555555", +"p$ c #5C5C5C", +"a$ c #6B6B6B", +"s$ c None", +"d$ c None", +"f$ c #464646", +"g$ c #636363", +"h$ c #6F6F6F", +"j$ c #545454", +"k$ c None", +"l$ c #242424", +"z$ c None", +"x$ c None", +"c$ c #737373", +"v$ c None", +"b$ c #585858", +"n$ c None", +"m$ c None", +"M$ c #464646", +"N$ c None", +"B$ c #4D4D4D", +"V$ c None", +"C$ c None", +"Z$ c #686868", +"A$ c None", +"S$ c None", +"D$ c #555555", +"F$ c None", +"G$ c None", +"H$ c #8A8A8A", +"J$ c None", +"K$ c #646464", +"L$ c #5F5F5F", +"P$ c #6E6E6E", +"I$ c None", +"U$ c #585858", +"Y$ c None", +"T$ c #8C8C8C", +"R$ c None", +"E$ c #494949", +"W$ c None", +"Q$ c #ACACAC", +"!$ c #585858", +"~$ c None", +"^$ c #6A6A6A", +"/$ c #555555", +"($ c #515151", +")$ c #CECECE", +"_$ c None", +"`$ c None", +"'$ c #6E6E6E", +"]$ c #5F5F5F", +"[$ c #555555", +"{$ c None", +"}$ c None", +"|$ c #F0F0F0", +" % c #585858", +".% c None", +"X% c #606060", +"o% c None", +"O% c #6A6A6A", +"+% c None", +"@% c None", +"#% c #111111", +"$% c #676767", +"%% c None", +"&% c None", +"*% c #555555", +"=% c None", +"-% c #333333", +";% c None", +":% c #4D4D4D", +">% c #6E6E6E", +",% c #555555", +"<% c None", +"1% c #5F5F5F", +"2% c #6F6F6F", +"3% c #555555", +"4% c None", +"5% c None", +"6% c None", +"7% c None", +"8% c #424242", +"9% c #777777", +"0% c #515151", +"q% c None", +"w% c #9B9B9B", +"e% c None", +"r% c #797979", +"t% c #6E6E6E", +"y% c #999999", +"u% c None", +"i% c None", +"p% c None", +"a% c None", +"s% c #545454", +"d% c #BBBBBB", +"f% c #585858", +"g% c None", +"h% c #727272", +"j% c #6B6B6B", +"k% c None", +"l% c #5B5B5B", +"z% c #DDDDDD", +"x% c #515151", +"c% c #5C5C5C", +"v% c None", +"b% c None", +"n% c None", +"m% c #FFFFFF", +"M% c None", +"N% c None", +"B% c None", +"V% c #646464", +"C% c None", +"Z% c #4D4D4D", +"A% c #202020", +"S% c #606060", +"D% c None", +"F% c #666666", +"G% c #6F6F6F", +"H% c #424242", +"J% c None", +"K% c None", +"L% c #5F5F5F", +"P% c None", +"I% c None", +"U% c #646464", +"Y% c None", +"T% c #515151", +"R% c None", +"E% c #6F6F6F", +"W% c None", +"Q% c None", +"!% c None", +"~% c #868686", +"^% c None", +"/% c None", +"(% c None", +")% c #585858", +"_% c #606060", +"`% c #5C5C5C", +"'% c None", +"]% c #5C5C5C", +"[% c #A8A8A8", +"{% c None", +"}% c None", +"|% c #4D4D4D", +" & c None", +".& c None", +"X& c #515151", +"o& c #5F5F5F", +"O& c None", +"+& c #CACACA", +"@& c None", +"#& c #636363", +"$& c None", +"%& c None", +"&& c None", +"*& c #ECECEC", +"=& c #545454", +"-& c None", +";& c #5F5F5F", +":& c None", +">& c #494949", +",& c #5C5C5C", +"<& c #4D4D4D", +"1& c None", +"2& c None", +"3& c None", +"4& c None", +"5& c #0D0D0D", +"6& c #4D4D4D", +"7& c #2F2F2F", +"8& c None", +"9& c None", +"0& c None", +"q& c #515151", +"w& c None", +"e& c #797979", +"r& c #515151", +"t& c #5F5F5F", +"y& c #585858", +"u& c #6A6A6A", +"i& c None", +"p& c #4D4D4D", +"a& c #737373", +"s& c #5B5B5B", +"d& c None", +"f& c #636363", +"g& c None", +"h& c #959595", +"j& c #6E6E6E", +"k& c #888888", +"l& c None", +"z& c #585858", +"x& c None", +"c& c None", +"v& c #B7B7B7", +"b& c None", +"n& c #5C5C5C", +"m& c None", +"M& c #676767", +"N& c None", +"B& c #545454", +"V& c #666666", +"C& c #575757", +"Z& c None", +"A& c None", +"S& c #636363", +"D& c #494949", +"F& c #D9D9D9", +"G& c #4D4D4D", +"H& c None", +"J& c #979797", +"K& c None", +"L& c #757575", +"P& c #FBFBFB", +"I& c #3E3E3E", +"U& c None", +"Y& c None", +"T& c None", +"R& c #575757", +"E& c #6A6A6A", +"W& c None", +"Q& c None", +"!& c #5C5C5C", +"~& c #545454", +"^& c None", +"/& c None", +"(& c #1C1C1C", +")& c None", +"_& c #5C5C5C", +"`& c None", +"'& c #7D7D7D", +"]& c #6B6B6B", +"[& c #5B5B5B", +"{& c #979797", +"}& c #3E3E3E", +"|& c None", +" * c #4D4D4D", +".* c None", +"X* c #6E6E6E", +"o* c None", +"O* c None", +"+* c #6A6A6A", +"@* c None", +"#* c #606060", +"$* c #585858", +"%* c #606060", +"&* c #757575", +"** c None", +"=* c None", +"-* c #5B5B5B", +";* c #828282", +":* c #4D4D4D", +">* c None", +",* c #585858", +"<* c None", +"1* c None", +"2* c None", +"3* c None", +"4* c #A4A4A4", +"5* c #535353", +"6* c #6B6B6B", +"7* c #C6C6C6", +"8* c #494949", +"9* c None", +"0* c #585858", +"q* c #4D4D4D", +"w* c #626262", +"e* c None", +"r* c None", +"t* c #636363", +"y* c #676767", +"u* c #E8E8E8", +"i* c #4D4D4D", +"p* c #6A6A6A", +"a* c #4C4C4C", +"s* c #575757", +"d* c None", +"f* c None", +"g* c #090909", +"h* c #888888", +"j* c None", +"k* c #666666", +"l* c None", +"z* c #4D4D4D", +"x* c None", +"c* c #575757", +"v* c None", +"b* c #2B2B2B", +"n* c #5B5B5B", +"m* c #494949", +"M* c #585858", +"N* c #4D4D4D", +"B* c None", +"V* c None", +"C* c None", +"Z* c None", +"A* c #6F6F6F", +"S* c None", +"D* c #626262", +"F* c None", +"G* c None", +"H* c None", +"J* c None", +"K* c #636363", +"L* c #767676", +"P* c #919191", +"I* c #757575", +"U* c None", +"Y* c #626262", +"T* c None", +"R* c #5B5B5B", +"E* c #585858", +"W* c #B3B3B3", +"Q* c None", +"!* c None", +"~* c #676767", +"^* c #6E6E6E", +"/* c #5C5C5C", +"(* c #D5D5D5", +")* c None", +"_* c None", +"`* c #727272", +"'* c #505050", +"]* c #494949", +"[* c None", +"{* c #5C5C5C", +"}* c #6A6A6A", +"|* c None", +" = c None", +".= c #5B5B5B", +"X= c None", +"o= c #F7F7F7", +"O= c None", +"+= c #727272", +"@= c #5C5C5C", +"#= c #181818", +"$= c #5F5F5F", +"%= c #494949", +"&= c None", +"*= c None", +"== c #3A3A3A", +"-= c None", +";= c None", +":= c #797979", +">= c None", +",= c #5C5C5C", +"<= c #5C5C5C", +"1= c None", +"2= c #585858", +"3= c None", +"4= c #A2A2A2", +"5= c #7E7E7E", +"6= c None", +"7= c #626262", +"8= c None", +"9= c #A0A0A0", +"0= c #505050", +"q= c #939393", +"w= c #6E6E6E", +"e= c #585858", +"r= c None", +"t= c #494949", +"y= c #C2C2C2", +"u= c #505050", +"i= c None", +"p= c #6A6A6A", +"a= c #575757", +"s= c #585858", +"d= c #E4E4E4", +"f= c None", +"g= c #494949", +"h= c #5E5E5E", +"j= c #626262", +"k= c None", +"l= c None", +"z= c #5B5B5B", +"x= c #050505", +"c= c None", +"v= c #585858", +"b= c #545454", +"n= c #636363", +"m= c #5E5E5E", +"M= c None", +"N= c #272727", +"B= c None", +"V= c #626262", +"C= c #6A6A6A", +"Z= c #676767", +"A= c #494949", +"S= c #363636", +"D= c #5E5E5E", +"F= c None", +"G= c None", +"H= c #6D6D6D", +"J= c None", +"K= c #6B6B6B", +"L= c #5B5B5B", +"P= c #4C4C4C", +"I= c #545454", +"U= c #8F8F8F", +"Y= c #8D8D8D", +"T= c None", +"R= c #575757", +"E= c #626262", +"W= c #585858", +"Q= c None", +"!= c #AFAFAF", +"~= c None", +"^= c None", +"/= c None", +"(= c None", +")= c None", +"_= c #D1D1D1", +"`= c None", +"'= c None", +"]= c #535353", +"[= c #545454", +"{= c #585858", +"}= c None", +"|= c None", +" - c #F3F3F3", +".- c None", +"X- c None", +"o- c #585858", +"O- c #141414", +"+- c None", +"@- c None", +"#- c None", +"$- c None", +"%- c None", +"&- c #363636", +"*- c None", +"=- c None", +"-- c #454545", +";- c #636363", +":- c None", +">- c #4C4C4C", +",- c #585858", +"<- c #5E5E5E", +"1- c #505050", +"2- c None", +"3- c #7A7A7A", +"4- c None", +"5- c None", +"6- c None", +"7- c #545454", +"8- c #9C9C9C", +"9- c #535353", +"0- c None", +"q- c None", +"w- c #BEBEBE", +"e- c None", +"r- c #6D6D6D", +"t- c None", +"y- c None", +"u- c #E0E0E0", +"i- c #454545", +"p- c #696969", +"a- c #5A5A5A", +"s- c #636363", +"d- c #545454", +"f- c #8F8F8F", +"g- c #535353", +"h- c #454545", +"j- c #575757", +"k- c #545454", +"l- c None", +"z- c #666666", +"x- c None", +"c- c None", +"v- c #5B5B5B", +"b- c #232323", +"n- c #727272", +"m- c None", +"M- c #454545", +"N- c #505050", +"B- c None", +"V- c #696969", +"C- c #626262", +"Z- c #5B5B5B", +"A- c #8B8B8B", +"S- c #545454", +"D- c #6E6E6E", +"F- c #676767", +"G- c #575757", +"H- c None", +"J- c #545454", +"K- c #575757", +"L- c #898989", +"P- c None", +"I- c None", +"U- c None", +"Y- c #4C4C4C", +"T- c #ABABAB", +"R- c #5A5A5A", +"E- c None", +"W- c None", +"Q- c #545454", +"!- c #5B5B5B", +"~- c #5E5E5E", +"^- c #787878", +"/- c #CDCDCD", +"(- c #808080", +")- c #545454", +"_- c None", +"`- c #EFEFEF", +"'- c #575757", +"]- c None", +"[- c #727272", +"{- c #7C7C7C", +"}- c #505050", +"|- c None", +" ; c #666666", +".; c #101010", +"X; c #545454", +"o; c #5E5E5E", +"O; c #5F5F5F", +"+; c #323232", +"@; c None", +"#; c None", +"$; c None", +"%; c #575757", +"&; c None", +"*; c #636363", +"=; c #535353", +"-; c #545454", +";; c None", +":; c None", +">; c None", +",; c #5E5E5E", +"<; c None", +"1; c #767676", +"2; c None", +"3; c None", +"4; c #575757", +"5; c None", +"6; c #989898", +"7; c #505050", +"8; c #696969", +"9; c None", +"0; c #636363", +"q; c #9A9A9A", +"w; c None", +"e; c #BABABA", +"r; c #626262", +"t; c #5B5B5B", +"y; c #5F5F5F", +"u; c #5E5E5E", +"i; c None", +"p; c #DCDCDC", +"a; c #6A6A6A", +"s; c None", +"d; c #717171", +"f; c None", +"g; c None", +"h; c #5E5E5E", +"j; c None", +"k; c #4C4C4C", +"l; c #FEFEFE", +"z; c None", +"x; c None", +"c; c #757575", +"v; c None", +"b; c #575757", +"n; c None", +"m; c #717171", +"M; c #1F1F1F", +"N; c #5B5B5B", +"B; c None", +"V; c #414141", +"C; c #5E5E5E", +"Z; c None", +"A; c #414141", +"S; c #3D3D3D", +"D; c None", +"F; c None", +"G; c #656565", +"H; c #696969", +"J; c None", +"K; c None", +"L; c #636363", +"P; c None", +"I; c #505050", +"U; c None", +"Y; c None", +"T; c #575757", +"R; c #8B8B8B", +"E; c #6E6E6E", +"W; c None", +"Q; c #858585", +"!; c None", +"~; c None", +"^; c #575757", +"/; c None", +"(; c #5A5A5A", +"); c #A7A7A7", +"_; c #6E6E6E", +"`; c #505050", +"'; c #C9C9C9", +"]; c None", +"[; c #747474", +"{; c #575757", +"}; c None", +"|; c #5E5E5E", +" : c None", +".: c #EBEBEB", +"X: c #5B5B5B", +"o: c #4C4C4C", +"O: c #5A5A5A", +"+: c None", +"@: c #0C0C0C", +"#: c #747474", +"$: c None", +"%: c None", +"&: c #2E2E2E", +"*: c #575757", +"=: c #5E5E5E", +"-: c #505050", +";: c None", +":: c None", +">: c #505050", +",: c None", +"<: c None", +"1: c #656565", +"2: c #484848", +"3: c #4C4C4C", +"4: c None", +"5: c None", +"6: c #5E5E5E", +"7: c #5B5B5B", +"8: c #6E6E6E", +"9: c #6A6A6A", +"0: c #727272", +"q: c None", +"w: c None", +"e: c #3D3D3D", +"r: c #949494", +"t: c None", +"y: c #5A5A5A", +"u: c None", +"i: c #666666", +"p: c #B6B6B6", +"a: c None", +"s: c #4C4C4C", +"d: c None", +"f: c None", +"g: c None", +"h: c #3D3D3D", +"j: c #D8D8D8", +"k: c #5F5F5F", +"l: c #4C4C4C", +"z: c None", +"x: c #787878", +"c: c #5A5A5A", +"v: c #FAFAFA", +"b: c None", +"n: c None", +"m: c #656565", +"M: c None", +"N: c #5F5F5F", +"B: c None", +"V: c #3D3D3D", +"C: c #1B1B1B", +"Z: c None", +"A: c #3D3D3D", +"S: c #4C4C4C", +"D: c None", +"F: c None", +"G: c None", +"H: c None", +"J: c #5F5F5F", +"K: c #484848", +"L: c #969696", +"P: c None", +"I: c #5E5E5E", +"U: c #616161", +"Y: c #5F5F5F", +"T: c #616161", +"R: c #6D6D6D", +"E: c None", +"W: c #5B5B5B", +"Q: c None", +"!: c None", +"~: c #818181", +"^: c None", +"/: c #4C4C4C", +"(: c None", +"): c #5E5E5E", +"_: c #A3A3A3", +"`: c None", +"': c None", +"]: c #666666", +"[: c #6A6A6A", +"{: c #C5C5C5", +"}: c #5A5A5A", +"|: c #4C4C4C", +" > c None", +".> c #575757", +"X> c None", +"o> c #747474", +"O> c None", +"+> c #535353", +"@> c #E7E7E7", +"#> c #747474", +"$> c None", +"%> c #4C4C4C", +"&> c None", +"*> c #656565", +"=> c None", +"-> c #6A6A6A", +";> c None", +":> c #616161", +">> c #4C4C4C", +",> c #080808", +"<> c #656565", +"1> c None", +"2> c None", +"3> c #5B5B5B", +"4> c #707070", +"5> c None", +"6> c #4C4C4C", +"7> c #2A2A2A", +"8> c None", +"9> c None", +"0> c None", +"q> c #4C4C4C", +"w> c #535353", +"e> c #616161", +"r> c None", +"t> c #6E6E6E", +"y> c None", +"u> c #4F4F4F", +"i> c #5B5B5B", +"p> c None", +"a> c None", +"s> c #909090", +"d> c None", +"f> c None", +"g> c None", +"h> c #4B4B4B", +"j> c None", +"k> c None", +"l> c #5A5A5A", +"z> c None", +"x> c #B2B2B2", +"c> c #656565", +"v> c #707070", +"b> c #7F7F7F", +"n> c #535353", +"m> c None", +"M> c #616161", +"N> c #747474", +"B> c None", +"V> c None", +"C> c #D4D4D4", +"Z> c None", +"A> c None", +"S> c #5E5E5E", +"D> c #575757", +"F> c #5A5A5A", +"G> c None", +"H> c #A1A1A1", +"J> c None", +"K> c None", +"L> c #F6F6F6", +"P> c #616161", +"I> c #393939", +"U> c None", +"Y> c None", +"T> c #171717", +"R> c #535353", +"E> c #393939", +"W> c #757575", +"Q> c None", +"!> c #616161", +"~> c #787878", +"^> c #393939", +"/> c #575757", +"(> c #4F4F4F", +")> c #5A5A5A", +"_> c None", +"`> c None", +"'> c None", +"]> c #5B5B5B", +"[> c #616161", +"{> c #707070", +"}> c None", +"|> c #575757", +" , c None", +"., c None", +"X, c None", +"o, c #535353", +"O, c None", +"+, c #7D7D7D", +"@, c #484848", +"#, c #5E5E5E", +"$, c #787878", +"%, c #9F9F9F", +"&, c None", +"*, c #5A5A5A", +"=, c #707070", +"-, c None", +";, c #696969", +":, c None", +">, c #696969", +",, c #C1C1C1", +"<, c None", +"1, c #4F4F4F", +"2, c None", +"3, c #5D5D5D", +"4, c None", +"5, c #5E5E5E", +"6, c None", +"7, c #484848", +"8, c #6D6D6D", +"9, c #535353", +"0, c #E3E3E3", +"q, c None", +"w, c None", +"e, c #575757", +"r, c #616161", +"t, c #484848", +"y, c None", +"u, c #5A5A5A", +"i, c #444444", +"p, c #929292", +"a, c #484848", +"s, c #262626", +"d, c None", +"f, c None", +"g, c #575757", +"h, c #616161", +"j, c None", +"k, c None", +"l, c #535353", +"z, c #484848", +"x, c None", +"c, c None", +"v, c None", +"b, c #575757", +"n, c #666666", +"m, c None", +"M, c None", +"N, c #6A6A6A", +"B, c None", +"V, c #535353", +"C, c None", +"Z, c None", +"A, c #666666", +"S, c #8C8C8C", +"D, c None", +"F, c None", +"G, c #626262", +"H, c #AEAEAE", +"J, c #6C6C6C", +"K, c #5A5A5A", +"L, c None", +"P, c #535353", +"I, c #656565", +"U, c None", +"Y, c #565656", +"T, c #D0D0D0", +"R, c #5A5A5A", +"E, c None", +"W, c #535353", +"Q, c None", +"!, c #575757", +"~, c #5D5D5D", +"^, c None", +"/, c #707070", +"(, c #4F4F4F", +"), c #F2F2F2", +"_, c None", +"`, c None", +"', c #717171", +"], c None", +"[, c #131313", +"{, c None", +"}, c #353535", +"|, c #5E5E5E", +" < c None", +".< c #535353", +"X< c None", +"o< c None", +"O< c #575757", +"+< c #9D9D9D", +"@< c #747474", +"#< c #575757", +"$< c #4F4F4F", +"%< c #6C6C6C", +"&< c None", +"*< c #656565", +"=< c #797979", +"-< c None", +";< c None", +":< c None", +">< c None", +",< c #5A5A5A", +"<< c #5D5D5D", +"1< c #535353", +"2< c None", +"3< c #9B9B9B", +"4< c None", +"5< c None", +"6< c None", +"7< c #656565", +"8< c None", +"9< c #BDBDBD", +"0< c #616161", +"q< c None", +"w< c #444444", +"e< c #DFDFDF", +"r< c #5A5A5A", +"t< c #595959", +"y< c #696969", +"u< c None", +"i< c #444444", +"p< c #535353", +"a< c #787878", +"s< c #000000", +"d< c None", +"f< c None", +"g< c #717171", +"h< c #4F4F4F", +"j< c #4B4B4B", +"k< c None", +"l< c #222222", +"z< c #5D5D5D", +"x< c None", +"c< c #616161", +/* pixels */ +" ", +" ", +" ", +" P%8#8#8#8#6,I%Q:fc yo*=.%5 **X,7%%:LXn$C%;#G$8#8#8#8#P% ", +" P%8#v%v%8#VoKOM,fO;$'>A&e 2%LOLOG%3X8.6*[:e$CXZ=Z=->Ho4@,@E;G%G%g<8:@3*%-gO8#v%v%8#P% ", +" P%v%8#O>%XF m-J=;>> ZX.o9:X%(Xl,h<($F.>:>:s@N*q>N*a#z,a#a#a#a#WXq>N*s@s@F.F.r&&.l,l,z#(XX%2%L* =x,PXJ=AXJ>6-s#v%8# ", +" P%v%v R$g u:9@i;]oD-n=o,4XY-/:N*a#z,M-H%A;*.!+!+}&,XQ@==,X==^>^>^>qo^>==Q@Q@,X}&!+!+*.A;A;`O. z,WXN*F.N-oX$*|+SXh$P #Ou:K#%&h v%v%P% ", +" $ 8#v d@W;wX5Xd#+=Q#~&:.F.s@a#. A;A;!+,X^>&-_#},-%j +;y+y+7&7&7&7&7&7&7&j y+7&7&y+7&7&y+y+j +;+;tO&-_#qo==,X}&A;H%. z,s@r&7,fop i:EO&&m$J;K;-=P%8#P% ", +" $ v%=**-U,^.p,8,p$Xo##F.A=. H%}&qo_#&-tO+;y+j y++;-%y+y+j -%+;+;-%tOtOtO},tOtO},tOtOtOtOtO+;+;j +;j j +;j j j j j +;-%tO&-^>Q@A:*.M$z,F.r&>-4;1#-OWO^&,:&Xuo$ P% ", +" P%n.(:a>J%G.K-E$z*>:A=`O*.Q@_#&--%j j j j +;j j -%},_#qoqo==A:}&A;*.H%H%. . `O. . `O. . . . `OA;A;A;*.A:A:Q@qo_#&--%+;+;-%j -%+;j j tO_#Q@,X*.M-_oF.f+k;=;S&D@c&_ K>c, ", +" {#Q%g&a%>,Y,a.-;A=M-*.==_#},+;+;j +;+;j -%tOqoQ@,XA:A;`O. M-z,A=A=WXN*N*>:>:r&>:>:r&r&r&r&r&r&f+>:F.F.s@N*a#z,_oM$. `OH%A;*.,X==_#tOj y+j j +;+;-%_#^>,X*.z,a#s@7-L#):_+^.Q=eXI ", +" T+IXl=|=>.g-*X3%z,M-A:==_#-%j +;y+y+-%},^>A:!+H%. M$z,A=q>s@r&-;-;3%,-}#]><=<=6XY:Y:%*%*`+`+`+,.`+%*%*Y:Y:6X<=]@]>,-hO3%b f+F.N*WXA=_o. `OA;A:==_#+;j y+y++;+;tO^>A:*.M-A=s@e,j$-*-o|&W${$s# ", +" r$x#y-&*,;l%+>WX`O,X^>},-%+;j 7&+;_#==A:*.H%M-z,q>F.>:f+hO,-io<=`+L;}O% Z$N,K=K=)XA*t>t>A*A*h@h@A*A*A*t>A*h@A*)Xc#c#N,F-Z$% }OL;]@io}##:s@a#M$`O!+}&,X_#tOy+7&+;-%tOqo,X!+. WX>:,-s%<-o%/&K%5; ", +" P%K.n%^-].m=/>F.M-*.^>},-%j 7&y+tOqo,X!+A;M-A=N*F.f+3%,-<=%*,.% Z$N,K=A*L.L.h@A*A*t>A*t>)Xt>A*A*A*t>)XN,c#)Xt>A*A*A*t>A*A*A*t>A*h@A*A*c#K=K=F-U%L;Y:<=,-b >:N*WXM$`OA;}&Q@&-+;7&7&+;},qo,XA;M$q>b .=[.- X={$y> ", +" G$` 6+!@[>@=F.A=A;==&--%y+7&j },^>,X!+`Oz,WXs@b 3%]>Y:L;F-N,c#A*L.h@h@A*A*t>)X}O6X]>]>io]>]>]>`+,.}OZ$N,c#t>A*c#N,Z$}O`+Y:<=ioioioio]>6X}Ot>h@A*A*A*A*c#[oZ$}O`+6Xio-;F.N*A=M-A;A:==&--%7&y++;&-^>!+. a#F.,-~@t<8&:$x*8# ", +" !%n }$9./.5@t=M$!+^>tOy+7&j tO_#,X}&. z,q>>:3%ioY:U%F-K=t>L.h@h@0:)XU%6X<=io<=U%K=A*h@h@A*t>h@a&c++$5= @H$s>M+y%mOh&n#X+rXgokOc+h@A*A*h@L.L.t>Z$`+}#io6XY:F-)Xh@h@t>K=[o}O`+<=hOb N*a#M-A;}&^>&-+;7&j tO_#==*._oh-r&1#jX~+O,cXG$ ", +" !%s#t@B-j&F@8%. ==_#+;7&7&-%qoQ@!+`Oz,N*r&hO]>,.% K=A*A*A*L.A*F-6X<=L;N,c#)XK=Z$A*+,P*iXoov&U e;d%e;e;d%BOCoy=:#kXkX{:{:y=5$9<9Y ", +" 8#R$wo7X[X9,xo`OQ@},y+&:y+tOqo!+. _oq>>:hO]@,.F-)XA*h@L.A*% ]@Y:}O[oF-% a&S,$#!=b@W*b@e;y=<@/-_=G##oC>G#_=T,_=_=_=;O;O)$)$;OT,T,_=G#G#_=G##o_=T,;O/-<@w-V+b@b@A#, >$~%t>U%[oZ$L;6X`+N,h@L.)X[o}OY:}#f+F.a#. !+==&--%j y+-%qo,X. a#UX]%9#POQXX v ", +" P%u%#O{oa$(,a,`O==+;y+7&-%tO^>*.M-a#F.hO]@`+Z$A*L.0:0:Z$%*,.Z$Z$U%L;c+E.$+!=b@Co+&)$#o(*#oG#T,)$>+>++&>+)$/->+/-6 6 /-/-6 /-6 /-/-)$/-/-)$/-6 +&6 +&+&6 6 T,#o(*G#_=/-7*BOooQ$@@ Ot>,.% N,F-,.L;c#L.t>N,L;%*iof+WX_o`OA:_#tOy+7&j &-,X. a#k;DX{>X.QXcX ", +" cXY>[@COo+IOH%Q@+;j y++;&-A:`O_oq>b ,-6X% )Xh@a&a&K=L;F-}O6X,.kOM+);$+A#y=_=V@#oT,)$>++&6 /-;O)$;O)$)$)$)$T,G#_=G#T,;OT,_=T,G#_=G#_=#o_=T,G#_=T,;O;O)$)$)$/-)$/-6 +&>++&>+T,C>#o6 93%F.A=M-*.^>tO+;y+j &-,X. q>$a#`O^>+;y+y+-%qoQ@`Oa#r&-;ioL;N,A*0:0:)X% % U%]>Z$H$9=@@A#kXG#C>_=;O+&>+/-/-/-;O;O;OT,G##oG#G##o#o#o#o(*C>(*V@(*(*V@V@V@V@V@(*V@V@C>(*(*C>#o#oG#_=_=G#T,;O;O;O/-/-/-6 +&>++&)$C>G#6 w-Q$_:3<.#%*]@Z$[oZ$)Xh@c#U%6X#M$*.Q@},j y+y+_#A:M-s@D&i#T Oo;% ", +" `#_$ <.@(>h:==tOy+y+j ^>}&. A=r&,-]@,.c#h@c+t>[oZ$U%io`+0.6;iXd%;O#o;O6 +&>+6 /-)$;OG##oT,G##oG##o#o(*(*(*j:j:j:j:j:j:j:F&D+D+W W W W F&j:j:{.j:j:j:F&j:{.(*(*(*C>#o_=#oG#T,G#_=T,)$;O/-6 +&';+&>+;O_=';x>coE.go]@<=}OZ$[o)XA*F-6X}#b q>_oH%Q@},j y+-%==!+. f+G&G x,k -= ", +" .Oo.<;r;u=z,!+&-j y+j _#A:`OA=>:3%6X% c#A*L.K=K=L;#+6 /-)$T,_=T,G#G#C>V@V@V@j:V@{.F&D+D+D+D+p;p;z%z%p;z%z%p;z%ZOZOZOZOz%z%z%z%z%z%z%p;W D+D+D+D+F&j:{.j:{.(*(*C>G#G#G#_=T,)$/-6 6 >+';';>+)$7*H,M+n#a&,-io[o)XN,c#K=`+iob WXM-*.,XtOy+j tO==A;_o}-vo!XO.y@ ", +" _XXOS*m.r&`Oqo+;y+y+&-A:A;A=F.3%<=U%c#L.L.N,F-]>]@1;L-h&oo>+6 >++&6 /-)$;O;OT,G#G##oC>C>{.{.F&D+F&D+W p;z%z%ZOZOZOZOe#o_=T,T,T,/-)$6 +&';s.+&6 y=@@n#Q;t>#M-!+qo-%y+y+tOQ@`Os@q.s&]Ol=k@ ", +" k$3&#@c:OXM-,XtOy+y+},==A;_oN*3%<=% t>0:h@N,Z$,-%*=< O$+7*6 +&+&6 6 )$)$;OT,T,G#(*C>(*{.j:F&F&W p;p;ZOZOeG#_=T,)$)$/-6 6 >+';';s.9<>$ @0:]>]>N,K=)Xc#L;,->:WXM-}&_#j y+j _#*._o/:,;0N*R+_#+;y+-%==!+M$q>b <=U%c#L.h@c#U%,-% +$H$x><@6 /-/-/-/-;OT,T,_=#o#o(*j:F&F&W W W ZOZOe2#vX@>2#@>u*u*l.6@l.6@l.u*u*u*@>@>vXvXvX@>vX2#0,d=d=0,KoKot$u-u-e+>+>++&s.,,u.Q;c+`+}#}OK=c#[o,.#<>:z,A;,X&-j y+},Q@`O/:bXQOr,d&I ", +" P%];l@c*tXA;==+;7&+;_#}&M-WXf+ioL;K=h@L.)XY:]>K==< OU kX>+)$;O6 )$;OT,_=#oC>(*j:F&F&D+W ZOZOe@>@>d=d=0,t$Kot$u-u-u-z%p;D+D+D+{.{.C>G##o;OT,;O>+>+>+/->+s.kX$OX+c+U%,-L;t>K=[oY:3%s@z,A;Q@-%7&+;qo*.. S:3>t&B:Y@cX ", +" $ 2o4%w+|+3oe:},y+7&-%Q@H%A=>:,-`+K=c+A*t>`+%*K=9%h&w-:#6 T,/-/-/-T,G#G#(*{.j:j:D+W z%ZOZOZOu-t$0,0,0,d=2#@>u*6@l..:6@6@*&7+7+7+r `-`-4O|$|$|$|$|$|$|$|$|$|$|$`-`-|$|$|$r r r r 7+*&.:.:*&.:6@u*vX2#2#2#0,KoKoKoe+>+6 6 <@kXb@S,0:F-]>`+h@c#F-6X#u*6@.:7+r 7+r |$r r 4O),4O4O),),;o;oL>P#P#;oP#P#P#P#P#;oL>L>;o;oP#4O4O), -4O`-r |$|$r r 7+l..:6@u*vX0,0,2#KoKoe#oT,)$;O/->++&)$>+<@e;P*K=U%Y:[ot>c#U%]>f+a#H%==-%7&-%&-!+_o!#)>6$Lo P% ", +" +%5O:>Z%}&&-y+7&tOA:`OA=b ]>U%c#a&a&F-`+U%N,M+d%7*;O;O>+/-;OT,G##o(*C>j:p;p;p;eL>o=L>o=G@v:I+v:+X+X+X+X+X+X+Xv:P&P&v:+Xo=L>L>G@L>P#P#P#P# -), -),`-`-7+.:.:l.u*vXu*2#2#0,0,t$eG#T,T,;O)$+&+&/-+&{:b@ OF-L;%*K=t>Z$%*hON*M-!+qo-%j },,X`O *X:y<`=F+ ", +" ,$=-4>g+:.A:-%&:y+qo!+_os@hO6XN,0:c+c#`+% 6XH$A#kX/-/-6 )$;O;OG#C>V@V@F&W z%p;e@>6@6@.:*&r |$),),), - - -L>o=L>o=v:+Xv:P&P&P&P&P&I+' ' m%m%m%m%m%m%m%m%l;I+I+' P&P&I+v:v:+Xv:G@o=G@G@P#;oP#),),4O`-r 7+7+l.6@u*u*@>2#d=u-t$t$ZOp;W D+j:V@C>C>_=T,;O/-+&';>++&,,$O9%U%L;L;)Xt>U%}#r&A=A;Q@&-7&+;qo*.q>l L=z;4o ", +" D:5>V-UX_o,X+;7&-%==H%WXb ioL;)XV A*U%U%Y:=<4*w-+&>+6 /-)$;O_=#oV@(*{.D+z%e@>u*l..:r r |$4O -P#P#L>L>L>+Xo=I+I+I+P&l;m%m%m%m%' I+' I+' I+P&I+I+I+I+v:P&I+I+I+I+' ' ' m%m%m%m%m%I+P&I+I++X+Xo=L>;o - -),4O4O7+`-.:*&.:u*@>2#KoKot$ej y+},,X. N*3%]@Z$c+0:% ,.<=Z$8-V+s.>++&6 ;OT,G#C>V@F&F&F&z%p;u-t$0,d=d=vXl.l.l.7+r |$4O4O -P#;oo=G@G@+XI+l;l;m%l;' I+I+' ' I+P&P&v:v:P&v:G@;oL>G@o=o=L>o=L>o=+Xv:+X+Xv:v:I+' ' ' l;' I+l;m%l;P&I+v:L>o=L>;o -),4O4O|$r 7+l.6@u*vX2#0,u-u-e+';';+&,,x> O% Y:}O[oh@`+,->:z,A;qo+;7&tO}&. 3#z=!:oOI ", +" <,8XP>Z%!+_#7&j qo!+_or&io,.K=c+t>% ]>]@; , kX+&>+6 )$;O_=C>(*(*{.p;p;eo=L>;oo=o=G@+Xv:+Xv:' ' I+I+m%l;l;I+v:v:o=o=;o -),4O`-`-.:.:l.@>vX2#d=t$e+';7*s.U [%3-io`+F-)X[o<=-;WXA;^>tOy++;==. &.8+x.v, ", +" P-m Po2:Q@-%y+j ^>A;a#b ]@F-L.a&K=Y:]>L.y%9<6 6 >+/-;OT,_=C>V@D+W z%z%e6@*&7+r 4O - -P#o=G@+XP&' l;l;I+I+I+P&P&P&G@+Xo=G@o=P#4O*&2#z%W j:V@C>_=)$';kXw-U v&v&d%,,<@+&)$#o(*j:F&p;t$@>*& -L>G@P&+Xv:v:+XI+l;' ' m%l;I+v:G@G@o=;oP#|$r 7+7+.:6@vX2#0,Kou-e+';';';b@s>c#<=[o[ot>%*hOq>`OA:&-7&y+qo`ON*i*m#Q v ", +" Y%#>j-f.^>+;y+tOQ@`OWX-;%*K=a&h@N,6X`+;*$+s./->+/-)$T,G#C>(*{.D+z%e;oG@P&l;m%' l;m%' P&+Xo=o=G@L>4Ol.Kot$p;#o7*v&[%mOY=rXV c#[oU%%*<=#<3%b b -;}#]@%*% c#a&+$ @n#co!=BO>+V@p;u-vX7+),o=G@v:I+I+m%l;' m%I+I+P&+Xo=;o),4O|$`-*&l.6@@>2#0,Kot$e#o_=;O/-+&+&s.>+,,$#=<}#F-Z$)X% ioF._o}&&-y+j qo!+A=bXm#$@Z>v ", +" G$nX%<0%i-I>j y+&-!+M$s@,-,.t>c+)XU%]@N,E.e;>+/-/-)$T,_=G#V@{.j:D+ZOt$u-0,d=@>u*.:l.r |$4O),;oL>G@v:I+' ' ' ' P&v:v:+XG@;o|$u*t$V@s.b@4*z@5=h@,.io3%>:z,*.,X},-%tO-%-%tO},},},-%+;j j +;},_#Q@!+M-N*b ]>}OV X+H T-d%;Op;d=*&),o=v:+Xl;' l;l;l;' P&+XG@;o -4O|$r *&l.@>vXvXd=Kou-eG#_=;O/->+';<@s.A#.#F-%*Z$A*[o]@f+_o*.qo+;j },}&z,3#R,`$k@_, ", +" c,e-N+k-M$qo7&7&qo!+A=f+io}Oh@0:)XY:,.0:cos./-6 )$;OT,G#(*{.W p;z%e%*ioWX}&},tO_#,XH%_oa#s@r&f+-;3%,-]>6X6X<=]>,--;>:N*WXz,. A;Q@&-j &:7&&-A;WX#<%*0:H$@@d%6 W d=7+;oo=v:I+' l;l;I+' P&G@;o;o -|$r *&l.l.@>d=d=Kou-ZOp;W F&{.C>G#)$;O6 s.';';5$H$V 6X[oc#[oY:f+N*H%Q@-%7&tO}&M$g=8OU>{@H# ", +" u<%OD*:*. qoy+j ^>H%a#b <=Z$L.0:[oY:Z$~:!=/-+&6 ;OT,T,C>V@j:D+z%ZOu-0,vXu*6@6@*&r 4O4O;oL>o=v:' l;' m%P&v:P&G@P#`-u*W +&p:%,~%a&L;f+H%},qoH%a#r&#<]@% Z$c#A*0:a&a&c+a&a&a&a&a&a&0:0:h@A*)Xc#N,[oF-}O`+]>,--;WXM-}&qo+;u@y+}&b }O9%{Xu.w-G#t$*&P#o=+XP&I+' I+' P&G@;oL> -),`-7+6@@>vXvX0,Koe_=T,;O6 +&7*<@{:iXgoL;L;N,K=,.#`ON*3%6XN,V A*U%%*)Xr:y=/-s.6 ;OT,#o{.F&W z%z%ZOt$d=2#@>l.7+r |$),;oL>o=v:I+' ' l;v:G@L>;o4O6@ZO/-p:mO.#N,-;,X-%,XM$r&<=Z$t>c+kO+$rX;*;*;*rX;*rX+,+,5=5=+$=<=<9%9%1;1;1;V c+c+a&a&L.0:h@)Xc#[o}OY:iohOF.M-==j b*j A;#<)X~%iXBOV@d=`-;oo=v:' l;I+l;P&+Xo=L>;o -|$*&l.u*vXd=0,u-z%z%p;F&{.(*#oT,;O+&';7*<@d%5=Z$,.Z$h@L;ios@M$A:tO7&tO,X. h-!$+ob&H# ", +" |-!;0#/:*.&-j -%==`Os@#<,.)Xc+t>,.U%h@4*)$+&>+)$T,T,G#(*j:D+p;ZOt$t$0,vX6@.:r 4O), -;oo=+XP&' m%l;v:o=;o4O*&2#C>w-_:Q;K=>:^>qoM$b 6X[o0:+$rX0.~%X+~%; ~%0.0.0.;*;*~:~:.#.#5=+$go3-=t>)Xc#N,N,% `+}#3%s@H%^>y+7&!+,-c+P*$O<@W u*),L>v:' m%m%I+v:G@;oP#),4Or 7+l.@>2#0,Kou-z%D+F&{.C>_=T,;O6 +&<@{:7*Y=K=,.}OL.U%]>f+_oA:_#y++;,X. i7&tOQ@`ON*,-L;t>c+c#6X}OkO!=)$s.6 )$;OT,G#V@D+z%p;ZOt$0,2#vXu**&r 4OP#P#o=v:' m%m%' v:G@;o4O.:ZO';!=r:V >:^>Q@WX}#F-a&go @L- O OH$L-L-; X+L-X+Q;Q;0.0.;*~:~:rX5=.#.#5=gogogo+$=<3-=<1;V c+a&a&0:L.L.L.A*t>t>K=c#N,Z$F-% }O]@<=L;t>V U%a#. 6X~:>$e;#o2#`-;o+XI+l;m%l;v:+Xo= -|$`-7+*&u*d=0,Kou-z%W W F&(*_=)$/-6 +&s.7*7*>$h@`+% 0:% ]>r&M$}&&-7&+;,X. 6>K-9*;;Y ", +" P%&hOL;)X0:N,%*U%+$W*/-+&>+/-_=C>(*{.D+z%eG@v:' ' m%' ' v:L>4Ol.p;:#u.X+6XA:}&A=hO% V +,Q;H$S,Y=Y=n#Y=Y= O OH$L-; X+; ~%Q;Q; @;*;*;*rX5=5=.#gogo+$3-=<={XU w-e;!=8-~%A*6Xz,M$Z$P*b@;O2#4OL>v:' m%l;I+P&G@;oP#),`-*&l.6@2#0,t$t$e#oT,6 +&';<@kX_:c+Y:,.h@[o<=r&_o,X},7&+;A:. q>=&AO,+Y ", +" %@8;9OA;&-7&+;Q@`Os@,-L;A*a&K=Y:% @(.6 >+/-;OT,#oV@j:F&p;ZOel.*&*&`-),P#G@P&' m%' P&P&G@P#.:D+y=iX+$f+A:_of+Y:0:~:; n#s>s>s>s>P*s>n#Y=n#S,S,L- OL-; ~%Q;Q;Q; @ @;*~:~:rXrX5=.#+$+$go=<=<= -4O7+.:l.u*2#0,t$e+)$6 ;O_=G#C>F&W p;t$0,d=@>6@l.7+|$4O;oL>G@I+' l;' v:G@P#r W 5$co0:N*A;z,3%)X.#H$s>r:M+h&h&E.P*P*P*s>{X{Xn#S, O; L-L-X+~%Q;~%0.0.~:;*;*.#.#.#5=5=+$+$3-+$kOkOkOV V c+V a&L.L.L.A*c#N,Z$h@mOx>BO6 C>_=;O/-_=;O)$/-6 {:e;u.L-)X#2#0,t$ZOW D+(*C>_=;O/-/-s.7*:#!=~:<=% K=[o6Xb z,}&&-y+-%Q@. s@v+-&N$ ", +" v%P;FX $_o&-y++;Q@H%s@#+/-;O_=C>V@F&p;u-t$0,d=u*l..:r ),P#;oo=P&I+m%P&+XL>;o*&p;,,y%K=a#M-A=,-L.~%P*H 6;H M+H M+M+r:E.E.P*{Xn#{X{XY= OL-L-X+; ; Q;Q; @0.~:rX~:rX.#5=5=+$3-+$3-3-=<=#o;O>+)$)$/-;O_=T,_=T,T,T,6 ,,H,P*A*#d=u-eT+ ", +" 8#h+@; %YX_#y+j ==`Oq>}#L;A*L.Z$%*t>>$y=)$6 /-)$G#V@j:F&p;ZOKo0,2#u**&`-|$),;oG@+XI+l;' P&G@;o7+Ko<@_:A*N*H%z,Y:=< Or:3<3+)$)$>+6 G#_=_=_=_=#oC>#o/-{:(.y%a&hO>:3%goW*(*l. -v:l;' ' v:o=L>;o),`-*&.:@>0,t$u-ZOp;j:{.#oT,;O6 ';<@';d%L-%*,.F-F-Y:-;z,}&&-7&-%A:z,F.f%:O-= ", +" j+F:q#j<7 7&+;Q@. N*#+)$)$G#j:F&D+z%ZOu-d=2#u**&r ),P#;oo=v:l;m%' +XL>4OvX)$$OrX-;A;_o`+5=P*mO>$co8-3<3{XS, OH$~%X+Q;0. @;*~:.#~:.#.#5=+,3-go3-=<=<=<9%9%1;V c+0:0:0:)X[o0:~%z@);<@T,';,,Co:#kX{:{:<@s.';>+/-T,_=_=_=G#C>C>G#(*(*#o>+e;%,1;r&N*,. Od%D+r L>I+m%m%P&+XG@;o),|$r l.u*2#d=u-z%p;F&j:(*T,)$6 +&7*s.,,H$L;`+N,N,Y:r&_o}&tOy+tOA:A=b ~&! z> ", +" {$}%P>|%==j j ^>. s@,-U%h@a&F-6XL._:;O>+6 )$_=G#V@F&D+z%u-t$d=u*l.7+`-4O;oo=+XP&I+m%P&o=P#l.j:V+{X6XA=!+]>.#z@8-$#$#%,co8->$8-3<3n# O; 0.~:~:.#+,go+$+$=t>N,F-N,9%~:S,Q$y=9#o(*#oV@(*(*6 d%8-A*q>N*t>3<{:u-|$+Xl;l;I+v:G@;oP#`-.:*&u*2#0,t$e}#}@r+` ", +" c,8 V.3:Q@-%y+qoH%q>,-L;)X0:N,Y:c#iX)$>++&)$_=C>{.F&W ZOt$0,2#6@6@*&`-),;o+XI+m%m%' v:o=4OKo7*>$c#>:Q@3%1;{X8-iX$#9=9=9=co8->$8->$mO6;6;6;H M+r:P*n# OQ;rX3-9%c+L.L.h@A*A*)X)X)Xc#c#N,[oN,[oF-Z$F-% % % % `+Y:F-)Xc+z@, );iX%,%,9=9=$#iXiXiX4*@@);Q$x>U 5$7*+&T,_=#o_=C>C>C>(*(*{.C>(*C>C>>+ooY=6Xz,#<=<, C>7+L>I+m%m%' P&L>),|$r 6@@>u*2#t$ZOp;F&V@{.G#)$/->+{:';kXL-,.,.)X[o<=f+_o,X-%j &-*.lXn*I.$>l- ", +" d&UOG+}&+;j &-!+WXhO,.)Xa&N,Y:N,%,';6 >+)$_=(*V@{.D+ZOKo0,2#@>l.*&`- -L>+XP&' ' I++X -*&W (.;*hOA:q>c#X+y%9=_:_:iXiX9=9=9=co>$>$8-mOmOy%6;H h&h&P* O @goc+)X[oU%L;L;%*6XY:Y:6X<=<=<=<=]>ioio}#io,-#L-0.Q;~%~%~%~%~%X+; X+; H$S,P*6;9=Q$p:5$7*/-T,_=_=#o(*C>(*C>V@C>V@#o#o(*G#,,@@;*b . `+s>,,0,),P&l;l;P&+Xo=L>4O7+l.u*vXd=t$e_=;O>++&{:s.:#~%Y:U%)XF-<=>:M-Q@+;y+qoH%N*R*@$~$G$ ", +" X#[;I;H%},y+tO}&A=-;`+c#a&c#Y:Z$h&,,)$>+/-T,C>{.j:F&p;t$d=vXu*l.7+|$ -L>+XP&l;m%P&L>P#6@G#@@)X_o*.U%~:E.$#u.@@4*4*iXiXiXiX9=9=%,>$>$mO3N*q>q>q>WXa#a#a#A=A=a#z,M$_o_oM-F.}O)XK=F-Z$Z$Z$[oZ$N,[o[oN,N,N,K=K=c#0:kO~:S,3<, U kX+&)$T,#oG##o(*(*#o(*V@V@(*V@{.j:C>/-e;3A=N*kOb@e<4OG@' l;I+v:;o;o),`-l.6@vX0,0,e+7*s.9<0.]>U%)XZ$ioN*`Oqoy+y+==M$N*s%c u+ ", +" 8#,$/=n&A===y+-%Q@M$b ]@[oc+c#Y:% Od%/-6 )$_=(*V@F&F&p;t$d=d=u*.:r 4OP#;oG@' m%' P&+X),2#';M+io!+b V n#$#4*u.););@@@@4*$#iXiX9=9=co8->$3<3<3^>==qo^>^>_#_#qo&-},qoH%N*N*q>N*N*q>N*q>N*q>q>s@s@N*F.s@N*s@>:r&#<6XZ$+$Y=9=W*Co<@6 _=#oG#C>V@(*(*(*V@{.V@V@{.j:{.(*G#<@!=S,io*.Z$4*C>7+G@I+l;' v:o=P#4O|$7+6@vX0,0,e+7*<@(..#}#L;A*}O,-q>A;&-&:+;A:M$r&mXd>H# ", +" P%R$i.h=w.==y++;==M-r&]@[oa&t>%*L;rXv&6 6 )$;OG#V@j:D+z%u-0,vXu*l.`-4O),L>P&l;m%' o=;o`-z%p:Q;a#A:6X;*3<);$+$+, ););u.@@@@_:_:iX_:9=9=co>$8-mOmOy%h&r:s>L-+,)X<=q>*._#j 7&&:u@u@u@u@b*b*b*b*7>7>b*p#N=&:},},},&-&-},&-&-},&-_#tO},_#_#_#_#_#&-_#&-^>==!+z,hOK=;*mOA#BOkX>+T,G#G##oC>C>(*C>V@{.{.j:{.V@j:{.V@V@#o,,6;F-z,6XE.s.@>),+Xm%m%P&+X -),4O*&6@@>d=t$ZOD+j:j:C>;O/-6 <@<@<@H,=<}#% A*}O#,.}Ogob@>++&/-T,G#V@j:W z%KoKo2#l.r r |$),L>+X' m%I++XP#l._=@@c#. M$[oY=@@, Q$T-, $+[%[%););u.u.4*_:_:iX9=co>$>$8-35o~#~#~#5o5o5oO-O-O-5o#=T@=X(&(&C:C:C:=XC:C:=XC:C:C:C:C:(&(&C:C:(&C:(&E+l$7&==a#,.goh&Q$d%{:>+T,G##o#oC>(*(*C>(*{.{.V@{.j:j:j:{.F&D+D+_=H,kOF.a#+$p:W |$I+m%l;P&G@ -),r 7+.:vX2#KoZOD+j:{.(*T,)$6 s.s.<@$+V ]@F-c#%*f+M$A:tOy+_#A;F.& #;/+G$ ", +" z$< b+!+-%y+_#*.a#-;L;0:0:}OL;V x>6 /-6 ;OG#{.j:D+z%t$0,vX6@*&`-),P#o=P&l;' P&G@;o@><@P*3%H%r&V 6;T-!=$OQ$$OT-$+$+[%[%);@@u.4*_:iX_:9=co>$8-8-3u@~OM;+.C:(&(&(&(&(&(&C:C:E+E++.M;M;M;M;+.M;+.+.+.+.+.+.+.+.M;M;+.M;M;+.+.M;+.M;b-^oTX+;M-Y:=+;O#o#o#oC>C>C>#o(*{.{.{.{.V@{.j:D+ZOt$ZOp;y=; r&j j F-$##o4OP&' m%v:+XL>4Or r .:u*0,t$eT,;O6 ';s.{:@@0:%*F-[o<=F.M-Q@j j &-H%C Ioo MX ", +" P%~;A>0*. &-&:tOA:_o-;%*)Xc+N,`+)X!=/->+/-;O_=C>D+D+z%Kod=d=@>*&`-4O;o+XP&l;l;P&G@P#2#w- @z,*.6X~%iXQ$A#!=H,H,H,, T-, [%$+u.4*u.4*_:iX_:9=%,co>$>$3$>$co>$co>$coco9=9=9=>$co%,coco%,co%,cococococo%,%,cococo%,co%,coco[%Y:b*7&M-%*+$6;A#w-7*>+)$G##oG##o#o(*(*(*V@{.{.{.{.z%e6Xa#j u@-;r:/-.:G@l;m%I+P&L>),|$r 6@2#0,Koe++&;O_=(*F&W z%u-2#2#u*.:r ),;oG@P&m%m%P&o= -KoV+3-. M-)Xh&, x>ooA#A#H,A#H,T-Q$, $+$+[%u.u.@@_:_:$#$#9=%,co>$8-mOH z@; 3-F-f+}&C.+.-%#om%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%kOl$j z,L;~:>$x>Cos.>+/-_=#oG#G#G#C>(*(*(*{.F&W W z%ZO,,3WXH$7*l.G@l;l;l;+XG@ -|$*&6@2#0,KoZOW F&V@G#_=6 ';s.';:#n#%*U%t>F-#H%N*}#[oc+)X%*Z$s>6 ';>+)$T,#oj:W ZOu-Ko2#@>*&r 4O;o+XI+l;l;P&+X),u-W*c+M-A==<%,A#W*x>oox>ooA#A#$OH,Q$T-$+[%[%);@@@@_:4*$#$#9=%,coco8-y%h&{X0.1;L;s@^>N=#=N,vXm%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%|$3%M;&-F.K=; _:p:y=s.6 /-_=#oG#G##oC>(*(*V@V@W z%)$Q$E.;*N,,.U%c#t>)X)XK=}O}#,Xu@M$0.y=u*G@I+m%' P&o=),`-*&6@2#0,t$z%W F&(*G#T,6 ';{:6 9<0.Y:}OA*,.3%A=}&+;y+&-A;b $oH+1@ ", +" $;.*R>*.tOy+&-*.WXhOU%L.L.% `+5=w-s.+&/-_=G#V@D+z%e),ZO!=t>. F.rX_:W*(.b@W*x>ooA#ooA#H,H,T-Q$, [%$+);u.@@4*4*iXiX$#$#%,co8-mOh&S,rX0:6XA=&-l$n#V `+}Ot>t>A*t>K=K=)XK=N,F-]>*.u@A;.#y=vXo=I+m%I+P& -4O|$*&u*2#d=u-e1;`+N,N,]@>:. ==j j Q@M$>O;&g% ", +" MXX-o#q>qo7&-%A:_o-;%*)X0:K=6XL.T-6 ';/-;OG#(*F&z%u-Kod=@>6@*&|$ -o=P&l;l;v:o=|$ZOQ$[o. b @u.W*v&b@b@b@b@W*oox>ooooH,Q$$O, $+$+[%[%@@u._:iXiX$#$#%,>$8-y%h&S,rXh@,-}&b*M;T@>+m%m%m%4OKoKoKod=0,0,d=d=2#d=0,2#d=2#2#2#vX2#2#2#2#2#vX2#vX@>@>u*2#l.v:m%m%m%n#y+b**.<=3-H H,9<{:6 /-;OT,T,_=_=#oz%ZOs., co5=Z$F-A*0:h@h@A*A*t>c#K=c#c#c#N,}O<=H%u@}&+,Cod=;o' m%' +XP#4Or *&vX@>d=t$ZOW V@#o_=)$>+<@7*<@coN,L;N,% ioq>H%^>y+-%}&A=-;p*E, ", +" >*<+OXI&+;7&^>. s@<=N,c+K=Y:Z$co)$';/-)$T,(*j:p;eP&l;l;P&L>4Oe<, }O. }#; [%b@v&V+v&v&b@(.W*x>ooA#ooH,H,$O, , [%$+[%);@@_:4*iX_:$#9=>$39%]>a#A:7>M;*.p;m%m%I+{.v&e;d%9Co<@>+)$T,)$_=W 0,G#A#u.s>h@F-t>a&c+0:0:h@A*t>t>A*t>)X)X)Xc#K=[o% <=. 7&A:=<5$2#L>I+l;' G@P#),r l.6@vX0,u-ZOF&{.C>_=T,>+<@>+w-H$%*`+)XL;hOA=}&tOy+qoH%f+w#C@-=P% ", +" u<1 M M-},y+},}&A=3%% 0:)X,.%*{X<@6 6 ;OT,C>V@F&eooooA#!=H,Q$Q$, , $+$+[%u.4*4*4*_:9=9=4*_:E.5==+.&-N*[o~%%,p:y=s.+&6 {.ZOj:CoT-mO9%c#t>c+a&0:0:a&0:0:L.h@t>A*A*A*)X)Xc#K=c#K=N,% ]@M$y+^>9%CovXo=I+m%I+L> -4O`-.:@>Kot$u-p;F&{.G#_=/-';{:>+p:+$]@U%)X%*F.M-Q@y++;A:_op** ", +" Zo] r&Q@y++;,XM$b Y:A*L.F-io+,p:6 >+/-_=G#j:F&p;t$0,d=u**&r -;o+X' m%v:L>),z%Q$% M-%*s>!=U e;d%U V+U v&v&(.b@x>x>ooA#ooH,Q$Q$, , [%[%[%u.u._:4*);[%iXP*; ; 5=V kOt>-;}&l$#=$+P&m%m%2#Cov&e;e;d%d%d%BO9t>)X)Xc#c#c#K=[oF-6X_o7&qo+,,,6@G@l;' v:L> -`-*&l.vX0,Koz%D+j:(*G#)$6 s.{:';T-h@Y:[o}O,-s@A;&-y+},*.N*i>A$poI ", +" -XF*B$H%+;7&qo`OF.]@N,V Z$6X)X4*>+>+/-T,G#(*D+z%ZO0,2#6@7+`-4OL>v:l;m%l;v: -u-A#N,. ]@P*x>d%BOd%BOU V+U U V+p:(.W*x>x>A#x>H,H,Q$Q$T-$+[%[%@@u., [%y%z@z@X+rXh&%,{X.#Z$s@_#M;l)Xc#c#N,c#K=Z$[oZ$]@_oy+}&;*kX*&v:l;m%v:P#),`-*&l.2#0,u-z%D+{.C>_=)$+&7*';:#3#%#&-&:-%!+a##+>+;OG#(*j:z%ed=b@h@M-}#Y=x>w-w-9<9H,H,$O$O, [%T-H,$O_:y%H S, @{X@@, [%$#n#=V+e;BOBOw-w-w-5$5$5$CoCoCoy=,,kXkXy=:#kX7*{:{:7*7*<@<@s.y=C>G@m%m%m%]@A%j _o`+ @4*(.x>b@u.3-h@V V c+9%3-=<1;1;1;V V a&L.0:a&0:0:h@h@A*A*A*t>)Xc#)XN,K=K=N,Z$[oZ$6XM-&:H%X++&r I+m%l;G@L>P#`-r @>2#Koe+7*>+9<.#Y:% K=Y:>:M-qo+;-%!+IO9Ov#,OP% ", +" ;=U&1,}&j y+==M$r&Y:A*c+}O]@ @V+/-+&6 G#C>j:D+e+e;d%BO9<9:)X0.8-cogoh@V c+a&9%go+$3-kOkO9%1;1;V V V a&a&a&a&0:L.h@h@A*A*t>t>t>t>c#N,N,K=Z$Z$[oF-<=`OC.z,Y=;O|$' m%I+v:L>),|$l.u*d=u-u-W {.(*_=T,/-';<@s.A#h@]@c#F-,-WX*.tO7&^>`Os@_&boc. ", +" q@Z P=0+},y+_#H%s@io[o0:A*U%L;u.s.>+/-T,C>V@D+p;KoKo2#6@7+4OP#o=P&l;I+P&;o@>w-3-M$b OA#w-CoCoCo5$w-BOBOd%e;U U p:(.(.b@x>x>A#ooooA#H,[%@@3<~%;*H @@[%A#(.W*$O[%$#h&;*N,r&,XA%A%!=' m%m%0,{:9A*A*t>)X)XK=[o[oN,[oN,N,% <=A;b*>:h&#o;o' l;' +XP#|$7+.:u*0,u-ZOW {.(*G#;O6 <@s.:#{XZ$U%K=L;-;_o==+;+;,Xz,-;o@:&O> ", +" ^%^@S>Uo^>j -%Q@z,hOL;0:0:,.%*h&,,>+>+;OG#V@j:p;ZOKo0,u**&`- -L>+X' m%I+o=*&<@;*M-s@L-!=9<,,,,,,CoCo5$5$w-BOe;U U V+v&(.b@b@x>W*oooo!=Q$z@+,s>u.iX, x>x>H,!=!=$O[%9=P*+$U%q>&-=XC.;Om%m%m%D+w-9+7*C>;om%m%G@rXp#7&^>,XWX<=}O)XkOgo+,go+$+$3-=<=<=)X)Xc#K=N,[oN,N,N,[o}O}#}&C.# -),7+l.2#t$u-z%F&{.G#T,)$+&<@{:9<1;]>c#K=<=>:H%&-y+&-A;6>9,[ y, ", +" G$ &GXM-tOy+qo`Os@6Xc#L.[o}OA*oo+&>+)$_=C>{.D+ZOt$d=2#.:`-P#;oG@P&l;P&G@`-C>r:z,_o @!=5$:#:#:#,,Co,,Co9<9<9(.v&y%;* O8-_:@@A#!=!=H,H,$O$OH,$Ou.3< O1;6XM$7&E+r&z%m%m%' G#9<5$5$,,,,Coy=y=:#:#:#kXkX{:{:{:7*<@<@';s.s.s.';+&+&>+6 {:j:+Xm%m% -hO#=s,-%*.F.,.0:=<=<3-+$+,+$+$3-=<=<=A*)Xc#c#c#K=[o[o[o[o[oZ$U%#<,XC.`+b@2#+X' ' G@L>L>|$*&@>2#Koeu*7+),L>G@P&m%I+G@P#0,[%,-M$1;T-5$kXkX:#y=y=y=y=,,w-5$9$co[%!=$O$OQ$$OH,H,H,$OQ$, _:M+Q;h@,-*.s,b-+$*&m%m%),6 5$,,,,,,y=y=:#:#:#{:kX7*{:7*7*<@s.s.';s.>+';+&+&+&>+6 6 +&t$I+m%m%p;^>5op#qoz,}#F-0:kO+,3-3-go+$+$+$3-=<=)X)Xc#c#K=N,[oN,Z$Z$Z$L;b },tOa&';r +Xl;' +Xo= -`-6@@>d=t$ZOW {.#oT,)$6 7*{:{: @`+[oN,]@F.A;_#y+},A;6>,*1.9 ", +" NXb+. j y+^>`Os@6Xh@L.U%U% @e;+&6 )$_=(*D+z%e+>+6 /->+6 /-+&T,.:l;m%l;!=b*(&&:,XWX]>N,c+=)X)Xc#c#K=K=[o[o[o[o[oZ$`+WXy+H%S,V@ -l;l;P&+X;o),7+l.@>d=u-ZOD+V@#o;O/-+&7*7*A#1;]>Z$Z$-;z,}&},j ==M-%>- d$c, ", +" 8#H&X*8%_#y+-%}&z,-;% c+A*Y:U%Q$<@+&/-)$#oV@W z%Ko2#@>.:7+),;o+X' l;v:o=.:+&X+_o}#h&w-<@<@<@<@7*{:kX:#,,Co,,5$d%Co:#{:$+{XE.mOM+>$$+$+$+$+$+[%[%T-T-T-Q$$OH,$OQ$Q$$+con#3-,.WX&-~#-%_=m%m%m%F&y=,,kXkXkX{:{:7*7*7*<@<@';';s.s.+&+&>+>+6 /-6 6 /-)$)$;O+&{. -m%m%),+,A%~Oj *.r&%*c#1;3-go5=go+,+,+$+$go+$3-=F.);e+<@kXs.z@[oF-F-6X>:. qo7&},*.f$y&v>o. ", +" T=N-H%y+j ^>. F.Y:A*A*%*}On#y=+&6 ;OT,(*j:p;e`-V@6;q>N*Q;V+s.s.s.';s.{:{:{::#,,Coy=s.>+b@8-8-mOs>h&u.[%[%[%[%[%$+, $+, , Q$Q$Q$Q$H,$OT-[%4*y%X+a&<=M$&:#=-;ZOm%l;' #o,,kX{:{:{:7*7*s.<@s.s.s.s.s.';+&>+>+>+6 6 /-/-)$/-/-;O_=';z%P&m%m%vXr&5oN=&-. hOU%h@9%gogo+,+,+,gogo+$go3-3-=<=)$6 ';7*<@BO.#]>% N,#,-% 0:t>]@t>v&/-';;O_=#oV@W e*&`-4OG@v:' l;v:4Oz%u.]>. 9%A#';+&+&+&';s.7*7*kXkXs.+&kX$+$#>${X{X%,[%u.u.);[%);[%[%, T-$+$+, Q$Q$T-Q$!=$O, );$#r: @c#3%A;^ol<~:7+m%m%4O)$y=kX7*{:7*<@<@<@s.s.+&';s.';>+>+/-6 >+/-/-)$;O;O;OT,T,_=/-d=m%m%m%;OtO~#u@^>A=]>[oa&3-5=5=.#5=gogo+$+$+$+$kO3-3-kOkO9%9%1;1;V V c+0:a&a&0:0:h@A*L.h@A*t>c#c#)Xc#K=N,N,F-U%%*)X.#8-T-iXL.z,a&kX7+' m%I++XP#`-*&u*2#Koey+&-*.EoO+-M$b `+A*A*L;F-s>kX+&6 T,_={.F&p;t$0,@>6@r 4O;oG@I+l;I+L>2#U a&M-)X@@{:/-6 6 >++&s.7*s.)$';p:, $+z@~%mO@@u.@@);u.4*u.u.);[%[%, $+, $+, , Q$T-Q$Q$$OT-u.8-s>+,% F.,XT@&:(.l;m%m%2#+&:#{:7*<@7*<@s.s.s.s.+&';+&+&>+>+6 6 6 /-)$;O;O)$_=_=T,T,V@`-m%m%m%@@^o=Xy+A:s@Y:t>1;3-+,5=.#.#go+,go+$go3-3-=<=A*A*t>c#)Xc#K=F-}ON,a&s>u.,,W u-6 %,Y:z,H$G#;ol;l;P&L>4Or .:u*2#Koe_o1+u,7$ ", +" 8#q%J$n@_#7&-%!+q>,-F-c+c#}#0:d%;O+&)$_=C>j:W e7+>+; q>`+6;y=/-6 /-6 >+6 /-/-y=p:H,y%L-P*$#@@@@4*4*4*u.@@u.@@u.[%[%[%[%, $+$+$+T-Q$T-T-, Q$Q$@@mO O9%`+a#&-#%A;V@m%m%m%j:{:s.7*<@';<@s.';';';';';>+>+>+6 6 /-/-)$;O)$;OT,T,T,_=_=)$z%o=m%m%.:c++.l}##d=0,ZOD+{._=;O6 +&kX_=co}OU%c#6Xr&. qo&:tOH%Eo/O@.L, ", +" $;+:j=S;+;j ^>. >:Y:t>h@% }OL-kX;O6 T,G#(*D+ZOu-Kou*.:`- -;ov:l;' v: -D+iX]>_oL-e;/-;O6 6 )$;Os.5$w-[%; ~%>$u._:iX_:_:@@_:_:@@@@u.@@@@[%[%[%[%$+$+$+$+T-Q$Q$T-T-Q$, _:M+~%h@}#. &:T>L;d=m%m%I+T,7*<@<@';s.s.+&';+&>+>+>+6 6 6 /-/-)$;O)$T,T,T,_=_=_=G#G#T,d=l;m%m%V@A=5oTXqoz,,-% a&3-5=5=5=.#5=5=gogogo+$+$+$3-=<=<=+s.';U L-U%L;F-}#WX!+-%+;Q@_ov./OW. ", +" A+w*|:E>&:-%!+WX}#Z$V K=}#=s>X+h&iXiX$#iX_:_:iX_:_:_:_:@@u.@@u.u.$+[%[%[%$+, , $+$+, Q$T-$+$+[%$#P*.#N,-;!+~Ol$S,),m%m%4O)$7*';s.+&';+&+&>+>+6 6 6 /-)$/-;O;O;O;O;OT,_=_=#o#o#oC>#oC>.:m%m%m%,,&:~#7&A:WXioN,V go.#5=.#5=.#.#+,gogogogo3-3-=<=a&+$X+p:e++&:#/-9=`+,.t>]@F.`O^>7&},H%z*r+)$G#C>W ZOu-0,@>*&r P#G@v:m%l;P&),#oz@-;,->$kXT,_=;O;OkX%,Q;z@coco9=9=9=9=9=$#_:iX_:@@4*_:_:4*u.u.@@@@[%$+[%[%[%$+$+$+$+T-T-Q$$+@@>$Y==+<@';+&>+>+>+>+6 6 /-/-/-)$;O;OT,T,T,_=G#_=G##o#oC>#o#o#oD+4Om%m%m%8-A%C:j !+F.Y:)X9%+,5=5=rXrX5=.#+,gogogo+$go3-=<3-=<=+W D+j:F&D+D+D+D+D+D+W W D+D+(*y=Y=}#,.Q$u*l;m%P&o=;or *&6@2#Koe+<@';b@ @% % U%hOa#A:+;-%A:a#yO+O/% ", +" m@6OT%,Xj j ,XA=hOU%L.c#]>3-b@+&>+)$G#C>F&D+u-t$d=l.r 4O;oo=I+I+v:;o2#W*}OM$0.V+)$V@_=!={XS,H >$8-cococo9=9=9=9=iX_:iXiX_:_:4*_:@@u.u.u.u.[%[%[%);[%, , $+$+T-T-, $+4*y%L-V Y:A=_#.;q>W m%m%' W +&+&>++&>+>+/-6 /-/-/-;O)$T,T,;O;O_=_=#oG##oC>C>(*(*(*(*C>t$G@m%m%u*L.(&l),`-*&vX0,t$p;F&V@C>_=6 ';{:s.%,Y:6Xt><=s@H%},j qo. F.^;&OP% ", +" ^%6=R-A=&-C.},. >:]@c#a&K=<=0.y=/-6 T,_=V@p;ZOt$d=@>.:`-P#o=v:m%P&G@|$6 ~%-;c#$O;Oy=>${Xy%3$>$%,9=9=9=9=$#iXiX4*4*_:_:4*4*_:@@u.4*u.[%[%[%[%$+$+$+, $+T-, [%$#H Q;A*,-H%&:5oN,6@m%m%P&G#s.>+6 6 6 6 /-/-)$)$;OT,T,_=_=_=_=G#G#C>#o#oC>(*V@V@V@j:(*@>m%m%m%;OWXT>N=qoz,,-F-c+3-5=rX~:.#rXrX5=5=+,gogo+,go+$+$3-=N,+$@@s.)$(*{.V@V@j:{.{.j:j:D+j:j:D+D+W z%W D+D+j:';y%iof+cod=v:m%' +XP#|$7+@>0,0,e+oo3-% F-,.3%z,Q@7&+;!+q>X;a+)$T,#oj:p;z%u-d=u*7+4O;oo=P&' G@o=t$$+]@b s>mOy%6;M+r:6;>$co>$>$cococo>$co9=9=$#$#iXiXiX_:_:4*_:iX@@u.u.u.u.);$+[%[%[%$+, , $+, $+@@coz@.#Z$r&!+l$l${XP#m%m%4O_=+&6 /-)$6 /-;O;OT,T,T,_=_=G#G##oG##o#oC>C>(*V@{.j:{.{.j:j:r m%m%m%p:b*T>&:,XN*]@K=V go.#rXrX.#rXrX5=5=.#+,+,+,+$+$+$=<3-=V@{.j:{.j:D+D+D+F&D+W z%W W F&F&{.9<.#a#0:,,4Om%l;v:L>4Or *&vXKou-p;D+(*T,;O6 s.7*:#%,,.Y:K=ioq>*.tOy+^>z,-;aXt ", +" H:o;f+==7&tO*.q>]@)Xc+N,}#~%kX6 6 T,G#V@F&p;u-0,@>.:r -o=G@m%P&G@),';kO. hOX+E.{Xh&mO>$mOmO3<>$8->$co>$>$co9=$#9=9=iXiXiXiX_:_:4*_:_:u.u.u.u.);[%[%[%[%$+$+$+, , [%iXmOn#=<`+WX^>T@Q@BOm%m%l;vX)$6 6 /-;O/-;OT,T,T,G#_=G##oG#C>C>#oC>C>C>V@V@{.j:F&F&j:j:eG#C>(*(*C>(*V@{.j:j:j:j:D+F&j:F&F&p;z%p;D+j:D+G#[%,.z,{Xu-v:m%I+o=P#`-r l.0,KoZOW {.G#T,/->+{:>+W*A*F-)X`+f+M-==y+-%H%s@I#lOG$ ", +" @%F;H@. tO7&^>M-b ,.L.A*U%t>$#s./-6 _=(*j:D+e$>$>$8->$>$>$>$co9=$#9=9=$#iX_:iXiX_:_:4*_:u.4*@@u.u.);[%$+[%[%, , [%[%);$#H X+0:]>M--%t+##o#o(*(*(*V@V@V@{.j:F&F&j:F&D+F&2#P&m%m%0,N,=Xl<&-M$,-F-0:=<5=;*;*~:;*rXrX.#.#5=5=go5=go+$+$3-kO=C>#o(*(*(*V@{.j:{.{.{.D+D+j:F&D+D+p;z%p;W W D++&n#N*% U 4Om%l;v:o=4Or 7+@>0,e{;A:j -%A:a#ioN,L.F-]>X+w-+&6 ;OG#{.D+z%e.:r -G@P&l;v:G@|$<@1;*.-;X+6;h&H mO3$>$8->$co>$>$co%,9=9=9=9=iX_:_:_:4*_:_:_:_:@@u.u.u.);$+$+[%[%$+$+$+$+);%,E. @t>hO*.C.[,1; -m%m%o=#o6 T,;O)$T,;OT,G#_=_=#o#o(*C>#o(*(*(*V@V@V@{.j:F&F&F&F&F&D+*&m%m%m%:#H%#=TXQ@WX]@N,V go.#;*;*;*~:.#.#.#5=5=+,gogo+,+$3-+$c+K=0: OcoQ$T,0,j:G#)$T,G#G#G#G#G##o(*C>(*V@j:{.{.{.{.D+D+F&F&D+D+z%p;z%z%W p;j:b@A*A=; ZOv:l;I+G@;o|$.:6@d=t$ZOW j:#o_=/-+&{:7*(.% Y:c#%*F.A;&-j _#. b D>`& ", +" Y&6o>:_#7&&-H%F.Y:A*c+F-]@z@';/-6 ;OC>{.D+ZOt$d=u*.:|$;oG@' l;G@;oKo[%s@`Oa&P*H h&H mOmO3$>$>$co>$co>$co9=9=9=9=iXiX_:iX4*4*4*_:4*u.u.u.@@);[%[%, [%$+, [%);4*8-s>+,}OF.Q@b-TXmOv:m%m%`-_=6 T,T,T,_=_=_=G#G#G##oC>C>(*(*V@V@{.{.j:j:j:j:F&D+D+D+W z%4Om%m%m%$+l$T@7&!+r&`+t>kO.#rX;*;*;*;*~:rX.#5=5=.#gogogo=(*(*(*C>V@{.{.j:{.j:D+D+D+F&D+p;p;W W W z%p;>+Y=a#F-9<|$l;' +Xo= -`-6@vXt$eh<2+Vo ", +" 9&_>7O`OtO&:==_o3%L;L.A*% c+);';6 )$G#(*{.W ZOKo2#u*r 4O;ov:l;P&G@7+s.go*.r&~%M+M+H M+H y%3$8->$8-8->$>$%,9=9=9=$#iXiXiXiX_:_:_:4*_:4*u.@@u.@@);$+[%[%[%$+[%u.$#y%S,kO6XA=_#T>H%kXm%m%P&vXG#;OT,_=_=#oG#G#G#C>C>(*(*(*V@V@j:{.j:F&j:F&W F&W W W p;z%0,L>m%m%I+n#T>+.tO`O3%}Oh@3-rX~:;*;*;*~:;*rX.#rX.#5=.#9%L.+$ OY=$+p;u-C>)$/-;O;OT,;OT,;OT,G#G#G#G#C>(*(*C>C>V@{.{.{.{.j:D+F&D+D+D+W p;z%W p;D+z%j:A#t>f+Y=z%G@' I+G@;o),.:u*2#u-z%F&{.#o)$>++&{:Co@@N,U%F-<=WX}&+;y+A:_o.<'$}$P% ", +" P%%&R%{ A:+;+;}&q>]>K=a&% <=s>{:+&>+T,#oV@D+p;t$d=vX*&4O -L>I+m%v:;ou-, r&M-)X{XM+H M+6;M+6;mOmOmO3<>$>$8-8-cocococo%,9=9=9=9=iX_:iXiXiX_:4*_:_:);u.u.@@);[%[%$+$+, );@@%,h&; c+hOA:&:@:6Xl.m%m%+Xz%T,_=_=G##oC>C>#o#o(*(*V@V@V@{.{.j:j:F&F&D+D+p;p;W z%z%p;p;6@I+m%m%t$}O=Xl$qoz,]>Z$c+go.#;* @ @;*;*;*rXrXrX+,1;kO~:~%mO+&z%C>>+>+)$)$/-)$T,T,T,T,;OT,G##o#oG##oC>(*(*(*(*V@{.j:{.j:F&D+D+D+F&p;ZOz%W W D+z%p;<@; _o% y=`-l;m%+X;o -r 6@@>KoeT,;O>+7*kXU K=Y:c#`+>:H%_#+;_#`O>:w#;<8# ", +" |OU#q>},7&},H%F.6XA*a&% ]@M++&+&/-T,#oF&p;ZOKod=6@r ), -v:l;l;o=7+6 Q;}&>:~:r:H H H 6;6;6;y%mOmOmO8->$>$>$cococo>$co9=9=9=9=$#iXiXiXiX_:_:_:4*@@u.u.u.u.[%[%[%$+););u.$#z@kO%*A=j l<.;~:' m%m%L>V@;O#o#oC>C>(*(*(*(*{.V@V@{.{.F&F&D+D+F&W W p;p;z%z%z%z%z%ZO|$m%m%m%Co!+=X7>,XN*]@c#9%5= @;*;* @ @ @0.;*9%1;.#+,0.(.z%T,/->+/-6 6 )$;O)$;O)$;OT,T,;OT,G##oG#G##oC>C>C>(*(*{.j:{.{.{.{.F&D+F&D+D+W p;z%p;D+D+p;V@$+}O3%H u-P&m%' L>),`-.:2#0,t$z%W V@G#;O>+<@kXd%.#F-c#}O3%_o,X-%+;*.q>:.a:uoP% ", +" Y _ob L;A*)XF-3-, +&>+/-_=(*D+z%t$KovX*&r ),L>P&l;v: -u-H,hO`O[o Oh&M+M+6;H 6;6;mOmOy%mO3<>$>$>$>$>$cococo%,9=9=9=9=iX_:_:iX_:4*_:_:_:u.u.u.u.);[%[%$+Q$$+mOP*;*]@`OA;^>l$7>9=m%m%m%4O(*T,C>C>(*(*V@V@V@V@j:j:j:F&F&D+D+W W W p;z%z%ZOz%ZOe+6 6 6 6 /-/-)$;O)$;OT,;O;OT,T,_=G#G#G#G#C>C>(*C>{.{.{.{.{.j:D+D+D+D+F&W p;p;p;W D+ZOp;:#0._oK=7*|$' l;P&;o`-*&6@0,t$e+';{:CoT-)X%*% <=q>}&j 7&Q@A=W,uO}$i= ", +" 7o'&f+==-%-%A:A=}#N,L.U%6Xr:{:s.6 ;OG#V@W eG@I+l;G@r T,Y=A:s@+,z@M+M+M+H 6;6;6;6;y%3<3$8-8-8-coco8-co9=9=9=9=iX_:iXiXiX4*_:4*4*u.@@@@u.[%Q$H,u.>$mOQ;Z$[oL.6XM$-%=Xz,{:m%m%' 6@#oG#(*(*{.V@V@j:{.j:F&F&F&D+j:W W p;W p;z%e+6 >++&';+&6 >+6 6 /-)$;O)$)$)$;OT,;OT,T,G#G#G#G##o(*(*(*(*C>V@j:{.{.{.j:D+D+D+D+W z%p;z%z%p;z%z%#o_:]@hO%,u-I+m%P&L>),7+l.vX0,u-p;{.(*G#/-';7*y=9]@N,%*r&`O},y+qo. r&D>(=cX ", +" P%+-~@N*&-j &-A;s@6Xt>0:L;6Xco;O';/-T,C>{.p;el.r P#;o+X' P&L>vXv&F-. L;; M+M+M+M+M+6;6;6;6;y%3$>$>$>$co%,%,co%,9=9=9=iXiX$#$#_:@@4*_:_:@@@@$+T-@@9=iXr:0:9%E.s>~:L.}#*.&:.;N,r m%m%+Xu-#oC>V@V@j:{.j:F&F&F&D+D+F&p;W p;p;z%z%ZOZOe+/-6 <@s.';';';+&>+6 6 6 6 )$)$;O;O)$T,T,T,;O;O_=G#G#G#G#C>(*C>C>(*j:j:{.V@{.j:D+D+D+D+F&p;p;p;W W F&p;z%kXX+s@V )$P#m%I+o=P#`-.:6@d=e}O-;M-^>j tO!+q>3:`XY ", +" ^%q+/@`O-%7&==M$b L;h@)XF-gooo6 ';)$_=(*j:z%u-d=@>l.`-),;ol;l;P&P#F&8-. q>V {Xr:r:M+6;H 6;6;y%6;6;mO3$>$8->$>$co%,>$co9=9=9=$#iXiXiXiX_:@@4*@@[%);$#%,M+~:3-P*>$mOcoH @[o>:Q@TXO-S,m%m%m%P#F&C>{.{.{.F&F&D+D+W D+p;W W z%z%z%ZOZOe,XN*%*L.a&9%.#L.K=r:V+V+kX/-)$s.<@s.<@s.+&';+&';>+6 6 /-6 /-)$;O)$;O;OT,T,T,T,T,_=#oG#G##o(*C>#o(*(*(*{.{.{.V@F&D+D+D+j:W p;p;p;z%u-d=u-)$y%z,#;O/-<@{:y=x>a&`+U%]>a#}&tOy+,XA=m*9#BX ", +" h 2.jo}&+;j }&a#}#N,h@L;U%3<';';>+T,G#V@D+e}# @z@z@h&H 6;H H 6;y%6;6;mO3$>$>$cococo>$co%,9=9=9=$#_:iX_:iX);@@iX9=$#Q;1;n#$#3<@@ooT->$S,3-L;WX},b-&:T-m%m%m%`-V@(*{.j:j:D+D+D+W W W z%p;z%z%ZOz%em%m%m%>$C:M;y+A;>:%*t>N,L;0.iX$+BOT,/-{::#kX<@s.7*s.<@s.+&+&';+&6 6 /-6 6 6 )$;O)$;OT,T,T,T,T,_=G#G##oG#C>(*C>(*(*(*{.{.j:V@V@j:D+D+F&F&p;t$Kou-ZOD+d%r:N,qoH%P*F&P&m%I+L>),r l.@>Kou-z%F&(*T,/-s.7*y={:a&]>% `+>:H%&-y+==M->>z Z:P% ", +" [O* o:qoj &-H%N*6Xh@0:6X]@$#_=';)$T,#o{.p;t$0,2#6@r 4Oo=I+I+P&G@2#Q$]@`OK= Or:E.h&H M+H H 6;6;6;6;y%3<3$8->$>$cocococo%,9=9=9=9=iX@@4*_:$#9=s>+,; M+y%@@v&V+H,@@iXmO; a&]@M$y+(&r&T,m%m%' 6@{.V@j:F&F&D+D+D+p;p;p;p;z%z%ZOZOZOu-e(*(*(*(*j:{.{.j:V@j:D+D+p;ZOZOe++^# ", +" 8#t-@oA=-%j ==M-r&L;0:L.,.[o);_=+&/-_=(*F&p;u-0,vXl.`-),G@I+I+v:4O(*n#A:r&3-s>h&h&r:M+M+M+H H 6;6;H y%3$8-8-co>$co>$co%,9=9=$#$#$#_:_:mO5= @mOh&>$b@w-(.$O$OT-[%%,M+Q;t>3%!+b*O-1;;om%m%o=Koj:F&D+D+W W W W z%ZOZOz%el$C.A:<=0:~%Q$BOV+ooooW*v&BO9<5$y=:#{:<@<@<@';+&+&';>++&/-6 /-/-6 /-)$)$)$T,T,T,T,T,T,_=#oG#G#_=G#(*C>(*(*{.{.j:j:j:j:p;z%z%ZOC>W*6;Q;[oY:%*% }Os@b*N*, d=I+m%+XP#|$7+u*vX0,e+s.:#kXcoh@F-% ,-z,A:+;+;}&A=s:3,|- ", +" .&}.8o. +;7&,Xz,hO% 0:K=Z$X+U /->+;O#oV@W ZOKod=6@*&|$ -+Xl;l;v:*&w-0:^>6X @P*r:h&h&h&H H H M+6;6;H H mO3$>$>$>$co>$coco9=9=$#@@>$; ~:n#h&H W*d%e;x>!=!=A#H,Q$[%>$z@rXZ$s@==N=T@6;m%m%m%;op;{.D+D+p;W p;z%p;z%ZOZOZOe;om%m%m%(.+;T>N===F.0:z@%,8-co9=_:4*u.$+oop:U 9<5$:#{:<@7*<@';+&+&+&+&6 /-6 6 6 )$;O;O;O)$;OT,T,;OT,_=G#G#G#_=G#C>C>(*C>(*j:j:{.j:F&u-u-:#cos>c+L;]@}O[oN,[oF-,-},A: O#o+Xm%P&L>),7+6@vX0,t$z%j:#o_=6 ';kX:#d%3-%*}O]@WX!+},&:==_o3:p-=* ", +" q@M,Yo!+y+y+}&WX}#N,L.L;Z$9=';';6 T,C>j:W el.r ),L>v:m%l;G@0,4*3%M$)XL-z@r:h&r:h&H M+H H 6;H y%6;y%3$8->$>$8->$>$9=_:@@{XrXY=6;z@4*CoBO(.ooooooA#A#A#H,, );8-Y=3-`+A=},b-+;b@m%m%m%|$W j:p;W p;z%z%ZOZOZOZOu-e@>vXl.+Xm%m%m%6;5o(&7&a#,.a&5=~%H$Y= OS,{Xr:y%$#, A#(.d%5$kX7*7*<@<@+&+&+&+&+&6 /-6 /-)$;O;O)$)$;OT,T,;OT,T,_=#oG##oG##oC>(*C>(*V@{.ZO0,/-!=8- @}O,.U%c#N,[o[o[o[o[o%*M--%% w-),m%I+o=P#r .:vXd=t$z%D+V@_=/->+<@:#{:3-]>% ,.F.H%&-7&_#. I;$%]- ", +" L,sXk;^>y+},H%s@]@t>L.<=`+[%#o';)$T,C>F&z%u-Kou*l.r ),L>I+l;P&P##oX+Q@b +$S,z@r:h&E.h&h&H H H H H y%y%6;y%3<3$>$8-8-_:4*E.~: Oz@Y=$#BOw-U (.(.b@W*x>x>A#x>!=$O);@@3< O1;6XM$7&+.3%C>m%m%I+l.W D+z%z%z%ZOe6@@>r v:m%m%v:3-@:l$-%H%-;U%t>a&c+c+c+a&1;+$ @S,6;_:$O(.d%y=kX<@';s.';+&+&+&>+6 /-6 6 6 /-;O)$)$)$;OT,;OT,;OT,G#G##oG#C>(*C>(*p;t$t$V+[%{X0:,.U%c#c#c#c#K=N,Z$N,N,N,% f+y+q>4*d=' l;v:P#|$7+6@2#Koej:z%Kod=@>*&|$ -L>l;l;+Xr ,,L.qo`+Q;{XP*E.h&r:r:r:H H H H 6;6;6;6;y%3<3$$#4*3<; X+E.Y=z@x>5$BOU V+v&v&p:b@b@x>x>x>x>!=$O[%iXy%~%A*ioH%TX5ogoL>m%m%o=0,p;p;z%e@>@>u*6@6@l.),' m%m%D+-;.;l<7&Q@A=-;]>6X]>}#ioio]@Y:% c+ @E.iXQ$p:w-:#7*s.s.';>++&';+&6 6 6 6 6 6 )$;O)$)$T,T,T,T,;O;OG#G#G#_=_=j:2#u-:#$Ou.+$[o% c#)Xc#)X)XK=c#c#N,Z$N,[oN,Z$io&-!+ OC>v:m%P&L>),r l.2#d=u-W V@#oT,6 s.7*7*M+K=[o[o# ", +" v (On>. +;j ,Xz,3%}O0:c#Z$;*v&T,+&)$_=V@D+ZOt$2#u*7+4O;oG@l;m%+X@>T-Y:H%)X; s>P*E.r:E.h&h&M+H H H H 6;6;6;6;mO3<>$4*8-S,L-P*Y=n#!=9x>A#A#H,$+9=r:~:[of+,Xl$=X8-m%m%m%P#u-z%ZOeu*u*6@6@l.l.6@*&o=m%m%m%!=7&~#M;TX-%A:`OM$M$. `OH%,X7&^>r&]@)X~:r:_:ooBO:#{:7*7*s.+&+&';';';>+/-/->+>+/-;O;O;O)$)$T,;OT,T,_=T,(*e% )Xt>)X)X)X)Xc#)XK=c#K=K=[oN,[o[oZ$Y:`O-%N,:#o=m%P&o= -r l.vXd=t$z%{.G#_=/-+&{:kX(.+,,.}O]>q>*.},7&,XA=~ T:T& ", +" .&ao#$!+j j *.WX}#[oA*,.N,%,<@>+6 T,#oj:p;u-KovX.:`-),o=P&' ' o=eooooA#$O);>${XgoU%q>},A%},(.m%m%m%4OZOZOu-ZOu-t$Ko0,Kod=d=d=2#2#d=vXvX@>@>@>6@@>6@l.l.l..:.:.:7+P&m%m%m%Y=a 5oE+l++&>+/-6 6 6 /-;O)$)$/-)$;O;O;O;OD+2#V@5$U 8-a&N,)XA*K=t>h@A*t>t>)X)Xc#)XK=K=K=[o[o[o[o[o}O>:u@q>!=4Om%I+G@ -|$*&u*2#t$z%F&C>G#;O+&7*kX:#~:,-U%%*F.. &-&:qoM-T%1O_*P% ", +" %o4+s A:+;-%H%s@]@t>h@<=% Q$#o';/-T,#oj:z%e7+`- -+X' l;P& -_=goqo}#~:{XP*z@P*r:r:r:r:M+H H H H y%8-8-E. Os>{X @H U Cow-:#:#9x>H,T-@@3< O9%6X_o7&E+}#{.m%m%I+.:z%u-t$evXvXu*u*6@6@6@.:l.l.*&.:.:4OI+m%m%;oc#s<~#=XE+~Ol$l<=XO-T@TXH%L.y=ZO.#tON*% +,h&);(.5$kX7*7*<@s.+&';>++&>+6 6 6 6 /-;O;O)$)$/-)$V@z%z%kX5$u.rXc#N,A*t>h@L.L.h@A*t>A*A*t>)X)X)XK=c#c#N,Z$[oN,[oZ$}#7&A:y%0,I+l;v:;o),r l.2#t$z%D+V@_=;O6 s.{:7* @}#}O}Or&M$qo7&&-A;>:K$^ !% ", +" f*0 dX^>+;&-. >:`+h@h@<=F-H,C>';/-_=C>F&p;e*&4OP#G@l;l;v:*&w-[o&-}O; {Xs>z@P*z@r:E.r:h&M+H 6;y%H s>{X{XX+n#Q$9A#$O, 4*y%~%h@,-A;N=T@;*G@m%m%G@@>u-t$t$t$Ko0,0,0,d=2#2#2#2#@>vXu*@>@>6@6@l.7+l.l.l..:*&.:7+L>' m%m%)$M$x=~#5oT>[,.;O-E+&:<=>$G#@>m%C>-;u@M$Y:=+CoBO Ot>)Xh@c#t>c+c+0:L.L.h@A*h@A*t>)Xt>)Xc#c#c#K=N,[oZ$[o[o[oY:^>qorX_=v:m%v:o= -`-l.vX0,e%}> ", +" P%/#',a#},j qoM-b L;0:t>L;=<(.G#>+)$#oV@D+p;t$2#u*7+),o=+Xl;I+v:vX);}#H%h@ O{XP*z@P*P*z@r:h&r:H H r:E.z@~%X+$+Cow-y=';<@CoCoCo5$5$w-BOBOe;e;U V+V+V+V+v&v&b@b@b@W*A#$O$+iXh&rXN,f+Q@~O+.9=m%m%m%P#d=t$KoKoKo0,d=0,2#2#vXvXvXvXu*@>6@6@6@l.l..:7+*&.:*&*&r 7+r +Xm%m%l;iXl$M..;t+a .;(&M$n#';t$o=m%m%m%_:7>u@`O]>9%{X4*(.9<:#7*7*s.s.s.';+&>++&>+6 6 6 6 )$#oV@T,>+,,y%0:N,A*t>L.c+1;V a&a&0:0:0:L.h@h@t>A*t>t>c#c#)XK=K=K=[o[oN,N,[o,.. +;}OCoG@m%v:+XP#`-l.@>d=t$p;F&C>T,/-';{:kXQ$go}OF-,-z,A:+;+;A:WX2O]$4- ", +" v%I-n=M$-%+;Q@z,#<% 0:)XK=n#w-)$6 )$G#{.D+p;KovX.:`-4OL>I+' P&o=e<{XM$F.kOS,s>P*E.z@E.r:r:r:r:r:r:L-Q;mOU :#:#>++&kX,,,,,,,,Co5$5$5$9A#H,$+>${Xgo,.q>},A%Q@d%m%m%m%4Ot$u-0,d=0,d=2#2#vXvXvX@>@>@>6@u*6@l.l..:.:*&.:7+7+r 7+`-`-4OP&m%m%+X+,r#5&t+t+u@1;5$D+ -m%m%m%m%m%m%Q;=XC.M-<=9%z@);p:5$kX7*7*s.';s.+&+&+&+&+&6 6 /-;O)$/-C>b@+$c#0:h@t>c+kO9%V 1;V a&a&a&0:0:0:A*h@t>)XA*t>c#)Xc#K=c#c#N,[o[o[o[o}O>:y+WX, `-m%I+G@ -|$7+@>0,u-z%F&#oT,)$>+7*:#9P&l;I+;oG#kOqo]>~:n#s>z@E.z@P*E.M+r:S,5=s>p:w-:#>+)$<@:#{::#y=:#,,,,,,Co5$9@>6@6@u*l.l.l..:.:*&7+7+.:r r `-`-|$|$P#' m%m%*&#t>)Xc#c#K=c#K=[o[oZ$[oZ$io+;,XM+t$' m%G@;o4O*&u*d=KoZO{.(*G#;O>+7*kXkXL-<=,.Y:s@H%&-y+^>_o/:L$Y% ", +" v%o*iO!+y+j *.s@<=K=h@6X)X$O;O>+)$_=(*F&p;u-d=vX6@`- -o=' m%I+`-,,% &-}OX+n#s>P*z@r:r: OrX O);BO5$/-;O/-s.7*<@7*{:kX,,y=y=,,,,y=5$5$w-9vX@>@>6@6@6@.:l..:*&.:.:7+*&r r r 7+`-`-4O4O4O4OG@m%m%l;V@.# O#ou*m%m%m%m%m%m%m%m%m%l;m%9%,L.1;1;t>0:3-go=t>)Xc#)X)Xc#K=c#N,Z$[o[o[o%*==_#;*#ov:m%P&;o),*&6@vXt$ZOD+(*G#;O6 s.{:{:; ,-L;L;r&M-_#7&qoM-O#7=Y% ", +" P%c=Y-A:j tO`OF.Y:t>h@]>[oA#G#>+)$_=(*F&p;ZOd=u**&),P#+Xm%' v:6@A#]@!+)X O{Xz@r:n#rXrX%,e;BO<@;O;O6 +&';';s.s.7*kX{::#:#kXy=,,,,Cow-w-BOBOBOd%e;e;U U U p:p:p:b@W*!=$+iXh&;*N,f+A:M;b-);m%m%m%L>vX2#@>vX@>@>@>6@6@6@.:l..:.:*&*&7+7+r r |$`-|$`-4O|$),4O|$ -P&m%m%m%4O0,P#m%m%m%m%m%m%m%l;' l;m%m%m%M+TXs,,Xb A*L-co!=9<,,kX{:{:7*<@<@7*/-W D+!=.#1;=c#c#)Xc#K=N,[oK=N,N,L;H%tO)XkXL>m%v:;o -r l.vXt$ey+&-. A.O b: ", +" %ox@l:,Xj &-. >:`+L.A*}#% A##o+&;O#o{.D+z%e<2#6@*&4OL>+Xl;' v:d=9=hOM$c+Y={X.#;*E.T-p:{:C>G#;O6 6 6 +&';+&s.<@s.7*{::#y=:#:#y=,,,,Cow-9A#, %,n#+$U%N*&-+.A:5$m%m%m%),2#2#@>u*@>u*u*l.l.l.*&*&*&7+7+7+r r |$|$4O4O|$4O),), -),),L>m%m%m%m%m%m%m%m%m%m%m%l;' I+P&I+m%m%m%m%=)X)X)Xc#c#c#K=N,[oK=% a#+;}#U o=m%P&o=P#`-l.@>0,u-W V@(*T,/-s.<@{:z@%*% N,3%z,==j tOH%s@%*k&Y P% ", +" s;W>a#^>j _#M$r&,.L.)X]>)XW*#o>+;O#oj:p;e<0,vXl.7+4Oo=v:m%' v:e++&';<@<@7*kXkXkX:#y=,,,,y=Co5$w-BO9Z$[ohOa#,X+;-%!+q>z,-;U%0:t>,.goU _=6 ;OG#j:p;ZOKovX.:r 4Oo=I+l;v:G@F&.#}&r&L-@@ooT,V@(*C>#oT,T,T,)$/-6 /-6 >++&+&s.<@7*{:{:{::#y=y=:#y=Co5$9P&m%m%G@*&u*@>l.l..:*&*&*&7+7+r r `-`-`-`-|$|$),), - -),),P#P#;o;oo=v:m%m%m%m%m%m%' I+I+P&P&P&P&I+l;m%m%m%m%V+`OA%},F.[o @mO, (.5$';#oG#ooH$L-kO% )X.#;*rX5=.#+,gogogogo+$3-=<3-=<=)X)Xc#K=c#K=N,[oZ$[oioy+==8-u*l;m%+XP#4O7+6@d=u-p;F&(*T,/->+7*{:!=5=L;F-ioq>*.-%j ,Xa##I+l;P&;oG#A*}&h@x>C>{.(*C>#o#oG#;O;OT,)$;O/-)$6 >+';';';<@<@{:kX{::#y=y=y=,,,,Co9<5$9Q$4*h&rX[or&A:M;7>(.m%m%m%;ou*l..:.:.:l..:*&7+7+*&r r |$`-|$|$|$4O -), -P#P#;oL>;o;oL>o=' m%m%l;' l;' P&P&P&P&P&' ' l;m%m%l;m%m% OTXs,,X-;A*L-9=!=:#T,U 3A*)Xt>)XK=K=K=N,[o[oK=Y:&-tOY=e_=T,>+7*kX9< OY:U%6XN**.tOy+Q@a#f+9-D,P% ", +" 8#Jo}X*.+;+;A:a#io[oL.U%t>$#{:>+)$#o(*j:W u-0,vXl.|$P#G@P&I+I+),kXL;A=3<>+(*C>G#C>#oG#_=T,T,T,;O;O)$/-6 >++&+&';<@s.<@{:{::#:#:#y=,,Co5$5$w-9<9=<,.WXtOE+`O6 m%m%m%),u*.:l.l.*&*&7+7+*&7+r |$`-`-|$4O|$),), -P# -;oL>L>L>L>L>G@+Xm%l;I+I+P&P&P&P&P&' I+l;m%m%m%m%m%l;m%m%N,(&b*H%]>1;M+Q$v&iXco; % ]@V ; @;* @rX.#rX.#rX5=5=+,+,go+$+$3-=<=<=A*t>)Xc#c#K=K=c#K=[o[o[oL;Q@-%rX(*+Xl;+X;o),*&u*2#KoZOj:V@G#T,>+7*kXkXn#]>U%Y:F.A;tO7&^>A=r&U.c@^% ", +" 8#+#}o!+j +;!+q><=K=h@]@h@oo6 ';)$_=(*F&p;u-d=u*.:`-P#+X' ' v:*&x>Y:#G#_=G#_=;OT,T,;O)$6 /->+';';>+';<@7*{:{::#,,y=y=,,,,Co5$5$w-9L>L>L>o=o=+X+X' P&v:v:P&P&I+' ' l;' m%m%m%m%l;m%m%m%2#3%E+j A=% rXH$P*y%N,#<0:; ~% @0.0.;*;*~:~:rX.#.#.#5=+,go+,+$gogo+$=<3-=t>A*t>)Xc#c#N,N,N,[oK=U%*.-%c++&G@m%P&o=4Or l.vXKoz%D+{.#oT,+&7*kX{:s>#:*,Y#h ", +" 8#+#I=!+j +;*.s@]@c#A*]@a&v&T,+&/-_=V@D+z%e<2#u*.:`-P#v:m%I++XvX4*`+[o$O_=#o(*#oC>G##oG#_=T,T,_=;O/-6 6 6 +&+&>++&s.<@{:{:kX:#y=y=,,,,,,5$5$w-9<9;oo=o=o=o=+X+Xv:v:v:P&I+' I+I+I+I+' ' l;m%' ' m%m%m%m%m%A#A:l$==s@L;1;U%>:%*5=; X+X+Q;0. @;* @~:~:~:rXrX.#5=.#5=go+,go+$+$3-=<=<=A*t>)Xc#c#c#c#c#[oZ$N,}Oz,_#F-9<;om%I+o=),`-.:vXKoz%W j:G#T,>+<@{:7*P*,-L;}O-;_o_#&:qoM->:J@m&1$ ", +" 8#+#V,}&&:-%H%s@<=)XA*]>L.v&T,>+)$_=(*D+p;u-d=u*7+|$P#v:m%' +Xt$M+]@a&b@_=#oC>C>#o(*#oG##oG#G#T,;O;O)$/-6 6 +&+&';<@s.7*kXkXkX:#y=,,,,,,5$w-Co97+7+7+7+`-|$|$4O4O4O),), -P# -P#;o;oL>;oL>G@o=+XG@G@G@+X+Xv:v:v:' I+I+' l;l;m%m%l;l;m%m%m%m%m%m%m%m%0.b*TX*._o,XA=t>go0.0.Q;Q;Q;Q;0. @ @;*;*~:;*.#.#rX.#.#rX+,go+,go+$+$go3-3-kOkOkO9%1;1;1;V V c+a&a&a&L.0:0:L.A*A*A*A*t>)Xc#)Xc#K=c#N,[o)Xa&h@hO]>ooP#m%P&G@4Or .:u*0,ZOW {.G#;O/-';{::#z@io,.F-3%M-_#7&_#M-r&]@!*`> ", +" ^%F,:.,X7&-%H%>:6XA*A*]>t>v&T,>+;O_=C>F&z%e<0,6@`-),;o+Xl;' v:e< O,-3-U #oC>(*#oC>C>C>#oG#T,)$T,T,;O;O6 6 6 +&';+&';<@7*{:{:kX:#kX:#,,,,5$5$5$5$w-BOd%V+V+p:(.(.H,[%%,kOz,A;A;j C:M-{.m%m%m%;o*&`-|$|$4O4O4O),),),P#P# -;o;oL>o=;oL>o=o=G@+XG@v:v:v:P&P&P&P&' l;l;l;l;m%m%m%l;l;m%m%m%m%m%m%l;I+v:}OM;lK=1;.#0.0.Q;X+~%~%Q;Q; @ @;*;*;*~:rX~:rX.#.#.#+,+,+,+$3-+$3-=<=t>c#c#)XK=Z$t>goH,#oA#U%F.$+4Om%I+P#),4O.:@>Koe:]@w%(% ", +" .&AoK:,X7&tO. r&`+h@t>ioA*p:T,>+;OG#(*F&ZOt$0,u*r -o=v:m%' +XW @3%5=BOC>C>(*{.(*(*(*C>G#_=;OT,/-/-;O;O6 6 +&';+&';<@<@<@7*kXy=y=:#y=,,y=,,9<9$t>}#}OU%N*A:7>A%c#u*m%m%' -r |$`-4O),),),), - -P#P#;oL>L>L>G@o=o=v:G@v:v:+XP&P&P&I+I+' l;l;m%m%m%m%m%m%m%m%m%m%m%m%l;P&G@9<)X&-M;l<7&}&s@Y:h@=<.#Q;Q;Q;~%X+Q;Q; @;* @ @;*;*;*~:rXrXrX5=.#+,+,go+$+$+$=<3-=t>)X[o% 0:>$';#oV@C>b@% M$4*),m%I+G@;o`-.:@>t$u-W j:#o;O/-';<@:#r:Y:L;[o#7&},H%>:Y:L:E- ", +" K@`*Uo==j &-. r&`+h@t>ioA*v&G#>+;O#oV@D+ZOKo2#6@7+),G@v:l;l;+Xp;+,b ;*BOC>V@C>{.(*(*(*C>G##o_=;OT,;O)$;O6 6 6 >++&s.<@s.7*{:7*7*kXy=,,,,Cow-BOBOBOe;U v&v&b@rX% 1;goL.Z$<=z,_#~Op#3L>o=o=o=G@G@o=v:+XP&I+P&I+I+I+I+l;l;m%l;m%m%m%m%m%m%m%m%m%m%v:;o;O+,*.TX~O+.^oj Q@A=ioF-c++,;*X+X+~%~%X+~%~%Q; @;* @~:;*;*~:~:rXrX.#.#+,+,go+,go+$+$3-=<=V@u-ZOF&T,b@Z$M-co7+m%l;+XP#`-.:vXd=u-W {.(*_=/-<@{:{:8-}O}OF-hOa#Q@j },H%F.6XTom> ", +" } [-q>==j &-M-f+U%0:t>]>t>v&_=+&)$#oV@F&ZOKo2#u*r P#o=P&m%I+o=F&9%>:~%9<#oV@C>j:V@(*C>C>G#G#G#T,T,T,;O;O/-6 /-+&';';s.s.s.7*{:kX:#,,,,Cow-5$9<9]@WX,X7&#=+;>+m%m%l;o=),),), - - -;o;o;oL>L>;oo=o=+XG@G@v:v:+XP&P&P&I+I+l;' l;' m%m%m%m%l;m%m%m%m%m%m%v:;oW P*-;+;~O#=C:l$C.tO!+A=-;,.A*kO~:0.~%; ~%~%X+~%Q;Q;0. @ @;*~:;*~:rXrXrX.#.#5=+,go+,go+$+$3-=<3-kO=<=<9%1;1;1;V V a&a&a&0:0:L.L.t>[oU%9%$#9<>+ed=u-W {.(*_=/-s.{:{:%,Z$% [o#j:{.V@(*(*C>G#G#_=T,T,;O)$)$/-6 6 >++&s.<@<@<@{:{:kX,,,,,,Co9L>o=o=L>o=+X+Xv:v:v:P&P&' ' P&l;l;m%m%m%m%m%m%m%m%l;m%m%m%P&L>ej &-,X. s@#<,.A*9%5= @~%X+; ; ; ~%~%Q;Q;Q; @ @ @;*;*~:~:rXrX5=.#5=5=+,gogogo+$3-3-=H$ood%C>d=d=z%W p;z%D+V@/-U 0:. P*0,' l;+X;o|$*&u*2#eZ$K=,-z,Q@j -%!+s@%*'XR ", +" V*]&WXqoy+_#M--;}Oa&A*<=V d%_=>+;OC>F&D+z%KovX6@7+4Oo=I+l;P&o=V@K=WX{X:#G#(*V@j:{.V@(*(*C>G#G#G#T,T,T,T,;O/-6 /-6 +&+&s.<@7*{:7*{::#,,,,y=(.P*~:rXA*N,=<5=1;Z$Y:#L>o=o=o=+XG@v:v:v:v:P&P&' I+I+m%m%m%m%m%m%' m%m%m%m%I+o=2#!=3-s@TX0O#%E+TXy+&-,XA;_o>:#kO.#;*0.X+; X+L-; ~%X+~%Q;X+ @ @ @;*;*~:~:.#.#rXrX.#5=+,5=+,+$+$go3-=$!=kX@>d=W F&F&D+p;p;D+D+V@s.U 1;M- Oe`-.:6@2#e+;OC>j:D+ZOKo@>l.r -L>I+I+v:o=(*Z$a#P*:#G#(*j:F&{.V@V@C>C>G#G#G#;O;O_=T,;O/-6 6 >++&+&s.<@<@7*7*kXy=kX4*Y=S,9%L;A*.#kON,,.}#b a#H%==+;b*b-=X.;p#[%' m%m%P&L> - -P#L>L>L>L>o=o=G@G@+X+Xv:v:P&P&P&I+I+I+m%m%m%m%m%m%l;' ' m%m%m%),CoY=Y:u@t+@:#=^oy+},==!+_os@b <=`+F-A*1;3-5=Q;X+~%X+X+H$; ; ~%~%~%Q;~%0. @ @ @;*;*~:rXrXrXrX5=+,go+,+,go+$+$=<=<=V@{.F&D+D+p;z%F&F&C>7*V+3-M-~%p;v:' v:P#`-7+u*d=u-W D+(*T,;O';7*{:T-3-F-N,ioq>,Xj -%!+N*Y::=T= ", +" K E;WXqoj _#M$3%U%a&A*`+goBO;O>+;O#oj:W ZOKo@>l.*&),o=P&I++X;o_=}Oq>r::##o(*{.F&{.j:j:C>(*#oG#G#T,T,T,;O;O;O/-6 >+';+&s.<@<@';s.v&%,8-~:F-t>.#.#h@L;]@b N*M-!+&-y+TX~OC:A%^>b Q;@>m%m%l;+XP#P#L>L>o=o=G@G@G@G@v:+Xv:P&P&P&I+I+' ' l;' m%m%m%l;' l;' ' l;m%m%o=[%^>JXr#~#b-C.-%^>}&. WXf+]>%*F-)Xa&9%+$rX0.Q;; L-X+; ; H$H$X+X+~%Q;0.Q;Q; @ @ @;*;*~:.#.#rX.#5=.#+,gogogo+$+$=<=<=_=)$';7*7*!=5=U%N,ioa#,Xj -%!+WX]>d;Y. ", +" 2$_;a#_#j &-M$3%% a&t>U%~:w-)$+&;O#oj:W z%Ko@>l.7+4Oo=P&' +XP#>+,.s@mO{:G##o{.W F&V@(*(*V@(*#oG#_=;OT,/-)$;O6 6 6 +&';+&;O7*!=u.H c+K=X+n#rXh@,.,-F._o}&^>-%C.^oC:+.y+q>0:A#),m%m%m%m%l;G@;oo=L>o=o=o=o=+Xv:+Xv:P&P&I+' I+l;l;' m%m%m%l;' ' ' ' I+I+I+' m%m%z%a#g*[,b-u@tO,XH%A=>:hO]@% t>a&9%3-.#;*0.~%; H$ OH$; L-L-L-; ; X+~%~%X+Q;Q; @ @ @;*;*;*rXrXrX.#.#.#5=+,+,+,go+$+$=<3-=<3-1;h@t>= @`+F-ioa#,Xj tO*.q>,-p=MO ", +" /;,@A=_#j _#M$3%}Oa&)XZ$Q;9<)$>+;O#oj:D+ZOKo@>l.7+4OL>P&l;+X),';`+s@8-7*G#C>j:F&j:V@{.V@(*(*G#_=G#T,T,;O)$;O/-6 /-T,>+BOU [%+,L.S,8-h&~%kOc#Y:r&M-,X},&:N=M;C:7>}&%*3[,lq>,Xj -%}&q>,-+ 9X ", +" #.,@z,_#+;_#M$3%U%L.t>F-0.w-;O>+;O#oj:W ZOt$@>l.r ),L>P&' +X -+&,.s@3<<@#oV@{.D+{.{.{.V@(*V@(*#oG#;OT,T,;OT,(*(*{:d%(.P*L.~:8-9=mOh&L-+,t>Y:>:A;},b*M;=Xb--%F.~%#om%m%m%m%m%m%m%m%l;' v:G@+Xv:+Xv:v:P&I+P&I+' ' l;l;l;m%m%' ' l;l;' I+I+I+' P&v:v:P&' m%m%l;Co#=~#p#tO`OF.<=}O)Xc+=<+,;*Q;~%; L-H$Y= OY=Y=S,H$S,S,L-; L-; ; ; X+~%~%~%~%Q;0. @;*;*;*;*rX.#rX5=5=.#.#+,+,.#gokO0:c+9%1;n#w-C>T,6 6 ;O)$T,T,T,_=#o#o#o(*(*(*{.{.F&D+j:j:V@;Oy=U Q;r&rX#o+X' v:P#`-7+l.2#u-W F&(*_=/-s.7*{:W*~%,.U%]>WX,Xj tO*.N*}#z-n: ", +" @X_;a#qoj &-M-hO}O0:t>,..#9<)$>+;O#oj:D+ZOKo@>l.r ),L>P&' +XP#6 L;N*6;{:C>j:{.D+F&j:{.{.(*(*C>#oG#T,T,{.j:/-kXy=9==<~:M+>$>$3~%1;U%b `O-%^oA%&:M$9%U P#m%m%m%m%m%m%m%l;' I+v:G@o=v:P&P&v:P&I+' I+' l;m%l;m%m%m%l;l;l;l;' ' I+' P&P&v:P&v:+XP&l;m%m% -~%T@(&y+!+F.6XK=c++$;*Q;; H$S,Y={Xn#n#Y={XY=n#Y=S,S, O OH$L-L-L-L-; X+~%~%~%Q;Q;0. @ @ @;*;*rX.#rX.#5=rX~:goc+0:9%0:9%, +&+&>+6 6 6 6 )$;O;OT,T,T,_=G##o(*(*C>V@j:D+D+j:F&{.;O,,V+0.f+;*C>+X' +XP#`-*&u*d=t$W D+(*_=/-s.<@7*A#;*L;% ioWX,Xj tO*.N*<=R:9o ", +" Y#E;WX_#j _#M$b U%a&t>Y:3-BO)$+&;OC>j:W eI+' v:L>G#% N*r:{:(*F&F&p;D+F&{.j:V@#o#o(*(*D+(*>+7*!=X++$n#6;M+8-%,>$8-y%r:Y=~:A*]@z,^>b-u@F-$O2#;om%m%m%m%m%m%m%l;' v:G@+X+XG@v:v:v:I+P&I+' l;' m%l;m%l;l;' l;' ' I+' I+I+P&v:P&v:+Xv:v:G@I+m%m%m%u->:(&l$&-_oioK=9%~:~%H$S,Y={Xs>s>s>P*n#n#{Xn#n#Y=Y= OH$S,L-H$; L-; ; ; ~%~%~%Q;Q;0. @ @ @;*;*~:.#~:;*rX+$1;9%0:A*mOU ,,<@6 >++&+&+&/-/-6 ;O;OT,;O;OT,G#G#C>(*(*V@{.j:D+F&j:V@;O:#V+~:s@Q;{.v:' +X -|$*&u*d=u-W F&(*G#/-';7*7*$Ogo% N,ioWX,Xj -%!+s@Y:h%0& ", +" E-G%WXqoy+&-M-b }O0:A*<=1;e;;O+&;O#o{.D+ZOKovX6@7+),o=P&l;v:o=(*K=q>z@7*C>V@D+W D+F&j:{.(*(*W F&T,)${:r:rX O{Xs>6;>$>$3<3<8-8-y%E.H$+$N,3%*.y+~#`+t$m%m%m%m%m%m%m%l;' P&+XG@G@+XG@+Xv:' I+P&' ' l;l;l;m%m%m%l;' ' I+' I+I+' ' P&v:P&v:v:v:+X+X+XG@' m%l;m%T,l$(&u@,X>:U%c+~:H$n#s>z@s>P*E.z@s>z@s>{X{Xn#n#n#Y=S,S, OL-H$L-; L-; X+~%~%X+Q;Q;0.;*;*;*;*;* @ @+,1;kOc+K=rXx>e;,,+&>+';7*<@';+&+&6 6 /-)$;O;O)$T,_=G##o#oC>(*(*V@j:D+F&j:V@T,{:d%+,A=X+W ' ' v:;o`-.:u*2#u-W F&(*_=)$s.7*7*[%9%F-K=}#a#==+;tO!+N*Y:Z@NX ", +" (%LOq>==j &-M-b ,.h@A*]>0:v&;O>+;O#oV@F&e<0,vX6@7+),o=P&m%P&o=V@t>q>Y=kXV@{.D+F&D+D+F&W ZOj:;O';, O On# Os>mO8-3:,XN=~On#l;m%m%m%l;l;I+I++X+Xo=G@o=+X+Xv:P&I+' ' I+' m%m%m%m%m%m%l;l;l;' I+' P&P&I+I+P&v:v:v:+X+X+X+Xo=o=I+m%l;' b@0OE++;`O,-K=+$~%{Xz@E.r:E.z@E.z@P*z@P*n#{X{Xn#n#Y=n#S, OH$L-L-; L-; ; ~%Q;~%Q;Q;Q; @;*Q;Q;rXgogo1;}O1;coA#p:+&/-<@kXkX7*s.<@';';+&+&6 6 )$;O;O)$T,T,T,G##o(*(*(*V@j:j:D+j:V@G#<@V+1;M-n#t$l;l;+X;o`-.:vXd=u-W j:C>_=/-s.{:{:4*0:F-N,}#a#==j },*.N*Y:(-0X ", +" *@,@N*,Xy+},`Or&`+h@t>}#A*v&T,>+;O#oV@D+ZOKo2#u*7+4OL>P&m%' G@j:L.q>L-:#{.F&D+D+z%e==E+Q@p:I+m%m%l;I++Xo=o=o=G@G@+X+Xv:v:v:P&' ' I+I+l;m%m%m%l;l;l;' l;' ' I+I+P&+X+X+XP&v:G@+X+Xo=G@G@o=+XI+m%m%r 5=5ob-_#a#6Xh@rX OP*r:r:E.E.E.z@z@P*P*z@{X{X{Xn#Y=n#n# OS, OH$ OH$L-X+; X+Q;~%Q;~%~%~%0.rX.#+$t>N,S,@@$O,,;O+&,,,,,,:#{:7*<@<@<@s.';';6 6 /-)$)$)$;OT,T,G#G#C>(*(*{.{.V@F&j:V@C>+&(.h@. h&@>m%I++XP#`-*&u*d=u-W V@(*_=)$<@{:{:9=c#F-F-#)X}#)XV+_=+&;OG#(*D+eG@+X+Xv:v:v:I+I+I+I+' m%l;l;m%m%m%l;I+' ' ' ' P&I+P&v:v:v:v:+X+X+XG@o=G@o=L>L>L>v:l;m%m%F&M$E+p#Q@>:}O1;~%{Xz@h&H h&h&E.z@E.z@P*P*P*{X{Xn#{Xn#Y= O OS,H$ OH$L-; ; ; ~%X+X+~%0.0.~:A*}OrXy%>$(./->+y=CoCoCoy=y=:#{:{:7*<@<@<@';+&+&6 /-/-;O)$T,;OT,G#G#G##o(*V@{.j:D+D+{.C>/-b@K=. mO*&m%I+G@;o`-*&@>Kou-p;V@#o_=/-s.{:kX37&},H%F.6X@ %% ", +" q@&#C ,X7&-%H%>:%*t>)Xiot>p:T,+&;OG#(*F&ZOe<0,6@r -o=+Xm%l;+Xp;go>:Q;:#F&z%z%0,(.iXy%H$5=L-3<8-6;H M+H 6;6;6;6;6;mO3<3o=L>L>P#+Xm%m%m%+&(&A%&:*.3%N,goL-P*r:h&H M+h&r:z@E.z@P*s>z@P*{X{X{Xn#n#S, OS, OH$L-L-L-; L-~%X+X+~%=C>(*j:j:j:D+V@#o;Ox>F-M$9=|$m%P&L>P#`-*&vXKoe:Y:q=%: ", +" ^%~Xk;,X7&tOA;s@6XA*A*]>A*p:;O>+;O_=(*D+p;e<0,u*r ),P#+Xm%I++Xz%X+hOrX7*z%<@Q$[%{X~:~%h&y%h&h&h&M+H H H 6;6;6;6;6;y%3o=L>;oL>;o;oP&m%m%I+$+@:b--%. ]>A*.#H$P*r:r:M+h&h&h&r:E.E.z@z@P*P*{X{X{Xn#n#n# O OS, OL-L-; ; OS,~:Z$c#~%S,H d%>+Coe;e;9(*{.j:j:j:(*C>T,!=U%a#@@4Om%P&L> -`-*&@>Koz%W j:#o;O/-s.7*7*M+]@`+F-b M$^>y+_#. f+Y:J&LX ", +" 8#}%}o}&y+-%A;N*<=)XA*ioL.V+T,+&/-G#(*D+z%u-d=u**&r P#v:m%I+v:eY:1;%,, mOQ;rXs>h&h&h&r:E.r:h&H H H H H H 6;6;6;6;mOmO6;H h&{X0.0:]@A=_#(&Q@v&I+m%m%I+v:v:I+I+' l;l;m%m%m%m%m%m%l;' I+I+' P&I+P&P&v:v:P&v:+X+Xv:G@G@o=G@L>L>L>o=L>;oL>P#L> -L>' m%m%u*kO[,s,_#a#`+c+ @Y=E.r:h&H M+h&r:r:z@E.z@z@P*s>s>{Xs>{Xn#n#S, OS,H$; Y=s>; c+N,+$~% OT-y=,,e;v&U e;BO9(*(*{.j:{.(*V@T,T-L;F.Q$ -m%I+o=),r l.vXt$z%W {.#o;O6 ';{:kXr:]>`+U%b M$qo7&_#M$b 6XqOn% ", +" 8#+#I=*.j j !+N*<=N,A*]>0:U )$s./-G#{.D+p;e}#M-tO#%]>eo=L>;oL>;oL>;oP#P#P#),+Xm%m%m%_=z,C:b*A:r&% kOX+s>r:M+H H H h&r:E.E.z@z@P*s>P*s>n#{Xs>n#n#n#H$n#z@P*3-[o1; @5=y%9<9m%I+G@ -r 6@2#KoZOF&j:G#T,>+<@7*{:s>,-,.U%f+`O_#y+qo_of+6Xt#C% ", +" 8#<*I=*.-%+;A:q>ioZ$A*]@L.b@>+s.)$G#V@j:W eP&I+I+l.oo#m%m%l;' l;m%l;m%m%l;m%m%m%m%m%m%' ' I+P&P&P&P&v:v:+X+X+X+XG@o=G@G@o=o=L>;o;oL>;o;oP#;o - -),),P&m%m%m%Co(&lz@P*n#n#n#Y=n#h&r:X+a&1;+$kOL-!=v&p:(.(.p:p:v&V+V+U U e;BOBO9+6 6 6 /-;O)$;O_=_=#o#o#o(*C>(*(*(*V@j:>+H ]@Z$9+{:kX{:s>hO,.,.r&H%&-+;==z,b N;>=K@ ", +" 8#Jo@+H%-%j Q@A=,-F-L.%*A*$+s.+&)$T,#o{.W eI+' I+4O5$6XqoK= Os>P*P*P*z@E.E.r:h&h&h&h&M+M+H H H H 6;6;6;H 6;mOH P*L-go[ob !+b*+.co+Xm%m%m%m%m%m%m%m%m%m%m%m%P&m%m%l;P&P&P&+X+XP&+X+Xv:+XG@G@G@o=o=L>o=L>;oL>P# -P#P#P# - -),),), -P&m%m%P&$#,>s,},z,<=L.rXS,E.h&M+H H H H M+r:h&E.z@z@P*P*P*{Xs>r:H s>=+6 6 )$;OT,T,T,_=G#G##oC>(*(*V@(*j:;OP*io=<';G@' G@P#4O7+u*d=u-z%j:(*G#T,>+{::#:#z@,-Y:6Xs@A;tOj Q@a#f+[&l+@& ", +" 8#;$(XM--%y+^>z,#+)$G#j:W ZO0,vX.:|$),o=I+' P&P#6 Z$_#L;~%s>z@z@z@z@z@z@E.M+M+h&h&M+H H M+H H H 6;6;H y%mOH P*; 3-Z$r&A:b-*.w-I+m%m%m%m%m%m%m%m%m%l.e;s.' m%m%m%I+v:P&+Xv:+X+Xv:+X+X+Xo=L>o=G@L>;oL>P#P#;o - - -), -),4O),4O -' m%m%2#L.a TX^>q>,.1;0.{XM+H H y%y%H H M+h&r:z@z@z@P*P*r:6;E.Q;+$.#V c#r:$+$Ooov&W*W*x>x>b@b@b@(.p:V+U U U e;e;BOBOBOw-w-5$,,,,,,y=y=:#{:{:{:<@<@+&+&';>+6 6 /-;OT,;OT,;O_=G##oC>#o#oj:ZOd=s.V WX0.V@v:m%+X),|$7+6@d=u-W j:(*T,)$>+<@kXCo{X<=%*]>N*!+-%j A:a#f+]=O=P% ", +" $ q:~*a#},j _#_o-;L;L.t>Z$~%BO;O';)$G#{.D+ZOKovX6@7+4Oo=I+l;P&o=j:a&^>io~:n#s>P*z@z@z@E.r:M+M+r:h&h&H H H H H H 6;6;H 6;mOH P*; =<[of+*.=Xc#Kol;m%m%m%m%m%m%;o(*x>A*u@c#m%m%m%m%I++X+Xv:v:G@G@v:+Xo=o=L>L>L>o=L>;oP#;o - -P# -P#),4O),|$|$`-L>m%m%l;';A;=XC.!+f+Z$+$L-P*h&6;y%y%6;H H H h&r:r:z@z@H mOY=;*;*=x>b@b@b@p:v&v&U U U e;e;e;BO9<9D+ZO{.6 :#E.. ==z@Kol;l;G@P#`-*&6@2#e3%a=@#P% ", +" Won-q>qoy+&-. f+L;h@A*%*V (._=+&T,G#(*F&z%t$d=6@*&4OL>v:l;' P&e<~:!+f+5=Y=P*P*z@E.z@E.r:h&h&r:r:h&H M+M+H H H 6;6;6;M+H 6;z@L-3-[o3%H%=Xy%o=m%m%m%m%G@d=7*~%M-~Ow@~# @m%m%m%' v:+XG@+Xv:o=o=G@G@L>L>L>;oP#P#;oP# -;oP#), - - -),|$|$4O`-`-+Xm%m%I+A#=XlrXS,E.M+y%y%y%6;H H H h&E.6;H z@H$Y=5=}Oc+3H,$O$O!=!=A#oooox>ooW*b@p:p:p:V+U U e;e;e;BOBO9<5$w-w-,,,,y=:#y=kX{:kX{:s.<@';+&';>+6 /-6 ;O;OT,;O_=C>F&D+C>6 A#S,V 3%&:A;_:`-m%I+o=;o`-l.@>2#u-W {.C>T,)$';kXkX$+9%% F-hO_oQ@j },A;s@eoVXSo$ ", +" c@,#z*Q@j tOH%>:Y:h@A*}#N,x>#o>+T,_=(*F&p;e<0,u*7+ -P#G@l;l;P&0,E.WXq>=s>E.z@E.E.z@E.h&h&r:h&r:H H H M+6;6;6;H H 6;H E.L-+,N,}#`OtOe;l;m%m%;oW h&#JX==BOm%m%m%I++XG@G@o=G@G@L>;oL>L>L>;o;o;oP#P# - - - -),4O4O4O4O4Or |$`-|$P&m%m%G@P*JXp#&-z,]@a&0.s>M+6;y%6;6;6;H H 6;6;M+E.s> Oc#% ; $#9=T-A#H,$+T-T-$OH,H,!=A#A#oooox>b@b@(.p:v&U U U U e;e;d%d%9<5$5$5$,,,,,,y=y=kX7*{::#<@<@<@';';>+6 6 /-;O;OT,C>(*#oC>7*mOkOt>F-`+a#7&>:x>;om%+X;o -`-l.vX0,ZOW V@G#T,)$';kX{:y%N,F-F-b M$^>y+&-`OF.`+| h.P% ", +" 6,zXD&A:y++;A;N*<=)Xh@}#N,x>G#+&/-T,(*D+p;u-2#@>.:`-L>v:' ' ' vX@@,-. a&S,n#s>z@P*z@r:r:E.r:h&r:M+h&M+H H M+M+6;6;6;6;H M+r:Y=~:h@Y:`OL;(*m%;ob@[o==l$O-5&.;[,0Ot+]>7+m%m%l;P&o=o=o=o=o=o=L>;oP#;o;o - -;oP#P# -),),4O4O|$|$4O`-|$`-r r ),' m%m%KoF-5&u@Q@q>L;kOX+z@y%mOmOy%6;6;6;6;6;3A#x>x>W*W*b@p:V+v&v&U V+V+e;e;BO9+6 6 ;O_=_={.T,@@~%c+Z$L;L;F-U%M$+;L;w-G@m%v:L>),7+6@@>t$z%p;V@#o;O>+s.{:{:{X]@U%}O>:M$_#&:_#. f+-.U=8# ", +" ^%pO>&!+j j }&WX]>N,A*<=K=!=;Os./-_=C>j:p;u-d=vXl.`-;oG@I+l;I+*&p:6X^>)XL-n#s>z@z@z@E.E.z@E.h&r:r:h&M+M+H H H H 6;6;6;M+M+M+s>Q;9%F-_oy%s.P*}#_#l<#=T@#=#=T@#=O-t+9%m%m%m%' G@L>;oL>L>L>;oP#;oP# -P#P#P#), - -),4O|$|$|$|$|$|$`-r r r *&L>l;m%I+,,}&#=&:!+b [o+$L-E.6;y%y%mO6;y%9=8-rX% h@s>r:8-u., [%);[%[%$+$+$+, Q$Q$$OH,H,A#ooA#oox>oob@b@b@(.p:V+U V+U e;e;BOBOBO9<5$5$Co,,,,y=y=kXkXkX{:s.<@';+&';>+>+)$C>j:Cos>+,A*% L;}O[oZ$Z$`+!+tO1;>+v:m%+X;o -7+@>2#Koz%D+C>G#)$>+7*{:{:; #<,.,.s@H%_#7&qo_o/:x v@P% ", +" v N 1-A;-%+;Q@A=hOZ$h@Y:)X$+>+s.6 ;OG#j:W el.r ),L>I+l;I+),<@c#_#Y:0.n#s>P*z@z@z@z@E.z@r:r:h&h&r:H H H H H 6;6;H H H M+z@H$+,A*]@N,U%M$j b*^o~Ob-N=l$~OC:O-A%6;m%m%m%I+;o;oL>;oP#P#;oP#P# - -), - -),4O4O|$4O|$`-`-r r r r r 7+7+7+v:m%m%v:iX(&A%+;. }#)X.#Y=h&6;6;y%9=@@n#[ot>~%Y=P*_:u.4*_:_:@@@@);););, [%[%, $+, Q$$O$OH,!=A#ooA#x>W*b@(.(.p:v&V+U e;U U e;d%d%w-w-5$CoCo,,y=:#y={:{:s.<@<@';';_=p;7*H ; 3-c#% Z$Z$Z$Z$F-F-F-]@},&-L-F&v:m%G@ -`-r @>d=t$W j:C>T,)$>+7*:#7* O}#`+6Xq>*.},j ==A=/:h,soP% ", +" v%C*]+M-},j ==_ob U%h@Z$c#mOkX>+>+;OG#{.F&z%KovXu**&),L>v:l;m%+X{.~:A:b .# Os>s>P*E.z@E.E.z@z@r:h&M+h&M+H H H H 6;6;6;H M+h&r:{XQ;+$h@L;,-H%j +;Q@,X^>&-7&N=+.O-A:d%m%m%l;v:;o;o;oP#;o;o -P# -),),),4O|$4O4O4O|$`-`-r r `-r 7+7+*&7+*&r ' m%m%G@0.w@p#_#A=Y:L.;*n#M+%,u.H 9%K=go~% Oy%iX4*iX$#_:_:4*@@4*@@););$+$+$+T-T-T-Q$$O$O$O!=!=!=!=!=A#x>W*W*b@p:v&V+U v&V+e;e;d%BO9$u*l;I+G@ -|$.:u*0,ZOW F&#oT,6 s.7*:#5$H$]>%*<=a#!+-%+;,XWX=@(;so ", +" 8#O@j.a#&-j _#. f+`+t>t>[oQ;d%;O+&;O#o(*F&p;e<0,u*7+|$P#o=l;I+v:Koy%>:z,1;S,{Xs>P*P*P*z@E.z@E.r:r:h&h&h&M+H H H H y%y%H M+H h&E.z@S,kO}#q>#<]>,-]>io>:M$Q@C.b-O-L;l.m%m%l;+X;o;oP#P# -P# -),),),4O4O4O4O|$|$`-`-r r r *&r 7+7+7+*&.:*&|$I+m%m%0,%*M.C.Q@N*,.c+; 3$%,$#9=iXiXiX4*_:@@u.u.u.[%);[%, $+T-Q$$OQ$!=Q$H,!=H,A#x>oox>W*b@p:p:p:v&V+V+V+U d%d%BO9Koz%W {.#oT,/-s.kX{:v&~:%*L;,-z,,X+;-%*.s@)-O:X@ ", +" P%g noN*qo+;tOA;F.Y:c#A*`+L.A#G#';;OG#C>j:D+e<0,u*.:r -+X' l;' *&x>%*,XK=L-Y=s>P*s>P*E.z@E.z@r:h&r:h&h&r:M+H H H 6;6;H H y%comO+,t>=<3-1;~:; rX=$>$8-%,co9=$#$#$#_:iXiX@@4*u.););[%, [%$+, T-Q$H,$O!=!=!=A#ooooW*b@W*W*b@(.p:v&V+e;e;d%5$';/-{:9=H 0.A*Z$c#t>t>)X)Xc#c#K=N,Z$Z$[oZ$Z$`+!+},h@s.v:m%+X;o4O7+l.2#KoZOW V@_=;O>+';:#kX4*V }OL;-;. ==y+_#`OF./$|XD: ", +" p>-@6&,Xj j !+WXioK=L.]>% $OG#<@)$G#C>{.W u-0,vX.:`- -G@v:m%l;4O<@h@-%6XQ;n#P*z@z@P*E.E.E.z@r:h&h&r:h&h&H H H H H mO>$3S,3$h&S,~:h@<=A=&-b-l$[%m%m%m%P&P#P# - - -),),4O),|$`-|$4O`-r |$`-7+7+r 7+.:*&*&*&*&*&*&6@6@G@m%m%+Xh&(&M;y+H%r&-;,.)X% N,Q;z@Y=n#n#{X{X{XP*P*P*E.h&E.E.h&r:h&H 6;y%6;6;mOmOmO3<3<>$co>$%,9=9=9=$#9=iX_:4*4*4*u.[%);[%$+[%$+, , Q$T-$O!=(.5$U iX6;z@a&F-t>A*t>A*A*A*t>t>)Xc#K=[o[o[o[o[o[o<=tO==H$F&' l;G@P#4O7+6@d=t$ZOF&(*_=;O';7*{:7*H$,.}OL;>:`Oqo&:_#. f+,.M@K. ", +" gON@fo*.+;y+,Xz,3%Z$L.]@F-$+)$';6 ;OG#(*W e OH {:>+,,9$O@@8-Y=9%`+q>},~Oz,';m%m%l;G@),P#),4O4O),),|$|$|$`-r `-`-r 7+r 7+7+.:7+.:.:.:.:*&6@6@l.l.I+m%m%;oh@g*b-b*qo. . s@Z$+$5=.#5=rX~:rX~:~:~: @0. @Q;Q;0.~%~%~%X+X+; L-L-L-H$ OH$ O OS,n#n#n#s>s>s>P*P*P*E.z@r:r:M+6;H 6;y%6;6;y%6;mOco$#4*3h@t>)Xc#K=N,[oZ$[o[o% -;7&M-iX6@m%P&o=),`-*&@>2#t$p;j:G#_=/-+&{::#7*rX#<`+%*N*A;tOy+^>A=,%#Xd !% ", +" .&i.i#. },j ^>_of+,.A*F-N,r:Co>+>+;O_=(*D+z%u-d=@>7+|$ -o=' l;I+d=>$s@M$L.; s>s>s>z@z@E.E.E.E.r:h&h&r:r:6;y%h&Y=n#n#X+z@v&#o;O+&<@kXkX5$d%v&!=_:z@+$`+WX-%+.h@l.m%m%I+;o),),4O4O|$4O4O`-`-|$`-r r r r 7+7+7+7+.:.:*&l.l.l.l.6@6@6@r l;m%m%vXN*,>~Ol$TX==>:]>`+[oK=K=)Xc#c#t>t>t>t>t>A*A*h@h@L.0:0:L.a&0:a&a&V V 1;9%1;9%9%9%kO=<=<3-+$3-+$go+$+$go+,5=rX.#.#rXrX~:rX;*Q;Q;rX+$+$[ob #h@0:h@0:L.h@h@t>t>t>)Xc#c#K=N,[oN,N,`+A=j ]>U P#m%P&;o),7+6@vXd=u-p;j:#o;O/-s.{:y=w-.#<=,.ioa#}&tO-%A:q>x%I.x-.& ", +" v%p%xOz,qo-%},`OF.6XA*0:F-1;!=T,+&;OT,#oF&z%u-0,@>*&r ),o=l;' P&*&b@% A:F-~%{XP*P*s>P*E.z@z@z@E.r:h&M+h&E.z@P*L- @, G##o;O)$6 ';+&7*kXkX,,BOx>_:z@kO6X_oj +.y%l;m%m%I+ -4O|$`-|$4O|$`-`-`-r `-`-7+7+7+7+*&.:*&.:.:l.l.6@6@6@6@6@u*P#l;m%P&W*y+@:(&l$7&^>H%a#F.f+b 3%hOhO#<}#,-}#,-,-}#io}#io<=ioioio<=]>]>]>6X]@]@6X]@6XY:Y:Y:%*`+`+`+`+,.,.`+,.L;,.L;U%}O% }OU%U%% }O,.U%b `OA;A=-;}#`+Z$c#h@L.L.0:L.A*A*A*A*t>c#)XK=N,[oN,N,F-]@}&qo3-6 +Xm%P&;o4O7+l.2#t$ZOp;j:_=)$6 <@y=kX);9%}O,.-;M$Q@j },A;F.&.&oN# ", +" P%3&c;F.==+;j !+q>ioK=L.6XL;);G#';/-;OC>j:D+eP*E.E.z@z@E.h&E.r:z@~%;*9=s.C>_=T,/-/-6 ';+&+&<@{:{:y=BOb@_:S,L.,-H%b*TXd%m%m%m%+Xr |$|$|$|$|$|$r r r r 7+7+7+*&*&.:l.l.l.l.l.6@6@6@6@@>u*@>6@P&m%m%o=Q;#=0OC:~ON=b*j qo,X!+A;A;A;H%A;A;H%H%H%H%`O`O`O`O. . . . . . . . M$M-M-M$M$M$_o_oM$_oz,z,A=A=A=A=z,z,a#a#A=a#a#WXWXa#A=a#. },y+},^>,XH%A=f+<=}OK=t>0:h@0:0:A*h@h@t>A*)X)XK=N,Z$Z$N,F-#. r&i@L@H. ", +" P%g>j*P=*.+;7&A:A=hOF-h@6XL;4*)$<@/-T,G#V@F&p;t$vXu**&|$;ov:m%l;o=W h&z,q>0:H$s>s>s>P*z@r:r:z@z@h& OrXY=5${.)$T,T,)$;O)$6 6 6 +&+&+&<@7*y=e;oo9=X+c#-;Q@s,WXT,m%m%l;o=r `-|$`-`-r r r r 7+7+*&.:*&.:.:l.l.l.l.6@6@6@@>u*u*@>u*@>l.m%m%m%P#Y:JX~##=C:~Ol$N=b*u@&:y+y+7&y+7&7&y+y+y+j y+j j j +;j +;j j +;+;+;-%+;+;-%+;+;-%-%-%tO-%-%tOtOtOtOtOtO},tOtOtOtO&-_#},b*s,N=TXb*b*C.+;^>H%N*,-L;Z$t>h@0:h@0:L.h@h@A*t>)Xc#c#c#K=K=[o,.s@&:>:!=7+m%l;o=),7+*&@>d=t$z%{.#o_=)$+&kX:#7*3-]>}OY:N**.-%7&==_o-;)Or.!% ", +" G$r*'*`O-%j Q@M$-;}Oh@}O[oH y=>+>+/-_=(*j:z%Kod=u**&4O -o=I+l;v:@>A#% A;% Q;n#{Xz@P*P*P*E.Y=5=~%Q$6 T,;O_=;OT,;O;O;O)$/-6 >++&';+&';7*y=BO$OmO~:}ON*&-lm%m%I+),`-`-r |$`-r r r 7+.:.:*&.:.:l.l.l.6@6@6@6@u*u*u*@>@>vXvXu*7+m%m%m%2#!+Qo[,O-~##=(&E+E++.A%A%A%A%~O~O~O~O~Ol+;,Xz,hO,.[ot>h@0:0:0:L.A*A*A*A*A*c#K=c#c#N,Z$]@*.&-a&s.G@m%+X;o),r u*0,t$u-W {.G#;O>++&{:y=,,go6XL;ioa#}&+;y+A:WX0%m:;=^% ", +" v >Z-a#&-j &-A;F.Y:A*h@Z$3-!=/-+&/-;O#o{.p;e<0,vX6@r ),;ov:' ' |$+&rX_#hO+$n#P*P*P*n#X+Q;9=6 >+_=V@(*_=G#G#T,;O/-)$)$6 6 6 +&';';<@{:,,e;T-r:+$Y:_oj (&_:m%m%m%v:|$r r r 7+7+7+7+*&.:*&.:l..:.:6@6@6@6@u*6@u*u*@>vXvX@>vXd=u*;om%m%l;j:3-hOL;L;L;U%% F-Z$Z$Z$F-Z$Z$F-F-F-[oZ$Z$Z$Z$[oZ$[o[oZ$[o[o[oZ$N,[oN,[o[o[o[o[o[oN,[o[o[o[o[o[o[oN,[o[oN,N,[oF-F-Z$F-F-F-Z$F-Z$t>hOC.b-&:^>_o#<,.[oh@L.0:a&0:0:h@A*h@t>)Xc#c#K=K=K=F-hO+;`O6;z%I+l;G@P#4O.:6@2#t$e+<@:#kXH,9%L;,.-;M-^>-%tO!+N*C G;|- ", +" $ )@w=y#==+;j !+a#}#N,0:`+%*iXT,s.>+;O#o{.W eZOcoA=_ot>L-s>~%Q;y%V+';/-p;F&V@C>C>G##o_=;OT,T,;O;O)$6 6 >++&';<@7*,,V+);n#a&#<*.&:TXs.m%m%m%G@7+r 7+7+7+*&*&.:*&*&l..:l.6@6@6@6@u*u*@>u*@>@>@>2#2#2#vXKo@>v:m%m%m%L>@>Ko2#d=d=2#d=2#vXvXvXvXvXvXvX2#vXvX2#d=2#d=2#d=d=2#2#2#d=2#d=vXvXvXvXvXvXvX2#vXvXvXvXvXvXvXvX2#vXvXvX2#2#vXd=d=d=vX2#2#2#2#vX4OS,7&M;b*^>A=#<,.K=h@0:0:a&a&0:L.h@h@t>A*A*t>)Xc#K=%*A=7&]>V+`-m%I+L> -r .:@>d=t$z%F&(*_=;O+&7*:#,,{X[oF-,.>:*._#y+_#`O>:SOa-K& ", +" 4,1=y$A;tO7&==M$-;U%A*%*6X%,)$s./-;OG#V@D+ZOu-0,u*.:`-P#o=' l;v:.:e;N,,X]>goY=Q$:#<@F&KoD+{.{.V@(*C>#oG#_=_=T,;O;O)$/-6 6 /->+';s.{:5$W*$#; )Xf+Q@TXa#W m%m%l;P#.:r *&.:7+*&.:l.l..:6@6@l.6@u*6@6@u*@>@>@>vX2#2#2#d=d=2#0,u*l;m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%L.+.A%&:Q@WXio% c#L.0:0:L.0:0:0:L.h@h@A*t>)XK=K=N,io,X,X.#T,+Xm%I+;o),r 6@2#t$eM-&-j &-H%F.Y:t>F-}Os>9+)$T,#oF&z%u-0,@>6@`-),;oP&m%I+),G#Y=*.]@mOBO{.@>e+>+';<@kXBO!=8- @% WX},l$go*&m%m%v:|$.:*&*&.:l.*&.:l.l.6@l.u*u*6@6@@>vX@>vX2#vXvX2#0,2#d=d=0,d=`-m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%l;m%m%m%m%m%m%m%m%m%m%l;l;l;l;l;l;l;m%l;l;m%m%m%m%m%m%m%m%m%l;l;l;l;l;m%m%' m%+Xs@5ol$j *.>:6XF-t>L.a&L.0:0:a&0:0:h@h@t>t>t>)Xc#F-f+y+q>@@0,' m%+XP#|$*&u*0,u-ZOW {.G#;O>+s.:#,,w-a&Y:,.#W+k.Y# ", +" ^%P:%;a#==+;-%}&WX]>K=h@}Oc#4*6 s.6 ;O_={.p;eKo, hOU%, D+ZOF&F&D+D+j:{.j:(*(*#o#o#o#oG#)$;O;O)$;O)$6 >+6 ';<@{:e;Q$M++$6XM-j A%b@I+m%m%G@*&*&.:*&.:l..:l..:6@6@6@6@@>@>u*@>@>@>vX2#vXvX2#d=d=d=0,t$0,7+o=o=o=L>o=o=o=o=o=L>L>L>o=o=o=L>o=L>L>L>L>L>L>;oL>;oL>L>L>P#;oL>;oL>;o;o;o;oP#;o - - - - - - -P# -),P# - -P# - - - - -),),),4O -v:m%m%m%BO^>T@N=},`Ob %*N,h@L.a&a&a&a&a&0:L.A*t>A*A*t>)Xc#,.M-7&F-:#4Om%P&;o -r l.@>d=e ", +" $ &%^*f+}&-%y+Q@_o-;% 0:,.ioH +&s.+&;O#oV@j:ZOKoKo@>.:`-P#G@I+m%I+*&7*=#oG#_=T,;O;O)$)$/-6 6 ';<@y=v&u.s>a&,-!+u@TXp;m%m%l;L>u*.:.:l.l.6@6@6@6@6@6@u*u*u*@>vX@>@>vX2#2#d=d=d=d=Ko0,0,Ko0,vXvXvXvXd=2#2#d=2#2#2#d=0,Kod=0,Ko0,0,t$u-t$t$t$ez,,-L;)Xh@a&c+V c+0:a&0:0:h@A*A*A*A*t>N,#<&-A;n#D+v:m%+XP#4O7+l.2#0,u-W {.C>T,6 ';{:y=w-V L;F-]@N*!+-%y+,X_o-;do&;T+ ", +" N#Do>O. &-&:qoH%r&`+)X[oL;L-V+<@+&/-;O(*{.D+eI+' l;;oz%%,A=U%Q$#oW D+j:D+D+{.{.j:(*C>(*C>G##oG#T,T,;O)$;O;O/->+>+7*,,W*9=; )X>:^>7>s@l.m%m%l;),u*l.l.l.u*@>6@6@u*@>@>vXvX@>@>vX2#2#2#d=d=d=d=0,KoKot$t$t$t$u-eC>(*C>G#G#_=_=_=G#G#T,T,T,;O;O)$;O;O/-/-/-/-6 6 6 >+>+>+>++&+&+&';';:#G#o=m%m%m%6XT@~OC.,Xq><=% )XL.c+c+V a&0:a&a&0:0:h@A*t>)Xc#U%_oC.<=V+*&' I+o= -4Or vX0,t$e+s.kX,,v&K=6X`+#<=K=0:N,Z$cos.+&6 )$G#j:D+z%Kod=@>*&|$ -o=' m%+X.:5$)X3%; {:F&D+D+{.F&F&{.{.(*C>(*C>#o#oT,;O;O;O)$;O;O/-+&+&{:9rX4Om%m%v:*&6@l.6@6@6@u*@>@>@>@>vX@>2#2#2#2#d=d=d=d=0,KoKoKoKoKot$t$u-eC>#o#oC>C>#oG#G#_=_=_=_=G#T,T,;O;O;O;O)$)$/-/-)$/-/-/->+';z%v:m%m%),H%O-^o+;A;>:6XZ$h@c+c+a&V V c+a&a&0:L.t>t>A*A*)X,-qo==rXV@v:m%P&P#),r l.2#0,u-z%F&C>_=)$+&{::#9i+x-I ", +" P%wOX$b }&tOy+Q@z,3%% L.% #F&p;e<0,vX6@`- -P#v:m%I+;oF&y%a#}OooC>F&F&j:D+F&{.{.{.(*(*(*C>G#G#_=T,T,T,;O;O)$>+';kXBO$O6;3-]@`O&:s,v&v:m%m%L>l.u*u*6@u*@>@>vX@>vX@>vX2#d=d=d=d=d=d=0,0,0,Kot$t$t$u-u-u-u-ZOeC>C>C>G##oG#G#G#_=_=_=_=T,T,;O)$;O;O)$/-)$/-/-6 /-6 >+>+s./-@>' m%m%e;tO#=p#&-M--;`+K=0:V V V c+V c+0:a&0:L.0:A*A*t>Z$a#&:,-x>l.m%' o=P#4O*&u*d=t$ZOD+{.G#T,/-<@kXkXV+A*L;[o]>WX}&-%y+==M$r&0%$$8# ", +" 3OA-ro. &-7&qoH%F.%*A*K=,.~%p:7*s.6 ;O#oV@D+ZOt$2#u*.:4O;oG@l;l;v:*&d%,.3%s>';F&D+D+j:D+F&j:{.{.(*(*C>#oG#_=T,_=_=)$;O;O+&';{:e;$+z@V ,-!+TXu@l.m%m%' ),@>u*u*@>@>vXvXvXvX2#2#2#d=0,0,0,0,Ko0,Ko0,KoKoKot$e(*(*C>G##o#o#oG#G##o_=_=_=T,;OT,T,T,;O;O)$)$/-/-/-6 6 6 6 6 >+>++&kXT,4Om%m%m%~%^o+.7>qoA=ioU%)Xa&1;1;1;1;1;c+a&0:a&0:L.A*A*)X6X,X&-+$#oo=l;+XL>P#r l.@>Kou-z%j:{.G#)$+&<@kXy=b@[oY:,.hO_o,X+;j !+q>X&^XQ,s# ", +" P%};a*WX^>j +;}&WX]>N,a&N,% 6;<@+&6 )$G#(*j:ZOt$d=u*.:`-),L>P&m%P&o={.{XN*Z$v&(*D+F&{.W D+{.{.j:(*C>(*C>G#_=_=T,T,;O;O/-';7*:#v&iX Ot>r&qob*F.P#m%m%I+r 2#u*@>vXvXvX2#2#2#2#2#d=0,0,KoKoKoKoKot$t$t$e(*(*C>#o#oG#G#G#G#G#_=T,_=T,T,T,)$;O;O)$)$)$)$/-/-6 6 6 6 >+>+>+>++&+&y=C>G@m%m%I+ioT@l$y+A:N*]@F-A*c+1;9%1;1;c+V c+0:a&a&h@A*h@K=s@7&-;T-6@l;l;G@;o4O*&u*2#Kou-W V@(*T,>+<@<@kXe;z@N,% 6Xr&`O_#j &-`O>:l,B;e%P% ", +" e%s*0%*.},7&==M$-;L;h@Z$#{.W t$KovX6@*&4OL>G@l;' +Xl.v&]>r&M++&D+F&j:F&F&j:j:{.V@(*(*G#_=_=G#_=T,T,;O)$>+7*Cox>co0.F-a#y+u@~:o=m%m%v:.:2#vXvX@>2#2#2#d=0,0,d=0,KoKo0,Kou-KoKou-e(*C>C>#o#oC>G#_=#o#oT,_=_=_=T,;OT,;O)$)$)$;O/-)$)$/-6 6 6 >++&>+>+>+';+&';s.{:p;P&m%m%vX}&O-N=-%A;r&`+N,0:1;1;9%1;1;V V a&L.0:0:0:L.A*`+,XtO=Kot$ZOj:V@#o;O>+<@y={:p:K=,.F-}#a#*.-%y+,Xz,-;*OU+P% ", +" 47&},H%s@]>K=N,6X+,!=7*';6 ;OG#V@F&ZOu-0,u**&r P#G@I+m%P&;o(*L-F.N,p:(*F&j:{.j:F&j:j:{.(*(*#oG#G#G#T,;OT,T,/-6 7*w-H,6;go6X. b*u@CoP&m%m%P#@>2#vXvXvX2#d=2#d=0,Ko0,0,Kot$Kot$u-t$u-u-e(*V@C>C>#o#oG#_=G##o_=_=G#T,;OT,T,T,)$;O;O/-/-)$/-/-/-6 /-6 >+>+>+';+&+&+&s.s.';{:';vX' m%l;b@C.T@TX&-M$hOU%t>c+9%9%9%1;1;1;1;1;c+0:0:L.)X% s@y+b [%l.m%l;G@;o),7+6@2#t$u-W j:(*_=)$>+7*y=:#Q$}OY:`+-;M$Q@j -%A;N*:oM&5% ", +" P%k@R&s@A:+;y+Q@A=,-% h@N,`+s>7*+&>+;OT,(*F&W eG#_=_=T,T,T,/->+:#e;, s>V ,-}&p#tO -l;m%l;r d=2#@>2#d=2#d=0,0,0,0,t$t$Kou-u-t$u-u-ZOe#o(*(*#oG#G#G#_=_=G#G#T,_=T,)$;O)$;O)$)$/-)$/-6 6 /-/->+6 >++&';+&';';s.';';<@s.y=)$4Om%m%o=+,A%+.C.Q@A=}#F-L.V 9%kO9%9%1;1;1;1;c+h@t>)X)X% a#^>+$_=+Xm%P&L> -|$7+@>Kou-ZOD+V@G#T,6 ';kX7*V+X+F-,.]>s@*.tOy+^>M-r&S.-:6Xh@c##<.#BO';s./-T,#oV@F&z%e.:`-P#;ov:l;' L>V@ ON*%*H,_=F&{.V@F&{.V@j:V@(*V@(*G#_=G#_=T,;O/-+&y=v&4*Y=A*>:},^of+l;m%m%' .:t$0,d=d=d=0,KoKoKoKoKot$t$t$t$u-eC>#o#oC>#oG#_=T,_=_=T,_=T,;OT,)$)$;O)$;O/-/-/-6 6 6 >+6 >+>+';s.+&s.';';s.<@s.s.<@CoV@+Xm%m%*&f+5ol$+;!+N*]@N,0:1;1;kOkOkO1;1;V 0:L.L.c+.#H$.#r&io);.:m%' G@;o|$7+l.2#t$e/ ", +" R$9;yXa#Q@y++;}&a#}#[ot>U%[oH kX>++&;OT,#oF&W u-KovXl.*&4O;oG@l;m%' .:9Q;d%(*j:{.j:j:j:{.V@(*V@(*G##o#o_=_=)$>+';:#W*%,; Y:==b-s,X+I+m%l;G@u*Ko0,0,Ko0,KoKoKot$Kou-t$t$u-u-u-e(*(*C>G##o#oG#G#G#G#G#T,_=_=T,;O;O)$;O)$)$)$/-/-/-)$6 +&+&6 >++&>++&';s.s.s.<@s.s.<@7*7*7*y=z%I+m%m%{.},[,TX&-. f+`+)Xc+9%9%=Q;(*P&m%v:o= -7+l.vX0,t$z%D+(*_=T,/-+&7*7*d%P*L;Y:Y:>:`Oqo7&&-M-r&]>e@$. ", +" A DOs@*.tOj qoM$-;%*t>c#3%+$w-6 ';/-)$G#j:F&e$+&{.j:F&D+j:j:j:V@(*(*#oG##oG#_=)$>+>+{:, rXY:H%b*#=C.kXl;m%m%),2#Kod=0,KoKo0,Kot$t$t$u-e(*C>C>C>#o#oG#_=G#_=T,_=_=T,;OT,T,)$/-)$)$6 /-6 /-6 >+6 6 +&';+&>++&';';';';<@s.s.<@7*<@7*7*kXs.vXl;m%' T-s,#=b*qoz,,-}OA*1;kOkO=<=<9%c+c++,coBOs.j:)$Y=z,`+b@),m%' o=;o4O7+6@2#0,e+s.y=+&[%[oL;U%,-WX}&+;+;Q@z,f+J.w:D% ", +" >*Y+`oFo^>j tOA;N*}#N,t>%*t>%,{:+&>+)$;O#o{.W eG#j:j:j:F&j:j:{.C>(*#o_=_=G##o{.(*$Oh&go,-}&+;TX#=_#|$m%m%l;*&d=t$Ko0,KoKot$t$u-u-e+>++&+&+&';';+&+&s.s.s.s.<@<@s.7*7*7*7*7*Co)$|$m%m%|$=<+.M;C.Q@q>]>Z$L.9%=<3-V A*kOS,T-BOV@*&u-)$iXL;a#M+ZO' m%P&L> -r .:u*d=KoZOD+{.#oT,6 +&7*:#,,h&6X6X%*r&. ^>y+},*.s@}#n>;; ", +" l-B=z+`@,X-%j ,X_ob L;L.K=}#+,Co/-s./-)$T,V@j:z%Kod=@>l.`-P#L>v:l;m%G@t$, %*q>P*kX{.{.j:F&j:j:V@C>C>G#(*eH%-%l$(&-;P&m%m%' @>u-t$Kot$u-u-u-e(*(*(*C>#o#o#o#o#oG#_=_=_=G#T,;OT,T,;OT,;O)$)$)$/-6 6 /->+>+>+>+>+';';+&';';s.';';<@s.<@7*7*7*7*{:kXkX{::#:#kX9<(*+Xm%m%Ko>:T>l$j *.>:%*K=V 1;0:0:rX9=A#{:u-vXZOW (*v&=T,)$+&s.:#>+[%L.,.U%}#WX*.},-%^>M$l 2@P$`: ", +" K.D=hOM$qo7&},`ON*io)X)X6Xh@@@{:';+&)$;OC>j:W ZOKo2#6@*&4O -G@v:l;P&),;OQ;A=L;);T,j:{.{.j:V@{.{.D+vXF&,,H,s>a&U%L;Y:}#q>}&y++.s,Q;P&m%m%o=0,ZOu-u-eC>C>#oG#G##o#o#o_=_=_=T,_=_=T,T,T,;O)$)$)$6 >+>+>+>+>+>+>++&+&';';s.';';s.s.<@<@<@<@7*7*{:kX{:kXkXkX:#:#,,y=y=,,Co,,9),r *&@>0,u-z%F&(*_=;O6 s.7*y=7*H Y:Y:`+f+M-,Xj tO!+N*hO*:eO3. ", +" !%P@N.t=,Xj j Q@_ob L;L.K=6X+,y=/-s./-T,_=j:W p;t$0,6@l.|$|$L>G@' l;v:@>p:c#M$5=e;V@j:(*V@p;d=e<6 Co_:rXt>% F-% U%Y:-;_o==&:T>j y=l;m%m%G@|$7+r 7+r `-r 7+7+7+7+7+7+7+.:.:l.l..:l.l.l..:l.l.l.l.l.6@6@6@6@u*@>u*u*u*@>u*@>vX@>@>@>@>vX@>2#2#2#2#2#2#2#2#2#2#KoKoKoKoKoKoKo0,0,KoKoKot$KoKoKot$t$t$t$t$ZOu-u-e:Y:9%s>);s.t$t$t$u-ZOz%ZOp;T,Q$N,a#; T,G@m%P&L> -`-7+6@2#t$ZOW F&#oT,;O+&s.kX+&T-kOL;,.}#WX*.&-y+qoM-l ,&E&4: ", +" D%F%s@M-_#7&},A;q>}#t>t>]>t>Q$<@7*>+T,_=(*F&W ZOKo@>@>*&r -L>P&m%l;P#F&8-#{:F&t$2#(*{:b@z@9%Z$F-Z$Z$Z$F-,.<=r&`O&-b*0OA;`-m%m%m%l;l;l;' l;l;l;l;l;l;l;l;l;l;l;' ' ' ' ' l;l;l;' l;l;' l;l;l;' ' l;' ' ' ' ' ' ' l;' ' ' ' l;' l;' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' I+I+' ' I+I+' ' I+I+' I+I+' I+' I+' l;m%m%r 3-(&+.C.^>N*}Ogo[%_=D+ZO0,0,ehO4;moD% ", +" 8#>o|,i-Q@+;+;Q@M-r&U%A*Z$U%rXCo+&<@)$T,G#V@j:W u-d=2#l.7+|$),G@I+m%P&7+6 ~%M$<=_:6 _=kX%,.#0:% U%}OZ$[oc#[oF-`+}#s@*.-%l$#=U%+Xm%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%j:N*O-l$&-_o}OE.b@';{.p;p;ZOe2#t$ZOW {.C>_=)$+&s.y=';x>go`+`+ioA=}&},y+^>M-q&D>9${@ ", +" 4,qX]*M-qoy+},*.WX,-K=t><=F-$+';s.>+/-T,G#V@W ZOt$d=@>l.7+r ;o+X' m%+Xl.5$0:A;[o{XY=1;% L;U%}OZ$K=[oZ$N,[o}O%*hOa#A:j E+b*y%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%m%,,&:O-C.WX[oX+u.5$6 _={.p;z%ZOz%W W W /-%,<=s@s>(*;om%v:o= -|$*&u*vXKoe:`+)XF-,.=+)$_=(*D+ZOZOt$2#@>.:7+ -o=v:m%m%o=d=oo% &-}&hO%*L;F-Z$N,Z$[o[oZ$[oZ$U%6Xb _o==&:(&tOL-v&W*x>x>x>x>x>x>x>x>x>x>x>x>x>x>x>x>oox>x>x>x>oox>x>x>x>x>x>x>x>x>x>x>x>A#oooox>x>W*x>W*W*b@W*W*W*b@W*b@W*W*W*W*W*W*b@b@W*W*b@W*b@b@W*W*x>x>x>W*W*x>W*W*W*W*b@W*W*x>W*x>W*x>x>W*x>x>W*b@W*x>x>x>W*W*x>W*W*b@9<+$b-^o!+,-1;E., BO+&#oF&p;z%ZOz%z%W #o$O)XN*kO6 4Ol;' L>P#|$7+l.vX0,u-z%D+j:C>T,6 ';{:y=7*T-kO`+%*}#WX*.&-7&^>M->:x%L%H*^% ", +" P%so!o:.M-_#y+},*.a##%*% iXy=<@s./-_=C>V@p;p;ep#}&]@Z$Z$F-Z$[oN,N,Z$F-Z$L;]>b M$==C.b-b-b*u@7>TXTXTXTXTXTXTXTXTXTX7>TXTX7>TX7>TXTXTX7>7>7>7>7>7>TXTXTX7>7>TX7>7>TXN=p#7>u@C.C.u@u@C.C.C.u@C.u@u@u@u@b*b*b*b*u@u@u@b*u@u@u@u@u@u@b*b*b*b*b*b*b*b*b*b*b*u@u@u@b*7>7>7>7>7>b*7>7>b*b*b*7>7>7>b*b*7>TX7>b*y+7>p#&-a#`++,6;A#,,/-#o{.D+p;z%z%p;{.V+.#WXL;e;r ' I+P&P#),r l.u*2#t$ZOz%j:C>G#;O6 s.:#,,7*X+Y:,.%*r&. ==j },}&WX!,=:~oso ", +" 8#o<:.F.,X+;7&qoM->:6X)X)X,.N,x>s.';/-T,T,#oF&D+ZOKo0,@>l.|$),L>v:I+m%v:4O)$; Q@&:WX`+F-F-F-F-[oN,[oZ$Z$,.]>f+z,Q@j TX~O=X5o~#5o~#5o~#~#~#~#~#O-5o5o5o5o5o5o5o~#5o5o~#5oT>5o5o5oT>T>T>5o5oT>O-O-5o=X(&(&(&E+E+(&C:(&(&C:(&C:(&C:=XC:C:C:=X=X=X=X=X=X=X=XT@=X=XT@=X=XT@=XT@T@=XT@T@T@T@#=T@T@=X#=#=#=#=#=#=#=T>#=#=#=#=#=#=5o5oT>C:E++.A%N=tO`O3%)XX+%,b@{:;O#oV@D+F&p;W j:kXP*s@-;4*0,I+m%P&G@ -4O*&u*vX0,u-p;D+V@_=T,/->+7*:#<@y%A*L;%*,-a#}&-%7&Q@M$r&#< .H*G$ ", +" P%0$#:b+H%^>j +;}&z,3%% h@L;`+n#e;s.+&6 )$G#{.j:p;u-t$2#u**&4OP#G@v:m%l;+X.:w-h@u@y+-;L;F-Z$[o[oZ$Z$F-U%,.<=-;q>!+tO7&b*TXp#s,N=s,N=s,s,s,s,s,s,N=N=N=N=N=p#N=N=N=p#p#p#p#p#TXTXp#7>7>s,b-^o&:j j -%tOtO-%-%-%-%j +;-%+;+;+;-%j +;+;+;j j y+j j y+y+y+7&7&7&7&7&7&&:&:&:7&&:&:&:C.C.C.C.u@C.C.u@u@u@u@C.C.C.u@b*u@C.7>s,s,&:-%-%tO&-_#^>A:M-b U%goE.[%e;s.)$G#V@j:F&j:F&6 iX<=F.S,(*o=m%I+o=;o4O*&6@@>0,u-ZOF&j:C>T,)$+&s.{:y=9<~:`+,.]@s@H%_#&:&-!+WXJ#y E:k@ ", +" I S#:.r&}&-%y+_#H%F.6Xc#t>`+U%Q$s.<@';/-T,(*V@W z%u-Ko2#6@7+4O;oG@' ' l;G@t$Q$]@p#},,-U%F-Z$Z$[o[oF-}OU%<=#C>C>{.V@/-$+K=N*kOkX),m%m%v: -),r l.2#0,u-z%p;j:C>_=)$6 s.kX{:<@ OZ$% ,.,-_oQ@-%j Q@M$b ,-j@{,!% ", +" KO=,i,H%^>y++;,XA=#<}O0:Z$L;goW*s.<@>+/-G#(*{.W z%u-0,@>.:|$),P#o=I+l;I+P#j:>$b TX^>io}OZ$N,N,[oZ$F-F-`+<=-;q>z,M-A;*.A;!+!+*.*.*.A;*.H%H%A;H%H%H%H%`OH%. . `O. `OM-_o`OQ@Q@z,hOio%*U%U%L;,.,.,.%*%*`+`+`+6XY:]@]@]@<=]@<=]>]>]>ioioioioioio}#,-,-}#}##<#:r&>:r&r&F.s@r&r&F.A=!+_ob #<<=`+`+%*`+,.U%}O% Z$K=0:5=S,3(*/-Q$1;s@t>(..:l;l;+XL>P#4O*&u*0,t$ZOW W V@_=;O/-+&7*{:y=$+c+%*L;]@F.A;},7&_#*.q>B..=5:>* ", +" I ;@O:(#*.tOy+&-A;s@]>[oh@L;]@co:#<@+&/-T,#o{.F&p;u-t$2#6@7+|$),;o+XI+m%' 4OG#M+s@7>,XioF-N,N,[oZ$F-% U%`+]@#:s@s@s@>:F.f+f+f+f+f+f+f+b b 3%hO-;q>_ohO% )X1;;*~:rXrX~:.#5=+,gogo3-+$+$3-3-kOkO9%9%1;1;1;V V c+c+a&a&a&L.L.0:h@A*A*t>t>t>)X)Xc#c#c#K=N,[oN,N,[o[oZ$% % % % Z$Z$Y:b ,-L;F-K=kO3-=<3-gogogo5=~:;*~: @X+Y=h&$#Q$V+Co<@>+;O_=#oC>6 Q$3-N*% , d=' m%v:o= -4O`-l.@>Koe)$/->+s.kX7*,,;*,.,.6X-;_oQ@+;j A:z,3%<=;X{@!% ", +" P%k@3 B&a#A:j j ==M-f+Y:)X[o%*L.$O6 <@+&)$_=V@V@D+ZOZOd=@>l.r `- -o=v:' m%I+|$;Os>WXb*}&}#% [oN,N,Z$}O% }O,.%*]><=<=ioio]>io]><=<=<=]@]@]@6X6XY:Y:%*`+`+,.`+]>,-Y:h@3-X+mO9=>$3P*{Xn#n#Y=Y=S,S, OH$ OL-X+~%X+~%~%Q; @ @;*;*;*~:~:.#rX.#go+,go+,+,gogo3-gogokOc#F-h@V kO0.{X{Xn#n#s>z@E.E.h&H H y%mO3+6 G##o>+Q$gos@%*4*u-P&m%P&L>P#|$7+.:@>d=t$ZOW j:(*G#T,/-';{:<@y=3<[o6X`+# ", +" 8#uXfX<&`Oqoj -%}&a##+;OG#C>{.D+p;t$0,2#l.7+`-P#o=+Xl;m%P&7++&Y=A=u@A;<=F-N,N,Z$F-% Z$F-U%}O,.L;L;L;U%}OU%F-Z$% F-Z$Z$[oN,N,K=N,K=[o,.F-=A#A#A#H,$OQ$Q$T-, , $+[%u.@@@@4*4*iXiXiXiX$#9=coco>$8-3<3s>{Xs>{XY=Y=S, O OS,n#S,5=3-gogo+$Y=co3<3p:d%5$kX';+&>+/-_=+&!=+,F.ioiXp;G@m%P&L>P#|$r .:vXd=0,eF.. ^>y+-%}&a#X&,`. ", +" Z>r*4.s@H%},7&_#H%s@io[oN,}OU%r:kX<@s.>+/-_=#oj:D+p;t$d=vX.:*&`- -o=I+m%m%+X.:s.L-M$C.H%io% Z$F-[oF-[o[oN,Z$Z$[o[o[oN,K=N,t>A*c#t>A*A*A*L.L.h@K=t>9%.#n#oo';<@y=5$Co5$w-BOd%e;U U V+v&v&p:b@W*W*x>A#H,!=!=H,$O$OQ$, , $+[%););@@@@4*_:_:iX$#$#9=%,%,>$3<8-3<3:-;9=j:o=m%I+G@;o4O*&.:u*2#Kot$p;F&(*G#T,6 >+<@:#<@U H$}O%*6Xf+_o,X+;j ==M$r&,=f;NOO> ", +" Zo[+4 z,==+;+;Q@M-f+<=t>Z$%*a&[%s.s.s.>+;O_=(*{.D+e<0,d=u*l.7+4OP#;oP&m%' o=l.s.Q;H%7&H%io% Z$[oZ$[oN,[oK=K=c#)Xt>)XA*A*t>A*h@0:L.L.h@0:L.1;.#X+9=>+(*6 +&+&<@7*{:{:kXy=,,Co5$w-9<9$cocomOr:P*Y=+$.#co4*@@@@u.u.$+T-Q$T-H,!=H,A#W*(.(.p:U d%d%d%9++&kXQ$+$F.F.8-#o),l;l;o=P# -`-.:u*vXKou-W D+V@_=;O;O+&s.{:7*,,H }Oio`+,-WXA;},j },*.s@T#t;g;G$ ", +" P%)&y:'+`Oqo7&tO}&A=b %*0:[oio5=v&s.<@';)$;O#oV@D+ZOt$Ko0,u**&`- -P#o=I+m%I+o=6@{:Q;. 7&A;]>}OZ$[o[oK=K=c#c#c#c#)Xt>t>A*t>h@L.h@A*L.a&3-rXmO';(*_=)$/-6 >++&';<@7*{:{:{::#y=,,Cow-5$9$#$#$#$#4*4*u.);$+, T-$OH,H,!=x>b@W*b@p:U e;e;9<5$5$Co:#{:{:<@';<@Cou.3-N*s@8-G#|$l;l;+XL> -|$*&l.@>0,t$ey++;}&a#hO_&/X1*v ", +" 5V@W ZOet>)XA*A*h@L.L.0:3-{X(.F&C>_=T,)$)$)$)$6 >+>++&s.<@7*{:kXkXy=,,CoCoCow-w-BOe;d%V+V+V+V+v&p:(.b@W*x>A#A#!=!=H,$OQ$Q$T-$+[%[%);u.4*4*_:_:iX9=%,co0.L.0.3<8->$8-%,$#iX_:_:);$+[%[%T-$OH,oooox>W*(.V+v&V+e;9 -4O7+u*@>d=t$ZOp;{.C>G#;O/-+&7*:#+&Q$=<% ,.]@f+M-Q@y+j ==M->:]>;-I$8# ", +" cO5.B.M$^>j j Q@M$f+<=c#N,U%)XP*s.{:+&6 )$_=C>{.W z%ZOKo@>u*l.`-4OP#G@' m%I+L>6@';{XN*C.}&hO}ON,K=N,N,)X)XK=)XA*A*A*A*t>a&rXT-#oV@C>C>V@C>G#T,;O;O)$;O/-/->++&';<@7*{:kX{:kXy=,,,,Co5$9<9x>ooA#A#H,H,$OQ$, $+$+[%$+[%u.@@@@@@H$kO+${XH 6;3<>$>$>$9=_:_:_:4*);$+$+, Q$Q$H,!=oox>b@p:v&v&U d%BOBOw-,,5$,,y=:#,,U coL.WXioco(*4Ol;' I+L> -4O*&u*2#0,u-z%p;D+V@#o;O/-6 <@:#{:d%P*F-]@%*#+)$_=(*F&W z%u-d=2#u*`-4O),P#+X' m%' L>*&>+P*f+u@A:hO,.F-[oK=c#c#)X)Xt>t>[o3-M+kXC>V@D+j:F&(*C>_=_=G#_=T,;O;O)$6 6 +&+&';<@7*{:kX:#y=,,,,Co5$w-9x>A#H,H,Q$T-Q$T-$+$+$+T-Q$cogo1;L-s>E.H 6;y%mO8-8->$9=9=iX_:4*@@$+[%$+, $O!=A#A#oob@b@(.V+U e;e;d%w-5$5$5$y=w-b@6;K=z,%*$#j: -l;' P&;o),|$r 6@2#d=0,ZOp;D+V@C>T,/-+&s.kXy=<@coF-6XL;]>N**.qoj -%Q@z,3%c%W-8=^% ", +" D.<:2Xn@!+},&:},!+a#-;U%t>Z$6Xgo,,{:<@+&6 T,G#{.D+W e+X' m%' v:|$T,H #F.Y:Z$c#K=[o[oK=1;L-p:>+G#e#oG#T,;OT,)$/-6 6 6 +&';';<@kX{:kX,,,,,,,,,,Cow-BOBOe;e;V+U U v&p:p:b@b@x>ooA#ooA#H,H,$OQ$Q$T-A#_:X+3-;*; ; P*E.r:H 6;6;y%3<>$co>$$#iXiXiX@@);$+[%, Q$$OH,!=A#x>b@p:V+U U d%d%9<9m%l;P&L>),4O|$l.@>2#KoeF.`O==y+j ==. r&]@2@PXh. ", +" _,k><%,6Xy+-%A=]@U%U%c#rX8-{:6 eW*x>x>ooA#!=!=H,A#H,r:5=~:~% @; s>r:E.z@z@h&6;6;6;3<3<>$co9=$#$#_:@@u.);[%$+T-Q$H,!=A#x>b@p:v&V+e;U U e;BO9 -4O|$l.u*2#KoZOz%D+{.#oT,;O>+s.7*:#<@H,3-,.,.6Xf+M-,X+;7&_#*.N*yOb=3$w& ", +" P%q<( S:`Oqoj j ==. s@ioN,A*%*% s>:#';s.+&)$T,G#{.D+p;ZOu-d=vX6@r r |$;oo=v:m%m%I+ -j:, Z$==&:`O`+L-!=kX#ou*2#e(*#oG#_=_=T,;O)$/-6 6 6 >+';<@<@{:kXy=y=,,,,Cow-w-5$BOBOe;e;e;V+U V+v&b@W*b@x>x>oox>W*x>co~%0.~:3-.#S,s>{X{Xs>P*z@r:h&H 6;6;6;mO>$>$9=9=$#4*_:@@);$+$+, $OH,!=A#ooW*(.p:v&U V+V+U U ooM+t>_os@= -|$r .:u*d=Kot$W W j:(*T,)$/-';{::#:#v&L-}O`+]@3%a#!+},y+},!+WXi*R>'O$X ", +" G$O*R=n@A;_#y+tOQ@M->:6Xc#Z$,.Z$H y=';s.6 )$T,#o{.F&D+ZO0,d=vXl..:`-),P#;ov:l;m%I+o=t$d%rXa#A:Y:%,#od=t$F&z%z%eC>_=_=_=T,T,)$/-6 6 6 +&';s.<@7*{:kXy=y=,,,,,,Co5$9z@E.E.h&6;6;6;3<8-8-co9=$#4*4*4*););[%, $O$OQ$!=oox>W*(.p:p:v&V+b@);X+Y:M--;Y=7*6@I+m%P&+X;o;o4Or .:vX2#d=Koz%F&j:C>_=T,)$';s.kXy=V+n#F-Y:Y:,-a#*._#7&tOA:z,hOM ~, XZ>P% ", +" I x-z.l>R#*.&-7&tOA:z,b ,.)X}O`+[o>$,,<@s./-;OT,C>{.{.W e/-6;F-b Z$y%w-C>j:p;W p;ZOp;ZOW D+D+D+j:V@(*C>#oG#T,T,)$)$;O/-6 >++&';<@<@7*{:kXy=:#,,,,,,Co5$9coE. OkOA*5=L-; L-L-L-H$S,n#n#{Xs>z@z@E.M+H H y%8-3<>$>$%,9=iX4*@@u.@@$+, T-T-H,!=A#oox>b@b@p:(.H,y%c+>:A=,.u.V@|$P&l;P&L>;o -4Or .:@>2#Koes@H%_#7&j ,XM$r&<==:v;q% ", +" _,[*5OaXa#!+tOy+},!+a#hO,.L.,.`+A*_:{:7*s.6 ;O;OC>V@F&D+z%u-d=2#@>.:`-4O -;oG@I+' ' +X|$F&Q$N,_o]>L-W*+&(*D+W p;ZOW p;W F&F&{.{.C>(*C>#oG#T,T,;O;O)$6 6 +&+&';<@<@{:{::#,,,,,,Co5$5$w-BOBOe;e;U U U e;V+H,8-r:+,)Xh@~:L- @0.~%X+L-; H$ OY=n#{Xs>P*E.r:r:h&H y%mOmO8->$>$%,_:_:4*@@);[%[%, Q$$OH,H,A#oox>oo!=4*Y=F-_oWX+$e;Koo=' ' +XL>P#4Or *&u*vX0,0,z%W F&V@#o_=)$/-6 7*:#kX9<6;% io}OY:>:. ==y+7&Q@M-F.,-P,{-po ", +" t vOp+M$A:+;j _#*.a#hO}OL.L;Y:1;T-7*7*<@)$)$T,#o{.F&W z%t$2#@>u*l.r 4O -L>+X' m%' +XL>d=w-3-N*3%L.>$9<;O{.z%p;F&D+D+D+F&j:j:{.(*C>C>#oG#_=;O;O/-6 /-6 +&';s.<@7*{:y=:#y=y=y=Co5$w-BO9N,3-~:;*;*;* @0.~%Q;X+L-L-L-S,n#{X{Xs>z@z@E.M+H H y%8->$8-8-%,iXiX_:@@@@@@[%[%T-T-H,A#!=!=H,u.M+3-ioH%r&z@6 *&+Xm%l;I++XL>4Or 6@l.@>2#Kou-z%W {.C>_=T,/->+s.y=kX:#%,Z$,-U%6X>:M$Q@j y+qo`ON*3%*O'oN& ", +" 8#W&U$t,. Q@+;+;Q@H%N*]>}OL.U%<=5=W*';7*>+6 )$T,#oV@j:W z%t$d=vX6@.:*&r ),L>+XP&l;l;P&+X6@+&E.]>A=]@~%, {:C>W W D+j:W j:F&V@V@(*(*#oG#G#_=;O;O;O/-6 6 >+s.<@<@';7*kXy=:#y=,,Co5$5$BO5$Co9d=Kou-z%W {.C>_=_=)$+&s.y=kX{:_:N,io}OY:f+M$}&tOy+&-!+a#b M*D#/%D%P% ", +" C+v$m#q*`O^>y++;==M-r&io% L.U%<= @BO';7*+&/-)$T,#oV@F&p;z%eG@' m%l;G@`-V@Q$kOA=f+Z$z@U ;OF&W D+F&{.j:V@{.C>(*C>G##oG#T,;O;O)$>+6 6 +&';<@<@{:{:kXkXy=y=,,y=:#kXV+x>u.~:Z$t>9%kOkO+$+,go+,5=.#~:~:;*0.Q;Q;X+; ; H$S,Y={X{Xs>E.P*r:M+6;6;6;3<8->$co%,9=iX$#4*u.););[%, , $+$# ON,f+!+r&; e;e -),4O`-.:l.vXd=KoZOp;W j:(*_=;O;O>+7*y=:#+&[%N,<=% Y:-;A=}&&-j tOA:z,-;]@l%xXpo ", +" / /Xm#V#%.&-j tO,XM$>:<=[oh@U%Y:~%Co+&7*+&/-;O_=G#V@D+p;z%e+X' m%m%P&P#Ko,,M+N,`O#G#G#_=;O;O;O;O)$>+>++&+&s.<@{:{:{:y=y=<@s.:#(.(.Y=1;)Xh@c+V 1;1;kO3-3-+$+,.#.#.#rX;* @0.Q;~%X+L-L-H$n#n#{XP*P*P*E.h&6;6;6;3<3<8-co%,coiX_:iX4*u., $+);iXr:9%hO!+A=1;4*)$*&P&m%I+v:G@L>),|$`-7+6@0,KoKoe+;OT,G#(*F&p;ZOu-u-KovXu*l.7+),P#L>G@P&l;' l;+X`-F&b@; ,-_o]@0.Q$<@C>j:F&j:V@(*#o#oC>#o_=T,T,)$;O;O/->+';+&';7*7*7*<@>+7*5$e;u.goL.t>)Xt>L.1;9%V 1;kO=<3-+$5=5=5=rX~:;*;*0.~%~%~%; H$; On#{Xs>s>z@r:r:M+H H y%mO8->$>$9=$#iX$#@@@@4*9=z@+$]@. A;L;H {:vX;o' m%I+G@o=L>P#`-l.l.@>2#Kou-e_=;O/-+&{:kX:#s.$O0:%*,.%*3%a#*.qoj j ==. >:io2=V&6. ", +" z>i&#&-;a#}&tO7&},A:M-F.]>Z$K=L;L;Q;e;s.<@<@>+)$_=C>(*{.W ZOu-t$0,vXu*.:7+`-4O -;o+Xv:m%m%l;+X.:G#_:9%q>z,}O~%[%y=T,V@V@V@C>#o#oG#_=_=;O/-)$;O/-6 6 ';';>+/-6 <@<@A#H$=[oZ$c#A*0:a&a&c+V 1;kOkO3-+$gogo.#~:rX~:;*0.Q;Q;~%; ; L-H$Y=n#{XP*P*P*z@r:h&M+6;mO3<3<8-9=$#9=$#$#3< O9%Y:z,,X#d=0,t$ZOW j:(*#oG#T,/-';7*y={:7*$+A*%*`+Y:3%a#A;qoj j ^>`OF.hO $8;f, ", +" _,X>$=p+a#A:},7&tO,XM$>:]@Z$)XL;%*;*W*{:s.s.';;O_=#oC>{.F&W z%u-KovXu*6@.:r |$),;oG@I+m%m%m%I+P#d={:E.)XN*M$U%;*$#BO6 G#(*C>#oG#_=_=;O/-;O;O/-6 )$T,;O+&s.,,%,~:a&Z$L;U%c#t>A*A*L.0:L.c+V 1;9%kO=<=<3-+$5=.#.#~: @0.0.~%X+~%X+H$S,S,n#s>s>{Xs>z@r:r:H 6;H y%mO>$co8-8-H ~%L.]>_oQ@f+=<[%V@`-P&m%m%l;v:L>P#),`-7+.:6@@>d=t$ZOp;F&j:(*G#T,T,/-';7*:#{::#_:N,6X,.]@3%WX*.&-j +;^>`OF.-;aO].;= ", +" 6< %>>_o}&tOy+},A:M$r&]@[o)XL;<=goT-7*';s.>+6 _=#oC>V@j:D+ZOu-Ko0,2#@>l.7+`-4O -o=P&I+l;m%l;v: -KoCoP*N,r&M-]@5=8-(.<@)$_=G#_=_=;O)$;O)$;OT,T,)$>+$+P*.#c#Y:%*% N,c#)X)XA*h@h@L.0:c+a&1;1;kO9%=<3-go+,+,.#.#~: @0.Q;Q;~%L-L- OH$Y={X{X{XE.E.E.r:H 6;6;6;mO3d=Kot$ZOW F&V@(*#oT,)$>++&7*{:kX,,9=N,]>L;6X3%a#A;&-7&j Q@`ON*3%[=|@4%G$ ", +" $@!$-:_o!+tO7&+;A:M->:<=[o)XU%io3-@@{:';s.';/-;O_=#o(*j:p;ZOel.*&r |$P#;o;o+XI+l;m%m%P&),0,,,E.K=-;M$#$H$c+U%<=`+F-[o[o[oK=c#)Xt>h@A*L.a&0:a&V 1;9%9%=<+$go+,.#5=.#~: @ @;* @~%; ; L- OS,n#{Xs>z@E.E.r:r:M+M+E.{XQ;9%F->:}&H%]@kOu._=.:G@m%m%l;P&o=;oP#4O`-*&l.u*vXd=t$eT,;O/->+s.<@{:kXBOmO[oio% Y:-;a#!+&-7&y+==`ON*-;: *#Got.P% ", +" P.`$b$s@_o}&tO&:tO,XM->:]@Z$t>}O]>a&co:#';<@+&>+)$T,G#(*{.D+p;ZOu-Kod=vX6@.:r |$), -P#o=v:' I+m%m%v:`-t$7*mOh@}#a#WX% @3A*A*L.0:0:c+V 1;9%kO=<3-+$+,5=5=.#~:;* @ @0.Q;~%; H$S,H$Y=n#{X{Xs>z@P*s>Y=~%+$K=}#M-==M$Y:.#!=#o6@o=m%m%' ' v:;o),),|$7+*&u*vX2#d=Koe*P% ", +" ^%soR@j-N*_o!+},y+tOA:M-F.}#U%A*}O]>c#M+y=>+{:s.6 T,;OT,#oV@F&W z%e+XP&l;m%' ' +X|$u-6 u.=<]>r&a#>:)X OiX4*mO Oh@`+6X}OZ$[oZ$Z$[oF-F-[oZ$[oN,K=c#A*h@h@L.L.a&a&c+1;9%9%kO+$go+$go.#rXrX;* @0.Q;~%H$H$; H$ OY=Y=Y=S,H$ @goA*6X_oQ@A;r&}O Op:#ou*o=l;l;m%P&+XG@L> -|$7+.:.:6@vXd=0,Koe3%S.V=I@H: ", +" !%|o +=+s@z,}&},y+-%A:. s@#+6 /-T,#o(*{.F&W z%t$0,0,2#u*6@7+r 4O4O;oG@G@P&I+' ' l;+X4Od=_=v&E.)X-;F.s@N*z,A;z,f+]>,.}OF-Z$[oZ$[oZ$Z$F-[oZ$[oN,c#c#t>h@h@h@L.0:c+c+V 9%kO3-+$+$+$5=5=rX;*~:;*Q;X+; ; L-L-; ~% @.#1;N,ioA=}&H%a#}#=2#0,u-e+';7*+&6 ;O_=G#(*{.j:W z%t$t$t$d=@>6@.:r `-4O),P#L>+X+X' l;m%' I+L>.:W <@T-; }OA===-%&:&:&-H%s@#t>A*h@a&0:a&V 1;1;9%=<+$+$go.#~:~: @ @0. @~:~:rX+$c+F-]>WX}&Q@*.M--;a&M+p:;OKo),P&m%l;l;I++Xo=L> -),|$`-*&6@vX2#0,0,t$ZOW j:{.(*#o_=)$/-+&s.{:y=7*';_:+$L;Y:%*<=>:z,!+&-j +;^>. s@3%(XF#&>cO ", +" !%;;q$B&F._oA:},7&+;==. WX-;%*N,F-L;Y:5=, {:)$7*s./-)$;O_=#o(*{.D+p;ZOu-t$d=vXu*u*.:7+r 4O -;oo=v:v:I+m%m%l;' +X -vX#o9<@@ Ot>f+!+^>tO7&&:_#}&_oF.hO]@`+,.% F-[o[oN,c#c#K=t>A*A*A*0:a&a&c+V 9%=<3-3-+$go3-+$kOa&)X% 6X-;z,H%,X*.H%M$,-V s>T-7*p;7+o=' l;m%' P&+XG@;o -),|$`-*&6@u*vX0,t$t$eG#T,)$6 ';<@kX{:+&BO%,K=%*`+Y:ios@M$}&},7&-%==. s@3%*OE== T. ", +" !%^,Y...s@A=A:},y+y+qoH%a#f+6XF-N,U%Y:0:6;BO6 <@<@+&6 )$T,G##oV@j:W z%ZOt$d=d=@>u*u*6@7+`-|$ -;oo=o=+XI+l;' m%m%' v:4O2#C>kXA#mOQ;N,F.==},},j y++;},^>}&M$q>>:-;,-]>6X`+}O% F-[o[o[oN,[oZ$F-% % L;6Xio-;s@_o*.,XQ@A:*.,X`O3%A*H$$#(.>+D+6@L>' l;m%l;' P&G@G@o=),|$`-`-*&6@@>d=d=0,t$eG#_=)$/->+s.7*:#7*+&$Os>Z$,.%*6X,-N*. Q@-%7&-%Q@M->:3%9O<>Q.<% ", +" s#nXj,b;>:WX!+},7&7&&-!+M$>:ioU%c#U%,.}O @b@s.';<@s.>+/-;OT,_=C>j:D+p;ZOu-u-0,vX2#vXu*.:r |$`- -P#P#G@P&v:I+l;m%m%m%' +X;o`-t$C>y=H,y%Q;c+Y:s@A;&--%-%+;+;+;tOtO},_#==A:!+H%`O`O. A;A;!+A:Q@Q@Q@,X,X,XA:^>==A;N*,.={:{.@>4Ov:I+' m%m%l;P&P&G@;oP#P#4O4Or *&.:u*vXvX0,u-u-ZOp;p;F&V@C>G#;O/-6 >++&{:kXs.:#iXkOF-6X,.6X-;a#A;==j 7&tOA:M$r&#<9O*>EXq% ", +" v Z>{%J+r&s@A;qoy+7&},Q@M-q>hO`+N,F-`+]@kOu.Co';<@<@';>+/-;O_=C>V@j:W p;z%ZOu-Ko2#2#vXl.*&7+7+|$4O), -o=o=v:P&P&m%m%m%m%l;I+v:;o`-0,C>s.e;!=$#s>Q;9%U%3%q>M-*.Q@qotO-%j +;-%-%},tO},^>==,X!+z,b 6Xt>+,L-r:iXH,9<+&F&vX),G@P&' m%m%m%' P&v:+Xo=;o -),4O`-*&7+.:u*2#2#0,Kot$ZOp;W F&{.C>G#T,;O/-+&';<@7*<@7*BOmO% }O<=`+]>>:z,}&_#j 7&&-A:z,b ,-9O(+0-q-8# ", +" O>)=b; o)+A;==-%7&+;==H%WX-;6X}ON,L;<=)XE.U s.';<@s.>+)$;OT,G##oV@F&F&p;p;ZOt$Kod=2#vX6@l..:r `-`-),;oL>L>o=+Xv:P&l;l;m%m%m%' I++Xo= -4O*&t$V@>+5$W*Q$);_:co6;z@ O~%~:~:~: @X+n#E.mO%,iX);!=V+:#6 D+vX7+4OL>G@I+' m%m%m%l;P&P&P&G@o=;o;oP#|$`-|$*&l.6@vXvX2#d=KoeG#G#T,)$/->+s.kX{:s.y=A# O}OY:%*]@}#F.. }&},y++;^>!+z,3%]>9O[ ]#uo8# ", +" @%:O5*.XN*. }&},y+j _#A;A=F.ioU%K=}O%*U%+$H,:#kX7*<@+&6 )$T,_=C>C>V@j:F&p;z%ZOu-Ko0,KovX@>u*.:*&7+|$),), -;oL>G@+X+X+XI+l;' m%m%m%m%' l;P&L>L>P#),|$`-r *&u*d=u-z%F&{.{.F&p;u-d=6@7+r 7+),;o;oo=I+' l;m%m%m%m%' I+v:P&+Xo=o=;oP#P#4O),`-*&.:6@@>@>2#d=Kot$u-z%p;W j:(*C>C>G#T,;O6 ';s.7*:#:#:#kX3bO)#v ", +" T+$&7.R*>:_o*.qoy+7&},A:M-N*-;6XZ$Z$%*%*h@; V+7*{:';+&+&>+)$;O_=C>V@V@F&F&p;z%ZOt$t$0,d=2#vX@>.:*&7+r `-|$4O), -;oL>G@P&P&v:P&' l;' l;m%m%m%m%m%m%l;l;I+I+P&P&P&v:+X+XP&+XP&I+I+' l;l;m%m%m%m%l;' l;m%' P&P&v:+Xo=;oP# - -4O|$r 7+*&.:l.6@2#2#d=0,Kou-z%z%W j:V@V@(*T,T,T,/->++&<@{:kXy=<@Q$Q;[o`+%*`+}#F._o*.^>j 7&},}&M$s@hO%*d-S g# ", +" n;H-++F>F.a#H%^>+;7&-%,X. A=r&<=L;N,[o6XF-=<3<,,<@{:<@';6 /-/-T,G##o(*V@{.F&p;ZOz%u-u-0,d=d=vXu*6@6@l.7+r `-4O), - -;oL>;oo=G@+X+XP&I+I+I+I+' ' l;' ' ' ' ' ' ' l;l;l;l;' ' I+' ' ' I+P&I+I++XG@+X+XG@L>L>P#P# -4O4O`-7+r .:.:6@u*@>2#d=d=Kou-u-ZOZOW F&V@V@C>#o_=/-/-/-+&s.7*kX:#7*CoY=V ,.Y:L;6XhON*M-!+_#y+j &-!+z,>:#go$O';<@kXs.>+>+/-/-;OG#G#(*V@j:D+D+W z%p;eG@v:+X+X+Xv:+Xv:v:P&P&P&v:P&v:G@G@G@v:v:G@G@L>;oL>L>P#;oP#P#),),),7+`-r 7+.:6@l.vXu*0,0,Kot$u-ZOeG#_=)$/-/->++&{:,,y=';<@6; @[o%*,.`+iof+a#*.Q@-%j -%==*.A=f+,-0%M>r=5# ", +" M#gXc-o&R*N*M-!+_#y+&:-%Q@`Oa#r&io,.% N,,.Y:9%H$(.;O+&kX<@';>+/-;O;O_=#o#o{.F&F&D+W z%e6@.:7+*&.:r `-`-|$4O),4O -;oP#;oP#;oP#L>o=L>o=o=o=o=o=L>o=L>L>L>P#P#;oP#P#),),4O|$|$`-|$`-7+*&.:*&l.u*@>vX2#d=d=t$Kou-u-ZOz%W D+F&V@#o#oC>T,)$)$6 >+s.7*:#y=>++&T-~%t>`+L;,.<=3%N*M$!+&-7&j },A:. N*#<<=/>e>Y$U@ ", +" P%D%g:q *,g=z,A;^>-%7&y+qo}&M-s@hO6X}O% K=6X}O+,y%U ;O+&kX7*+&6 /-)$T,_=#o#o(*{.F&W D+W ZOeu*u*l.l.*&r r *&r `-`-`-`-`-),), - -),),),4O),4O),4O),),),),4O`-`-`-`-`-7+7+7+7+*&.:.:u*@>vXvX2#d=d=0,t$Kou-eG#G#_=;O)$/->+';{::#:#6 )$x>{XkO`+,.`+Y:iof+z,A;==+;7&+;&-}&M$>:#<]+|;e&%@K& ", +" ` ,o=$w$7,s@M$}&&-j 7&-%Q@H%z,F.hO%*Z$}O[o6XF- @3(*V@V@j:W W p;eu*@>u*l..:.:*&.:*&r r r r r r r 7+r r 7+r 7+r r r 7+.:.:.:l.l.l.u*6@6@vXvX@>2#0,0,0,t$u-u-u-ZOZOW W F&V@{.V@C>#o_=T,;O6 6 >+';7*kXkX/-s.Q$E.go,.%*%*%*<=b WXM-}&},y+j tO,X`Oa#b #<1XP>+@p@ ", +" cX],rO7<+>@+z,H%==-%y++;},A:. WX>:hO,.F-U%}O,.U% @%,!=<@6 <@7*s.';>+)$T,T,_=G##o(*(*V@{.F&W W ZOZOe6@6@6@6@6@u*6@6@u*6@u*6@u*6@6@6@6@6@u*@>vX2#2#@>d=0,2#Kot$Kot$e+s.s.{:y=7*6 y=, H kO%*`+,.6X<=-;s@M$*.^>+;j +;qo!+M$>:hO]@o,@O:-hX ", +" P%2,Q&KX6:%=N*_o*.^>-%y+-%qo}&. a#r&}#,.[oU%L;U%%*+,%,H,,,+&s.7*7*+&+&6 )$)$T,T,_=G#C>C>V@j:D+D+W W p;z%ZOZOu-u-u-t$Kot$t$0,d=d=2#d=d=d=0,d=d=d=0,d=d=d=d=d=d=d=d=vX2#d=d=t$t$KoKoKou-u-eG##o_=T,T,)$6 +&>+s.7*7*kX<@<@d%T-M+L.Y:,.%*6X]@#8# ", +" s# #@-I,E* $a#`O,X&-y+y+tOqo}&. WXf+io,.Z$}O,.Z$%*a&>$x>d%s.<@7*7*<@';+&>+/-)$;O_=_=G#C>C>(*(*{.j:j:j:W W W z%ZOz%ZOe_=_=;O)$;O6 >+';';s.{:{:{:<@kXU T-n#Z$Y:,.<=Y:Y:3%N*_oA;Q@},y+y+-%^>!+z,r&#<6XaX(=h#F=8# ", +" 8#_XpXE#1%:*>:z,H%Q@&-7&7&},==!+M-a#r&}#%*[oZ$6XF-,.)XM+W*V+y=7*<@s.7*s.';>+6 6 /-)$;O_=_=_=#o#oC>V@V@{.F&j:F&D+W W W p;z%W W z%ZOz%ZOZOZOz%z%ZOZOZOz%z%z%p;p;p;W D+W p;F&{.j:F&{.V@V@C>#o#o#o_=T,T,;O;O/-6 >++&';s.7*7*kX:#:#d%(.Q$L-U%`+,.]@`+]@hO>:z,A;,X_#-%7&-%qo!+M-N*3%eoD>X%2-z:Mo ", +" P%8#4$ ,A@e=[$6>_o*.==tO7&j &-^>A:. WXf+}#%*Z$[o%*}OF-U%c+_:9+/-/-;O)$;OT,_=G##oG#(*(*(*C>V@j:j:{.{.W D+F&j:D+D+D+D+D+D+F&F&D+D+F&F&F&D+F&D+j:{.j:V@(*(*(*(*C>#oG##o_=T,T,)$)$T,/-6 +&+&';<@7*kXy=:#w-e;(.r:c#,.F-`+]@%*]@-;F.z,A;Q@qo-%&:+;qoA:. a#r&T#b,FOJ*}%K@8# ", +" v ^:C,# 9O>>WX. }&==},&:y+},==}&. q>r&#<<=U%[oU%Y:L;c#N,=<@@w-,,y=:#kX{:s.s.s.+&>+6 6 6 ;O)$;OT,T,T,_=G##oG#C>(*(*(*(*(*(*(*(*(*V@(*V@(*C>(*C>#oC>(*C>(*(*#oG#G#G#_=T,T,;O;O;O)$/-6 6 >++&+&s.7*kXkXkXy=:#CoCov&mOh@[oN,,.6X,.,.io-;N*z,H%,Xqo+;&:+;_#A:H%_oF.,-[#5,f;!OM:v% ", +" P%$:G=r><tO7&y+},,X*.. a#F.hO]@`+F-N,U%Y:% h@A*rX, 7*7*:#kX{:<@<@s.';s.+&>+>+6 /-6 )$)$T,)$;OT,_=_=_=_=T,T,_=G##oG#G#G#G##oG#G#_=;O;O_=_=_=_=;O)$T,T,)$6 )$)$>+>+>+';<@s.7*7*{:kX:#:#kX:#w-8-c+c#t>L;Y:}OL;6X,-r&q>_oA;A:^>+;&:j },Q@*.z,F.3%X;N:#,G>Q>X G$ ", +" $ i=#-':Po7:[$q&A=`OA:qo-%y+y+},==}&H%A=F.b }#Y:`+}O[oL;Y:Z$c+V goH (.{:s.7*{:7*<@<@<@<@';s.+&>+>+>+/-)$)$/-)$/-/-;O;OT,T,T,T,T,;OT,T,T,T,T,T,;O)$/-)$/-/-/-6 >+6 >+s.';s.7*7*<@<@kX{:{:<@7*,,H,n#1;c+L.}O<=,.,.]@]>3%>:q>M-*.Q@&-j 7&+;&-==*.M-q>b {**oI:C&F$TOcX$ ", +" 8#2o1>lo_OX:=@A.z,`O}&qo-%y+7&-%qo==A;M$a#>:3%]>Y:,.% [o`+<=,.t>=<+$+,E.oo:#s.<@7*7*7*<@';<@7*s.s.';<@<@s.>+';6 6 >++&+&+&+&>++&>++&+&+&+&>+>++&<@<@s.s.<@<@<@<@<@{:7*kXkX7*<@7*Co[%S,3-+,3-K=6X]@L;U%]@]>,-f+N*_o. }&==tOy+y+-%},Q@H%M-q>-;J#{=t&5+nOy.uo ", +" G$5#Z,6#.+aX=@A.q>M$!+^>tO-%j 7&tO^>}&`Oz,N*>:3%]@%*%*,.F-Z$`+]>]@c#5=rX;*z@$+e;:#<@<@<@7*<@7*{:7*kX<@s.<@s.';<@<@<@<@<@<@<@s.<@<@<@s.<@s.s.{:{:7*7*{:{:{:<@<@7*{:kXV+4*S,rX~:kO[o,-]@`+,.`+]@<=,-b s@a#. *.Q@_#y+7&+;-%_#,XA;A=s@b o-Q-m+L+ :E@y,$ ", +" T+l&j#r%h;sO-+y#WXM$H%A:&-+;y+j +;},==*.H%M$WX>:hO<=`+,.L;L;}OZ$U%}#,-F-3-~%H$ OL-P*>$$OU ,,{:7*s.{:7*7*<@{:<@{:7*7*{:7*<@kX{:7*<@7*7*<@<@{:{:{:Cov&[%y%n#H$S,H$0.kOL;#<]>,.% `+%*]@}#,-f+s@z,. *.A:_#-%j j j tO==!+M-A=F.yOX;{+*,c-%j j j +;tO==}&A;M$A=N*f+#<<=`+L;`+Y:L;% U%%*<=}##<6XK=+,; n#s>P*P*E.M+y%8-%,4*[%$OooW*x>A#T-);$#>$8-y%h&E.z@s>s>{X~%kO[o<=#<}#]>Y:L;L;6X6X<=<=]>#<>:WXM$H%!+,X_#-%y+y+j -%_#,X`O_oq>r&yO}#g+ %+*> dM$A;A:_#-%y++;+;+;&-^>,X!+`Oz,WXs@r&hOio]@`+,.%*%*%*%*,.% U%6X]>}#hO#]>}#3%>:q>_oM-*.}&Q@_#-%+;+;j j },Q@*.. A=F.3%#a#M-*.,Xqo-%7&j +;-%},_#==}&A;`O_oWXq>s@f+#<}#<=%*,.U%L;`+%*`+`+`+Y:`+`+}OF-F-% L;`+,.`+`+U%F-% }OU%,.%*Y:`+`+Y:Y:Y:6X%*6Xio#H-_-5; ", +" MXk,#+;:f&R>b#hO>:N*z,. !+A:^>tOy+7&y+-%tO},&-^>A:*.`O. _oz,q>F.F.r&3%}#}#]@]@Y:%*L;`+,.U%U%L;}OL;%*`+,.L;U%U%,.,.%*6XY:]@io}#}#3%r&s@q>a#z,M$. A;}&==_#},},tO+;y+7&-%_#,X}&H%_oWXr&3%,-]@7:E ]:~.P:ROi= ", +" 2>k=~X$,X%l,0%b F.N*_oM-A;A:==&-tOy+7&7&y+-%},_#qoQ@A:*.A;`OM-_oA=WXN*s@s@s@>:r&f+b f+-;-;3%3%hO3%-;f+b r&r&>:s@s@q>a#a#z,M$M-H%*.!+Q@^>_#&--%+;y+7&7&+;&-qo,X!+`Oz,q>>:b 3%*%M*DXj%VOs+5-6% ", +" 6+'.xX/,y;(X_#tO-%y+7&y+y++;tO&-_#qo^>==,X}&!+A;A;H%`OM-M-M$_oM$_o_oM-M$M$M-M-`OH%H%H%*.!+A:Q@==^>qo_#},-%j j &:7&j tO},^>Q@A:A;. A=N*f+-;#.,u ", +" l-@*1o5ObOGOD>.qo_#},-%j 7&&:&:7&y++;-%-%},},},_#_#_#&-_#qoqoqo_#_#_#qo_#_#},},},-%-%+;j j 7&&:7&j +;-%&-_#^>Q@!+H%M$a#q>F.r&3%}#W=(X/>t*0@2;).;+8# ", +" P%v%x*QX8>B%H=a;_%s=p&i*-;r&s@WXz,M-`OA;!+Q@^>_#_#_#},-%-%+;j y+7&y+7&&:&:7&C.7&7&7&7&&:&:7&&:7&7&j j +;tOtO&-&-_#^>Q@A:!+A;M-z,a#s@>:f+hO}#0%1:s@WXWXz,_o`OH%A;}&A:A:==^>^>_#qo_#&-_#&-_#_#_#_#&-_#_#&-qo^>^>,X!+!+!+A;. M$A=a#N*>:F.f+3%,-}#<#3>i@n,H;G;0#W#%%H#$-^#!% ", +" ` x&Y@:;L&.$U:*N*A=a#A=_o_o_oM-M-M-M-M-M$M-M-M$_oM$A=a#A=WXN*q>F.F.>:f+hO#<3%B.`;{==O0;y*1:P>!>#@-$}=HOi= ", +" ^#U-~;d+=oP$]$~-v#[>G,W@#*iohOyO-;hOhO3%-;-;-;b b -;b f+b f+f+b b b -;hOhOhO#<#<3%#<#:XV%%*C=k*]$]Xo>=%],x#x#!%v ", +" !%2o3=D:0o9>B+f zo)o-*O$7#]@Y:Y:Y:%*%*`+`+`+Y:<=io]>6X%*`+%*%*%*Y:%*%*Y:`%C;u;:@g$x:T$b.4:5#3=Z;Z&Y 8# ", +" G$f>=>k#D:m>'%H>q;{&f->@C#k+~>m;;,^Oz-R:h%XXi l#R;Bo{&+<4=k%|#X@{Od.r@8# ", +" $ $ $ $ 2&;.Q::+m,Z#d:j;x$G=Y.3;>Xn:@-0&8@8=2<>;%:C$f<} l*I $ $ $ P% ", +" ", +" ", +" " +}; diff --git a/share/pixmaps/bitcoin32.png b/share/pixmaps/bitcoin32.png new file mode 100644 index 0000000..be7c119 Binary files /dev/null and b/share/pixmaps/bitcoin32.png differ diff --git a/share/pixmaps/bitcoin32.xpm b/share/pixmaps/bitcoin32.xpm new file mode 100644 index 0000000..25503f6 --- /dev/null +++ b/share/pixmaps/bitcoin32.xpm @@ -0,0 +1,348 @@ +/* XPM */ +static char *graphic[] = { +/* width height num_colors chars_per_pixel */ +"32 32 309 2", +/* colors */ +" c None", +". c #444444", +"X c #474747", +"o c #666666", +"O c #888888", +"+ c None", +"@ c #AAAAAA", +"# c None", +"$ c #CCCCCC", +"% c #474747", +"& c #EEEEEE", +"* c #535353", +"= c None", +"- c #757575", +"; c #979797", +": c #B9B9B9", +"> c #DBDBDB", +", c #FDFDFD", +"< c #626262", +"1 c #848484", +"2 c #A6A6A6", +"3 c #C8C8C8", +"4 c #EAEAEA", +"5 c #686868", +"6 c #737373", +"7 c #717171", +"8 c #939393", +"9 c #B5B5B5", +"0 c #D7D7D7", +"q c #F9F9F9", +"w c #3C3C3C", +"e c #5E5E5E", +"r c #808080", +"t c #4E4E4E", +"y c None", +"u c #A2A2A2", +"i c None", +"p c #C4C4C4", +"a c #E6E6E6", +"s c #4B4B4B", +"d c None", +"f c None", +"g c #6D6D6D", +"h c #8F8F8F", +"j c #B1B1B1", +"k c #D3D3D3", +"l c #F5F5F5", +"z c None", +"x c None", +"c c #434343", +"v c None", +"b c #5A5A5A", +"n c None", +"m c #7C7C7C", +"M c None", +"N c #9E9E9E", +"B c #C0C0C0", +"V c None", +"C c #E2E2E2", +"Z c None", +"A c #5C5C5C", +"S c #474747", +"D c None", +"F c #696969", +"G c #8B8B8B", +"H c None", +"J c #ADADAD", +"K c #CFCFCF", +"L c #4A4A4A", +"P c #F1F1F1", +"I c None", +"U c #565656", +"Y c #787878", +"T c #9A9A9A", +"R c #BCBCBC", +"E c #3F3F3F", +"W c #DEDEDE", +"Q c #6F6F6F", +"! c None", +"~ c #434343", +"^ c #656565", +"/ c None", +"( c #878787", +") c #A9A9A9", +"_ c #CBCBCB", +"` c #EDEDED", +"' c None", +"] c #525252", +"[ c None", +"{ c None", +"} c #747474", +"| c #6B6B6B", +" . c #969696", +".. c #B8B8B8", +"X. c None", +"o. c #DADADA", +"O. c #515151", +"+. c None", +"@. c #FCFCFC", +"#. c None", +"$. c #3F3F3F", +"%. c #464646", +"&. c #616161", +"*. c #838383", +"=. c #A5A5A5", +"-. c #C7C7C7", +";. c #3B3B3B", +":. c None", +">. c #E9E9E9", +",. c #464646", +"<. c None", +"1. c #3B3B3B", +"2. c None", +"3. c #4E4E4E", +"4. c #707070", +"5. c #929292", +"6. c #676767", +"7. c #B4B4B4", +"8. c #424242", +"9. c #D6D6D6", +"0. c #F8F8F8", +"q. c #3B3B3B", +"w. c None", +"e. c None", +"r. c #5D5D5D", +"t. c None", +"y. c #7F7F7F", +"u. c None", +"i. c #A1A1A1", +"p. c #C3C3C3", +"a. c #E5E5E5", +"s. c None", +"d. c None", +"f. c None", +"g. c #373737", +"h. c None", +"j. c #4D4D4D", +"k. c #4A4A4A", +"l. c #6C6C6C", +"z. c #8E8E8E", +"x. c #424242", +"c. c None", +"v. c #B0B0B0", +"b. c #4D4D4D", +"n. c #D2D2D2", +"m. c None", +"M. c #F4F4F4", +"N. c #424242", +"B. c None", +"V. c #4D4D4D", +"C. c None", +"Z. c #7B7B7B", +"A. c #9D9D9D", +"S. c None", +"D. c #BFBFBF", +"F. c #E1E1E1", +"G. c None", +"H. c #636363", +"J. c None", +"K. c #424242", +"L. c #464646", +"P. c None", +"I. c None", +"U. c #686868", +"Y. c #8A8A8A", +"T. c #424242", +"R. c None", +"E. c #ACACAC", +"W. c #CECECE", +"Q. c None", +"!. c #424242", +"~. c #F0F0F0", +"^. c #555555", +"/. c #777777", +"(. c None", +"). c #999999", +"_. c None", +"`. c #BBBBBB", +"'. c #DDDDDD", +"]. c None", +"[. c #FFFFFF", +"{. c #424242", +"}. c None", +"|. c #868686", +" X c #A8A8A8", +".X c #CACACA", +"XX c #3E3E3E", +"oX c #ECECEC", +"OX c #454545", +"+X c #515151", +"@X c #737373", +"#X c #959595", +"$X c #B7B7B7", +"%X c #D9D9D9", +"&X c #454545", +"*X c #FBFBFB", +"=X c None", +"-X c None", +";X c #454545", +":X c #606060", +">X c #A4A4A4", +",X c #3A3A3A", +"o c #484848", +",o c #6A6A6A", +"X&o+XE uX+. ", +" m.;X* _X3 C P 0.*X[X[X@.0.& roXoh 6X8.v ", +" { ^XPX=oa.eXM.%X..u 8 8XwokX0 6oxXC VXg XX/ ", +" d ~ G 9.~.eX.6owX|Xg.s. ", +" I.,.- wXl ro; @ 2o=.mXm _ Xo-.K r =.XoyXg g a.P 4o,o%.KX ", +" bX+XB ~.oo*oeo9 2 GX@XkX$ p -.W (Xy.Y g 7Xr.F 4 4 YXS 1X ", +" <.DX8 pX$oGX..2o.o XJ gX_ !X$ k W.] Z.0o7 g r N j xXW O MXu. ", +" 'XLXX@ ).} W W.#opXGXU y.&o_X..9.a D.n.JXB >ow. ", +"h.b.y.rol 8X8XwoN yXQXZ.8 a ]XTX~.*.7 ) =ok 9.]XCX0 2 P o.(Xt d.", +"IX7o=.& o.&o#XUX.oE.7.7XYXooC oo>.L.F =.HXlX#o-.=.m :XW 2X).T._.", +"M s eoeX: O v.eoB eo@ogX'.>.~X6opX=.: /.3 @o5.PX,o,o9o..6o9X. Z ", +"Q.9o!X$o..3 k .XXop .} $oJXM.0.[.[.).l.z.Z./.PX7XF gXGXl B dX+ ", +"Q tXW.M...wXwX.X7.Y +Xv.[.q , [.kX< tX|XWXr 0oPX( kXW.: ~.-.* 6.", +"qo%oHXM.: $ 7..o} &ooX[.RX, RXJX* &o_XoJ.", +"! >o`.$o`.r _X_XFXEX,oRXxX~.*X_X&.8X8 yX) J @oeo$XUXEX: P j {.# ", +"e.!.i.oXTXy.i.`X.X( QX*XoX>.$o|X%o4.(X@X} /.PX%oF F }Xrooo .AXy ", +"= j.mX'.6oGX0 0 #oy.HXJXa -oa.oo-oroW TXo.]XF.tXo ^ O $o0 l.O.}.", +" 2.3.iX6o4oeoC iXY & 4 oopXCXW TX]X9.k 4olX$ dXy.Z.K & `.. f ", +" X.K. r c z ", +" 6 dX`.~X4 F U dX$.k.}Xg g F SX^ :Xb e F mXB @o>.a 7.{.#. ", +" =X&XgXK 6oa 7 (XJ 3 4o!XiXVX@ou UXN J eo$X2 pX4 _ e V.n ", +" o3Xy.wX~.~X@op 0 wXW.YXwo( |.5.A.*o .@ 4 ~X4o@X;.I ", +" G.w y.4o~Xl K @o Xr < EXOo/.&o*.NX4oxXa _ @X3of. ", +" aX,XU.: CX~.6oo.7.*o .T XD.W xX& TX7.e DXcX ", +" i OX>o( D.> 4 xX0.0.eX$oP 2X%X`.r . L 5X ", +" S.[ N.dX4.UX9X=o-.-.B j #Xl.L.x.' C. ", +" (.BXrXK.~ >o/X/X>o~ ZX0X{Xx ", +" zXqXt.)X| w.-X1o " +}; diff --git a/share/pixmaps/bitcoin64.png b/share/pixmaps/bitcoin64.png new file mode 100644 index 0000000..d224bfc Binary files /dev/null and b/share/pixmaps/bitcoin64.png differ diff --git a/share/pixmaps/bitcoin64.xpm b/share/pixmaps/bitcoin64.xpm new file mode 100644 index 0000000..4f6d5a0 --- /dev/null +++ b/share/pixmaps/bitcoin64.xpm @@ -0,0 +1,612 @@ +/* XPM */ +static char *graphic[] = { +/* width height num_colors chars_per_pixel */ +"64 64 541 2", +/* colors */ +" c None", +". c None", +"X c #444444", +"o c None", +"O c #404040", +"+ c #666666", +"@ c #565656", +"# c #474747", +"$ c #888888", +"% c #4A4A4A", +"& c None", +"* c #404040", +"= c #AAAAAA", +"- c #CCCCCC", +"; c None", +": c #EEEEEE", +"> c #474747", +", c #4A4A4A", +"< c None", +"1 c #404040", +"2 c #434343", +"3 c #404040", +"4 c None", +"5 c None", +"6 c None", +"7 c #535353", +"8 c #404040", +"9 c #404040", +"0 c #757575", +"q c #404040", +"w c #979797", +"e c #B9B9B9", +"r c #404040", +"t c None", +"y c #DBDBDB", +"u c #404040", +"i c None", +"p c #404040", +"a c #FDFDFD", +"s c #404040", +"d c #474747", +"f c #4A4A4A", +"g c #404040", +"h c #626262", +"j c #848484", +"k c None", +"l c #515151", +"z c None", +"x c #A6A6A6", +"c c #C8C8C8", +"v c None", +"b c None", +"n c #EAEAEA", +"m c #515151", +"M c None", +"N c #4F4F4F", +"B c #434343", +"V c None", +"C c None", +"Z c #717171", +"A c None", +"S c #939393", +"D c None", +"F c #B5B5B5", +"G c #4A4A4A", +"H c #474747", +"J c #D7D7D7", +"K c #434343", +"L c #525252", +"P c #474747", +"I c #F9F9F9", +"U c None", +"Y c #3C3C3C", +"T c #5E5E5E", +"R c #808080", +"E c #4D4D4D", +"W c #555555", +"Q c None", +"! c #A2A2A2", +"~ c None", +"^ c #525252", +"/ c #C4C4C4", +"( c None", +") c #E6E6E6", +"_ c None", +"` c None", +"' c None", +"] c None", +"[ c #4B4B4B", +"{ c #4D4D4D", +"} c #6D6D6D", +"| c #8F8F8F", +" . c #464646", +".. c None", +"X. c None", +"o. c #B1B1B1", +"O. c None", +"+. c #D3D3D3", +"@. c #F5F5F5", +"#. c None", +"$. c None", +"%. c None", +"&. c None", +"*. c #383838", +"=. c #434343", +"-. c #5A5A5A", +";. c #434343", +":. c #7C7C7C", +">. c None", +",. c None", +"<. c #9E9E9E", +"1. c #464646", +"2. c #515151", +"3. c #4E4E4E", +"4. c #C0C0C0", +"5. c None", +"6. c #E2E2E2", +"7. c #474747", +"8. c #3F3F3F", +"9. c #474747", +"0. c #464646", +"q. c #474747", +"w. c #696969", +"e. c None", +"r. c #4A4A4A", +"t. c None", +"y. c None", +"u. c #464646", +"i. c #8B8B8B", +"p. c None", +"a. c #ADADAD", +"s. c None", +"d. c #CFCFCF", +"f. c #494949", +"g. c #464646", +"h. c #515151", +"j. c #F1F1F1", +"k. c None", +"l. c #434343", +"z. c None", +"x. c #515151", +"c. c None", +"v. c #343434", +"b. c #565656", +"n. c #787878", +"m. c None", +"M. c None", +"N. c #9A9A9A", +"B. c #4A4A4A", +"V. c #434343", +"C. c #BCBCBC", +"Z. c #3F3F3F", +"A. c #DEDEDE", +"S. c #3F3F3F", +"D. c #434343", +"F. c #3F3F3F", +"G. c None", +"H. c #4D4D4D", +"J. c #434343", +"K. c None", +"L. c #3F3F3F", +"P. c None", +"I. c #434343", +"U. c #3F3F3F", +"Y. c #656565", +"T. c None", +"R. c #3F3F3F", +"E. c #878787", +"W. c #424242", +"Q. c None", +"!. c #A9A9A9", +"~. c #464646", +"^. c #CBCBCB", +"/. c #3F3F3F", +"(. c #4D4D4D", +"). c #EDEDED", +"_. c None", +"`. c #464646", +"'. c None", +"]. c #3F3F3F", +"[. c #525252", +"{. c #464646", +"}. c None", +"|. c #747474", +" X c #3F3F3F", +".X c #969696", +"XX c #3F3F3F", +"oX c #4D4D4D", +"OX c #B8B8B8", +"+X c #555555", +"@X c #DADADA", +"#X c #545454", +"$X c #3F3F3F", +"%X c #3F3F3F", +"&X c #FCFCFC", +"*X c None", +"=X c None", +"-X c #464646", +";X c #3F3F3F", +":X c #616161", +">X c #545454", +",X c None", +"o c #DDDDDD", +",o c #424242", +"O c #AFAFAF", +",O c #535353", +"+ c None", +",+ c #4E4E4E", +"<+ c None", +"1+ c #4C4C4C", +"2+ c None", +"3+ c #6E6E6E", +"4+ c None", +"5+ c #909090", +"6+ c None", +"7+ c #B2B2B2", +"8+ c None", +"9+ c #D4D4D4", +"0+ c #F6F6F6", +"q+ c None", +"w+ c #404040", +"e+ c None", +"r+ c #474747", +"t+ c #404040", +"y+ c #444444", +"u+ c #5B5B5B", +"i+ c #444444", +"p+ c #7D7D7D", +"a+ c #4F4F4F", +"s+ c #474747", +"d+ c None", +"f+ c #9F9F9F", +"g+ c #4F4F4F", +"h+ c None", +"j+ c #C1C1C1", +"k+ c #444444", +"l+ c None", +"z+ c #E3E3E3", +"x+ c #474747", +"c+ c #404040", +"v+ c None", +"b+ c #484848", +"n+ c None", +"m+ c #484848", +"M+ c #404040", +"N+ c #6A6A6A", +"B+ c None", +"V+ c #444444", +"C+ c #8C8C8C", +"Z+ c None", +"A+ c #AEAEAE", +"S+ c #525252", +"D+ c None", +"F+ c #D0D0D0", +"G+ c #F2F2F2", +"H+ c #474747", +"J+ c #525252", +"K+ c #353535", +"L+ c #434343", +"P+ c #575757", +"I+ c #797979", +"U+ c #9B9B9B", +"Y+ c #4B4B4B", +"T+ c #BDBDBD", +"R+ c #444444", +"E+ c #DFDFDF", +"W+ c None", +"Q+ c #404040", +"!+ c #444444", +"~+ c None", +"^+ c #404040", +/* pixels */ +" o .loD+4+l+T.t.lXC D+y.>.cX ", +" cXi ,XEoO.{ GO0.~.d Go2XJO# x+=o,+9X_ o+( 9o ", +" AXc.K.SXu.OOO mOD.q.mX1+ToTo1+&OiO1oq L.gX.Ol h+t 4 ", +" >.s.HOr+M+9 )XN 7O:X'XqX|.n.]op+n.hoRo+ @+@o[ J.so*OeXq+UOAX ", +" MXP.RO2 U.uOfo@+} p+| jXa.zoT+QXzXXOQXT+zogOf+-ORXw.-.1+SOF.0.,.@O5 ", +" o >+D L+7o)XP+w.]ow o.XO- +.uX}O@Xy ZOZOy boJ ~oobo9+- T+jXhX0o0XBOgXPoP.oJ F+j+jXI+`o`XR.wXBXAX ", +" Uo1OO+V.7OqX.XT+o~o5XFobOvOp B Q . ", +" Uoz.c+%O0oEOzoO-O*+A+gOroFo|oMOI+1+<.5XHoHo5X5X5X5X!O7+-.DXHooXOQXj+zX/ Ho!O!X&ou+DXWo$+n.0 hoqXRo=O0oYO>Oj.lOz+uX/ -O7Oxo~O'o ", +" AXHO3oP+p+-oJ +Oj.@.eOkoe j+T+'Oa.x Fo!.{X.XUXU+>ozXzX;+5X!OtoF+!XZ fo0 RX8On.bO|.Z Ro} N+YO'XXOKXuoE+GXF 0 mX9OIoMX ", +" B+kO9.N+*+d.E+).0++.w OX;+T+FXWOFoWO= {X!.E.@oT+J Ho5X!O^.jO.oboO5X9+boy +.*+!OKX6X@X^.U+`o%XdOV ", +" 9om.#O7 p+eO}O) KX+Oi.C+N.qO<.f+! *+3XWOroWO| @o*+z+oboy >oA.rO6.).a.q._O.XA+4.c ^.jOd.F+o>oE+!Xz+xXJo: M. ", +"cX[oQ+-.C+to>ouo0+7+RXrX.Xw &oEX3Xa.yX'OyXA+5+b.{X/OrO6.+OxX:+6X).6.P+v.fo} |.*+zX!O^.jO.o- XO{X5+8ORo=O'X@+zo2O) J zXSomX;.4O9o", +"AX#+nOYOEXF+rOXoXo.X$+rXMO! A+OXeOT+-oOXF gO$+UXjOn +O) Jon /O).2OF+u+n.'O!o@+w j+c !O/ yXqOEOn.hoqX3+VXN+@+.Xj.n @XtoS [.1 e.MX", +". , !+w.!.9+6.2O) E.Wo= -o;+5X;+XOQXC.e 'OWOqX} xXn 6X/O).lOXoG+pX).ZO2O +WOu+|oQXe x Wo$+8O#o0 |.Z Ro} =OYOp+6X).>o.of+-.r >X( ", +"fO1.m+qX!ouXxXKX@X`O4.d.F+jO!OHozX4.T+VO'O<.@+DXlOuo).lOj.2O@.(omoa a DOKX$+YOMOMOsO$+]o:.8On.bO|._OqX3+VXpOqXA.XorOF+gO:Xs x.%.", +"<+yo1+|.-o}O) 0+F+*+O`ORo@o[.VX= G+(opX +&Xa DOa moa.(OmXUXVX:.wo$ E.EOOY.KOFO;o", +"fOs+q.3+7+~o+OKXy p+C+S ko.XMOMOE.dXzo) T+4.I +I pX0+@.2OG+KX`ON j w .XrXDX| MOx FXF e -oeOj+/ 5X^..oF+- 'O| rOlOA.d.= @+s }X_.", +"Uo% ,o+ ro+.6.j.:+woE.S `O.Xw w i.Z -O0o*.&o(o(o0+@.2OG+XoXon Ro`o-O`OWorXN.Fo= A+o.yXzoe C.QXXO;+HoQX= -O3+$+6XuoZOjOEXP+q #XHX", +"AXBo8 AoU+d.A.: j.&oR rXS kow MOko$ 0 P+I.4.pXKXG+j.Xo: ).Xo9+foAoO7+yXa.N.So_ON+T &oXoJoboc 5+N zO] 5 ", +"cX.+Z.b.[Xc y n @.F 8O5+S .XEX!.zoT+FXRX+ E+KXj.lO: )./On XoFXK+`Xu+:X0oY.+ pOw.=OVX3+RoZ qXVX=OZ _OqX} w.Ao-oG++O~o4.]oq.)o..9o", +" VotXToI+T+J ) KXuXp+5+*+F / - jO!OF _Oj : Xo: uo/On JoJo2OOXqX|.n.I+I+8O8ORX8ORXRXRX:.RXn.UXKOYOZ Z 3++ Roy lOrO.*X].u+5+toy n @.Hox 9+}O~oGXd.5XU+h tolOJo:+) xXz+6.!XrOrOA.>oZOy @X}OJ uX~o+.GXoZO@Xbo}OuX~o9++.oZOy @Xy xXwomX$ 'O^.>OA+: uoA.F+zo_OiOH d+O0oYOY.dXu+`o@+YO3+R 5+MOMOkoS 5+DXi.sOwoj $+]o8On.:.EODX`O&oWOeO4.f+zXXo/OA.GX4.woN 9 }oD+ ", +" OF+ZO:+G+lO7+N+Z U+T+F+9+ogO! 4.GX+.On.To$X$O O4 ", +" 6+=Xvop b.:.>O^.uXE+:+lOKXn !Ox w j } 0oY.w.} Z bOI+p+hXhXp+8OSof+^./O2Ouo+OZOGX5Xro_O[ $X:Ok.>. ", +" 6OCodo$Xfo_O|o/ GXy 6.6XXoKXlOy e w p+Ro+ Y.Y.'X=O_ORXsOf+T+ZOlOG+).) A.J d.4.N.w.m+$XvXk.^O ", +" 6+& H+gomX0oC+F - 9+y !X:+).G+KXG+n E++.^.;+Ho- 9+rO/OG+2OXo/OxXE+booJ o@XJ +.jO/ !o`OZ 7 I.nOwOe+o+cX ", +" o . WXH.=.Nom+7ORo[X3XOX/ ^.d.GX+.9+~o9+9++.+M X.S+ooi+Oor s g KOKOKOg g s :ol.X+ZXbX4XaO_. + + + + CFBundleIconFile + bitcoin.icns + CFBundlePackageType + APPL + CFBundleGetInfoString + $VERSION, Copyright © 2009-$YEAR The Bitcoin developers + CFBundleShortVersionString + $VERSION + CFBundleVersion + $VERSION + CFBundleSignature + ???? + CFBundleExecutable + CryptoLottoToken-Qt + CFBundleIdentifier + org.CryptoLottoToken.CryptoLottoToken-Qt + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + org.CryptoLottoToken.CryptoLottoTokenPayment + CFBundleURLSchemes + + CryptoLottoToken + + + + NSHighResolutionCapable + + + diff --git a/share/qt/clean_mac_info_plist.py b/share/qt/clean_mac_info_plist.py new file mode 100644 index 0000000..df677f5 --- /dev/null +++ b/share/qt/clean_mac_info_plist.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# Jonas Schnelli, 2013 +# make sure the Bitcoin-Qt.app contains the right plist (including the right version) +# fix made because of serval bugs in Qt mac deployment (https://bugreports.qt-project.org/browse/QTBUG-21267) + +from string import Template +from datetime import date + +bitcoinDir = "./"; + +inFile = bitcoinDir+"/share/qt/Info.plist" +outFile = "Bitcoin-Qt.app/Contents/Info.plist" +version = "unknown"; + +fileForGrabbingVersion = bitcoinDir+"bitcoin-qt.pro" +for line in open(fileForGrabbingVersion): + lineArr = line.replace(" ", "").split("="); + if lineArr[0].startswith("VERSION"): + version = lineArr[1].replace("\n", ""); + +fIn = open(inFile, "r") +fileContent = fIn.read() +s = Template(fileContent) +newFileContent = s.substitute(VERSION=version,YEAR=date.today().year) + +fOut = open(outFile, "w"); +fOut.write(newFileContent); + +print "Info.plist fresh created" \ No newline at end of file diff --git a/share/qt/extract_strings_qt.py b/share/qt/extract_strings_qt.py new file mode 100644 index 0000000..1267b18 --- /dev/null +++ b/share/qt/extract_strings_qt.py @@ -0,0 +1,72 @@ +#!/usr/bin/python +''' +Extract _("...") strings for translation and convert to Qt4 stringdefs so that +they can be picked up by Qt linguist. +''' +from subprocess import Popen, PIPE +import glob +import operator + +OUT_CPP="src/qt/bitcoinstrings.cpp" +EMPTY=['""'] + +def parse_po(text): + """ + Parse 'po' format produced by xgettext. + Return a list of (msgid,msgstr) tuples. + """ + messages = [] + msgid = [] + msgstr = [] + in_msgid = False + in_msgstr = False + + for line in text.split('\n'): + line = line.rstrip('\r') + if line.startswith('msgid '): + if in_msgstr: + messages.append((msgid, msgstr)) + in_msgstr = False + # message start + in_msgid = True + + msgid = [line[6:]] + elif line.startswith('msgstr '): + in_msgid = False + in_msgstr = True + msgstr = [line[7:]] + elif line.startswith('"'): + if in_msgid: + msgid.append(line) + if in_msgstr: + msgstr.append(line) + + if in_msgstr: + messages.append((msgid, msgstr)) + + return messages + +files = glob.glob('src/*.cpp') + glob.glob('src/*.h') + +# xgettext -n --keyword=_ $FILES +child = Popen(['xgettext','--output=-','-n','--keyword=_'] + files, stdout=PIPE) +(out, err) = child.communicate() + +messages = parse_po(out) + +f = open(OUT_CPP, 'w') +f.write("""#include +// Automatically generated by extract_strings.py +#ifdef __GNUC__ +#define UNUSED __attribute__((unused)) +#else +#define UNUSED +#endif +""") +f.write('static const char UNUSED *bitcoin_strings[] = {\n') +messages.sort(key=operator.itemgetter(0)) +for (msgid, msgstr) in messages: + if msgid != EMPTY: + f.write('QT_TRANSLATE_NOOP("bitcoin-core", %s),\n' % ('\n'.join(msgid))) +f.write('};') +f.close() diff --git a/share/qt/img/reload.xcf b/share/qt/img/reload.xcf new file mode 100644 index 0000000..dc8be62 Binary files /dev/null and b/share/qt/img/reload.xcf differ diff --git a/share/qt/make_spinner.py b/share/qt/make_spinner.py new file mode 100644 index 0000000..136aff3 --- /dev/null +++ b/share/qt/make_spinner.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# W.J. van der Laan, 2011 +# Make spinning .mng animation from a .png +# Requires imagemagick 6.7+ +from __future__ import division +from os import path +from PIL import Image +from subprocess import Popen + +SRC='img/reload_scaled.png' +DST='../../src/qt/res/movies/update_spinner.mng' +TMPDIR='/tmp' +TMPNAME='tmp-%03i.png' +NUMFRAMES=35 +FRAMERATE=10.0 +CONVERT='convert' +CLOCKWISE=True +DSIZE=(16,16) + +im_src = Image.open(SRC) + +if CLOCKWISE: + im_src = im_src.transpose(Image.FLIP_LEFT_RIGHT) + +def frame_to_filename(frame): + return path.join(TMPDIR, TMPNAME % frame) + +frame_files = [] +for frame in xrange(NUMFRAMES): + rotation = (frame + 0.5) / NUMFRAMES * 360.0 + if CLOCKWISE: + rotation = -rotation + im_new = im_src.rotate(rotation, Image.BICUBIC) + im_new.thumbnail(DSIZE, Image.ANTIALIAS) + outfile = frame_to_filename(frame) + im_new.save(outfile, 'png') + frame_files.append(outfile) + +p = Popen([CONVERT, "-delay", str(FRAMERATE), "-dispose", "2"] + frame_files + [DST]) +p.communicate() + + + diff --git a/share/qt/make_windows_icon.sh b/share/qt/make_windows_icon.sh new file mode 100644 index 0000000..bf607b1 --- /dev/null +++ b/share/qt/make_windows_icon.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# create multiresolution windows icon +ICON_SRC=../../src/qt/res/icons/bitcoin.png +ICON_DST=../../src/qt/res/icons/bitcoin.ico +convert ${ICON_SRC} -resize 16x16 bitcoin-16.png +convert ${ICON_SRC} -resize 32x32 bitcoin-32.png +convert ${ICON_SRC} -resize 48x48 bitcoin-48.png +convert bitcoin-16.png bitcoin-32.png bitcoin-48.png ${ICON_DST} + diff --git a/share/setup.nsi b/share/setup.nsi new file mode 100644 index 0000000..42ab8ca --- /dev/null +++ b/share/setup.nsi @@ -0,0 +1,162 @@ +Name CryptoLottoToken + +RequestExecutionLevel highest +SetCompressor /SOLID lzma + +# General Symbol Definitions +!define REGKEY "SOFTWARE\$(^Name)" +!define VERSION 0.8.6.1 +!define COMPANY "CryptoLottoToken project" +!define URL http://www.CryptoLottoToken.org/ + +# MUI Symbol Definitions +!define MUI_ICON "../share/pixmaps/bitcoin.ico" +!define MUI_WELCOMEFINISHPAGE_BITMAP "../share/pixmaps/nsis-wizard.bmp" +!define MUI_HEADERIMAGE +!define MUI_HEADERIMAGE_RIGHT +!define MUI_HEADERIMAGE_BITMAP "../share/pixmaps/nsis-header.bmp" +!define MUI_FINISHPAGE_NOAUTOCLOSE +!define MUI_STARTMENUPAGE_REGISTRY_ROOT HKLM +!define MUI_STARTMENUPAGE_REGISTRY_KEY ${REGKEY} +!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME StartMenuGroup +!define MUI_STARTMENUPAGE_DEFAULTFOLDER CryptoLottoToken +!define MUI_FINISHPAGE_RUN $INSTDIR\CryptoLottoToken-qt.exe +!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico" +!define MUI_UNWELCOMEFINISHPAGE_BITMAP "../share/pixmaps/nsis-wizard.bmp" +!define MUI_UNFINISHPAGE_NOAUTOCLOSE + +# Included files +!include Sections.nsh +!include MUI2.nsh + +# Variables +Var StartMenuGroup + +# Installer pages +!insertmacro MUI_PAGE_WELCOME +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_STARTMENU Application $StartMenuGroup +!insertmacro MUI_PAGE_INSTFILES +!insertmacro MUI_PAGE_FINISH +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES + +# Installer languages +!insertmacro MUI_LANGUAGE English + +# Installer attributes +OutFile CryptoLottoToken-0.8.6.1-win32-setup.exe +InstallDir $PROGRAMFILES\CryptoLottoToken +CRCCheck on +XPStyle on +BrandingText " " +ShowInstDetails show +VIProductVersion 0.8.6.1 +VIAddVersionKey ProductName CryptoLottoToken +VIAddVersionKey ProductVersion "${VERSION}" +VIAddVersionKey CompanyName "${COMPANY}" +VIAddVersionKey CompanyWebsite "${URL}" +VIAddVersionKey FileVersion "${VERSION}" +VIAddVersionKey FileDescription "" +VIAddVersionKey LegalCopyright "" +InstallDirRegKey HKCU "${REGKEY}" Path +ShowUninstDetails show + +# Installer sections +Section -Main SEC0000 + SetOutPath $INSTDIR + SetOverwrite on + File ../release/CryptoLottoToken-qt.exe + File /oname=COPYING.txt ../COPYING + File /oname=readme.txt ../doc/README_windows.txt + SetOutPath $INSTDIR\daemon + File ../src/CryptoLottoTokend.exe + SetOutPath $INSTDIR\src + File /r /x *.exe /x *.o ../src\*.* + SetOutPath $INSTDIR + WriteRegStr HKCU "${REGKEY}\Components" Main 1 + + # Remove old wxwidgets-based-bitcoin executable and locales: + Delete /REBOOTOK $INSTDIR\CryptoLottoToken.exe + RMDir /r /REBOOTOK $INSTDIR\locale +SectionEnd + +Section -post SEC0001 + WriteRegStr HKCU "${REGKEY}" Path $INSTDIR + SetOutPath $INSTDIR + WriteUninstaller $INSTDIR\uninstall.exe + !insertmacro MUI_STARTMENU_WRITE_BEGIN Application + CreateDirectory $SMPROGRAMS\$StartMenuGroup + CreateShortcut "$SMPROGRAMS\$StartMenuGroup\CryptoLottoToken.lnk" $INSTDIR\CryptoLottoToken-qt.exe + CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Uninstall CryptoLottoToken.lnk" $INSTDIR\uninstall.exe + !insertmacro MUI_STARTMENU_WRITE_END + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayName "$(^Name)" + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayVersion "${VERSION}" + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" Publisher "${COMPANY}" + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" URLInfoAbout "${URL}" + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayIcon $INSTDIR\uninstall.exe + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" UninstallString $INSTDIR\uninstall.exe + WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoModify 1 + WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoRepair 1 + WriteRegStr HKCR "CryptoLottoToken" "URL Protocol" "" + WriteRegStr HKCR "CryptoLottoToken" "" "URL:CryptoLottoToken" + WriteRegStr HKCR "CryptoLottoToken\DefaultIcon" "" $INSTDIR\CryptoLottoToken-qt.exe + WriteRegStr HKCR "CryptoLottoToken\shell\open\command" "" '"$INSTDIR\CryptoLottoToken-qt.exe" "%1"' +SectionEnd + +# Macro for selecting uninstaller sections +!macro SELECT_UNSECTION SECTION_NAME UNSECTION_ID + Push $R0 + ReadRegStr $R0 HKCU "${REGKEY}\Components" "${SECTION_NAME}" + StrCmp $R0 1 0 next${UNSECTION_ID} + !insertmacro SelectSection "${UNSECTION_ID}" + GoTo done${UNSECTION_ID} +next${UNSECTION_ID}: + !insertmacro UnselectSection "${UNSECTION_ID}" +done${UNSECTION_ID}: + Pop $R0 +!macroend + +# Uninstaller sections +Section /o -un.Main UNSEC0000 + Delete /REBOOTOK $INSTDIR\CryptoLottoToken-qt.exe + Delete /REBOOTOK $INSTDIR\COPYING.txt + Delete /REBOOTOK $INSTDIR\readme.txt + RMDir /r /REBOOTOK $INSTDIR\daemon + RMDir /r /REBOOTOK $INSTDIR\src + DeleteRegValue HKCU "${REGKEY}\Components" Main +SectionEnd + +Section -un.post UNSEC0001 + DeleteRegKey HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" + Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\Uninstall CryptoLottoToken.lnk" + Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\CryptoLottoToken.lnk" + Delete /REBOOTOK "$SMSTARTUP\CryptoLottoToken.lnk" + Delete /REBOOTOK $INSTDIR\uninstall.exe + Delete /REBOOTOK $INSTDIR\debug.log + Delete /REBOOTOK $INSTDIR\db.log + DeleteRegValue HKCU "${REGKEY}" StartMenuGroup + DeleteRegValue HKCU "${REGKEY}" Path + DeleteRegKey /IfEmpty HKCU "${REGKEY}\Components" + DeleteRegKey /IfEmpty HKCU "${REGKEY}" + DeleteRegKey HKCR "CryptoLottoToken" + RmDir /REBOOTOK $SMPROGRAMS\$StartMenuGroup + RmDir /REBOOTOK $INSTDIR + Push $R0 + StrCpy $R0 $StartMenuGroup 1 + StrCmp $R0 ">" no_smgroup +no_smgroup: + Pop $R0 +SectionEnd + +# Installer functions +Function .onInit + InitPluginsDir +FunctionEnd + +# Uninstaller functions +Function un.onInit + ReadRegStr $INSTDIR HKCU "${REGKEY}" Path + !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuGroup + !insertmacro SELECT_UNSECTION Main ${UNSEC0000} +FunctionEnd diff --git a/share/ui.rc b/share/ui.rc new file mode 100644 index 0000000..c3cece1 --- /dev/null +++ b/share/ui.rc @@ -0,0 +1,15 @@ +bitcoin ICON "pixmaps/bitcoin.ico" + +#include "wx/msw/wx.rc" + +check ICON "pixmaps/check.ico" +send16 BITMAP "pixmaps/send16.bmp" +send16mask BITMAP "pixmaps/send16mask.bmp" +send16masknoshadow BITMAP "pixmaps/send16masknoshadow.bmp" +send20 BITMAP "pixmaps/send20.bmp" +send20mask BITMAP "pixmaps/send20mask.bmp" +addressbook16 BITMAP "pixmaps/addressbook16.bmp" +addressbook16mask BITMAP "pixmaps/addressbook16mask.bmp" +addressbook20 BITMAP "pixmaps/addressbook20.bmp" +addressbook20mask BITMAP "pixmaps/addressbook20mask.bmp" +favicon ICON "pixmaps/favicon.ico" diff --git a/src/addrman.cpp b/src/addrman.cpp new file mode 100644 index 0000000..780edde --- /dev/null +++ b/src/addrman.cpp @@ -0,0 +1,528 @@ +// Copyright (c) 2012 Pieter Wuille +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "addrman.h" +#include "hash.h" + +using namespace std; + +int CAddrInfo::GetTriedBucket(const std::vector &nKey) const +{ + CDataStream ss1(SER_GETHASH, 0); + std::vector vchKey = GetKey(); + ss1 << nKey << vchKey; + uint64 hash1 = Hash(ss1.begin(), ss1.end()).Get64(); + + CDataStream ss2(SER_GETHASH, 0); + std::vector vchGroupKey = GetGroup(); + ss2 << nKey << vchGroupKey << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP); + uint64 hash2 = Hash(ss2.begin(), ss2.end()).Get64(); + return hash2 % ADDRMAN_TRIED_BUCKET_COUNT; +} + +int CAddrInfo::GetNewBucket(const std::vector &nKey, const CNetAddr& src) const +{ + CDataStream ss1(SER_GETHASH, 0); + std::vector vchGroupKey = GetGroup(); + std::vector vchSourceGroupKey = src.GetGroup(); + ss1 << nKey << vchGroupKey << vchSourceGroupKey; + uint64 hash1 = Hash(ss1.begin(), ss1.end()).Get64(); + + CDataStream ss2(SER_GETHASH, 0); + ss2 << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP); + uint64 hash2 = Hash(ss2.begin(), ss2.end()).Get64(); + return hash2 % ADDRMAN_NEW_BUCKET_COUNT; +} + +bool CAddrInfo::IsTerrible(int64 nNow) const +{ + if (nLastTry && nLastTry >= nNow-60) // never remove things tried the last minute + return false; + + if (nTime > nNow + 10*60) // came in a flying DeLorean + return true; + + if (nTime==0 || nNow-nTime > ADDRMAN_HORIZON_DAYS*86400) // not seen in over a month + return true; + + if (nLastSuccess==0 && nAttempts>=ADDRMAN_RETRIES) // tried three times and never a success + return true; + + if (nNow-nLastSuccess > ADDRMAN_MIN_FAIL_DAYS*86400 && nAttempts>=ADDRMAN_MAX_FAILURES) // 10 successive failures in the last week + return true; + + return false; +} + +double CAddrInfo::GetChance(int64 nNow) const +{ + double fChance = 1.0; + + int64 nSinceLastSeen = nNow - nTime; + int64 nSinceLastTry = nNow - nLastTry; + + if (nSinceLastSeen < 0) nSinceLastSeen = 0; + if (nSinceLastTry < 0) nSinceLastTry = 0; + + fChance *= 600.0 / (600.0 + nSinceLastSeen); + + // deprioritize very recent attempts away + if (nSinceLastTry < 60*10) + fChance *= 0.01; + + // deprioritize 50% after each failed attempt + for (int n=0; n::iterator it = mapAddr.find(addr); + if (it == mapAddr.end()) + return NULL; + if (pnId) + *pnId = (*it).second; + std::map::iterator it2 = mapInfo.find((*it).second); + if (it2 != mapInfo.end()) + return &(*it2).second; + return NULL; +} + +CAddrInfo* CAddrMan::Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId) +{ + int nId = nIdCount++; + mapInfo[nId] = CAddrInfo(addr, addrSource); + mapAddr[addr] = nId; + mapInfo[nId].nRandomPos = vRandom.size(); + vRandom.push_back(nId); + if (pnId) + *pnId = nId; + return &mapInfo[nId]; +} + +void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2) +{ + if (nRndPos1 == nRndPos2) + return; + + assert(nRndPos1 < vRandom.size() && nRndPos2 < vRandom.size()); + + int nId1 = vRandom[nRndPos1]; + int nId2 = vRandom[nRndPos2]; + + assert(mapInfo.count(nId1) == 1); + assert(mapInfo.count(nId2) == 1); + + mapInfo[nId1].nRandomPos = nRndPos2; + mapInfo[nId2].nRandomPos = nRndPos1; + + vRandom[nRndPos1] = nId2; + vRandom[nRndPos2] = nId1; +} + +int CAddrMan::SelectTried(int nKBucket) +{ + std::vector &vTried = vvTried[nKBucket]; + + // random shuffle the first few elements (using the entire list) + // find the least recently tried among them + int64 nOldest = -1; + int nOldestPos = -1; + for (unsigned int i = 0; i < ADDRMAN_TRIED_ENTRIES_INSPECT_ON_EVICT && i < vTried.size(); i++) + { + int nPos = GetRandInt(vTried.size() - i) + i; + int nTemp = vTried[nPos]; + vTried[nPos] = vTried[i]; + vTried[i] = nTemp; + assert(nOldest == -1 || mapInfo.count(nTemp) == 1); + if (nOldest == -1 || mapInfo[nTemp].nLastSuccess < mapInfo[nOldest].nLastSuccess) { + nOldest = nTemp; + nOldestPos = nPos; + } + } + + return nOldestPos; +} + +int CAddrMan::ShrinkNew(int nUBucket) +{ + assert(nUBucket >= 0 && (unsigned int)nUBucket < vvNew.size()); + std::set &vNew = vvNew[nUBucket]; + + // first look for deletable items + for (std::set::iterator it = vNew.begin(); it != vNew.end(); it++) + { + assert(mapInfo.count(*it)); + CAddrInfo &info = mapInfo[*it]; + if (info.IsTerrible()) + { + if (--info.nRefCount == 0) + { + SwapRandom(info.nRandomPos, vRandom.size()-1); + vRandom.pop_back(); + mapAddr.erase(info); + mapInfo.erase(*it); + nNew--; + } + vNew.erase(it); + return 0; + } + } + + // otherwise, select four randomly, and pick the oldest of those to replace + int n[4] = {GetRandInt(vNew.size()), GetRandInt(vNew.size()), GetRandInt(vNew.size()), GetRandInt(vNew.size())}; + int nI = 0; + int nOldest = -1; + for (std::set::iterator it = vNew.begin(); it != vNew.end(); it++) + { + if (nI == n[0] || nI == n[1] || nI == n[2] || nI == n[3]) + { + assert(nOldest == -1 || mapInfo.count(*it) == 1); + if (nOldest == -1 || mapInfo[*it].nTime < mapInfo[nOldest].nTime) + nOldest = *it; + } + nI++; + } + assert(mapInfo.count(nOldest) == 1); + CAddrInfo &info = mapInfo[nOldest]; + if (--info.nRefCount == 0) + { + SwapRandom(info.nRandomPos, vRandom.size()-1); + vRandom.pop_back(); + mapAddr.erase(info); + mapInfo.erase(nOldest); + nNew--; + } + vNew.erase(nOldest); + + return 1; +} + +void CAddrMan::MakeTried(CAddrInfo& info, int nId, int nOrigin) +{ + assert(vvNew[nOrigin].count(nId) == 1); + + // remove the entry from all new buckets + for (std::vector >::iterator it = vvNew.begin(); it != vvNew.end(); it++) + { + if ((*it).erase(nId)) + info.nRefCount--; + } + nNew--; + + assert(info.nRefCount == 0); + + // what tried bucket to move the entry to + int nKBucket = info.GetTriedBucket(nKey); + std::vector &vTried = vvTried[nKBucket]; + + // first check whether there is place to just add it + if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE) + { + vTried.push_back(nId); + nTried++; + info.fInTried = true; + return; + } + + // otherwise, find an item to evict + int nPos = SelectTried(nKBucket); + + // find which new bucket it belongs to + assert(mapInfo.count(vTried[nPos]) == 1); + int nUBucket = mapInfo[vTried[nPos]].GetNewBucket(nKey); + std::set &vNew = vvNew[nUBucket]; + + // remove the to-be-replaced tried entry from the tried set + CAddrInfo& infoOld = mapInfo[vTried[nPos]]; + infoOld.fInTried = false; + infoOld.nRefCount = 1; + // do not update nTried, as we are going to move something else there immediately + + // check whether there is place in that one, + if (vNew.size() < ADDRMAN_NEW_BUCKET_SIZE) + { + // if so, move it back there + vNew.insert(vTried[nPos]); + } else { + // otherwise, move it to the new bucket nId came from (there is certainly place there) + vvNew[nOrigin].insert(vTried[nPos]); + } + nNew++; + + vTried[nPos] = nId; + // we just overwrote an entry in vTried; no need to update nTried + info.fInTried = true; + return; +} + +void CAddrMan::Good_(const CService &addr, int64 nTime) +{ +// printf("Good: addr=%s\n", addr.ToString().c_str()); + + int nId; + CAddrInfo *pinfo = Find(addr, &nId); + + // if not found, bail out + if (!pinfo) + return; + + CAddrInfo &info = *pinfo; + + // check whether we are talking about the exact same CService (including same port) + if (info != addr) + return; + + // update info + info.nLastSuccess = nTime; + info.nLastTry = nTime; + info.nTime = nTime; + info.nAttempts = 0; + + // if it is already in the tried set, don't do anything else + if (info.fInTried) + return; + + // find a bucket it is in now + int nRnd = GetRandInt(vvNew.size()); + int nUBucket = -1; + for (unsigned int n = 0; n < vvNew.size(); n++) + { + int nB = (n+nRnd) % vvNew.size(); + std::set &vNew = vvNew[nB]; + if (vNew.count(nId)) + { + nUBucket = nB; + break; + } + } + + // if no bucket is found, something bad happened; + // TODO: maybe re-add the node, but for now, just bail out + if (nUBucket == -1) return; + + printf("Moving %s to tried\n", addr.ToString().c_str()); + + // move nId to the tried tables + MakeTried(info, nId, nUBucket); +} + +bool CAddrMan::Add_(const CAddress &addr, const CNetAddr& source, int64 nTimePenalty) +{ + if (!addr.IsRoutable()) + return false; + + bool fNew = false; + int nId; + CAddrInfo *pinfo = Find(addr, &nId); + + if (pinfo) + { + // periodically update nTime + bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60); + int64 nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60); + if (addr.nTime && (!pinfo->nTime || pinfo->nTime < addr.nTime - nUpdateInterval - nTimePenalty)) + pinfo->nTime = max((int64)0, addr.nTime - nTimePenalty); + + // add services + pinfo->nServices |= addr.nServices; + + // do not update if no new information is present + if (!addr.nTime || (pinfo->nTime && addr.nTime <= pinfo->nTime)) + return false; + + // do not update if the entry was already in the "tried" table + if (pinfo->fInTried) + return false; + + // do not update if the max reference count is reached + if (pinfo->nRefCount == ADDRMAN_NEW_BUCKETS_PER_ADDRESS) + return false; + + // stochastic test: previous nRefCount == N: 2^N times harder to increase it + int nFactor = 1; + for (int n=0; nnRefCount; n++) + nFactor *= 2; + if (nFactor > 1 && (GetRandInt(nFactor) != 0)) + return false; + } else { + pinfo = Create(addr, source, &nId); + pinfo->nTime = max((int64)0, (int64)pinfo->nTime - nTimePenalty); +// printf("Added %s [nTime=%fhr]\n", pinfo->ToString().c_str(), (GetAdjustedTime() - pinfo->nTime) / 3600.0); + nNew++; + fNew = true; + } + + int nUBucket = pinfo->GetNewBucket(nKey, source); + std::set &vNew = vvNew[nUBucket]; + if (!vNew.count(nId)) + { + pinfo->nRefCount++; + if (vNew.size() == ADDRMAN_NEW_BUCKET_SIZE) + ShrinkNew(nUBucket); + vvNew[nUBucket].insert(nId); + } + return fNew; +} + +void CAddrMan::Attempt_(const CService &addr, int64 nTime) +{ + CAddrInfo *pinfo = Find(addr); + + // if not found, bail out + if (!pinfo) + return; + + CAddrInfo &info = *pinfo; + + // check whether we are talking about the exact same CService (including same port) + if (info != addr) + return; + + // update info + info.nLastTry = nTime; + info.nAttempts++; +} + +CAddress CAddrMan::Select_(int nUnkBias) +{ + if (size() == 0) + return CAddress(); + + double nCorTried = sqrt(nTried) * (100.0 - nUnkBias); + double nCorNew = sqrt(nNew) * nUnkBias; + if ((nCorTried + nCorNew)*GetRandInt(1<<30)/(1<<30) < nCorTried) + { + // use a tried node + double fChanceFactor = 1.0; + while(1) + { + int nKBucket = GetRandInt(vvTried.size()); + std::vector &vTried = vvTried[nKBucket]; + if (vTried.size() == 0) continue; + int nPos = GetRandInt(vTried.size()); + assert(mapInfo.count(vTried[nPos]) == 1); + CAddrInfo &info = mapInfo[vTried[nPos]]; + if (GetRandInt(1<<30) < fChanceFactor*info.GetChance()*(1<<30)) + return info; + fChanceFactor *= 1.2; + } + } else { + // use a new node + double fChanceFactor = 1.0; + while(1) + { + int nUBucket = GetRandInt(vvNew.size()); + std::set &vNew = vvNew[nUBucket]; + if (vNew.size() == 0) continue; + int nPos = GetRandInt(vNew.size()); + std::set::iterator it = vNew.begin(); + while (nPos--) + it++; + assert(mapInfo.count(*it) == 1); + CAddrInfo &info = mapInfo[*it]; + if (GetRandInt(1<<30) < fChanceFactor*info.GetChance()*(1<<30)) + return info; + fChanceFactor *= 1.2; + } + } +} + +#ifdef DEBUG_ADDRMAN +int CAddrMan::Check_() +{ + std::set setTried; + std::map mapNew; + + if (vRandom.size() != nTried + nNew) return -7; + + for (std::map::iterator it = mapInfo.begin(); it != mapInfo.end(); it++) + { + int n = (*it).first; + CAddrInfo &info = (*it).second; + if (info.fInTried) + { + + if (!info.nLastSuccess) return -1; + if (info.nRefCount) return -2; + setTried.insert(n); + } else { + if (info.nRefCount < 0 || info.nRefCount > ADDRMAN_NEW_BUCKETS_PER_ADDRESS) return -3; + if (!info.nRefCount) return -4; + mapNew[n] = info.nRefCount; + } + if (mapAddr[info] != n) return -5; + if (info.nRandomPos<0 || info.nRandomPos>=vRandom.size() || vRandom[info.nRandomPos] != n) return -14; + if (info.nLastTry < 0) return -6; + if (info.nLastSuccess < 0) return -8; + } + + if (setTried.size() != nTried) return -9; + if (mapNew.size() != nNew) return -10; + + for (int n=0; n &vTried = vvTried[n]; + for (std::vector::iterator it = vTried.begin(); it != vTried.end(); it++) + { + if (!setTried.count(*it)) return -11; + setTried.erase(*it); + } + } + + for (int n=0; n &vNew = vvNew[n]; + for (std::set::iterator it = vNew.begin(); it != vNew.end(); it++) + { + if (!mapNew.count(*it)) return -12; + if (--mapNew[*it] == 0) + mapNew.erase(*it); + } + } + + if (setTried.size()) return -13; + if (mapNew.size()) return -15; + + return 0; +} +#endif + +void CAddrMan::GetAddr_(std::vector &vAddr) +{ + int nNodes = ADDRMAN_GETADDR_MAX_PCT*vRandom.size()/100; + if (nNodes > ADDRMAN_GETADDR_MAX) + nNodes = ADDRMAN_GETADDR_MAX; + + // perform a random shuffle over the first nNodes elements of vRandom (selecting from all) + for (int n = 0; n nUpdateInterval) + info.nTime = nTime; +} diff --git a/src/addrman.h b/src/addrman.h new file mode 100644 index 0000000..7af6afd --- /dev/null +++ b/src/addrman.h @@ -0,0 +1,503 @@ +// Copyright (c) 2012 Pieter Wuille +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef _BITCOIN_ADDRMAN +#define _BITCOIN_ADDRMAN 1 + +#include "netbase.h" +#include "protocol.h" +#include "util.h" +#include "sync.h" + + +#include +#include + +#include + + +/** Extended statistics about a CAddress */ +class CAddrInfo : public CAddress +{ +private: + // where knowledge about this address first came from + CNetAddr source; + + // last successful connection by us + int64 nLastSuccess; + + // last try whatsoever by us: + // int64 CAddress::nLastTry + + // connection attempts since last successful attempt + int nAttempts; + + // reference count in new sets (memory only) + int nRefCount; + + // in tried set? (memory only) + bool fInTried; + + // position in vRandom + int nRandomPos; + + friend class CAddrMan; + +public: + + IMPLEMENT_SERIALIZE( + CAddress* pthis = (CAddress*)(this); + READWRITE(*pthis); + READWRITE(source); + READWRITE(nLastSuccess); + READWRITE(nAttempts); + ) + + void Init() + { + nLastSuccess = 0; + nLastTry = 0; + nAttempts = 0; + nRefCount = 0; + fInTried = false; + nRandomPos = -1; + } + + CAddrInfo(const CAddress &addrIn, const CNetAddr &addrSource) : CAddress(addrIn), source(addrSource) + { + Init(); + } + + CAddrInfo() : CAddress(), source() + { + Init(); + } + + // Calculate in which "tried" bucket this entry belongs + int GetTriedBucket(const std::vector &nKey) const; + + // Calculate in which "new" bucket this entry belongs, given a certain source + int GetNewBucket(const std::vector &nKey, const CNetAddr& src) const; + + // Calculate in which "new" bucket this entry belongs, using its default source + int GetNewBucket(const std::vector &nKey) const + { + return GetNewBucket(nKey, source); + } + + // Determine whether the statistics about this entry are bad enough so that it can just be deleted + bool IsTerrible(int64 nNow = GetAdjustedTime()) const; + + // Calculate the relative chance this entry should be given when selecting nodes to connect to + double GetChance(int64 nNow = GetAdjustedTime()) const; + +}; + +// Stochastic address manager +// +// Design goals: +// * Only keep a limited number of addresses around, so that addr.dat and memory requirements do not grow without bound. +// * Keep the address tables in-memory, and asynchronously dump the entire to able in addr.dat. +// * Make sure no (localized) attacker can fill the entire table with his nodes/addresses. +// +// To that end: +// * Addresses are organized into buckets. +// * Address that have not yet been tried go into 256 "new" buckets. +// * Based on the address range (/16 for IPv4) of source of the information, 32 buckets are selected at random +// * The actual bucket is chosen from one of these, based on the range the address itself is located. +// * One single address can occur in up to 4 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. +// * Addresses of nodes that are known to be accessible go into 64 "tried" buckets. +// * Each address range selects at random 4 of these buckets. +// * The actual bucket is chosen from one of these, based on the full address. +// * When adding a new good address to a full bucket, a randomly chosen entry (with a bias favoring less recently +// tried ones) is evicted from it, back to the "new" buckets. +// * Bucket selection is based on cryptographic hashing, using a randomly-generated 256-bit key, which should not +// be observable by adversaries. +// * Several indexes are kept for high performance. Defining DEBUG_ADDRMAN will introduce frequent (and expensive) +// consistency checks for the entire data structure. + +// total number of buckets for tried addresses +#define ADDRMAN_TRIED_BUCKET_COUNT 64 + +// maximum allowed number of entries in buckets for tried addresses +#define ADDRMAN_TRIED_BUCKET_SIZE 64 + +// total number of buckets for new addresses +#define ADDRMAN_NEW_BUCKET_COUNT 256 + +// maximum allowed number of entries in buckets for new addresses +#define ADDRMAN_NEW_BUCKET_SIZE 64 + +// over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread +#define ADDRMAN_TRIED_BUCKETS_PER_GROUP 4 + +// over how many buckets entries with new addresses originating from a single group are spread +#define ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP 32 + +// in how many buckets for entries with new addresses a single address may occur +#define ADDRMAN_NEW_BUCKETS_PER_ADDRESS 4 + +// how many entries in a bucket with tried addresses are inspected, when selecting one to replace +#define ADDRMAN_TRIED_ENTRIES_INSPECT_ON_EVICT 4 + +// how old addresses can maximally be +#define ADDRMAN_HORIZON_DAYS 30 + +// after how many failed attempts we give up on a new node +#define ADDRMAN_RETRIES 3 + +// how many successive failures are allowed ... +#define ADDRMAN_MAX_FAILURES 10 + +// ... in at least this many days +#define ADDRMAN_MIN_FAIL_DAYS 7 + +// the maximum percentage of nodes to return in a getaddr call +#define ADDRMAN_GETADDR_MAX_PCT 23 + +// the maximum number of nodes to return in a getaddr call +#define ADDRMAN_GETADDR_MAX 2500 + +/** Stochastical (IP) address manager */ +class CAddrMan +{ +private: + // critical section to protect the inner data structures + mutable CCriticalSection cs; + + // secret key to randomize bucket select with + std::vector nKey; + + // last used nId + int nIdCount; + + // table with information about all nIds + std::map mapInfo; + + // find an nId based on its network address + std::map mapAddr; + + // randomly-ordered vector of all nIds + std::vector vRandom; + + // number of "tried" entries + int nTried; + + // list of "tried" buckets + std::vector > vvTried; + + // number of (unique) "new" entries + int nNew; + + // list of "new" buckets + std::vector > vvNew; + +protected: + + // Find an entry. + CAddrInfo* Find(const CNetAddr& addr, int *pnId = NULL); + + // find an entry, creating it if necessary. + // nTime and nServices of found node is updated, if necessary. + CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = NULL); + + // Swap two elements in vRandom. + void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2); + + // Return position in given bucket to replace. + int SelectTried(int nKBucket); + + // Remove an element from a "new" bucket. + // This is the only place where actual deletes occur. + // They are never deleted while in the "tried" table, only possibly evicted back to the "new" table. + int ShrinkNew(int nUBucket); + + // Move an entry from the "new" table(s) to the "tried" table + // @pre vvUnkown[nOrigin].count(nId) != 0 + void MakeTried(CAddrInfo& info, int nId, int nOrigin); + + // Mark an entry "good", possibly moving it from "new" to "tried". + void Good_(const CService &addr, int64 nTime); + + // Add an entry to the "new" table. + bool Add_(const CAddress &addr, const CNetAddr& source, int64 nTimePenalty); + + // Mark an entry as attempted to connect. + void Attempt_(const CService &addr, int64 nTime); + + // Select an address to connect to. + // nUnkBias determines how much to favor new addresses over tried ones (min=0, max=100) + CAddress Select_(int nUnkBias); + +#ifdef DEBUG_ADDRMAN + // Perform consistency check. Returns an error code or zero. + int Check_(); +#endif + + // Select several addresses at once. + void GetAddr_(std::vector &vAddr); + + // Mark an entry as currently-connected-to. + void Connected_(const CService &addr, int64 nTime); + +public: + + IMPLEMENT_SERIALIZE + (({ + // serialized format: + // * version byte (currently 0) + // * nKey + // * nNew + // * nTried + // * number of "new" buckets + // * all nNew addrinfos in vvNew + // * all nTried addrinfos in vvTried + // * for each bucket: + // * number of elements + // * for each element: index + // + // Notice that vvTried, mapAddr and vVector are never encoded explicitly; + // they are instead reconstructed from the other information. + // + // vvNew is serialized, but only used if ADDRMAN_UNKOWN_BUCKET_COUNT didn't change, + // otherwise it is reconstructed as well. + // + // This format is more complex, but significantly smaller (at most 1.5 MiB), and supports + // changes to the ADDRMAN_ parameters without breaking the on-disk structure. + { + LOCK(cs); + unsigned char nVersion = 0; + READWRITE(nVersion); + READWRITE(nKey); + READWRITE(nNew); + READWRITE(nTried); + + CAddrMan *am = const_cast(this); + if (fWrite) + { + int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT; + READWRITE(nUBuckets); + std::map mapUnkIds; + int nIds = 0; + for (std::map::iterator it = am->mapInfo.begin(); it != am->mapInfo.end(); it++) + { + if (nIds == nNew) break; // this means nNew was wrong, oh ow + mapUnkIds[(*it).first] = nIds; + CAddrInfo &info = (*it).second; + if (info.nRefCount) + { + READWRITE(info); + nIds++; + } + } + nIds = 0; + for (std::map::iterator it = am->mapInfo.begin(); it != am->mapInfo.end(); it++) + { + if (nIds == nTried) break; // this means nTried was wrong, oh ow + CAddrInfo &info = (*it).second; + if (info.fInTried) + { + READWRITE(info); + nIds++; + } + } + for (std::vector >::iterator it = am->vvNew.begin(); it != am->vvNew.end(); it++) + { + const std::set &vNew = (*it); + int nSize = vNew.size(); + READWRITE(nSize); + for (std::set::iterator it2 = vNew.begin(); it2 != vNew.end(); it2++) + { + int nIndex = mapUnkIds[*it2]; + READWRITE(nIndex); + } + } + } else { + int nUBuckets = 0; + READWRITE(nUBuckets); + am->nIdCount = 0; + am->mapInfo.clear(); + am->mapAddr.clear(); + am->vRandom.clear(); + am->vvTried = std::vector >(ADDRMAN_TRIED_BUCKET_COUNT, std::vector(0)); + am->vvNew = std::vector >(ADDRMAN_NEW_BUCKET_COUNT, std::set()); + for (int n = 0; n < am->nNew; n++) + { + CAddrInfo &info = am->mapInfo[n]; + READWRITE(info); + am->mapAddr[info] = n; + info.nRandomPos = vRandom.size(); + am->vRandom.push_back(n); + if (nUBuckets != ADDRMAN_NEW_BUCKET_COUNT) + { + am->vvNew[info.GetNewBucket(am->nKey)].insert(n); + info.nRefCount++; + } + } + am->nIdCount = am->nNew; + int nLost = 0; + for (int n = 0; n < am->nTried; n++) + { + CAddrInfo info; + READWRITE(info); + std::vector &vTried = am->vvTried[info.GetTriedBucket(am->nKey)]; + if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE) + { + info.nRandomPos = vRandom.size(); + info.fInTried = true; + am->vRandom.push_back(am->nIdCount); + am->mapInfo[am->nIdCount] = info; + am->mapAddr[info] = am->nIdCount; + vTried.push_back(am->nIdCount); + am->nIdCount++; + } else { + nLost++; + } + } + am->nTried -= nLost; + for (int b = 0; b < nUBuckets; b++) + { + std::set &vNew = am->vvNew[b]; + int nSize = 0; + READWRITE(nSize); + for (int n = 0; n < nSize; n++) + { + int nIndex = 0; + READWRITE(nIndex); + CAddrInfo &info = am->mapInfo[nIndex]; + if (nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS) + { + info.nRefCount++; + vNew.insert(nIndex); + } + } + } + } + } + });) + + CAddrMan() : vRandom(0), vvTried(ADDRMAN_TRIED_BUCKET_COUNT, std::vector(0)), vvNew(ADDRMAN_NEW_BUCKET_COUNT, std::set()) + { + nKey.resize(32); + RAND_bytes(&nKey[0], 32); + + nIdCount = 0; + nTried = 0; + nNew = 0; + } + + // Return the number of (unique) addresses in all tables. + int size() + { + return vRandom.size(); + } + + // Consistency check + void Check() + { +#ifdef DEBUG_ADDRMAN + { + LOCK(cs); + int err; + if ((err=Check_())) + printf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err); + } +#endif + } + + // Add a single address. + bool Add(const CAddress &addr, const CNetAddr& source, int64 nTimePenalty = 0) + { + bool fRet = false; + { + LOCK(cs); + Check(); + fRet |= Add_(addr, source, nTimePenalty); + Check(); + } + if (fRet) + printf("Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort().c_str(), source.ToString().c_str(), nTried, nNew); + return fRet; + } + + // Add multiple addresses. + bool Add(const std::vector &vAddr, const CNetAddr& source, int64 nTimePenalty = 0) + { + int nAdd = 0; + { + LOCK(cs); + Check(); + for (std::vector::const_iterator it = vAddr.begin(); it != vAddr.end(); it++) + nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0; + Check(); + } + if (nAdd) + printf("Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString().c_str(), nTried, nNew); + return nAdd > 0; + } + + // Mark an entry as accessible. + void Good(const CService &addr, int64 nTime = GetAdjustedTime()) + { + { + LOCK(cs); + Check(); + Good_(addr, nTime); + Check(); + } + } + + // Mark an entry as connection attempted to. + void Attempt(const CService &addr, int64 nTime = GetAdjustedTime()) + { + { + LOCK(cs); + Check(); + Attempt_(addr, nTime); + Check(); + } + } + + // Choose an address to connect to. + // nUnkBias determines how much "new" entries are favored over "tried" ones (0-100). + CAddress Select(int nUnkBias = 50) + { + CAddress addrRet; + { + LOCK(cs); + Check(); + addrRet = Select_(nUnkBias); + Check(); + } + return addrRet; + } + + // Return a bunch of addresses, selected at random. + std::vector GetAddr() + { + Check(); + std::vector vAddr; + { + LOCK(cs); + GetAddr_(vAddr); + } + Check(); + return vAddr; + } + + // Mark an entry as currently-connected-to. + void Connected(const CService &addr, int64 nTime = GetAdjustedTime()) + { + { + LOCK(cs); + Check(); + Connected_(addr, nTime); + Check(); + } + } +}; + +#endif diff --git a/src/alert.cpp b/src/alert.cpp new file mode 100644 index 0000000..27ee612 --- /dev/null +++ b/src/alert.cpp @@ -0,0 +1,258 @@ +// +// Alert system +// + +#include +#include +#include +#include +#include + +#include "alert.h" +#include "key.h" +#include "net.h" +#include "sync.h" +#include "ui_interface.h" + +using namespace std; + +map mapAlerts; +CCriticalSection cs_mapAlerts; + +static const char* pszMainKey = "0401827103a6892d5023693c80f3a49c8f13f8d45b8c857fbcbc8bc4a8e4d3eb4b10f4d4604fa08dce601aaf0f470216fe1b51850b4acf21b179c45070ac7b03a9"; +static const char* pszTestKey = "04302290343f912c401d56d68b123028bf52e5fca1939df127f63c6467cdf9c8e2c14b61104cf817d0b780da337893ecc4aaff1309e536162dabbdb45200ca2b0a"; + +void CUnsignedAlert::SetNull() +{ + nVersion = 1; + nRelayUntil = 0; + nExpiration = 0; + nID = 0; + nCancel = 0; + setCancel.clear(); + nMinVer = 0; + nMaxVer = 0; + setSubVer.clear(); + nPriority = 0; + + strComment.clear(); + strStatusBar.clear(); + strReserved.clear(); +} + +std::string CUnsignedAlert::ToString() const +{ + std::string strSetCancel; + BOOST_FOREACH(int n, setCancel) + strSetCancel += strprintf("%d ", n); + std::string strSetSubVer; + BOOST_FOREACH(std::string str, setSubVer) + strSetSubVer += "\"" + str + "\" "; + return strprintf( + "CAlert(\n" + " nVersion = %d\n" + " nRelayUntil = %"PRI64d"\n" + " nExpiration = %"PRI64d"\n" + " nID = %d\n" + " nCancel = %d\n" + " setCancel = %s\n" + " nMinVer = %d\n" + " nMaxVer = %d\n" + " setSubVer = %s\n" + " nPriority = %d\n" + " strComment = \"%s\"\n" + " strStatusBar = \"%s\"\n" + ")\n", + nVersion, + nRelayUntil, + nExpiration, + nID, + nCancel, + strSetCancel.c_str(), + nMinVer, + nMaxVer, + strSetSubVer.c_str(), + nPriority, + strComment.c_str(), + strStatusBar.c_str()); +} + +void CUnsignedAlert::print() const +{ + printf("%s", ToString().c_str()); +} + +void CAlert::SetNull() +{ + CUnsignedAlert::SetNull(); + vchMsg.clear(); + vchSig.clear(); +} + +bool CAlert::IsNull() const +{ + return (nExpiration == 0); +} + +uint256 CAlert::GetHash() const +{ + return Hash(this->vchMsg.begin(), this->vchMsg.end()); +} + +bool CAlert::IsInEffect() const +{ + return (GetAdjustedTime() < nExpiration); +} + +bool CAlert::Cancels(const CAlert& alert) const +{ + if (!IsInEffect()) + return false; // this was a no-op before 31403 + return (alert.nID <= nCancel || setCancel.count(alert.nID)); +} + +bool CAlert::AppliesTo(int nVersion, std::string strSubVerIn) const +{ + // TODO: rework for client-version-embedded-in-strSubVer ? + return (IsInEffect() && + nMinVer <= nVersion && nVersion <= nMaxVer && + (setSubVer.empty() || setSubVer.count(strSubVerIn))); +} + +bool CAlert::AppliesToMe() const +{ + return AppliesTo(PROTOCOL_VERSION, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector())); +} + +bool CAlert::RelayTo(CNode* pnode) const +{ + if (!IsInEffect()) + return false; + // returns true if wasn't already contained in the set + if (pnode->setKnown.insert(GetHash()).second) + { + if (AppliesTo(pnode->nVersion, pnode->strSubVer) || + AppliesToMe() || + GetAdjustedTime() < nRelayUntil) + { + pnode->PushMessage("alert", *this); + return true; + } + } + return false; +} + +bool CAlert::CheckSignature() const +{ + CPubKey key(ParseHex(fTestNet ? pszTestKey : pszMainKey)); + if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig)) + return error("CAlert::CheckSignature() : verify signature failed"); + + // Now unserialize the data + CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION); + sMsg >> *(CUnsignedAlert*)this; + return true; +} + +CAlert CAlert::getAlertByHash(const uint256 &hash) +{ + CAlert retval; + { + LOCK(cs_mapAlerts); + map::iterator mi = mapAlerts.find(hash); + if(mi != mapAlerts.end()) + retval = mi->second; + } + return retval; +} + +bool CAlert::ProcessAlert(bool fThread) +{ + if (!CheckSignature()) + return false; + if (!IsInEffect()) + return false; + + // alert.nID=max is reserved for if the alert key is + // compromised. It must have a pre-defined message, + // must never expire, must apply to all versions, + // and must cancel all previous + // alerts or it will be ignored (so an attacker can't + // send an "everything is OK, don't panic" version that + // cannot be overridden): + int maxInt = std::numeric_limits::max(); + if (nID == maxInt) + { + if (!( + nExpiration == maxInt && + nCancel == (maxInt-1) && + nMinVer == 0 && + nMaxVer == maxInt && + setSubVer.empty() && + nPriority == maxInt && + strStatusBar == "URGENT: Alert key compromised, upgrade required" + )) + return false; + } + + { + LOCK(cs_mapAlerts); + // Cancel previous alerts + for (map::iterator mi = mapAlerts.begin(); mi != mapAlerts.end();) + { + const CAlert& alert = (*mi).second; + if (Cancels(alert)) + { + printf("cancelling alert %d\n", alert.nID); + uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED); + mapAlerts.erase(mi++); + } + else if (!alert.IsInEffect()) + { + printf("expiring alert %d\n", alert.nID); + uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED); + mapAlerts.erase(mi++); + } + else + mi++; + } + + // Check if this alert has been cancelled + BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) + { + const CAlert& alert = item.second; + if (alert.Cancels(*this)) + { + printf("alert already cancelled by %d\n", alert.nID); + return false; + } + } + + // Add to mapAlerts + mapAlerts.insert(make_pair(GetHash(), *this)); + // Notify UI and -alertnotify if it applies to me + if(AppliesToMe()) + { + uiInterface.NotifyAlertChanged(GetHash(), CT_NEW); + std::string strCmd = GetArg("-alertnotify", ""); + if (!strCmd.empty()) + { + // Alert text should be plain ascii coming from a trusted source, but to + // be safe we first strip anything not in safeChars, then add single quotes around + // the whole string before passing it to the shell: + std::string singleQuote("'"); + std::string safeStatus = SanitizeString(strStatusBar); + safeStatus = singleQuote+safeStatus+singleQuote; + boost::replace_all(strCmd, "%s", safeStatus); + + if (fThread) + boost::thread t(runCommand, strCmd); // thread runs free + else + runCommand(strCmd); + } + } + } + + printf("accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe()); + return true; +} diff --git a/src/alert.h b/src/alert.h new file mode 100644 index 0000000..25e140f --- /dev/null +++ b/src/alert.h @@ -0,0 +1,102 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef _BITCOINALERT_H_ +#define _BITCOINALERT_H_ 1 + +#include +#include + +#include "uint256.h" +#include "util.h" + +class CNode; + +/** Alerts are for notifying old versions if they become too obsolete and + * need to upgrade. The message is displayed in the status bar. + * Alert messages are broadcast as a vector of signed data. Unserializing may + * not read the entire buffer if the alert is for a newer version, but older + * versions can still relay the original data. + */ +class CUnsignedAlert +{ +public: + int nVersion; + int64 nRelayUntil; // when newer nodes stop relaying to newer nodes + int64 nExpiration; + int nID; + int nCancel; + std::set setCancel; + int nMinVer; // lowest version inclusive + int nMaxVer; // highest version inclusive + std::set setSubVer; // empty matches all + int nPriority; + + // Actions + std::string strComment; + std::string strStatusBar; + std::string strReserved; + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(nRelayUntil); + READWRITE(nExpiration); + READWRITE(nID); + READWRITE(nCancel); + READWRITE(setCancel); + READWRITE(nMinVer); + READWRITE(nMaxVer); + READWRITE(setSubVer); + READWRITE(nPriority); + + READWRITE(strComment); + READWRITE(strStatusBar); + READWRITE(strReserved); + ) + + void SetNull(); + + std::string ToString() const; + void print() const; +}; + +/** An alert is a combination of a serialized CUnsignedAlert and a signature. */ +class CAlert : public CUnsignedAlert +{ +public: + std::vector vchMsg; + std::vector vchSig; + + CAlert() + { + SetNull(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(vchMsg); + READWRITE(vchSig); + ) + + void SetNull(); + bool IsNull() const; + uint256 GetHash() const; + bool IsInEffect() const; + bool Cancels(const CAlert& alert) const; + bool AppliesTo(int nVersion, std::string strSubVerIn) const; + bool AppliesToMe() const; + bool RelayTo(CNode* pnode) const; + bool CheckSignature() const; + bool ProcessAlert(bool fThread = true); + + /* + * Get copy of (active) alert object by hash. Returns a null alert if it is not found. + */ + static CAlert getAlertByHash(const uint256 &hash); +}; + +#endif diff --git a/src/allocators.h b/src/allocators.h new file mode 100644 index 0000000..85af8fe --- /dev/null +++ b/src/allocators.h @@ -0,0 +1,271 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_ALLOCATORS_H +#define BITCOIN_ALLOCATORS_H + +#include +#include +#include +#include +#include // for OPENSSL_cleanse() + +#ifdef WIN32 +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0501 +#define WIN32_LEAN_AND_MEAN 1 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +// This is used to attempt to keep keying material out of swap +// Note that VirtualLock does not provide this as a guarantee on Windows, +// but, in practice, memory that has been VirtualLock'd almost never gets written to +// the pagefile except in rare circumstances where memory is extremely low. +#else +#include +#include // for PAGESIZE +#include // for sysconf +#endif + +/** + * Thread-safe class to keep track of locked (ie, non-swappable) memory pages. + * + * Memory locks do not stack, that is, pages which have been locked several times by calls to mlock() + * will be unlocked by a single call to munlock(). This can result in keying material ending up in swap when + * those functions are used naively. This class simulates stacking memory locks by keeping a counter per page. + * + * @note By using a map from each page base address to lock count, this class is optimized for + * small objects that span up to a few pages, mostly smaller than a page. To support large allocations, + * something like an interval tree would be the preferred data structure. + */ +template class LockedPageManagerBase +{ +public: + LockedPageManagerBase(size_t page_size): + page_size(page_size) + { + // Determine bitmask for extracting page from address + assert(!(page_size & (page_size-1))); // size must be power of two + page_mask = ~(page_size - 1); + } + + // For all pages in affected range, increase lock count + void LockRange(void *p, size_t size) + { + boost::mutex::scoped_lock lock(mutex); + if(!size) return; + const size_t base_addr = reinterpret_cast(p); + const size_t start_page = base_addr & page_mask; + const size_t end_page = (base_addr + size - 1) & page_mask; + for(size_t page = start_page; page <= end_page; page += page_size) + { + Histogram::iterator it = histogram.find(page); + if(it == histogram.end()) // Newly locked page + { + locker.Lock(reinterpret_cast(page), page_size); + histogram.insert(std::make_pair(page, 1)); + } + else // Page was already locked; increase counter + { + it->second += 1; + } + } + } + + // For all pages in affected range, decrease lock count + void UnlockRange(void *p, size_t size) + { + boost::mutex::scoped_lock lock(mutex); + if(!size) return; + const size_t base_addr = reinterpret_cast(p); + const size_t start_page = base_addr & page_mask; + const size_t end_page = (base_addr + size - 1) & page_mask; + for(size_t page = start_page; page <= end_page; page += page_size) + { + Histogram::iterator it = histogram.find(page); + assert(it != histogram.end()); // Cannot unlock an area that was not locked + // Decrease counter for page, when it is zero, the page will be unlocked + it->second -= 1; + if(it->second == 0) // Nothing on the page anymore that keeps it locked + { + // Unlock page and remove the count from histogram + locker.Unlock(reinterpret_cast(page), page_size); + histogram.erase(it); + } + } + } + + // Get number of locked pages for diagnostics + int GetLockedPageCount() + { + boost::mutex::scoped_lock lock(mutex); + return histogram.size(); + } + +private: + Locker locker; + boost::mutex mutex; + size_t page_size, page_mask; + // map of page base address to lock count + typedef std::map Histogram; + Histogram histogram; +}; + +/** Determine system page size in bytes */ +static inline size_t GetSystemPageSize() +{ + size_t page_size; +#if defined(WIN32) + SYSTEM_INFO sSysInfo; + GetSystemInfo(&sSysInfo); + page_size = sSysInfo.dwPageSize; +#elif defined(PAGESIZE) // defined in limits.h + page_size = PAGESIZE; +#else // assume some POSIX OS + page_size = sysconf(_SC_PAGESIZE); +#endif + return page_size; +} + +/** + * OS-dependent memory page locking/unlocking. + * Defined as policy class to make stubbing for test possible. + */ +class MemoryPageLocker +{ +public: + /** Lock memory pages. + * addr and len must be a multiple of the system page size + */ + bool Lock(const void *addr, size_t len) + { +#ifdef WIN32 + return VirtualLock(const_cast(addr), len); +#else + return mlock(addr, len) == 0; +#endif + } + /** Unlock memory pages. + * addr and len must be a multiple of the system page size + */ + bool Unlock(const void *addr, size_t len) + { +#ifdef WIN32 + return VirtualUnlock(const_cast(addr), len); +#else + return munlock(addr, len) == 0; +#endif + } +}; + +/** + * Singleton class to keep track of locked (ie, non-swappable) memory pages, for use in + * std::allocator templates. + */ +class LockedPageManager: public LockedPageManagerBase +{ +public: + static LockedPageManager instance; // instantiated in util.cpp +private: + LockedPageManager(): + LockedPageManagerBase(GetSystemPageSize()) + {} +}; + +// +// Functions for directly locking/unlocking memory objects. +// Intended for non-dynamically allocated structures. +// +template void LockObject(const T &t) { + LockedPageManager::instance.LockRange((void*)(&t), sizeof(T)); +} + +template void UnlockObject(const T &t) { + OPENSSL_cleanse((void*)(&t), sizeof(T)); + LockedPageManager::instance.UnlockRange((void*)(&t), sizeof(T)); +} + +// +// Allocator that locks its contents from being paged +// out of memory and clears its contents before deletion. +// +template +struct secure_allocator : public std::allocator +{ + // MSVC8 default copy constructor is broken + typedef std::allocator base; + typedef typename base::size_type size_type; + typedef typename base::difference_type difference_type; + typedef typename base::pointer pointer; + typedef typename base::const_pointer const_pointer; + typedef typename base::reference reference; + typedef typename base::const_reference const_reference; + typedef typename base::value_type value_type; + secure_allocator() throw() {} + secure_allocator(const secure_allocator& a) throw() : base(a) {} + template + secure_allocator(const secure_allocator& a) throw() : base(a) {} + ~secure_allocator() throw() {} + template struct rebind + { typedef secure_allocator<_Other> other; }; + + T* allocate(std::size_t n, const void *hint = 0) + { + T *p; + p = std::allocator::allocate(n, hint); + if (p != NULL) + LockedPageManager::instance.LockRange(p, sizeof(T) * n); + return p; + } + + void deallocate(T* p, std::size_t n) + { + if (p != NULL) + { + OPENSSL_cleanse(p, sizeof(T) * n); + LockedPageManager::instance.UnlockRange(p, sizeof(T) * n); + } + std::allocator::deallocate(p, n); + } +}; + + +// +// Allocator that clears its contents before deletion. +// +template +struct zero_after_free_allocator : public std::allocator +{ + // MSVC8 default copy constructor is broken + typedef std::allocator base; + typedef typename base::size_type size_type; + typedef typename base::difference_type difference_type; + typedef typename base::pointer pointer; + typedef typename base::const_pointer const_pointer; + typedef typename base::reference reference; + typedef typename base::const_reference const_reference; + typedef typename base::value_type value_type; + zero_after_free_allocator() throw() {} + zero_after_free_allocator(const zero_after_free_allocator& a) throw() : base(a) {} + template + zero_after_free_allocator(const zero_after_free_allocator& a) throw() : base(a) {} + ~zero_after_free_allocator() throw() {} + template struct rebind + { typedef zero_after_free_allocator<_Other> other; }; + + void deallocate(T* p, std::size_t n) + { + if (p != NULL) + OPENSSL_cleanse(p, sizeof(T) * n); + std::allocator::deallocate(p, n); + } +}; + +// This is exactly like std::string, but with a custom allocator. +typedef std::basic_string, secure_allocator > SecureString; + +#endif diff --git a/src/base58.h b/src/base58.h new file mode 100644 index 0000000..0c860a5 --- /dev/null +++ b/src/base58.h @@ -0,0 +1,460 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin Developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +// +// Why base-58 instead of standard base-64 encoding? +// - Don't want 0OIl characters that look the same in some fonts and +// could be used to create visually identical looking account numbers. +// - A string with non-luckynumeric characters is not as easily accepted as an account number. +// - E-mail usually won't line-break if there's no punctuation to break at. +// - Double-clicking selects the whole number as one word if it's all luckynumeric. +// +#ifndef BITCOIN_BASE58_H +#define BITCOIN_BASE58_H + +#include +#include + +#include "bignum.h" +#include "key.h" +#include "script.h" +#include "allocators.h" + +static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + +// Encode a byte sequence as a base58-encoded string +inline std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend) +{ + CAutoBN_CTX pctx; + CBigNum bn58 = 58; + CBigNum bn0 = 0; + + // Convert big endian data to little endian + // Extra zero at the end make sure bignum will interpret as a positive number + std::vector vchTmp(pend-pbegin+1, 0); + reverse_copy(pbegin, pend, vchTmp.begin()); + + // Convert little endian data to bignum + CBigNum bn; + bn.setvch(vchTmp); + + // Convert bignum to std::string + std::string str; + // Expected size increase from base58 conversion is approximately 137% + // use 138% to be safe + str.reserve((pend - pbegin) * 138 / 100 + 1); + CBigNum dv; + CBigNum rem; + while (bn > bn0) + { + if (!BN_div(&dv, &rem, &bn, &bn58, pctx)) + throw bignum_error("EncodeBase58 : BN_div failed"); + bn = dv; + unsigned int c = rem.getulong(); + str += pszBase58[c]; + } + + // Leading zeroes encoded as base58 zeros + for (const unsigned char* p = pbegin; p < pend && *p == 0; p++) + str += pszBase58[0]; + + // Convert little endian std::string to big endian + reverse(str.begin(), str.end()); + return str; +} + +// Encode a byte vector as a base58-encoded string +inline std::string EncodeBase58(const std::vector& vch) +{ + return EncodeBase58(&vch[0], &vch[0] + vch.size()); +} + +// Decode a base58-encoded string psz into byte vector vchRet +// returns true if decoding is successful +inline bool DecodeBase58(const char* psz, std::vector& vchRet) +{ + CAutoBN_CTX pctx; + vchRet.clear(); + CBigNum bn58 = 58; + CBigNum bn = 0; + CBigNum bnChar; + while (isspace(*psz)) + psz++; + + // Convert big endian string to bignum + for (const char* p = psz; *p; p++) + { + const char* p1 = strchr(pszBase58, *p); + if (p1 == NULL) + { + while (isspace(*p)) + p++; + if (*p != '\0') + return false; + break; + } + bnChar.setulong(p1 - pszBase58); + if (!BN_mul(&bn, &bn, &bn58, pctx)) + throw bignum_error("DecodeBase58 : BN_mul failed"); + bn += bnChar; + } + + // Get bignum as little endian data + std::vector vchTmp = bn.getvch(); + + // Trim off sign byte if present + if (vchTmp.size() >= 2 && vchTmp.end()[-1] == 0 && vchTmp.end()[-2] >= 0x80) + vchTmp.erase(vchTmp.end()-1); + + // Restore leading zeros + int nLeadingZeros = 0; + for (const char* p = psz; *p == pszBase58[0]; p++) + nLeadingZeros++; + vchRet.assign(nLeadingZeros + vchTmp.size(), 0); + + // Convert little endian data to big endian + reverse_copy(vchTmp.begin(), vchTmp.end(), vchRet.end() - vchTmp.size()); + return true; +} + +// Decode a base58-encoded string str into byte vector vchRet +// returns true if decoding is successful +inline bool DecodeBase58(const std::string& str, std::vector& vchRet) +{ + return DecodeBase58(str.c_str(), vchRet); +} + + + + +// Encode a byte vector to a base58-encoded string, including checksum +inline std::string EncodeBase58Check(const std::vector& vchIn) +{ + // add 4-byte hash check to the end + std::vector vch(vchIn); + uint256 hash = Hash(vch.begin(), vch.end()); + vch.insert(vch.end(), (unsigned char*)&hash, (unsigned char*)&hash + 4); + return EncodeBase58(vch); +} + +// Decode a base58-encoded string psz that includes a checksum, into byte vector vchRet +// returns true if decoding is successful +inline bool DecodeBase58Check(const char* psz, std::vector& vchRet) +{ + if (!DecodeBase58(psz, vchRet)) + return false; + if (vchRet.size() < 4) + { + vchRet.clear(); + return false; + } + uint256 hash = Hash(vchRet.begin(), vchRet.end()-4); + if (memcmp(&hash, &vchRet.end()[-4], 4) != 0) + { + vchRet.clear(); + return false; + } + vchRet.resize(vchRet.size()-4); + return true; +} + +// Decode a base58-encoded string str that includes a checksum, into byte vector vchRet +// returns true if decoding is successful +inline bool DecodeBase58Check(const std::string& str, std::vector& vchRet) +{ + return DecodeBase58Check(str.c_str(), vchRet); +} + + + + + +/** Base class for all base58-encoded data */ +class CBase58Data +{ +protected: + // the version byte + unsigned char nVersion; + + // the actually encoded data + typedef std::vector > vector_uchar; + vector_uchar vchData; + + CBase58Data() + { + nVersion = 0; + vchData.clear(); + } + + void SetData(int nVersionIn, const void* pdata, size_t nSize) + { + nVersion = nVersionIn; + vchData.resize(nSize); + if (!vchData.empty()) + memcpy(&vchData[0], pdata, nSize); + } + + void SetData(int nVersionIn, const unsigned char *pbegin, const unsigned char *pend) + { + SetData(nVersionIn, (void*)pbegin, pend - pbegin); + } + +public: + bool SetString(const char* psz) + { + std::vector vchTemp; + DecodeBase58Check(psz, vchTemp); + if (vchTemp.empty()) + { + vchData.clear(); + nVersion = 0; + return false; + } + nVersion = vchTemp[0]; + vchData.resize(vchTemp.size() - 1); + if (!vchData.empty()) + memcpy(&vchData[0], &vchTemp[1], vchData.size()); + OPENSSL_cleanse(&vchTemp[0], vchData.size()); + return true; + } + + bool SetString(const std::string& str) + { + return SetString(str.c_str()); + } + + std::string ToString() const + { + std::vector vch(1, nVersion); + vch.insert(vch.end(), vchData.begin(), vchData.end()); + return EncodeBase58Check(vch); + } + + int CompareTo(const CBase58Data& b58) const + { + if (nVersion < b58.nVersion) return -1; + if (nVersion > b58.nVersion) return 1; + if (vchData < b58.vchData) return -1; + if (vchData > b58.vchData) return 1; + return 0; + } + + bool operator==(const CBase58Data& b58) const { return CompareTo(b58) == 0; } + bool operator<=(const CBase58Data& b58) const { return CompareTo(b58) <= 0; } + bool operator>=(const CBase58Data& b58) const { return CompareTo(b58) >= 0; } + bool operator< (const CBase58Data& b58) const { return CompareTo(b58) < 0; } + bool operator> (const CBase58Data& b58) const { return CompareTo(b58) > 0; } +}; + +/** base58-encoded Bitcoin addresses. + * Public-key-hash-addresses have version 0 (or 111 testnet). + * The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. + * Script-hash-addresses have version 5 (or 196 testnet). + * The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. + */ +class CBitcoinAddress; +class CBitcoinAddressVisitor : public boost::static_visitor +{ +private: + CBitcoinAddress *addr; +public: + CBitcoinAddressVisitor(CBitcoinAddress *addrIn) : addr(addrIn) { } + bool operator()(const CKeyID &id) const; + bool operator()(const CScriptID &id) const; + bool operator()(const CNoDestination &no) const; +}; + +class CBitcoinAddress : public CBase58Data +{ +public: + enum + { + PUBKEY_ADDRESS = 28, + SCRIPT_ADDRESS = 5, + PUBKEY_ADDRESS_TEST = 112, + SCRIPT_ADDRESS_TEST = 195, + }; + + bool Set(const CKeyID &id) { + SetData(fTestNet ? PUBKEY_ADDRESS_TEST : PUBKEY_ADDRESS, &id, 20); + return true; + } + + bool Set(const CScriptID &id) { + SetData(fTestNet ? SCRIPT_ADDRESS_TEST : SCRIPT_ADDRESS, &id, 20); + return true; + } + + bool Set(const CTxDestination &dest) + { + return boost::apply_visitor(CBitcoinAddressVisitor(this), dest); + } + + bool IsValid() const + { + unsigned int nExpectedSize = 20; + bool fExpectTestNet = false; + switch(nVersion) + { + case PUBKEY_ADDRESS: + nExpectedSize = 20; // Hash of public key + fExpectTestNet = false; + break; + case SCRIPT_ADDRESS: + nExpectedSize = 20; // Hash of CScript + fExpectTestNet = false; + break; + + case PUBKEY_ADDRESS_TEST: + nExpectedSize = 20; + fExpectTestNet = true; + break; + case SCRIPT_ADDRESS_TEST: + nExpectedSize = 20; + fExpectTestNet = true; + break; + + default: + return false; + } + return fExpectTestNet == fTestNet && vchData.size() == nExpectedSize; + } + + CBitcoinAddress() + { + } + + CBitcoinAddress(const CTxDestination &dest) + { + Set(dest); + } + + CBitcoinAddress(const std::string& strAddress) + { + SetString(strAddress); + } + + CBitcoinAddress(const char* pszAddress) + { + SetString(pszAddress); + } + + CTxDestination Get() const { + if (!IsValid()) + return CNoDestination(); + switch (nVersion) { + case PUBKEY_ADDRESS: + case PUBKEY_ADDRESS_TEST: { + uint160 id; + memcpy(&id, &vchData[0], 20); + return CKeyID(id); + } + case SCRIPT_ADDRESS: + case SCRIPT_ADDRESS_TEST: { + uint160 id; + memcpy(&id, &vchData[0], 20); + return CScriptID(id); + } + } + return CNoDestination(); + } + + bool GetKeyID(CKeyID &keyID) const { + if (!IsValid()) + return false; + switch (nVersion) { + case PUBKEY_ADDRESS: + case PUBKEY_ADDRESS_TEST: { + uint160 id; + memcpy(&id, &vchData[0], 20); + keyID = CKeyID(id); + return true; + } + default: return false; + } + } + + bool IsScript() const { + if (!IsValid()) + return false; + switch (nVersion) { + case SCRIPT_ADDRESS: + case SCRIPT_ADDRESS_TEST: { + return true; + } + default: return false; + } + } +}; + +bool inline CBitcoinAddressVisitor::operator()(const CKeyID &id) const { return addr->Set(id); } +bool inline CBitcoinAddressVisitor::operator()(const CScriptID &id) const { return addr->Set(id); } +bool inline CBitcoinAddressVisitor::operator()(const CNoDestination &id) const { return false; } + +/** A base58-encoded secret key */ +class CBitcoinSecret : public CBase58Data +{ +public: + enum + { + PRIVKEY_ADDRESS = CBitcoinAddress::PUBKEY_ADDRESS + 128, + PRIVKEY_ADDRESS_TEST = CBitcoinAddress::PUBKEY_ADDRESS_TEST + 128, + }; + + void SetKey(const CKey& vchSecret) + { + assert(vchSecret.IsValid()); + SetData(fTestNet ? PRIVKEY_ADDRESS_TEST : PRIVKEY_ADDRESS, vchSecret.begin(), vchSecret.size()); + if (vchSecret.IsCompressed()) + vchData.push_back(1); + } + + CKey GetKey() + { + CKey ret; + ret.Set(&vchData[0], &vchData[32], vchData.size() > 32 && vchData[32] == 1); + return ret; + } + + bool IsValid() const + { + bool fExpectTestNet = false; + switch(nVersion) + { + case PRIVKEY_ADDRESS: + break; + + case PRIVKEY_ADDRESS_TEST: + fExpectTestNet = true; + break; + + default: + return false; + } + return fExpectTestNet == fTestNet && (vchData.size() == 32 || (vchData.size() == 33 && vchData[32] == 1)); + } + + bool SetString(const char* pszSecret) + { + return CBase58Data::SetString(pszSecret) && IsValid(); + } + + bool SetString(const std::string& strSecret) + { + return SetString(strSecret.c_str()); + } + + CBitcoinSecret(const CKey& vchSecret) + { + SetKey(vchSecret); + } + + CBitcoinSecret() + { + } +}; + +#endif // BITCOIN_BASE58_H diff --git a/src/bignum.h b/src/bignum.h new file mode 100644 index 0000000..0881807 --- /dev/null +++ b/src/bignum.h @@ -0,0 +1,590 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_BIGNUM_H +#define BITCOIN_BIGNUM_H + +#include +#include +#include + +#include "util.h" // for uint64 + +/** Errors thrown by the bignum class */ +class bignum_error : public std::runtime_error +{ +public: + explicit bignum_error(const std::string& str) : std::runtime_error(str) {} +}; + + +/** RAII encapsulated BN_CTX (OpenSSL bignum context) */ +class CAutoBN_CTX +{ +protected: + BN_CTX* pctx; + BN_CTX* operator=(BN_CTX* pnew) { return pctx = pnew; } + +public: + CAutoBN_CTX() + { + pctx = BN_CTX_new(); + if (pctx == NULL) + throw bignum_error("CAutoBN_CTX : BN_CTX_new() returned NULL"); + } + + ~CAutoBN_CTX() + { + if (pctx != NULL) + BN_CTX_free(pctx); + } + + operator BN_CTX*() { return pctx; } + BN_CTX& operator*() { return *pctx; } + BN_CTX** operator&() { return &pctx; } + bool operator!() { return (pctx == NULL); } +}; + + +/** C++ wrapper for BIGNUM (OpenSSL bignum) */ +class CBigNum : public BIGNUM +{ +public: + CBigNum() + { + BN_init(this); + } + + CBigNum(const CBigNum& b) + { + BN_init(this); + if (!BN_copy(this, &b)) + { + BN_clear_free(this); + throw bignum_error("CBigNum::CBigNum(const CBigNum&) : BN_copy failed"); + } + } + + CBigNum& operator=(const CBigNum& b) + { + if (!BN_copy(this, &b)) + throw bignum_error("CBigNum::operator= : BN_copy failed"); + return (*this); + } + + ~CBigNum() + { + BN_clear_free(this); + } + + //CBigNum(char n) is not portable. Use 'signed char' or 'unsigned char'. + CBigNum(signed char n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(short n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(int n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(long n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(int64 n) { BN_init(this); setint64(n); } + CBigNum(unsigned char n) { BN_init(this); setulong(n); } + CBigNum(unsigned short n) { BN_init(this); setulong(n); } + CBigNum(unsigned int n) { BN_init(this); setulong(n); } + CBigNum(unsigned long n) { BN_init(this); setulong(n); } + CBigNum(uint64 n) { BN_init(this); setuint64(n); } + explicit CBigNum(uint256 n) { BN_init(this); setuint256(n); } + + explicit CBigNum(const std::vector& vch) + { + BN_init(this); + setvch(vch); + } + + void setulong(unsigned long n) + { + if (!BN_set_word(this, n)) + throw bignum_error("CBigNum conversion from unsigned long : BN_set_word failed"); + } + + unsigned long getulong() const + { + return BN_get_word(this); + } + + unsigned int getuint() const + { + return BN_get_word(this); + } + + int getint() const + { + unsigned long n = BN_get_word(this); + if (!BN_is_negative(this)) + return (n > (unsigned long)std::numeric_limits::max() ? std::numeric_limits::max() : n); + else + return (n > (unsigned long)std::numeric_limits::max() ? std::numeric_limits::min() : -(int)n); + } + + void setint64(int64 sn) + { + unsigned char pch[sizeof(sn) + 6]; + unsigned char* p = pch + 4; + bool fNegative; + uint64 n; + + if (sn < (int64)0) + { + // Since the minimum signed integer cannot be represented as positive so long as its type is signed, + // and it's not well-defined what happens if you make it unsigned before negating it, + // we instead increment the negative integer by 1, convert it, then increment the (now positive) unsigned integer by 1 to compensate + n = -(sn + 1); + ++n; + fNegative = true; + } else { + n = sn; + fNegative = false; + } + + bool fLeadingZeroes = true; + for (int i = 0; i < 8; i++) + { + unsigned char c = (n >> 56) & 0xff; + n <<= 8; + if (fLeadingZeroes) + { + if (c == 0) + continue; + if (c & 0x80) + *p++ = (fNegative ? 0x80 : 0); + else if (fNegative) + c |= 0x80; + fLeadingZeroes = false; + } + *p++ = c; + } + unsigned int nSize = p - (pch + 4); + pch[0] = (nSize >> 24) & 0xff; + pch[1] = (nSize >> 16) & 0xff; + pch[2] = (nSize >> 8) & 0xff; + pch[3] = (nSize) & 0xff; + BN_mpi2bn(pch, p - pch, this); + } + + void setuint64(uint64 n) + { + unsigned char pch[sizeof(n) + 6]; + unsigned char* p = pch + 4; + bool fLeadingZeroes = true; + for (int i = 0; i < 8; i++) + { + unsigned char c = (n >> 56) & 0xff; + n <<= 8; + if (fLeadingZeroes) + { + if (c == 0) + continue; + if (c & 0x80) + *p++ = 0; + fLeadingZeroes = false; + } + *p++ = c; + } + unsigned int nSize = p - (pch + 4); + pch[0] = (nSize >> 24) & 0xff; + pch[1] = (nSize >> 16) & 0xff; + pch[2] = (nSize >> 8) & 0xff; + pch[3] = (nSize) & 0xff; + BN_mpi2bn(pch, p - pch, this); + } + + void setuint256(uint256 n) + { + unsigned char pch[sizeof(n) + 6]; + unsigned char* p = pch + 4; + bool fLeadingZeroes = true; + unsigned char* pbegin = (unsigned char*)&n; + unsigned char* psrc = pbegin + sizeof(n); + while (psrc != pbegin) + { + unsigned char c = *(--psrc); + if (fLeadingZeroes) + { + if (c == 0) + continue; + if (c & 0x80) + *p++ = 0; + fLeadingZeroes = false; + } + *p++ = c; + } + unsigned int nSize = p - (pch + 4); + pch[0] = (nSize >> 24) & 0xff; + pch[1] = (nSize >> 16) & 0xff; + pch[2] = (nSize >> 8) & 0xff; + pch[3] = (nSize >> 0) & 0xff; + BN_mpi2bn(pch, p - pch, this); + } + + uint256 getuint256() const + { + unsigned int nSize = BN_bn2mpi(this, NULL); + if (nSize < 4) + return 0; + std::vector vch(nSize); + BN_bn2mpi(this, &vch[0]); + if (vch.size() > 4) + vch[4] &= 0x7f; + uint256 n = 0; + for (unsigned int i = 0, j = vch.size()-1; i < sizeof(n) && j >= 4; i++, j--) + ((unsigned char*)&n)[i] = vch[j]; + return n; + } + + void setvch(const std::vector& vch) + { + std::vector vch2(vch.size() + 4); + unsigned int nSize = vch.size(); + // BIGNUM's byte stream format expects 4 bytes of + // big endian size data info at the front + vch2[0] = (nSize >> 24) & 0xff; + vch2[1] = (nSize >> 16) & 0xff; + vch2[2] = (nSize >> 8) & 0xff; + vch2[3] = (nSize >> 0) & 0xff; + // swap data to big endian + reverse_copy(vch.begin(), vch.end(), vch2.begin() + 4); + BN_mpi2bn(&vch2[0], vch2.size(), this); + } + + std::vector getvch() const + { + unsigned int nSize = BN_bn2mpi(this, NULL); + if (nSize <= 4) + return std::vector(); + std::vector vch(nSize); + BN_bn2mpi(this, &vch[0]); + vch.erase(vch.begin(), vch.begin() + 4); + reverse(vch.begin(), vch.end()); + return vch; + } + + // The "compact" format is a representation of a whole + // number N using an unsigned 32bit number similar to a + // floating point format. + // The most significant 8 bits are the unsigned exponent of base 256. + // This exponent can be thought of as "number of bytes of N". + // The lower 23 bits are the mantissa. + // Bit number 24 (0x800000) represents the sign of N. + // N = (-1^sign) * mantissa * 256^(exponent-3) + // + // Satoshi's original implementation used BN_bn2mpi() and BN_mpi2bn(). + // MPI uses the most significant bit of the first byte as sign. + // Thus 0x1234560000 is compact (0x05123456) + // and 0xc0de000000 is compact (0x0600c0de) + // (0x05c0de00) would be -0x40de000000 + // + // Bitcoin only uses this "compact" format for encoding difficulty + // targets, which are unsigned 256bit quantities. Thus, all the + // complexities of the sign bit and using base 256 are probably an + // implementation accident. + // + // This implementation directly uses shifts instead of going + // through an intermediate MPI representation. + CBigNum& SetCompact(unsigned int nCompact) + { + unsigned int nSize = nCompact >> 24; + bool fNegative =(nCompact & 0x00800000) != 0; + unsigned int nWord = nCompact & 0x007fffff; + if (nSize <= 3) + { + nWord >>= 8*(3-nSize); + BN_set_word(this, nWord); + } + else + { + BN_set_word(this, nWord); + BN_lshift(this, this, 8*(nSize-3)); + } + BN_set_negative(this, fNegative); + return *this; + } + + unsigned int GetCompact() const + { + unsigned int nSize = BN_num_bytes(this); + unsigned int nCompact = 0; + if (nSize <= 3) + nCompact = BN_get_word(this) << 8*(3-nSize); + else + { + CBigNum bn; + BN_rshift(&bn, this, 8*(nSize-3)); + nCompact = BN_get_word(&bn); + } + // The 0x00800000 bit denotes the sign. + // Thus, if it is already set, divide the mantissa by 256 and increase the exponent. + if (nCompact & 0x00800000) + { + nCompact >>= 8; + nSize++; + } + nCompact |= nSize << 24; + nCompact |= (BN_is_negative(this) ? 0x00800000 : 0); + return nCompact; + } + + void SetHex(const std::string& str) + { + // skip 0x + const char* psz = str.c_str(); + while (isspace(*psz)) + psz++; + bool fNegative = false; + if (*psz == '-') + { + fNegative = true; + psz++; + } + if (psz[0] == '0' && tolower(psz[1]) == 'x') + psz += 2; + while (isspace(*psz)) + psz++; + + // hex string to bignum + static const signed char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 }; + *this = 0; + while (isxdigit(*psz)) + { + *this <<= 4; + int n = phexdigit[(unsigned char)*psz++]; + *this += n; + } + if (fNegative) + *this = 0 - *this; + } + + std::string ToString(int nBase=10) const + { + CAutoBN_CTX pctx; + CBigNum bnBase = nBase; + CBigNum bn0 = 0; + std::string str; + CBigNum bn = *this; + BN_set_negative(&bn, false); + CBigNum dv; + CBigNum rem; + if (BN_cmp(&bn, &bn0) == 0) + return "0"; + while (BN_cmp(&bn, &bn0) > 0) + { + if (!BN_div(&dv, &rem, &bn, &bnBase, pctx)) + throw bignum_error("CBigNum::ToString() : BN_div failed"); + bn = dv; + unsigned int c = rem.getulong(); + str += "0123456789abcdef"[c]; + } + if (BN_is_negative(this)) + str += "-"; + reverse(str.begin(), str.end()); + return str; + } + + std::string GetHex() const + { + return ToString(16); + } + + unsigned int GetSerializeSize(int nType=0, int nVersion=PROTOCOL_VERSION) const + { + return ::GetSerializeSize(getvch(), nType, nVersion); + } + + template + void Serialize(Stream& s, int nType=0, int nVersion=PROTOCOL_VERSION) const + { + ::Serialize(s, getvch(), nType, nVersion); + } + + template + void Unserialize(Stream& s, int nType=0, int nVersion=PROTOCOL_VERSION) + { + std::vector vch; + ::Unserialize(s, vch, nType, nVersion); + setvch(vch); + } + + + bool operator!() const + { + return BN_is_zero(this); + } + + CBigNum& operator+=(const CBigNum& b) + { + if (!BN_add(this, this, &b)) + throw bignum_error("CBigNum::operator+= : BN_add failed"); + return *this; + } + + CBigNum& operator-=(const CBigNum& b) + { + *this = *this - b; + return *this; + } + + CBigNum& operator*=(const CBigNum& b) + { + CAutoBN_CTX pctx; + if (!BN_mul(this, this, &b, pctx)) + throw bignum_error("CBigNum::operator*= : BN_mul failed"); + return *this; + } + + CBigNum& operator/=(const CBigNum& b) + { + *this = *this / b; + return *this; + } + + CBigNum& operator%=(const CBigNum& b) + { + *this = *this % b; + return *this; + } + + CBigNum& operator<<=(unsigned int shift) + { + if (!BN_lshift(this, this, shift)) + throw bignum_error("CBigNum:operator<<= : BN_lshift failed"); + return *this; + } + + CBigNum& operator>>=(unsigned int shift) + { + // Note: BN_rshift segfaults on 64-bit if 2^shift is greater than the number + // if built on ubuntu 9.04 or 9.10, probably depends on version of OpenSSL + CBigNum a = 1; + a <<= shift; + if (BN_cmp(&a, this) > 0) + { + *this = 0; + return *this; + } + + if (!BN_rshift(this, this, shift)) + throw bignum_error("CBigNum:operator>>= : BN_rshift failed"); + return *this; + } + + + CBigNum& operator++() + { + // prefix operator + if (!BN_add(this, this, BN_value_one())) + throw bignum_error("CBigNum::operator++ : BN_add failed"); + return *this; + } + + const CBigNum operator++(int) + { + // postfix operator + const CBigNum ret = *this; + ++(*this); + return ret; + } + + CBigNum& operator--() + { + // prefix operator + CBigNum r; + if (!BN_sub(&r, this, BN_value_one())) + throw bignum_error("CBigNum::operator-- : BN_sub failed"); + *this = r; + return *this; + } + + const CBigNum operator--(int) + { + // postfix operator + const CBigNum ret = *this; + --(*this); + return ret; + } + + + friend inline const CBigNum operator-(const CBigNum& a, const CBigNum& b); + friend inline const CBigNum operator/(const CBigNum& a, const CBigNum& b); + friend inline const CBigNum operator%(const CBigNum& a, const CBigNum& b); +}; + + + +inline const CBigNum operator+(const CBigNum& a, const CBigNum& b) +{ + CBigNum r; + if (!BN_add(&r, &a, &b)) + throw bignum_error("CBigNum::operator+ : BN_add failed"); + return r; +} + +inline const CBigNum operator-(const CBigNum& a, const CBigNum& b) +{ + CBigNum r; + if (!BN_sub(&r, &a, &b)) + throw bignum_error("CBigNum::operator- : BN_sub failed"); + return r; +} + +inline const CBigNum operator-(const CBigNum& a) +{ + CBigNum r(a); + BN_set_negative(&r, !BN_is_negative(&r)); + return r; +} + +inline const CBigNum operator*(const CBigNum& a, const CBigNum& b) +{ + CAutoBN_CTX pctx; + CBigNum r; + if (!BN_mul(&r, &a, &b, pctx)) + throw bignum_error("CBigNum::operator* : BN_mul failed"); + return r; +} + +inline const CBigNum operator/(const CBigNum& a, const CBigNum& b) +{ + CAutoBN_CTX pctx; + CBigNum r; + if (!BN_div(&r, NULL, &a, &b, pctx)) + throw bignum_error("CBigNum::operator/ : BN_div failed"); + return r; +} + +inline const CBigNum operator%(const CBigNum& a, const CBigNum& b) +{ + CAutoBN_CTX pctx; + CBigNum r; + if (!BN_mod(&r, &a, &b, pctx)) + throw bignum_error("CBigNum::operator% : BN_div failed"); + return r; +} + +inline const CBigNum operator<<(const CBigNum& a, unsigned int shift) +{ + CBigNum r; + if (!BN_lshift(&r, &a, shift)) + throw bignum_error("CBigNum:operator<< : BN_lshift failed"); + return r; +} + +inline const CBigNum operator>>(const CBigNum& a, unsigned int shift) +{ + CBigNum r = a; + r >>= shift; + return r; +} + +inline bool operator==(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) == 0); } +inline bool operator!=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) != 0); } +inline bool operator<=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) <= 0); } +inline bool operator>=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) >= 0); } +inline bool operator<(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) < 0); } +inline bool operator>(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) > 0); } + +#endif diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp new file mode 100644 index 0000000..f9a1387 --- /dev/null +++ b/src/bitcoinrpc.cpp @@ -0,0 +1,1306 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "init.h" +#include "util.h" +#include "sync.h" +#include "ui_interface.h" +#include "base58.h" +#include "bitcoinrpc.h" +#include "db.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace boost; +using namespace boost::asio; + +using namespace json_spirit; + +static std::string strRPCUserColonPass; + +// These are created by StartRPCThreads, destroyed in StopRPCThreads +static asio::io_service* rpc_io_service = NULL; +static ssl::context* rpc_ssl_context = NULL; +static boost::thread_group* rpc_worker_group = NULL; + +static inline unsigned short GetDefaultRPCPort() +{ + return GetBoolArg("-testnet", false) ? 19154 : 9154; +} + +Object JSONRPCError(int code, const string& message) +{ + Object error; + error.push_back(Pair("code", code)); + error.push_back(Pair("message", message)); + return error; +} + +void RPCTypeCheck(const Array& params, + const list& typesExpected, + bool fAllowNull) +{ + unsigned int i = 0; + BOOST_FOREACH(Value_type t, typesExpected) + { + if (params.size() <= i) + break; + + const Value& v = params[i]; + if (!((v.type() == t) || (fAllowNull && (v.type() == null_type)))) + { + string err = strprintf("Expected type %s, got %s", + Value_type_name[t], Value_type_name[v.type()]); + throw JSONRPCError(RPC_TYPE_ERROR, err); + } + i++; + } +} + +void RPCTypeCheck(const Object& o, + const map& typesExpected, + bool fAllowNull) +{ + BOOST_FOREACH(const PAIRTYPE(string, Value_type)& t, typesExpected) + { + const Value& v = find_value(o, t.first); + if (!fAllowNull && v.type() == null_type) + throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first.c_str())); + + if (!((v.type() == t.second) || (fAllowNull && (v.type() == null_type)))) + { + string err = strprintf("Expected type %s for %s, got %s", + Value_type_name[t.second], t.first.c_str(), Value_type_name[v.type()]); + throw JSONRPCError(RPC_TYPE_ERROR, err); + } + } +} + +int64 AmountFromValue(const Value& value) +{ + double dAmount = value.get_real(); + if (dAmount <= 0.0 || dAmount > 5000000000.0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); + int64 nAmount = roundint64(dAmount * COIN); + if (!MoneyRange(nAmount)) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); + return nAmount; +} + +Value ValueFromAmount(int64 amount) +{ + return (double)amount / (double)COIN; +} + +std::string HexBits(unsigned int nBits) +{ + union { + int32_t nBits; + char cBits[4]; + } uBits; + uBits.nBits = htonl((int32_t)nBits); + return HexStr(BEGIN(uBits.cBits), END(uBits.cBits)); +} + + + +/// +/// Note: This interface may still be subject to change. +/// + +string CRPCTable::help(string strCommand) const +{ + string strRet; + set setDone; + for (map::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi) + { + const CRPCCommand *pcmd = mi->second; + string strMethod = mi->first; + // We already filter duplicates, but these deprecated screw up the sort order + if (strMethod.find("label") != string::npos) + continue; + if (strCommand != "" && strMethod != strCommand) + continue; + if (pcmd->reqWallet && !pwalletMain) + continue; + + try + { + Array params; + rpcfn_type pfn = pcmd->actor; + if (setDone.insert(pfn).second) + (*pfn)(params, true); + } + catch (std::exception& e) + { + // Help text is returned in an exception + string strHelp = string(e.what()); + if (strCommand == "") + if (strHelp.find('\n') != string::npos) + strHelp = strHelp.substr(0, strHelp.find('\n')); + strRet += strHelp + "\n"; + } + } + if (strRet == "") + strRet = strprintf("help: unknown command: %s\n", strCommand.c_str()); + strRet = strRet.substr(0,strRet.size()-1); + return strRet; +} + +Value help(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "help [command]\n" + "List commands, or get help for a command."); + + string strCommand; + if (params.size() > 0) + strCommand = params[0].get_str(); + + return tableRPC.help(strCommand); +} + + +Value stop(const Array& params, bool fHelp) +{ + // Accept the deprecated and ignored 'detach' boolean argument + if (fHelp || params.size() > 1) + throw runtime_error( + "stop\n" + "Stop CryptoLottoToken server."); + // Shutdown will take long enough that the response should get back + StartShutdown(); + return "CryptoLottoToken server stopping"; +} + + + +// +// Call Table +// + + +static const CRPCCommand vRPCCommands[] = +{ // name actor (function) okSafeMode threadSafe reqWallet + // ------------------------ ----------------------- ---------- ---------- --------- + { "help", &help, true, true, false }, + { "stop", &stop, true, true, false }, + { "getblockcount", &getblockcount, true, false, false }, + { "getbestblockhash", &getbestblockhash, true, false, false }, + { "getconnectioncount", &getconnectioncount, true, false, false }, + { "getpeerinfo", &getpeerinfo, true, false, false }, + { "addnode", &addnode, true, true, false }, + { "getaddednodeinfo", &getaddednodeinfo, true, true, false }, + { "getdifficulty", &getdifficulty, true, false, false }, + { "getnetworkhashps", &getnetworkhashps, true, false, false }, + { "getgenerate", &getgenerate, true, false, false }, + { "setgenerate", &setgenerate, true, false, true }, + { "gethashespersec", &gethashespersec, true, false, false }, + { "getinfo", &getinfo, true, false, false }, + { "getmininginfo", &getmininginfo, true, false, false }, + { "getnewaddress", &getnewaddress, true, false, true }, + { "getaccountaddress", &getaccountaddress, true, false, true }, + { "setaccount", &setaccount, true, false, true }, + { "getaccount", &getaccount, false, false, true }, + { "getaddressesbyaccount", &getaddressesbyaccount, true, false, true }, + { "sendtoaddress", &sendtoaddress, false, false, true }, + { "getreceivedbyaddress", &getreceivedbyaddress, false, false, true }, + { "getreceivedbyaccount", &getreceivedbyaccount, false, false, true }, + { "listreceivedbyaddress", &listreceivedbyaddress, false, false, true }, + { "listreceivedbyaccount", &listreceivedbyaccount, false, false, true }, + { "backupwallet", &backupwallet, true, false, true }, + { "keypoolrefill", &keypoolrefill, true, false, true }, + { "walletpassphrase", &walletpassphrase, true, false, true }, + { "walletpassphrasechange", &walletpassphrasechange, false, false, true }, + { "walletlock", &walletlock, true, false, true }, + { "encryptwallet", &encryptwallet, false, false, true }, + { "validateaddress", &validateaddress, true, false, false }, + { "getbalance", &getbalance, false, false, true }, + { "move", &movecmd, false, false, true }, + { "sendfrom", &sendfrom, false, false, true }, + { "sendmany", &sendmany, false, false, true }, + { "addmultisigaddress", &addmultisigaddress, false, false, true }, + { "createmultisig", &createmultisig, true, true , false }, + { "getrawmempool", &getrawmempool, true, false, false }, + { "getblock", &getblock, false, false, false }, + { "getblockhash", &getblockhash, false, false, false }, + { "gettransaction", &gettransaction, false, false, true }, + { "listtransactions", &listtransactions, false, false, true }, + { "listaddressgroupings", &listaddressgroupings, false, false, true }, + { "signmessage", &signmessage, false, false, true }, + { "verifymessage", &verifymessage, false, false, false }, + { "getwork", &getwork, true, false, true }, + { "getworkex", &getworkex, true, false, true }, + { "listaccounts", &listaccounts, false, false, true }, + { "settxfee", &settxfee, false, false, true }, + { "getblocktemplate", &getblocktemplate, true, false, false }, + { "submitblock", &submitblock, false, false, false }, + { "setmininput", &setmininput, false, false, false }, + { "listsinceblock", &listsinceblock, false, false, true }, + { "dumpprivkey", &dumpprivkey, true, false, true }, + { "importprivkey", &importprivkey, false, false, true }, + { "listunspent", &listunspent, false, false, true }, + { "getrawtransaction", &getrawtransaction, false, false, false }, + { "createrawtransaction", &createrawtransaction, false, false, false }, + { "decoderawtransaction", &decoderawtransaction, false, false, false }, + { "signrawtransaction", &signrawtransaction, false, false, false }, + { "sendrawtransaction", &sendrawtransaction, false, false, false }, + { "gettxoutsetinfo", &gettxoutsetinfo, true, false, false }, + { "gettxout", &gettxout, true, false, false }, + { "lockunspent", &lockunspent, false, false, true }, + { "listlockunspent", &listlockunspent, false, false, true }, + { "verifychain", &verifychain, true, false, false }, +}; + +CRPCTable::CRPCTable() +{ + unsigned int vcidx; + for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++) + { + const CRPCCommand *pcmd; + + pcmd = &vRPCCommands[vcidx]; + mapCommands[pcmd->name] = pcmd; + } +} + +const CRPCCommand *CRPCTable::operator[](string name) const +{ + map::const_iterator it = mapCommands.find(name); + if (it == mapCommands.end()) + return NULL; + return (*it).second; +} + +// +// HTTP protocol +// +// This ain't Apache. We're just using HTTP header for the length field +// and to be compatible with other JSON-RPC implementations. +// + +string HTTPPost(const string& strMsg, const map& mapRequestHeaders) +{ + ostringstream s; + s << "POST / HTTP/1.1\r\n" + << "User-Agent: CryptoLottoToken-json-rpc/" << FormatFullVersion() << "\r\n" + << "Host: 127.0.0.1\r\n" + << "Content-Type: application/json\r\n" + << "Content-Length: " << strMsg.size() << "\r\n" + << "Connection: close\r\n" + << "Accept: application/json\r\n"; + BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders) + s << item.first << ": " << item.second << "\r\n"; + s << "\r\n" << strMsg; + + return s.str(); +} + +string rfc1123Time() +{ + char buffer[64]; + time_t now; + time(&now); + struct tm* now_gmt = gmtime(&now); + string locale(setlocale(LC_TIME, NULL)); + setlocale(LC_TIME, "C"); // we want POSIX (aka "C") weekday/month strings + strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt); + setlocale(LC_TIME, locale.c_str()); + return string(buffer); +} + +static string HTTPReply(int nStatus, const string& strMsg, bool keepalive) +{ + if (nStatus == HTTP_UNAUTHORIZED) + return strprintf("HTTP/1.0 401 Authorization Required\r\n" + "Date: %s\r\n" + "Server: CryptoLottoToken-json-rpc/%s\r\n" + "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 296\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Error\r\n" + "\r\n" + "\r\n" + "

401 Unauthorized.

\r\n" + "\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str()); + const char *cStatus; + if (nStatus == HTTP_OK) cStatus = "OK"; + else if (nStatus == HTTP_BAD_REQUEST) cStatus = "Bad Request"; + else if (nStatus == HTTP_FORBIDDEN) cStatus = "Forbidden"; + else if (nStatus == HTTP_NOT_FOUND) cStatus = "Not Found"; + else if (nStatus == HTTP_INTERNAL_SERVER_ERROR) cStatus = "Internal Server Error"; + else cStatus = ""; + return strprintf( + "HTTP/1.1 %d %s\r\n" + "Date: %s\r\n" + "Connection: %s\r\n" + "Content-Length: %"PRIszu"\r\n" + "Content-Type: application/json\r\n" + "Server: CryptoLottoToken-json-rpc/%s\r\n" + "\r\n" + "%s", + nStatus, + cStatus, + rfc1123Time().c_str(), + keepalive ? "keep-alive" : "close", + strMsg.size(), + FormatFullVersion().c_str(), + strMsg.c_str()); +} + +bool ReadHTTPRequestLine(std::basic_istream& stream, int &proto, + string& http_method, string& http_uri) +{ + string str; + getline(stream, str); + + // HTTP request line is space-delimited + vector vWords; + boost::split(vWords, str, boost::is_any_of(" ")); + if (vWords.size() < 2) + return false; + + // HTTP methods permitted: GET, POST + http_method = vWords[0]; + if (http_method != "GET" && http_method != "POST") + return false; + + // HTTP URI must be an absolute path, relative to current host + http_uri = vWords[1]; + if (http_uri.size() == 0 || http_uri[0] != '/') + return false; + + // parse proto, if present + string strProto = ""; + if (vWords.size() > 2) + strProto = vWords[2]; + + proto = 0; + const char *ver = strstr(strProto.c_str(), "HTTP/1."); + if (ver != NULL) + proto = atoi(ver+7); + + return true; +} + +int ReadHTTPStatus(std::basic_istream& stream, int &proto) +{ + string str; + getline(stream, str); + vector vWords; + boost::split(vWords, str, boost::is_any_of(" ")); + if (vWords.size() < 2) + return HTTP_INTERNAL_SERVER_ERROR; + proto = 0; + const char *ver = strstr(str.c_str(), "HTTP/1."); + if (ver != NULL) + proto = atoi(ver+7); + return atoi(vWords[1].c_str()); +} + +int ReadHTTPHeaders(std::basic_istream& stream, map& mapHeadersRet) +{ + int nLen = 0; + loop + { + string str; + std::getline(stream, str); + if (str.empty() || str == "\r") + break; + string::size_type nColon = str.find(":"); + if (nColon != string::npos) + { + string strHeader = str.substr(0, nColon); + boost::trim(strHeader); + boost::to_lower(strHeader); + string strValue = str.substr(nColon+1); + boost::trim(strValue); + mapHeadersRet[strHeader] = strValue; + if (strHeader == "content-length") + nLen = atoi(strValue.c_str()); + } + } + return nLen; +} + +int ReadHTTPMessage(std::basic_istream& stream, map& mapHeadersRet, string& strMessageRet, + int nProto) +{ + mapHeadersRet.clear(); + strMessageRet = ""; + + // Read header + int nLen = ReadHTTPHeaders(stream, mapHeadersRet); + if (nLen < 0 || nLen > (int)MAX_SIZE) + return HTTP_INTERNAL_SERVER_ERROR; + + // Read message + if (nLen > 0) + { + vector vch(nLen); + stream.read(&vch[0], nLen); + strMessageRet = string(vch.begin(), vch.end()); + } + + string sConHdr = mapHeadersRet["connection"]; + + if ((sConHdr != "close") && (sConHdr != "keep-alive")) + { + if (nProto >= 1) + mapHeadersRet["connection"] = "keep-alive"; + else + mapHeadersRet["connection"] = "close"; + } + + return HTTP_OK; +} + +bool HTTPAuthorized(map& mapHeaders) +{ + string strAuth = mapHeaders["authorization"]; + if (strAuth.substr(0,6) != "Basic ") + return false; + string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64); + string strUserPass = DecodeBase64(strUserPass64); + return TimingResistantEqual(strUserPass, strRPCUserColonPass); +} + +// +// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility, +// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were +// unspecified (HTTP errors and contents of 'error'). +// +// 1.0 spec: http://json-rpc.org/wiki/specification +// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http +// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx +// + +string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id) +{ + Object request; + 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"; +} + +Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id) +{ + Object reply; + if (error.type() != null_type) + reply.push_back(Pair("result", Value::null)); + else + reply.push_back(Pair("result", result)); + reply.push_back(Pair("error", error)); + reply.push_back(Pair("id", id)); + return reply; +} + +string JSONRPCReply(const Value& result, const Value& error, const Value& id) +{ + Object reply = JSONRPCReplyObj(result, error, id); + return write_string(Value(reply), false) + "\n"; +} + +void ErrorReply(std::ostream& stream, const Object& objError, const Value& 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); + stream << HTTPReply(nStatus, strReply, false) << std::flush; +} + +bool ClientAllowed(const boost::asio::ip::address& address) +{ + // Make sure that IPv4-compatible and IPv4-mapped IPv6 addresses are treated as IPv4 addresses + if (address.is_v6() + && (address.to_v6().is_v4_compatible() + || address.to_v6().is_v4_mapped())) + return ClientAllowed(address.to_v6().to_v4()); + + if (address == asio::ip::address_v4::loopback() + || address == asio::ip::address_v6::loopback() + || (address.is_v4() + // Check whether IPv4 addresses match 127.0.0.0/8 (loopback subnet) + && (address.to_v4().to_ulong() & 0xff000000) == 0x7f000000)) + return true; + + const string strAddress = address.to_string(); + const vector& vAllow = mapMultiArgs["-rpcallowip"]; + BOOST_FOREACH(string strAllow, vAllow) + if (WildcardMatch(strAddress, strAllow)) + return true; + return false; +} + +// +// IOStream device that speaks SSL but can also speak non-SSL +// +template +class SSLIOStreamDevice : public iostreams::device { +public: + SSLIOStreamDevice(asio::ssl::stream &streamIn, bool fUseSSLIn) : stream(streamIn) + { + fUseSSL = fUseSSLIn; + fNeedHandshake = fUseSSLIn; + } + + void handshake(ssl::stream_base::handshake_type role) + { + if (!fNeedHandshake) return; + fNeedHandshake = false; + stream.handshake(role); + } + std::streamsize read(char* s, std::streamsize n) + { + handshake(ssl::stream_base::server); // HTTPS servers read first + if (fUseSSL) return stream.read_some(asio::buffer(s, n)); + return stream.next_layer().read_some(asio::buffer(s, n)); + } + std::streamsize write(const char* s, std::streamsize n) + { + handshake(ssl::stream_base::client); // HTTPS clients write first + if (fUseSSL) return asio::write(stream, asio::buffer(s, n)); + return asio::write(stream.next_layer(), asio::buffer(s, n)); + } + bool connect(const std::string& server, const std::string& port) + { + ip::tcp::resolver resolver(stream.get_io_service()); + ip::tcp::resolver::query query(server.c_str(), port.c_str()); + ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); + ip::tcp::resolver::iterator end; + boost::system::error_code error = asio::error::host_not_found; + while (error && endpoint_iterator != end) + { + stream.lowest_layer().close(); + stream.lowest_layer().connect(*endpoint_iterator++, error); + } + if (error) + return false; + return true; + } + +private: + bool fNeedHandshake; + bool fUseSSL; + asio::ssl::stream& stream; +}; + +class AcceptedConnection +{ +public: + virtual ~AcceptedConnection() {} + + virtual std::iostream& stream() = 0; + virtual std::string peer_address_to_string() const = 0; + virtual void close() = 0; +}; + +template +class AcceptedConnectionImpl : public AcceptedConnection +{ +public: + AcceptedConnectionImpl( + asio::io_service& io_service, + ssl::context &context, + bool fUseSSL) : + sslStream(io_service, context), + _d(sslStream, fUseSSL), + _stream(_d) + { + } + + virtual std::iostream& stream() + { + return _stream; + } + + virtual std::string peer_address_to_string() const + { + return peer.address().to_string(); + } + + virtual void close() + { + _stream.close(); + } + + typename Protocol::endpoint peer; + asio::ssl::stream sslStream; + +private: + SSLIOStreamDevice _d; + iostreams::stream< SSLIOStreamDevice > _stream; +}; + +void ServiceConnection(AcceptedConnection *conn); + +// Forward declaration required for RPCListen +template +static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor > acceptor, + ssl::context& context, + bool fUseSSL, + AcceptedConnection* conn, + const boost::system::error_code& error); + +/** + * Sets up I/O resources to accept and handle a new connection. + */ +template +static void RPCListen(boost::shared_ptr< basic_socket_acceptor > acceptor, + ssl::context& context, + const bool fUseSSL) +{ + // Accept connection + AcceptedConnectionImpl* conn = new AcceptedConnectionImpl(acceptor->get_io_service(), context, fUseSSL); + + acceptor->async_accept( + conn->sslStream.lowest_layer(), + conn->peer, + boost::bind(&RPCAcceptHandler, + acceptor, + boost::ref(context), + fUseSSL, + conn, + boost::asio::placeholders::error)); +} + +/** + * Accept and handle incoming connection. + */ +template +static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor > acceptor, + ssl::context& context, + const bool fUseSSL, + AcceptedConnection* conn, + const boost::system::error_code& error) +{ + // Immediately start accepting new connections, except when we're cancelled or our socket is closed. + if (error != asio::error::operation_aborted && acceptor->is_open()) + RPCListen(acceptor, context, fUseSSL); + + AcceptedConnectionImpl* tcp_conn = dynamic_cast< AcceptedConnectionImpl* >(conn); + + // TODO: Actually handle errors + if (error) + { + delete conn; + } + + // Restrict callers by IP. It is important to + // do this before starting client thread, to filter out + // certain DoS and misbehaving clients. + else if (tcp_conn && !ClientAllowed(tcp_conn->peer.address())) + { + // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake. + if (!fUseSSL) + conn->stream() << HTTPReply(HTTP_FORBIDDEN, "", false) << std::flush; + delete conn; + } + else { + ServiceConnection(conn); + conn->close(); + delete conn; + } +} + +void StartRPCThreads() +{ + strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; + if ((mapArgs["-rpcpassword"] == "") || + (mapArgs["-rpcuser"] == mapArgs["-rpcpassword"])) + { + unsigned char rand_pwd[32]; + RAND_bytes(rand_pwd, 32); + string strWhatAmI = "To use CryptoLottoTokend"; + if (mapArgs.count("-server")) + strWhatAmI = strprintf(_("To use the %s option"), "\"-server\""); + else if (mapArgs.count("-daemon")) + strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\""); + uiInterface.ThreadSafeMessageBox(strprintf( + _("%s, you must set a rpcpassword in the configuration file:\n" + "%s\n" + "It is recommended you use the following random password:\n" + "rpcuser=CryptoLottoTokenrpc\n" + "rpcpassword=%s\n" + "(you do not need to remember this password)\n" + "The username and password MUST NOT be the same.\n" + "If the file does not exist, create it with owner-readable-only file permissions.\n" + "It is also recommended to set alertnotify so you are notified of problems;\n" + "for example: alertnotify=echo %%s | mail -s \"CryptoLottoToken Alert\" admin@foo.com\n"), + strWhatAmI.c_str(), + GetConfigFile().string().c_str(), + EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()), + "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return; + } + + assert(rpc_io_service == NULL); + rpc_io_service = new asio::io_service(); + rpc_ssl_context = new ssl::context(*rpc_io_service, ssl::context::sslv23); + + const bool fUseSSL = GetBoolArg("-rpcssl"); + + if (fUseSSL) + { + rpc_ssl_context->set_options(ssl::context::no_sslv2); + + filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert")); + if (!pathCertFile.is_complete()) pathCertFile = filesystem::path(GetDataDir()) / pathCertFile; + if (filesystem::exists(pathCertFile)) rpc_ssl_context->use_certificate_chain_file(pathCertFile.string()); + else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string().c_str()); + + filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem")); + if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile; + if (filesystem::exists(pathPKFile)) rpc_ssl_context->use_private_key_file(pathPKFile.string(), ssl::context::pem); + else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string().c_str()); + + string strCiphers = GetArg("-rpcsslciphers", "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH"); + SSL_CTX_set_cipher_list(rpc_ssl_context->impl(), strCiphers.c_str()); + } + + // Try a dual IPv6/IPv4 socket, falling back to separate IPv4 and IPv6 sockets + const bool loopback = !mapArgs.count("-rpcallowip"); + asio::ip::address bindAddress = loopback ? asio::ip::address_v6::loopback() : asio::ip::address_v6::any(); + ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", GetDefaultRPCPort())); + boost::system::error_code v6_only_error; + boost::shared_ptr acceptor(new ip::tcp::acceptor(*rpc_io_service)); + + bool fListening = false; + std::string strerr; + try + { + acceptor->open(endpoint.protocol()); + acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + + // Try making the socket dual IPv6/IPv4 (if listening on the "any" address) + acceptor->set_option(boost::asio::ip::v6_only(loopback), v6_only_error); + + acceptor->bind(endpoint); + acceptor->listen(socket_base::max_connections); + + RPCListen(acceptor, *rpc_ssl_context, fUseSSL); + + fListening = true; + } + catch(boost::system::system_error &e) + { + strerr = strprintf(_("An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s"), endpoint.port(), e.what()); + } + + try { + // If dual IPv6/IPv4 failed (or we're opening loopback interfaces only), open IPv4 separately + if (!fListening || loopback || v6_only_error) + { + bindAddress = loopback ? asio::ip::address_v4::loopback() : asio::ip::address_v4::any(); + endpoint.address(bindAddress); + + acceptor.reset(new ip::tcp::acceptor(*rpc_io_service)); + acceptor->open(endpoint.protocol()); + acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + acceptor->bind(endpoint); + acceptor->listen(socket_base::max_connections); + + RPCListen(acceptor, *rpc_ssl_context, fUseSSL); + + fListening = true; + } + } + catch(boost::system::system_error &e) + { + strerr = strprintf(_("An error occurred while setting up the RPC port %u for listening on IPv4: %s"), endpoint.port(), e.what()); + } + + if (!fListening) { + uiInterface.ThreadSafeMessageBox(strerr, "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return; + } + + rpc_worker_group = new boost::thread_group(); + for (int i = 0; i < GetArg("-rpcthreads", 4); i++) + rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service)); +} + +void StopRPCThreads() +{ + if (rpc_io_service == NULL) return; + + rpc_io_service->stop(); + rpc_worker_group->join_all(); + delete rpc_worker_group; rpc_worker_group = NULL; + delete rpc_ssl_context; rpc_ssl_context = NULL; + delete rpc_io_service; rpc_io_service = NULL; +} + +class JSONRequest +{ +public: + Value id; + string strMethod; + Array params; + + JSONRequest() { id = Value::null; } + void parse(const Value& valRequest); +}; + +void JSONRequest::parse(const Value& valRequest) +{ + // Parse request + if (valRequest.type() != obj_type) + throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object"); + const Object& 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) + throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method"); + if (valMethod.type() != str_type) + throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); + strMethod = valMethod.get_str(); + if (strMethod != "getwork" && strMethod != "getworkex" && strMethod != "getblocktemplate") + printf("ThreadRPCServer method=%s\n", strMethod.c_str()); + + // Parse params + Value valParams = find_value(request, "params"); + if (valParams.type() == array_type) + params = valParams.get_array(); + else if (valParams.type() == null_type) + params = Array(); + else + throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array"); +} + +static Object JSONRPCExecOne(const Value& req) +{ + Object rpc_result; + + JSONRequest jreq; + try { + jreq.parse(req); + + Value result = tableRPC.execute(jreq.strMethod, jreq.params); + rpc_result = JSONRPCReplyObj(result, Value::null, jreq.id); + } + catch (Object& objError) + { + rpc_result = JSONRPCReplyObj(Value::null, objError, jreq.id); + } + catch (std::exception& e) + { + rpc_result = JSONRPCReplyObj(Value::null, + JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); + } + + return rpc_result; +} + +static string JSONRPCExecBatch(const Array& vReq) +{ + Array ret; + for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++) + ret.push_back(JSONRPCExecOne(vReq[reqIdx])); + + return write_string(Value(ret), false) + "\n"; +} + +void ServiceConnection(AcceptedConnection *conn) +{ + bool fRun = true; + while (fRun) + { + int nProto = 0; + map mapHeaders; + string strRequest, strMethod, strURI; + + // Read HTTP request line + if (!ReadHTTPRequestLine(conn->stream(), nProto, strMethod, strURI)) + break; + + // Read HTTP message headers and body + ReadHTTPMessage(conn->stream(), mapHeaders, strRequest, nProto); + + if (strURI != "/") { + conn->stream() << HTTPReply(HTTP_NOT_FOUND, "", false) << std::flush; + break; + } + + // Check authorization + if (mapHeaders.count("authorization") == 0) + { + conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush; + break; + } + if (!HTTPAuthorized(mapHeaders)) + { + printf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string().c_str()); + /* Deter brute-forcing short passwords. + If this results in a DOS the user really + shouldn't have their RPC port exposed.*/ + if (mapArgs["-rpcpassword"].size() < 20) + MilliSleep(250); + + conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush; + break; + } + if (mapHeaders["connection"] == "close") + fRun = false; + + JSONRequest jreq; + try + { + // Parse request + Value valRequest; + if (!read_string(strRequest, valRequest)) + throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); + + string strReply; + + // singleton request + if (valRequest.type() == obj_type) { + jreq.parse(valRequest); + + Value result = tableRPC.execute(jreq.strMethod, jreq.params); + + // Send reply + strReply = JSONRPCReply(result, Value::null, jreq.id); + + // array of requests + } else if (valRequest.type() == array_type) + strReply = JSONRPCExecBatch(valRequest.get_array()); + else + throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); + + conn->stream() << HTTPReply(HTTP_OK, strReply, fRun) << std::flush; + } + catch (Object& objError) + { + ErrorReply(conn->stream(), objError, jreq.id); + break; + } + catch (std::exception& e) + { + ErrorReply(conn->stream(), JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); + break; + } + } +} + +json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array ¶ms) const +{ + // Find method + const CRPCCommand *pcmd = tableRPC[strMethod]; + if (!pcmd) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); + if (pcmd->reqWallet && !pwalletMain) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)"); + + // Observe safe mode + string strWarning = GetWarnings("rpc"); + if (strWarning != "" && !GetBoolArg("-disablesafemode") && + !pcmd->okSafeMode) + throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning); + + try + { + // Execute + Value result; + { + if (pcmd->threadSafe) + result = pcmd->actor(params, false); + else if (!pwalletMain) { + LOCK(cs_main); + result = pcmd->actor(params, false); + } else { + LOCK2(cs_main, pwalletMain->cs_wallet); + result = pcmd->actor(params, false); + } + } + return result; + } + catch (std::exception& e) + { + throw JSONRPCError(RPC_MISC_ERROR, e.what()); + } +} + + +Object CallRPC(const string& strMethod, const Array& params) +{ + if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") + throw runtime_error(strprintf( + _("You must set rpcpassword= in the configuration file:\n%s\n" + "If the file does not exist, create it with owner-readable-only file permissions."), + GetConfigFile().string().c_str())); + + // Connect to localhost + bool fUseSSL = GetBoolArg("-rpcssl"); + asio::io_service io_service; + ssl::context context(io_service, ssl::context::sslv23); + context.set_options(ssl::context::no_sslv2); + asio::ssl::stream sslStream(io_service, context); + SSLIOStreamDevice d(sslStream, fUseSSL); + iostreams::stream< SSLIOStreamDevice > stream(d); + if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(GetDefaultRPCPort())))) + throw runtime_error("couldn't connect to server"); + + // HTTP basic authentication + string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); + map mapRequestHeaders; + mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; + + // Send request + string strRequest = JSONRPCRequest(strMethod, params, 1); + string strPost = HTTPPost(strRequest, mapRequestHeaders); + stream << strPost << std::flush; + + // Receive HTTP reply status + int nProto = 0; + int nStatus = ReadHTTPStatus(stream, nProto); + + // Receive HTTP reply message headers and body + map mapHeaders; + string strReply; + ReadHTTPMessage(stream, mapHeaders, strReply, nProto); + + if (nStatus == HTTP_UNAUTHORIZED) + throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); + else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR) + throw runtime_error(strprintf("server returned HTTP error %d", nStatus)); + else if (strReply.empty()) + throw runtime_error("no response from server"); + + // Parse reply + Value valReply; + if (!read_string(strReply, valReply)) + throw runtime_error("couldn't parse reply from server"); + const Object& reply = valReply.get_obj(); + if (reply.empty()) + throw runtime_error("expected reply to have result, error and id properties"); + + return reply; +} + + + + +template +void ConvertTo(Value& value, bool fAllowNull=false) +{ + if (fAllowNull && value.type() == null_type) + return; + if (value.type() == str_type) + { + // reinterpret string as unquoted json value + Value value2; + string strJSON = value.get_str(); + if (!read_string(strJSON, value2)) + throw runtime_error(string("Error parsing JSON:")+strJSON); + ConvertTo(value2, fAllowNull); + value = value2; + } + else + { + value = value.get_value(); + } +} + +// Convert strings to command-specific RPC representation +Array RPCConvertValues(const std::string &strMethod, const std::vector &strParams) +{ + Array params; + BOOST_FOREACH(const std::string ¶m, strParams) + params.push_back(param); + + int n = params.size(); + + // + // Special case non-string parameter types + // + if (strMethod == "stop" && n > 0) ConvertTo(params[0]); + if (strMethod == "getaddednodeinfo" && n > 0) ConvertTo(params[0]); + if (strMethod == "setgenerate" && n > 0) ConvertTo(params[0]); + if (strMethod == "setgenerate" && n > 1) ConvertTo(params[1]); + if (strMethod == "getnetworkhashps" && n > 0) ConvertTo(params[0]); + if (strMethod == "getnetworkhashps" && n > 1) ConvertTo(params[1]); + if (strMethod == "sendtoaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "settxfee" && n > 0) ConvertTo(params[0]); + if (strMethod == "setmininput" && n > 0) ConvertTo(params[0]); + if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo(params[1]); + if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo(params[0]); + if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo(params[0]); + if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo(params[1]); + if (strMethod == "getbalance" && n > 1) ConvertTo(params[1]); + if (strMethod == "getblockhash" && n > 0) ConvertTo(params[0]); + if (strMethod == "move" && n > 2) ConvertTo(params[2]); + if (strMethod == "move" && n > 3) ConvertTo(params[3]); + if (strMethod == "sendfrom" && n > 2) ConvertTo(params[2]); + if (strMethod == "sendfrom" && n > 3) ConvertTo(params[3]); + if (strMethod == "listtransactions" && n > 1) ConvertTo(params[1]); + if (strMethod == "listtransactions" && n > 2) ConvertTo(params[2]); + if (strMethod == "listaccounts" && n > 0) ConvertTo(params[0]); + if (strMethod == "walletpassphrase" && n > 1) ConvertTo(params[1]); + if (strMethod == "getblocktemplate" && n > 0) ConvertTo(params[0]); + if (strMethod == "listsinceblock" && n > 1) ConvertTo(params[1]); + if (strMethod == "sendmany" && n > 1) ConvertTo(params[1]); + if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]); + if (strMethod == "addmultisigaddress" && n > 0) ConvertTo(params[0]); + if (strMethod == "addmultisigaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "createmultisig" && n > 0) ConvertTo(params[0]); + if (strMethod == "createmultisig" && n > 1) ConvertTo(params[1]); + if (strMethod == "listunspent" && n > 0) ConvertTo(params[0]); + if (strMethod == "listunspent" && n > 1) ConvertTo(params[1]); + if (strMethod == "listunspent" && n > 2) ConvertTo(params[2]); + if (strMethod == "getblock" && n > 1) ConvertTo(params[1]); + if (strMethod == "getrawtransaction" && n > 1) ConvertTo(params[1]); + if (strMethod == "createrawtransaction" && n > 0) ConvertTo(params[0]); + if (strMethod == "createrawtransaction" && n > 1) ConvertTo(params[1]); + if (strMethod == "signrawtransaction" && n > 1) ConvertTo(params[1], true); + if (strMethod == "signrawtransaction" && n > 2) ConvertTo(params[2], true); + if (strMethod == "gettxout" && n > 1) ConvertTo(params[1]); + if (strMethod == "gettxout" && n > 2) ConvertTo(params[2]); + if (strMethod == "lockunspent" && n > 0) ConvertTo(params[0]); + if (strMethod == "lockunspent" && n > 1) ConvertTo(params[1]); + if (strMethod == "importprivkey" && n > 2) ConvertTo(params[2]); + if (strMethod == "verifychain" && n > 0) ConvertTo(params[0]); + if (strMethod == "verifychain" && n > 1) ConvertTo(params[1]); + + return params; +} + +int CommandLineRPC(int argc, char *argv[]) +{ + string strPrint; + int nRet = 0; + try + { + // Skip switches + while (argc > 1 && IsSwitchChar(argv[1][0])) + { + argc--; + argv++; + } + + // Method + if (argc < 2) + throw runtime_error("too few parameters"); + string strMethod = argv[1]; + + // Parameters default to strings + std::vector strParams(&argv[2], &argv[argc]); + Array params = RPCConvertValues(strMethod, strParams); + + // Execute + Object reply = CallRPC(strMethod, params); + + // Parse reply + const Value& result = find_value(reply, "result"); + const Value& error = find_value(reply, "error"); + + if (error.type() != null_type) + { + // Error + strPrint = "error: " + write_string(error, false); + int code = find_value(error.get_obj(), "code").get_int(); + nRet = abs(code); + } + else + { + // Result + if (result.type() == null_type) + strPrint = ""; + else if (result.type() == str_type) + strPrint = result.get_str(); + else + strPrint = write_string(result, true); + } + } + catch (boost::thread_interrupted) { + throw; + } + catch (std::exception& e) { + strPrint = string("error: ") + e.what(); + nRet = 87; + } + catch (...) { + PrintException(NULL, "CommandLineRPC()"); + } + + if (strPrint != "") + { + fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); + } + return nRet; +} + + + + +#ifdef TEST +int main(int argc, char *argv[]) +{ +#ifdef _MSC_VER + // Turn off Microsoft heap dump noise + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); +#endif + setbuf(stdin, NULL); + setbuf(stdout, NULL); + setbuf(stderr, NULL); + + try + { + if (argc >= 2 && string(argv[1]) == "-server") + { + printf("server ready\n"); + ThreadRPCServer(NULL); + } + else + { + return CommandLineRPC(argc, argv); + } + } + catch (boost::thread_interrupted) { + throw; + } + catch (std::exception& e) { + PrintException(&e, "main()"); + } catch (...) { + PrintException(NULL, "main()"); + } + return 0; +} +#endif + +const CRPCTable tableRPC; diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h new file mode 100644 index 0000000..bea7712 --- /dev/null +++ b/src/bitcoinrpc.h @@ -0,0 +1,209 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef _BITCOINRPC_H_ +#define _BITCOINRPC_H_ 1 + +#include +#include +#include + +class CBlockIndex; +class CReserveKey; + +#include "json/json_spirit_reader_template.h" +#include "json/json_spirit_writer_template.h" +#include "json/json_spirit_utils.h" + +#include "util.h" + +// HTTP status codes +enum HTTPStatusCode +{ + HTTP_OK = 200, + HTTP_BAD_REQUEST = 400, + HTTP_UNAUTHORIZED = 401, + HTTP_FORBIDDEN = 403, + HTTP_NOT_FOUND = 404, + HTTP_INTERNAL_SERVER_ERROR = 500, +}; + +// Bitcoin RPC error codes +enum RPCErrorCode +{ + // Standard JSON-RPC 2.0 errors + RPC_INVALID_REQUEST = -32600, + RPC_METHOD_NOT_FOUND = -32601, + RPC_INVALID_PARAMS = -32602, + RPC_INTERNAL_ERROR = -32603, + RPC_PARSE_ERROR = -32700, + + // General application defined errors + RPC_MISC_ERROR = -1, // std::exception thrown in command handling + RPC_FORBIDDEN_BY_SAFE_MODE = -2, // Server is in safe mode, and command is not allowed in safe mode + RPC_TYPE_ERROR = -3, // Unexpected type was passed as parameter + RPC_INVALID_ADDRESS_OR_KEY = -5, // Invalid address or key + RPC_OUT_OF_MEMORY = -7, // Ran out of memory during operation + RPC_INVALID_PARAMETER = -8, // Invalid, missing or duplicate parameter + RPC_DATABASE_ERROR = -20, // Database error + RPC_DESERIALIZATION_ERROR = -22, // Error parsing or validating structure in raw format + + // P2P client errors + RPC_CLIENT_NOT_CONNECTED = -9, // Bitcoin is not connected + RPC_CLIENT_IN_INITIAL_DOWNLOAD = -10, // Still downloading initial blocks + + // Wallet errors + RPC_WALLET_ERROR = -4, // Unspecified problem with wallet (key not found etc.) + RPC_WALLET_INSUFFICIENT_FUNDS = -6, // Not enough funds in wallet or account + RPC_WALLET_INVALID_ACCOUNT_NAME = -11, // Invalid account name + RPC_WALLET_KEYPOOL_RAN_OUT = -12, // Keypool ran out, call keypoolrefill first + RPC_WALLET_UNLOCK_NEEDED = -13, // Enter the wallet passphrase with walletpassphrase first + RPC_WALLET_PASSPHRASE_INCORRECT = -14, // The wallet passphrase entered was incorrect + RPC_WALLET_WRONG_ENC_STATE = -15, // Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.) + RPC_WALLET_ENCRYPTION_FAILED = -16, // Failed to encrypt the wallet + RPC_WALLET_ALREADY_UNLOCKED = -17, // Wallet is already unlocked +}; + +json_spirit::Object JSONRPCError(int code, const std::string& message); + +void StartRPCThreads(); +void StopRPCThreads(); +int CommandLineRPC(int argc, char *argv[]); + +/** Convert parameter values for RPC call from strings to command-specific JSON objects. */ +json_spirit::Array RPCConvertValues(const std::string &strMethod, const std::vector &strParams); + +/* + Type-check arguments; throws JSONRPCError if wrong type given. Does not check that + 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& 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& typesExpected, bool fAllowNull=false); + +typedef json_spirit::Value(*rpcfn_type)(const json_spirit::Array& params, bool fHelp); + +class CRPCCommand +{ +public: + std::string name; + rpcfn_type actor; + bool okSafeMode; + bool threadSafe; + bool reqWallet; +}; + +/** + * Bitcoin RPC command dispatcher. + */ +class CRPCTable +{ +private: + std::map mapCommands; +public: + CRPCTable(); + const CRPCCommand* operator[](std::string name) const; + std::string help(std::string name) const; + + /** + * Execute a method. + * @param method Method to execute + * @param params Array of arguments (JSON objects) + * @returns Result of the call. + * @throws an exception (json_spirit::Value) when an error happens. + */ + json_spirit::Value execute(const std::string &method, const json_spirit::Array ¶ms) const; +}; + +extern const CRPCTable tableRPC; + +extern void InitRPCMining(); +extern void ShutdownRPCMining(); + +extern int64 nWalletUnlockTime; +extern int64 AmountFromValue(const json_spirit::Value& value); +extern json_spirit::Value ValueFromAmount(int64 amount); +extern double GetDifficulty(const CBlockIndex* blockindex = NULL); +extern std::string HexBits(unsigned int nBits); +extern std::string HelpRequiringPassphrase(); +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 addnode(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getaddednodeinfo(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 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 getnetworkhashps(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value gethashespersec(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getmininginfo(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getworkex(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getwork(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 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 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 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 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 signrawtransaction(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value sendrawtransaction(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 setmininput(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); + +#endif diff --git a/src/bloom.cpp b/src/bloom.cpp new file mode 100644 index 0000000..6bdffdb --- /dev/null +++ b/src/bloom.cpp @@ -0,0 +1,182 @@ +// Copyright (c) 2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include +#include + +#include "bloom.h" +#include "main.h" +#include "script.h" + +#define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455 +#define LN2 0.6931471805599453094172321214581765680755001343602552 + +using namespace std; + +static const unsigned char bit_mask[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; + +CBloomFilter::CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweakIn, unsigned char nFlagsIn) : +// The ideal size for a bloom filter with a given number of elements and false positive rate is: +// - nElements * log(fp rate) / ln(2)^2 +// We ignore filter parameters which will create a bloom filter larger than the protocol limits +vData(min((unsigned int)(-1 / LN2SQUARED * nElements * log(nFPRate)), MAX_BLOOM_FILTER_SIZE * 8) / 8), +// The ideal number of hash functions is filter size * ln(2) / number of elements +// Again, we ignore filter parameters which will create a bloom filter with more hash functions than the protocol limits +// See http://en.wikipedia.org/wiki/Bloom_filter for an explanation of these formulas +isFull(false), +isEmpty(false), +nHashFuncs(min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)), +nTweak(nTweakIn), +nFlags(nFlagsIn) +{ +} + +inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, const std::vector& vDataToHash) const +{ + // 0xFBA4C795 chosen as it guarantees a reasonable bit difference between nHashNum values. + return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash) % (vData.size() * 8); +} + +void CBloomFilter::insert(const vector& vKey) +{ + if (isFull) + return; + for (unsigned int i = 0; i < nHashFuncs; i++) + { + unsigned int nIndex = Hash(i, vKey); + // Sets bit nIndex of vData + vData[nIndex >> 3] |= bit_mask[7 & nIndex]; + } + isEmpty = false; +} + +void CBloomFilter::insert(const COutPoint& outpoint) +{ + CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); + stream << outpoint; + vector data(stream.begin(), stream.end()); + insert(data); +} + +void CBloomFilter::insert(const uint256& hash) +{ + vector data(hash.begin(), hash.end()); + insert(data); +} + +bool CBloomFilter::contains(const vector& vKey) const +{ + if (isFull) + return true; + if (isEmpty) + return false; + for (unsigned int i = 0; i < nHashFuncs; i++) + { + unsigned int nIndex = Hash(i, vKey); + // Checks bit nIndex of vData + if (!(vData[nIndex >> 3] & bit_mask[7 & nIndex])) + return false; + } + return true; +} + +bool CBloomFilter::contains(const COutPoint& outpoint) const +{ + CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); + stream << outpoint; + vector data(stream.begin(), stream.end()); + return contains(data); +} + +bool CBloomFilter::contains(const uint256& hash) const +{ + vector data(hash.begin(), hash.end()); + return contains(data); +} + +bool CBloomFilter::IsWithinSizeConstraints() const +{ + return vData.size() <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS; +} + +bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx, const uint256& hash) +{ + bool fFound = false; + // Match if the filter contains the hash of tx + // for finding tx when they appear in a block + if (isFull) + return true; + if (isEmpty) + return false; + if (contains(hash)) + fFound = true; + + for (unsigned int i = 0; i < tx.vout.size(); i++) + { + const CTxOut& txout = tx.vout[i]; + // Match if the filter contains any arbitrary script data element in any scriptPubKey in tx + // If this matches, also add the specific output that was matched. + // This means clients don't have to update the filter themselves when a new relevant tx + // is discovered in order to find spending transactions, which avoids round-tripping and race conditions. + CScript::const_iterator pc = txout.scriptPubKey.begin(); + vector data; + while (pc < txout.scriptPubKey.end()) + { + opcodetype opcode; + if (!txout.scriptPubKey.GetOp(pc, opcode, data)) + break; + if (data.size() != 0 && contains(data)) + { + fFound = true; + if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_ALL) + insert(COutPoint(hash, i)); + else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY) + { + txnouttype type; + vector > vSolutions; + if (Solver(txout.scriptPubKey, type, vSolutions) && + (type == TX_PUBKEY || type == TX_MULTISIG)) + insert(COutPoint(hash, i)); + } + break; + } + } + } + + if (fFound) + return true; + + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + // Match if the filter contains an outpoint tx spends + if (contains(txin.prevout)) + return true; + + // Match if the filter contains any arbitrary script data element in any scriptSig in tx + CScript::const_iterator pc = txin.scriptSig.begin(); + vector data; + while (pc < txin.scriptSig.end()) + { + opcodetype opcode; + if (!txin.scriptSig.GetOp(pc, opcode, data)) + break; + if (data.size() != 0 && contains(data)) + return true; + } + } + + return false; +} + +void CBloomFilter::UpdateEmptyFull() +{ + bool full = true; + bool empty = true; + for (unsigned int i = 0; i < vData.size(); i++) + { + full &= vData[i] == 0xff; + empty &= vData[i] == 0; + } + isFull = full; + isEmpty = empty; +} diff --git a/src/bloom.h b/src/bloom.h new file mode 100644 index 0000000..f482bfc --- /dev/null +++ b/src/bloom.h @@ -0,0 +1,91 @@ +// Copyright (c) 2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_BLOOM_H +#define BITCOIN_BLOOM_H + +#include + +#include "uint256.h" +#include "serialize.h" + +class COutPoint; +class CTransaction; + +// 20,000 items with fp rate < 0.1% or 10,000 items and <0.0001% +static const unsigned int MAX_BLOOM_FILTER_SIZE = 36000; // bytes +static const unsigned int MAX_HASH_FUNCS = 50; + +// First two bits of nFlags control how much IsRelevantAndUpdate actually updates +// The remaining bits are reserved +enum bloomflags +{ + BLOOM_UPDATE_NONE = 0, + BLOOM_UPDATE_ALL = 1, + // Only adds outpoints to the filter if the output is a pay-to-pubkey/pay-to-multisig script + BLOOM_UPDATE_P2PUBKEY_ONLY = 2, + BLOOM_UPDATE_MASK = 3, +}; + +/** + * BloomFilter is a probabilistic filter which SPV clients provide + * so that we can filter the transactions we sends them. + * + * This allows for significantly more efficient transaction and block downloads. + * + * Because bloom filters are probabilistic, an SPV node can increase the false- + * positive rate, making us send them transactions which aren't actually theirs, + * allowing clients to trade more bandwidth for more privacy by obfuscating which + * keys are owned by them. + */ +class CBloomFilter +{ +private: + std::vector vData; + bool isFull; + bool isEmpty; + unsigned int nHashFuncs; + unsigned int nTweak; + unsigned char nFlags; + + unsigned int Hash(unsigned int nHashNum, const std::vector& vDataToHash) const; + +public: + // Creates a new bloom filter which will provide the given fp rate when filled with the given number of elements + // Note that if the given parameters will result in a filter outside the bounds of the protocol limits, + // the filter created will be as close to the given parameters as possible within the protocol limits. + // This will apply if nFPRate is very low or nElements is unreasonably high. + // nTweak is a constant which is added to the seed value passed to the hash function + // It should generally always be a random value (and is largely only exposed for unit testing) + // nFlags should be one of the BLOOM_UPDATE_* enums (not _MASK) + CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweak, unsigned char nFlagsIn); + CBloomFilter() : isFull(true) {} + + IMPLEMENT_SERIALIZE + ( + READWRITE(vData); + READWRITE(nHashFuncs); + READWRITE(nTweak); + READWRITE(nFlags); + ) + + void insert(const std::vector& vKey); + void insert(const COutPoint& outpoint); + void insert(const uint256& hash); + + bool contains(const std::vector& vKey) const; + bool contains(const COutPoint& outpoint) const; + bool contains(const uint256& hash) const; + + // True if the size is <= MAX_BLOOM_FILTER_SIZE and the number of hash functions is <= MAX_HASH_FUNCS + // (catch a filter which was just deserialized which was too big) + bool IsWithinSizeConstraints() const; + + // Also adds any outputs which match the filter to the filter (to match their spending txes) + bool IsRelevantAndUpdate(const CTransaction& tx, const uint256& hash); + + // Checks for empty and full filters to avoid wasting cpu + void UpdateEmptyFull(); +}; + +#endif /* BITCOIN_BLOOM_H */ diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp new file mode 100644 index 0000000..c2d5d84 --- /dev/null +++ b/src/checkpoints.cpp @@ -0,0 +1,138 @@ +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include // for 'map_list_of()' +#include + +#include "checkpoints.h" + +#include "main.h" +#include "uint256.h" + +namespace Checkpoints +{ + typedef std::map MapCheckpoints; + + // How many times we expect transactions after the last checkpoint to + // be slower. This number is a compromise, as it can't be accurate for + // every system. When reindexing from a fast disk with a slow CPU, it + // can be up to 20, while when downloading from a slow network with a + // fast multicore CPU, it won't be much higher than 1. + static const double fSigcheckVerificationFactor = 5.0; + + + struct CCheckpointData { + const MapCheckpoints *mapCheckpoints; + int64 nTimeLastCheckpoint; + int64 nTransactionsLastCheckpoint; + double fTransactionsPerDay; + }; + + // What makes a good checkpoint block? + // + Is surrounded by blocks with reasonable timestamps + // (no blocks before with a timestamp after, none after with + // timestamp before) + static MapCheckpoints mapCheckpoints = + boost::assign::map_list_of + ( 0, uint256("0x00000326fcb9e2d71672332c1cf1e525c08323d247cad5b16b3bbf71d497aa01")) + ; + static const CCheckpointData data = { + &mapCheckpoints, + 1388880557, // * UNIX timestamp of last checkpoint block + 0, // * total number of transactions between genesis and last checkpoint + // (the tx=... number in the SetBestChain debug.log lines) + 8000.0 // * estimated number of transactions per day after checkpoint + }; + + static MapCheckpoints mapCheckpointsTestnet = + boost::assign::map_list_of + ( 0, uint256("0x")) + ; + static const CCheckpointData dataTestnet = { + &mapCheckpointsTestnet, + 1369685559, + 37581, + 300 + }; + + const CCheckpointData &Checkpoints() { + if (fTestNet) + return dataTestnet; + else + return data; + } + + bool CheckBlock(int nHeight, const uint256& hash) + { + if (fTestNet) return true; // Testnet has no checkpoints + if (!GetBoolArg("-checkpoints", true)) + return true; + + const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; + + MapCheckpoints::const_iterator i = checkpoints.find(nHeight); + if (i == checkpoints.end()) return true; + return hash == i->second; + } + + // Guess how far we are in the verification process at the given block index + double GuessVerificationProgress(CBlockIndex *pindex) { + if (pindex==NULL) + return 0.0; + + int64 nNow = time(NULL); + + double fWorkBefore = 0.0; // Amount of work done before pindex + double fWorkAfter = 0.0; // Amount of work left after pindex (estimated) + // Work is defined as: 1.0 per transaction before the last checkoint, and + // fSigcheckVerificationFactor per transaction after. + + const CCheckpointData &data = Checkpoints(); + + if (pindex->nChainTx <= data.nTransactionsLastCheckpoint) { + double nCheapBefore = pindex->nChainTx; + double nCheapAfter = data.nTransactionsLastCheckpoint - pindex->nChainTx; + double nExpensiveAfter = (nNow - data.nTimeLastCheckpoint)/86400.0*data.fTransactionsPerDay; + fWorkBefore = nCheapBefore; + fWorkAfter = nCheapAfter + nExpensiveAfter*fSigcheckVerificationFactor; + } else { + double nCheapBefore = data.nTransactionsLastCheckpoint; + double nExpensiveBefore = pindex->nChainTx - data.nTransactionsLastCheckpoint; + double nExpensiveAfter = (nNow - pindex->nTime)/86400.0*data.fTransactionsPerDay; + fWorkBefore = nCheapBefore + nExpensiveBefore*fSigcheckVerificationFactor; + fWorkAfter = nExpensiveAfter*fSigcheckVerificationFactor; + } + + return fWorkBefore / (fWorkBefore + fWorkAfter); + } + + int GetTotalBlocksEstimate() + { + if (fTestNet) return 0; // Testnet has no checkpoints + if (!GetBoolArg("-checkpoints", true)) + return 0; + + const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; + + return checkpoints.rbegin()->first; + } + + CBlockIndex* GetLastCheckpoint(const std::map& mapBlockIndex) + { + if (fTestNet) return NULL; // Testnet has no checkpoints + if (!GetBoolArg("-checkpoints", true)) + return NULL; + + const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; + + BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints) + { + const uint256& hash = i.second; + std::map::const_iterator t = mapBlockIndex.find(hash); + if (t != mapBlockIndex.end()) + return t->second; + } + return NULL; + } +} diff --git a/src/checkpoints.h b/src/checkpoints.h new file mode 100644 index 0000000..3d56885 --- /dev/null +++ b/src/checkpoints.h @@ -0,0 +1,29 @@ +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_CHECKPOINT_H +#define BITCOIN_CHECKPOINT_H + +#include + +class uint256; +class CBlockIndex; + +/** Block-chain checkpoints are compiled-in sanity checks. + * They are updated every release or three. + */ +namespace Checkpoints +{ + // Returns true if block passes checkpoint checks + bool CheckBlock(int nHeight, const uint256& hash); + + // Return conservative estimate of total number of blocks, 0 if unknown + int GetTotalBlocksEstimate(); + + // Returns last CBlockIndex* in mapBlockIndex that is a checkpoint + CBlockIndex* GetLastCheckpoint(const std::map& mapBlockIndex); + + double GuessVerificationProgress(CBlockIndex *pindex); +} + +#endif diff --git a/src/checkqueue.h b/src/checkqueue.h new file mode 100644 index 0000000..eba424f --- /dev/null +++ b/src/checkqueue.h @@ -0,0 +1,192 @@ +// Copyright (c) 2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef CHECKQUEUE_H +#define CHECKQUEUE_H + +#include +#include +#include + +#include +#include + +template class CCheckQueueControl; + +/** Queue for verifications that have to be performed. + * The verifications are represented by a type T, which must provide an + * operator(), returning a bool. + * + * One thread (the master) is assumed to push batches of verifications + * onto the queue, where they are processed by N-1 worker threads. When + * the master is done adding work, it temporarily joins the worker pool + * as an N'th worker, until all jobs are done. + */ +template class CCheckQueue { +private: + // Mutex to protect the inner state + boost::mutex mutex; + + // Worker threads block on this when out of work + boost::condition_variable condWorker; + + // Master thread blocks on this when out of work + boost::condition_variable condMaster; + + // The queue of elements to be processed. + // As the order of booleans doesn't matter, it is used as a LIFO (stack) + std::vector queue; + + // The number of workers (including the master) that are idle. + int nIdle; + + // The total number of workers (including the master). + int nTotal; + + // The temporary evaluation result. + bool fAllOk; + + // Number of verifications that haven't completed yet. + // This includes elements that are not anymore in queue, but still in + // worker's own batches. + unsigned int nTodo; + + // Whether we're shutting down. + bool fQuit; + + // The maximum number of elements to be processed in one batch + unsigned int nBatchSize; + + // Internal function that does bulk of the verification work. + bool Loop(bool fMaster = false) { + boost::condition_variable &cond = fMaster ? condMaster : condWorker; + std::vector vChecks; + vChecks.reserve(nBatchSize); + unsigned int nNow = 0; + bool fOk = true; + do { + { + boost::unique_lock lock(mutex); + // first do the clean-up of the previous loop run (allowing us to do it in the same critsect) + if (nNow) { + fAllOk &= fOk; + nTodo -= nNow; + if (nTodo == 0 && !fMaster) + // We processed the last element; inform the master he can exit and return the result + condMaster.notify_one(); + } else { + // first iteration + nTotal++; + } + // logically, the do loop starts here + while (queue.empty()) { + if ((fMaster || fQuit) && nTodo == 0) { + nTotal--; + bool fRet = fAllOk; + // reset the status for new work later + if (fMaster) + fAllOk = true; + // return the current status + return fRet; + } + nIdle++; + cond.wait(lock); // wait + nIdle--; + } + // Decide how many work units to process now. + // * Do not try to do everything at once, but aim for increasingly smaller batches so + // all workers finish approximately simultaneously. + // * Try to account for idle jobs which will instantly start helping. + // * Don't do batches smaller than 1 (duh), or larger than nBatchSize. + nNow = std::max(1U, std::min(nBatchSize, (unsigned int)queue.size() / (nTotal + nIdle + 1))); + vChecks.resize(nNow); + for (unsigned int i = 0; i < nNow; i++) { + // We want the lock on the mutex to be as short as possible, so swap jobs from the global + // queue to the local batch vector instead of copying. + vChecks[i].swap(queue.back()); + queue.pop_back(); + } + // Check whether we need to do work at all + fOk = fAllOk; + } + // execute work + BOOST_FOREACH(T &check, vChecks) + if (fOk) + fOk = check(); + vChecks.clear(); + } while(true); + } + +public: + // Create a new check queue + CCheckQueue(unsigned int nBatchSizeIn) : + nIdle(0), nTotal(0), fAllOk(true), nTodo(0), fQuit(false), nBatchSize(nBatchSizeIn) {} + + // Worker thread + void Thread() { + Loop(); + } + + // Wait until execution finishes, and return whether all evaluations where succesful. + bool Wait() { + return Loop(true); + } + + // Add a batch of checks to the queue + void Add(std::vector &vChecks) { + boost::unique_lock lock(mutex); + BOOST_FOREACH(T &check, vChecks) { + queue.push_back(T()); + check.swap(queue.back()); + } + nTodo += vChecks.size(); + if (vChecks.size() == 1) + condWorker.notify_one(); + else if (vChecks.size() > 1) + condWorker.notify_all(); + } + + ~CCheckQueue() { + } + + friend class CCheckQueueControl; +}; + +/** RAII-style controller object for a CCheckQueue that guarantees the passed + * queue is finished before continuing. + */ +template class CCheckQueueControl { +private: + CCheckQueue *pqueue; + bool fDone; + +public: + CCheckQueueControl(CCheckQueue *pqueueIn) : pqueue(pqueueIn), fDone(false) { + // passed queue is supposed to be unused, or NULL + if (pqueue != NULL) { + assert(pqueue->nTotal == pqueue->nIdle); + assert(pqueue->nTodo == 0); + assert(pqueue->fAllOk == true); + } + } + + bool Wait() { + if (pqueue == NULL) + return true; + bool fRet = pqueue->Wait(); + fDone = true; + return fRet; + } + + void Add(std::vector &vChecks) { + if (pqueue != NULL) + pqueue->Add(vChecks); + } + + ~CCheckQueueControl() { + if (!fDone) + Wait(); + } +}; + +#endif diff --git a/src/clientversion.h b/src/clientversion.h new file mode 100644 index 0000000..ce9d96c --- /dev/null +++ b/src/clientversion.h @@ -0,0 +1,28 @@ +#ifndef CLIENTVERSION_H +#define CLIENTVERSION_H + +// +// client versioning and copyright year +// + +// These need to be macros, as version.cpp's and bitcoin-qt.rc's voodoo requires it +#define CLIENT_VERSION_MAJOR 1 +#define CLIENT_VERSION_MINOR 0 +#define CLIENT_VERSION_REVISION 0 +#define CLIENT_VERSION_BUILD 1 + +// did not update version + +// Set to true for release, false for prerelease or test build +#define CLIENT_VERSION_IS_RELEASE true + +// Copyright year (2009-this) +// Todo: update this when changing our copyright comments in the source +#define COPYRIGHT_YEAR 2015 + +// Converts the parameter X to a string after macro replacement on X has been performed. +// Don't merge these into one macro! +#define STRINGIZE(X) DO_STRINGIZE(X) +#define DO_STRINGIZE(X) #X + +#endif // CLIENTVERSION_H diff --git a/src/coincontrol.h b/src/coincontrol.h new file mode 100644 index 0000000..236b586 --- /dev/null +++ b/src/coincontrol.h @@ -0,0 +1,57 @@ +#ifndef COINCONTROL_H +#define COINCONTROL_H + +/** Coin Control Features. */ +class CCoinControl +{ +public: + CTxDestination destChange; + + CCoinControl() + { + SetNull(); + } + + void SetNull() + { + destChange = CNoDestination(); + setSelected.clear(); + } + + bool HasSelected() const + { + return (setSelected.size() > 0); + } + + bool IsSelected(const uint256& hash, unsigned int n) const + { + COutPoint outpt(hash, n); + return (setSelected.count(outpt) > 0); + } + + void Select(COutPoint& output) + { + setSelected.insert(output); + } + + void UnSelect(COutPoint& output) + { + setSelected.erase(output); + } + + void UnSelectAll() + { + setSelected.clear(); + } + + void ListSelected(std::vector& vOutpoints) + { + vOutpoints.assign(setSelected.begin(), setSelected.end()); + } + +private: + std::set setSelected; + +}; + +#endif // COINCONTROL_H diff --git a/src/compat.h b/src/compat.h new file mode 100644 index 0000000..7062216 --- /dev/null +++ b/src/compat.h @@ -0,0 +1,64 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef _BITCOIN_COMPAT_H +#define _BITCOIN_COMPAT_H 1 + +#ifdef WIN32 +#define _WIN32_WINNT 0x0501 +#define WIN32_LEAN_AND_MEAN 1 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#define FD_SETSIZE 1024 // max number of fds in fd_set +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +typedef u_int SOCKET; +#ifdef WIN32 +#define MSG_NOSIGNAL 0 +#define MSG_DONTWAIT 0 +typedef int socklen_t; +#else +#include "errno.h" +#define WSAGetLastError() errno +#define WSAEINVAL EINVAL +#define WSAEALREADY EALREADY +#define WSAEWOULDBLOCK EWOULDBLOCK +#define WSAEMSGSIZE EMSGSIZE +#define WSAEINTR EINTR +#define WSAEINPROGRESS EINPROGRESS +#define WSAEADDRINUSE EADDRINUSE +#define WSAENOTSOCK EBADF +#define INVALID_SOCKET (SOCKET)(~0) +#define SOCKET_ERROR -1 +#endif + +inline int myclosesocket(SOCKET& hSocket) +{ + if (hSocket == INVALID_SOCKET) + return WSAENOTSOCK; +#ifdef WIN32 + int ret = closesocket(hSocket); +#else + int ret = close(hSocket); +#endif + hSocket = INVALID_SOCKET; + return ret; +} +#define closesocket(s) myclosesocket(s) + + +#endif diff --git a/src/crypter.cpp b/src/crypter.cpp new file mode 100644 index 0000000..32baabd --- /dev/null +++ b/src/crypter.cpp @@ -0,0 +1,121 @@ +// Copyright (c) 2009-2012 The Bitcoin Developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include +#ifdef WIN32 +#include +#endif + +#include "crypter.h" + +bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::vector& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod) +{ + if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE) + return false; + + int i = 0; + if (nDerivationMethod == 0) + i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha512(), &chSalt[0], + (unsigned char *)&strKeyData[0], strKeyData.size(), nRounds, chKey, chIV); + + if (i != (int)WALLET_CRYPTO_KEY_SIZE) + { + OPENSSL_cleanse(chKey, sizeof(chKey)); + OPENSSL_cleanse(chIV, sizeof(chIV)); + return false; + } + + fKeySet = true; + return true; +} + +bool CCrypter::SetKey(const CKeyingMaterial& chNewKey, const std::vector& chNewIV) +{ + if (chNewKey.size() != WALLET_CRYPTO_KEY_SIZE || chNewIV.size() != WALLET_CRYPTO_KEY_SIZE) + return false; + + memcpy(&chKey[0], &chNewKey[0], sizeof chKey); + memcpy(&chIV[0], &chNewIV[0], sizeof chIV); + + fKeySet = true; + return true; +} + +bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector &vchCiphertext) +{ + if (!fKeySet) + return false; + + // max ciphertext len for a n bytes of plaintext is + // n + AES_BLOCK_SIZE - 1 bytes + int nLen = vchPlaintext.size(); + int nCLen = nLen + AES_BLOCK_SIZE, nFLen = 0; + vchCiphertext = std::vector (nCLen); + + EVP_CIPHER_CTX ctx; + + bool fOk = true; + + EVP_CIPHER_CTX_init(&ctx); + if (fOk) fOk = EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV); + if (fOk) fOk = EVP_EncryptUpdate(&ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen); + if (fOk) fOk = EVP_EncryptFinal_ex(&ctx, (&vchCiphertext[0])+nCLen, &nFLen); + EVP_CIPHER_CTX_cleanup(&ctx); + + if (!fOk) return false; + + vchCiphertext.resize(nCLen + nFLen); + return true; +} + +bool CCrypter::Decrypt(const std::vector& vchCiphertext, CKeyingMaterial& vchPlaintext) +{ + if (!fKeySet) + return false; + + // plaintext will always be equal to or lesser than length of ciphertext + int nLen = vchCiphertext.size(); + int nPLen = nLen, nFLen = 0; + + vchPlaintext = CKeyingMaterial(nPLen); + + EVP_CIPHER_CTX ctx; + + bool fOk = true; + + EVP_CIPHER_CTX_init(&ctx); + if (fOk) fOk = EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV); + if (fOk) fOk = EVP_DecryptUpdate(&ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen); + if (fOk) fOk = EVP_DecryptFinal_ex(&ctx, (&vchPlaintext[0])+nPLen, &nFLen); + EVP_CIPHER_CTX_cleanup(&ctx); + + if (!fOk) return false; + + vchPlaintext.resize(nPLen + nFLen); + return true; +} + + +bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector &vchCiphertext) +{ + CCrypter cKeyCrypter; + std::vector chIV(WALLET_CRYPTO_KEY_SIZE); + memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE); + if(!cKeyCrypter.SetKey(vMasterKey, chIV)) + return false; + return cKeyCrypter.Encrypt(*((const CKeyingMaterial*)&vchPlaintext), vchCiphertext); +} + +bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext) +{ + CCrypter cKeyCrypter; + std::vector chIV(WALLET_CRYPTO_KEY_SIZE); + memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE); + if(!cKeyCrypter.SetKey(vMasterKey, chIV)) + return false; + return cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext)); +} diff --git a/src/crypter.h b/src/crypter.h new file mode 100644 index 0000000..4134c1b --- /dev/null +++ b/src/crypter.h @@ -0,0 +1,107 @@ +// Copyright (c) 2009-2012 The Bitcoin Developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef __CRYPTER_H__ +#define __CRYPTER_H__ + +#include "allocators.h" /* for SecureString */ +#include "key.h" +#include "serialize.h" + +const unsigned int WALLET_CRYPTO_KEY_SIZE = 32; +const unsigned int WALLET_CRYPTO_SALT_SIZE = 8; + +/* +Private key encryption is done based on a CMasterKey, +which holds a salt and random encryption key. + +CMasterKeys are encrypted using AES-256-CBC using a key +derived using derivation method nDerivationMethod +(0 == EVP_sha512()) and derivation iterations nDeriveIterations. +vchOtherDerivationParameters is provided for alternative algorithms +which may require more parameters (such as scrypt). + +Wallet Private Keys are then encrypted using AES-256-CBC +with the double-sha256 of the public key as the IV, and the +master key's key as the encryption key (see keystore.[ch]). +*/ + +/** Master key for wallet encryption */ +class CMasterKey +{ +public: + std::vector vchCryptedKey; + std::vector vchSalt; + // 0 = EVP_sha512() + // 1 = scrypt() + unsigned int nDerivationMethod; + unsigned int nDeriveIterations; + // Use this for more parameters to key derivation, + // such as the various parameters to scrypt + std::vector vchOtherDerivationParameters; + + IMPLEMENT_SERIALIZE + ( + READWRITE(vchCryptedKey); + READWRITE(vchSalt); + READWRITE(nDerivationMethod); + READWRITE(nDeriveIterations); + READWRITE(vchOtherDerivationParameters); + ) + CMasterKey() + { + // 25000 rounds is just under 0.1 seconds on a 1.86 GHz Pentium M + // ie slightly lower than the lowest hardware we need bother supporting + nDeriveIterations = 25000; + nDerivationMethod = 0; + vchOtherDerivationParameters = std::vector(0); + } +}; + +typedef std::vector > CKeyingMaterial; + +/** Encryption/decryption context with key information */ +class CCrypter +{ +private: + unsigned char chKey[WALLET_CRYPTO_KEY_SIZE]; + unsigned char chIV[WALLET_CRYPTO_KEY_SIZE]; + bool fKeySet; + +public: + bool SetKeyFromPassphrase(const SecureString &strKeyData, const std::vector& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod); + bool Encrypt(const CKeyingMaterial& vchPlaintext, std::vector &vchCiphertext); + bool Decrypt(const std::vector& vchCiphertext, CKeyingMaterial& vchPlaintext); + bool SetKey(const CKeyingMaterial& chNewKey, const std::vector& chNewIV); + + void CleanKey() + { + OPENSSL_cleanse(chKey, sizeof(chKey)); + OPENSSL_cleanse(chIV, sizeof(chIV)); + fKeySet = false; + } + + CCrypter() + { + fKeySet = false; + + // Try to keep the key data out of swap (and be a bit over-careful to keep the IV that we don't even use out of swap) + // Note that this does nothing about suspend-to-disk (which will put all our key data on disk) + // Note as well that at no point in this program is any attempt made to prevent stealing of keys by reading the memory of the running process. + LockedPageManager::instance.LockRange(&chKey[0], sizeof chKey); + LockedPageManager::instance.LockRange(&chIV[0], sizeof chIV); + } + + ~CCrypter() + { + CleanKey(); + + LockedPageManager::instance.UnlockRange(&chKey[0], sizeof chKey); + LockedPageManager::instance.UnlockRange(&chIV[0], sizeof chIV); + } +}; + +bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector &vchCiphertext); +bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext); + +#endif diff --git a/src/db.cpp b/src/db.cpp new file mode 100644 index 0000000..2bda065 --- /dev/null +++ b/src/db.cpp @@ -0,0 +1,584 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "db.h" +#include "util.h" +#include "main.h" +#include +#include + +#ifndef WIN32 +#include "sys/stat.h" +#endif + +using namespace std; +using namespace boost; + + +unsigned int nWalletDBUpdated; + + + +// +// CDB +// + +CDBEnv bitdb; + +void CDBEnv::EnvShutdown() +{ + if (!fDbEnvInit) + return; + + fDbEnvInit = false; + int ret = dbenv.close(0); + if (ret != 0) + printf("EnvShutdown exception: %s (%d)\n", DbEnv::strerror(ret), ret); + if (!fMockDb) + DbEnv(0).remove(path.string().c_str(), 0); +} + +CDBEnv::CDBEnv() : dbenv(DB_CXX_NO_EXCEPTIONS) +{ + fDbEnvInit = false; + fMockDb = false; +} + +CDBEnv::~CDBEnv() +{ + EnvShutdown(); +} + +void CDBEnv::Close() +{ + EnvShutdown(); +} + +bool CDBEnv::Open(const boost::filesystem::path& pathIn) +{ + if (fDbEnvInit) + return true; + + boost::this_thread::interruption_point(); + + path = pathIn; + filesystem::path pathLogDir = path / "database"; + filesystem::create_directory(pathLogDir); + filesystem::path pathErrorFile = path / "db.log"; + printf("dbenv.open LogDir=%s ErrorFile=%s\n", pathLogDir.string().c_str(), pathErrorFile.string().c_str()); + + unsigned int nEnvFlags = 0; + if (GetBoolArg("-privdb", true)) + nEnvFlags |= DB_PRIVATE; + + dbenv.set_lg_dir(pathLogDir.string().c_str()); + dbenv.set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet + dbenv.set_lg_bsize(0x10000); + dbenv.set_lg_max(1048576); + dbenv.set_lk_max_locks(40000); + dbenv.set_lk_max_objects(40000); + dbenv.set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug + dbenv.set_flags(DB_AUTO_COMMIT, 1); + dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1); + dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1); + int ret = dbenv.open(path.string().c_str(), + DB_CREATE | + DB_INIT_LOCK | + DB_INIT_LOG | + DB_INIT_MPOOL | + DB_INIT_TXN | + DB_THREAD | + DB_RECOVER | + nEnvFlags, + S_IRUSR | S_IWUSR); + if (ret != 0) + return error("CDB() : error %s (%d) opening database environment", DbEnv::strerror(ret), ret); + + fDbEnvInit = true; + fMockDb = false; + return true; +} + +void CDBEnv::MakeMock() +{ + if (fDbEnvInit) + throw runtime_error("CDBEnv::MakeMock(): already initialized"); + + boost::this_thread::interruption_point(); + + printf("CDBEnv::MakeMock()\n"); + + dbenv.set_cachesize(1, 0, 1); + dbenv.set_lg_bsize(10485760*4); + dbenv.set_lg_max(10485760); + dbenv.set_lk_max_locks(10000); + dbenv.set_lk_max_objects(10000); + dbenv.set_flags(DB_AUTO_COMMIT, 1); + dbenv.log_set_config(DB_LOG_IN_MEMORY, 1); + int ret = dbenv.open(NULL, + DB_CREATE | + DB_INIT_LOCK | + DB_INIT_LOG | + DB_INIT_MPOOL | + DB_INIT_TXN | + DB_THREAD | + DB_PRIVATE, + S_IRUSR | S_IWUSR); + if (ret > 0) + throw runtime_error(strprintf("CDBEnv::MakeMock(): error %d opening database environment", ret)); + + fDbEnvInit = true; + fMockDb = true; +} + +CDBEnv::VerifyResult CDBEnv::Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile)) +{ + LOCK(cs_db); + assert(mapFileUseCount.count(strFile) == 0); + + Db db(&dbenv, 0); + int result = db.verify(strFile.c_str(), NULL, NULL, 0); + if (result == 0) + return VERIFY_OK; + else if (recoverFunc == NULL) + return RECOVER_FAIL; + + // Try to recover: + bool fRecovered = (*recoverFunc)(*this, strFile); + return (fRecovered ? RECOVER_OK : RECOVER_FAIL); +} + +bool CDBEnv::Salvage(std::string strFile, bool fAggressive, + std::vector& vResult) +{ + LOCK(cs_db); + assert(mapFileUseCount.count(strFile) == 0); + + u_int32_t flags = DB_SALVAGE; + if (fAggressive) flags |= DB_AGGRESSIVE; + + stringstream strDump; + + Db db(&dbenv, 0); + int result = db.verify(strFile.c_str(), NULL, &strDump, flags); + if (result == DB_VERIFY_BAD) + { + printf("Error: Salvage found errors, all data may not be recoverable.\n"); + if (!fAggressive) + { + printf("Error: Rerun with aggressive mode to ignore errors and continue.\n"); + return false; + } + } + if (result != 0 && result != DB_VERIFY_BAD) + { + printf("ERROR: db salvage failed: %d\n",result); + return false; + } + + // Format of bdb dump is ascii lines: + // header lines... + // HEADER=END + // hexadecimal key + // hexadecimal value + // ... repeated + // DATA=END + + string strLine; + while (!strDump.eof() && strLine != "HEADER=END") + getline(strDump, strLine); // Skip past header + + std::string keyHex, valueHex; + while (!strDump.eof() && keyHex != "DATA=END") + { + getline(strDump, keyHex); + if (keyHex != "DATA_END") + { + getline(strDump, valueHex); + vResult.push_back(make_pair(ParseHex(keyHex),ParseHex(valueHex))); + } + } + + return (result == 0); +} + + +void CDBEnv::CheckpointLSN(std::string strFile) +{ + dbenv.txn_checkpoint(0, 0, 0); + if (fMockDb) + return; + dbenv.lsn_reset(strFile.c_str(), 0); +} + + +CDB::CDB(const char *pszFile, const char* pszMode) : + pdb(NULL), activeTxn(NULL) +{ + int ret; + if (pszFile == NULL) + return; + + fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); + bool fCreate = strchr(pszMode, 'c'); + unsigned int nFlags = DB_THREAD; + if (fCreate) + nFlags |= DB_CREATE; + + { + LOCK(bitdb.cs_db); + if (!bitdb.Open(GetDataDir())) + throw runtime_error("env open failed"); + + strFile = pszFile; + ++bitdb.mapFileUseCount[strFile]; + pdb = bitdb.mapDb[strFile]; + if (pdb == NULL) + { + pdb = new Db(&bitdb.dbenv, 0); + + bool fMockDb = bitdb.IsMock(); + if (fMockDb) + { + DbMpoolFile*mpf = pdb->get_mpf(); + ret = mpf->set_flags(DB_MPOOL_NOFILE, 1); + if (ret != 0) + throw runtime_error(strprintf("CDB() : failed to configure for no temp file backing for database %s", pszFile)); + } + + ret = pdb->open(NULL, // Txn pointer + fMockDb ? NULL : pszFile, // Filename + fMockDb ? pszFile : "main", // Logical db name + DB_BTREE, // Database type + nFlags, // Flags + 0); + + if (ret != 0) + { + delete pdb; + pdb = NULL; + --bitdb.mapFileUseCount[strFile]; + strFile = ""; + throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret)); + } + + if (fCreate && !Exists(string("version"))) + { + bool fTmp = fReadOnly; + fReadOnly = false; + WriteVersion(CLIENT_VERSION); + fReadOnly = fTmp; + } + + bitdb.mapDb[strFile] = pdb; + } + } +} + +void CDB::Flush() +{ + if (activeTxn) + return; + + // Flush database activity from memory pool to disk log + unsigned int nMinutes = 0; + if (fReadOnly) + nMinutes = 1; + + bitdb.dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100)*1024 : 0, nMinutes, 0); +} + +void CDB::Close() +{ + if (!pdb) + return; + if (activeTxn) + activeTxn->abort(); + activeTxn = NULL; + pdb = NULL; + + Flush(); + + { + LOCK(bitdb.cs_db); + --bitdb.mapFileUseCount[strFile]; + } +} + +void CDBEnv::CloseDb(const string& strFile) +{ + { + LOCK(cs_db); + if (mapDb[strFile] != NULL) + { + // Close the database handle + Db* pdb = mapDb[strFile]; + pdb->close(0); + delete pdb; + mapDb[strFile] = NULL; + } + } +} + +bool CDBEnv::RemoveDb(const string& strFile) +{ + this->CloseDb(strFile); + + LOCK(cs_db); + int rc = dbenv.dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT); + return (rc == 0); +} + +bool CDB::Rewrite(const string& strFile, const char* pszSkip) +{ + while (true) + { + { + LOCK(bitdb.cs_db); + if (!bitdb.mapFileUseCount.count(strFile) || bitdb.mapFileUseCount[strFile] == 0) + { + // Flush log data to the dat file + bitdb.CloseDb(strFile); + bitdb.CheckpointLSN(strFile); + bitdb.mapFileUseCount.erase(strFile); + + bool fSuccess = true; + printf("Rewriting %s...\n", strFile.c_str()); + string strFileRes = strFile + ".rewrite"; + { // surround usage of db with extra {} + CDB db(strFile.c_str(), "r"); + Db* pdbCopy = new Db(&bitdb.dbenv, 0); + + int ret = pdbCopy->open(NULL, // Txn pointer + strFileRes.c_str(), // Filename + "main", // Logical db name + DB_BTREE, // Database type + DB_CREATE, // Flags + 0); + if (ret > 0) + { + printf("Cannot create database file %s\n", strFileRes.c_str()); + fSuccess = false; + } + + Dbc* pcursor = db.GetCursor(); + if (pcursor) + while (fSuccess) + { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT); + if (ret == DB_NOTFOUND) + { + pcursor->close(); + break; + } + else if (ret != 0) + { + pcursor->close(); + fSuccess = false; + break; + } + if (pszSkip && + strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0) + continue; + if (strncmp(&ssKey[0], "\x07version", 8) == 0) + { + // Update version: + ssValue.clear(); + ssValue << CLIENT_VERSION; + } + Dbt datKey(&ssKey[0], ssKey.size()); + Dbt datValue(&ssValue[0], ssValue.size()); + int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE); + if (ret2 > 0) + fSuccess = false; + } + if (fSuccess) + { + db.Close(); + bitdb.CloseDb(strFile); + if (pdbCopy->close(0)) + fSuccess = false; + delete pdbCopy; + } + } + if (fSuccess) + { + Db dbA(&bitdb.dbenv, 0); + if (dbA.remove(strFile.c_str(), NULL, 0)) + fSuccess = false; + Db dbB(&bitdb.dbenv, 0); + if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0)) + fSuccess = false; + } + if (!fSuccess) + printf("Rewriting of %s FAILED!\n", strFileRes.c_str()); + return fSuccess; + } + } + MilliSleep(100); + } + return false; +} + + +void CDBEnv::Flush(bool fShutdown) +{ + int64 nStart = GetTimeMillis(); + // Flush log data to the actual data file + // on all files that are not in use + printf("Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started"); + if (!fDbEnvInit) + return; + { + LOCK(cs_db); + map::iterator mi = mapFileUseCount.begin(); + while (mi != mapFileUseCount.end()) + { + string strFile = (*mi).first; + int nRefCount = (*mi).second; + printf("%s refcount=%d\n", strFile.c_str(), nRefCount); + if (nRefCount == 0) + { + // Move log data to the dat file + CloseDb(strFile); + printf("%s checkpoint\n", strFile.c_str()); + dbenv.txn_checkpoint(0, 0, 0); + printf("%s detach\n", strFile.c_str()); + if (!fMockDb) + dbenv.lsn_reset(strFile.c_str(), 0); + printf("%s closed\n", strFile.c_str()); + mapFileUseCount.erase(mi++); + } + else + mi++; + } + printf("DBFlush(%s)%s ended %15"PRI64d"ms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started", GetTimeMillis() - nStart); + if (fShutdown) + { + char** listp; + if (mapFileUseCount.empty()) + { + dbenv.log_archive(&listp, DB_ARCH_REMOVE); + Close(); + if (!fMockDb) + boost::filesystem::remove_all(path / "database"); + } + } + } +} + + + + + + + + + + + +// +// CAddrDB +// + + +CAddrDB::CAddrDB() +{ + pathAddr = GetDataDir() / "peers.dat"; +} + +bool CAddrDB::Write(const CAddrMan& addr) +{ + // Generate random temporary filename + unsigned short randv = 0; + RAND_bytes((unsigned char *)&randv, sizeof(randv)); + std::string tmpfn = strprintf("peers.dat.%04x", randv); + + // serialize addresses, checksum data up to that point, then append csum + CDataStream ssPeers(SER_DISK, CLIENT_VERSION); + ssPeers << FLATDATA(pchMessageStart); + ssPeers << addr; + uint256 hash = Hash(ssPeers.begin(), ssPeers.end()); + ssPeers << hash; + + // open temp output file, and associate with CAutoFile + boost::filesystem::path pathTmp = GetDataDir() / tmpfn; + FILE *file = fopen(pathTmp.string().c_str(), "wb"); + CAutoFile fileout = CAutoFile(file, SER_DISK, CLIENT_VERSION); + if (!fileout) + return error("CAddrman::Write() : open failed"); + + // Write and commit header, data + try { + fileout << ssPeers; + } + catch (std::exception &e) { + return error("CAddrman::Write() : I/O error"); + } + FileCommit(fileout); + fileout.fclose(); + + // replace existing peers.dat, if any, with new peers.dat.XXXX + if (!RenameOver(pathTmp, pathAddr)) + return error("CAddrman::Write() : Rename-into-place failed"); + + return true; +} + +bool CAddrDB::Read(CAddrMan& addr) +{ + // open input file, and associate with CAutoFile + FILE *file = fopen(pathAddr.string().c_str(), "rb"); + CAutoFile filein = CAutoFile(file, SER_DISK, CLIENT_VERSION); + if (!filein) + return error("CAddrman::Read() : open failed"); + + // use file size to size memory buffer + int fileSize = GetFilesize(filein); + int dataSize = fileSize - sizeof(uint256); + //Don't try to resize to a negative number if file is small + if ( dataSize < 0 ) dataSize = 0; + vector vchData; + vchData.resize(dataSize); + uint256 hashIn; + + // read data and checksum from file + try { + filein.read((char *)&vchData[0], dataSize); + filein >> hashIn; + } + catch (std::exception &e) { + return error("CAddrman::Read() 2 : I/O error or stream data corrupted"); + } + filein.fclose(); + + CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION); + + // verify stored checksum matches input data + uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end()); + if (hashIn != hashTmp) + return error("CAddrman::Read() : checksum mismatch; data corrupted"); + + unsigned char pchMsgTmp[4]; + try { + // de-serialize file header (pchMessageStart magic number) and + ssPeers >> FLATDATA(pchMsgTmp); + + // verify the network matches ours + if (memcmp(pchMsgTmp, pchMessageStart, sizeof(pchMsgTmp))) + return error("CAddrman::Read() : invalid network magic number"); + + // de-serialize address data into one CAddrMan object + ssPeers >> addr; + } + catch (std::exception &e) { + return error("CAddrman::Read() : I/O error or stream data corrupted"); + } + + return true; +} + diff --git a/src/db.h b/src/db.h new file mode 100644 index 0000000..ea440c4 --- /dev/null +++ b/src/db.h @@ -0,0 +1,327 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_DB_H +#define BITCOIN_DB_H + +#include "main.h" + +#include +#include +#include + +#include + +class CAddress; +class CAddrMan; +class CBlockLocator; +class CDiskBlockIndex; +class CMasterKey; +class COutPoint; +class CWallet; +class CWalletTx; + +extern unsigned int nWalletDBUpdated; + +void ThreadFlushWalletDB(const std::string& strWalletFile); +bool BackupWallet(const CWallet& wallet, const std::string& strDest); + + +class CDBEnv +{ +private: + bool fDbEnvInit; + bool fMockDb; + boost::filesystem::path path; + + void EnvShutdown(); + +public: + mutable CCriticalSection cs_db; + DbEnv dbenv; + std::map mapFileUseCount; + std::map mapDb; + + CDBEnv(); + ~CDBEnv(); + void MakeMock(); + bool IsMock() { return fMockDb; } + + /* + * Verify that database file strFile is OK. If it is not, + * call the callback to try to recover. + * This must be called BEFORE strFile is opened. + * Returns true if strFile is OK. + */ + enum VerifyResult { VERIFY_OK, RECOVER_OK, RECOVER_FAIL }; + VerifyResult Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile)); + /* + * Salvage data from a file that Verify says is bad. + * fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method documentation). + * Appends binary key/value pairs to vResult, returns true if successful. + * NOTE: reads the entire database into memory, so cannot be used + * for huge databases. + */ + typedef std::pair, std::vector > KeyValPair; + bool Salvage(std::string strFile, bool fAggressive, std::vector& vResult); + + bool Open(const boost::filesystem::path &path); + void Close(); + void Flush(bool fShutdown); + void CheckpointLSN(std::string strFile); + + void CloseDb(const std::string& strFile); + bool RemoveDb(const std::string& strFile); + + DbTxn *TxnBegin(int flags=DB_TXN_WRITE_NOSYNC) + { + DbTxn* ptxn = NULL; + int ret = dbenv.txn_begin(NULL, &ptxn, flags); + if (!ptxn || ret != 0) + return NULL; + return ptxn; + } +}; + +extern CDBEnv bitdb; + + +/** RAII class that provides access to a Berkeley database */ +class CDB +{ +protected: + Db* pdb; + std::string strFile; + DbTxn *activeTxn; + bool fReadOnly; + + explicit CDB(const char* pszFile, const char* pszMode="r+"); + ~CDB() { Close(); } +public: + void Flush(); + void Close(); +private: + CDB(const CDB&); + void operator=(const CDB&); + +protected: + template + bool Read(const K& key, T& value) + { + if (!pdb) + return false; + + // Key + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], ssKey.size()); + + // Read + Dbt datValue; + datValue.set_flags(DB_DBT_MALLOC); + int ret = pdb->get(activeTxn, &datKey, &datValue, 0); + memset(datKey.get_data(), 0, datKey.get_size()); + if (datValue.get_data() == NULL) + return false; + + // Unserialize value + try { + CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION); + ssValue >> value; + } + catch (std::exception &e) { + return false; + } + + // Clear and free memory + memset(datValue.get_data(), 0, datValue.get_size()); + free(datValue.get_data()); + return (ret == 0); + } + + template + bool Write(const K& key, const T& value, bool fOverwrite=true) + { + if (!pdb) + return false; + if (fReadOnly) + assert(!"Write called on database in read-only mode"); + + // Key + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], ssKey.size()); + + // Value + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + ssValue.reserve(10000); + ssValue << value; + Dbt datValue(&ssValue[0], ssValue.size()); + + // Write + int ret = pdb->put(activeTxn, &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE)); + + // Clear memory in case it was a private key + memset(datKey.get_data(), 0, datKey.get_size()); + memset(datValue.get_data(), 0, datValue.get_size()); + return (ret == 0); + } + + template + bool Erase(const K& key) + { + if (!pdb) + return false; + if (fReadOnly) + assert(!"Erase called on database in read-only mode"); + + // Key + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], ssKey.size()); + + // Erase + int ret = pdb->del(activeTxn, &datKey, 0); + + // Clear memory + memset(datKey.get_data(), 0, datKey.get_size()); + return (ret == 0 || ret == DB_NOTFOUND); + } + + template + bool Exists(const K& key) + { + if (!pdb) + return false; + + // Key + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], ssKey.size()); + + // Exists + int ret = pdb->exists(activeTxn, &datKey, 0); + + // Clear memory + memset(datKey.get_data(), 0, datKey.get_size()); + return (ret == 0); + } + + Dbc* GetCursor() + { + if (!pdb) + return NULL; + Dbc* pcursor = NULL; + int ret = pdb->cursor(NULL, &pcursor, 0); + if (ret != 0) + return NULL; + return pcursor; + } + + int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, unsigned int fFlags=DB_NEXT) + { + // Read at cursor + Dbt datKey; + if (fFlags == DB_SET || fFlags == DB_SET_RANGE || fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) + { + datKey.set_data(&ssKey[0]); + datKey.set_size(ssKey.size()); + } + Dbt datValue; + if (fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) + { + datValue.set_data(&ssValue[0]); + datValue.set_size(ssValue.size()); + } + datKey.set_flags(DB_DBT_MALLOC); + datValue.set_flags(DB_DBT_MALLOC); + int ret = pcursor->get(&datKey, &datValue, fFlags); + if (ret != 0) + return ret; + else if (datKey.get_data() == NULL || datValue.get_data() == NULL) + return 99999; + + // Convert to streams + ssKey.SetType(SER_DISK); + ssKey.clear(); + ssKey.write((char*)datKey.get_data(), datKey.get_size()); + ssValue.SetType(SER_DISK); + ssValue.clear(); + ssValue.write((char*)datValue.get_data(), datValue.get_size()); + + // Clear and free memory + memset(datKey.get_data(), 0, datKey.get_size()); + memset(datValue.get_data(), 0, datValue.get_size()); + free(datKey.get_data()); + free(datValue.get_data()); + return 0; + } + +public: + bool TxnBegin() + { + if (!pdb || activeTxn) + return false; + DbTxn* ptxn = bitdb.TxnBegin(); + if (!ptxn) + return false; + activeTxn = ptxn; + return true; + } + + bool TxnCommit() + { + if (!pdb || !activeTxn) + return false; + int ret = activeTxn->commit(0); + activeTxn = NULL; + return (ret == 0); + } + + bool TxnAbort() + { + if (!pdb || !activeTxn) + return false; + int ret = activeTxn->abort(); + activeTxn = NULL; + return (ret == 0); + } + + bool ReadVersion(int& nVersion) + { + nVersion = 0; + return Read(std::string("version"), nVersion); + } + + bool WriteVersion(int nVersion) + { + return Write(std::string("version"), nVersion); + } + + bool static Rewrite(const std::string& strFile, const char* pszSkip = NULL); +}; + + + + + + + + +/** Access to the (IP) address database (peers.dat) */ +class CAddrDB +{ +private: + boost::filesystem::path pathAddr; +public: + CAddrDB(); + bool Write(const CAddrMan& addr); + bool Read(CAddrMan& addr); +}; + +#endif // BITCOIN_DB_H diff --git a/src/hash.cpp b/src/hash.cpp new file mode 100644 index 0000000..bddd8ab --- /dev/null +++ b/src/hash.cpp @@ -0,0 +1,58 @@ +#include "hash.h" + +inline uint32_t ROTL32 ( uint32_t x, int8_t r ) +{ + return (x << r) | (x >> (32 - r)); +} + +unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector& vDataToHash) +{ + // The following is MurmurHash3 (x86_32), see http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp + uint32_t h1 = nHashSeed; + const uint32_t c1 = 0xcc9e2d51; + const uint32_t c2 = 0x1b873593; + + const int nblocks = vDataToHash.size() / 4; + + //---------- + // body + const uint32_t * blocks = (const uint32_t *)(&vDataToHash[0] + nblocks*4); + + for(int i = -nblocks; i; i++) + { + uint32_t k1 = blocks[i]; + + k1 *= c1; + k1 = ROTL32(k1,15); + k1 *= c2; + + h1 ^= k1; + h1 = ROTL32(h1,13); + h1 = h1*5+0xe6546b64; + } + + //---------- + // tail + const uint8_t * tail = (const uint8_t*)(&vDataToHash[0] + nblocks*4); + + uint32_t k1 = 0; + + switch(vDataToHash.size() & 3) + { + case 3: k1 ^= tail[2] << 16; + case 2: k1 ^= tail[1] << 8; + case 1: k1 ^= tail[0]; + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + h1 ^= vDataToHash.size(); + h1 ^= h1 >> 16; + h1 *= 0x85ebca6b; + h1 ^= h1 >> 13; + h1 *= 0xc2b2ae35; + h1 ^= h1 >> 16; + + return h1; +} diff --git a/src/hash.h b/src/hash.h new file mode 100644 index 0000000..536ab71 --- /dev/null +++ b/src/hash.h @@ -0,0 +1,126 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_HASH_H +#define BITCOIN_HASH_H + +#include "uint256.h" +#include "serialize.h" + +#include +#include +#include + +template +inline uint256 Hash(const T1 pbegin, const T1 pend) +{ + static unsigned char pblank[1]; + uint256 hash1; + SHA256((pbegin == pend ? pblank : (unsigned char*)&pbegin[0]), (pend - pbegin) * sizeof(pbegin[0]), (unsigned char*)&hash1); + uint256 hash2; + SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} + +class CHashWriter +{ +private: + SHA256_CTX ctx; + +public: + int nType; + int nVersion; + + void Init() { + SHA256_Init(&ctx); + } + + CHashWriter(int nTypeIn, int nVersionIn) : nType(nTypeIn), nVersion(nVersionIn) { + Init(); + } + + CHashWriter& write(const char *pch, size_t size) { + SHA256_Update(&ctx, pch, size); + return (*this); + } + + // invalidates the object + uint256 GetHash() { + uint256 hash1; + SHA256_Final((unsigned char*)&hash1, &ctx); + uint256 hash2; + SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; + } + + template + CHashWriter& operator<<(const T& obj) { + // Serialize to this stream + ::Serialize(*this, obj, nType, nVersion); + return (*this); + } +}; + + +template +inline uint256 Hash(const T1 p1begin, const T1 p1end, + const T2 p2begin, const T2 p2end) +{ + static unsigned char pblank[1]; + uint256 hash1; + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0])); + SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0])); + SHA256_Final((unsigned char*)&hash1, &ctx); + uint256 hash2; + SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} + +template +inline uint256 Hash(const T1 p1begin, const T1 p1end, + const T2 p2begin, const T2 p2end, + const T3 p3begin, const T3 p3end) +{ + static unsigned char pblank[1]; + uint256 hash1; + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0])); + SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0])); + SHA256_Update(&ctx, (p3begin == p3end ? pblank : (unsigned char*)&p3begin[0]), (p3end - p3begin) * sizeof(p3begin[0])); + SHA256_Final((unsigned char*)&hash1, &ctx); + uint256 hash2; + SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} + +template +uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION) +{ + CHashWriter ss(nType, nVersion); + ss << obj; + return ss.GetHash(); +} + +template +inline uint160 Hash160(const T1 pbegin, const T1 pend) +{ + static unsigned char pblank[1]; + uint256 hash1; + SHA256((pbegin == pend ? pblank : (unsigned char*)&pbegin[0]), (pend - pbegin) * sizeof(pbegin[0]), (unsigned char*)&hash1); + uint160 hash2; + RIPEMD160((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} + +inline uint160 Hash160(const std::vector& vch) +{ + return Hash160(vch.begin(), vch.end()); +} + +unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector& vDataToHash); + +#endif diff --git a/src/init.cpp b/src/init.cpp new file mode 100644 index 0000000..ea9d18e --- /dev/null +++ b/src/init.cpp @@ -0,0 +1,1181 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "txdb.h" +#include "walletdb.h" +#include "bitcoinrpc.h" +#include "net.h" +#include "init.h" +#include "util.h" +#include "ui_interface.h" + +#include +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif + +#if defined(USE_SSE2) +#if !defined(MAC_OSX) && (defined(_M_IX86) || defined(__i386__) || defined(__i386)) +#ifdef _MSC_VER +// MSVC 64bit is unable to use inline asm +#include +#else +// GCC Linux or i686-w64-mingw32 +#include +#endif +#endif +#endif + +using namespace std; +using namespace boost; + +CWallet* pwalletMain; +CClientUIInterface uiInterface; + +#ifdef WIN32 +// Win32 LevelDB doesn't use filedescriptors, and the ones used for +// accessing block files, don't count towards to fd_set size limit +// anyway. +#define MIN_CORE_FILEDESCRIPTORS 0 +#else +#define MIN_CORE_FILEDESCRIPTORS 150 +#endif + +// Used to pass flags to the Bind() function +enum BindFlags { + BF_NONE = 0, + BF_EXPLICIT = (1U << 0), + BF_REPORT_ERROR = (1U << 1) +}; + +////////////////////////////////////////////////////////////////////////////// +// +// Shutdown +// + +// +// Thread management and startup/shutdown: +// +// The network-processing threads are all part of a thread group +// created by AppInit() or the Qt main() function. +// +// A clean exit happens when StartShutdown() or the SIGTERM +// signal handler sets fRequestShutdown, which triggers +// the DetectShutdownThread(), which interrupts the main thread group. +// DetectShutdownThread() then exits, which causes AppInit() to +// continue (it .joins the shutdown thread). +// Shutdown() is then +// called to clean up database connections, and stop other +// threads that should only be stopped after the main network-processing +// threads have exited. +// +// Note that if running -daemon the parent process returns from AppInit2 +// before adding any threads to the threadGroup, so .join_all() returns +// immediately and the parent exits from main(). +// +// Shutdown for Qt is very similar, only it uses a QTimer to detect +// fRequestShutdown getting set, and then does the normal Qt +// shutdown thing. +// + +volatile bool fRequestShutdown = false; + +void StartShutdown() +{ + fRequestShutdown = true; +} +bool ShutdownRequested() +{ + return fRequestShutdown; +} + +static CCoinsViewDB *pcoinsdbview; + +void Shutdown() +{ + printf("Shutdown : In progress...\n"); + static CCriticalSection cs_Shutdown; + TRY_LOCK(cs_Shutdown, lockShutdown); + if (!lockShutdown) return; + + RenameThread("bitcoin-shutoff"); + nTransactionsUpdated++; + StopRPCThreads(); + ShutdownRPCMining(); + if (pwalletMain) + bitdb.Flush(false); + GenerateBitcoins(false, NULL); + StopNode(); + { + LOCK(cs_main); + if (pwalletMain) + pwalletMain->SetBestChain(CBlockLocator(pindexBest)); + if (pblocktree) + pblocktree->Flush(); + if (pcoinsTip) + pcoinsTip->Flush(); + delete pcoinsTip; pcoinsTip = NULL; + delete pcoinsdbview; pcoinsdbview = NULL; + delete pblocktree; pblocktree = NULL; + } + if (pwalletMain) + bitdb.Flush(true); + boost::filesystem::remove(GetPidFile()); + UnregisterWallet(pwalletMain); + if (pwalletMain) + delete pwalletMain; + printf("Shutdown : done\n"); +} + +// +// Signal handlers are very limited in what they are allowed to do, so: +// +void DetectShutdownThread(boost::thread_group* threadGroup) +{ + // Tell the main threads to shutdown. + while (!fRequestShutdown) + { + MilliSleep(200); + if (fRequestShutdown) + threadGroup->interrupt_all(); + } +} + +void HandleSIGTERM(int) +{ + fRequestShutdown = true; +} + +void HandleSIGHUP(int) +{ + fReopenDebugLog = true; +} + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// Start +// +#if !defined(QT_GUI) +bool AppInit(int argc, char* argv[]) +{ + boost::thread_group threadGroup; + boost::thread* detectShutdownThread = NULL; + + bool fRet = false; + try + { + // + // Parameters + // + // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main() + ParseParameters(argc, argv); + if (!boost::filesystem::is_directory(GetDataDir(false))) + { + fprintf(stderr, "Error: Specified directory does not exist\n"); + Shutdown(); + } + ReadConfigFile(mapArgs, mapMultiArgs); + + if (mapArgs.count("-?") || mapArgs.count("--help")) + { + // First part of help message is specific to bitcoind / RPC client + std::string strUsage = _("CryptoLottoToken version") + " " + FormatFullVersion() + "\n\n" + + _("Usage:") + "\n" + + " CryptoLottoTokend [options] " + "\n" + + " CryptoLottoTokend [options] [params] " + _("Send command to -server or CryptoLottoTokend") + "\n" + + " CryptoLottoTokend [options] help " + _("List commands") + "\n" + + " CryptoLottoTokend [options] help " + _("Get help for a command") + "\n"; + + strUsage += "\n" + HelpMessage(); + + fprintf(stdout, "%s", strUsage.c_str()); + return false; + } + + // Command-line RPC + for (int i = 1; i < argc; i++) + if (!IsSwitchChar(argv[i][0]) && !boost::algorithm::istarts_with(argv[i], "CryptoLottoToken:")) + fCommandLine = true; + + if (fCommandLine) + { + int ret = CommandLineRPC(argc, argv); + exit(ret); + } +#if !defined(WIN32) + fDaemon = GetBoolArg("-daemon"); + if (fDaemon) + { + // Daemonize + pid_t pid = fork(); + if (pid < 0) + { + fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno); + return false; + } + if (pid > 0) // Parent process, pid is child process id + { + CreatePidFile(GetPidFile(), pid); + return true; + } + // Child process falls through to rest of initialization + + pid_t sid = setsid(); + if (sid < 0) + fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno); + } +#endif + + detectShutdownThread = new boost::thread(boost::bind(&DetectShutdownThread, &threadGroup)); + fRet = AppInit2(threadGroup); + } + catch (std::exception& e) { + PrintExceptionContinue(&e, "AppInit()"); + } catch (...) { + PrintExceptionContinue(NULL, "AppInit()"); + } + if (!fRet) { + if (detectShutdownThread) + detectShutdownThread->interrupt(); + threadGroup.interrupt_all(); + } + + if (detectShutdownThread) + { + detectShutdownThread->join(); + delete detectShutdownThread; + detectShutdownThread = NULL; + } + Shutdown(); + + return fRet; +} + +extern void noui_connect(); +int main(int argc, char* argv[]) +{ + bool fRet = false; + + // Connect bitcoind signal handlers + noui_connect(); + + fRet = AppInit(argc, argv); + + if (fRet && fDaemon) + return 0; + + return (fRet ? 0 : 1); +} +#endif + +bool static InitError(const std::string &str) +{ + uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_ERROR); + return false; +} + +bool static InitWarning(const std::string &str) +{ + uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING); + return true; +} + +bool static Bind(const CService &addr, unsigned int flags) { + if (!(flags & BF_EXPLICIT) && IsLimited(addr)) + return false; + std::string strError; + if (!BindListenPort(addr, strError)) { + if (flags & BF_REPORT_ERROR) + return InitError(strError); + return false; + } + return true; +} + +// Core-specific options shared between UI and daemon +std::string HelpMessage() +{ + string strUsage = _("Options:") + "\n" + + " -? " + _("This help message") + "\n" + + " -conf= " + _("Specify configuration file (default: CryptoLottoToken.conf)") + "\n" + + " -pid= " + _("Specify pid file (default: CryptoLottoTokend.pid)") + "\n" + + " -gen " + _("Generate coins (default: 0)") + "\n" + + " -datadir= " + _("Specify data directory") + "\n" + + " -dbcache= " + _("Set database cache size in megabytes (default: 25)") + "\n" + + " -timeout= " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n" + + " -proxy= " + _("Connect through socks proxy") + "\n" + + " -socks= " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\n" + + " -tor= " + _("Use proxy to reach tor hidden services (default: same as -proxy)") + "\n" + " -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n" + + " -port= " + _("Listen for connections on (default: 9157 or testnet: 19157)") + "\n" + + " -maxconnections= " + _("Maintain at most connections to peers (default: 125)") + "\n" + + " -addnode= " + _("Add a node to connect to and attempt to keep the connection open") + "\n" + + " -connect= " + _("Connect only to the specified node(s)") + "\n" + + " -seednode= " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n" + + " -externalip= " + _("Specify your own public address") + "\n" + + " -onlynet= " + _("Only connect to nodes in network (IPv4, IPv6 or Tor)") + "\n" + + " -discover " + _("Discover own IP address (default: 1 when listening and no -externalip)") + "\n" + + " -checkpoints " + _("Only accept block chain matching built-in checkpoints (default: 1)") + "\n" + + " -irc " + _("Find peers using internet relay chat (default: 0)") + "\n" + + " -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n" + + " -bind= " + _("Bind to given address and always listen on it. Use [host]:port notation for IPv6") + "\n" + + " -dnsseed " + _("Find peers using DNS lookup (default: 1 unless -connect)") + "\n" + + " -banscore= " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n" + + " -bantime= " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n" + + " -maxreceivebuffer= " + _("Maximum per-connection receive buffer, *1000 bytes (default: 5000)") + "\n" + + " -maxsendbuffer= " + _("Maximum per-connection send buffer, *1000 bytes (default: 1000)") + "\n" + + " -bloomfilters " + _("Allow peers to set bloom filters (default: 1)") + "\n" + +#ifdef USE_UPNP +#if USE_UPNP + " -upnp " + _("Use UPnP to map the listening port (default: 1 when listening)") + "\n" + +#else + " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n" + +#endif +#endif + " -paytxfee= " + _("Fee per KB to add to transactions you send") + "\n" + + " -mininput= " + _("When creating transactions, ignore inputs with value less than this (default: 1000000)") + "\n" + +#ifdef QT_GUI + " -server " + _("Accept command line and JSON-RPC commands") + "\n" + +#endif +#if !defined(WIN32) && !defined(QT_GUI) + " -daemon " + _("Run in the background as a daemon and accept commands") + "\n" + +#endif + " -testnet " + _("Use the test network") + "\n" + + " -debug " + _("Output extra debugging information. Implies all other -debug* options") + "\n" + + " -debugnet " + _("Output extra network debugging information") + "\n" + + " -logtimestamps " + _("Prepend debug output with timestamp (default: 1)") + "\n" + + " -shrinkdebugfile " + _("Shrink debug.log file on client startup (default: 1 when no -debug)") + "\n" + + " -printtoconsole " + _("Send trace/debug info to console instead of debug.log file") + "\n" + +#ifdef WIN32 + " -printtodebugger " + _("Send trace/debug info to debugger") + "\n" + +#endif + " -rpcuser= " + _("Username for JSON-RPC connections") + "\n" + + " -rpcpassword= " + _("Password for JSON-RPC connections") + "\n" + + " -rpcport= " + _("Listen for JSON-RPC connections on (default: 9154 or testnet: 19154)") + "\n" + + " -rpcallowip= " + _("Allow JSON-RPC connections from specified IP address") + "\n" + +#ifndef QT_GUI + " -rpcconnect= " + _("Send commands to node running on (default: 127.0.0.1)") + "\n" + +#endif + " -rpcthreads= " + _("Set the number of threads to service RPC calls (default: 4)") + "\n" + + " -blocknotify= " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" + + " -walletnotify= " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n" + + " -alertnotify= " + _("Execute command when a relevant alert is received (%s in cmd is replaced by message)") + "\n" + + " -upgradewallet " + _("Upgrade wallet to latest format") + "\n" + + " -keypool= " + _("Set key pool size to (default: 100)") + "\n" + + " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n" + + " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n" + + " -checkblocks= " + _("How many blocks to check at startup (default: 288, 0 = all)") + "\n" + + " -checklevel= " + _("How thorough the block verification is (0-4, default: 3)") + "\n" + + " -txindex " + _("Maintain a full transaction index (default: 0)") + "\n" + + " -loadblock= " + _("Imports blocks from external blk000??.dat file") + "\n" + + " -reindex " + _("Rebuild block chain index from current blk000??.dat files") + "\n" + + " -par= " + _("Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0)") + "\n" + + + "\n" + _("Block creation options:") + "\n" + + " -blockminsize= " + _("Set minimum block size in bytes (default: 0)") + "\n" + + " -blockmaxsize= " + _("Set maximum block size in bytes (default: 250000)") + "\n" + + " -blockprioritysize= " + _("Set maximum size of high-priority/low-fee transactions in bytes (default: 27000)") + "\n" + + + "\n" + _("SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions)") + "\n" + + " -rpcssl " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n" + + " -rpcsslcertificatechainfile= " + _("Server certificate file (default: server.cert)") + "\n" + + " -rpcsslprivatekeyfile= " + _("Server private key (default: server.pem)") + "\n" + + " -rpcsslciphers= " + _("Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)") + "\n"; + + return strUsage; +} + +struct CImportingNow +{ + CImportingNow() { + assert(fImporting == false); + fImporting = true; + } + + ~CImportingNow() { + assert(fImporting == true); + fImporting = false; + } +}; + +void ThreadImport(std::vector vImportFiles) +{ + RenameThread("bitcoin-loadblk"); + + // -reindex + if (fReindex) { + CImportingNow imp; + int nFile = 0; + while (true) { + CDiskBlockPos pos(nFile, 0); + FILE *file = OpenBlockFile(pos, true); + if (!file) + break; + printf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile); + LoadExternalBlockFile(file, &pos); + nFile++; + } + pblocktree->WriteReindexing(false); + fReindex = false; + printf("Reindexing finished\n"); + // To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked): + InitBlockIndex(); + } + + // hardcoded $DATADIR/bootstrap.dat + filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat"; + if (filesystem::exists(pathBootstrap)) { + FILE *file = fopen(pathBootstrap.string().c_str(), "rb"); + if (file) { + CImportingNow imp; + filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; + printf("Importing bootstrap.dat...\n"); + LoadExternalBlockFile(file); + RenameOver(pathBootstrap, pathBootstrapOld); + } + } + + // -loadblock= + BOOST_FOREACH(boost::filesystem::path &path, vImportFiles) { + FILE *file = fopen(path.string().c_str(), "rb"); + if (file) { + CImportingNow imp; + printf("Importing %s...\n", path.string().c_str()); + LoadExternalBlockFile(file); + } + } +} + +/** Initialize bitcoin. + * @pre Parameters should be parsed and config file should be read. + */ +bool AppInit2(boost::thread_group& threadGroup) +{ + // ********************************************************* Step 1: setup +#ifdef _MSC_VER + // Turn off Microsoft heap dump noise + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); +#endif +#if _MSC_VER >= 1400 + // Disable confusing "helpful" text message on abort, Ctrl-C + _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); +#endif +#ifdef WIN32 + // Enable Data Execution Prevention (DEP) + // Minimum supported OS versions: WinXP SP3, WinVista >= SP1, Win Server 2008 + // A failure is non-critical and needs no further attention! +#ifndef PROCESS_DEP_ENABLE +// We define this here, because GCCs winbase.h limits this to _WIN32_WINNT >= 0x0601 (Windows 7), +// which is not correct. Can be removed, when GCCs winbase.h is fixed! +#define PROCESS_DEP_ENABLE 0x00000001 +#endif + typedef BOOL (WINAPI *PSETPROCDEPPOL)(DWORD); + PSETPROCDEPPOL setProcDEPPol = (PSETPROCDEPPOL)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetProcessDEPPolicy"); + if (setProcDEPPol != NULL) setProcDEPPol(PROCESS_DEP_ENABLE); + + // Initialize Windows Sockets + WSADATA wsadata; + int ret = WSAStartup(MAKEWORD(2,2), &wsadata); + if (ret != NO_ERROR || LOBYTE(wsadata.wVersion ) != 2 || HIBYTE(wsadata.wVersion) != 2) + { + return InitError(strprintf("Error: Winsock library failed to start (WSAStartup returned error %d)", ret)); + } +#endif +#ifndef WIN32 + umask(077); + + // Clean shutdown on SIGTERM + struct sigaction sa; + sa.sa_handler = HandleSIGTERM; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + + // Reopen debug.log on SIGHUP + struct sigaction sa_hup; + sa_hup.sa_handler = HandleSIGHUP; + sigemptyset(&sa_hup.sa_mask); + sa_hup.sa_flags = 0; + sigaction(SIGHUP, &sa_hup, NULL); +#endif + +#if defined(USE_SSE2) + unsigned int cpuid_edx=0; +#if !defined(MAC_OSX) && (defined(_M_IX86) || defined(__i386__) || defined(__i386)) + // 32bit x86 Linux or Windows, detect cpuid features +#if defined(_MSC_VER) + // MSVC + int x86cpuid[4]; + __cpuid(x86cpuid, 1); + cpuid_edx = (unsigned int)buffer[3]; +#else + // Linux or i686-w64-mingw32 (gcc-4.6.3) + unsigned int eax, ebx, ecx; + __get_cpuid(1, &eax, &ebx, &ecx, &cpuid_edx); +#endif +#endif +#endif + + // ********************************************************* Step 2: parameter interactions + + fTestNet = GetBoolArg("-testnet"); + fBloomFilters = GetBoolArg("-bloomfilters", true); + if (fBloomFilters) + nLocalServices |= NODE_BLOOM; + + // FedoraCoin: Keep irc seeding on by default for now. +// if (fTestNet) +// { + SoftSetBoolArg("-irc", true); +// } + + if (mapArgs.count("-bind")) { + // when specifying an explicit binding address, you want to listen on it + // even when -connect or -proxy is specified + SoftSetBoolArg("-listen", true); + } + + if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) { + // when only connecting to trusted nodes, do not seed via DNS, or listen by default + SoftSetBoolArg("-dnsseed", false); + SoftSetBoolArg("-listen", false); + } + + if (mapArgs.count("-proxy")) { + // to protect privacy, do not listen by default if a proxy server is specified + SoftSetBoolArg("-listen", false); + } + + if (!GetBoolArg("-listen", true)) { + // do not map ports or try to retrieve public IP when not listening (pointless) + SoftSetBoolArg("-upnp", false); + SoftSetBoolArg("-discover", false); + } + + if (mapArgs.count("-externalip")) { + // if an explicit public IP is specified, do not try to find others + SoftSetBoolArg("-discover", false); + } + + if (GetBoolArg("-salvagewallet")) { + // Rewrite just private keys: rescan to find transactions + SoftSetBoolArg("-rescan", true); + } + + // Make sure enough file descriptors are available + int nBind = std::max((int)mapArgs.count("-bind"), 1); + nMaxConnections = GetArg("-maxconnections", 125); + nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0); + int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS); + if (nFD < MIN_CORE_FILEDESCRIPTORS) + return InitError(_("Not enough file descriptors available.")); + if (nFD - MIN_CORE_FILEDESCRIPTORS < nMaxConnections) + nMaxConnections = nFD - MIN_CORE_FILEDESCRIPTORS; + + // ********************************************************* Step 3: parameter-to-internal-flags + + fDebug = GetBoolArg("-debug"); + fBenchmark = GetBoolArg("-benchmark"); + + // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency + nScriptCheckThreads = GetArg("-par", 0); + if (nScriptCheckThreads <= 0) + nScriptCheckThreads += boost::thread::hardware_concurrency(); + if (nScriptCheckThreads <= 1) + nScriptCheckThreads = 0; + else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS) + nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS; + + // -debug implies fDebug* + if (fDebug) + fDebugNet = true; + else + fDebugNet = GetBoolArg("-debugnet"); + + if (fDaemon) + fServer = true; + else + fServer = GetBoolArg("-server"); + + /* force fServer when running without GUI */ +#if !defined(QT_GUI) + fServer = true; +#endif + fPrintToConsole = GetBoolArg("-printtoconsole"); + fPrintToDebugger = GetBoolArg("-printtodebugger"); + fLogTimestamps = GetBoolArg("-logtimestamps", true); + bool fDisableWallet = GetBoolArg("-disablewallet", false); + + if (mapArgs.count("-timeout")) + { + int nNewTimeout = GetArg("-timeout", 5000); + if (nNewTimeout > 0 && nNewTimeout < 600000) + nConnectTimeout = nNewTimeout; + } + + // Continue to put "/P2SH/" in the coinbase to monitor + // BIP16 support. + // This can be removed eventually... + const char* pszP2SH = "/P2SH/"; + COINBASE_FLAGS << std::vector(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 + // a transaction spammer can cheaply fill blocks using + // 1-satoshi-fee transactions. It should be set above the real + // cost to you of processing a transaction. + if (mapArgs.count("-mintxfee")) + { + int64 n = 0; + if (ParseMoney(mapArgs["-mintxfee"], n) && n > 0) + CTransaction::nMinTxFee = n; + else + return InitError(strprintf(_("Invalid amount for -mintxfee=: '%s'"), mapArgs["-mintxfee"].c_str())); + } + if (mapArgs.count("-minrelaytxfee")) + { + int64 n = 0; + if (ParseMoney(mapArgs["-minrelaytxfee"], n) && n > 0) + CTransaction::nMinRelayTxFee = n; + else + return InitError(strprintf(_("Invalid amount for -minrelaytxfee=: '%s'"), mapArgs["-minrelaytxfee"].c_str())); + } + + if (mapArgs.count("-paytxfee")) + { + if (!ParseMoney(mapArgs["-paytxfee"], nTransactionFee)) + return InitError(strprintf(_("Invalid amount for -paytxfee=: '%s'"), mapArgs["-paytxfee"].c_str())); + if (nTransactionFee > 0.00001 * 500 * COIN) + InitWarning(_("Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.")); + } + + if (mapArgs.count("-mininput")) + { + if (!ParseMoney(mapArgs["-mininput"], nMinimumInputValue)) + return InitError(strprintf(_("Invalid amount for -mininput=: '%s'"), mapArgs["-mininput"].c_str())); + } + + // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log + + std::string strDataDir = GetDataDir().string(); + + // Make sure only a single Bitcoin process is using the data directory. + 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. CryptoLottoToken is probably already running."), strDataDir.c_str())); + + if (GetBoolArg("-shrinkdebugfile", !fDebug)) + ShrinkDebugFile(); + printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); + printf("CryptoLottoToken version %s (%s)\n", FormatFullVersion().c_str(), CLIENT_DATE.c_str()); + printf("Using OpenSSL version %s\n", SSLeay_version(SSLEAY_VERSION)); + if (!fLogTimestamps) + printf("Startup time: %s\n", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()).c_str()); + printf("Default data directory %s\n", GetDefaultDataDir().string().c_str()); + printf("Using data directory %s\n", strDataDir.c_str()); + printf("Using at most %i connections (%i file descriptors available)\n", nMaxConnections, nFD); + std::ostringstream strErrors; + + if (fDaemon) + fprintf(stdout, "CryptoLottoToken server starting\n"); + + if (nScriptCheckThreads) { + printf("Using %u threads for script verification\n", nScriptCheckThreads); + for (int i=0; i nets; + BOOST_FOREACH(std::string snet, mapMultiArgs["-onlynet"]) { + enum Network net = ParseNetwork(snet); + if (net == NET_UNROUTABLE) + return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet.c_str())); + nets.insert(net); + } + for (int n = 0; n < NET_MAX; n++) { + enum Network net = (enum Network)n; + if (!nets.count(net)) + SetLimited(net); + } + } +#if defined(USE_IPV6) +#if ! USE_IPV6 + else + SetLimited(NET_IPV6); +#endif +#endif + + CService addrProxy; + bool fProxy = false; + if (mapArgs.count("-proxy")) { + addrProxy = CService(mapArgs["-proxy"], 9050); + if (!addrProxy.IsValid()) + return InitError(strprintf(_("Invalid -proxy address: '%s'"), mapArgs["-proxy"].c_str())); + + if (!IsLimited(NET_IPV4)) + SetProxy(NET_IPV4, addrProxy, nSocksVersion); + if (nSocksVersion > 4) { +#ifdef USE_IPV6 + if (!IsLimited(NET_IPV6)) + SetProxy(NET_IPV6, addrProxy, nSocksVersion); +#endif + SetNameProxy(addrProxy, nSocksVersion); + } + fProxy = true; + } + + // -tor can override normal proxy, -notor disables tor entirely + if (!(mapArgs.count("-tor") && mapArgs["-tor"] == "0") && (fProxy || mapArgs.count("-tor"))) { + CService addrOnion; + if (!mapArgs.count("-tor")) + addrOnion = addrProxy; + else + addrOnion = CService(mapArgs["-tor"], 9050); + if (!addrOnion.IsValid()) + return InitError(strprintf(_("Invalid -tor address: '%s'"), mapArgs["-tor"].c_str())); + SetProxy(NET_TOR, addrOnion, 5); + SetReachable(NET_TOR); + } + + // see Step 2: parameter interactions for more information about these + fNoListen = !GetBoolArg("-listen", true); + fDiscover = GetBoolArg("-discover", true); + fNameLookup = GetBoolArg("-dns", true); + + bool fBound = false; + if (!fNoListen) { + if (mapArgs.count("-bind")) { + BOOST_FOREACH(std::string strBind, mapMultiArgs["-bind"]) { + CService addrBind; + if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) + return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind.c_str())); + fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR)); + } + } + else { + struct in_addr inaddr_any; + inaddr_any.s_addr = INADDR_ANY; +#ifdef USE_IPV6 + fBound |= Bind(CService(in6addr_any, GetListenPort()), BF_NONE); +#endif + fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE); + } + if (!fBound) + return InitError(_("Failed to listen on any port. Use -listen=0 if you want this.")); + } + + if (mapArgs.count("-externalip")) { + BOOST_FOREACH(string strAddr, mapMultiArgs["-externalip"]) { + CService addrLocal(strAddr, GetListenPort(), fNameLookup); + if (!addrLocal.IsValid()) + return InitError(strprintf(_("Cannot resolve -externalip address: '%s'"), strAddr.c_str())); + AddLocal(CService(strAddr, GetListenPort(), fNameLookup), LOCAL_MANUAL); + } + } + + BOOST_FOREACH(string strDest, mapMultiArgs["-seednode"]) + AddOneShot(strDest); + + // ********************************************************* Step 7: load block chain + +#if defined(USE_SSE2) + scrypt_detect_sse2(cpuid_edx); +#endif + + fReindex = GetBoolArg("-reindex"); + + // Upgrading to 0.8; hard-link the old blknnnn.dat files into /blocks/ + filesystem::path blocksDir = GetDataDir() / "blocks"; + if (!filesystem::exists(blocksDir)) + { + filesystem::create_directories(blocksDir); + bool linked = false; + for (unsigned int i = 1; i < 10000; i++) { + filesystem::path source = GetDataDir() / strprintf("blk%04u.dat", i); + if (!filesystem::exists(source)) break; + filesystem::path dest = blocksDir / strprintf("blk%05u.dat", i-1); + try { + filesystem::create_hard_link(source, dest); + printf("Hardlinked %s -> %s\n", source.string().c_str(), dest.string().c_str()); + linked = true; + } catch (filesystem::filesystem_error & e) { + // Note: hardlink creation failing is not a disaster, it just means + // blocks will get re-downloaded from peers. + printf("Error hardlinking blk%04u.dat : %s\n", i, e.what()); + break; + } + } + if (linked) + { + fReindex = true; + } + } + + // cache size calculations + size_t nTotalCache = GetArg("-dbcache", 25) << 20; + if (nTotalCache < (1 << 22)) + nTotalCache = (1 << 22); // total cache cannot be less than 4 MiB + size_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 + nTotalCache -= nCoinDBCache; + nCoinCacheSize = nTotalCache / 300; // coins in memory require around 300 bytes + + bool fLoaded = false; + while (!fLoaded) { + bool fReset = fReindex; + std::string strLoadError; + + uiInterface.InitMessage(_("Loading block index...")); + + nStart = GetTimeMillis(); + do { + try { + UnloadBlockIndex(); + delete pcoinsTip; + delete pcoinsdbview; + delete pblocktree; + + pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex); + pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex); + pcoinsTip = new CCoinsViewCache(*pcoinsdbview); + + if (fReindex) + pblocktree->WriteReindexing(true); + + if (!LoadBlockIndex()) { + strLoadError = _("Error loading block database"); + break; + } + + // If the loaded chain has a wrong genesis, bail out immediately + // (we're likely using a testnet datadir, or the other way around). + if (!mapBlockIndex.empty() && pindexGenesisBlock == NULL) + return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?")); + + // Initialize the block index (no-op if non-empty database was already loaded) + if (!InitBlockIndex()) { + strLoadError = _("Error initializing block database"); + break; + } + + // Check for changed -txindex state + if (fTxIndex != GetBoolArg("-txindex", false)) { + strLoadError = _("You need to rebuild the database using -reindex to change -txindex"); + break; + } + + uiInterface.InitMessage(_("Verifying blocks...")); + if (!VerifyDB(GetArg("-checklevel", 3), + GetArg( "-checkblocks", 288))) { + strLoadError = _("Corrupted block database detected"); + break; + } + } catch(std::exception &e) { + strLoadError = _("Error opening block database"); + break; + } + + fLoaded = true; + } while(false); + + if (!fLoaded) { + // first suggest a reindex + if (!fReset) { + bool fRet = uiInterface.ThreadSafeMessageBox( + strLoadError + ".\n\n" + _("Do you want to rebuild the block database now?"), + "", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT); + if (fRet) { + fReindex = true; + fRequestShutdown = false; + } else { + return false; + } + } else { + return InitError(strLoadError); + } + } + } + + // as LoadBlockIndex can take several minutes, it's possible the user + // requested to kill bitcoin-qt during the last operation. If so, exit. + // As the program has not fully started yet, Shutdown() is possibly overkill. + if (fRequestShutdown) + { + printf("Shutdown requested. Exiting.\n"); + return false; + } + printf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart); + + if (GetBoolArg("-printblockindex") || GetBoolArg("-printblocktree")) + { + PrintBlockTree(); + return false; + } + + if (mapArgs.count("-printblock")) + { + string strMatch = mapArgs["-printblock"]; + int nFound = 0; + for (map::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) + { + uint256 hash = (*mi).first; + if (strncmp(hash.ToString().c_str(), strMatch.c_str(), strMatch.size()) == 0) + { + CBlockIndex* pindex = (*mi).second; + CBlock block; + block.ReadFromDisk(pindex); + block.BuildMerkleTree(); + block.print(); + printf("\n"); + nFound++; + } + } + if (nFound == 0) + printf("No blocks matching %s were found\n", strMatch.c_str()); + return false; + } + + // ********************************************************* Step 8: load wallet + + if (fDisableWallet) { + printf("Wallet disabled!\n"); + pwalletMain = NULL; + } else { + uiInterface.InitMessage(_("Loading wallet...")); + + nStart = GetTimeMillis(); + bool fFirstRun = true; + pwalletMain = new CWallet("wallet.dat"); + DBErrors nLoadWalletRet = pwalletMain->LoadWallet(fFirstRun); + if (nLoadWalletRet != DB_LOAD_OK) + { + if (nLoadWalletRet == DB_CORRUPT) + strErrors << _("Error loading wallet.dat: Wallet corrupted") << "\n"; + else if (nLoadWalletRet == DB_NONCRITICAL_ERROR) + { + string msg(_("Warning: error reading wallet.dat! All keys read correctly, but transaction data" + " or address book entries might be missing or incorrect.")); + InitWarning(msg); + } + else if (nLoadWalletRet == DB_TOO_NEW) + strErrors << _("Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken") << "\n"; + else if (nLoadWalletRet == DB_NEED_REWRITE) + { + strErrors << _("Wallet needed to be rewritten: restart CryptoLottoToken to complete") << "\n"; + printf("%s", strErrors.str().c_str()); + return InitError(strErrors.str()); + } + else + strErrors << _("Error loading wallet.dat") << "\n"; + } + + if (GetBoolArg("-upgradewallet", fFirstRun)) + { + int nMaxVersion = GetArg("-upgradewallet", 0); + if (nMaxVersion == 0) // the -upgradewallet without argument case + { + printf("Performing wallet upgrade to %i\n", FEATURE_LATEST); + nMaxVersion = CLIENT_VERSION; + pwalletMain->SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately + } + else + printf("Allowing wallet upgrade up to %i\n", nMaxVersion); + if (nMaxVersion < pwalletMain->GetVersion()) + strErrors << _("Cannot downgrade wallet") << "\n"; + pwalletMain->SetMaxVersion(nMaxVersion); + } + + if (fFirstRun) + { + // Create new keyUser and set as default key + RandAddSeedPerfmon(); + + CPubKey newDefaultKey; + if (pwalletMain->GetKeyFromPool(newDefaultKey, false)) { + pwalletMain->SetDefaultKey(newDefaultKey); + if (!pwalletMain->SetAddressBookName(pwalletMain->vchDefaultKey.GetID(), "")) + strErrors << _("Cannot write default address") << "\n"; + } + + pwalletMain->SetBestChain(CBlockLocator(pindexBest)); + } + + printf("%s", strErrors.str().c_str()); + printf(" wallet %15"PRI64d"ms\n", GetTimeMillis() - nStart); + + RegisterWallet(pwalletMain); + + CBlockIndex *pindexRescan = pindexBest; + if (GetBoolArg("-rescan")) + pindexRescan = pindexGenesisBlock; + else + { + CWalletDB walletdb("wallet.dat"); + CBlockLocator locator; + if (walletdb.ReadBestBlock(locator)) + pindexRescan = locator.GetBlockIndex(); + else + pindexRescan = pindexGenesisBlock; + } + if (pindexBest && pindexBest != pindexRescan) + { + uiInterface.InitMessage(_("Rescanning...")); + printf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight); + nStart = GetTimeMillis(); + pwalletMain->ScanForWalletTransactions(pindexRescan, true); + printf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart); + pwalletMain->SetBestChain(CBlockLocator(pindexBest)); + nWalletDBUpdated++; + } + } // (!fDisableWallet) + + // ********************************************************* Step 9: import blocks + + // scan for better chains in the block chain database, that are not yet connected in the active best chain + CValidationState state; + if (!ConnectBestBlock(state)) + strErrors << "Failed to connect best block"; + + std::vector vImportFiles; + if (mapArgs.count("-loadblock")) + { + BOOST_FOREACH(string strFile, mapMultiArgs["-loadblock"]) + vImportFiles.push_back(strFile); + } + threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles)); + + // ********************************************************* Step 10: load peers + + uiInterface.InitMessage(_("Loading addresses...")); + + nStart = GetTimeMillis(); + + { + CAddrDB adb; + if (!adb.Read(addrman)) + printf("Invalid or missing peers.dat; recreating\n"); + } + + printf("Loaded %i addresses from peers.dat %"PRI64d"ms\n", + addrman.size(), GetTimeMillis() - nStart); + + // ********************************************************* Step 11: start node + + if (!CheckDiskSpace()) + return false; + + if (!strErrors.str().empty()) + return InitError(strErrors.str()); + + RandAddSeedPerfmon(); + + //// debug print + printf("mapBlockIndex.size() = %"PRIszu"\n", mapBlockIndex.size()); + printf("nBestHeight = %d\n", nBestHeight); + printf("setKeyPool.size() = %"PRIszu"\n", pwalletMain ? pwalletMain->setKeyPool.size() : 0); + printf("mapWallet.size() = %"PRIszu"\n", pwalletMain ? pwalletMain->mapWallet.size() : 0); + printf("mapAddressBook.size() = %"PRIszu"\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0); + + StartNode(threadGroup); + + // InitRPCMining is needed here so getwork/getblocktemplate in the GUI debug console works properly. + InitRPCMining(); + if (fServer) + StartRPCThreads(); + + // Generate coins in the background + if (pwalletMain) + GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain); + + // ********************************************************* Step 12: finished + + uiInterface.InitMessage(_("Done loading")); + + if (pwalletMain) { + // Add wallet transactions that aren't already in a block to mapTransactions + pwalletMain->ReacceptWalletTransactions(); + + // Run a thread to flush wallet periodically + threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(pwalletMain->strWalletFile))); + } + + return !fRequestShutdown; +} diff --git a/src/init.h b/src/init.h new file mode 100644 index 0000000..5927670 --- /dev/null +++ b/src/init.h @@ -0,0 +1,18 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_INIT_H +#define BITCOIN_INIT_H + +#include "wallet.h" + +extern CWallet* pwalletMain; + +void StartShutdown(); +bool ShutdownRequested(); +void Shutdown(); +bool AppInit2(boost::thread_group& threadGroup); +std::string HelpMessage(); + +#endif diff --git a/src/irc.cpp b/src/irc.cpp new file mode 100644 index 0000000..188418a --- /dev/null +++ b/src/irc.cpp @@ -0,0 +1,402 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2011-2012 CryptoLottoToken Developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "irc.h" +#include "net.h" +#include "base58.h" +#include "version.h" +#include + +#include // for startswith() and endswith() + +using namespace std; +using namespace boost; + +int nGotIRCAddresses = 0; + +void ThreadIRCSeed2(); + + + + +#pragma pack(push, 1) +struct ircaddr +{ + struct in_addr ip; + short port; +}; +#pragma pack(pop) + +string EncodeAddress(const CService& addr) +{ + struct ircaddr tmp; + if (addr.GetInAddr(&tmp.ip)) + { + tmp.port = htons(addr.GetPort()); + + vector vch(UBEGIN(tmp), UEND(tmp)); + return string("u") + EncodeBase58Check(vch); + } + return ""; +} + +bool DecodeAddress(string str, CService& addr) +{ + vector vch; + if (!DecodeBase58Check(str.substr(1), vch)) + return false; + + struct ircaddr tmp; + if (vch.size() != sizeof(tmp)) + return false; + memcpy(&tmp, &vch[0], sizeof(tmp)); + + addr = CService(tmp.ip, ntohs(tmp.port)); + return true; +} + + + + + + +static bool Send(SOCKET hSocket, const char* pszSend) +{ + if (strstr(pszSend, "PONG") != pszSend) + printf("IRC SENDING: %s\n", pszSend); + const char* psz = pszSend; + const char* pszEnd = psz + strlen(psz); + while (psz < pszEnd) + { + int ret = send(hSocket, psz, pszEnd - psz, MSG_NOSIGNAL); + if (ret < 0) + return false; + psz += ret; + } + return true; +} + +bool RecvLineIRC(SOCKET hSocket, string& strLine) +{ + loop + { + bool fRet = RecvLine(hSocket, strLine); + if (fRet) + { + boost::this_thread::interruption_point(); + vector vWords; + ParseString(strLine, ' ', vWords); + if (vWords.size() >= 1 && vWords[0] == "PING") + { + strLine[1] = 'O'; + strLine += '\r'; + Send(hSocket, strLine.c_str()); + continue; + } + } + return fRet; + } +} + +int RecvUntil(SOCKET hSocket, const char* psz1, const char* psz2=NULL, const char* psz3=NULL, const char* psz4=NULL) +{ + loop + { + string strLine; + strLine.reserve(10000); + if (!RecvLineIRC(hSocket, strLine)) + return 0; + printf("IRC %s\n", strLine.c_str()); + if (psz1 && strLine.find(psz1) != string::npos) + return 1; + if (psz2 && strLine.find(psz2) != string::npos) + return 2; + if (psz3 && strLine.find(psz3) != string::npos) + return 3; + if (psz4 && strLine.find(psz4) != string::npos) + return 4; + } +} + +bool Wait(int nSeconds) +{ + boost::this_thread::interruption_point(); + printf("IRC waiting %d seconds to reconnect\n", nSeconds); + for (int i = 0; i < nSeconds; i++) + { + boost::this_thread::interruption_point(); + MilliSleep(1000); + } + return true; +} + +bool RecvCodeLine(SOCKET hSocket, const char* psz1, string& strRet) +{ + strRet.clear(); + loop + { + string strLine; + if (!RecvLineIRC(hSocket, strLine)) + return false; + + vector vWords; + ParseString(strLine, ' ', vWords); + if (vWords.size() < 2) + continue; + + if (vWords[1] == psz1) + { + printf("IRC %s\n", strLine.c_str()); + strRet = strLine; + return true; + } + } +} + +bool GetIPFromIRC(SOCKET hSocket, string strMyName, CNetAddr& ipRet) +{ + Send(hSocket, strprintf("USERHOST %s\r", strMyName.c_str()).c_str()); + + string strLine; + if (!RecvCodeLine(hSocket, "302", strLine)) + return false; + + vector vWords; + ParseString(strLine, ' ', vWords); + if (vWords.size() < 4) + return false; + + string str = vWords[3]; + if (str.rfind("@") == string::npos) + return false; + string strHost = str.substr(str.rfind("@")+1); + + // Hybrid IRC used by lfnet always returns IP when you userhost yourself, + // but in case another IRC is ever used this should work. + printf("GetIPFromIRC() got userhost %s\n", strHost.c_str()); + CNetAddr addr(strHost, true); + if (!addr.IsValid()) + return false; + ipRet = addr; + + return true; +} + + + +void ThreadIRCSeed() +{ + // Make this thread recognisable as the IRC seeding thread + RenameThread("CryptoLottoToken-ircseed"); + + try + { + ThreadIRCSeed2(); + } + catch (boost::thread_interrupted) { + throw; + } + catch (std::exception& e) { + PrintExceptionContinue(&e, "ThreadIRCSeed()"); + } catch (...) { + PrintExceptionContinue(NULL, "ThreadIRCSeed()"); + } + printf("ThreadIRCSeed exited\n"); +} + +void ThreadIRCSeed2() +{ + /* Dont advertise on IRC if we don't allow incoming connections */ + if (mapArgs.count("-connect") || fNoListen) + return; + + if (!GetBoolArg("-irc", false)) + return; + + printf("ThreadIRCSeed started\n"); + int nErrorWait = 10; + int nRetryWait = 10; + + + + while (true) + { + CService addrConnect("92.243.23.21", 6667); // irc.lfnet.org + + CService addrIRC("irc.lfnet.org", 6667, true); + if (addrIRC.IsValid()) + addrConnect = addrIRC; + + SOCKET hSocket; + if (!ConnectSocket(addrConnect, hSocket)) + { + printf("IRC connect failed\n"); + nErrorWait = nErrorWait * 11 / 10; + if (Wait(nErrorWait += 60)) + continue; + else + return; + } + + if (!RecvUntil(hSocket, "Found your hostname", "using your IP address instead", "Couldn't look up your hostname", "ignoring hostname")) + { + closesocket(hSocket); + hSocket = INVALID_SOCKET; + nErrorWait = nErrorWait * 11 / 10; + if (Wait(nErrorWait += 60)) + continue; + else + return; + } + + CNetAddr addrIPv4("1.2.3.4"); // arbitrary IPv4 address to make GetLocal prefer IPv4 addresses + CService addrLocal; + string strMyName; + if (GetLocal(addrLocal, &addrIPv4)) + strMyName = EncodeAddress(GetLocalAddress(&addrConnect)); + + if (strMyName == "") + strMyName = strprintf("x%u", GetRand(1000000000)); + + strMyName = strprintf("%s-%s", strMyName.c_str(), boost::lexical_cast(PROTOCOL_VERSION_SHORT).c_str()); + + Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str()); + Send(hSocket, strprintf("USER %s 8 * : %s\r", strMyName.c_str(), strMyName.c_str()).c_str()); + + int nRet = RecvUntil(hSocket, " 004 ", " 433 "); + if (nRet != 1) + { + closesocket(hSocket); + hSocket = INVALID_SOCKET; + if (nRet == 2) + { + printf("IRC name already in use\n"); + Wait(10); + continue; + } + nErrorWait = nErrorWait * 11 / 10; + if (Wait(nErrorWait += 60)) + continue; + else + return; + } + MilliSleep(500); + + // Get our external IP from the IRC server and re-nick before joining the channel + CNetAddr addrFromIRC; + if (GetIPFromIRC(hSocket, strMyName, addrFromIRC)) + { + printf("GetIPFromIRC() returned %s\n", addrFromIRC.ToString().c_str()); + if (addrFromIRC.IsRoutable()) + { + // IRC lets you to re-nick + AddLocal(addrFromIRC, LOCAL_IRC); + strMyName = EncodeAddress(GetLocalAddress(&addrConnect)); + strMyName = strprintf("%s-%s", strMyName.c_str(), boost::lexical_cast(PROTOCOL_VERSION_SHORT).c_str()); + Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str()); + } + } + + if (fTestNet) { + Send(hSocket, "JOIN #CryptoLottoTokenTEST0\r"); + Send(hSocket, "WHO #CryptoLottoTokenTEST0\r"); + } else { + // randomly join #CryptoLottoToken00-#CryptoLottoToken99 + // network is now over 3k peers , get them to join 50 random channels! + // channel_number = 0; + int channel_number = 0; //GetRandInt(50); + + Send(hSocket, strprintf("JOIN #CryptoLottoToken%02d\r", channel_number).c_str()); + Send(hSocket, strprintf("WHO #CryptoLottoToken%02d\r", channel_number).c_str()); + } + + int64 nStart = GetTime(); + string strLine; + strLine.reserve(10000); + while (RecvLineIRC(hSocket, strLine)) + { + boost::this_thread::interruption_point(); + if (strLine.empty() || strLine.size() > 900 || strLine[0] != ':') + continue; + + vector vWords; + ParseString(strLine, ' ', vWords); + if (vWords.size() < 2) + continue; + + std::string strName; + + if (vWords[1] == "352" && vWords.size() >= 8) + { + // index 7 is limited to 16 characters + // could get full length name at index 10, but would be different from join messages + strName = vWords[7].c_str(); + printf("IRC got who\n"); + } + + if (vWords[1] == "JOIN" && vWords[0].size() > 1) + { + // :username!username@50000007.F000000B.90000002.IP JOIN :#channelname + strName = vWords[0].substr(1, vWords[0].find('!', 1) - 1); + printf("IRC got join\n"); + } + + if (boost::algorithm::starts_with(strName, "u")) + { + CAddress addr; + if (DecodeAddress(strName, addr)) + { + addr.nTime = GetAdjustedTime(); + if (addrman.Add(addr, addrConnect, 51 * 60)) + printf("IRC got new address: %s\n", addr.ToString().c_str()); + nGotIRCAddresses++; + } + else + { + printf("IRC decode failed\n"); + } + } + } + closesocket(hSocket); + hSocket = INVALID_SOCKET; + + if (GetTime() - nStart > 20 * 60) + { + nErrorWait /= 3; + nRetryWait /= 3; + } + + nRetryWait = nRetryWait * 11 / 10; + if (!Wait(nRetryWait += 60)) + return; + } +} + + + + + + + + + + +#ifdef TEST +int main(int argc, char *argv[]) +{ + WSADATA wsadata; + if (WSAStartup(MAKEWORD(2,2), &wsadata) != NO_ERROR) + { + printf("Error at WSAStartup()\n"); + return false; + } + + ThreadIRCSeed(NULL); + + WSACleanup(); + return 0; +} +#endif \ No newline at end of file diff --git a/src/irc.h b/src/irc.h new file mode 100644 index 0000000..718d6d0 --- /dev/null +++ b/src/irc.h @@ -0,0 +1,13 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2011-2012 CryptoLottoToken Developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_IRC_H +#define BITCOIN_IRC_H + +void ThreadIRCSeed(); + +extern int nGotIRCAddresses; + +#endif diff --git a/src/json/LICENSE.txt b/src/json/LICENSE.txt new file mode 100644 index 0000000..797d536 --- /dev/null +++ b/src/json/LICENSE.txt @@ -0,0 +1,24 @@ +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 new file mode 100644 index 0000000..ac1879d --- /dev/null +++ b/src/json/json_spirit.h @@ -0,0 +1,18 @@ +#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 new file mode 100644 index 0000000..1720850 --- /dev/null +++ b/src/json/json_spirit_error_position.h @@ -0,0 +1,54 @@ +#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 + +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 new file mode 100644 index 0000000..aa4f637 --- /dev/null +++ b/src/json/json_spirit_reader.cpp @@ -0,0 +1,137 @@ +// 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 new file mode 100644 index 0000000..96494a9 --- /dev/null +++ b/src/json/json_spirit_reader.h @@ -0,0 +1,62 @@ +#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 + +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 new file mode 100644 index 0000000..4dec00e --- /dev/null +++ b/src/json/json_spirit_reader_template.h @@ -0,0 +1,612 @@ +#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 +#include +#include + +#if BOOST_VERSION >= 103800 + #include + #include + #include + #include + #include + #define spirit_namespace boost::spirit::classic +#else + #include + #include + #include + #include + #include + #define spirit_namespace boost::spirit +#endif + +namespace json_spirit +{ + const spirit_namespace::int_parser < boost::int64_t > int64_p = spirit_namespace::int_parser < boost::int64_t >(); + const spirit_namespace::uint_parser< boost::uint64_t > uint64_p = spirit_namespace::uint_parser< boost::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( boost::int64_t i ) + { + add_to_current( i ); + } + + void new_uint64( boost::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( boost::int64_t ) > Int_action; + typedef boost::function< void( boost::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 new file mode 100644 index 0000000..7e59c9a --- /dev/null +++ b/src/json/json_spirit_stream_reader.h @@ -0,0 +1,70 @@ +#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 new file mode 100644 index 0000000..553e3b9 --- /dev/null +++ b/src/json/json_spirit_utils.h @@ -0,0 +1,61 @@ +#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 + +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 new file mode 100644 index 0000000..44d2f06 --- /dev/null +++ b/src/json/json_spirit_value.cpp @@ -0,0 +1,8 @@ +/* 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 new file mode 100644 index 0000000..7e83a2a --- /dev/null +++ b/src/json/json_spirit_value.h @@ -0,0 +1,534 @@ +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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( boost::int64_t value ); + Value_impl( boost::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; + boost::int64_t get_int64() const; + boost::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, boost::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< boost::int64_t >( value ) ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( boost::int64_t value ) + : type_( int_type ) + , v_( value ) + , is_uint64_( false ) + { + } + + template< class Config > + Value_impl< Config >::Value_impl( boost::uint64_t value ) + : type_( int_type ) + , v_( static_cast< boost::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 > + boost::int64_t Value_impl< Config >::get_int64() const + { + check_type( int_type ); + + return boost::get< boost::int64_t >( v_ ); + } + + template< class Config > + boost::uint64_t Value_impl< Config >::get_uint64() const + { + check_type( int_type ); + + return static_cast< boost::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 > + boost::int64_t get_value( const Value& value, Type_to_type< boost::int64_t > ) + { + return value.get_int64(); + } + + template< class Value > + boost::uint64_t get_value( const Value& value, Type_to_type< boost::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 new file mode 100644 index 0000000..d24a632 --- /dev/null +++ b/src/json/json_spirit_writer.cpp @@ -0,0 +1,95 @@ +// 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 new file mode 100644 index 0000000..52e1406 --- /dev/null +++ b/src/json/json_spirit_writer.h @@ -0,0 +1,50 @@ +#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 + +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 new file mode 100644 index 0000000..6b4978a --- /dev/null +++ b/src/json/json_spirit_writer_template.h @@ -0,0 +1,249 @@ +#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 +#include +#include + +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/key.cpp b/src/key.cpp new file mode 100644 index 0000000..e01fca9 --- /dev/null +++ b/src/key.cpp @@ -0,0 +1,413 @@ +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include + +#include "key.h" + + +// anonymous namespace with local implementation code (OpenSSL interaction) +namespace { + +// Generate a private key from just the secret parameter +int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key) +{ + int ok = 0; + BN_CTX *ctx = NULL; + EC_POINT *pub_key = NULL; + + if (!eckey) return 0; + + const EC_GROUP *group = EC_KEY_get0_group(eckey); + + if ((ctx = BN_CTX_new()) == NULL) + goto err; + + pub_key = EC_POINT_new(group); + + if (pub_key == NULL) + goto err; + + if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx)) + goto err; + + EC_KEY_set_private_key(eckey,priv_key); + EC_KEY_set_public_key(eckey,pub_key); + + ok = 1; + +err: + + if (pub_key) + EC_POINT_free(pub_key); + if (ctx != NULL) + BN_CTX_free(ctx); + + return(ok); +} + +// Perform ECDSA key recovery (see SEC1 4.1.6) for curves over (mod p)-fields +// recid selects which key is recovered +// if check is non-zero, additional checks are performed +int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check) +{ + if (!eckey) return 0; + + int ret = 0; + BN_CTX *ctx = NULL; + + BIGNUM *x = NULL; + BIGNUM *e = NULL; + BIGNUM *order = NULL; + BIGNUM *sor = NULL; + BIGNUM *eor = NULL; + BIGNUM *field = NULL; + EC_POINT *R = NULL; + EC_POINT *O = NULL; + EC_POINT *Q = NULL; + BIGNUM *rr = NULL; + BIGNUM *zero = NULL; + int n = 0; + int i = recid / 2; + + const EC_GROUP *group = EC_KEY_get0_group(eckey); + if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; } + BN_CTX_start(ctx); + order = BN_CTX_get(ctx); + if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; } + x = BN_CTX_get(ctx); + if (!BN_copy(x, order)) { ret=-1; goto err; } + if (!BN_mul_word(x, i)) { ret=-1; goto err; } + if (!BN_add(x, x, ecsig->r)) { ret=-1; goto err; } + field = BN_CTX_get(ctx); + if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; } + if (BN_cmp(x, field) >= 0) { ret=0; goto err; } + if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } + if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; } + if (check) + { + if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } + if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; } + if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; } + } + if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } + n = EC_GROUP_get_degree(group); + e = BN_CTX_get(ctx); + if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; } + if (8*msglen > n) BN_rshift(e, e, 8-(n & 7)); + zero = BN_CTX_get(ctx); + if (!BN_zero(zero)) { ret=-1; goto err; } + if (!BN_mod_sub(e, zero, e, order, ctx)) { ret=-1; goto err; } + rr = BN_CTX_get(ctx); + if (!BN_mod_inverse(rr, ecsig->r, order, ctx)) { ret=-1; goto err; } + sor = BN_CTX_get(ctx); + if (!BN_mod_mul(sor, ecsig->s, rr, order, ctx)) { ret=-1; goto err; } + eor = BN_CTX_get(ctx); + if (!BN_mod_mul(eor, e, rr, order, ctx)) { ret=-1; goto err; } + if (!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret=-2; goto err; } + if (!EC_KEY_set_public_key(eckey, Q)) { ret=-2; goto err; } + + ret = 1; + +err: + if (ctx) { + BN_CTX_end(ctx); + BN_CTX_free(ctx); + } + if (R != NULL) EC_POINT_free(R); + if (O != NULL) EC_POINT_free(O); + if (Q != NULL) EC_POINT_free(Q); + return ret; +} + +// RAII Wrapper around OpenSSL's EC_KEY +class CECKey { +private: + EC_KEY *pkey; + +public: + CECKey() { + pkey = EC_KEY_new_by_curve_name(NID_secp256k1); + assert(pkey != NULL); + } + + ~CECKey() { + EC_KEY_free(pkey); + } + + void GetSecretBytes(unsigned char vch[32]) const { + const BIGNUM *bn = EC_KEY_get0_private_key(pkey); + assert(bn); + int nBytes = BN_num_bytes(bn); + int n=BN_bn2bin(bn,&vch[32 - nBytes]); + assert(n == nBytes); + memset(vch, 0, 32 - nBytes); + } + + void SetSecretBytes(const unsigned char vch[32]) { + BIGNUM bn; + BN_init(&bn); + assert(BN_bin2bn(vch, 32, &bn)); + assert(EC_KEY_regenerate_key(pkey, &bn)); + BN_clear_free(&bn); + } + + void GetPrivKey(CPrivKey &privkey, bool fCompressed) { + EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); + int nSize = i2d_ECPrivateKey(pkey, NULL); + assert(nSize); + privkey.resize(nSize); + unsigned char* pbegin = &privkey[0]; + int nSize2 = i2d_ECPrivateKey(pkey, &pbegin); + assert(nSize == nSize2); + } + + bool SetPrivKey(const CPrivKey &privkey) { + const unsigned char* pbegin = &privkey[0]; + if (d2i_ECPrivateKey(&pkey, &pbegin, privkey.size())) { + // d2i_ECPrivateKey returns true if parsing succeeds. + // This doesn't necessarily mean the key is valid. + if (EC_KEY_check_key(pkey)) + return true; + } + return false; + } + + void GetPubKey(CPubKey &pubkey, bool fCompressed) { + EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); + int nSize = i2o_ECPublicKey(pkey, NULL); + assert(nSize); + assert(nSize <= 65); + unsigned char c[65]; + unsigned char *pbegin = c; + int nSize2 = i2o_ECPublicKey(pkey, &pbegin); + assert(nSize == nSize2); + pubkey.Set(&c[0], &c[nSize]); + } + + bool SetPubKey(const CPubKey &pubkey) { + const unsigned char* pbegin = pubkey.begin(); + return o2i_ECPublicKey(&pkey, &pbegin, pubkey.size()); + } + + bool Sign(const uint256 &hash, std::vector& vchSig) { + unsigned int nSize = ECDSA_size(pkey); + vchSig.resize(nSize); // Make sure it is big enough + assert(ECDSA_sign(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], &nSize, pkey)); + vchSig.resize(nSize); // Shrink to fit actual size + return true; + } +/* + bool Verify(const uint256 &hash, const std::vector& vchSig) { + // -1 = error, 0 = bad sig, 1 = good + if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1) + return false; + return true; + }*/ + + bool Verify(const uint256 &hash, const std::vector& vchSig) { + // New versions of OpenSSL will reject non-canonical DER signatures. de/re-serialize first. + unsigned char *norm_der = NULL; + ECDSA_SIG *norm_sig = ECDSA_SIG_new(); + const unsigned char* sigptr = &vchSig[0]; + d2i_ECDSA_SIG(&norm_sig, &sigptr, vchSig.size()); + int derlen = i2d_ECDSA_SIG(norm_sig, &norm_der); + ECDSA_SIG_free(norm_sig); + if (derlen <= 0) + return false; + + // -1 = error, 0 = bad sig, 1 = good + bool ret = ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), norm_der, derlen, pkey) == 1; + OPENSSL_free(norm_der); + return ret; +} + + bool SignCompact(const uint256 &hash, unsigned char *p64, int &rec) { + bool fOk = false; + ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey); + if (sig==NULL) + return false; + memset(p64, 0, 64); + int nBitsR = BN_num_bits(sig->r); + int nBitsS = BN_num_bits(sig->s); + if (nBitsR <= 256 && nBitsS <= 256) { + CPubKey pubkey; + GetPubKey(pubkey, true); + for (int i=0; i<4; i++) { + CECKey keyRec; + if (ECDSA_SIG_recover_key_GFp(keyRec.pkey, sig, (unsigned char*)&hash, sizeof(hash), i, 1) == 1) { + CPubKey pubkeyRec; + keyRec.GetPubKey(pubkeyRec, true); + if (pubkeyRec == pubkey) { + rec = i; + fOk = true; + break; + } + } + } + assert(fOk); + BN_bn2bin(sig->r,&p64[32-(nBitsR+7)/8]); + BN_bn2bin(sig->s,&p64[64-(nBitsS+7)/8]); + } + ECDSA_SIG_free(sig); + return fOk; + } + + // reconstruct public key from a compact signature + // This is only slightly more CPU intensive than just verifying it. + // If this function succeeds, the recovered public key is guaranteed to be valid + // (the signature is a valid signature of the given data for that key) + bool Recover(const uint256 &hash, const unsigned char *p64, int rec) + { + if (rec<0 || rec>=3) + return false; + ECDSA_SIG *sig = ECDSA_SIG_new(); + BN_bin2bn(&p64[0], 32, sig->r); + BN_bin2bn(&p64[32], 32, sig->s); + bool ret = ECDSA_SIG_recover_key_GFp(pkey, sig, (unsigned char*)&hash, sizeof(hash), rec, 0) == 1; + ECDSA_SIG_free(sig); + return ret; + } +}; + +}; // end of anonymous namespace + +bool CKey::Check(const unsigned char *vch) { + // Do not convert to OpenSSL's data structures for range-checking keys, + // it's easy enough to do directly. + static const unsigned char vchMax[32] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE, + 0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B, + 0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x40 + }; + bool fIsZero = true; + for (int i=0; i<32 && fIsZero; i++) + if (vch[i] != 0) + fIsZero = false; + if (fIsZero) + return false; + for (int i=0; i<32; i++) { + if (vch[i] < vchMax[i]) + return true; + if (vch[i] > vchMax[i]) + return false; + } + return true; +} + +void CKey::MakeNewKey(bool fCompressedIn) { + do { + RAND_bytes(vch, sizeof(vch)); + } while (!Check(vch)); + fValid = true; + fCompressed = fCompressedIn; +} + +bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) { + CECKey key; + if (!key.SetPrivKey(privkey)) + return false; + key.GetSecretBytes(vch); + fCompressed = fCompressedIn; + fValid = true; + return true; +} + +CPrivKey CKey::GetPrivKey() const { + assert(fValid); + CECKey key; + key.SetSecretBytes(vch); + CPrivKey privkey; + key.GetPrivKey(privkey, fCompressed); + return privkey; +} + +CPubKey CKey::GetPubKey() const { + assert(fValid); + CECKey key; + key.SetSecretBytes(vch); + CPubKey pubkey; + key.GetPubKey(pubkey, fCompressed); + return pubkey; +} + +bool CKey::Sign(const uint256 &hash, std::vector& vchSig) const { + if (!fValid) + return false; + CECKey key; + key.SetSecretBytes(vch); + return key.Sign(hash, vchSig); +} + +bool CKey::SignCompact(const uint256 &hash, std::vector& vchSig) const { + if (!fValid) + return false; + CECKey key; + key.SetSecretBytes(vch); + vchSig.resize(65); + int rec = -1; + if (!key.SignCompact(hash, &vchSig[1], rec)) + return false; + assert(rec != -1); + vchSig[0] = 27 + rec + (fCompressed ? 4 : 0); + return true; +} + +bool CPubKey::Verify(const uint256 &hash, const std::vector& vchSig) const { + if (!IsValid()) + return false; + CECKey key; + if (!key.SetPubKey(*this)) + return false; + if (!key.Verify(hash, vchSig)) + return false; + return true; +} + +bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector& vchSig) { + if (vchSig.size() != 65) + return false; + CECKey key; + if (!key.Recover(hash, &vchSig[1], (vchSig[0] - 27) & ~4)) + return false; + key.GetPubKey(*this, (vchSig[0] - 27) & 4); + return true; +} + +bool CPubKey::VerifyCompact(const uint256 &hash, const std::vector& vchSig) const { + if (!IsValid()) + return false; + if (vchSig.size() != 65) + return false; + CECKey key; + if (!key.Recover(hash, &vchSig[1], (vchSig[0] - 27) & ~4)) + return false; + CPubKey pubkeyRec; + key.GetPubKey(pubkeyRec, IsCompressed()); + if (*this != pubkeyRec) + return false; + return true; +} + +bool CPubKey::IsFullyValid() const { + if (!IsValid()) + return false; + CECKey key; + if (!key.SetPubKey(*this)) + return false; + return true; +} + +bool CPubKey::Decompress() { + if (!IsValid()) + return false; + CECKey key; + if (!key.SetPubKey(*this)) + return false; + key.GetPubKey(*this, false); + return true; +} diff --git a/src/key.h b/src/key.h new file mode 100644 index 0000000..ce469ad --- /dev/null +++ b/src/key.h @@ -0,0 +1,256 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_KEY_H +#define BITCOIN_KEY_H + +#include + +#include "allocators.h" +#include "serialize.h" +#include "uint256.h" +#include "hash.h" + +// secp256k1: +// const unsigned int PRIVATE_KEY_SIZE = 279; +// const unsigned int PUBLIC_KEY_SIZE = 65; +// const unsigned int SIGNATURE_SIZE = 72; +// +// see www.keylength.com +// script supports up to 75 for single byte push + +/** A reference to a CKey: the Hash160 of its serialized public key */ +class CKeyID : public uint160 +{ +public: + CKeyID() : uint160(0) { } + CKeyID(const uint160 &in) : uint160(in) { } +}; + +/** A reference to a CScript: the Hash160 of its serialization (see script.h) */ +class CScriptID : public uint160 +{ +public: + CScriptID() : uint160(0) { } + CScriptID(const uint160 &in) : uint160(in) { } +}; + +/** An encapsulated public key. */ +class CPubKey { +private: + // Just store the serialized data. + // Its length can very cheaply be computed from the first byte. + unsigned char vch[65]; + + // Compute the length of a pubkey with a given first byte. + unsigned int static GetLen(unsigned char chHeader) { + if (chHeader == 2 || chHeader == 3) + return 33; + if (chHeader == 4 || chHeader == 6 || chHeader == 7) + return 65; + return 0; + } + + // Set this key data to be invalid + void Invalidate() { + vch[0] = 0xFF; + } + +public: + // Construct an invalid public key. + CPubKey() { + Invalidate(); + } + + // Initialize a public key using begin/end iterators to byte data. + template + void Set(const T pbegin, const T pend) { + int len = pend == pbegin ? 0 : GetLen(pbegin[0]); + if (len && len == (pend-pbegin)) + memcpy(vch, (unsigned char*)&pbegin[0], len); + else + Invalidate(); + } + + // Construct a public key using begin/end iterators to byte data. + template + CPubKey(const T pbegin, const T pend) { + Set(pbegin, pend); + } + + // Construct a public key from a byte vector. + CPubKey(const std::vector &vch) { + Set(vch.begin(), vch.end()); + } + + // Simple read-only vector-like interface to the pubkey data. + unsigned int size() const { return GetLen(vch[0]); } + const unsigned char *begin() const { return vch; } + const unsigned char *end() const { return vch+size(); } + const unsigned char &operator[](unsigned int pos) const { return vch[pos]; } + + // Comparator implementation. + friend bool operator==(const CPubKey &a, const CPubKey &b) { + return a.vch[0] == b.vch[0] && + memcmp(a.vch, b.vch, a.size()) == 0; + } + friend bool operator!=(const CPubKey &a, const CPubKey &b) { + return !(a == b); + } + friend bool operator<(const CPubKey &a, const CPubKey &b) { + return a.vch[0] < b.vch[0] || + (a.vch[0] == b.vch[0] && memcmp(a.vch, b.vch, a.size()) < 0); + } + + // Implement serialization, as if this was a byte vector. + unsigned int GetSerializeSize(int nType, int nVersion) const { + return size() + 1; + } + template void Serialize(Stream &s, int nType, int nVersion) const { + unsigned int len = size(); + ::WriteCompactSize(s, len); + s.write((char*)vch, len); + } + template void Unserialize(Stream &s, int nType, int nVersion) { + unsigned int len = ::ReadCompactSize(s); + if (len <= 65) { + s.read((char*)vch, len); + } else { + // invalid pubkey, skip available data + char dummy; + while (len--) + s.read(&dummy, 1); + Invalidate(); + } + } + + // Get the KeyID of this public key (hash of its serialization) + CKeyID GetID() const { + return CKeyID(Hash160(vch, vch+size())); + } + + // Get the 256-bit hash of this public key. + uint256 GetHash() const { + return Hash(vch, vch+size()); + } + + // just check syntactic correctness. + bool IsValid() const { + return size() > 0; + } + + // fully validate whether this is a valid public key (more expensive than IsValid()) + bool IsFullyValid() const; + + // Check whether this is a compressed public key. + bool IsCompressed() const { + return size() == 33; + } + + // Verify a DER signature (~72 bytes). + // If this public key is not fully valid, the return value will be false. + bool Verify(const uint256 &hash, const std::vector& vchSig) const; + + // Verify a compact signature (~65 bytes). + // See CKey::SignCompact. + bool VerifyCompact(const uint256 &hash, const std::vector& vchSig) const; + + // Recover a public key from a compact signature. + bool RecoverCompact(const uint256 &hash, const std::vector& vchSig); + + // Turn this public key into an uncompressed public key. + bool Decompress(); +}; + + +// secure_allocator is defined in allocators.h +// CPrivKey is a serialized private key, with all parameters included (279 bytes) +typedef std::vector > CPrivKey; + +/** An encapsulated private key. */ +class CKey { +private: + // Whether this private key is valid. We check for correctness when modifying the key + // data, so fValid should always correspond to the actual state. + bool fValid; + + // Whether the public key corresponding to this private key is (to be) compressed. + bool fCompressed; + + // The actual byte data + unsigned char vch[32]; + + // Check whether the 32-byte array pointed to be vch is valid keydata. + bool static Check(const unsigned char *vch); +public: + + // Construct an invalid private key. + CKey() : fValid(false) { + LockObject(vch); + } + + // Copy constructor. This is necessary because of memlocking. + CKey(const CKey &secret) : fValid(secret.fValid), fCompressed(secret.fCompressed) { + LockObject(vch); + memcpy(vch, secret.vch, sizeof(vch)); + } + + // Destructor (again necessary because of memlocking). + ~CKey() { + UnlockObject(vch); + } + + // Initialize using begin and end iterators to byte data. + template + void Set(const T pbegin, const T pend, bool fCompressedIn) { + if (pend - pbegin != 32) { + fValid = false; + return; + } + if (Check(&pbegin[0])) { + memcpy(vch, (unsigned char*)&pbegin[0], 32); + fValid = true; + fCompressed = fCompressedIn; + } else { + fValid = false; + } + } + + // Simple read-only vector-like interface. + unsigned int size() const { return (fValid ? 32 : 0); } + const unsigned char *begin() const { return vch; } + const unsigned char *end() const { return vch + size(); } + + // Check whether this private key is valid. + bool IsValid() const { return fValid; } + + // Check whether the public key corresponding to this private key is (to be) compressed. + bool IsCompressed() const { return fCompressed; } + + // Initialize from a CPrivKey (serialized OpenSSL private key data). + bool SetPrivKey(const CPrivKey &vchPrivKey, bool fCompressed); + + // Generate a new private key using a cryptographic PRNG. + void MakeNewKey(bool fCompressed); + + // Convert the private key to a CPrivKey (serialized OpenSSL private key data). + // This is expensive. + CPrivKey GetPrivKey() const; + + // Compute the public key from a private key. + // This is expensive. + CPubKey GetPubKey() const; + + // Create a DER-serialized signature. + bool Sign(const uint256 &hash, std::vector& vchSig) const; + + // Create a compact signature (65 bytes), which allows reconstructing the used public key. + // The format is one header byte, followed by two times 32 bytes for the serialized r and s values. + // The header byte: 0x1B = first key with even y, 0x1C = first key with odd y, + // 0x1D = second key with even y, 0x1E = second key with odd y, + // add 0x04 for compressed keys. + bool SignCompact(const uint256 &hash, std::vector& vchSig) const; +}; + +#endif diff --git a/src/keystore.cpp b/src/keystore.cpp new file mode 100644 index 0000000..808f8c2 --- /dev/null +++ b/src/keystore.cpp @@ -0,0 +1,205 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "keystore.h" +#include "script.h" + +bool CKeyStore::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const +{ + CKey key; + if (!GetKey(address, key)) + return false; + vchPubKeyOut = key.GetPubKey(); + return true; +} + +bool CKeyStore::AddKey(const CKey &key) { + return AddKeyPubKey(key, key.GetPubKey()); +} + +bool CBasicKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey) +{ + LOCK(cs_KeyStore); + mapKeys[pubkey.GetID()] = key; + return true; +} + +bool CBasicKeyStore::AddCScript(const CScript& redeemScript) +{ + LOCK(cs_KeyStore); + mapScripts[redeemScript.GetID()] = redeemScript; + return true; +} + +bool CBasicKeyStore::HaveCScript(const CScriptID& hash) const +{ + LOCK(cs_KeyStore); + return mapScripts.count(hash) > 0; +} + +bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const +{ + LOCK(cs_KeyStore); + ScriptMap::const_iterator mi = mapScripts.find(hash); + if (mi != mapScripts.end()) + { + redeemScriptOut = (*mi).second; + return true; + } + return false; +} + +bool CCryptoKeyStore::SetCrypted() +{ + LOCK(cs_KeyStore); + if (fUseCrypto) + return true; + if (!mapKeys.empty()) + return false; + fUseCrypto = true; + return true; +} + +bool CCryptoKeyStore::Lock() +{ + if (!SetCrypted()) + return false; + + { + LOCK(cs_KeyStore); + vMasterKey.clear(); + } + + NotifyStatusChanged(this); + return true; +} + +bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) +{ + { + LOCK(cs_KeyStore); + if (!SetCrypted()) + return false; + + CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); + for (; mi != mapCryptedKeys.end(); ++mi) + { + const CPubKey &vchPubKey = (*mi).second.first; + const std::vector &vchCryptedSecret = (*mi).second.second; + CKeyingMaterial vchSecret; + if(!DecryptSecret(vMasterKeyIn, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) + return false; + if (vchSecret.size() != 32) + return false; + CKey key; + key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); + if (key.GetPubKey() == vchPubKey) + break; + return false; + } + vMasterKey = vMasterKeyIn; + } + NotifyStatusChanged(this); + return true; +} + +bool CCryptoKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey) +{ + { + LOCK(cs_KeyStore); + if (!IsCrypted()) + return CBasicKeyStore::AddKeyPubKey(key, pubkey); + + if (IsLocked()) + return false; + + std::vector vchCryptedSecret; + CKeyingMaterial vchSecret(key.begin(), key.end()); + if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), vchCryptedSecret)) + return false; + + if (!AddCryptedKey(pubkey, vchCryptedSecret)) + return false; + } + return true; +} + + +bool CCryptoKeyStore::AddCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret) +{ + { + LOCK(cs_KeyStore); + if (!SetCrypted()) + return false; + + mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret); + } + return true; +} + +bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey& keyOut) const +{ + { + LOCK(cs_KeyStore); + if (!IsCrypted()) + return CBasicKeyStore::GetKey(address, keyOut); + + CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); + if (mi != mapCryptedKeys.end()) + { + const CPubKey &vchPubKey = (*mi).second.first; + const std::vector &vchCryptedSecret = (*mi).second.second; + CKeyingMaterial vchSecret; + if (!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) + return false; + if (vchSecret.size() != 32) + return false; + keyOut.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); + return true; + } + } + return false; +} + +bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const +{ + { + LOCK(cs_KeyStore); + if (!IsCrypted()) + return CKeyStore::GetPubKey(address, vchPubKeyOut); + + CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); + if (mi != mapCryptedKeys.end()) + { + vchPubKeyOut = (*mi).second.first; + return true; + } + } + return false; +} + +bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn) +{ + { + LOCK(cs_KeyStore); + if (!mapCryptedKeys.empty() || IsCrypted()) + return false; + + fUseCrypto = true; + BOOST_FOREACH(KeyMap::value_type& mKey, mapKeys) + { + const CKey &key = mKey.second; + CPubKey vchPubKey = key.GetPubKey(); + CKeyingMaterial vchSecret(key.begin(), key.end()); + std::vector vchCryptedSecret; + if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), vchCryptedSecret)) + return false; + if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) + return false; + } + mapKeys.clear(); + } + return true; +} diff --git a/src/keystore.h b/src/keystore.h new file mode 100644 index 0000000..49a7bf5 --- /dev/null +++ b/src/keystore.h @@ -0,0 +1,175 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_KEYSTORE_H +#define BITCOIN_KEYSTORE_H + +#include "crypter.h" +#include "sync.h" +#include + +class CScript; + +/** A virtual base class for key stores */ +class CKeyStore +{ +protected: + mutable CCriticalSection cs_KeyStore; + +public: + virtual ~CKeyStore() {} + + // Add a key to the store. + virtual bool AddKeyPubKey(const CKey &key, const CPubKey &pubkey) =0; + virtual bool AddKey(const CKey &key); + + // Check whether a key corresponding to a given address is present in the store. + virtual bool HaveKey(const CKeyID &address) const =0; + virtual bool GetKey(const CKeyID &address, CKey& keyOut) const =0; + virtual void GetKeys(std::set &setAddress) const =0; + virtual bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; + + // Support for BIP 0013 : see https://en.bitcoin.it/wiki/BIP_0013 + virtual bool AddCScript(const CScript& redeemScript) =0; + virtual bool HaveCScript(const CScriptID &hash) const =0; + virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const =0; +}; + +typedef std::map KeyMap; +typedef std::map ScriptMap; + +/** Basic key store, that keeps keys in an address->secret map */ +class CBasicKeyStore : public CKeyStore +{ +protected: + KeyMap mapKeys; + ScriptMap mapScripts; + +public: + bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); + bool HaveKey(const CKeyID &address) const + { + bool result; + { + LOCK(cs_KeyStore); + result = (mapKeys.count(address) > 0); + } + return result; + } + void GetKeys(std::set &setAddress) const + { + setAddress.clear(); + { + LOCK(cs_KeyStore); + KeyMap::const_iterator mi = mapKeys.begin(); + while (mi != mapKeys.end()) + { + setAddress.insert((*mi).first); + mi++; + } + } + } + bool GetKey(const CKeyID &address, CKey &keyOut) const + { + { + LOCK(cs_KeyStore); + KeyMap::const_iterator mi = mapKeys.find(address); + if (mi != mapKeys.end()) + { + keyOut = mi->second; + return true; + } + } + return false; + } + virtual bool AddCScript(const CScript& redeemScript); + virtual bool HaveCScript(const CScriptID &hash) const; + virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const; +}; + +typedef std::map > > CryptedKeyMap; + +/** Keystore which keeps the private keys encrypted. + * It derives from the basic key store, which is used if no encryption is active. + */ +class CCryptoKeyStore : public CBasicKeyStore +{ +private: + CryptedKeyMap mapCryptedKeys; + + CKeyingMaterial vMasterKey; + + // if fUseCrypto is true, mapKeys must be empty + // if fUseCrypto is false, vMasterKey must be empty + bool fUseCrypto; + +protected: + bool SetCrypted(); + + // will encrypt previously unencrypted keys + bool EncryptKeys(CKeyingMaterial& vMasterKeyIn); + + bool Unlock(const CKeyingMaterial& vMasterKeyIn); + +public: + CCryptoKeyStore() : fUseCrypto(false) + { + } + + bool IsCrypted() const + { + return fUseCrypto; + } + + bool IsLocked() const + { + if (!IsCrypted()) + return false; + bool result; + { + LOCK(cs_KeyStore); + result = vMasterKey.empty(); + } + return result; + } + + bool Lock(); + + virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret); + bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); + bool HaveKey(const CKeyID &address) const + { + { + LOCK(cs_KeyStore); + if (!IsCrypted()) + return CBasicKeyStore::HaveKey(address); + return mapCryptedKeys.count(address) > 0; + } + return false; + } + bool GetKey(const CKeyID &address, CKey& keyOut) const; + bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; + void GetKeys(std::set &setAddress) const + { + if (!IsCrypted()) + { + CBasicKeyStore::GetKeys(setAddress); + return; + } + setAddress.clear(); + CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); + while (mi != mapCryptedKeys.end()) + { + setAddress.insert((*mi).first); + mi++; + } + } + + /* Wallet status (encrypted, locked) changed. + * Note: Called without locks held. + */ + boost::signals2::signal NotifyStatusChanged; +}; + +#endif diff --git a/src/leveldb-lin/AUTHORS b/src/leveldb-lin/AUTHORS new file mode 100644 index 0000000..27a9407 --- /dev/null +++ b/src/leveldb-lin/AUTHORS @@ -0,0 +1,8 @@ +# Names should be added to this file like so: +# Name or Organization + +Google Inc. + +# Initial version authors: +Jeffrey Dean +Sanjay Ghemawat diff --git a/src/leveldb-lin/LICENSE b/src/leveldb-lin/LICENSE new file mode 100644 index 0000000..8e80208 --- /dev/null +++ b/src/leveldb-lin/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2011 The LevelDB Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/leveldb-lin/Makefile b/src/leveldb-lin/Makefile new file mode 100644 index 0000000..42c4952 --- /dev/null +++ b/src/leveldb-lin/Makefile @@ -0,0 +1,202 @@ +# Copyright (c) 2011 The LevelDB Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. See the AUTHORS file for names of contributors. + +#----------------------------------------------- +# Uncomment exactly one of the lines labelled (A), (B), and (C) below +# to switch between compilation modes. + +OPT ?= -O2 -DNDEBUG # (A) Production use (optimized mode) +# OPT ?= -g2 # (B) Debug mode, w/ full line-level debugging symbols +# OPT ?= -O2 -g2 -DNDEBUG # (C) Profiling mode: opt, but w/debugging symbols +#----------------------------------------------- + +# detect what platform we're building on +$(shell CC=$(CC) CXX=$(CXX) TARGET_OS=$(TARGET_OS) \ + ./build_detect_platform build_config.mk ./) +# this file is generated by the previous line to set build flags and sources +include build_config.mk + +CFLAGS += -I. -I./include $(PLATFORM_CCFLAGS) $(OPT) +CXXFLAGS += -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) + +LDFLAGS += $(PLATFORM_LDFLAGS) +LIBS += $(PLATFORM_LIBS) + +LIBOBJECTS = $(SOURCES:.cc=.o) +MEMENVOBJECTS = $(MEMENV_SOURCES:.cc=.o) + +TESTUTIL = ./util/testutil.o +TESTHARNESS = ./util/testharness.o $(TESTUTIL) + +TESTS = \ + arena_test \ + bloom_test \ + c_test \ + cache_test \ + coding_test \ + corruption_test \ + crc32c_test \ + db_test \ + dbformat_test \ + env_test \ + filename_test \ + filter_block_test \ + log_test \ + memenv_test \ + skiplist_test \ + table_test \ + version_edit_test \ + version_set_test \ + write_batch_test + +PROGRAMS = db_bench leveldbutil $(TESTS) +BENCHMARKS = db_bench_sqlite3 db_bench_tree_db + +LIBRARY = libleveldb.a +MEMENVLIBRARY = libmemenv.a + +default: all + +# Should we build shared libraries? +ifneq ($(PLATFORM_SHARED_EXT),) + +ifneq ($(PLATFORM_SHARED_VERSIONED),true) +SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT) +SHARED2 = $(SHARED1) +SHARED3 = $(SHARED1) +SHARED = $(SHARED1) +else +# Update db.h if you change these. +SHARED_MAJOR = 1 +SHARED_MINOR = 9 +SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT) +SHARED2 = $(SHARED1).$(SHARED_MAJOR) +SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR) +SHARED = $(SHARED1) $(SHARED2) $(SHARED3) +$(SHARED1): $(SHARED3) + ln -fs $(SHARED3) $(SHARED1) +$(SHARED2): $(SHARED3) + ln -fs $(SHARED3) $(SHARED2) +endif + +$(SHARED3): + $(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED2) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SOURCES) -o $(SHARED3) $(LIBS) + +endif # PLATFORM_SHARED_EXT + +all: $(SHARED) $(LIBRARY) + +check: all $(PROGRAMS) $(TESTS) + for t in $(TESTS); do echo "***** Running $$t"; ./$$t || exit 1; done + +clean: + -rm -f $(PROGRAMS) $(BENCHMARKS) $(LIBRARY) $(SHARED) $(MEMENVLIBRARY) */*.o */*/*.o ios-x86/*/*.o ios-arm/*/*.o build_config.mk + -rm -rf ios-x86/* ios-arm/* + +$(LIBRARY): $(LIBOBJECTS) + rm -f $@ + $(AR) -rs $@ $(LIBOBJECTS) + +db_bench: db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) + $(CXX) $(LDFLAGS) db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LIBS) + +db_bench_sqlite3: doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) + $(CXX) $(LDFLAGS) doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) -o $@ -lsqlite3 $(LIBS) + +db_bench_tree_db: doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) + $(CXX) $(LDFLAGS) doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) -o $@ -lkyotocabinet $(LIBS) + +leveldbutil: db/leveldb_main.o $(LIBOBJECTS) + $(CXX) $(LDFLAGS) db/leveldb_main.o $(LIBOBJECTS) -o $@ $(LIBS) + +arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +bloom_test: util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +c_test: db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +cache_test: util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +coding_test: util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +corruption_test: db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +crc32c_test: util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +db_test: db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +dbformat_test: db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +env_test: util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +filter_block_test: table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +table_test: table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +skiplist_test: db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +version_edit_test: db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +version_set_test: db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +write_batch_test: db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(MEMENVLIBRARY) : $(MEMENVOBJECTS) + rm -f $@ + $(AR) -rs $@ $(MEMENVOBJECTS) + +memenv_test : helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) + $(CXX) $(LDFLAGS) helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) -o $@ $(LIBS) + +ifeq ($(PLATFORM), IOS) +# For iOS, create universal object files to be used on both the simulator and +# a device. +PLATFORMSROOT=/Applications/Xcode.app/Contents/Developer/Platforms +SIMULATORROOT=$(PLATFORMSROOT)/iPhoneSimulator.platform/Developer +DEVICEROOT=$(PLATFORMSROOT)/iPhoneOS.platform/Developer +IOSVERSION=$(shell defaults read $(PLATFORMSROOT)/iPhoneOS.platform/version CFBundleShortVersionString) + +.cc.o: + mkdir -p ios-x86/$(dir $@) + $(CXX) $(CXXFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -c $< -o ios-x86/$@ + mkdir -p ios-arm/$(dir $@) + $(DEVICEROOT)/usr/bin/$(CXX) $(CXXFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk -arch armv6 -arch armv7 -c $< -o ios-arm/$@ + lipo ios-x86/$@ ios-arm/$@ -create -output $@ + +.c.o: + mkdir -p ios-x86/$(dir $@) + $(CC) $(CFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -c $< -o ios-x86/$@ + mkdir -p ios-arm/$(dir $@) + $(DEVICEROOT)/usr/bin/$(CC) $(CFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk -arch armv6 -arch armv7 -c $< -o ios-arm/$@ + lipo ios-x86/$@ ios-arm/$@ -create -output $@ + +else +.cc.o: + $(CXX) $(CXXFLAGS) -c $< -o $@ + +.c.o: + $(CC) $(CFLAGS) -c $< -o $@ +endif diff --git a/src/leveldb-lin/NEWS b/src/leveldb-lin/NEWS new file mode 100644 index 0000000..3fd9924 --- /dev/null +++ b/src/leveldb-lin/NEWS @@ -0,0 +1,17 @@ +Release 1.2 2011-05-16 +---------------------- + +Fixes for larger databases (tested up to one billion 100-byte entries, +i.e., ~100GB). + +(1) Place hard limit on number of level-0 files. This fixes errors +of the form "too many open files". + +(2) Fixed memtable management. Before the fix, a heavy write burst +could cause unbounded memory usage. + +A fix for a logging bug where the reader would incorrectly complain +about corruption. + +Allow public access to WriteBatch contents so that users can easily +wrap a DB. diff --git a/src/leveldb-lin/README b/src/leveldb-lin/README new file mode 100644 index 0000000..3618ade --- /dev/null +++ b/src/leveldb-lin/README @@ -0,0 +1,51 @@ +leveldb: A key-value store +Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com) + +The code under this directory implements a system for maintaining a +persistent key/value store. + +See doc/index.html for more explanation. +See doc/impl.html for a brief overview of the implementation. + +The public interface is in include/*.h. Callers should not include or +rely on the details of any other header files in this package. Those +internal APIs may be changed without warning. + +Guide to header files: + +include/db.h + Main interface to the DB: Start here + +include/options.h + Control over the behavior of an entire database, and also + control over the behavior of individual reads and writes. + +include/comparator.h + Abstraction for user-specified comparison function. If you want + just bytewise comparison of keys, you can use the default comparator, + but clients can write their own comparator implementations if they + want custom ordering (e.g. to handle different character + encodings, etc.) + +include/iterator.h + Interface for iterating over data. You can get an iterator + from a DB object. + +include/write_batch.h + Interface for atomically applying multiple updates to a database. + +include/slice.h + A simple module for maintaining a pointer and a length into some + other byte array. + +include/status.h + Status is returned from many of the public interfaces and is used + to report success and various kinds of errors. + +include/env.h + Abstraction of the OS environment. A posix implementation of + this interface is in util/env_posix.cc + +include/table.h +include/table_builder.h + Lower-level modules that most clients probably won't use directly diff --git a/src/leveldb-lin/TODO b/src/leveldb-lin/TODO new file mode 100644 index 0000000..e603c07 --- /dev/null +++ b/src/leveldb-lin/TODO @@ -0,0 +1,14 @@ +ss +- Stats + +db +- Maybe implement DB::BulkDeleteForRange(start_key, end_key) + that would blow away files whose ranges are entirely contained + within [start_key..end_key]? For Chrome, deletion of obsolete + object stores, etc. can be done in the background anyway, so + probably not that important. +- There have been requests for MultiGet. + +After a range is completely deleted, what gets rid of the +corresponding files if we do no future changes to that range. Make +the conditions for triggering compactions fire in more situations? diff --git a/src/leveldb-lin/WINDOWS.md b/src/leveldb-lin/WINDOWS.md new file mode 100644 index 0000000..5b76c24 --- /dev/null +++ b/src/leveldb-lin/WINDOWS.md @@ -0,0 +1,39 @@ +# Building LevelDB On Windows + +## Prereqs + +Install the [Windows Software Development Kit version 7.1](http://www.microsoft.com/downloads/dlx/en-us/listdetailsview.aspx?FamilyID=6b6c21d2-2006-4afa-9702-529fa782d63b). + +Download and extract the [Snappy source distribution](http://snappy.googlecode.com/files/snappy-1.0.5.tar.gz) + +1. Open the "Windows SDK 7.1 Command Prompt" : + Start Menu -> "Microsoft Windows SDK v7.1" > "Windows SDK 7.1 Command Prompt" +2. Change the directory to the leveldb project + +## Building the Static lib + +* 32 bit Version + + setenv /x86 + msbuild.exe /p:Configuration=Release /p:Platform=Win32 /p:Snappy=..\snappy-1.0.5 + +* 64 bit Version + + setenv /x64 + msbuild.exe /p:Configuration=Release /p:Platform=x64 /p:Snappy=..\snappy-1.0.5 + + +## Building and Running the Benchmark app + +* 32 bit Version + + setenv /x86 + msbuild.exe /p:Configuration=Benchmark /p:Platform=Win32 /p:Snappy=..\snappy-1.0.5 + Benchmark\leveldb.exe + +* 64 bit Version + + setenv /x64 + msbuild.exe /p:Configuration=Benchmark /p:Platform=x64 /p:Snappy=..\snappy-1.0.5 + x64\Benchmark\leveldb.exe + diff --git a/src/leveldb-lin/build_config.mk b/src/leveldb-lin/build_config.mk new file mode 100644 index 0000000..146412a --- /dev/null +++ b/src/leveldb-lin/build_config.mk @@ -0,0 +1,13 @@ +SOURCES=db/builder.cc db/c.cc db/dbformat.cc db/db_impl.cc db/db_iter.cc db/filename.cc db/log_reader.cc db/log_writer.cc db/memtable.cc db/repair.cc db/table_cache.cc db/version_edit.cc db/version_set.cc db/write_batch.cc table/block_builder.cc table/block.cc table/filter_block.cc table/format.cc table/iterator.cc table/merger.cc table/table_builder.cc table/table.cc table/two_level_iterator.cc util/arena.cc util/bloom.cc util/cache.cc util/coding.cc util/comparator.cc util/crc32c.cc util/env.cc util/env_posix.cc util/env_win.cc util/filter_policy.cc util/hash.cc util/histogram.cc util/logging.cc util/options.cc util/status.cc port/port_posix.cc +MEMENV_SOURCES=helpers/memenv/memenv.cc +CC=cc +CXX=g++ +PLATFORM=OS_LINUX +PLATFORM_LDFLAGS=-pthread +PLATFORM_LIBS= +PLATFORM_CCFLAGS= -fno-builtin-memcmp -pthread -DOS_LINUX -DLEVELDB_PLATFORM_POSIX +PLATFORM_CXXFLAGS= -fno-builtin-memcmp -pthread -DOS_LINUX -DLEVELDB_PLATFORM_POSIX +PLATFORM_SHARED_CFLAGS=-fPIC +PLATFORM_SHARED_EXT=so +PLATFORM_SHARED_LDFLAGS=-shared -Wl,-soname -Wl, +PLATFORM_SHARED_VERSIONED=true diff --git a/src/leveldb-lin/build_detect_platform b/src/leveldb-lin/build_detect_platform new file mode 100644 index 0000000..609cb51 --- /dev/null +++ b/src/leveldb-lin/build_detect_platform @@ -0,0 +1,200 @@ +#!/bin/sh +# +# Detects OS we're compiling on and outputs a file specified by the first +# argument, which in turn gets read while processing Makefile. +# +# The output will set the following variables: +# CC C Compiler path +# CXX C++ Compiler path +# PLATFORM_LDFLAGS Linker flags +# PLATFORM_LIBS Libraries flags +# PLATFORM_SHARED_EXT Extension for shared libraries +# PLATFORM_SHARED_LDFLAGS Flags for building shared library +# This flag is embedded just before the name +# of the shared library without intervening spaces +# PLATFORM_SHARED_CFLAGS Flags for compiling objects for shared library +# PLATFORM_CCFLAGS C compiler flags +# PLATFORM_CXXFLAGS C++ compiler flags. Will contain: +# PLATFORM_SHARED_VERSIONED Set to 'true' if platform supports versioned +# shared libraries, empty otherwise. +# +# The PLATFORM_CCFLAGS and PLATFORM_CXXFLAGS might include the following: +# +# -DLEVELDB_CSTDATOMIC_PRESENT if is present +# -DLEVELDB_PLATFORM_POSIX for Posix-based platforms +# -DSNAPPY if the Snappy library is present +# + +OUTPUT=$1 +PREFIX=$2 +if test -z "$OUTPUT" || test -z "$PREFIX"; then + echo "usage: $0 " >&2 + exit 1 +fi + +# Delete existing output, if it exists +rm -f $OUTPUT +touch $OUTPUT + +if test -z "$CC"; then + CC=cc +fi + +if test -z "$CXX"; then + CXX=g++ +fi + +# Detect OS +if test -z "$TARGET_OS"; then + TARGET_OS=`uname -s` +fi + +COMMON_FLAGS= +CROSS_COMPILE= +PLATFORM_CCFLAGS= +PLATFORM_CXXFLAGS= +PLATFORM_LDFLAGS= +PLATFORM_LIBS= +PLATFORM_SHARED_EXT="so" +PLATFORM_SHARED_LDFLAGS="-shared -Wl,-soname -Wl," +PLATFORM_SHARED_CFLAGS="-fPIC" +PLATFORM_SHARED_VERSIONED=true + +MEMCMP_FLAG= +if [ "$CXX" = "g++" ]; then + # Use libc's memcmp instead of GCC's memcmp. This results in ~40% + # performance improvement on readrandom under gcc 4.4.3 on Linux/x86. + MEMCMP_FLAG="-fno-builtin-memcmp" +fi + +case "$TARGET_OS" in + Darwin) + PLATFORM=OS_MACOSX + COMMON_FLAGS="$MEMCMP_FLAG -DOS_MACOSX" + PLATFORM_SHARED_EXT=dylib + [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd` + PLATFORM_SHARED_LDFLAGS="-dynamiclib -install_name $INSTALL_PATH/" + PORT_FILE=port/port_posix.cc + ;; + Linux) + PLATFORM=OS_LINUX + COMMON_FLAGS="$MEMCMP_FLAG -pthread -DOS_LINUX" + PLATFORM_LDFLAGS="-pthread" + PORT_FILE=port/port_posix.cc + ;; + SunOS) + PLATFORM=OS_SOLARIS + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_SOLARIS" + PLATFORM_LIBS="-lpthread -lrt" + PORT_FILE=port/port_posix.cc + ;; + FreeBSD) + PLATFORM=OS_FREEBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_FREEBSD" + PLATFORM_LIBS="-lpthread" + PORT_FILE=port/port_posix.cc + ;; + NetBSD) + PLATFORM=OS_NETBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_NETBSD" + PLATFORM_LIBS="-lpthread -lgcc_s" + PORT_FILE=port/port_posix.cc + ;; + OpenBSD) + PLATFORM=OS_OPENBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_OPENBSD" + PLATFORM_LDFLAGS="-pthread" + PORT_FILE=port/port_posix.cc + ;; + DragonFly) + PLATFORM=OS_DRAGONFLYBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_DRAGONFLYBSD" + PLATFORM_LIBS="-lpthread" + PORT_FILE=port/port_posix.cc + ;; + OS_ANDROID_CROSSCOMPILE) + PLATFORM=OS_ANDROID + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_ANDROID -DLEVELDB_PLATFORM_POSIX" + PLATFORM_LDFLAGS="" # All pthread features are in the Android C library + PORT_FILE=port/port_posix.cc + CROSS_COMPILE=true + ;; + HP-UX) + PLATFORM=OS_HPUX + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_HPUX" + PLATFORM_LDFLAGS="-pthread" + PORT_FILE=port/port_posix.cc + # man ld: +h internal_name + PLATFORM_SHARED_LDFLAGS="-shared -Wl,+h -Wl," + ;; + OS_WINDOWS_CROSSCOMPILE | NATIVE_WINDOWS) + PLATFORM=OS_WINDOWS + COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_WINDOWS -DLEVELDB_PLATFORM_WINDOWS -DWINVER=0x0500 -D__USE_MINGW_ANSI_STDIO=1" + PLATFORM_SOURCES="util/env_win.cc" + PLATFORM_LIBS="-lshlwapi" + PORT_FILE=port/port_win.cc + CROSS_COMPILE=true + ;; + *) + echo "Unknown platform!" >&2 + exit 1 +esac + +# We want to make a list of all cc files within util, db, table, and helpers +# except for the test and benchmark files. By default, find will output a list +# of all files matching either rule, so we need to append -print to make the +# prune take effect. +DIRS="$PREFIX/db $PREFIX/util $PREFIX/table" + +set -f # temporarily disable globbing so that our patterns aren't expanded +PRUNE_TEST="-name *test*.cc -prune" +PRUNE_BENCH="-name *_bench.cc -prune" +PRUNE_TOOL="-name leveldb_main.cc -prune" +PORTABLE_FILES=`find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o $PRUNE_TOOL -o -name '*.cc' -print | sort | sed "s,^$PREFIX/,," | tr "\n" " "` + +set +f # re-enable globbing + +# The sources consist of the portable files, plus the platform-specific port +# file. +echo "SOURCES=$PORTABLE_FILES $PORT_FILE" >> $OUTPUT +echo "MEMENV_SOURCES=helpers/memenv/memenv.cc" >> $OUTPUT + +if [ "$CROSS_COMPILE" = "true" ]; then + # Cross-compiling; do not try any compilation tests. + true +else + # If -std=c++0x works, use . Otherwise use port_posix.h. + $CXX $CXXFLAGS -std=c++0x -x c++ - -o /dev/null 2>/dev/null < + int main() {} +EOF + if [ "$?" = 0 ]; then + COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX -DLEVELDB_CSTDATOMIC_PRESENT" + PLATFORM_CXXFLAGS="-std=c++0x" + else + COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX" + fi + + # Test whether tcmalloc is available + $CXX $CXXFLAGS -x c++ - -o /dev/null -ltcmalloc 2>/dev/null <> $OUTPUT +echo "CXX=$CXX" >> $OUTPUT +echo "PLATFORM=$PLATFORM" >> $OUTPUT +echo "PLATFORM_LDFLAGS=$PLATFORM_LDFLAGS" >> $OUTPUT +echo "PLATFORM_LIBS=$PLATFORM_LIBS" >> $OUTPUT +echo "PLATFORM_CCFLAGS=$PLATFORM_CCFLAGS" >> $OUTPUT +echo "PLATFORM_CXXFLAGS=$PLATFORM_CXXFLAGS" >> $OUTPUT +echo "PLATFORM_SHARED_CFLAGS=$PLATFORM_SHARED_CFLAGS" >> $OUTPUT +echo "PLATFORM_SHARED_EXT=$PLATFORM_SHARED_EXT" >> $OUTPUT +echo "PLATFORM_SHARED_LDFLAGS=$PLATFORM_SHARED_LDFLAGS" >> $OUTPUT +echo "PLATFORM_SHARED_VERSIONED=$PLATFORM_SHARED_VERSIONED" >> $OUTPUT diff --git a/src/leveldb-lin/db/builder.cc b/src/leveldb-lin/db/builder.cc new file mode 100644 index 0000000..f419882 --- /dev/null +++ b/src/leveldb-lin/db/builder.cc @@ -0,0 +1,88 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/builder.h" + +#include "db/filename.h" +#include "db/dbformat.h" +#include "db/table_cache.h" +#include "db/version_edit.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" + +namespace leveldb { + +Status BuildTable(const std::string& dbname, + Env* env, + const Options& options, + TableCache* table_cache, + Iterator* iter, + FileMetaData* meta) { + Status s; + meta->file_size = 0; + iter->SeekToFirst(); + + std::string fname = TableFileName(dbname, meta->number); + if (iter->Valid()) { + WritableFile* file; + s = env->NewWritableFile(fname, &file); + if (!s.ok()) { + return s; + } + + TableBuilder* builder = new TableBuilder(options, file); + meta->smallest.DecodeFrom(iter->key()); + for (; iter->Valid(); iter->Next()) { + Slice key = iter->key(); + meta->largest.DecodeFrom(key); + builder->Add(key, iter->value()); + } + + // Finish and check for builder errors + if (s.ok()) { + s = builder->Finish(); + if (s.ok()) { + meta->file_size = builder->FileSize(); + assert(meta->file_size > 0); + } + } else { + builder->Abandon(); + } + delete builder; + + // Finish and check for file errors + if (s.ok()) { + s = file->Sync(); + } + if (s.ok()) { + s = file->Close(); + } + delete file; + file = NULL; + + if (s.ok()) { + // Verify that the table is usable + Iterator* it = table_cache->NewIterator(ReadOptions(), + meta->number, + meta->file_size); + s = it->status(); + delete it; + } + } + + // Check for input iterator errors + if (!iter->status().ok()) { + s = iter->status(); + } + + if (s.ok() && meta->file_size > 0) { + // Keep it + } else { + env->DeleteFile(fname); + } + return s; +} + +} // namespace leveldb diff --git a/src/leveldb-lin/db/builder.h b/src/leveldb-lin/db/builder.h new file mode 100644 index 0000000..62431fc --- /dev/null +++ b/src/leveldb-lin/db/builder.h @@ -0,0 +1,34 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_BUILDER_H_ +#define STORAGE_LEVELDB_DB_BUILDER_H_ + +#include "leveldb/status.h" + +namespace leveldb { + +struct Options; +struct FileMetaData; + +class Env; +class Iterator; +class TableCache; +class VersionEdit; + +// Build a Table file from the contents of *iter. The generated file +// will be named according to meta->number. On success, the rest of +// *meta will be filled with metadata about the generated table. +// If no data is present in *iter, meta->file_size will be set to +// zero, and no Table file will be produced. +extern Status BuildTable(const std::string& dbname, + Env* env, + const Options& options, + TableCache* table_cache, + Iterator* iter, + FileMetaData* meta); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_BUILDER_H_ diff --git a/src/leveldb-lin/db/builder.o b/src/leveldb-lin/db/builder.o new file mode 100644 index 0000000..418e21d Binary files /dev/null and b/src/leveldb-lin/db/builder.o differ diff --git a/src/leveldb-lin/db/c.cc b/src/leveldb-lin/db/c.cc new file mode 100644 index 0000000..08ff0ad --- /dev/null +++ b/src/leveldb-lin/db/c.cc @@ -0,0 +1,595 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/c.h" + +#include +#include +#include "leveldb/cache.h" +#include "leveldb/comparator.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/filter_policy.h" +#include "leveldb/iterator.h" +#include "leveldb/options.h" +#include "leveldb/status.h" +#include "leveldb/write_batch.h" + +using leveldb::Cache; +using leveldb::Comparator; +using leveldb::CompressionType; +using leveldb::DB; +using leveldb::Env; +using leveldb::FileLock; +using leveldb::FilterPolicy; +using leveldb::Iterator; +using leveldb::kMajorVersion; +using leveldb::kMinorVersion; +using leveldb::Logger; +using leveldb::NewBloomFilterPolicy; +using leveldb::NewLRUCache; +using leveldb::Options; +using leveldb::RandomAccessFile; +using leveldb::Range; +using leveldb::ReadOptions; +using leveldb::SequentialFile; +using leveldb::Slice; +using leveldb::Snapshot; +using leveldb::Status; +using leveldb::WritableFile; +using leveldb::WriteBatch; +using leveldb::WriteOptions; + +extern "C" { + +struct leveldb_t { DB* rep; }; +struct leveldb_iterator_t { Iterator* rep; }; +struct leveldb_writebatch_t { WriteBatch rep; }; +struct leveldb_snapshot_t { const Snapshot* rep; }; +struct leveldb_readoptions_t { ReadOptions rep; }; +struct leveldb_writeoptions_t { WriteOptions rep; }; +struct leveldb_options_t { Options rep; }; +struct leveldb_cache_t { Cache* rep; }; +struct leveldb_seqfile_t { SequentialFile* rep; }; +struct leveldb_randomfile_t { RandomAccessFile* rep; }; +struct leveldb_writablefile_t { WritableFile* rep; }; +struct leveldb_logger_t { Logger* rep; }; +struct leveldb_filelock_t { FileLock* rep; }; + +struct leveldb_comparator_t : public Comparator { + void* state_; + void (*destructor_)(void*); + int (*compare_)( + void*, + const char* a, size_t alen, + const char* b, size_t blen); + const char* (*name_)(void*); + + virtual ~leveldb_comparator_t() { + (*destructor_)(state_); + } + + virtual int Compare(const Slice& a, const Slice& b) const { + return (*compare_)(state_, a.data(), a.size(), b.data(), b.size()); + } + + virtual const char* Name() const { + return (*name_)(state_); + } + + // No-ops since the C binding does not support key shortening methods. + virtual void FindShortestSeparator(std::string*, const Slice&) const { } + virtual void FindShortSuccessor(std::string* key) const { } +}; + +struct leveldb_filterpolicy_t : public FilterPolicy { + void* state_; + void (*destructor_)(void*); + const char* (*name_)(void*); + char* (*create_)( + void*, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length); + unsigned char (*key_match_)( + void*, + const char* key, size_t length, + const char* filter, size_t filter_length); + + virtual ~leveldb_filterpolicy_t() { + (*destructor_)(state_); + } + + virtual const char* Name() const { + return (*name_)(state_); + } + + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + std::vector key_pointers(n); + std::vector key_sizes(n); + for (int i = 0; i < n; i++) { + key_pointers[i] = keys[i].data(); + key_sizes[i] = keys[i].size(); + } + size_t len; + char* filter = (*create_)(state_, &key_pointers[0], &key_sizes[0], n, &len); + dst->append(filter, len); + free(filter); + } + + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const { + return (*key_match_)(state_, key.data(), key.size(), + filter.data(), filter.size()); + } +}; + +struct leveldb_env_t { + Env* rep; + bool is_default; +}; + +static bool SaveError(char** errptr, const Status& s) { + assert(errptr != NULL); + if (s.ok()) { + return false; + } else if (*errptr == NULL) { + *errptr = strdup(s.ToString().c_str()); + } else { + // TODO(sanjay): Merge with existing error? + free(*errptr); + *errptr = strdup(s.ToString().c_str()); + } + return true; +} + +static char* CopyString(const std::string& str) { + char* result = reinterpret_cast(malloc(sizeof(char) * str.size())); + memcpy(result, str.data(), sizeof(char) * str.size()); + return result; +} + +leveldb_t* leveldb_open( + const leveldb_options_t* options, + const char* name, + char** errptr) { + DB* db; + if (SaveError(errptr, DB::Open(options->rep, std::string(name), &db))) { + return NULL; + } + leveldb_t* result = new leveldb_t; + result->rep = db; + return result; +} + +void leveldb_close(leveldb_t* db) { + delete db->rep; + delete db; +} + +void leveldb_put( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + const char* val, size_t vallen, + char** errptr) { + SaveError(errptr, + db->rep->Put(options->rep, Slice(key, keylen), Slice(val, vallen))); +} + +void leveldb_delete( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + char** errptr) { + SaveError(errptr, db->rep->Delete(options->rep, Slice(key, keylen))); +} + + +void leveldb_write( + leveldb_t* db, + const leveldb_writeoptions_t* options, + leveldb_writebatch_t* batch, + char** errptr) { + SaveError(errptr, db->rep->Write(options->rep, &batch->rep)); +} + +char* leveldb_get( + leveldb_t* db, + const leveldb_readoptions_t* options, + const char* key, size_t keylen, + size_t* vallen, + char** errptr) { + char* result = NULL; + std::string tmp; + Status s = db->rep->Get(options->rep, Slice(key, keylen), &tmp); + if (s.ok()) { + *vallen = tmp.size(); + result = CopyString(tmp); + } else { + *vallen = 0; + if (!s.IsNotFound()) { + SaveError(errptr, s); + } + } + return result; +} + +leveldb_iterator_t* leveldb_create_iterator( + leveldb_t* db, + const leveldb_readoptions_t* options) { + leveldb_iterator_t* result = new leveldb_iterator_t; + result->rep = db->rep->NewIterator(options->rep); + return result; +} + +const leveldb_snapshot_t* leveldb_create_snapshot( + leveldb_t* db) { + leveldb_snapshot_t* result = new leveldb_snapshot_t; + result->rep = db->rep->GetSnapshot(); + return result; +} + +void leveldb_release_snapshot( + leveldb_t* db, + const leveldb_snapshot_t* snapshot) { + db->rep->ReleaseSnapshot(snapshot->rep); + delete snapshot; +} + +char* leveldb_property_value( + leveldb_t* db, + const char* propname) { + std::string tmp; + if (db->rep->GetProperty(Slice(propname), &tmp)) { + // We use strdup() since we expect human readable output. + return strdup(tmp.c_str()); + } else { + return NULL; + } +} + +void leveldb_approximate_sizes( + leveldb_t* db, + int num_ranges, + const char* const* range_start_key, const size_t* range_start_key_len, + const char* const* range_limit_key, const size_t* range_limit_key_len, + uint64_t* sizes) { + Range* ranges = new Range[num_ranges]; + for (int i = 0; i < num_ranges; i++) { + ranges[i].start = Slice(range_start_key[i], range_start_key_len[i]); + ranges[i].limit = Slice(range_limit_key[i], range_limit_key_len[i]); + } + db->rep->GetApproximateSizes(ranges, num_ranges, sizes); + delete[] ranges; +} + +void leveldb_compact_range( + leveldb_t* db, + const char* start_key, size_t start_key_len, + const char* limit_key, size_t limit_key_len) { + Slice a, b; + db->rep->CompactRange( + // Pass NULL Slice if corresponding "const char*" is NULL + (start_key ? (a = Slice(start_key, start_key_len), &a) : NULL), + (limit_key ? (b = Slice(limit_key, limit_key_len), &b) : NULL)); +} + +void leveldb_destroy_db( + const leveldb_options_t* options, + const char* name, + char** errptr) { + SaveError(errptr, DestroyDB(name, options->rep)); +} + +void leveldb_repair_db( + const leveldb_options_t* options, + const char* name, + char** errptr) { + SaveError(errptr, RepairDB(name, options->rep)); +} + +void leveldb_iter_destroy(leveldb_iterator_t* iter) { + delete iter->rep; + delete iter; +} + +unsigned char leveldb_iter_valid(const leveldb_iterator_t* iter) { + return iter->rep->Valid(); +} + +void leveldb_iter_seek_to_first(leveldb_iterator_t* iter) { + iter->rep->SeekToFirst(); +} + +void leveldb_iter_seek_to_last(leveldb_iterator_t* iter) { + iter->rep->SeekToLast(); +} + +void leveldb_iter_seek(leveldb_iterator_t* iter, const char* k, size_t klen) { + iter->rep->Seek(Slice(k, klen)); +} + +void leveldb_iter_next(leveldb_iterator_t* iter) { + iter->rep->Next(); +} + +void leveldb_iter_prev(leveldb_iterator_t* iter) { + iter->rep->Prev(); +} + +const char* leveldb_iter_key(const leveldb_iterator_t* iter, size_t* klen) { + Slice s = iter->rep->key(); + *klen = s.size(); + return s.data(); +} + +const char* leveldb_iter_value(const leveldb_iterator_t* iter, size_t* vlen) { + Slice s = iter->rep->value(); + *vlen = s.size(); + return s.data(); +} + +void leveldb_iter_get_error(const leveldb_iterator_t* iter, char** errptr) { + SaveError(errptr, iter->rep->status()); +} + +leveldb_writebatch_t* leveldb_writebatch_create() { + return new leveldb_writebatch_t; +} + +void leveldb_writebatch_destroy(leveldb_writebatch_t* b) { + delete b; +} + +void leveldb_writebatch_clear(leveldb_writebatch_t* b) { + b->rep.Clear(); +} + +void leveldb_writebatch_put( + leveldb_writebatch_t* b, + const char* key, size_t klen, + const char* val, size_t vlen) { + b->rep.Put(Slice(key, klen), Slice(val, vlen)); +} + +void leveldb_writebatch_delete( + leveldb_writebatch_t* b, + const char* key, size_t klen) { + b->rep.Delete(Slice(key, klen)); +} + +void leveldb_writebatch_iterate( + leveldb_writebatch_t* b, + void* state, + void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen), + void (*deleted)(void*, const char* k, size_t klen)) { + class H : public WriteBatch::Handler { + public: + void* state_; + void (*put_)(void*, const char* k, size_t klen, const char* v, size_t vlen); + void (*deleted_)(void*, const char* k, size_t klen); + virtual void Put(const Slice& key, const Slice& value) { + (*put_)(state_, key.data(), key.size(), value.data(), value.size()); + } + virtual void Delete(const Slice& key) { + (*deleted_)(state_, key.data(), key.size()); + } + }; + H handler; + handler.state_ = state; + handler.put_ = put; + handler.deleted_ = deleted; + b->rep.Iterate(&handler); +} + +leveldb_options_t* leveldb_options_create() { + return new leveldb_options_t; +} + +void leveldb_options_destroy(leveldb_options_t* options) { + delete options; +} + +void leveldb_options_set_comparator( + leveldb_options_t* opt, + leveldb_comparator_t* cmp) { + opt->rep.comparator = cmp; +} + +void leveldb_options_set_filter_policy( + leveldb_options_t* opt, + leveldb_filterpolicy_t* policy) { + opt->rep.filter_policy = policy; +} + +void leveldb_options_set_create_if_missing( + leveldb_options_t* opt, unsigned char v) { + opt->rep.create_if_missing = v; +} + +void leveldb_options_set_error_if_exists( + leveldb_options_t* opt, unsigned char v) { + opt->rep.error_if_exists = v; +} + +void leveldb_options_set_paranoid_checks( + leveldb_options_t* opt, unsigned char v) { + opt->rep.paranoid_checks = v; +} + +void leveldb_options_set_env(leveldb_options_t* opt, leveldb_env_t* env) { + opt->rep.env = (env ? env->rep : NULL); +} + +void leveldb_options_set_info_log(leveldb_options_t* opt, leveldb_logger_t* l) { + opt->rep.info_log = (l ? l->rep : NULL); +} + +void leveldb_options_set_write_buffer_size(leveldb_options_t* opt, size_t s) { + opt->rep.write_buffer_size = s; +} + +void leveldb_options_set_max_open_files(leveldb_options_t* opt, int n) { + opt->rep.max_open_files = n; +} + +void leveldb_options_set_cache(leveldb_options_t* opt, leveldb_cache_t* c) { + opt->rep.block_cache = c->rep; +} + +void leveldb_options_set_block_size(leveldb_options_t* opt, size_t s) { + opt->rep.block_size = s; +} + +void leveldb_options_set_block_restart_interval(leveldb_options_t* opt, int n) { + opt->rep.block_restart_interval = n; +} + +void leveldb_options_set_compression(leveldb_options_t* opt, int t) { + opt->rep.compression = static_cast(t); +} + +leveldb_comparator_t* leveldb_comparator_create( + void* state, + void (*destructor)(void*), + int (*compare)( + void*, + const char* a, size_t alen, + const char* b, size_t blen), + const char* (*name)(void*)) { + leveldb_comparator_t* result = new leveldb_comparator_t; + result->state_ = state; + result->destructor_ = destructor; + result->compare_ = compare; + result->name_ = name; + return result; +} + +void leveldb_comparator_destroy(leveldb_comparator_t* cmp) { + delete cmp; +} + +leveldb_filterpolicy_t* leveldb_filterpolicy_create( + void* state, + void (*destructor)(void*), + char* (*create_filter)( + void*, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length), + unsigned char (*key_may_match)( + void*, + const char* key, size_t length, + const char* filter, size_t filter_length), + const char* (*name)(void*)) { + leveldb_filterpolicy_t* result = new leveldb_filterpolicy_t; + result->state_ = state; + result->destructor_ = destructor; + result->create_ = create_filter; + result->key_match_ = key_may_match; + result->name_ = name; + return result; +} + +void leveldb_filterpolicy_destroy(leveldb_filterpolicy_t* filter) { + delete filter; +} + +leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom(int bits_per_key) { + // Make a leveldb_filterpolicy_t, but override all of its methods so + // they delegate to a NewBloomFilterPolicy() instead of user + // supplied C functions. + struct Wrapper : public leveldb_filterpolicy_t { + const FilterPolicy* rep_; + ~Wrapper() { delete rep_; } + const char* Name() const { return rep_->Name(); } + void CreateFilter(const Slice* keys, int n, std::string* dst) const { + return rep_->CreateFilter(keys, n, dst); + } + bool KeyMayMatch(const Slice& key, const Slice& filter) const { + return rep_->KeyMayMatch(key, filter); + } + static void DoNothing(void*) { } + }; + Wrapper* wrapper = new Wrapper; + wrapper->rep_ = NewBloomFilterPolicy(bits_per_key); + wrapper->state_ = NULL; + wrapper->destructor_ = &Wrapper::DoNothing; + return wrapper; +} + +leveldb_readoptions_t* leveldb_readoptions_create() { + return new leveldb_readoptions_t; +} + +void leveldb_readoptions_destroy(leveldb_readoptions_t* opt) { + delete opt; +} + +void leveldb_readoptions_set_verify_checksums( + leveldb_readoptions_t* opt, + unsigned char v) { + opt->rep.verify_checksums = v; +} + +void leveldb_readoptions_set_fill_cache( + leveldb_readoptions_t* opt, unsigned char v) { + opt->rep.fill_cache = v; +} + +void leveldb_readoptions_set_snapshot( + leveldb_readoptions_t* opt, + const leveldb_snapshot_t* snap) { + opt->rep.snapshot = (snap ? snap->rep : NULL); +} + +leveldb_writeoptions_t* leveldb_writeoptions_create() { + return new leveldb_writeoptions_t; +} + +void leveldb_writeoptions_destroy(leveldb_writeoptions_t* opt) { + delete opt; +} + +void leveldb_writeoptions_set_sync( + leveldb_writeoptions_t* opt, unsigned char v) { + opt->rep.sync = v; +} + +leveldb_cache_t* leveldb_cache_create_lru(size_t capacity) { + leveldb_cache_t* c = new leveldb_cache_t; + c->rep = NewLRUCache(capacity); + return c; +} + +void leveldb_cache_destroy(leveldb_cache_t* cache) { + delete cache->rep; + delete cache; +} + +leveldb_env_t* leveldb_create_default_env() { + leveldb_env_t* result = new leveldb_env_t; + result->rep = Env::Default(); + result->is_default = true; + return result; +} + +void leveldb_env_destroy(leveldb_env_t* env) { + if (!env->is_default) delete env->rep; + delete env; +} + +void leveldb_free(void* ptr) { + free(ptr); +} + +int leveldb_major_version() { + return kMajorVersion; +} + +int leveldb_minor_version() { + return kMinorVersion; +} + +} // end extern "C" diff --git a/src/leveldb-lin/db/c.o b/src/leveldb-lin/db/c.o new file mode 100644 index 0000000..ebae6fc Binary files /dev/null and b/src/leveldb-lin/db/c.o differ diff --git a/src/leveldb-lin/db/c_test.c b/src/leveldb-lin/db/c_test.c new file mode 100644 index 0000000..7cd5ee0 --- /dev/null +++ b/src/leveldb-lin/db/c_test.c @@ -0,0 +1,390 @@ +/* Copyright (c) 2011 The LevelDB Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. See the AUTHORS file for names of contributors. */ + +#include "leveldb/c.h" + +#include +#include +#include +#include +#include +#include + +const char* phase = ""; +static char dbname[200]; + +static void StartPhase(const char* name) { + fprintf(stderr, "=== Test %s\n", name); + phase = name; +} + +static const char* GetTempDir(void) { + const char* ret = getenv("TEST_TMPDIR"); + if (ret == NULL || ret[0] == '\0') + ret = "/tmp"; + return ret; +} + +#define CheckNoError(err) \ + if ((err) != NULL) { \ + fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, (err)); \ + abort(); \ + } + +#define CheckCondition(cond) \ + if (!(cond)) { \ + fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, #cond); \ + abort(); \ + } + +static void CheckEqual(const char* expected, const char* v, size_t n) { + if (expected == NULL && v == NULL) { + // ok + } else if (expected != NULL && v != NULL && n == strlen(expected) && + memcmp(expected, v, n) == 0) { + // ok + return; + } else { + fprintf(stderr, "%s: expected '%s', got '%s'\n", + phase, + (expected ? expected : "(null)"), + (v ? v : "(null")); + abort(); + } +} + +static void Free(char** ptr) { + if (*ptr) { + free(*ptr); + *ptr = NULL; + } +} + +static void CheckGet( + leveldb_t* db, + const leveldb_readoptions_t* options, + const char* key, + const char* expected) { + char* err = NULL; + size_t val_len; + char* val; + val = leveldb_get(db, options, key, strlen(key), &val_len, &err); + CheckNoError(err); + CheckEqual(expected, val, val_len); + Free(&val); +} + +static void CheckIter(leveldb_iterator_t* iter, + const char* key, const char* val) { + size_t len; + const char* str; + str = leveldb_iter_key(iter, &len); + CheckEqual(key, str, len); + str = leveldb_iter_value(iter, &len); + CheckEqual(val, str, len); +} + +// Callback from leveldb_writebatch_iterate() +static void CheckPut(void* ptr, + const char* k, size_t klen, + const char* v, size_t vlen) { + int* state = (int*) ptr; + CheckCondition(*state < 2); + switch (*state) { + case 0: + CheckEqual("bar", k, klen); + CheckEqual("b", v, vlen); + break; + case 1: + CheckEqual("box", k, klen); + CheckEqual("c", v, vlen); + break; + } + (*state)++; +} + +// Callback from leveldb_writebatch_iterate() +static void CheckDel(void* ptr, const char* k, size_t klen) { + int* state = (int*) ptr; + CheckCondition(*state == 2); + CheckEqual("bar", k, klen); + (*state)++; +} + +static void CmpDestroy(void* arg) { } + +static int CmpCompare(void* arg, const char* a, size_t alen, + const char* b, size_t blen) { + int n = (alen < blen) ? alen : blen; + int r = memcmp(a, b, n); + if (r == 0) { + if (alen < blen) r = -1; + else if (alen > blen) r = +1; + } + return r; +} + +static const char* CmpName(void* arg) { + return "foo"; +} + +// Custom filter policy +static unsigned char fake_filter_result = 1; +static void FilterDestroy(void* arg) { } +static const char* FilterName(void* arg) { + return "TestFilter"; +} +static char* FilterCreate( + void* arg, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length) { + *filter_length = 4; + char* result = malloc(4); + memcpy(result, "fake", 4); + return result; +} +unsigned char FilterKeyMatch( + void* arg, + const char* key, size_t length, + const char* filter, size_t filter_length) { + CheckCondition(filter_length == 4); + CheckCondition(memcmp(filter, "fake", 4) == 0); + return fake_filter_result; +} + +int main(int argc, char** argv) { + leveldb_t* db; + leveldb_comparator_t* cmp; + leveldb_cache_t* cache; + leveldb_env_t* env; + leveldb_options_t* options; + leveldb_readoptions_t* roptions; + leveldb_writeoptions_t* woptions; + char* err = NULL; + int run = -1; + + CheckCondition(leveldb_major_version() >= 1); + CheckCondition(leveldb_minor_version() >= 1); + + snprintf(dbname, sizeof(dbname), + "%s/leveldb_c_test-%d", + GetTempDir(), + ((int) geteuid())); + + StartPhase("create_objects"); + cmp = leveldb_comparator_create(NULL, CmpDestroy, CmpCompare, CmpName); + env = leveldb_create_default_env(); + cache = leveldb_cache_create_lru(100000); + + options = leveldb_options_create(); + leveldb_options_set_comparator(options, cmp); + leveldb_options_set_error_if_exists(options, 1); + leveldb_options_set_cache(options, cache); + leveldb_options_set_env(options, env); + leveldb_options_set_info_log(options, NULL); + leveldb_options_set_write_buffer_size(options, 100000); + leveldb_options_set_paranoid_checks(options, 1); + leveldb_options_set_max_open_files(options, 10); + leveldb_options_set_block_size(options, 1024); + leveldb_options_set_block_restart_interval(options, 8); + leveldb_options_set_compression(options, leveldb_no_compression); + + roptions = leveldb_readoptions_create(); + leveldb_readoptions_set_verify_checksums(roptions, 1); + leveldb_readoptions_set_fill_cache(roptions, 0); + + woptions = leveldb_writeoptions_create(); + leveldb_writeoptions_set_sync(woptions, 1); + + StartPhase("destroy"); + leveldb_destroy_db(options, dbname, &err); + Free(&err); + + StartPhase("open_error"); + db = leveldb_open(options, dbname, &err); + CheckCondition(err != NULL); + Free(&err); + + StartPhase("leveldb_free"); + db = leveldb_open(options, dbname, &err); + CheckCondition(err != NULL); + leveldb_free(err); + err = NULL; + + StartPhase("open"); + leveldb_options_set_create_if_missing(options, 1); + db = leveldb_open(options, dbname, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", NULL); + + StartPhase("put"); + leveldb_put(db, woptions, "foo", 3, "hello", 5, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", "hello"); + + StartPhase("compactall"); + leveldb_compact_range(db, NULL, 0, NULL, 0); + CheckGet(db, roptions, "foo", "hello"); + + StartPhase("compactrange"); + leveldb_compact_range(db, "a", 1, "z", 1); + CheckGet(db, roptions, "foo", "hello"); + + StartPhase("writebatch"); + { + leveldb_writebatch_t* wb = leveldb_writebatch_create(); + leveldb_writebatch_put(wb, "foo", 3, "a", 1); + leveldb_writebatch_clear(wb); + leveldb_writebatch_put(wb, "bar", 3, "b", 1); + leveldb_writebatch_put(wb, "box", 3, "c", 1); + leveldb_writebatch_delete(wb, "bar", 3); + leveldb_write(db, woptions, wb, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", "hello"); + CheckGet(db, roptions, "bar", NULL); + CheckGet(db, roptions, "box", "c"); + int pos = 0; + leveldb_writebatch_iterate(wb, &pos, CheckPut, CheckDel); + CheckCondition(pos == 3); + leveldb_writebatch_destroy(wb); + } + + StartPhase("iter"); + { + leveldb_iterator_t* iter = leveldb_create_iterator(db, roptions); + CheckCondition(!leveldb_iter_valid(iter)); + leveldb_iter_seek_to_first(iter); + CheckCondition(leveldb_iter_valid(iter)); + CheckIter(iter, "box", "c"); + leveldb_iter_next(iter); + CheckIter(iter, "foo", "hello"); + leveldb_iter_prev(iter); + CheckIter(iter, "box", "c"); + leveldb_iter_prev(iter); + CheckCondition(!leveldb_iter_valid(iter)); + leveldb_iter_seek_to_last(iter); + CheckIter(iter, "foo", "hello"); + leveldb_iter_seek(iter, "b", 1); + CheckIter(iter, "box", "c"); + leveldb_iter_get_error(iter, &err); + CheckNoError(err); + leveldb_iter_destroy(iter); + } + + StartPhase("approximate_sizes"); + { + int i; + int n = 20000; + char keybuf[100]; + char valbuf[100]; + uint64_t sizes[2]; + const char* start[2] = { "a", "k00000000000000010000" }; + size_t start_len[2] = { 1, 21 }; + const char* limit[2] = { "k00000000000000010000", "z" }; + size_t limit_len[2] = { 21, 1 }; + leveldb_writeoptions_set_sync(woptions, 0); + for (i = 0; i < n; i++) { + snprintf(keybuf, sizeof(keybuf), "k%020d", i); + snprintf(valbuf, sizeof(valbuf), "v%020d", i); + leveldb_put(db, woptions, keybuf, strlen(keybuf), valbuf, strlen(valbuf), + &err); + CheckNoError(err); + } + leveldb_approximate_sizes(db, 2, start, start_len, limit, limit_len, sizes); + CheckCondition(sizes[0] > 0); + CheckCondition(sizes[1] > 0); + } + + StartPhase("property"); + { + char* prop = leveldb_property_value(db, "nosuchprop"); + CheckCondition(prop == NULL); + prop = leveldb_property_value(db, "leveldb.stats"); + CheckCondition(prop != NULL); + Free(&prop); + } + + StartPhase("snapshot"); + { + const leveldb_snapshot_t* snap; + snap = leveldb_create_snapshot(db); + leveldb_delete(db, woptions, "foo", 3, &err); + CheckNoError(err); + leveldb_readoptions_set_snapshot(roptions, snap); + CheckGet(db, roptions, "foo", "hello"); + leveldb_readoptions_set_snapshot(roptions, NULL); + CheckGet(db, roptions, "foo", NULL); + leveldb_release_snapshot(db, snap); + } + + StartPhase("repair"); + { + leveldb_close(db); + leveldb_options_set_create_if_missing(options, 0); + leveldb_options_set_error_if_exists(options, 0); + leveldb_repair_db(options, dbname, &err); + CheckNoError(err); + db = leveldb_open(options, dbname, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", NULL); + CheckGet(db, roptions, "bar", NULL); + CheckGet(db, roptions, "box", "c"); + leveldb_options_set_create_if_missing(options, 1); + leveldb_options_set_error_if_exists(options, 1); + } + + StartPhase("filter"); + for (run = 0; run < 2; run++) { + // First run uses custom filter, second run uses bloom filter + CheckNoError(err); + leveldb_filterpolicy_t* policy; + if (run == 0) { + policy = leveldb_filterpolicy_create( + NULL, FilterDestroy, FilterCreate, FilterKeyMatch, FilterName); + } else { + policy = leveldb_filterpolicy_create_bloom(10); + } + + // Create new database + leveldb_close(db); + leveldb_destroy_db(options, dbname, &err); + leveldb_options_set_filter_policy(options, policy); + db = leveldb_open(options, dbname, &err); + CheckNoError(err); + leveldb_put(db, woptions, "foo", 3, "foovalue", 8, &err); + CheckNoError(err); + leveldb_put(db, woptions, "bar", 3, "barvalue", 8, &err); + CheckNoError(err); + leveldb_compact_range(db, NULL, 0, NULL, 0); + + fake_filter_result = 1; + CheckGet(db, roptions, "foo", "foovalue"); + CheckGet(db, roptions, "bar", "barvalue"); + if (phase == 0) { + // Must not find value when custom filter returns false + fake_filter_result = 0; + CheckGet(db, roptions, "foo", NULL); + CheckGet(db, roptions, "bar", NULL); + fake_filter_result = 1; + + CheckGet(db, roptions, "foo", "foovalue"); + CheckGet(db, roptions, "bar", "barvalue"); + } + leveldb_options_set_filter_policy(options, NULL); + leveldb_filterpolicy_destroy(policy); + } + + StartPhase("cleanup"); + leveldb_close(db); + leveldb_options_destroy(options); + leveldb_readoptions_destroy(roptions); + leveldb_writeoptions_destroy(woptions); + leveldb_cache_destroy(cache); + leveldb_comparator_destroy(cmp); + leveldb_env_destroy(env); + + fprintf(stderr, "PASS\n"); + return 0; +} diff --git a/src/leveldb-lin/db/corruption_test.cc b/src/leveldb-lin/db/corruption_test.cc new file mode 100644 index 0000000..31b2d5f --- /dev/null +++ b/src/leveldb-lin/db/corruption_test.cc @@ -0,0 +1,359 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" + +#include +#include +#include +#include +#include "leveldb/cache.h" +#include "leveldb/env.h" +#include "leveldb/table.h" +#include "leveldb/write_batch.h" +#include "db/db_impl.h" +#include "db/filename.h" +#include "db/log_format.h" +#include "db/version_set.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +static const int kValueSize = 1000; + +class CorruptionTest { + public: + test::ErrorEnv env_; + std::string dbname_; + Cache* tiny_cache_; + Options options_; + DB* db_; + + CorruptionTest() { + tiny_cache_ = NewLRUCache(100); + options_.env = &env_; + dbname_ = test::TmpDir() + "/db_test"; + DestroyDB(dbname_, options_); + + db_ = NULL; + options_.create_if_missing = true; + Reopen(); + options_.create_if_missing = false; + } + + ~CorruptionTest() { + delete db_; + DestroyDB(dbname_, Options()); + delete tiny_cache_; + } + + Status TryReopen(Options* options = NULL) { + delete db_; + db_ = NULL; + Options opt = (options ? *options : options_); + opt.env = &env_; + opt.block_cache = tiny_cache_; + return DB::Open(opt, dbname_, &db_); + } + + void Reopen(Options* options = NULL) { + ASSERT_OK(TryReopen(options)); + } + + void RepairDB() { + delete db_; + db_ = NULL; + ASSERT_OK(::leveldb::RepairDB(dbname_, options_)); + } + + void Build(int n) { + std::string key_space, value_space; + WriteBatch batch; + for (int i = 0; i < n; i++) { + //if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n); + Slice key = Key(i, &key_space); + batch.Clear(); + batch.Put(key, Value(i, &value_space)); + ASSERT_OK(db_->Write(WriteOptions(), &batch)); + } + } + + void Check(int min_expected, int max_expected) { + int next_expected = 0; + int missed = 0; + int bad_keys = 0; + int bad_values = 0; + int correct = 0; + std::string value_space; + Iterator* iter = db_->NewIterator(ReadOptions()); + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + uint64_t key; + Slice in(iter->key()); + if (!ConsumeDecimalNumber(&in, &key) || + !in.empty() || + key < next_expected) { + bad_keys++; + continue; + } + missed += (key - next_expected); + next_expected = key + 1; + if (iter->value() != Value(key, &value_space)) { + bad_values++; + } else { + correct++; + } + } + delete iter; + + fprintf(stderr, + "expected=%d..%d; got=%d; bad_keys=%d; bad_values=%d; missed=%d\n", + min_expected, max_expected, correct, bad_keys, bad_values, missed); + ASSERT_LE(min_expected, correct); + ASSERT_GE(max_expected, correct); + } + + void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) { + // Pick file to corrupt + std::vector filenames; + ASSERT_OK(env_.GetChildren(dbname_, &filenames)); + uint64_t number; + FileType type; + std::string fname; + int picked_number = -1; + for (int i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) && + type == filetype && + int(number) > picked_number) { // Pick latest file + fname = dbname_ + "/" + filenames[i]; + picked_number = number; + } + } + ASSERT_TRUE(!fname.empty()) << filetype; + + struct stat sbuf; + if (stat(fname.c_str(), &sbuf) != 0) { + const char* msg = strerror(errno); + ASSERT_TRUE(false) << fname << ": " << msg; + } + + if (offset < 0) { + // Relative to end of file; make it absolute + if (-offset > sbuf.st_size) { + offset = 0; + } else { + offset = sbuf.st_size + offset; + } + } + if (offset > sbuf.st_size) { + offset = sbuf.st_size; + } + if (offset + bytes_to_corrupt > sbuf.st_size) { + bytes_to_corrupt = sbuf.st_size - offset; + } + + // Do it + std::string contents; + Status s = ReadFileToString(Env::Default(), fname, &contents); + ASSERT_TRUE(s.ok()) << s.ToString(); + for (int i = 0; i < bytes_to_corrupt; i++) { + contents[i + offset] ^= 0x80; + } + s = WriteStringToFile(Env::Default(), contents, fname); + ASSERT_TRUE(s.ok()) << s.ToString(); + } + + int Property(const std::string& name) { + std::string property; + int result; + if (db_->GetProperty(name, &property) && + sscanf(property.c_str(), "%d", &result) == 1) { + return result; + } else { + return -1; + } + } + + // Return the ith key + Slice Key(int i, std::string* storage) { + char buf[100]; + snprintf(buf, sizeof(buf), "%016d", i); + storage->assign(buf, strlen(buf)); + return Slice(*storage); + } + + // Return the value to associate with the specified key + Slice Value(int k, std::string* storage) { + Random r(k); + return test::RandomString(&r, kValueSize, storage); + } +}; + +TEST(CorruptionTest, Recovery) { + Build(100); + Check(100, 100); + Corrupt(kLogFile, 19, 1); // WriteBatch tag for first record + Corrupt(kLogFile, log::kBlockSize + 1000, 1); // Somewhere in second block + Reopen(); + + // The 64 records in the first two log blocks are completely lost. + Check(36, 36); +} + +TEST(CorruptionTest, RecoverWriteError) { + env_.writable_file_error_ = true; + Status s = TryReopen(); + ASSERT_TRUE(!s.ok()); +} + +TEST(CorruptionTest, NewFileErrorDuringWrite) { + // Do enough writing to force minor compaction + env_.writable_file_error_ = true; + const int num = 3 + (Options().write_buffer_size / kValueSize); + std::string value_storage; + Status s; + for (int i = 0; s.ok() && i < num; i++) { + WriteBatch batch; + batch.Put("a", Value(100, &value_storage)); + s = db_->Write(WriteOptions(), &batch); + } + ASSERT_TRUE(!s.ok()); + ASSERT_GE(env_.num_writable_file_errors_, 1); + env_.writable_file_error_ = false; + Reopen(); +} + +TEST(CorruptionTest, TableFile) { + Build(100); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + dbi->TEST_CompactRange(0, NULL, NULL); + dbi->TEST_CompactRange(1, NULL, NULL); + + Corrupt(kTableFile, 100, 1); + Check(99, 99); +} + +TEST(CorruptionTest, TableFileIndexData) { + Build(10000); // Enough to build multiple Tables + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + + Corrupt(kTableFile, -2000, 500); + Reopen(); + Check(5000, 9999); +} + +TEST(CorruptionTest, MissingDescriptor) { + Build(1000); + RepairDB(); + Reopen(); + Check(1000, 1000); +} + +TEST(CorruptionTest, SequenceNumberRecovery) { + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v3")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v4")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v5")); + RepairDB(); + Reopen(); + std::string v; + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("v5", v); + // Write something. If sequence number was not recovered properly, + // it will be hidden by an earlier write. + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v6")); + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("v6", v); + Reopen(); + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("v6", v); +} + +TEST(CorruptionTest, CorruptedDescriptor) { + ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello")); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + dbi->TEST_CompactRange(0, NULL, NULL); + + Corrupt(kDescriptorFile, 0, 1000); + Status s = TryReopen(); + ASSERT_TRUE(!s.ok()); + + RepairDB(); + Reopen(); + std::string v; + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("hello", v); +} + +TEST(CorruptionTest, CompactionInputError) { + Build(10); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(1, Property("leveldb.num-files-at-level" + NumberToString(last))); + + Corrupt(kTableFile, 100, 1); + Check(9, 9); + + // Force compactions by writing lots of values + Build(10000); + Check(10000, 10000); +} + +TEST(CorruptionTest, CompactionInputErrorParanoid) { + Options options; + options.paranoid_checks = true; + options.write_buffer_size = 1048576; + Reopen(&options); + DBImpl* dbi = reinterpret_cast(db_); + + // Fill levels >= 1 so memtable compaction outputs to level 1 + for (int level = 1; level < config::kNumLevels; level++) { + dbi->Put(WriteOptions(), "", "begin"); + dbi->Put(WriteOptions(), "~", "end"); + dbi->TEST_CompactMemTable(); + } + + Build(10); + dbi->TEST_CompactMemTable(); + ASSERT_EQ(1, Property("leveldb.num-files-at-level0")); + + Corrupt(kTableFile, 100, 1); + Check(9, 9); + + // Write must eventually fail because of corrupted table + Status s; + std::string tmp1, tmp2; + for (int i = 0; i < 10000 && s.ok(); i++) { + s = db_->Put(WriteOptions(), Key(i, &tmp1), Value(i, &tmp2)); + } + ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db"; +} + +TEST(CorruptionTest, UnrelatedKeys) { + Build(10); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + Corrupt(kTableFile, 100, 1); + + std::string tmp1, tmp2; + ASSERT_OK(db_->Put(WriteOptions(), Key(1000, &tmp1), Value(1000, &tmp2))); + std::string v; + ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v)); + ASSERT_EQ(Value(1000, &tmp2).ToString(), v); + dbi->TEST_CompactMemTable(); + ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v)); + ASSERT_EQ(Value(1000, &tmp2).ToString(), v); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/db/db_bench.cc b/src/leveldb-lin/db/db_bench.cc new file mode 100644 index 0000000..7abdf87 --- /dev/null +++ b/src/leveldb-lin/db/db_bench.cc @@ -0,0 +1,979 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include +#include "db/db_impl.h" +#include "db/version_set.h" +#include "leveldb/cache.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/write_batch.h" +#include "port/port.h" +#include "util/crc32c.h" +#include "util/histogram.h" +#include "util/mutexlock.h" +#include "util/random.h" +#include "util/testutil.h" + +// Comma-separated list of operations to run in the specified order +// Actual benchmarks: +// fillseq -- write N values in sequential key order in async mode +// fillrandom -- write N values in random key order in async mode +// overwrite -- overwrite N values in random key order in async mode +// fillsync -- write N/100 values in random key order in sync mode +// fill100K -- write N/1000 100K values in random order in async mode +// deleteseq -- delete N keys in sequential order +// deleterandom -- delete N keys in random order +// readseq -- read N times sequentially +// readreverse -- read N times in reverse order +// readrandom -- read N times in random order +// readmissing -- read N missing keys in random order +// readhot -- read N times in random order from 1% section of DB +// seekrandom -- N random seeks +// crc32c -- repeated crc32c of 4K of data +// acquireload -- load N*1000 times +// Meta operations: +// compact -- Compact the entire DB +// stats -- Print DB stats +// sstables -- Print sstable info +// heapprofile -- Dump a heap profile (if supported by this port) +static const char* FLAGS_benchmarks = + "fillseq," + "fillsync," + "fillrandom," + "overwrite," + "readrandom," + "readrandom," // Extra run to allow previous compactions to quiesce + "readseq," + "readreverse," + "compact," + "readrandom," + "readseq," + "readreverse," + "fill100K," + "crc32c," + "snappycomp," + "snappyuncomp," + "acquireload," + ; + +// Number of key/values to place in database +static int FLAGS_num = 1000000; + +// Number of read operations to do. If negative, do FLAGS_num reads. +static int FLAGS_reads = -1; + +// Number of concurrent threads to run. +static int FLAGS_threads = 1; + +// Size of each value +static int FLAGS_value_size = 100; + +// Arrange to generate values that shrink to this fraction of +// their original size after compression +static double FLAGS_compression_ratio = 0.5; + +// Print histogram of operation timings +static bool FLAGS_histogram = false; + +// Number of bytes to buffer in memtable before compacting +// (initialized to default value by "main") +static int FLAGS_write_buffer_size = 0; + +// Number of bytes to use as a cache of uncompressed data. +// Negative means use default settings. +static int FLAGS_cache_size = -1; + +// Maximum number of files to keep open at the same time (use default if == 0) +static int FLAGS_open_files = 0; + +// Bloom filter bits per key. +// Negative means use default settings. +static int FLAGS_bloom_bits = -1; + +// If true, do not destroy the existing database. If you set this +// flag and also specify a benchmark that wants a fresh database, that +// benchmark will fail. +static bool FLAGS_use_existing_db = false; + +// Use the db with the following name. +static const char* FLAGS_db = NULL; + +namespace leveldb { + +namespace { + +// Helper for quickly generating random data. +class RandomGenerator { + private: + std::string data_; + int pos_; + + public: + RandomGenerator() { + // We use a limited amount of data over and over again and ensure + // that it is larger than the compression window (32KB), and also + // large enough to serve all typical value sizes we want to write. + Random rnd(301); + std::string piece; + while (data_.size() < 1048576) { + // Add a short fragment that is as compressible as specified + // by FLAGS_compression_ratio. + test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece); + data_.append(piece); + } + pos_ = 0; + } + + Slice Generate(int len) { + if (pos_ + len > data_.size()) { + pos_ = 0; + assert(len < data_.size()); + } + pos_ += len; + return Slice(data_.data() + pos_ - len, len); + } +}; + +static Slice TrimSpace(Slice s) { + int start = 0; + while (start < s.size() && isspace(s[start])) { + start++; + } + int limit = s.size(); + while (limit > start && isspace(s[limit-1])) { + limit--; + } + return Slice(s.data() + start, limit - start); +} + +static void AppendWithSpace(std::string* str, Slice msg) { + if (msg.empty()) return; + if (!str->empty()) { + str->push_back(' '); + } + str->append(msg.data(), msg.size()); +} + +class Stats { + private: + double start_; + double finish_; + double seconds_; + int done_; + int next_report_; + int64_t bytes_; + double last_op_finish_; + Histogram hist_; + std::string message_; + + public: + Stats() { Start(); } + + void Start() { + next_report_ = 100; + last_op_finish_ = start_; + hist_.Clear(); + done_ = 0; + bytes_ = 0; + seconds_ = 0; + start_ = Env::Default()->NowMicros(); + finish_ = start_; + message_.clear(); + } + + void Merge(const Stats& other) { + hist_.Merge(other.hist_); + done_ += other.done_; + bytes_ += other.bytes_; + seconds_ += other.seconds_; + if (other.start_ < start_) start_ = other.start_; + if (other.finish_ > finish_) finish_ = other.finish_; + + // Just keep the messages from one thread + if (message_.empty()) message_ = other.message_; + } + + void Stop() { + finish_ = Env::Default()->NowMicros(); + seconds_ = (finish_ - start_) * 1e-6; + } + + void AddMessage(Slice msg) { + AppendWithSpace(&message_, msg); + } + + void FinishedSingleOp() { + if (FLAGS_histogram) { + double now = Env::Default()->NowMicros(); + double micros = now - last_op_finish_; + hist_.Add(micros); + if (micros > 20000) { + fprintf(stderr, "long op: %.1f micros%30s\r", micros, ""); + fflush(stderr); + } + last_op_finish_ = now; + } + + done_++; + if (done_ >= next_report_) { + if (next_report_ < 1000) next_report_ += 100; + else if (next_report_ < 5000) next_report_ += 500; + else if (next_report_ < 10000) next_report_ += 1000; + else if (next_report_ < 50000) next_report_ += 5000; + else if (next_report_ < 100000) next_report_ += 10000; + else if (next_report_ < 500000) next_report_ += 50000; + else next_report_ += 100000; + fprintf(stderr, "... finished %d ops%30s\r", done_, ""); + fflush(stderr); + } + } + + void AddBytes(int64_t n) { + bytes_ += n; + } + + void Report(const Slice& name) { + // Pretend at least one op was done in case we are running a benchmark + // that does not call FinishedSingleOp(). + if (done_ < 1) done_ = 1; + + std::string extra; + if (bytes_ > 0) { + // Rate is computed on actual elapsed time, not the sum of per-thread + // elapsed times. + double elapsed = (finish_ - start_) * 1e-6; + char rate[100]; + snprintf(rate, sizeof(rate), "%6.1f MB/s", + (bytes_ / 1048576.0) / elapsed); + extra = rate; + } + AppendWithSpace(&extra, message_); + + fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", + name.ToString().c_str(), + seconds_ * 1e6 / done_, + (extra.empty() ? "" : " "), + extra.c_str()); + if (FLAGS_histogram) { + fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str()); + } + fflush(stdout); + } +}; + +// State shared by all concurrent executions of the same benchmark. +struct SharedState { + port::Mutex mu; + port::CondVar cv; + int total; + + // Each thread goes through the following states: + // (1) initializing + // (2) waiting for others to be initialized + // (3) running + // (4) done + + int num_initialized; + int num_done; + bool start; + + SharedState() : cv(&mu) { } +}; + +// Per-thread state for concurrent executions of the same benchmark. +struct ThreadState { + int tid; // 0..n-1 when running in n threads + Random rand; // Has different seeds for different threads + Stats stats; + SharedState* shared; + + ThreadState(int index) + : tid(index), + rand(1000 + index) { + } +}; + +} // namespace + +class Benchmark { + private: + Cache* cache_; + const FilterPolicy* filter_policy_; + DB* db_; + int num_; + int value_size_; + int entries_per_batch_; + WriteOptions write_options_; + int reads_; + int heap_counter_; + + void PrintHeader() { + const int kKeySize = 16; + PrintEnvironment(); + fprintf(stdout, "Keys: %d bytes each\n", kKeySize); + fprintf(stdout, "Values: %d bytes each (%d bytes after compression)\n", + FLAGS_value_size, + static_cast(FLAGS_value_size * FLAGS_compression_ratio + 0.5)); + fprintf(stdout, "Entries: %d\n", num_); + fprintf(stdout, "RawSize: %.1f MB (estimated)\n", + ((static_cast(kKeySize + FLAGS_value_size) * num_) + / 1048576.0)); + fprintf(stdout, "FileSize: %.1f MB (estimated)\n", + (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) + / 1048576.0)); + PrintWarnings(); + fprintf(stdout, "------------------------------------------------\n"); + } + + void PrintWarnings() { +#if defined(__GNUC__) && !defined(__OPTIMIZE__) + fprintf(stdout, + "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n" + ); +#endif +#ifndef NDEBUG + fprintf(stdout, + "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n"); +#endif + + // See if snappy is working by attempting to compress a compressible string + const char text[] = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"; + std::string compressed; + if (!port::Snappy_Compress(text, sizeof(text), &compressed)) { + fprintf(stdout, "WARNING: Snappy compression is not enabled\n"); + } else if (compressed.size() >= sizeof(text)) { + fprintf(stdout, "WARNING: Snappy compression is not effective\n"); + } + } + + void PrintEnvironment() { + fprintf(stderr, "LevelDB: version %d.%d\n", + kMajorVersion, kMinorVersion); + +#if defined(__linux) + time_t now = time(NULL); + fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline + + FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); + if (cpuinfo != NULL) { + char line[1000]; + int num_cpus = 0; + std::string cpu_type; + std::string cache_size; + while (fgets(line, sizeof(line), cpuinfo) != NULL) { + const char* sep = strchr(line, ':'); + if (sep == NULL) { + continue; + } + Slice key = TrimSpace(Slice(line, sep - 1 - line)); + Slice val = TrimSpace(Slice(sep + 1)); + if (key == "model name") { + ++num_cpus; + cpu_type = val.ToString(); + } else if (key == "cache size") { + cache_size = val.ToString(); + } + } + fclose(cpuinfo); + fprintf(stderr, "CPU: %d * %s\n", num_cpus, cpu_type.c_str()); + fprintf(stderr, "CPUCache: %s\n", cache_size.c_str()); + } +#endif + } + + public: + Benchmark() + : cache_(FLAGS_cache_size >= 0 ? NewLRUCache(FLAGS_cache_size) : NULL), + filter_policy_(FLAGS_bloom_bits >= 0 + ? NewBloomFilterPolicy(FLAGS_bloom_bits) + : NULL), + db_(NULL), + num_(FLAGS_num), + value_size_(FLAGS_value_size), + entries_per_batch_(1), + reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), + heap_counter_(0) { + std::vector files; + Env::Default()->GetChildren(FLAGS_db, &files); + for (int i = 0; i < files.size(); i++) { + if (Slice(files[i]).starts_with("heap-")) { + Env::Default()->DeleteFile(std::string(FLAGS_db) + "/" + files[i]); + } + } + if (!FLAGS_use_existing_db) { + DestroyDB(FLAGS_db, Options()); + } + } + + ~Benchmark() { + delete db_; + delete cache_; + delete filter_policy_; + } + + void Run() { + PrintHeader(); + Open(); + + const char* benchmarks = FLAGS_benchmarks; + while (benchmarks != NULL) { + const char* sep = strchr(benchmarks, ','); + Slice name; + if (sep == NULL) { + name = benchmarks; + benchmarks = NULL; + } else { + name = Slice(benchmarks, sep - benchmarks); + benchmarks = sep + 1; + } + + // Reset parameters that may be overriddden bwlow + num_ = FLAGS_num; + reads_ = (FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads); + value_size_ = FLAGS_value_size; + entries_per_batch_ = 1; + write_options_ = WriteOptions(); + + void (Benchmark::*method)(ThreadState*) = NULL; + bool fresh_db = false; + int num_threads = FLAGS_threads; + + if (name == Slice("fillseq")) { + fresh_db = true; + method = &Benchmark::WriteSeq; + } else if (name == Slice("fillbatch")) { + fresh_db = true; + entries_per_batch_ = 1000; + method = &Benchmark::WriteSeq; + } else if (name == Slice("fillrandom")) { + fresh_db = true; + method = &Benchmark::WriteRandom; + } else if (name == Slice("overwrite")) { + fresh_db = false; + method = &Benchmark::WriteRandom; + } else if (name == Slice("fillsync")) { + fresh_db = true; + num_ /= 1000; + write_options_.sync = true; + method = &Benchmark::WriteRandom; + } else if (name == Slice("fill100K")) { + fresh_db = true; + num_ /= 1000; + value_size_ = 100 * 1000; + method = &Benchmark::WriteRandom; + } else if (name == Slice("readseq")) { + method = &Benchmark::ReadSequential; + } else if (name == Slice("readreverse")) { + method = &Benchmark::ReadReverse; + } else if (name == Slice("readrandom")) { + method = &Benchmark::ReadRandom; + } else if (name == Slice("readmissing")) { + method = &Benchmark::ReadMissing; + } else if (name == Slice("seekrandom")) { + method = &Benchmark::SeekRandom; + } else if (name == Slice("readhot")) { + method = &Benchmark::ReadHot; + } else if (name == Slice("readrandomsmall")) { + reads_ /= 1000; + method = &Benchmark::ReadRandom; + } else if (name == Slice("deleteseq")) { + method = &Benchmark::DeleteSeq; + } else if (name == Slice("deleterandom")) { + method = &Benchmark::DeleteRandom; + } else if (name == Slice("readwhilewriting")) { + num_threads++; // Add extra thread for writing + method = &Benchmark::ReadWhileWriting; + } else if (name == Slice("compact")) { + method = &Benchmark::Compact; + } else if (name == Slice("crc32c")) { + method = &Benchmark::Crc32c; + } else if (name == Slice("acquireload")) { + method = &Benchmark::AcquireLoad; + } else if (name == Slice("snappycomp")) { + method = &Benchmark::SnappyCompress; + } else if (name == Slice("snappyuncomp")) { + method = &Benchmark::SnappyUncompress; + } else if (name == Slice("heapprofile")) { + HeapProfile(); + } else if (name == Slice("stats")) { + PrintStats("leveldb.stats"); + } else if (name == Slice("sstables")) { + PrintStats("leveldb.sstables"); + } else { + if (name != Slice()) { // No error message for empty name + fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str()); + } + } + + if (fresh_db) { + if (FLAGS_use_existing_db) { + fprintf(stdout, "%-12s : skipped (--use_existing_db is true)\n", + name.ToString().c_str()); + method = NULL; + } else { + delete db_; + db_ = NULL; + DestroyDB(FLAGS_db, Options()); + Open(); + } + } + + if (method != NULL) { + RunBenchmark(num_threads, name, method); + } + } + } + + private: + struct ThreadArg { + Benchmark* bm; + SharedState* shared; + ThreadState* thread; + void (Benchmark::*method)(ThreadState*); + }; + + static void ThreadBody(void* v) { + ThreadArg* arg = reinterpret_cast(v); + SharedState* shared = arg->shared; + ThreadState* thread = arg->thread; + { + MutexLock l(&shared->mu); + shared->num_initialized++; + if (shared->num_initialized >= shared->total) { + shared->cv.SignalAll(); + } + while (!shared->start) { + shared->cv.Wait(); + } + } + + thread->stats.Start(); + (arg->bm->*(arg->method))(thread); + thread->stats.Stop(); + + { + MutexLock l(&shared->mu); + shared->num_done++; + if (shared->num_done >= shared->total) { + shared->cv.SignalAll(); + } + } + } + + void RunBenchmark(int n, Slice name, + void (Benchmark::*method)(ThreadState*)) { + SharedState shared; + shared.total = n; + shared.num_initialized = 0; + shared.num_done = 0; + shared.start = false; + + ThreadArg* arg = new ThreadArg[n]; + for (int i = 0; i < n; i++) { + arg[i].bm = this; + arg[i].method = method; + arg[i].shared = &shared; + arg[i].thread = new ThreadState(i); + arg[i].thread->shared = &shared; + Env::Default()->StartThread(ThreadBody, &arg[i]); + } + + shared.mu.Lock(); + while (shared.num_initialized < n) { + shared.cv.Wait(); + } + + shared.start = true; + shared.cv.SignalAll(); + while (shared.num_done < n) { + shared.cv.Wait(); + } + shared.mu.Unlock(); + + for (int i = 1; i < n; i++) { + arg[0].thread->stats.Merge(arg[i].thread->stats); + } + arg[0].thread->stats.Report(name); + + for (int i = 0; i < n; i++) { + delete arg[i].thread; + } + delete[] arg; + } + + void Crc32c(ThreadState* thread) { + // Checksum about 500MB of data total + const int size = 4096; + const char* label = "(4K per op)"; + std::string data(size, 'x'); + int64_t bytes = 0; + uint32_t crc = 0; + while (bytes < 500 * 1048576) { + crc = crc32c::Value(data.data(), size); + thread->stats.FinishedSingleOp(); + bytes += size; + } + // Print so result is not dead + fprintf(stderr, "... crc=0x%x\r", static_cast(crc)); + + thread->stats.AddBytes(bytes); + thread->stats.AddMessage(label); + } + + void AcquireLoad(ThreadState* thread) { + int dummy; + port::AtomicPointer ap(&dummy); + int count = 0; + void *ptr = NULL; + thread->stats.AddMessage("(each op is 1000 loads)"); + while (count < 100000) { + for (int i = 0; i < 1000; i++) { + ptr = ap.Acquire_Load(); + } + count++; + thread->stats.FinishedSingleOp(); + } + if (ptr == NULL) exit(1); // Disable unused variable warning. + } + + void SnappyCompress(ThreadState* thread) { + RandomGenerator gen; + Slice input = gen.Generate(Options().block_size); + int64_t bytes = 0; + int64_t produced = 0; + bool ok = true; + std::string compressed; + while (ok && bytes < 1024 * 1048576) { // Compress 1G + ok = port::Snappy_Compress(input.data(), input.size(), &compressed); + produced += compressed.size(); + bytes += input.size(); + thread->stats.FinishedSingleOp(); + } + + if (!ok) { + thread->stats.AddMessage("(snappy failure)"); + } else { + char buf[100]; + snprintf(buf, sizeof(buf), "(output: %.1f%%)", + (produced * 100.0) / bytes); + thread->stats.AddMessage(buf); + thread->stats.AddBytes(bytes); + } + } + + void SnappyUncompress(ThreadState* thread) { + RandomGenerator gen; + Slice input = gen.Generate(Options().block_size); + std::string compressed; + bool ok = port::Snappy_Compress(input.data(), input.size(), &compressed); + int64_t bytes = 0; + char* uncompressed = new char[input.size()]; + while (ok && bytes < 1024 * 1048576) { // Compress 1G + ok = port::Snappy_Uncompress(compressed.data(), compressed.size(), + uncompressed); + bytes += input.size(); + thread->stats.FinishedSingleOp(); + } + delete[] uncompressed; + + if (!ok) { + thread->stats.AddMessage("(snappy failure)"); + } else { + thread->stats.AddBytes(bytes); + } + } + + void Open() { + assert(db_ == NULL); + Options options; + options.create_if_missing = !FLAGS_use_existing_db; + options.block_cache = cache_; + options.write_buffer_size = FLAGS_write_buffer_size; + options.max_open_files = FLAGS_open_files; + options.filter_policy = filter_policy_; + Status s = DB::Open(options, FLAGS_db, &db_); + if (!s.ok()) { + fprintf(stderr, "open error: %s\n", s.ToString().c_str()); + exit(1); + } + } + + void WriteSeq(ThreadState* thread) { + DoWrite(thread, true); + } + + void WriteRandom(ThreadState* thread) { + DoWrite(thread, false); + } + + void DoWrite(ThreadState* thread, bool seq) { + if (num_ != FLAGS_num) { + char msg[100]; + snprintf(msg, sizeof(msg), "(%d ops)", num_); + thread->stats.AddMessage(msg); + } + + RandomGenerator gen; + WriteBatch batch; + Status s; + int64_t bytes = 0; + for (int i = 0; i < num_; i += entries_per_batch_) { + batch.Clear(); + for (int j = 0; j < entries_per_batch_; j++) { + const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num); + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + batch.Put(key, gen.Generate(value_size_)); + bytes += value_size_ + strlen(key); + thread->stats.FinishedSingleOp(); + } + s = db_->Write(write_options_, &batch); + if (!s.ok()) { + fprintf(stderr, "put error: %s\n", s.ToString().c_str()); + exit(1); + } + } + thread->stats.AddBytes(bytes); + } + + void ReadSequential(ThreadState* thread) { + Iterator* iter = db_->NewIterator(ReadOptions()); + int i = 0; + int64_t bytes = 0; + for (iter->SeekToFirst(); i < reads_ && iter->Valid(); iter->Next()) { + bytes += iter->key().size() + iter->value().size(); + thread->stats.FinishedSingleOp(); + ++i; + } + delete iter; + thread->stats.AddBytes(bytes); + } + + void ReadReverse(ThreadState* thread) { + Iterator* iter = db_->NewIterator(ReadOptions()); + int i = 0; + int64_t bytes = 0; + for (iter->SeekToLast(); i < reads_ && iter->Valid(); iter->Prev()) { + bytes += iter->key().size() + iter->value().size(); + thread->stats.FinishedSingleOp(); + ++i; + } + delete iter; + thread->stats.AddBytes(bytes); + } + + void ReadRandom(ThreadState* thread) { + ReadOptions options; + std::string value; + int found = 0; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = thread->rand.Next() % FLAGS_num; + snprintf(key, sizeof(key), "%016d", k); + if (db_->Get(options, key, &value).ok()) { + found++; + } + thread->stats.FinishedSingleOp(); + } + char msg[100]; + snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_); + thread->stats.AddMessage(msg); + } + + void ReadMissing(ThreadState* thread) { + ReadOptions options; + std::string value; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = thread->rand.Next() % FLAGS_num; + snprintf(key, sizeof(key), "%016d.", k); + db_->Get(options, key, &value); + thread->stats.FinishedSingleOp(); + } + } + + void ReadHot(ThreadState* thread) { + ReadOptions options; + std::string value; + const int range = (FLAGS_num + 99) / 100; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = thread->rand.Next() % range; + snprintf(key, sizeof(key), "%016d", k); + db_->Get(options, key, &value); + thread->stats.FinishedSingleOp(); + } + } + + void SeekRandom(ThreadState* thread) { + ReadOptions options; + std::string value; + int found = 0; + for (int i = 0; i < reads_; i++) { + Iterator* iter = db_->NewIterator(options); + char key[100]; + const int k = thread->rand.Next() % FLAGS_num; + snprintf(key, sizeof(key), "%016d", k); + iter->Seek(key); + if (iter->Valid() && iter->key() == key) found++; + delete iter; + thread->stats.FinishedSingleOp(); + } + char msg[100]; + snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_); + thread->stats.AddMessage(msg); + } + + void DoDelete(ThreadState* thread, bool seq) { + RandomGenerator gen; + WriteBatch batch; + Status s; + for (int i = 0; i < num_; i += entries_per_batch_) { + batch.Clear(); + for (int j = 0; j < entries_per_batch_; j++) { + const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num); + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + batch.Delete(key); + thread->stats.FinishedSingleOp(); + } + s = db_->Write(write_options_, &batch); + if (!s.ok()) { + fprintf(stderr, "del error: %s\n", s.ToString().c_str()); + exit(1); + } + } + } + + void DeleteSeq(ThreadState* thread) { + DoDelete(thread, true); + } + + void DeleteRandom(ThreadState* thread) { + DoDelete(thread, false); + } + + void ReadWhileWriting(ThreadState* thread) { + if (thread->tid > 0) { + ReadRandom(thread); + } else { + // Special thread that keeps writing until other threads are done. + RandomGenerator gen; + while (true) { + { + MutexLock l(&thread->shared->mu); + if (thread->shared->num_done + 1 >= thread->shared->num_initialized) { + // Other threads have finished + break; + } + } + + const int k = thread->rand.Next() % FLAGS_num; + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + Status s = db_->Put(write_options_, key, gen.Generate(value_size_)); + if (!s.ok()) { + fprintf(stderr, "put error: %s\n", s.ToString().c_str()); + exit(1); + } + } + + // Do not count any of the preceding work/delay in stats. + thread->stats.Start(); + } + } + + void Compact(ThreadState* thread) { + db_->CompactRange(NULL, NULL); + } + + void PrintStats(const char* key) { + std::string stats; + if (!db_->GetProperty(key, &stats)) { + stats = "(failed)"; + } + fprintf(stdout, "\n%s\n", stats.c_str()); + } + + static void WriteToFile(void* arg, const char* buf, int n) { + reinterpret_cast(arg)->Append(Slice(buf, n)); + } + + void HeapProfile() { + char fname[100]; + snprintf(fname, sizeof(fname), "%s/heap-%04d", FLAGS_db, ++heap_counter_); + WritableFile* file; + Status s = Env::Default()->NewWritableFile(fname, &file); + if (!s.ok()) { + fprintf(stderr, "%s\n", s.ToString().c_str()); + return; + } + bool ok = port::GetHeapProfile(WriteToFile, file); + delete file; + if (!ok) { + fprintf(stderr, "heap profiling not supported\n"); + Env::Default()->DeleteFile(fname); + } + } +}; + +} // namespace leveldb + +int main(int argc, char** argv) { + FLAGS_write_buffer_size = leveldb::Options().write_buffer_size; + FLAGS_open_files = leveldb::Options().max_open_files; + std::string default_db_path; + + for (int i = 1; i < argc; i++) { + double d; + int n; + char junk; + if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) { + FLAGS_benchmarks = argv[i] + strlen("--benchmarks="); + } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) { + FLAGS_compression_ratio = d; + } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_histogram = n; + } else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_use_existing_db = n; + } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) { + FLAGS_num = n; + } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) { + FLAGS_reads = n; + } else if (sscanf(argv[i], "--threads=%d%c", &n, &junk) == 1) { + FLAGS_threads = n; + } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) { + FLAGS_value_size = n; + } else if (sscanf(argv[i], "--write_buffer_size=%d%c", &n, &junk) == 1) { + FLAGS_write_buffer_size = n; + } else if (sscanf(argv[i], "--cache_size=%d%c", &n, &junk) == 1) { + FLAGS_cache_size = n; + } else if (sscanf(argv[i], "--bloom_bits=%d%c", &n, &junk) == 1) { + FLAGS_bloom_bits = n; + } else if (sscanf(argv[i], "--open_files=%d%c", &n, &junk) == 1) { + FLAGS_open_files = n; + } else if (strncmp(argv[i], "--db=", 5) == 0) { + FLAGS_db = argv[i] + 5; + } else { + fprintf(stderr, "Invalid flag '%s'\n", argv[i]); + exit(1); + } + } + + // Choose a location for the test database if none given with --db= + if (FLAGS_db == NULL) { + leveldb::Env::Default()->GetTestDirectory(&default_db_path); + default_db_path += "/dbbench"; + FLAGS_db = default_db_path.c_str(); + } + + leveldb::Benchmark benchmark; + benchmark.Run(); + return 0; +} diff --git a/src/leveldb-lin/db/db_impl.cc b/src/leveldb-lin/db/db_impl.cc new file mode 100644 index 0000000..c9de169 --- /dev/null +++ b/src/leveldb-lin/db/db_impl.cc @@ -0,0 +1,1467 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/db_impl.h" + +#include +#include +#include +#include +#include +#include +#include "db/builder.h" +#include "db/db_iter.h" +#include "db/dbformat.h" +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "db/memtable.h" +#include "db/table_cache.h" +#include "db/version_set.h" +#include "db/write_batch_internal.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/status.h" +#include "leveldb/table.h" +#include "leveldb/table_builder.h" +#include "port/port.h" +#include "table/block.h" +#include "table/merger.h" +#include "table/two_level_iterator.h" +#include "util/coding.h" +#include "util/logging.h" +#include "util/mutexlock.h" + +namespace leveldb { + +// Information kept for every waiting writer +struct DBImpl::Writer { + Status status; + WriteBatch* batch; + bool sync; + bool done; + port::CondVar cv; + + explicit Writer(port::Mutex* mu) : cv(mu) { } +}; + +struct DBImpl::CompactionState { + Compaction* const compaction; + + // Sequence numbers < smallest_snapshot are not significant since we + // will never have to service a snapshot below smallest_snapshot. + // Therefore if we have seen a sequence number S <= smallest_snapshot, + // we can drop all entries for the same key with sequence numbers < S. + SequenceNumber smallest_snapshot; + + // Files produced by compaction + struct Output { + uint64_t number; + uint64_t file_size; + InternalKey smallest, largest; + }; + std::vector outputs; + + // State kept for output being generated + WritableFile* outfile; + TableBuilder* builder; + + uint64_t total_bytes; + + Output* current_output() { return &outputs[outputs.size()-1]; } + + explicit CompactionState(Compaction* c) + : compaction(c), + outfile(NULL), + builder(NULL), + total_bytes(0) { + } +}; + +// Fix user-supplied options to be reasonable +template +static void ClipToRange(T* ptr, V minvalue, V maxvalue) { + if (static_cast(*ptr) > maxvalue) *ptr = maxvalue; + if (static_cast(*ptr) < minvalue) *ptr = minvalue; +} +Options SanitizeOptions(const std::string& dbname, + const InternalKeyComparator* icmp, + const InternalFilterPolicy* ipolicy, + const Options& src) { + Options result = src; + result.comparator = icmp; + result.filter_policy = (src.filter_policy != NULL) ? ipolicy : NULL; + ClipToRange(&result.max_open_files, 20, 50000); + ClipToRange(&result.write_buffer_size, 64<<10, 1<<30); + ClipToRange(&result.block_size, 1<<10, 4<<20); + if (result.info_log == NULL) { + // Open a log file in the same directory as the db + src.env->CreateDir(dbname); // In case it does not exist + src.env->RenameFile(InfoLogFileName(dbname), OldInfoLogFileName(dbname)); + Status s = src.env->NewLogger(InfoLogFileName(dbname), &result.info_log); + if (!s.ok()) { + // No place suitable for logging + result.info_log = NULL; + } + } + if (result.block_cache == NULL) { + result.block_cache = NewLRUCache(8 << 20); + } + return result; +} + +DBImpl::DBImpl(const Options& options, const std::string& dbname) + : env_(options.env), + internal_comparator_(options.comparator), + internal_filter_policy_(options.filter_policy), + options_(SanitizeOptions( + dbname, &internal_comparator_, &internal_filter_policy_, options)), + owns_info_log_(options_.info_log != options.info_log), + owns_cache_(options_.block_cache != options.block_cache), + dbname_(dbname), + db_lock_(NULL), + shutting_down_(NULL), + bg_cv_(&mutex_), + mem_(new MemTable(internal_comparator_)), + imm_(NULL), + logfile_(NULL), + logfile_number_(0), + log_(NULL), + tmp_batch_(new WriteBatch), + bg_compaction_scheduled_(false), + manual_compaction_(NULL) { + mem_->Ref(); + has_imm_.Release_Store(NULL); + + // Reserve ten files or so for other uses and give the rest to TableCache. + const int table_cache_size = options.max_open_files - 10; + table_cache_ = new TableCache(dbname_, &options_, table_cache_size); + + versions_ = new VersionSet(dbname_, &options_, table_cache_, + &internal_comparator_); +} + +DBImpl::~DBImpl() { + // Wait for background work to finish + mutex_.Lock(); + shutting_down_.Release_Store(this); // Any non-NULL value is ok + while (bg_compaction_scheduled_) { + bg_cv_.Wait(); + } + mutex_.Unlock(); + + if (db_lock_ != NULL) { + env_->UnlockFile(db_lock_); + } + + delete versions_; + if (mem_ != NULL) mem_->Unref(); + if (imm_ != NULL) imm_->Unref(); + delete tmp_batch_; + delete log_; + delete logfile_; + delete table_cache_; + + if (owns_info_log_) { + delete options_.info_log; + } + if (owns_cache_) { + delete options_.block_cache; + } +} + +Status DBImpl::NewDB() { + VersionEdit new_db; + new_db.SetComparatorName(user_comparator()->Name()); + new_db.SetLogNumber(0); + new_db.SetNextFile(2); + new_db.SetLastSequence(0); + + const std::string manifest = DescriptorFileName(dbname_, 1); + WritableFile* file; + Status s = env_->NewWritableFile(manifest, &file); + if (!s.ok()) { + return s; + } + { + log::Writer log(file); + std::string record; + new_db.EncodeTo(&record); + s = log.AddRecord(record); + if (s.ok()) { + s = file->Close(); + } + } + delete file; + if (s.ok()) { + // Make "CURRENT" file that points to the new manifest file. + s = SetCurrentFile(env_, dbname_, 1); + } else { + env_->DeleteFile(manifest); + } + return s; +} + +void DBImpl::MaybeIgnoreError(Status* s) const { + if (s->ok() || options_.paranoid_checks) { + // No change needed + } else { + Log(options_.info_log, "Ignoring error %s", s->ToString().c_str()); + *s = Status::OK(); + } +} + +void DBImpl::DeleteObsoleteFiles() { + // Make a set of all of the live files + std::set live = pending_outputs_; + versions_->AddLiveFiles(&live); + + std::vector filenames; + env_->GetChildren(dbname_, &filenames); // Ignoring errors on purpose + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type)) { + bool keep = true; + switch (type) { + case kLogFile: + keep = ((number >= versions_->LogNumber()) || + (number == versions_->PrevLogNumber())); + break; + case kDescriptorFile: + // Keep my manifest file, and any newer incarnations' + // (in case there is a race that allows other incarnations) + keep = (number >= versions_->ManifestFileNumber()); + break; + case kTableFile: + keep = (live.find(number) != live.end()); + break; + case kTempFile: + // Any temp files that are currently being written to must + // be recorded in pending_outputs_, which is inserted into "live" + keep = (live.find(number) != live.end()); + break; + case kCurrentFile: + case kDBLockFile: + case kInfoLogFile: + keep = true; + break; + } + + if (!keep) { + if (type == kTableFile) { + table_cache_->Evict(number); + } + Log(options_.info_log, "Delete type=%d #%lld\n", + int(type), + static_cast(number)); + env_->DeleteFile(dbname_ + "/" + filenames[i]); + } + } + } +} + +Status DBImpl::Recover(VersionEdit* edit) { + mutex_.AssertHeld(); + + // Ignore error from CreateDir since the creation of the DB is + // committed only when the descriptor is created, and this directory + // may already exist from a previous failed creation attempt. + env_->CreateDir(dbname_); + assert(db_lock_ == NULL); + Status s = env_->LockFile(LockFileName(dbname_), &db_lock_); + if (!s.ok()) { + return s; + } + + if (!env_->FileExists(CurrentFileName(dbname_))) { + if (options_.create_if_missing) { + s = NewDB(); + if (!s.ok()) { + return s; + } + } else { + return Status::InvalidArgument( + dbname_, "does not exist (create_if_missing is false)"); + } + } else { + if (options_.error_if_exists) { + return Status::InvalidArgument( + dbname_, "exists (error_if_exists is true)"); + } + } + + s = versions_->Recover(); + if (s.ok()) { + SequenceNumber max_sequence(0); + + // Recover from all newer log files than the ones named in the + // descriptor (new log files may have been added by the previous + // incarnation without registering them in the descriptor). + // + // Note that PrevLogNumber() is no longer used, but we pay + // attention to it in case we are recovering a database + // produced by an older version of leveldb. + const uint64_t min_log = versions_->LogNumber(); + const uint64_t prev_log = versions_->PrevLogNumber(); + std::vector filenames; + s = env_->GetChildren(dbname_, &filenames); + if (!s.ok()) { + return s; + } + uint64_t number; + FileType type; + std::vector logs; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) + && type == kLogFile + && ((number >= min_log) || (number == prev_log))) { + logs.push_back(number); + } + } + + // Recover in the order in which the logs were generated + std::sort(logs.begin(), logs.end()); + for (size_t i = 0; i < logs.size(); i++) { + s = RecoverLogFile(logs[i], edit, &max_sequence); + + // The previous incarnation may not have written any MANIFEST + // records after allocating this log number. So we manually + // update the file number allocation counter in VersionSet. + versions_->MarkFileNumberUsed(logs[i]); + } + + if (s.ok()) { + if (versions_->LastSequence() < max_sequence) { + versions_->SetLastSequence(max_sequence); + } + } + } + + return s; +} + +Status DBImpl::RecoverLogFile(uint64_t log_number, + VersionEdit* edit, + SequenceNumber* max_sequence) { + struct LogReporter : public log::Reader::Reporter { + Env* env; + Logger* info_log; + const char* fname; + Status* status; // NULL if options_.paranoid_checks==false + virtual void Corruption(size_t bytes, const Status& s) { + Log(info_log, "%s%s: dropping %d bytes; %s", + (this->status == NULL ? "(ignoring error) " : ""), + fname, static_cast(bytes), s.ToString().c_str()); + if (this->status != NULL && this->status->ok()) *this->status = s; + } + }; + + mutex_.AssertHeld(); + + // Open the log file + std::string fname = LogFileName(dbname_, log_number); + SequentialFile* file; + Status status = env_->NewSequentialFile(fname, &file); + if (!status.ok()) { + MaybeIgnoreError(&status); + return status; + } + + // Create the log reader. + LogReporter reporter; + reporter.env = env_; + reporter.info_log = options_.info_log; + reporter.fname = fname.c_str(); + reporter.status = (options_.paranoid_checks ? &status : NULL); + // We intentially make log::Reader do checksumming even if + // paranoid_checks==false so that corruptions cause entire commits + // to be skipped instead of propagating bad information (like overly + // large sequence numbers). + log::Reader reader(file, &reporter, true/*checksum*/, + 0/*initial_offset*/); + Log(options_.info_log, "Recovering log #%llu", + (unsigned long long) log_number); + + // Read all the records and add to a memtable + std::string scratch; + Slice record; + WriteBatch batch; + MemTable* mem = NULL; + while (reader.ReadRecord(&record, &scratch) && + status.ok()) { + if (record.size() < 12) { + reporter.Corruption( + record.size(), Status::Corruption("log record too small")); + continue; + } + WriteBatchInternal::SetContents(&batch, record); + + if (mem == NULL) { + mem = new MemTable(internal_comparator_); + mem->Ref(); + } + status = WriteBatchInternal::InsertInto(&batch, mem); + MaybeIgnoreError(&status); + if (!status.ok()) { + break; + } + const SequenceNumber last_seq = + WriteBatchInternal::Sequence(&batch) + + WriteBatchInternal::Count(&batch) - 1; + if (last_seq > *max_sequence) { + *max_sequence = last_seq; + } + + if (mem->ApproximateMemoryUsage() > options_.write_buffer_size) { + status = WriteLevel0Table(mem, edit, NULL); + if (!status.ok()) { + // Reflect errors immediately so that conditions like full + // file-systems cause the DB::Open() to fail. + break; + } + mem->Unref(); + mem = NULL; + } + } + + if (status.ok() && mem != NULL) { + status = WriteLevel0Table(mem, edit, NULL); + // Reflect errors immediately so that conditions like full + // file-systems cause the DB::Open() to fail. + } + + if (mem != NULL) mem->Unref(); + delete file; + return status; +} + +Status DBImpl::WriteLevel0Table(MemTable* mem, VersionEdit* edit, + Version* base) { + mutex_.AssertHeld(); + const uint64_t start_micros = env_->NowMicros(); + FileMetaData meta; + meta.number = versions_->NewFileNumber(); + pending_outputs_.insert(meta.number); + Iterator* iter = mem->NewIterator(); + Log(options_.info_log, "Level-0 table #%llu: started", + (unsigned long long) meta.number); + + Status s; + { + mutex_.Unlock(); + s = BuildTable(dbname_, env_, options_, table_cache_, iter, &meta); + mutex_.Lock(); + } + + Log(options_.info_log, "Level-0 table #%llu: %lld bytes %s", + (unsigned long long) meta.number, + (unsigned long long) meta.file_size, + s.ToString().c_str()); + delete iter; + pending_outputs_.erase(meta.number); + + + // Note that if file_size is zero, the file has been deleted and + // should not be added to the manifest. + int level = 0; + if (s.ok() && meta.file_size > 0) { + const Slice min_user_key = meta.smallest.user_key(); + const Slice max_user_key = meta.largest.user_key(); + if (base != NULL) { + level = base->PickLevelForMemTableOutput(min_user_key, max_user_key); + } + edit->AddFile(level, meta.number, meta.file_size, + meta.smallest, meta.largest); + } + + CompactionStats stats; + stats.micros = env_->NowMicros() - start_micros; + stats.bytes_written = meta.file_size; + stats_[level].Add(stats); + return s; +} + +Status DBImpl::CompactMemTable() { + mutex_.AssertHeld(); + assert(imm_ != NULL); + + // Save the contents of the memtable as a new Table + VersionEdit edit; + Version* base = versions_->current(); + base->Ref(); + Status s = WriteLevel0Table(imm_, &edit, base); + base->Unref(); + + if (s.ok() && shutting_down_.Acquire_Load()) { + s = Status::IOError("Deleting DB during memtable compaction"); + } + + // Replace immutable memtable with the generated Table + if (s.ok()) { + edit.SetPrevLogNumber(0); + edit.SetLogNumber(logfile_number_); // Earlier logs no longer needed + s = versions_->LogAndApply(&edit, &mutex_); + } + + if (s.ok()) { + // Commit to the new state + imm_->Unref(); + imm_ = NULL; + has_imm_.Release_Store(NULL); + DeleteObsoleteFiles(); + } + + return s; +} + +void DBImpl::CompactRange(const Slice* begin, const Slice* end) { + int max_level_with_files = 1; + { + MutexLock l(&mutex_); + Version* base = versions_->current(); + for (int level = 1; level < config::kNumLevels; level++) { + if (base->OverlapInLevel(level, begin, end)) { + max_level_with_files = level; + } + } + } + TEST_CompactMemTable(); // TODO(sanjay): Skip if memtable does not overlap + for (int level = 0; level < max_level_with_files; level++) { + TEST_CompactRange(level, begin, end); + } +} + +void DBImpl::TEST_CompactRange(int level, const Slice* begin,const Slice* end) { + assert(level >= 0); + assert(level + 1 < config::kNumLevels); + + InternalKey begin_storage, end_storage; + + ManualCompaction manual; + manual.level = level; + manual.done = false; + if (begin == NULL) { + manual.begin = NULL; + } else { + begin_storage = InternalKey(*begin, kMaxSequenceNumber, kValueTypeForSeek); + manual.begin = &begin_storage; + } + if (end == NULL) { + manual.end = NULL; + } else { + end_storage = InternalKey(*end, 0, static_cast(0)); + manual.end = &end_storage; + } + + MutexLock l(&mutex_); + while (!manual.done) { + while (manual_compaction_ != NULL) { + bg_cv_.Wait(); + } + manual_compaction_ = &manual; + MaybeScheduleCompaction(); + while (manual_compaction_ == &manual) { + bg_cv_.Wait(); + } + } +} + +Status DBImpl::TEST_CompactMemTable() { + // NULL batch means just wait for earlier writes to be done + Status s = Write(WriteOptions(), NULL); + if (s.ok()) { + // Wait until the compaction completes + MutexLock l(&mutex_); + while (imm_ != NULL && bg_error_.ok()) { + bg_cv_.Wait(); + } + if (imm_ != NULL) { + s = bg_error_; + } + } + return s; +} + +void DBImpl::MaybeScheduleCompaction() { + mutex_.AssertHeld(); + if (bg_compaction_scheduled_) { + // Already scheduled + } else if (shutting_down_.Acquire_Load()) { + // DB is being deleted; no more background compactions + } else if (imm_ == NULL && + manual_compaction_ == NULL && + !versions_->NeedsCompaction()) { + // No work to be done + } else { + bg_compaction_scheduled_ = true; + env_->Schedule(&DBImpl::BGWork, this); + } +} + +void DBImpl::BGWork(void* db) { + reinterpret_cast(db)->BackgroundCall(); +} + +void DBImpl::BackgroundCall() { + MutexLock l(&mutex_); + assert(bg_compaction_scheduled_); + if (!shutting_down_.Acquire_Load()) { + Status s = BackgroundCompaction(); + if (s.ok()) { + // Success + } else if (shutting_down_.Acquire_Load()) { + // Error most likely due to shutdown; do not wait + } else { + // Wait a little bit before retrying background compaction in + // case this is an environmental problem and we do not want to + // chew up resources for failed compactions for the duration of + // the problem. + bg_cv_.SignalAll(); // In case a waiter can proceed despite the error + Log(options_.info_log, "Waiting after background compaction error: %s", + s.ToString().c_str()); + mutex_.Unlock(); + env_->SleepForMicroseconds(1000000); + mutex_.Lock(); + } + } + + bg_compaction_scheduled_ = false; + + // Previous compaction may have produced too many files in a level, + // so reschedule another compaction if needed. + MaybeScheduleCompaction(); + bg_cv_.SignalAll(); +} + +Status DBImpl::BackgroundCompaction() { + mutex_.AssertHeld(); + + if (imm_ != NULL) { + return CompactMemTable(); + } + + Compaction* c; + bool is_manual = (manual_compaction_ != NULL); + InternalKey manual_end; + if (is_manual) { + ManualCompaction* m = manual_compaction_; + c = versions_->CompactRange(m->level, m->begin, m->end); + m->done = (c == NULL); + if (c != NULL) { + manual_end = c->input(0, c->num_input_files(0) - 1)->largest; + } + Log(options_.info_log, + "Manual compaction at level-%d from %s .. %s; will stop at %s\n", + m->level, + (m->begin ? m->begin->DebugString().c_str() : "(begin)"), + (m->end ? m->end->DebugString().c_str() : "(end)"), + (m->done ? "(end)" : manual_end.DebugString().c_str())); + } else { + c = versions_->PickCompaction(); + } + + Status status; + if (c == NULL) { + // Nothing to do + } else if (!is_manual && c->IsTrivialMove()) { + // Move file to next level + assert(c->num_input_files(0) == 1); + FileMetaData* f = c->input(0, 0); + c->edit()->DeleteFile(c->level(), f->number); + c->edit()->AddFile(c->level() + 1, f->number, f->file_size, + f->smallest, f->largest); + status = versions_->LogAndApply(c->edit(), &mutex_); + VersionSet::LevelSummaryStorage tmp; + Log(options_.info_log, "Moved #%lld to level-%d %lld bytes %s: %s\n", + static_cast(f->number), + c->level() + 1, + static_cast(f->file_size), + status.ToString().c_str(), + versions_->LevelSummary(&tmp)); + } else { + CompactionState* compact = new CompactionState(c); + status = DoCompactionWork(compact); + CleanupCompaction(compact); + c->ReleaseInputs(); + DeleteObsoleteFiles(); + } + delete c; + + if (status.ok()) { + // Done + } else if (shutting_down_.Acquire_Load()) { + // Ignore compaction errors found during shutting down + } else { + Log(options_.info_log, + "Compaction error: %s", status.ToString().c_str()); + if (options_.paranoid_checks && bg_error_.ok()) { + bg_error_ = status; + } + } + + if (is_manual) { + ManualCompaction* m = manual_compaction_; + if (!status.ok()) { + m->done = true; + } + if (!m->done) { + // We only compacted part of the requested range. Update *m + // to the range that is left to be compacted. + m->tmp_storage = manual_end; + m->begin = &m->tmp_storage; + } + manual_compaction_ = NULL; + } + return status; +} + +void DBImpl::CleanupCompaction(CompactionState* compact) { + mutex_.AssertHeld(); + if (compact->builder != NULL) { + // May happen if we get a shutdown call in the middle of compaction + compact->builder->Abandon(); + delete compact->builder; + } else { + assert(compact->outfile == NULL); + } + delete compact->outfile; + for (size_t i = 0; i < compact->outputs.size(); i++) { + const CompactionState::Output& out = compact->outputs[i]; + pending_outputs_.erase(out.number); + } + delete compact; +} + +Status DBImpl::OpenCompactionOutputFile(CompactionState* compact) { + assert(compact != NULL); + assert(compact->builder == NULL); + uint64_t file_number; + { + mutex_.Lock(); + file_number = versions_->NewFileNumber(); + pending_outputs_.insert(file_number); + CompactionState::Output out; + out.number = file_number; + out.smallest.Clear(); + out.largest.Clear(); + compact->outputs.push_back(out); + mutex_.Unlock(); + } + + // Make the output file + std::string fname = TableFileName(dbname_, file_number); + Status s = env_->NewWritableFile(fname, &compact->outfile); + if (s.ok()) { + compact->builder = new TableBuilder(options_, compact->outfile); + } + return s; +} + +Status DBImpl::FinishCompactionOutputFile(CompactionState* compact, + Iterator* input) { + assert(compact != NULL); + assert(compact->outfile != NULL); + assert(compact->builder != NULL); + + const uint64_t output_number = compact->current_output()->number; + assert(output_number != 0); + + // Check for iterator errors + Status s = input->status(); + const uint64_t current_entries = compact->builder->NumEntries(); + if (s.ok()) { + s = compact->builder->Finish(); + } else { + compact->builder->Abandon(); + } + const uint64_t current_bytes = compact->builder->FileSize(); + compact->current_output()->file_size = current_bytes; + compact->total_bytes += current_bytes; + delete compact->builder; + compact->builder = NULL; + + // Finish and check for file errors + if (s.ok()) { + s = compact->outfile->Sync(); + } + if (s.ok()) { + s = compact->outfile->Close(); + } + delete compact->outfile; + compact->outfile = NULL; + + if (s.ok() && current_entries > 0) { + // Verify that the table is usable + Iterator* iter = table_cache_->NewIterator(ReadOptions(), + output_number, + current_bytes); + s = iter->status(); + delete iter; + if (s.ok()) { + Log(options_.info_log, + "Generated table #%llu: %lld keys, %lld bytes", + (unsigned long long) output_number, + (unsigned long long) current_entries, + (unsigned long long) current_bytes); + } + } + return s; +} + + +Status DBImpl::InstallCompactionResults(CompactionState* compact) { + mutex_.AssertHeld(); + Log(options_.info_log, "Compacted %d@%d + %d@%d files => %lld bytes", + compact->compaction->num_input_files(0), + compact->compaction->level(), + compact->compaction->num_input_files(1), + compact->compaction->level() + 1, + static_cast(compact->total_bytes)); + + // Add compaction outputs + compact->compaction->AddInputDeletions(compact->compaction->edit()); + const int level = compact->compaction->level(); + for (size_t i = 0; i < compact->outputs.size(); i++) { + const CompactionState::Output& out = compact->outputs[i]; + compact->compaction->edit()->AddFile( + level + 1, + out.number, out.file_size, out.smallest, out.largest); + } + return versions_->LogAndApply(compact->compaction->edit(), &mutex_); +} + +Status DBImpl::DoCompactionWork(CompactionState* compact) { + const uint64_t start_micros = env_->NowMicros(); + int64_t imm_micros = 0; // Micros spent doing imm_ compactions + + Log(options_.info_log, "Compacting %d@%d + %d@%d files", + compact->compaction->num_input_files(0), + compact->compaction->level(), + compact->compaction->num_input_files(1), + compact->compaction->level() + 1); + + assert(versions_->NumLevelFiles(compact->compaction->level()) > 0); + assert(compact->builder == NULL); + assert(compact->outfile == NULL); + if (snapshots_.empty()) { + compact->smallest_snapshot = versions_->LastSequence(); + } else { + compact->smallest_snapshot = snapshots_.oldest()->number_; + } + + // Release mutex while we're actually doing the compaction work + mutex_.Unlock(); + + Iterator* input = versions_->MakeInputIterator(compact->compaction); + input->SeekToFirst(); + Status status; + ParsedInternalKey ikey; + std::string current_user_key; + bool has_current_user_key = false; + SequenceNumber last_sequence_for_key = kMaxSequenceNumber; + for (; input->Valid() && !shutting_down_.Acquire_Load(); ) { + // Prioritize immutable compaction work + if (has_imm_.NoBarrier_Load() != NULL) { + const uint64_t imm_start = env_->NowMicros(); + mutex_.Lock(); + if (imm_ != NULL) { + CompactMemTable(); + bg_cv_.SignalAll(); // Wakeup MakeRoomForWrite() if necessary + } + mutex_.Unlock(); + imm_micros += (env_->NowMicros() - imm_start); + } + + Slice key = input->key(); + if (compact->compaction->ShouldStopBefore(key) && + compact->builder != NULL) { + status = FinishCompactionOutputFile(compact, input); + if (!status.ok()) { + break; + } + } + + // Handle key/value, add to state, etc. + bool drop = false; + if (!ParseInternalKey(key, &ikey)) { + // Do not hide error keys + current_user_key.clear(); + has_current_user_key = false; + last_sequence_for_key = kMaxSequenceNumber; + } else { + if (!has_current_user_key || + user_comparator()->Compare(ikey.user_key, + Slice(current_user_key)) != 0) { + // First occurrence of this user key + current_user_key.assign(ikey.user_key.data(), ikey.user_key.size()); + has_current_user_key = true; + last_sequence_for_key = kMaxSequenceNumber; + } + + if (last_sequence_for_key <= compact->smallest_snapshot) { + // Hidden by an newer entry for same user key + drop = true; // (A) + } else if (ikey.type == kTypeDeletion && + ikey.sequence <= compact->smallest_snapshot && + compact->compaction->IsBaseLevelForKey(ikey.user_key)) { + // For this user key: + // (1) there is no data in higher levels + // (2) data in lower levels will have larger sequence numbers + // (3) data in layers that are being compacted here and have + // smaller sequence numbers will be dropped in the next + // few iterations of this loop (by rule (A) above). + // Therefore this deletion marker is obsolete and can be dropped. + drop = true; + } + + last_sequence_for_key = ikey.sequence; + } +#if 0 + Log(options_.info_log, + " Compact: %s, seq %d, type: %d %d, drop: %d, is_base: %d, " + "%d smallest_snapshot: %d", + ikey.user_key.ToString().c_str(), + (int)ikey.sequence, ikey.type, kTypeValue, drop, + compact->compaction->IsBaseLevelForKey(ikey.user_key), + (int)last_sequence_for_key, (int)compact->smallest_snapshot); +#endif + + if (!drop) { + // Open output file if necessary + if (compact->builder == NULL) { + status = OpenCompactionOutputFile(compact); + if (!status.ok()) { + break; + } + } + if (compact->builder->NumEntries() == 0) { + compact->current_output()->smallest.DecodeFrom(key); + } + compact->current_output()->largest.DecodeFrom(key); + compact->builder->Add(key, input->value()); + + // Close output file if it is big enough + if (compact->builder->FileSize() >= + compact->compaction->MaxOutputFileSize()) { + status = FinishCompactionOutputFile(compact, input); + if (!status.ok()) { + break; + } + } + } + + input->Next(); + } + + if (status.ok() && shutting_down_.Acquire_Load()) { + status = Status::IOError("Deleting DB during compaction"); + } + if (status.ok() && compact->builder != NULL) { + status = FinishCompactionOutputFile(compact, input); + } + if (status.ok()) { + status = input->status(); + } + delete input; + input = NULL; + + CompactionStats stats; + stats.micros = env_->NowMicros() - start_micros - imm_micros; + for (int which = 0; which < 2; which++) { + for (int i = 0; i < compact->compaction->num_input_files(which); i++) { + stats.bytes_read += compact->compaction->input(which, i)->file_size; + } + } + for (size_t i = 0; i < compact->outputs.size(); i++) { + stats.bytes_written += compact->outputs[i].file_size; + } + + mutex_.Lock(); + stats_[compact->compaction->level() + 1].Add(stats); + + if (status.ok()) { + status = InstallCompactionResults(compact); + } + VersionSet::LevelSummaryStorage tmp; + Log(options_.info_log, + "compacted to: %s", versions_->LevelSummary(&tmp)); + return status; +} + +namespace { +struct IterState { + port::Mutex* mu; + Version* version; + MemTable* mem; + MemTable* imm; +}; + +static void CleanupIteratorState(void* arg1, void* arg2) { + IterState* state = reinterpret_cast(arg1); + state->mu->Lock(); + state->mem->Unref(); + if (state->imm != NULL) state->imm->Unref(); + state->version->Unref(); + state->mu->Unlock(); + delete state; +} +} // namespace + +Iterator* DBImpl::NewInternalIterator(const ReadOptions& options, + SequenceNumber* latest_snapshot) { + IterState* cleanup = new IterState; + mutex_.Lock(); + *latest_snapshot = versions_->LastSequence(); + + // Collect together all needed child iterators + std::vector list; + list.push_back(mem_->NewIterator()); + mem_->Ref(); + if (imm_ != NULL) { + list.push_back(imm_->NewIterator()); + imm_->Ref(); + } + versions_->current()->AddIterators(options, &list); + Iterator* internal_iter = + NewMergingIterator(&internal_comparator_, &list[0], list.size()); + versions_->current()->Ref(); + + cleanup->mu = &mutex_; + cleanup->mem = mem_; + cleanup->imm = imm_; + cleanup->version = versions_->current(); + internal_iter->RegisterCleanup(CleanupIteratorState, cleanup, NULL); + + mutex_.Unlock(); + return internal_iter; +} + +Iterator* DBImpl::TEST_NewInternalIterator() { + SequenceNumber ignored; + return NewInternalIterator(ReadOptions(), &ignored); +} + +int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes() { + MutexLock l(&mutex_); + return versions_->MaxNextLevelOverlappingBytes(); +} + +Status DBImpl::Get(const ReadOptions& options, + const Slice& key, + std::string* value) { + Status s; + MutexLock l(&mutex_); + SequenceNumber snapshot; + if (options.snapshot != NULL) { + snapshot = reinterpret_cast(options.snapshot)->number_; + } else { + snapshot = versions_->LastSequence(); + } + + MemTable* mem = mem_; + MemTable* imm = imm_; + Version* current = versions_->current(); + mem->Ref(); + if (imm != NULL) imm->Ref(); + current->Ref(); + + bool have_stat_update = false; + Version::GetStats stats; + + // Unlock while reading from files and memtables + { + mutex_.Unlock(); + // First look in the memtable, then in the immutable memtable (if any). + LookupKey lkey(key, snapshot); + if (mem->Get(lkey, value, &s)) { + // Done + } else if (imm != NULL && imm->Get(lkey, value, &s)) { + // Done + } else { + s = current->Get(options, lkey, value, &stats); + have_stat_update = true; + } + mutex_.Lock(); + } + + if (have_stat_update && current->UpdateStats(stats)) { + MaybeScheduleCompaction(); + } + mem->Unref(); + if (imm != NULL) imm->Unref(); + current->Unref(); + return s; +} + +Iterator* DBImpl::NewIterator(const ReadOptions& options) { + SequenceNumber latest_snapshot; + Iterator* internal_iter = NewInternalIterator(options, &latest_snapshot); + return NewDBIterator( + &dbname_, env_, user_comparator(), internal_iter, + (options.snapshot != NULL + ? reinterpret_cast(options.snapshot)->number_ + : latest_snapshot)); +} + +const Snapshot* DBImpl::GetSnapshot() { + MutexLock l(&mutex_); + return snapshots_.New(versions_->LastSequence()); +} + +void DBImpl::ReleaseSnapshot(const Snapshot* s) { + MutexLock l(&mutex_); + snapshots_.Delete(reinterpret_cast(s)); +} + +// Convenience methods +Status DBImpl::Put(const WriteOptions& o, const Slice& key, const Slice& val) { + return DB::Put(o, key, val); +} + +Status DBImpl::Delete(const WriteOptions& options, const Slice& key) { + return DB::Delete(options, key); +} + +Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) { + Writer w(&mutex_); + w.batch = my_batch; + w.sync = options.sync; + w.done = false; + + MutexLock l(&mutex_); + writers_.push_back(&w); + while (!w.done && &w != writers_.front()) { + w.cv.Wait(); + } + if (w.done) { + return w.status; + } + + // May temporarily unlock and wait. + Status status = MakeRoomForWrite(my_batch == NULL); + uint64_t last_sequence = versions_->LastSequence(); + Writer* last_writer = &w; + if (status.ok() && my_batch != NULL) { // NULL batch is for compactions + WriteBatch* updates = BuildBatchGroup(&last_writer); + WriteBatchInternal::SetSequence(updates, last_sequence + 1); + last_sequence += WriteBatchInternal::Count(updates); + + // Add to log and apply to memtable. We can release the lock + // during this phase since &w is currently responsible for logging + // and protects against concurrent loggers and concurrent writes + // into mem_. + { + mutex_.Unlock(); + status = log_->AddRecord(WriteBatchInternal::Contents(updates)); + if (status.ok() && options.sync) { + status = logfile_->Sync(); + } + if (status.ok()) { + status = WriteBatchInternal::InsertInto(updates, mem_); + } + mutex_.Lock(); + } + if (updates == tmp_batch_) tmp_batch_->Clear(); + + versions_->SetLastSequence(last_sequence); + } + + while (true) { + Writer* ready = writers_.front(); + writers_.pop_front(); + if (ready != &w) { + ready->status = status; + ready->done = true; + ready->cv.Signal(); + } + if (ready == last_writer) break; + } + + // Notify new head of write queue + if (!writers_.empty()) { + writers_.front()->cv.Signal(); + } + + return status; +} + +// REQUIRES: Writer list must be non-empty +// REQUIRES: First writer must have a non-NULL batch +WriteBatch* DBImpl::BuildBatchGroup(Writer** last_writer) { + assert(!writers_.empty()); + Writer* first = writers_.front(); + WriteBatch* result = first->batch; + assert(result != NULL); + + size_t size = WriteBatchInternal::ByteSize(first->batch); + + // Allow the group to grow up to a maximum size, but if the + // original write is small, limit the growth so we do not slow + // down the small write too much. + size_t max_size = 1 << 20; + if (size <= (128<<10)) { + max_size = size + (128<<10); + } + + *last_writer = first; + std::deque::iterator iter = writers_.begin(); + ++iter; // Advance past "first" + for (; iter != writers_.end(); ++iter) { + Writer* w = *iter; + if (w->sync && !first->sync) { + // Do not include a sync write into a batch handled by a non-sync write. + break; + } + + if (w->batch != NULL) { + size += WriteBatchInternal::ByteSize(w->batch); + if (size > max_size) { + // Do not make batch too big + break; + } + + // Append to *reuslt + if (result == first->batch) { + // Switch to temporary batch instead of disturbing caller's batch + result = tmp_batch_; + assert(WriteBatchInternal::Count(result) == 0); + WriteBatchInternal::Append(result, first->batch); + } + WriteBatchInternal::Append(result, w->batch); + } + *last_writer = w; + } + return result; +} + +// REQUIRES: mutex_ is held +// REQUIRES: this thread is currently at the front of the writer queue +Status DBImpl::MakeRoomForWrite(bool force) { + mutex_.AssertHeld(); + assert(!writers_.empty()); + bool allow_delay = !force; + Status s; + while (true) { + if (!bg_error_.ok()) { + // Yield previous error + s = bg_error_; + break; + } else if ( + allow_delay && + versions_->NumLevelFiles(0) >= config::kL0_SlowdownWritesTrigger) { + // We are getting close to hitting a hard limit on the number of + // L0 files. Rather than delaying a single write by several + // seconds when we hit the hard limit, start delaying each + // individual write by 1ms to reduce latency variance. Also, + // this delay hands over some CPU to the compaction thread in + // case it is sharing the same core as the writer. + mutex_.Unlock(); + env_->SleepForMicroseconds(1000); + allow_delay = false; // Do not delay a single write more than once + mutex_.Lock(); + } else if (!force && + (mem_->ApproximateMemoryUsage() <= options_.write_buffer_size)) { + // There is room in current memtable + break; + } else if (imm_ != NULL) { + // We have filled up the current memtable, but the previous + // one is still being compacted, so we wait. + bg_cv_.Wait(); + } else if (versions_->NumLevelFiles(0) >= config::kL0_StopWritesTrigger) { + // There are too many level-0 files. + Log(options_.info_log, "waiting...\n"); + bg_cv_.Wait(); + } else { + // Attempt to switch to a new memtable and trigger compaction of old + assert(versions_->PrevLogNumber() == 0); + uint64_t new_log_number = versions_->NewFileNumber(); + WritableFile* lfile = NULL; + s = env_->NewWritableFile(LogFileName(dbname_, new_log_number), &lfile); + if (!s.ok()) { + // Avoid chewing through file number space in a tight loop. + versions_->ReuseFileNumber(new_log_number); + break; + } + delete log_; + delete logfile_; + logfile_ = lfile; + logfile_number_ = new_log_number; + log_ = new log::Writer(lfile); + imm_ = mem_; + has_imm_.Release_Store(imm_); + mem_ = new MemTable(internal_comparator_); + mem_->Ref(); + force = false; // Do not force another compaction if have room + MaybeScheduleCompaction(); + } + } + return s; +} + +bool DBImpl::GetProperty(const Slice& property, std::string* value) { + value->clear(); + + MutexLock l(&mutex_); + Slice in = property; + Slice prefix("leveldb."); + if (!in.starts_with(prefix)) return false; + in.remove_prefix(prefix.size()); + + if (in.starts_with("num-files-at-level")) { + in.remove_prefix(strlen("num-files-at-level")); + uint64_t level; + bool ok = ConsumeDecimalNumber(&in, &level) && in.empty(); + if (!ok || level >= config::kNumLevels) { + return false; + } else { + char buf[100]; + snprintf(buf, sizeof(buf), "%d", + versions_->NumLevelFiles(static_cast(level))); + *value = buf; + return true; + } + } else if (in == "stats") { + char buf[200]; + snprintf(buf, sizeof(buf), + " Compactions\n" + "Level Files Size(MB) Time(sec) Read(MB) Write(MB)\n" + "--------------------------------------------------\n" + ); + value->append(buf); + for (int level = 0; level < config::kNumLevels; level++) { + int files = versions_->NumLevelFiles(level); + if (stats_[level].micros > 0 || files > 0) { + snprintf( + buf, sizeof(buf), + "%3d %8d %8.0f %9.0f %8.0f %9.0f\n", + level, + files, + versions_->NumLevelBytes(level) / 1048576.0, + stats_[level].micros / 1e6, + stats_[level].bytes_read / 1048576.0, + stats_[level].bytes_written / 1048576.0); + value->append(buf); + } + } + return true; + } else if (in == "sstables") { + *value = versions_->current()->DebugString(); + return true; + } + + return false; +} + +void DBImpl::GetApproximateSizes( + const Range* range, int n, + uint64_t* sizes) { + // TODO(opt): better implementation + Version* v; + { + MutexLock l(&mutex_); + versions_->current()->Ref(); + v = versions_->current(); + } + + for (int i = 0; i < n; i++) { + // Convert user_key into a corresponding internal key. + InternalKey k1(range[i].start, kMaxSequenceNumber, kValueTypeForSeek); + InternalKey k2(range[i].limit, kMaxSequenceNumber, kValueTypeForSeek); + uint64_t start = versions_->ApproximateOffsetOf(v, k1); + uint64_t limit = versions_->ApproximateOffsetOf(v, k2); + sizes[i] = (limit >= start ? limit - start : 0); + } + + { + MutexLock l(&mutex_); + v->Unref(); + } +} + +// Default implementations of convenience methods that subclasses of DB +// can call if they wish +Status DB::Put(const WriteOptions& opt, const Slice& key, const Slice& value) { + WriteBatch batch; + batch.Put(key, value); + return Write(opt, &batch); +} + +Status DB::Delete(const WriteOptions& opt, const Slice& key) { + WriteBatch batch; + batch.Delete(key); + return Write(opt, &batch); +} + +DB::~DB() { } + +Status DB::Open(const Options& options, const std::string& dbname, + DB** dbptr) { + *dbptr = NULL; + + DBImpl* impl = new DBImpl(options, dbname); + impl->mutex_.Lock(); + VersionEdit edit; + Status s = impl->Recover(&edit); // Handles create_if_missing, error_if_exists + if (s.ok()) { + uint64_t new_log_number = impl->versions_->NewFileNumber(); + WritableFile* lfile; + s = options.env->NewWritableFile(LogFileName(dbname, new_log_number), + &lfile); + if (s.ok()) { + edit.SetLogNumber(new_log_number); + impl->logfile_ = lfile; + impl->logfile_number_ = new_log_number; + impl->log_ = new log::Writer(lfile); + s = impl->versions_->LogAndApply(&edit, &impl->mutex_); + } + if (s.ok()) { + impl->DeleteObsoleteFiles(); + impl->MaybeScheduleCompaction(); + } + } + impl->mutex_.Unlock(); + if (s.ok()) { + *dbptr = impl; + } else { + delete impl; + } + return s; +} + +Snapshot::~Snapshot() { +} + +Status DestroyDB(const std::string& dbname, const Options& options) { + Env* env = options.env; + std::vector filenames; + // Ignore error in case directory does not exist + env->GetChildren(dbname, &filenames); + if (filenames.empty()) { + return Status::OK(); + } + + FileLock* lock; + const std::string lockname = LockFileName(dbname); + Status result = env->LockFile(lockname, &lock); + if (result.ok()) { + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) && + type != kDBLockFile) { // Lock file will be deleted at end + Status del = env->DeleteFile(dbname + "/" + filenames[i]); + if (result.ok() && !del.ok()) { + result = del; + } + } + } + env->UnlockFile(lock); // Ignore error since state is already gone + env->DeleteFile(lockname); + env->DeleteDir(dbname); // Ignore error in case dir contains other files + } + return result; +} + +} // namespace leveldb diff --git a/src/leveldb-lin/db/db_impl.h b/src/leveldb-lin/db/db_impl.h new file mode 100644 index 0000000..bd29dd8 --- /dev/null +++ b/src/leveldb-lin/db/db_impl.h @@ -0,0 +1,202 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_DB_IMPL_H_ +#define STORAGE_LEVELDB_DB_DB_IMPL_H_ + +#include +#include +#include "db/dbformat.h" +#include "db/log_writer.h" +#include "db/snapshot.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "port/port.h" +#include "port/thread_annotations.h" + +namespace leveldb { + +class MemTable; +class TableCache; +class Version; +class VersionEdit; +class VersionSet; + +class DBImpl : public DB { + public: + DBImpl(const Options& options, const std::string& dbname); + virtual ~DBImpl(); + + // Implementations of the DB interface + virtual Status Put(const WriteOptions&, const Slice& key, const Slice& value); + virtual Status Delete(const WriteOptions&, const Slice& key); + virtual Status Write(const WriteOptions& options, WriteBatch* updates); + virtual Status Get(const ReadOptions& options, + const Slice& key, + std::string* value); + virtual Iterator* NewIterator(const ReadOptions&); + virtual const Snapshot* GetSnapshot(); + virtual void ReleaseSnapshot(const Snapshot* snapshot); + virtual bool GetProperty(const Slice& property, std::string* value); + virtual void GetApproximateSizes(const Range* range, int n, uint64_t* sizes); + virtual void CompactRange(const Slice* begin, const Slice* end); + + // Extra methods (for testing) that are not in the public DB interface + + // Compact any files in the named level that overlap [*begin,*end] + void TEST_CompactRange(int level, const Slice* begin, const Slice* end); + + // Force current memtable contents to be compacted. + Status TEST_CompactMemTable(); + + // Return an internal iterator over the current state of the database. + // The keys of this iterator are internal keys (see format.h). + // The returned iterator should be deleted when no longer needed. + Iterator* TEST_NewInternalIterator(); + + // Return the maximum overlapping data (in bytes) at next level for any + // file at a level >= 1. + int64_t TEST_MaxNextLevelOverlappingBytes(); + + private: + friend class DB; + struct CompactionState; + struct Writer; + + Iterator* NewInternalIterator(const ReadOptions&, + SequenceNumber* latest_snapshot); + + Status NewDB(); + + // Recover the descriptor from persistent storage. May do a significant + // amount of work to recover recently logged updates. Any changes to + // be made to the descriptor are added to *edit. + Status Recover(VersionEdit* edit) EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + void MaybeIgnoreError(Status* s) const; + + // Delete any unneeded files and stale in-memory entries. + void DeleteObsoleteFiles(); + + // Compact the in-memory write buffer to disk. Switches to a new + // log-file/memtable and writes a new descriptor iff successful. + Status CompactMemTable() + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Status RecoverLogFile(uint64_t log_number, + VersionEdit* edit, + SequenceNumber* max_sequence) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Status WriteLevel0Table(MemTable* mem, VersionEdit* edit, Version* base) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Status MakeRoomForWrite(bool force /* compact even if there is room? */) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + WriteBatch* BuildBatchGroup(Writer** last_writer); + + void MaybeScheduleCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_); + static void BGWork(void* db); + void BackgroundCall(); + Status BackgroundCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_); + void CleanupCompaction(CompactionState* compact) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + Status DoCompactionWork(CompactionState* compact) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Status OpenCompactionOutputFile(CompactionState* compact); + Status FinishCompactionOutputFile(CompactionState* compact, Iterator* input); + Status InstallCompactionResults(CompactionState* compact) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Constant after construction + Env* const env_; + const InternalKeyComparator internal_comparator_; + const InternalFilterPolicy internal_filter_policy_; + const Options options_; // options_.comparator == &internal_comparator_ + bool owns_info_log_; + bool owns_cache_; + const std::string dbname_; + + // table_cache_ provides its own synchronization + TableCache* table_cache_; + + // Lock over the persistent DB state. Non-NULL iff successfully acquired. + FileLock* db_lock_; + + // State below is protected by mutex_ + port::Mutex mutex_; + port::AtomicPointer shutting_down_; + port::CondVar bg_cv_; // Signalled when background work finishes + MemTable* mem_; + MemTable* imm_; // Memtable being compacted + port::AtomicPointer has_imm_; // So bg thread can detect non-NULL imm_ + WritableFile* logfile_; + uint64_t logfile_number_; + log::Writer* log_; + + // Queue of writers. + std::deque writers_; + WriteBatch* tmp_batch_; + + SnapshotList snapshots_; + + // Set of table files to protect from deletion because they are + // part of ongoing compactions. + std::set pending_outputs_; + + // Has a background compaction been scheduled or is running? + bool bg_compaction_scheduled_; + + // Information for a manual compaction + struct ManualCompaction { + int level; + bool done; + const InternalKey* begin; // NULL means beginning of key range + const InternalKey* end; // NULL means end of key range + InternalKey tmp_storage; // Used to keep track of compaction progress + }; + ManualCompaction* manual_compaction_; + + VersionSet* versions_; + + // Have we encountered a background error in paranoid mode? + Status bg_error_; + + // Per level compaction stats. stats_[level] stores the stats for + // compactions that produced data for the specified "level". + struct CompactionStats { + int64_t micros; + int64_t bytes_read; + int64_t bytes_written; + + CompactionStats() : micros(0), bytes_read(0), bytes_written(0) { } + + void Add(const CompactionStats& c) { + this->micros += c.micros; + this->bytes_read += c.bytes_read; + this->bytes_written += c.bytes_written; + } + }; + CompactionStats stats_[config::kNumLevels]; + + // No copying allowed + DBImpl(const DBImpl&); + void operator=(const DBImpl&); + + const Comparator* user_comparator() const { + return internal_comparator_.user_comparator(); + } +}; + +// Sanitize db options. The caller should delete result.info_log if +// it is not equal to src.info_log. +extern Options SanitizeOptions(const std::string& db, + const InternalKeyComparator* icmp, + const InternalFilterPolicy* ipolicy, + const Options& src); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_DB_IMPL_H_ diff --git a/src/leveldb-lin/db/db_impl.o b/src/leveldb-lin/db/db_impl.o new file mode 100644 index 0000000..9eb2c00 Binary files /dev/null and b/src/leveldb-lin/db/db_impl.o differ diff --git a/src/leveldb-lin/db/db_iter.cc b/src/leveldb-lin/db/db_iter.cc new file mode 100644 index 0000000..87dca2d --- /dev/null +++ b/src/leveldb-lin/db/db_iter.cc @@ -0,0 +1,299 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/db_iter.h" + +#include "db/filename.h" +#include "db/dbformat.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "port/port.h" +#include "util/logging.h" +#include "util/mutexlock.h" + +namespace leveldb { + +#if 0 +static void DumpInternalIter(Iterator* iter) { + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + ParsedInternalKey k; + if (!ParseInternalKey(iter->key(), &k)) { + fprintf(stderr, "Corrupt '%s'\n", EscapeString(iter->key()).c_str()); + } else { + fprintf(stderr, "@ '%s'\n", k.DebugString().c_str()); + } + } +} +#endif + +namespace { + +// Memtables and sstables that make the DB representation contain +// (userkey,seq,type) => uservalue entries. DBIter +// combines multiple entries for the same userkey found in the DB +// representation into a single entry while accounting for sequence +// numbers, deletion markers, overwrites, etc. +class DBIter: public Iterator { + public: + // Which direction is the iterator currently moving? + // (1) When moving forward, the internal iterator is positioned at + // the exact entry that yields this->key(), this->value() + // (2) When moving backwards, the internal iterator is positioned + // just before all entries whose user key == this->key(). + enum Direction { + kForward, + kReverse + }; + + DBIter(const std::string* dbname, Env* env, + const Comparator* cmp, Iterator* iter, SequenceNumber s) + : dbname_(dbname), + env_(env), + user_comparator_(cmp), + iter_(iter), + sequence_(s), + direction_(kForward), + valid_(false) { + } + virtual ~DBIter() { + delete iter_; + } + virtual bool Valid() const { return valid_; } + virtual Slice key() const { + assert(valid_); + return (direction_ == kForward) ? ExtractUserKey(iter_->key()) : saved_key_; + } + virtual Slice value() const { + assert(valid_); + return (direction_ == kForward) ? iter_->value() : saved_value_; + } + virtual Status status() const { + if (status_.ok()) { + return iter_->status(); + } else { + return status_; + } + } + + virtual void Next(); + virtual void Prev(); + virtual void Seek(const Slice& target); + virtual void SeekToFirst(); + virtual void SeekToLast(); + + private: + void FindNextUserEntry(bool skipping, std::string* skip); + void FindPrevUserEntry(); + bool ParseKey(ParsedInternalKey* key); + + inline void SaveKey(const Slice& k, std::string* dst) { + dst->assign(k.data(), k.size()); + } + + inline void ClearSavedValue() { + if (saved_value_.capacity() > 1048576) { + std::string empty; + swap(empty, saved_value_); + } else { + saved_value_.clear(); + } + } + + const std::string* const dbname_; + Env* const env_; + const Comparator* const user_comparator_; + Iterator* const iter_; + SequenceNumber const sequence_; + + Status status_; + std::string saved_key_; // == current key when direction_==kReverse + std::string saved_value_; // == current raw value when direction_==kReverse + Direction direction_; + bool valid_; + + // No copying allowed + DBIter(const DBIter&); + void operator=(const DBIter&); +}; + +inline bool DBIter::ParseKey(ParsedInternalKey* ikey) { + if (!ParseInternalKey(iter_->key(), ikey)) { + status_ = Status::Corruption("corrupted internal key in DBIter"); + return false; + } else { + return true; + } +} + +void DBIter::Next() { + assert(valid_); + + if (direction_ == kReverse) { // Switch directions? + direction_ = kForward; + // iter_ is pointing just before the entries for this->key(), + // so advance into the range of entries for this->key() and then + // use the normal skipping code below. + if (!iter_->Valid()) { + iter_->SeekToFirst(); + } else { + iter_->Next(); + } + if (!iter_->Valid()) { + valid_ = false; + saved_key_.clear(); + return; + } + } + + // Temporarily use saved_key_ as storage for key to skip. + std::string* skip = &saved_key_; + SaveKey(ExtractUserKey(iter_->key()), skip); + FindNextUserEntry(true, skip); +} + +void DBIter::FindNextUserEntry(bool skipping, std::string* skip) { + // Loop until we hit an acceptable entry to yield + assert(iter_->Valid()); + assert(direction_ == kForward); + do { + ParsedInternalKey ikey; + if (ParseKey(&ikey) && ikey.sequence <= sequence_) { + switch (ikey.type) { + case kTypeDeletion: + // Arrange to skip all upcoming entries for this key since + // they are hidden by this deletion. + SaveKey(ikey.user_key, skip); + skipping = true; + break; + case kTypeValue: + if (skipping && + user_comparator_->Compare(ikey.user_key, *skip) <= 0) { + // Entry hidden + } else { + valid_ = true; + saved_key_.clear(); + return; + } + break; + } + } + iter_->Next(); + } while (iter_->Valid()); + saved_key_.clear(); + valid_ = false; +} + +void DBIter::Prev() { + assert(valid_); + + if (direction_ == kForward) { // Switch directions? + // iter_ is pointing at the current entry. Scan backwards until + // the key changes so we can use the normal reverse scanning code. + assert(iter_->Valid()); // Otherwise valid_ would have been false + SaveKey(ExtractUserKey(iter_->key()), &saved_key_); + while (true) { + iter_->Prev(); + if (!iter_->Valid()) { + valid_ = false; + saved_key_.clear(); + ClearSavedValue(); + return; + } + if (user_comparator_->Compare(ExtractUserKey(iter_->key()), + saved_key_) < 0) { + break; + } + } + direction_ = kReverse; + } + + FindPrevUserEntry(); +} + +void DBIter::FindPrevUserEntry() { + assert(direction_ == kReverse); + + ValueType value_type = kTypeDeletion; + if (iter_->Valid()) { + do { + ParsedInternalKey ikey; + if (ParseKey(&ikey) && ikey.sequence <= sequence_) { + if ((value_type != kTypeDeletion) && + user_comparator_->Compare(ikey.user_key, saved_key_) < 0) { + // We encountered a non-deleted value in entries for previous keys, + break; + } + value_type = ikey.type; + if (value_type == kTypeDeletion) { + saved_key_.clear(); + ClearSavedValue(); + } else { + Slice raw_value = iter_->value(); + if (saved_value_.capacity() > raw_value.size() + 1048576) { + std::string empty; + swap(empty, saved_value_); + } + SaveKey(ExtractUserKey(iter_->key()), &saved_key_); + saved_value_.assign(raw_value.data(), raw_value.size()); + } + } + iter_->Prev(); + } while (iter_->Valid()); + } + + if (value_type == kTypeDeletion) { + // End + valid_ = false; + saved_key_.clear(); + ClearSavedValue(); + direction_ = kForward; + } else { + valid_ = true; + } +} + +void DBIter::Seek(const Slice& target) { + direction_ = kForward; + ClearSavedValue(); + saved_key_.clear(); + AppendInternalKey( + &saved_key_, ParsedInternalKey(target, sequence_, kValueTypeForSeek)); + iter_->Seek(saved_key_); + if (iter_->Valid()) { + FindNextUserEntry(false, &saved_key_ /* temporary storage */); + } else { + valid_ = false; + } +} + +void DBIter::SeekToFirst() { + direction_ = kForward; + ClearSavedValue(); + iter_->SeekToFirst(); + if (iter_->Valid()) { + FindNextUserEntry(false, &saved_key_ /* temporary storage */); + } else { + valid_ = false; + } +} + +void DBIter::SeekToLast() { + direction_ = kReverse; + ClearSavedValue(); + iter_->SeekToLast(); + FindPrevUserEntry(); +} + +} // anonymous namespace + +Iterator* NewDBIterator( + const std::string* dbname, + Env* env, + const Comparator* user_key_comparator, + Iterator* internal_iter, + const SequenceNumber& sequence) { + return new DBIter(dbname, env, user_key_comparator, internal_iter, sequence); +} + +} // namespace leveldb diff --git a/src/leveldb-lin/db/db_iter.h b/src/leveldb-lin/db/db_iter.h new file mode 100644 index 0000000..d9e1b17 --- /dev/null +++ b/src/leveldb-lin/db/db_iter.h @@ -0,0 +1,26 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_DB_ITER_H_ +#define STORAGE_LEVELDB_DB_DB_ITER_H_ + +#include +#include "leveldb/db.h" +#include "db/dbformat.h" + +namespace leveldb { + +// Return a new iterator that converts internal keys (yielded by +// "*internal_iter") that were live at the specified "sequence" number +// into appropriate user keys. +extern Iterator* NewDBIterator( + const std::string* dbname, + Env* env, + const Comparator* user_key_comparator, + Iterator* internal_iter, + const SequenceNumber& sequence); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_DB_ITER_H_ diff --git a/src/leveldb-lin/db/db_iter.o b/src/leveldb-lin/db/db_iter.o new file mode 100644 index 0000000..463b2f1 Binary files /dev/null and b/src/leveldb-lin/db/db_iter.o differ diff --git a/src/leveldb-lin/db/db_test.cc b/src/leveldb-lin/db/db_test.cc new file mode 100644 index 0000000..684ea3b --- /dev/null +++ b/src/leveldb-lin/db/db_test.cc @@ -0,0 +1,2027 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" +#include "leveldb/filter_policy.h" +#include "db/db_impl.h" +#include "db/filename.h" +#include "db/version_set.h" +#include "db/write_batch_internal.h" +#include "leveldb/cache.h" +#include "leveldb/env.h" +#include "leveldb/table.h" +#include "util/hash.h" +#include "util/logging.h" +#include "util/mutexlock.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +static std::string RandomString(Random* rnd, int len) { + std::string r; + test::RandomString(rnd, len, &r); + return r; +} + +namespace { +class AtomicCounter { + private: + port::Mutex mu_; + int count_; + public: + AtomicCounter() : count_(0) { } + void Increment() { + MutexLock l(&mu_); + count_++; + } + int Read() { + MutexLock l(&mu_); + return count_; + } + void Reset() { + MutexLock l(&mu_); + count_ = 0; + } +}; +} + +// Special Env used to delay background operations +class SpecialEnv : public EnvWrapper { + public: + // sstable Sync() calls are blocked while this pointer is non-NULL. + port::AtomicPointer delay_sstable_sync_; + + // Simulate no-space errors while this pointer is non-NULL. + port::AtomicPointer no_space_; + + // Simulate non-writable file system while this pointer is non-NULL + port::AtomicPointer non_writable_; + + // Force sync of manifest files to fail while this pointer is non-NULL + port::AtomicPointer manifest_sync_error_; + + // Force write to manifest files to fail while this pointer is non-NULL + port::AtomicPointer manifest_write_error_; + + bool count_random_reads_; + AtomicCounter random_read_counter_; + + AtomicCounter sleep_counter_; + + explicit SpecialEnv(Env* base) : EnvWrapper(base) { + delay_sstable_sync_.Release_Store(NULL); + no_space_.Release_Store(NULL); + non_writable_.Release_Store(NULL); + count_random_reads_ = false; + manifest_sync_error_.Release_Store(NULL); + manifest_write_error_.Release_Store(NULL); + } + + Status NewWritableFile(const std::string& f, WritableFile** r) { + class SSTableFile : public WritableFile { + private: + SpecialEnv* env_; + WritableFile* base_; + + public: + SSTableFile(SpecialEnv* env, WritableFile* base) + : env_(env), + base_(base) { + } + ~SSTableFile() { delete base_; } + Status Append(const Slice& data) { + if (env_->no_space_.Acquire_Load() != NULL) { + // Drop writes on the floor + return Status::OK(); + } else { + return base_->Append(data); + } + } + Status Close() { return base_->Close(); } + Status Flush() { return base_->Flush(); } + Status Sync() { + while (env_->delay_sstable_sync_.Acquire_Load() != NULL) { + env_->SleepForMicroseconds(100000); + } + return base_->Sync(); + } + }; + class ManifestFile : public WritableFile { + private: + SpecialEnv* env_; + WritableFile* base_; + public: + ManifestFile(SpecialEnv* env, WritableFile* b) : env_(env), base_(b) { } + ~ManifestFile() { delete base_; } + Status Append(const Slice& data) { + if (env_->manifest_write_error_.Acquire_Load() != NULL) { + return Status::IOError("simulated writer error"); + } else { + return base_->Append(data); + } + } + Status Close() { return base_->Close(); } + Status Flush() { return base_->Flush(); } + Status Sync() { + if (env_->manifest_sync_error_.Acquire_Load() != NULL) { + return Status::IOError("simulated sync error"); + } else { + return base_->Sync(); + } + } + }; + + if (non_writable_.Acquire_Load() != NULL) { + return Status::IOError("simulated write error"); + } + + Status s = target()->NewWritableFile(f, r); + if (s.ok()) { + if (strstr(f.c_str(), ".sst") != NULL) { + *r = new SSTableFile(this, *r); + } else if (strstr(f.c_str(), "MANIFEST") != NULL) { + *r = new ManifestFile(this, *r); + } + } + return s; + } + + Status NewRandomAccessFile(const std::string& f, RandomAccessFile** r) { + class CountingFile : public RandomAccessFile { + private: + RandomAccessFile* target_; + AtomicCounter* counter_; + public: + CountingFile(RandomAccessFile* target, AtomicCounter* counter) + : target_(target), counter_(counter) { + } + virtual ~CountingFile() { delete target_; } + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + counter_->Increment(); + return target_->Read(offset, n, result, scratch); + } + }; + + Status s = target()->NewRandomAccessFile(f, r); + if (s.ok() && count_random_reads_) { + *r = new CountingFile(*r, &random_read_counter_); + } + return s; + } + + virtual void SleepForMicroseconds(int micros) { + sleep_counter_.Increment(); + target()->SleepForMicroseconds(micros); + } +}; + +class DBTest { + private: + const FilterPolicy* filter_policy_; + + // Sequence of option configurations to try + enum OptionConfig { + kDefault, + kFilter, + kUncompressed, + kEnd + }; + int option_config_; + + public: + std::string dbname_; + SpecialEnv* env_; + DB* db_; + + Options last_options_; + + DBTest() : option_config_(kDefault), + env_(new SpecialEnv(Env::Default())) { + filter_policy_ = NewBloomFilterPolicy(10); + dbname_ = test::TmpDir() + "/db_test"; + DestroyDB(dbname_, Options()); + db_ = NULL; + Reopen(); + } + + ~DBTest() { + delete db_; + DestroyDB(dbname_, Options()); + delete env_; + delete filter_policy_; + } + + // Switch to a fresh database with the next option configuration to + // test. Return false if there are no more configurations to test. + bool ChangeOptions() { + option_config_++; + if (option_config_ >= kEnd) { + return false; + } else { + DestroyAndReopen(); + return true; + } + } + + // Return the current option configuration. + Options CurrentOptions() { + Options options; + switch (option_config_) { + case kFilter: + options.filter_policy = filter_policy_; + break; + case kUncompressed: + options.compression = kNoCompression; + break; + default: + break; + } + return options; + } + + DBImpl* dbfull() { + return reinterpret_cast(db_); + } + + void Reopen(Options* options = NULL) { + ASSERT_OK(TryReopen(options)); + } + + void Close() { + delete db_; + db_ = NULL; + } + + void DestroyAndReopen(Options* options = NULL) { + delete db_; + db_ = NULL; + DestroyDB(dbname_, Options()); + ASSERT_OK(TryReopen(options)); + } + + Status TryReopen(Options* options) { + delete db_; + db_ = NULL; + Options opts; + if (options != NULL) { + opts = *options; + } else { + opts = CurrentOptions(); + opts.create_if_missing = true; + } + last_options_ = opts; + + return DB::Open(opts, dbname_, &db_); + } + + Status Put(const std::string& k, const std::string& v) { + return db_->Put(WriteOptions(), k, v); + } + + Status Delete(const std::string& k) { + return db_->Delete(WriteOptions(), k); + } + + std::string Get(const std::string& k, const Snapshot* snapshot = NULL) { + ReadOptions options; + options.snapshot = snapshot; + std::string result; + Status s = db_->Get(options, k, &result); + if (s.IsNotFound()) { + result = "NOT_FOUND"; + } else if (!s.ok()) { + result = s.ToString(); + } + return result; + } + + // Return a string that contains all key,value pairs in order, + // formatted like "(k1->v1)(k2->v2)". + std::string Contents() { + std::vector forward; + std::string result; + Iterator* iter = db_->NewIterator(ReadOptions()); + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + std::string s = IterStatus(iter); + result.push_back('('); + result.append(s); + result.push_back(')'); + forward.push_back(s); + } + + // Check reverse iteration results are the reverse of forward results + int matched = 0; + for (iter->SeekToLast(); iter->Valid(); iter->Prev()) { + ASSERT_LT(matched, forward.size()); + ASSERT_EQ(IterStatus(iter), forward[forward.size() - matched - 1]); + matched++; + } + ASSERT_EQ(matched, forward.size()); + + delete iter; + return result; + } + + std::string AllEntriesFor(const Slice& user_key) { + Iterator* iter = dbfull()->TEST_NewInternalIterator(); + InternalKey target(user_key, kMaxSequenceNumber, kTypeValue); + iter->Seek(target.Encode()); + std::string result; + if (!iter->status().ok()) { + result = iter->status().ToString(); + } else { + result = "[ "; + bool first = true; + while (iter->Valid()) { + ParsedInternalKey ikey; + if (!ParseInternalKey(iter->key(), &ikey)) { + result += "CORRUPTED"; + } else { + if (last_options_.comparator->Compare(ikey.user_key, user_key) != 0) { + break; + } + if (!first) { + result += ", "; + } + first = false; + switch (ikey.type) { + case kTypeValue: + result += iter->value().ToString(); + break; + case kTypeDeletion: + result += "DEL"; + break; + } + } + iter->Next(); + } + if (!first) { + result += " "; + } + result += "]"; + } + delete iter; + return result; + } + + int NumTableFilesAtLevel(int level) { + std::string property; + ASSERT_TRUE( + db_->GetProperty("leveldb.num-files-at-level" + NumberToString(level), + &property)); + return atoi(property.c_str()); + } + + int TotalTableFiles() { + int result = 0; + for (int level = 0; level < config::kNumLevels; level++) { + result += NumTableFilesAtLevel(level); + } + return result; + } + + // Return spread of files per level + std::string FilesPerLevel() { + std::string result; + int last_non_zero_offset = 0; + for (int level = 0; level < config::kNumLevels; level++) { + int f = NumTableFilesAtLevel(level); + char buf[100]; + snprintf(buf, sizeof(buf), "%s%d", (level ? "," : ""), f); + result += buf; + if (f > 0) { + last_non_zero_offset = result.size(); + } + } + result.resize(last_non_zero_offset); + return result; + } + + int CountFiles() { + std::vector files; + env_->GetChildren(dbname_, &files); + return static_cast(files.size()); + } + + uint64_t Size(const Slice& start, const Slice& limit) { + Range r(start, limit); + uint64_t size; + db_->GetApproximateSizes(&r, 1, &size); + return size; + } + + void Compact(const Slice& start, const Slice& limit) { + db_->CompactRange(&start, &limit); + } + + // Do n memtable compactions, each of which produces an sstable + // covering the range [small,large]. + void MakeTables(int n, const std::string& small, const std::string& large) { + for (int i = 0; i < n; i++) { + Put(small, "begin"); + Put(large, "end"); + dbfull()->TEST_CompactMemTable(); + } + } + + // Prevent pushing of new sstables into deeper levels by adding + // tables that cover a specified range to all levels. + void FillLevels(const std::string& smallest, const std::string& largest) { + MakeTables(config::kNumLevels, smallest, largest); + } + + void DumpFileCounts(const char* label) { + fprintf(stderr, "---\n%s:\n", label); + fprintf(stderr, "maxoverlap: %lld\n", + static_cast( + dbfull()->TEST_MaxNextLevelOverlappingBytes())); + for (int level = 0; level < config::kNumLevels; level++) { + int num = NumTableFilesAtLevel(level); + if (num > 0) { + fprintf(stderr, " level %3d : %d files\n", level, num); + } + } + } + + std::string DumpSSTableList() { + std::string property; + db_->GetProperty("leveldb.sstables", &property); + return property; + } + + std::string IterStatus(Iterator* iter) { + std::string result; + if (iter->Valid()) { + result = iter->key().ToString() + "->" + iter->value().ToString(); + } else { + result = "(invalid)"; + } + return result; + } +}; + +TEST(DBTest, Empty) { + do { + ASSERT_TRUE(db_ != NULL); + ASSERT_EQ("NOT_FOUND", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, ReadWrite) { + do { + ASSERT_OK(Put("foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + ASSERT_OK(Put("bar", "v2")); + ASSERT_OK(Put("foo", "v3")); + ASSERT_EQ("v3", Get("foo")); + ASSERT_EQ("v2", Get("bar")); + } while (ChangeOptions()); +} + +TEST(DBTest, PutDeleteGet) { + do { + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2")); + ASSERT_EQ("v2", Get("foo")); + ASSERT_OK(db_->Delete(WriteOptions(), "foo")); + ASSERT_EQ("NOT_FOUND", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetFromImmutableLayer) { + do { + Options options = CurrentOptions(); + options.env = env_; + options.write_buffer_size = 100000; // Small write buffer + Reopen(&options); + + ASSERT_OK(Put("foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + + env_->delay_sstable_sync_.Release_Store(env_); // Block sync calls + Put("k1", std::string(100000, 'x')); // Fill memtable + Put("k2", std::string(100000, 'y')); // Trigger compaction + ASSERT_EQ("v1", Get("foo")); + env_->delay_sstable_sync_.Release_Store(NULL); // Release sync calls + } while (ChangeOptions()); +} + +TEST(DBTest, GetFromVersions) { + do { + ASSERT_OK(Put("foo", "v1")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v1", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetSnapshot) { + do { + // Try with both a short key and a long key + for (int i = 0; i < 2; i++) { + std::string key = (i == 0) ? std::string("foo") : std::string(200, 'x'); + ASSERT_OK(Put(key, "v1")); + const Snapshot* s1 = db_->GetSnapshot(); + ASSERT_OK(Put(key, "v2")); + ASSERT_EQ("v2", Get(key)); + ASSERT_EQ("v1", Get(key, s1)); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v2", Get(key)); + ASSERT_EQ("v1", Get(key, s1)); + db_->ReleaseSnapshot(s1); + } + } while (ChangeOptions()); +} + +TEST(DBTest, GetLevel0Ordering) { + do { + // Check that we process level-0 files in correct order. The code + // below generates two level-0 files where the earlier one comes + // before the later one in the level-0 file list since the earlier + // one has a smaller "smallest" key. + ASSERT_OK(Put("bar", "b")); + ASSERT_OK(Put("foo", "v1")); + dbfull()->TEST_CompactMemTable(); + ASSERT_OK(Put("foo", "v2")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v2", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetOrderedByLevels) { + do { + ASSERT_OK(Put("foo", "v1")); + Compact("a", "z"); + ASSERT_EQ("v1", Get("foo")); + ASSERT_OK(Put("foo", "v2")); + ASSERT_EQ("v2", Get("foo")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v2", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetPicksCorrectFile) { + do { + // Arrange to have multiple files in a non-level-0 level. + ASSERT_OK(Put("a", "va")); + Compact("a", "b"); + ASSERT_OK(Put("x", "vx")); + Compact("x", "y"); + ASSERT_OK(Put("f", "vf")); + Compact("f", "g"); + ASSERT_EQ("va", Get("a")); + ASSERT_EQ("vf", Get("f")); + ASSERT_EQ("vx", Get("x")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetEncountersEmptyLevel) { + do { + // Arrange for the following to happen: + // * sstable A in level 0 + // * nothing in level 1 + // * sstable B in level 2 + // Then do enough Get() calls to arrange for an automatic compaction + // of sstable A. A bug would cause the compaction to be marked as + // occuring at level 1 (instead of the correct level 0). + + // Step 1: First place sstables in levels 0 and 2 + int compaction_count = 0; + while (NumTableFilesAtLevel(0) == 0 || + NumTableFilesAtLevel(2) == 0) { + ASSERT_LE(compaction_count, 100) << "could not fill levels 0 and 2"; + compaction_count++; + Put("a", "begin"); + Put("z", "end"); + dbfull()->TEST_CompactMemTable(); + } + + // Step 2: clear level 1 if necessary. + dbfull()->TEST_CompactRange(1, NULL, NULL); + ASSERT_EQ(NumTableFilesAtLevel(0), 1); + ASSERT_EQ(NumTableFilesAtLevel(1), 0); + ASSERT_EQ(NumTableFilesAtLevel(2), 1); + + // Step 3: read a bunch of times + for (int i = 0; i < 1000; i++) { + ASSERT_EQ("NOT_FOUND", Get("missing")); + } + + // Step 4: Wait for compaction to finish + env_->SleepForMicroseconds(1000000); + + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + } while (ChangeOptions()); +} + +TEST(DBTest, IterEmpty) { + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek("foo"); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterSingle) { + ASSERT_OK(Put("a", "va")); + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek(""); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek("a"); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek("b"); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterMulti) { + ASSERT_OK(Put("a", "va")); + ASSERT_OK(Put("b", "vb")); + ASSERT_OK(Put("c", "vc")); + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek(""); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Seek("a"); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Seek("ax"); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Seek("b"); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Seek("z"); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + // Switch from reverse to forward + iter->SeekToLast(); + iter->Prev(); + iter->Prev(); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + + // Switch from forward to reverse + iter->SeekToFirst(); + iter->Next(); + iter->Next(); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + + // Make sure iter stays at snapshot + ASSERT_OK(Put("a", "va2")); + ASSERT_OK(Put("a2", "va3")); + ASSERT_OK(Put("b", "vb2")); + ASSERT_OK(Put("c", "vc2")); + ASSERT_OK(Delete("b")); + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterSmallAndLargeMix) { + ASSERT_OK(Put("a", "va")); + ASSERT_OK(Put("b", std::string(100000, 'b'))); + ASSERT_OK(Put("c", "vc")); + ASSERT_OK(Put("d", std::string(100000, 'd'))); + ASSERT_OK(Put("e", std::string(100000, 'e'))); + + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b')); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd')); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e')); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e')); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd')); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b')); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterMultiWithDelete) { + do { + ASSERT_OK(Put("a", "va")); + ASSERT_OK(Put("b", "vb")); + ASSERT_OK(Put("c", "vc")); + ASSERT_OK(Delete("b")); + ASSERT_EQ("NOT_FOUND", Get("b")); + + Iterator* iter = db_->NewIterator(ReadOptions()); + iter->Seek("c"); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + delete iter; + } while (ChangeOptions()); +} + +TEST(DBTest, Recover) { + do { + ASSERT_OK(Put("foo", "v1")); + ASSERT_OK(Put("baz", "v5")); + + Reopen(); + ASSERT_EQ("v1", Get("foo")); + + ASSERT_EQ("v1", Get("foo")); + ASSERT_EQ("v5", Get("baz")); + ASSERT_OK(Put("bar", "v2")); + ASSERT_OK(Put("foo", "v3")); + + Reopen(); + ASSERT_EQ("v3", Get("foo")); + ASSERT_OK(Put("foo", "v4")); + ASSERT_EQ("v4", Get("foo")); + ASSERT_EQ("v2", Get("bar")); + ASSERT_EQ("v5", Get("baz")); + } while (ChangeOptions()); +} + +TEST(DBTest, RecoveryWithEmptyLog) { + do { + ASSERT_OK(Put("foo", "v1")); + ASSERT_OK(Put("foo", "v2")); + Reopen(); + Reopen(); + ASSERT_OK(Put("foo", "v3")); + Reopen(); + ASSERT_EQ("v3", Get("foo")); + } while (ChangeOptions()); +} + +// Check that writes done during a memtable compaction are recovered +// if the database is shutdown during the memtable compaction. +TEST(DBTest, RecoverDuringMemtableCompaction) { + do { + Options options = CurrentOptions(); + options.env = env_; + options.write_buffer_size = 1000000; + Reopen(&options); + + // Trigger a long memtable compaction and reopen the database during it + ASSERT_OK(Put("foo", "v1")); // Goes to 1st log file + ASSERT_OK(Put("big1", std::string(10000000, 'x'))); // Fills memtable + ASSERT_OK(Put("big2", std::string(1000, 'y'))); // Triggers compaction + ASSERT_OK(Put("bar", "v2")); // Goes to new log file + + Reopen(&options); + ASSERT_EQ("v1", Get("foo")); + ASSERT_EQ("v2", Get("bar")); + ASSERT_EQ(std::string(10000000, 'x'), Get("big1")); + ASSERT_EQ(std::string(1000, 'y'), Get("big2")); + } while (ChangeOptions()); +} + +static std::string Key(int i) { + char buf[100]; + snprintf(buf, sizeof(buf), "key%06d", i); + return std::string(buf); +} + +TEST(DBTest, MinorCompactionsHappen) { + Options options = CurrentOptions(); + options.write_buffer_size = 10000; + Reopen(&options); + + const int N = 500; + + int starting_num_tables = TotalTableFiles(); + for (int i = 0; i < N; i++) { + ASSERT_OK(Put(Key(i), Key(i) + std::string(1000, 'v'))); + } + int ending_num_tables = TotalTableFiles(); + ASSERT_GT(ending_num_tables, starting_num_tables); + + for (int i = 0; i < N; i++) { + ASSERT_EQ(Key(i) + std::string(1000, 'v'), Get(Key(i))); + } + + Reopen(); + + for (int i = 0; i < N; i++) { + ASSERT_EQ(Key(i) + std::string(1000, 'v'), Get(Key(i))); + } +} + +TEST(DBTest, RecoverWithLargeLog) { + { + Options options = CurrentOptions(); + Reopen(&options); + ASSERT_OK(Put("big1", std::string(200000, '1'))); + ASSERT_OK(Put("big2", std::string(200000, '2'))); + ASSERT_OK(Put("small3", std::string(10, '3'))); + ASSERT_OK(Put("small4", std::string(10, '4'))); + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + } + + // Make sure that if we re-open with a small write buffer size that + // we flush table files in the middle of a large log file. + Options options = CurrentOptions(); + options.write_buffer_size = 100000; + Reopen(&options); + ASSERT_EQ(NumTableFilesAtLevel(0), 3); + ASSERT_EQ(std::string(200000, '1'), Get("big1")); + ASSERT_EQ(std::string(200000, '2'), Get("big2")); + ASSERT_EQ(std::string(10, '3'), Get("small3")); + ASSERT_EQ(std::string(10, '4'), Get("small4")); + ASSERT_GT(NumTableFilesAtLevel(0), 1); +} + +TEST(DBTest, CompactionsGenerateMultipleFiles) { + Options options = CurrentOptions(); + options.write_buffer_size = 100000000; // Large write buffer + Reopen(&options); + + Random rnd(301); + + // Write 8MB (80 values, each 100K) + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + std::vector values; + for (int i = 0; i < 80; i++) { + values.push_back(RandomString(&rnd, 100000)); + ASSERT_OK(Put(Key(i), values[i])); + } + + // Reopening moves updates to level-0 + Reopen(&options); + dbfull()->TEST_CompactRange(0, NULL, NULL); + + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + ASSERT_GT(NumTableFilesAtLevel(1), 1); + for (int i = 0; i < 80; i++) { + ASSERT_EQ(Get(Key(i)), values[i]); + } +} + +TEST(DBTest, RepeatedWritesToSameKey) { + Options options = CurrentOptions(); + options.env = env_; + options.write_buffer_size = 100000; // Small write buffer + Reopen(&options); + + // We must have at most one file per level except for level-0, + // which may have up to kL0_StopWritesTrigger files. + const int kMaxFiles = config::kNumLevels + config::kL0_StopWritesTrigger; + + Random rnd(301); + std::string value = RandomString(&rnd, 2 * options.write_buffer_size); + for (int i = 0; i < 5 * kMaxFiles; i++) { + Put("key", value); + ASSERT_LE(TotalTableFiles(), kMaxFiles); + fprintf(stderr, "after %d: %d files\n", int(i+1), TotalTableFiles()); + } +} + +TEST(DBTest, SparseMerge) { + Options options = CurrentOptions(); + options.compression = kNoCompression; + Reopen(&options); + + FillLevels("A", "Z"); + + // Suppose there is: + // small amount of data with prefix A + // large amount of data with prefix B + // small amount of data with prefix C + // and that recent updates have made small changes to all three prefixes. + // Check that we do not do a compaction that merges all of B in one shot. + const std::string value(1000, 'x'); + Put("A", "va"); + // Write approximately 100MB of "B" values + for (int i = 0; i < 100000; i++) { + char key[100]; + snprintf(key, sizeof(key), "B%010d", i); + Put(key, value); + } + Put("C", "vc"); + dbfull()->TEST_CompactMemTable(); + dbfull()->TEST_CompactRange(0, NULL, NULL); + + // Make sparse update + Put("A", "va2"); + Put("B100", "bvalue2"); + Put("C", "vc2"); + dbfull()->TEST_CompactMemTable(); + + // Compactions should not cause us to create a situation where + // a file overlaps too much data at the next level. + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); + dbfull()->TEST_CompactRange(0, NULL, NULL); + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); + dbfull()->TEST_CompactRange(1, NULL, NULL); + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); +} + +static bool Between(uint64_t val, uint64_t low, uint64_t high) { + bool result = (val >= low) && (val <= high); + if (!result) { + fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n", + (unsigned long long)(val), + (unsigned long long)(low), + (unsigned long long)(high)); + } + return result; +} + +TEST(DBTest, ApproximateSizes) { + do { + Options options = CurrentOptions(); + options.write_buffer_size = 100000000; // Large write buffer + options.compression = kNoCompression; + DestroyAndReopen(); + + ASSERT_TRUE(Between(Size("", "xyz"), 0, 0)); + Reopen(&options); + ASSERT_TRUE(Between(Size("", "xyz"), 0, 0)); + + // Write 8MB (80 values, each 100K) + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + const int N = 80; + static const int S1 = 100000; + static const int S2 = 105000; // Allow some expansion from metadata + Random rnd(301); + for (int i = 0; i < N; i++) { + ASSERT_OK(Put(Key(i), RandomString(&rnd, S1))); + } + + // 0 because GetApproximateSizes() does not account for memtable space + ASSERT_TRUE(Between(Size("", Key(50)), 0, 0)); + + // Check sizes across recovery by reopening a few times + for (int run = 0; run < 3; run++) { + Reopen(&options); + + for (int compact_start = 0; compact_start < N; compact_start += 10) { + for (int i = 0; i < N; i += 10) { + ASSERT_TRUE(Between(Size("", Key(i)), S1*i, S2*i)); + ASSERT_TRUE(Between(Size("", Key(i)+".suffix"), S1*(i+1), S2*(i+1))); + ASSERT_TRUE(Between(Size(Key(i), Key(i+10)), S1*10, S2*10)); + } + ASSERT_TRUE(Between(Size("", Key(50)), S1*50, S2*50)); + ASSERT_TRUE(Between(Size("", Key(50)+".suffix"), S1*50, S2*50)); + + std::string cstart_str = Key(compact_start); + std::string cend_str = Key(compact_start + 9); + Slice cstart = cstart_str; + Slice cend = cend_str; + dbfull()->TEST_CompactRange(0, &cstart, &cend); + } + + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + ASSERT_GT(NumTableFilesAtLevel(1), 0); + } + } while (ChangeOptions()); +} + +TEST(DBTest, ApproximateSizes_MixOfSmallAndLarge) { + do { + Options options = CurrentOptions(); + options.compression = kNoCompression; + Reopen(); + + Random rnd(301); + std::string big1 = RandomString(&rnd, 100000); + ASSERT_OK(Put(Key(0), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(1), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(2), big1)); + ASSERT_OK(Put(Key(3), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(4), big1)); + ASSERT_OK(Put(Key(5), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(6), RandomString(&rnd, 300000))); + ASSERT_OK(Put(Key(7), RandomString(&rnd, 10000))); + + // Check sizes across recovery by reopening a few times + for (int run = 0; run < 3; run++) { + Reopen(&options); + + ASSERT_TRUE(Between(Size("", Key(0)), 0, 0)); + ASSERT_TRUE(Between(Size("", Key(1)), 10000, 11000)); + ASSERT_TRUE(Between(Size("", Key(2)), 20000, 21000)); + ASSERT_TRUE(Between(Size("", Key(3)), 120000, 121000)); + ASSERT_TRUE(Between(Size("", Key(4)), 130000, 131000)); + ASSERT_TRUE(Between(Size("", Key(5)), 230000, 231000)); + ASSERT_TRUE(Between(Size("", Key(6)), 240000, 241000)); + ASSERT_TRUE(Between(Size("", Key(7)), 540000, 541000)); + ASSERT_TRUE(Between(Size("", Key(8)), 550000, 560000)); + + ASSERT_TRUE(Between(Size(Key(3), Key(5)), 110000, 111000)); + + dbfull()->TEST_CompactRange(0, NULL, NULL); + } + } while (ChangeOptions()); +} + +TEST(DBTest, IteratorPinsRef) { + Put("foo", "hello"); + + // Get iterator that will yield the current contents of the DB. + Iterator* iter = db_->NewIterator(ReadOptions()); + + // Write to force compactions + Put("foo", "newvalue1"); + for (int i = 0; i < 100; i++) { + ASSERT_OK(Put(Key(i), Key(i) + std::string(100000, 'v'))); // 100K values + } + Put("foo", "newvalue2"); + + iter->SeekToFirst(); + ASSERT_TRUE(iter->Valid()); + ASSERT_EQ("foo", iter->key().ToString()); + ASSERT_EQ("hello", iter->value().ToString()); + iter->Next(); + ASSERT_TRUE(!iter->Valid()); + delete iter; +} + +TEST(DBTest, Snapshot) { + do { + Put("foo", "v1"); + const Snapshot* s1 = db_->GetSnapshot(); + Put("foo", "v2"); + const Snapshot* s2 = db_->GetSnapshot(); + Put("foo", "v3"); + const Snapshot* s3 = db_->GetSnapshot(); + + Put("foo", "v4"); + ASSERT_EQ("v1", Get("foo", s1)); + ASSERT_EQ("v2", Get("foo", s2)); + ASSERT_EQ("v3", Get("foo", s3)); + ASSERT_EQ("v4", Get("foo")); + + db_->ReleaseSnapshot(s3); + ASSERT_EQ("v1", Get("foo", s1)); + ASSERT_EQ("v2", Get("foo", s2)); + ASSERT_EQ("v4", Get("foo")); + + db_->ReleaseSnapshot(s1); + ASSERT_EQ("v2", Get("foo", s2)); + ASSERT_EQ("v4", Get("foo")); + + db_->ReleaseSnapshot(s2); + ASSERT_EQ("v4", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, HiddenValuesAreRemoved) { + do { + Random rnd(301); + FillLevels("a", "z"); + + std::string big = RandomString(&rnd, 50000); + Put("foo", big); + Put("pastfoo", "v"); + const Snapshot* snapshot = db_->GetSnapshot(); + Put("foo", "tiny"); + Put("pastfoo2", "v2"); // Advance sequence number one more + + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + ASSERT_GT(NumTableFilesAtLevel(0), 0); + + ASSERT_EQ(big, Get("foo", snapshot)); + ASSERT_TRUE(Between(Size("", "pastfoo"), 50000, 60000)); + db_->ReleaseSnapshot(snapshot); + ASSERT_EQ(AllEntriesFor("foo"), "[ tiny, " + big + " ]"); + Slice x("x"); + dbfull()->TEST_CompactRange(0, NULL, &x); + ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]"); + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + ASSERT_GE(NumTableFilesAtLevel(1), 1); + dbfull()->TEST_CompactRange(1, NULL, &x); + ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]"); + + ASSERT_TRUE(Between(Size("", "pastfoo"), 0, 1000)); + } while (ChangeOptions()); +} + +TEST(DBTest, DeletionMarkers1) { + Put("foo", "v1"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level + + // Place a table at level last-1 to prevent merging with preceding mutation + Put("a", "begin"); + Put("z", "end"); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ(NumTableFilesAtLevel(last), 1); + ASSERT_EQ(NumTableFilesAtLevel(last-1), 1); + + Delete("foo"); + Put("foo", "v2"); + ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2 + ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]"); + Slice z("z"); + dbfull()->TEST_CompactRange(last-2, NULL, &z); + // DEL eliminated, but v1 remains because we aren't compacting that level + // (DEL can be eliminated because v2 hides v1). + ASSERT_EQ(AllEntriesFor("foo"), "[ v2, v1 ]"); + dbfull()->TEST_CompactRange(last-1, NULL, NULL); + // Merging last-1 w/ last, so we are the base level for "foo", so + // DEL is removed. (as is v1). + ASSERT_EQ(AllEntriesFor("foo"), "[ v2 ]"); +} + +TEST(DBTest, DeletionMarkers2) { + Put("foo", "v1"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level + + // Place a table at level last-1 to prevent merging with preceding mutation + Put("a", "begin"); + Put("z", "end"); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ(NumTableFilesAtLevel(last), 1); + ASSERT_EQ(NumTableFilesAtLevel(last-1), 1); + + Delete("foo"); + ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2 + ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); + dbfull()->TEST_CompactRange(last-2, NULL, NULL); + // DEL kept: "last" file overlaps + ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); + dbfull()->TEST_CompactRange(last-1, NULL, NULL); + // Merging last-1 w/ last, so we are the base level for "foo", so + // DEL is removed. (as is v1). + ASSERT_EQ(AllEntriesFor("foo"), "[ ]"); +} + +TEST(DBTest, OverlapInLevel0) { + do { + ASSERT_EQ(config::kMaxMemCompactLevel, 2) << "Fix test to match config"; + + // Fill levels 1 and 2 to disable the pushing of new memtables to levels > 0. + ASSERT_OK(Put("100", "v100")); + ASSERT_OK(Put("999", "v999")); + dbfull()->TEST_CompactMemTable(); + ASSERT_OK(Delete("100")); + ASSERT_OK(Delete("999")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("0,1,1", FilesPerLevel()); + + // Make files spanning the following ranges in level-0: + // files[0] 200 .. 900 + // files[1] 300 .. 500 + // Note that files are sorted by smallest key. + ASSERT_OK(Put("300", "v300")); + ASSERT_OK(Put("500", "v500")); + dbfull()->TEST_CompactMemTable(); + ASSERT_OK(Put("200", "v200")); + ASSERT_OK(Put("600", "v600")); + ASSERT_OK(Put("900", "v900")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("2,1,1", FilesPerLevel()); + + // Compact away the placeholder files we created initially + dbfull()->TEST_CompactRange(1, NULL, NULL); + dbfull()->TEST_CompactRange(2, NULL, NULL); + ASSERT_EQ("2", FilesPerLevel()); + + // Do a memtable compaction. Before bug-fix, the compaction would + // not detect the overlap with level-0 files and would incorrectly place + // the deletion in a deeper level. + ASSERT_OK(Delete("600")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("3", FilesPerLevel()); + ASSERT_EQ("NOT_FOUND", Get("600")); + } while (ChangeOptions()); +} + +TEST(DBTest, L0_CompactionBug_Issue44_a) { + Reopen(); + ASSERT_OK(Put("b", "v")); + Reopen(); + ASSERT_OK(Delete("b")); + ASSERT_OK(Delete("a")); + Reopen(); + ASSERT_OK(Delete("a")); + Reopen(); + ASSERT_OK(Put("a", "v")); + Reopen(); + Reopen(); + ASSERT_EQ("(a->v)", Contents()); + env_->SleepForMicroseconds(1000000); // Wait for compaction to finish + ASSERT_EQ("(a->v)", Contents()); +} + +TEST(DBTest, L0_CompactionBug_Issue44_b) { + Reopen(); + Put("",""); + Reopen(); + Delete("e"); + Put("",""); + Reopen(); + Put("c", "cv"); + Reopen(); + Put("",""); + Reopen(); + Put("",""); + env_->SleepForMicroseconds(1000000); // Wait for compaction to finish + Reopen(); + Put("d","dv"); + Reopen(); + Put("",""); + Reopen(); + Delete("d"); + Delete("b"); + Reopen(); + ASSERT_EQ("(->)(c->cv)", Contents()); + env_->SleepForMicroseconds(1000000); // Wait for compaction to finish + ASSERT_EQ("(->)(c->cv)", Contents()); +} + +TEST(DBTest, ComparatorCheck) { + class NewComparator : public Comparator { + public: + virtual const char* Name() const { return "leveldb.NewComparator"; } + virtual int Compare(const Slice& a, const Slice& b) const { + return BytewiseComparator()->Compare(a, b); + } + virtual void FindShortestSeparator(std::string* s, const Slice& l) const { + BytewiseComparator()->FindShortestSeparator(s, l); + } + virtual void FindShortSuccessor(std::string* key) const { + BytewiseComparator()->FindShortSuccessor(key); + } + }; + NewComparator cmp; + Options new_options = CurrentOptions(); + new_options.comparator = &cmp; + Status s = TryReopen(&new_options); + ASSERT_TRUE(!s.ok()); + ASSERT_TRUE(s.ToString().find("comparator") != std::string::npos) + << s.ToString(); +} + +TEST(DBTest, CustomComparator) { + class NumberComparator : public Comparator { + public: + virtual const char* Name() const { return "test.NumberComparator"; } + virtual int Compare(const Slice& a, const Slice& b) const { + return ToNumber(a) - ToNumber(b); + } + virtual void FindShortestSeparator(std::string* s, const Slice& l) const { + ToNumber(*s); // Check format + ToNumber(l); // Check format + } + virtual void FindShortSuccessor(std::string* key) const { + ToNumber(*key); // Check format + } + private: + static int ToNumber(const Slice& x) { + // Check that there are no extra characters. + ASSERT_TRUE(x.size() >= 2 && x[0] == '[' && x[x.size()-1] == ']') + << EscapeString(x); + int val; + char ignored; + ASSERT_TRUE(sscanf(x.ToString().c_str(), "[%i]%c", &val, &ignored) == 1) + << EscapeString(x); + return val; + } + }; + NumberComparator cmp; + Options new_options = CurrentOptions(); + new_options.create_if_missing = true; + new_options.comparator = &cmp; + new_options.filter_policy = NULL; // Cannot use bloom filters + new_options.write_buffer_size = 1000; // Compact more often + DestroyAndReopen(&new_options); + ASSERT_OK(Put("[10]", "ten")); + ASSERT_OK(Put("[0x14]", "twenty")); + for (int i = 0; i < 2; i++) { + ASSERT_EQ("ten", Get("[10]")); + ASSERT_EQ("ten", Get("[0xa]")); + ASSERT_EQ("twenty", Get("[20]")); + ASSERT_EQ("twenty", Get("[0x14]")); + ASSERT_EQ("NOT_FOUND", Get("[15]")); + ASSERT_EQ("NOT_FOUND", Get("[0xf]")); + Compact("[0]", "[9999]"); + } + + for (int run = 0; run < 2; run++) { + for (int i = 0; i < 1000; i++) { + char buf[100]; + snprintf(buf, sizeof(buf), "[%d]", i*10); + ASSERT_OK(Put(buf, buf)); + } + Compact("[0]", "[1000000]"); + } +} + +TEST(DBTest, ManualCompaction) { + ASSERT_EQ(config::kMaxMemCompactLevel, 2) + << "Need to update this test to match kMaxMemCompactLevel"; + + MakeTables(3, "p", "q"); + ASSERT_EQ("1,1,1", FilesPerLevel()); + + // Compaction range falls before files + Compact("", "c"); + ASSERT_EQ("1,1,1", FilesPerLevel()); + + // Compaction range falls after files + Compact("r", "z"); + ASSERT_EQ("1,1,1", FilesPerLevel()); + + // Compaction range overlaps files + Compact("p1", "p9"); + ASSERT_EQ("0,0,1", FilesPerLevel()); + + // Populate a different range + MakeTables(3, "c", "e"); + ASSERT_EQ("1,1,2", FilesPerLevel()); + + // Compact just the new range + Compact("b", "f"); + ASSERT_EQ("0,0,2", FilesPerLevel()); + + // Compact all + MakeTables(1, "a", "z"); + ASSERT_EQ("0,1,2", FilesPerLevel()); + db_->CompactRange(NULL, NULL); + ASSERT_EQ("0,0,1", FilesPerLevel()); +} + +TEST(DBTest, DBOpen_Options) { + std::string dbname = test::TmpDir() + "/db_options_test"; + DestroyDB(dbname, Options()); + + // Does not exist, and create_if_missing == false: error + DB* db = NULL; + Options opts; + opts.create_if_missing = false; + Status s = DB::Open(opts, dbname, &db); + ASSERT_TRUE(strstr(s.ToString().c_str(), "does not exist") != NULL); + ASSERT_TRUE(db == NULL); + + // Does not exist, and create_if_missing == true: OK + opts.create_if_missing = true; + s = DB::Open(opts, dbname, &db); + ASSERT_OK(s); + ASSERT_TRUE(db != NULL); + + delete db; + db = NULL; + + // Does exist, and error_if_exists == true: error + opts.create_if_missing = false; + opts.error_if_exists = true; + s = DB::Open(opts, dbname, &db); + ASSERT_TRUE(strstr(s.ToString().c_str(), "exists") != NULL); + ASSERT_TRUE(db == NULL); + + // Does exist, and error_if_exists == false: OK + opts.create_if_missing = true; + opts.error_if_exists = false; + s = DB::Open(opts, dbname, &db); + ASSERT_OK(s); + ASSERT_TRUE(db != NULL); + + delete db; + db = NULL; +} + +TEST(DBTest, Locking) { + DB* db2 = NULL; + Status s = DB::Open(CurrentOptions(), dbname_, &db2); + ASSERT_TRUE(!s.ok()) << "Locking did not prevent re-opening db"; +} + +// Check that number of files does not grow when we are out of space +TEST(DBTest, NoSpace) { + Options options = CurrentOptions(); + options.env = env_; + Reopen(&options); + + ASSERT_OK(Put("foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + Compact("a", "z"); + const int num_files = CountFiles(); + env_->no_space_.Release_Store(env_); // Force out-of-space errors + env_->sleep_counter_.Reset(); + for (int i = 0; i < 5; i++) { + for (int level = 0; level < config::kNumLevels-1; level++) { + dbfull()->TEST_CompactRange(level, NULL, NULL); + } + } + env_->no_space_.Release_Store(NULL); + ASSERT_LT(CountFiles(), num_files + 3); + + // Check that compaction attempts slept after errors + ASSERT_GE(env_->sleep_counter_.Read(), 5); +} + +TEST(DBTest, NonWritableFileSystem) { + Options options = CurrentOptions(); + options.write_buffer_size = 1000; + options.env = env_; + Reopen(&options); + ASSERT_OK(Put("foo", "v1")); + env_->non_writable_.Release_Store(env_); // Force errors for new files + std::string big(100000, 'x'); + int errors = 0; + for (int i = 0; i < 20; i++) { + fprintf(stderr, "iter %d; errors %d\n", i, errors); + if (!Put("foo", big).ok()) { + errors++; + env_->SleepForMicroseconds(100000); + } + } + ASSERT_GT(errors, 0); + env_->non_writable_.Release_Store(NULL); +} + +TEST(DBTest, ManifestWriteError) { + // Test for the following problem: + // (a) Compaction produces file F + // (b) Log record containing F is written to MANIFEST file, but Sync() fails + // (c) GC deletes F + // (d) After reopening DB, reads fail since deleted F is named in log record + + // We iterate twice. In the second iteration, everything is the + // same except the log record never makes it to the MANIFEST file. + for (int iter = 0; iter < 2; iter++) { + port::AtomicPointer* error_type = (iter == 0) + ? &env_->manifest_sync_error_ + : &env_->manifest_write_error_; + + // Insert foo=>bar mapping + Options options = CurrentOptions(); + options.env = env_; + options.create_if_missing = true; + options.error_if_exists = false; + DestroyAndReopen(&options); + ASSERT_OK(Put("foo", "bar")); + ASSERT_EQ("bar", Get("foo")); + + // Memtable compaction (will succeed) + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("bar", Get("foo")); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo=>bar is now in last level + + // Merging compaction (will fail) + error_type->Release_Store(env_); + dbfull()->TEST_CompactRange(last, NULL, NULL); // Should fail + ASSERT_EQ("bar", Get("foo")); + + // Recovery: should not lose data + error_type->Release_Store(NULL); + Reopen(&options); + ASSERT_EQ("bar", Get("foo")); + } +} + +TEST(DBTest, FilesDeletedAfterCompaction) { + ASSERT_OK(Put("foo", "v2")); + Compact("a", "z"); + const int num_files = CountFiles(); + for (int i = 0; i < 10; i++) { + ASSERT_OK(Put("foo", "v2")); + Compact("a", "z"); + } + ASSERT_EQ(CountFiles(), num_files); +} + +TEST(DBTest, BloomFilter) { + env_->count_random_reads_ = true; + Options options = CurrentOptions(); + options.env = env_; + options.block_cache = NewLRUCache(0); // Prevent cache hits + options.filter_policy = NewBloomFilterPolicy(10); + Reopen(&options); + + // Populate multiple layers + const int N = 10000; + for (int i = 0; i < N; i++) { + ASSERT_OK(Put(Key(i), Key(i))); + } + Compact("a", "z"); + for (int i = 0; i < N; i += 100) { + ASSERT_OK(Put(Key(i), Key(i))); + } + dbfull()->TEST_CompactMemTable(); + + // Prevent auto compactions triggered by seeks + env_->delay_sstable_sync_.Release_Store(env_); + + // Lookup present keys. Should rarely read from small sstable. + env_->random_read_counter_.Reset(); + for (int i = 0; i < N; i++) { + ASSERT_EQ(Key(i), Get(Key(i))); + } + int reads = env_->random_read_counter_.Read(); + fprintf(stderr, "%d present => %d reads\n", N, reads); + ASSERT_GE(reads, N); + ASSERT_LE(reads, N + 2*N/100); + + // Lookup present keys. Should rarely read from either sstable. + env_->random_read_counter_.Reset(); + for (int i = 0; i < N; i++) { + ASSERT_EQ("NOT_FOUND", Get(Key(i) + ".missing")); + } + reads = env_->random_read_counter_.Read(); + fprintf(stderr, "%d missing => %d reads\n", N, reads); + ASSERT_LE(reads, 3*N/100); + + env_->delay_sstable_sync_.Release_Store(NULL); + Close(); + delete options.block_cache; + delete options.filter_policy; +} + +// Multi-threaded test: +namespace { + +static const int kNumThreads = 4; +static const int kTestSeconds = 10; +static const int kNumKeys = 1000; + +struct MTState { + DBTest* test; + port::AtomicPointer stop; + port::AtomicPointer counter[kNumThreads]; + port::AtomicPointer thread_done[kNumThreads]; +}; + +struct MTThread { + MTState* state; + int id; +}; + +static void MTThreadBody(void* arg) { + MTThread* t = reinterpret_cast(arg); + int id = t->id; + DB* db = t->state->test->db_; + uintptr_t counter = 0; + fprintf(stderr, "... starting thread %d\n", id); + Random rnd(1000 + id); + std::string value; + char valbuf[1500]; + while (t->state->stop.Acquire_Load() == NULL) { + t->state->counter[id].Release_Store(reinterpret_cast(counter)); + + int key = rnd.Uniform(kNumKeys); + char keybuf[20]; + snprintf(keybuf, sizeof(keybuf), "%016d", key); + + if (rnd.OneIn(2)) { + // Write values of the form . + // We add some padding for force compactions. + snprintf(valbuf, sizeof(valbuf), "%d.%d.%-1000d", + key, id, static_cast(counter)); + ASSERT_OK(db->Put(WriteOptions(), Slice(keybuf), Slice(valbuf))); + } else { + // Read a value and verify that it matches the pattern written above. + Status s = db->Get(ReadOptions(), Slice(keybuf), &value); + if (s.IsNotFound()) { + // Key has not yet been written + } else { + // Check that the writer thread counter is >= the counter in the value + ASSERT_OK(s); + int k, w, c; + ASSERT_EQ(3, sscanf(value.c_str(), "%d.%d.%d", &k, &w, &c)) << value; + ASSERT_EQ(k, key); + ASSERT_GE(w, 0); + ASSERT_LT(w, kNumThreads); + ASSERT_LE(c, reinterpret_cast( + t->state->counter[w].Acquire_Load())); + } + } + counter++; + } + t->state->thread_done[id].Release_Store(t); + fprintf(stderr, "... stopping thread %d after %d ops\n", id, int(counter)); +} + +} // namespace + +TEST(DBTest, MultiThreaded) { + do { + // Initialize state + MTState mt; + mt.test = this; + mt.stop.Release_Store(0); + for (int id = 0; id < kNumThreads; id++) { + mt.counter[id].Release_Store(0); + mt.thread_done[id].Release_Store(0); + } + + // Start threads + MTThread thread[kNumThreads]; + for (int id = 0; id < kNumThreads; id++) { + thread[id].state = &mt; + thread[id].id = id; + env_->StartThread(MTThreadBody, &thread[id]); + } + + // Let them run for a while + env_->SleepForMicroseconds(kTestSeconds * 1000000); + + // Stop the threads and wait for them to finish + mt.stop.Release_Store(&mt); + for (int id = 0; id < kNumThreads; id++) { + while (mt.thread_done[id].Acquire_Load() == NULL) { + env_->SleepForMicroseconds(100000); + } + } + } while (ChangeOptions()); +} + +namespace { +typedef std::map KVMap; +} + +class ModelDB: public DB { + public: + class ModelSnapshot : public Snapshot { + public: + KVMap map_; + }; + + explicit ModelDB(const Options& options): options_(options) { } + ~ModelDB() { } + virtual Status Put(const WriteOptions& o, const Slice& k, const Slice& v) { + return DB::Put(o, k, v); + } + virtual Status Delete(const WriteOptions& o, const Slice& key) { + return DB::Delete(o, key); + } + virtual Status Get(const ReadOptions& options, + const Slice& key, std::string* value) { + assert(false); // Not implemented + return Status::NotFound(key); + } + virtual Iterator* NewIterator(const ReadOptions& options) { + if (options.snapshot == NULL) { + KVMap* saved = new KVMap; + *saved = map_; + return new ModelIter(saved, true); + } else { + const KVMap* snapshot_state = + &(reinterpret_cast(options.snapshot)->map_); + return new ModelIter(snapshot_state, false); + } + } + virtual const Snapshot* GetSnapshot() { + ModelSnapshot* snapshot = new ModelSnapshot; + snapshot->map_ = map_; + return snapshot; + } + + virtual void ReleaseSnapshot(const Snapshot* snapshot) { + delete reinterpret_cast(snapshot); + } + virtual Status Write(const WriteOptions& options, WriteBatch* batch) { + class Handler : public WriteBatch::Handler { + public: + KVMap* map_; + virtual void Put(const Slice& key, const Slice& value) { + (*map_)[key.ToString()] = value.ToString(); + } + virtual void Delete(const Slice& key) { + map_->erase(key.ToString()); + } + }; + Handler handler; + handler.map_ = &map_; + return batch->Iterate(&handler); + } + + virtual bool GetProperty(const Slice& property, std::string* value) { + return false; + } + virtual void GetApproximateSizes(const Range* r, int n, uint64_t* sizes) { + for (int i = 0; i < n; i++) { + sizes[i] = 0; + } + } + virtual void CompactRange(const Slice* start, const Slice* end) { + } + + private: + class ModelIter: public Iterator { + public: + ModelIter(const KVMap* map, bool owned) + : map_(map), owned_(owned), iter_(map_->end()) { + } + ~ModelIter() { + if (owned_) delete map_; + } + virtual bool Valid() const { return iter_ != map_->end(); } + virtual void SeekToFirst() { iter_ = map_->begin(); } + virtual void SeekToLast() { + if (map_->empty()) { + iter_ = map_->end(); + } else { + iter_ = map_->find(map_->rbegin()->first); + } + } + virtual void Seek(const Slice& k) { + iter_ = map_->lower_bound(k.ToString()); + } + virtual void Next() { ++iter_; } + virtual void Prev() { --iter_; } + virtual Slice key() const { return iter_->first; } + virtual Slice value() const { return iter_->second; } + virtual Status status() const { return Status::OK(); } + private: + const KVMap* const map_; + const bool owned_; // Do we own map_ + KVMap::const_iterator iter_; + }; + const Options options_; + KVMap map_; +}; + +static std::string RandomKey(Random* rnd) { + int len = (rnd->OneIn(3) + ? 1 // Short sometimes to encourage collisions + : (rnd->OneIn(100) ? rnd->Skewed(10) : rnd->Uniform(10))); + return test::RandomKey(rnd, len); +} + +static bool CompareIterators(int step, + DB* model, + DB* db, + const Snapshot* model_snap, + const Snapshot* db_snap) { + ReadOptions options; + options.snapshot = model_snap; + Iterator* miter = model->NewIterator(options); + options.snapshot = db_snap; + Iterator* dbiter = db->NewIterator(options); + bool ok = true; + int count = 0; + for (miter->SeekToFirst(), dbiter->SeekToFirst(); + ok && miter->Valid() && dbiter->Valid(); + miter->Next(), dbiter->Next()) { + count++; + if (miter->key().compare(dbiter->key()) != 0) { + fprintf(stderr, "step %d: Key mismatch: '%s' vs. '%s'\n", + step, + EscapeString(miter->key()).c_str(), + EscapeString(dbiter->key()).c_str()); + ok = false; + break; + } + + if (miter->value().compare(dbiter->value()) != 0) { + fprintf(stderr, "step %d: Value mismatch for key '%s': '%s' vs. '%s'\n", + step, + EscapeString(miter->key()).c_str(), + EscapeString(miter->value()).c_str(), + EscapeString(miter->value()).c_str()); + ok = false; + } + } + + if (ok) { + if (miter->Valid() != dbiter->Valid()) { + fprintf(stderr, "step %d: Mismatch at end of iterators: %d vs. %d\n", + step, miter->Valid(), dbiter->Valid()); + ok = false; + } + } + fprintf(stderr, "%d entries compared: ok=%d\n", count, ok); + delete miter; + delete dbiter; + return ok; +} + +TEST(DBTest, Randomized) { + Random rnd(test::RandomSeed()); + do { + ModelDB model(CurrentOptions()); + const int N = 10000; + const Snapshot* model_snap = NULL; + const Snapshot* db_snap = NULL; + std::string k, v; + for (int step = 0; step < N; step++) { + if (step % 100 == 0) { + fprintf(stderr, "Step %d of %d\n", step, N); + } + // TODO(sanjay): Test Get() works + int p = rnd.Uniform(100); + if (p < 45) { // Put + k = RandomKey(&rnd); + v = RandomString(&rnd, + rnd.OneIn(20) + ? 100 + rnd.Uniform(100) + : rnd.Uniform(8)); + ASSERT_OK(model.Put(WriteOptions(), k, v)); + ASSERT_OK(db_->Put(WriteOptions(), k, v)); + + } else if (p < 90) { // Delete + k = RandomKey(&rnd); + ASSERT_OK(model.Delete(WriteOptions(), k)); + ASSERT_OK(db_->Delete(WriteOptions(), k)); + + + } else { // Multi-element batch + WriteBatch b; + const int num = rnd.Uniform(8); + for (int i = 0; i < num; i++) { + if (i == 0 || !rnd.OneIn(10)) { + k = RandomKey(&rnd); + } else { + // Periodically re-use the same key from the previous iter, so + // we have multiple entries in the write batch for the same key + } + if (rnd.OneIn(2)) { + v = RandomString(&rnd, rnd.Uniform(10)); + b.Put(k, v); + } else { + b.Delete(k); + } + } + ASSERT_OK(model.Write(WriteOptions(), &b)); + ASSERT_OK(db_->Write(WriteOptions(), &b)); + } + + if ((step % 100) == 0) { + ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL)); + ASSERT_TRUE(CompareIterators(step, &model, db_, model_snap, db_snap)); + // Save a snapshot from each DB this time that we'll use next + // time we compare things, to make sure the current state is + // preserved with the snapshot + if (model_snap != NULL) model.ReleaseSnapshot(model_snap); + if (db_snap != NULL) db_->ReleaseSnapshot(db_snap); + + Reopen(); + ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL)); + + model_snap = model.GetSnapshot(); + db_snap = db_->GetSnapshot(); + } + } + if (model_snap != NULL) model.ReleaseSnapshot(model_snap); + if (db_snap != NULL) db_->ReleaseSnapshot(db_snap); + } while (ChangeOptions()); +} + +std::string MakeKey(unsigned int num) { + char buf[30]; + snprintf(buf, sizeof(buf), "%016u", num); + return std::string(buf); +} + +void BM_LogAndApply(int iters, int num_base_files) { + std::string dbname = test::TmpDir() + "/leveldb_test_benchmark"; + DestroyDB(dbname, Options()); + + DB* db = NULL; + Options opts; + opts.create_if_missing = true; + Status s = DB::Open(opts, dbname, &db); + ASSERT_OK(s); + ASSERT_TRUE(db != NULL); + + delete db; + db = NULL; + + Env* env = Env::Default(); + + port::Mutex mu; + MutexLock l(&mu); + + InternalKeyComparator cmp(BytewiseComparator()); + Options options; + VersionSet vset(dbname, &options, NULL, &cmp); + ASSERT_OK(vset.Recover()); + VersionEdit vbase; + uint64_t fnum = 1; + for (int i = 0; i < num_base_files; i++) { + InternalKey start(MakeKey(2*fnum), 1, kTypeValue); + InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion); + vbase.AddFile(2, fnum++, 1 /* file size */, start, limit); + } + ASSERT_OK(vset.LogAndApply(&vbase, &mu)); + + uint64_t start_micros = env->NowMicros(); + + for (int i = 0; i < iters; i++) { + VersionEdit vedit; + vedit.DeleteFile(2, fnum); + InternalKey start(MakeKey(2*fnum), 1, kTypeValue); + InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion); + vedit.AddFile(2, fnum++, 1 /* file size */, start, limit); + vset.LogAndApply(&vedit, &mu); + } + uint64_t stop_micros = env->NowMicros(); + unsigned int us = stop_micros - start_micros; + char buf[16]; + snprintf(buf, sizeof(buf), "%d", num_base_files); + fprintf(stderr, + "BM_LogAndApply/%-6s %8d iters : %9u us (%7.0f us / iter)\n", + buf, iters, us, ((float)us) / iters); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + if (argc > 1 && std::string(argv[1]) == "--benchmark") { + leveldb::BM_LogAndApply(1000, 1); + leveldb::BM_LogAndApply(1000, 100); + leveldb::BM_LogAndApply(1000, 10000); + leveldb::BM_LogAndApply(100, 100000); + return 0; + } + + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/db/dbformat.cc b/src/leveldb-lin/db/dbformat.cc new file mode 100644 index 0000000..28e11b3 --- /dev/null +++ b/src/leveldb-lin/db/dbformat.cc @@ -0,0 +1,140 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include "db/dbformat.h" +#include "port/port.h" +#include "util/coding.h" + +namespace leveldb { + +static uint64_t PackSequenceAndType(uint64_t seq, ValueType t) { + assert(seq <= kMaxSequenceNumber); + assert(t <= kValueTypeForSeek); + return (seq << 8) | t; +} + +void AppendInternalKey(std::string* result, const ParsedInternalKey& key) { + result->append(key.user_key.data(), key.user_key.size()); + PutFixed64(result, PackSequenceAndType(key.sequence, key.type)); +} + +std::string ParsedInternalKey::DebugString() const { + char buf[50]; + snprintf(buf, sizeof(buf), "' @ %llu : %d", + (unsigned long long) sequence, + int(type)); + std::string result = "'"; + result += user_key.ToString(); + result += buf; + return result; +} + +std::string InternalKey::DebugString() const { + std::string result; + ParsedInternalKey parsed; + if (ParseInternalKey(rep_, &parsed)) { + result = parsed.DebugString(); + } else { + result = "(bad)"; + result.append(EscapeString(rep_)); + } + return result; +} + +const char* InternalKeyComparator::Name() const { + return "leveldb.InternalKeyComparator"; +} + +int InternalKeyComparator::Compare(const Slice& akey, const Slice& bkey) const { + // Order by: + // increasing user key (according to user-supplied comparator) + // decreasing sequence number + // decreasing type (though sequence# should be enough to disambiguate) + int r = user_comparator_->Compare(ExtractUserKey(akey), ExtractUserKey(bkey)); + if (r == 0) { + const uint64_t anum = DecodeFixed64(akey.data() + akey.size() - 8); + const uint64_t bnum = DecodeFixed64(bkey.data() + bkey.size() - 8); + if (anum > bnum) { + r = -1; + } else if (anum < bnum) { + r = +1; + } + } + return r; +} + +void InternalKeyComparator::FindShortestSeparator( + std::string* start, + const Slice& limit) const { + // Attempt to shorten the user portion of the key + Slice user_start = ExtractUserKey(*start); + Slice user_limit = ExtractUserKey(limit); + std::string tmp(user_start.data(), user_start.size()); + user_comparator_->FindShortestSeparator(&tmp, user_limit); + if (tmp.size() < user_start.size() && + user_comparator_->Compare(user_start, tmp) < 0) { + // User key has become shorter physically, but larger logically. + // Tack on the earliest possible number to the shortened user key. + PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek)); + assert(this->Compare(*start, tmp) < 0); + assert(this->Compare(tmp, limit) < 0); + start->swap(tmp); + } +} + +void InternalKeyComparator::FindShortSuccessor(std::string* key) const { + Slice user_key = ExtractUserKey(*key); + std::string tmp(user_key.data(), user_key.size()); + user_comparator_->FindShortSuccessor(&tmp); + if (tmp.size() < user_key.size() && + user_comparator_->Compare(user_key, tmp) < 0) { + // User key has become shorter physically, but larger logically. + // Tack on the earliest possible number to the shortened user key. + PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek)); + assert(this->Compare(*key, tmp) < 0); + key->swap(tmp); + } +} + +const char* InternalFilterPolicy::Name() const { + return user_policy_->Name(); +} + +void InternalFilterPolicy::CreateFilter(const Slice* keys, int n, + std::string* dst) const { + // We rely on the fact that the code in table.cc does not mind us + // adjusting keys[]. + Slice* mkey = const_cast(keys); + for (int i = 0; i < n; i++) { + mkey[i] = ExtractUserKey(keys[i]); + // TODO(sanjay): Suppress dups? + } + user_policy_->CreateFilter(keys, n, dst); +} + +bool InternalFilterPolicy::KeyMayMatch(const Slice& key, const Slice& f) const { + return user_policy_->KeyMayMatch(ExtractUserKey(key), f); +} + +LookupKey::LookupKey(const Slice& user_key, SequenceNumber s) { + size_t usize = user_key.size(); + size_t needed = usize + 13; // A conservative estimate + char* dst; + if (needed <= sizeof(space_)) { + dst = space_; + } else { + dst = new char[needed]; + } + start_ = dst; + dst = EncodeVarint32(dst, usize + 8); + kstart_ = dst; + memcpy(dst, user_key.data(), usize); + dst += usize; + EncodeFixed64(dst, PackSequenceAndType(s, kValueTypeForSeek)); + dst += 8; + end_ = dst; +} + +} // namespace leveldb diff --git a/src/leveldb-lin/db/dbformat.h b/src/leveldb-lin/db/dbformat.h new file mode 100644 index 0000000..f7f64da --- /dev/null +++ b/src/leveldb-lin/db/dbformat.h @@ -0,0 +1,227 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_FORMAT_H_ +#define STORAGE_LEVELDB_DB_FORMAT_H_ + +#include +#include "leveldb/comparator.h" +#include "leveldb/db.h" +#include "leveldb/filter_policy.h" +#include "leveldb/slice.h" +#include "leveldb/table_builder.h" +#include "util/coding.h" +#include "util/logging.h" + +namespace leveldb { + +// Grouping of constants. We may want to make some of these +// parameters set via options. +namespace config { +static const int kNumLevels = 7; + +// Level-0 compaction is started when we hit this many files. +static const int kL0_CompactionTrigger = 4; + +// Soft limit on number of level-0 files. We slow down writes at this point. +static const int kL0_SlowdownWritesTrigger = 8; + +// Maximum number of level-0 files. We stop writes at this point. +static const int kL0_StopWritesTrigger = 12; + +// Maximum level to which a new compacted memtable is pushed if it +// does not create overlap. We try to push to level 2 to avoid the +// relatively expensive level 0=>1 compactions and to avoid some +// expensive manifest file operations. We do not push all the way to +// the largest level since that can generate a lot of wasted disk +// space if the same key space is being repeatedly overwritten. +static const int kMaxMemCompactLevel = 2; + +} // namespace config + +class InternalKey; + +// Value types encoded as the last component of internal keys. +// DO NOT CHANGE THESE ENUM VALUES: they are embedded in the on-disk +// data structures. +enum ValueType { + kTypeDeletion = 0x0, + kTypeValue = 0x1 +}; +// kValueTypeForSeek defines the ValueType that should be passed when +// constructing a ParsedInternalKey object for seeking to a particular +// sequence number (since we sort sequence numbers in decreasing order +// and the value type is embedded as the low 8 bits in the sequence +// number in internal keys, we need to use the highest-numbered +// ValueType, not the lowest). +static const ValueType kValueTypeForSeek = kTypeValue; + +typedef uint64_t SequenceNumber; + +// We leave eight bits empty at the bottom so a type and sequence# +// can be packed together into 64-bits. +static const SequenceNumber kMaxSequenceNumber = + ((0x1ull << 56) - 1); + +struct ParsedInternalKey { + Slice user_key; + SequenceNumber sequence; + ValueType type; + + ParsedInternalKey() { } // Intentionally left uninitialized (for speed) + ParsedInternalKey(const Slice& u, const SequenceNumber& seq, ValueType t) + : user_key(u), sequence(seq), type(t) { } + std::string DebugString() const; +}; + +// Return the length of the encoding of "key". +inline size_t InternalKeyEncodingLength(const ParsedInternalKey& key) { + return key.user_key.size() + 8; +} + +// Append the serialization of "key" to *result. +extern void AppendInternalKey(std::string* result, + const ParsedInternalKey& key); + +// Attempt to parse an internal key from "internal_key". On success, +// stores the parsed data in "*result", and returns true. +// +// On error, returns false, leaves "*result" in an undefined state. +extern bool ParseInternalKey(const Slice& internal_key, + ParsedInternalKey* result); + +// Returns the user key portion of an internal key. +inline Slice ExtractUserKey(const Slice& internal_key) { + assert(internal_key.size() >= 8); + return Slice(internal_key.data(), internal_key.size() - 8); +} + +inline ValueType ExtractValueType(const Slice& internal_key) { + assert(internal_key.size() >= 8); + const size_t n = internal_key.size(); + uint64_t num = DecodeFixed64(internal_key.data() + n - 8); + unsigned char c = num & 0xff; + return static_cast(c); +} + +// A comparator for internal keys that uses a specified comparator for +// the user key portion and breaks ties by decreasing sequence number. +class InternalKeyComparator : public Comparator { + private: + const Comparator* user_comparator_; + public: + explicit InternalKeyComparator(const Comparator* c) : user_comparator_(c) { } + virtual const char* Name() const; + virtual int Compare(const Slice& a, const Slice& b) const; + virtual void FindShortestSeparator( + std::string* start, + const Slice& limit) const; + virtual void FindShortSuccessor(std::string* key) const; + + const Comparator* user_comparator() const { return user_comparator_; } + + int Compare(const InternalKey& a, const InternalKey& b) const; +}; + +// Filter policy wrapper that converts from internal keys to user keys +class InternalFilterPolicy : public FilterPolicy { + private: + const FilterPolicy* const user_policy_; + public: + explicit InternalFilterPolicy(const FilterPolicy* p) : user_policy_(p) { } + virtual const char* Name() const; + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const; + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const; +}; + +// Modules in this directory should keep internal keys wrapped inside +// the following class instead of plain strings so that we do not +// incorrectly use string comparisons instead of an InternalKeyComparator. +class InternalKey { + private: + std::string rep_; + public: + InternalKey() { } // Leave rep_ as empty to indicate it is invalid + InternalKey(const Slice& user_key, SequenceNumber s, ValueType t) { + AppendInternalKey(&rep_, ParsedInternalKey(user_key, s, t)); + } + + void DecodeFrom(const Slice& s) { rep_.assign(s.data(), s.size()); } + Slice Encode() const { + assert(!rep_.empty()); + return rep_; + } + + Slice user_key() const { return ExtractUserKey(rep_); } + + void SetFrom(const ParsedInternalKey& p) { + rep_.clear(); + AppendInternalKey(&rep_, p); + } + + void Clear() { rep_.clear(); } + + std::string DebugString() const; +}; + +inline int InternalKeyComparator::Compare( + const InternalKey& a, const InternalKey& b) const { + return Compare(a.Encode(), b.Encode()); +} + +inline bool ParseInternalKey(const Slice& internal_key, + ParsedInternalKey* result) { + const size_t n = internal_key.size(); + if (n < 8) return false; + uint64_t num = DecodeFixed64(internal_key.data() + n - 8); + unsigned char c = num & 0xff; + result->sequence = num >> 8; + result->type = static_cast(c); + result->user_key = Slice(internal_key.data(), n - 8); + return (c <= static_cast(kTypeValue)); +} + +// A helper class useful for DBImpl::Get() +class LookupKey { + public: + // Initialize *this for looking up user_key at a snapshot with + // the specified sequence number. + LookupKey(const Slice& user_key, SequenceNumber sequence); + + ~LookupKey(); + + // Return a key suitable for lookup in a MemTable. + Slice memtable_key() const { return Slice(start_, end_ - start_); } + + // Return an internal key (suitable for passing to an internal iterator) + Slice internal_key() const { return Slice(kstart_, end_ - kstart_); } + + // Return the user key + Slice user_key() const { return Slice(kstart_, end_ - kstart_ - 8); } + + private: + // We construct a char array of the form: + // klength varint32 <-- start_ + // userkey char[klength] <-- kstart_ + // tag uint64 + // <-- end_ + // The array is a suitable MemTable key. + // The suffix starting with "userkey" can be used as an InternalKey. + const char* start_; + const char* kstart_; + const char* end_; + char space_[200]; // Avoid allocation for short keys + + // No copying allowed + LookupKey(const LookupKey&); + void operator=(const LookupKey&); +}; + +inline LookupKey::~LookupKey() { + if (start_ != space_) delete[] start_; +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_FORMAT_H_ diff --git a/src/leveldb-lin/db/dbformat.o b/src/leveldb-lin/db/dbformat.o new file mode 100644 index 0000000..4f66490 Binary files /dev/null and b/src/leveldb-lin/db/dbformat.o differ diff --git a/src/leveldb-lin/db/dbformat_test.cc b/src/leveldb-lin/db/dbformat_test.cc new file mode 100644 index 0000000..5d82f5d --- /dev/null +++ b/src/leveldb-lin/db/dbformat_test.cc @@ -0,0 +1,112 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/dbformat.h" +#include "util/logging.h" +#include "util/testharness.h" + +namespace leveldb { + +static std::string IKey(const std::string& user_key, + uint64_t seq, + ValueType vt) { + std::string encoded; + AppendInternalKey(&encoded, ParsedInternalKey(user_key, seq, vt)); + return encoded; +} + +static std::string Shorten(const std::string& s, const std::string& l) { + std::string result = s; + InternalKeyComparator(BytewiseComparator()).FindShortestSeparator(&result, l); + return result; +} + +static std::string ShortSuccessor(const std::string& s) { + std::string result = s; + InternalKeyComparator(BytewiseComparator()).FindShortSuccessor(&result); + return result; +} + +static void TestKey(const std::string& key, + uint64_t seq, + ValueType vt) { + std::string encoded = IKey(key, seq, vt); + + Slice in(encoded); + ParsedInternalKey decoded("", 0, kTypeValue); + + ASSERT_TRUE(ParseInternalKey(in, &decoded)); + ASSERT_EQ(key, decoded.user_key.ToString()); + ASSERT_EQ(seq, decoded.sequence); + ASSERT_EQ(vt, decoded.type); + + ASSERT_TRUE(!ParseInternalKey(Slice("bar"), &decoded)); +} + +class FormatTest { }; + +TEST(FormatTest, InternalKey_EncodeDecode) { + const char* keys[] = { "", "k", "hello", "longggggggggggggggggggggg" }; + const uint64_t seq[] = { + 1, 2, 3, + (1ull << 8) - 1, 1ull << 8, (1ull << 8) + 1, + (1ull << 16) - 1, 1ull << 16, (1ull << 16) + 1, + (1ull << 32) - 1, 1ull << 32, (1ull << 32) + 1 + }; + for (int k = 0; k < sizeof(keys) / sizeof(keys[0]); k++) { + for (int s = 0; s < sizeof(seq) / sizeof(seq[0]); s++) { + TestKey(keys[k], seq[s], kTypeValue); + TestKey("hello", 1, kTypeDeletion); + } + } +} + +TEST(FormatTest, InternalKeyShortSeparator) { + // When user keys are same + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 99, kTypeValue))); + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 101, kTypeValue))); + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 100, kTypeValue))); + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 100, kTypeDeletion))); + + // When user keys are misordered + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("bar", 99, kTypeValue))); + + // When user keys are different, but correctly ordered + ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek), + Shorten(IKey("foo", 100, kTypeValue), + IKey("hello", 200, kTypeValue))); + + // When start user key is prefix of limit user key + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foobar", 200, kTypeValue))); + + // When limit user key is prefix of start user key + ASSERT_EQ(IKey("foobar", 100, kTypeValue), + Shorten(IKey("foobar", 100, kTypeValue), + IKey("foo", 200, kTypeValue))); +} + +TEST(FormatTest, InternalKeyShortestSuccessor) { + ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek), + ShortSuccessor(IKey("foo", 100, kTypeValue))); + ASSERT_EQ(IKey("\xff\xff", 100, kTypeValue), + ShortSuccessor(IKey("\xff\xff", 100, kTypeValue))); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/db/filename.cc b/src/leveldb-lin/db/filename.cc new file mode 100644 index 0000000..3c4d49f --- /dev/null +++ b/src/leveldb-lin/db/filename.cc @@ -0,0 +1,139 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include "db/filename.h" +#include "db/dbformat.h" +#include "leveldb/env.h" +#include "util/logging.h" + +namespace leveldb { + +// A utility routine: write "data" to the named file and Sync() it. +extern Status WriteStringToFileSync(Env* env, const Slice& data, + const std::string& fname); + +static std::string MakeFileName(const std::string& name, uint64_t number, + const char* suffix) { + char buf[100]; + snprintf(buf, sizeof(buf), "/%06llu.%s", + static_cast(number), + suffix); + return name + buf; +} + +std::string LogFileName(const std::string& name, uint64_t number) { + assert(number > 0); + return MakeFileName(name, number, "log"); +} + +std::string TableFileName(const std::string& name, uint64_t number) { + assert(number > 0); + return MakeFileName(name, number, "sst"); +} + +std::string DescriptorFileName(const std::string& dbname, uint64_t number) { + assert(number > 0); + char buf[100]; + snprintf(buf, sizeof(buf), "/MANIFEST-%06llu", + static_cast(number)); + return dbname + buf; +} + +std::string CurrentFileName(const std::string& dbname) { + return dbname + "/CURRENT"; +} + +std::string LockFileName(const std::string& dbname) { + return dbname + "/LOCK"; +} + +std::string TempFileName(const std::string& dbname, uint64_t number) { + assert(number > 0); + return MakeFileName(dbname, number, "dbtmp"); +} + +std::string InfoLogFileName(const std::string& dbname) { + return dbname + "/LOG"; +} + +// Return the name of the old info log file for "dbname". +std::string OldInfoLogFileName(const std::string& dbname) { + return dbname + "/LOG.old"; +} + + +// Owned filenames have the form: +// dbname/CURRENT +// dbname/LOCK +// dbname/LOG +// dbname/LOG.old +// dbname/MANIFEST-[0-9]+ +// dbname/[0-9]+.(log|sst) +bool ParseFileName(const std::string& fname, + uint64_t* number, + FileType* type) { + Slice rest(fname); + if (rest == "CURRENT") { + *number = 0; + *type = kCurrentFile; + } else if (rest == "LOCK") { + *number = 0; + *type = kDBLockFile; + } else if (rest == "LOG" || rest == "LOG.old") { + *number = 0; + *type = kInfoLogFile; + } else if (rest.starts_with("MANIFEST-")) { + rest.remove_prefix(strlen("MANIFEST-")); + uint64_t num; + if (!ConsumeDecimalNumber(&rest, &num)) { + return false; + } + if (!rest.empty()) { + return false; + } + *type = kDescriptorFile; + *number = num; + } else { + // Avoid strtoull() to keep filename format independent of the + // current locale + uint64_t num; + if (!ConsumeDecimalNumber(&rest, &num)) { + return false; + } + Slice suffix = rest; + if (suffix == Slice(".log")) { + *type = kLogFile; + } else if (suffix == Slice(".sst")) { + *type = kTableFile; + } else if (suffix == Slice(".dbtmp")) { + *type = kTempFile; + } else { + return false; + } + *number = num; + } + return true; +} + +Status SetCurrentFile(Env* env, const std::string& dbname, + uint64_t descriptor_number) { + // Remove leading "dbname/" and add newline to manifest file name + std::string manifest = DescriptorFileName(dbname, descriptor_number); + Slice contents = manifest; + assert(contents.starts_with(dbname + "/")); + contents.remove_prefix(dbname.size() + 1); + std::string tmp = TempFileName(dbname, descriptor_number); + Status s = WriteStringToFileSync(env, contents.ToString() + "\n", tmp); + if (s.ok()) { + s = env->RenameFile(tmp, CurrentFileName(dbname)); + } + if (!s.ok()) { + env->DeleteFile(tmp); + } + return s; +} + +} // namespace leveldb diff --git a/src/leveldb-lin/db/filename.h b/src/leveldb-lin/db/filename.h new file mode 100644 index 0000000..d5d09b1 --- /dev/null +++ b/src/leveldb-lin/db/filename.h @@ -0,0 +1,80 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// File names used by DB code + +#ifndef STORAGE_LEVELDB_DB_FILENAME_H_ +#define STORAGE_LEVELDB_DB_FILENAME_H_ + +#include +#include +#include "leveldb/slice.h" +#include "leveldb/status.h" +#include "port/port.h" + +namespace leveldb { + +class Env; + +enum FileType { + kLogFile, + kDBLockFile, + kTableFile, + kDescriptorFile, + kCurrentFile, + kTempFile, + kInfoLogFile // Either the current one, or an old one +}; + +// Return the name of the log file with the specified number +// in the db named by "dbname". The result will be prefixed with +// "dbname". +extern std::string LogFileName(const std::string& dbname, uint64_t number); + +// Return the name of the sstable with the specified number +// in the db named by "dbname". The result will be prefixed with +// "dbname". +extern std::string TableFileName(const std::string& dbname, uint64_t number); + +// Return the name of the descriptor file for the db named by +// "dbname" and the specified incarnation number. The result will be +// prefixed with "dbname". +extern std::string DescriptorFileName(const std::string& dbname, + uint64_t number); + +// Return the name of the current file. This file contains the name +// of the current manifest file. The result will be prefixed with +// "dbname". +extern std::string CurrentFileName(const std::string& dbname); + +// Return the name of the lock file for the db named by +// "dbname". The result will be prefixed with "dbname". +extern std::string LockFileName(const std::string& dbname); + +// Return the name of a temporary file owned by the db named "dbname". +// The result will be prefixed with "dbname". +extern std::string TempFileName(const std::string& dbname, uint64_t number); + +// Return the name of the info log file for "dbname". +extern std::string InfoLogFileName(const std::string& dbname); + +// Return the name of the old info log file for "dbname". +extern std::string OldInfoLogFileName(const std::string& dbname); + +// If filename is a leveldb file, store the type of the file in *type. +// The number encoded in the filename is stored in *number. If the +// filename was successfully parsed, returns true. Else return false. +extern bool ParseFileName(const std::string& filename, + uint64_t* number, + FileType* type); + +// Make the CURRENT file point to the descriptor file with the +// specified number. +extern Status SetCurrentFile(Env* env, const std::string& dbname, + uint64_t descriptor_number); + + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_FILENAME_H_ diff --git a/src/leveldb-lin/db/filename.o b/src/leveldb-lin/db/filename.o new file mode 100644 index 0000000..29f0dbc Binary files /dev/null and b/src/leveldb-lin/db/filename.o differ diff --git a/src/leveldb-lin/db/filename_test.cc b/src/leveldb-lin/db/filename_test.cc new file mode 100644 index 0000000..47353d6 --- /dev/null +++ b/src/leveldb-lin/db/filename_test.cc @@ -0,0 +1,122 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/filename.h" + +#include "db/dbformat.h" +#include "port/port.h" +#include "util/logging.h" +#include "util/testharness.h" + +namespace leveldb { + +class FileNameTest { }; + +TEST(FileNameTest, Parse) { + Slice db; + FileType type; + uint64_t number; + + // Successful parses + static struct { + const char* fname; + uint64_t number; + FileType type; + } cases[] = { + { "100.log", 100, kLogFile }, + { "0.log", 0, kLogFile }, + { "0.sst", 0, kTableFile }, + { "CURRENT", 0, kCurrentFile }, + { "LOCK", 0, kDBLockFile }, + { "MANIFEST-2", 2, kDescriptorFile }, + { "MANIFEST-7", 7, kDescriptorFile }, + { "LOG", 0, kInfoLogFile }, + { "LOG.old", 0, kInfoLogFile }, + { "18446744073709551615.log", 18446744073709551615ull, kLogFile }, + }; + for (int i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + std::string f = cases[i].fname; + ASSERT_TRUE(ParseFileName(f, &number, &type)) << f; + ASSERT_EQ(cases[i].type, type) << f; + ASSERT_EQ(cases[i].number, number) << f; + } + + // Errors + static const char* errors[] = { + "", + "foo", + "foo-dx-100.log", + ".log", + "", + "manifest", + "CURREN", + "CURRENTX", + "MANIFES", + "MANIFEST", + "MANIFEST-", + "XMANIFEST-3", + "MANIFEST-3x", + "LOC", + "LOCKx", + "LO", + "LOGx", + "18446744073709551616.log", + "184467440737095516150.log", + "100", + "100.", + "100.lop" + }; + for (int i = 0; i < sizeof(errors) / sizeof(errors[0]); i++) { + std::string f = errors[i]; + ASSERT_TRUE(!ParseFileName(f, &number, &type)) << f; + }; +} + +TEST(FileNameTest, Construction) { + uint64_t number; + FileType type; + std::string fname; + + fname = CurrentFileName("foo"); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(0, number); + ASSERT_EQ(kCurrentFile, type); + + fname = LockFileName("foo"); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(0, number); + ASSERT_EQ(kDBLockFile, type); + + fname = LogFileName("foo", 192); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(192, number); + ASSERT_EQ(kLogFile, type); + + fname = TableFileName("bar", 200); + ASSERT_EQ("bar/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(200, number); + ASSERT_EQ(kTableFile, type); + + fname = DescriptorFileName("bar", 100); + ASSERT_EQ("bar/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(100, number); + ASSERT_EQ(kDescriptorFile, type); + + fname = TempFileName("tmp", 999); + ASSERT_EQ("tmp/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(999, number); + ASSERT_EQ(kTempFile, type); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/db/leveldb_main.cc b/src/leveldb-lin/db/leveldb_main.cc new file mode 100644 index 0000000..995d761 --- /dev/null +++ b/src/leveldb-lin/db/leveldb_main.cc @@ -0,0 +1,238 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include "db/dbformat.h" +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/version_edit.h" +#include "db/write_batch_internal.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "leveldb/options.h" +#include "leveldb/status.h" +#include "leveldb/table.h" +#include "leveldb/write_batch.h" +#include "util/logging.h" + +namespace leveldb { + +namespace { + +bool GuessType(const std::string& fname, FileType* type) { + size_t pos = fname.rfind('/'); + std::string basename; + if (pos == std::string::npos) { + basename = fname; + } else { + basename = std::string(fname.data() + pos + 1, fname.size() - pos - 1); + } + uint64_t ignored; + return ParseFileName(basename, &ignored, type); +} + +// Notified when log reader encounters corruption. +class CorruptionReporter : public log::Reader::Reporter { + public: + virtual void Corruption(size_t bytes, const Status& status) { + printf("corruption: %d bytes; %s\n", + static_cast(bytes), + status.ToString().c_str()); + } +}; + +// Print contents of a log file. (*func)() is called on every record. +bool PrintLogContents(Env* env, const std::string& fname, + void (*func)(Slice)) { + SequentialFile* file; + Status s = env->NewSequentialFile(fname, &file); + if (!s.ok()) { + fprintf(stderr, "%s\n", s.ToString().c_str()); + return false; + } + CorruptionReporter reporter; + log::Reader reader(file, &reporter, true, 0); + Slice record; + std::string scratch; + while (reader.ReadRecord(&record, &scratch)) { + printf("--- offset %llu; ", + static_cast(reader.LastRecordOffset())); + (*func)(record); + } + delete file; + return true; +} + +// Called on every item found in a WriteBatch. +class WriteBatchItemPrinter : public WriteBatch::Handler { + public: + uint64_t offset_; + uint64_t sequence_; + + virtual void Put(const Slice& key, const Slice& value) { + printf(" put '%s' '%s'\n", + EscapeString(key).c_str(), + EscapeString(value).c_str()); + } + virtual void Delete(const Slice& key) { + printf(" del '%s'\n", + EscapeString(key).c_str()); + } +}; + + +// Called on every log record (each one of which is a WriteBatch) +// found in a kLogFile. +static void WriteBatchPrinter(Slice record) { + if (record.size() < 12) { + printf("log record length %d is too small\n", + static_cast(record.size())); + return; + } + WriteBatch batch; + WriteBatchInternal::SetContents(&batch, record); + printf("sequence %llu\n", + static_cast(WriteBatchInternal::Sequence(&batch))); + WriteBatchItemPrinter batch_item_printer; + Status s = batch.Iterate(&batch_item_printer); + if (!s.ok()) { + printf(" error: %s\n", s.ToString().c_str()); + } +} + +bool DumpLog(Env* env, const std::string& fname) { + return PrintLogContents(env, fname, WriteBatchPrinter); +} + +// Called on every log record (each one of which is a WriteBatch) +// found in a kDescriptorFile. +static void VersionEditPrinter(Slice record) { + VersionEdit edit; + Status s = edit.DecodeFrom(record); + if (!s.ok()) { + printf("%s\n", s.ToString().c_str()); + return; + } + printf("%s", edit.DebugString().c_str()); +} + +bool DumpDescriptor(Env* env, const std::string& fname) { + return PrintLogContents(env, fname, VersionEditPrinter); +} + +bool DumpTable(Env* env, const std::string& fname) { + uint64_t file_size; + RandomAccessFile* file = NULL; + Table* table = NULL; + Status s = env->GetFileSize(fname, &file_size); + if (s.ok()) { + s = env->NewRandomAccessFile(fname, &file); + } + if (s.ok()) { + // We use the default comparator, which may or may not match the + // comparator used in this database. However this should not cause + // problems since we only use Table operations that do not require + // any comparisons. In particular, we do not call Seek or Prev. + s = Table::Open(Options(), file, file_size, &table); + } + if (!s.ok()) { + fprintf(stderr, "%s\n", s.ToString().c_str()); + delete table; + delete file; + return false; + } + + ReadOptions ro; + ro.fill_cache = false; + Iterator* iter = table->NewIterator(ro); + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + ParsedInternalKey key; + if (!ParseInternalKey(iter->key(), &key)) { + printf("badkey '%s' => '%s'\n", + EscapeString(iter->key()).c_str(), + EscapeString(iter->value()).c_str()); + } else { + char kbuf[20]; + const char* type; + if (key.type == kTypeDeletion) { + type = "del"; + } else if (key.type == kTypeValue) { + type = "val"; + } else { + snprintf(kbuf, sizeof(kbuf), "%d", static_cast(key.type)); + type = kbuf; + } + printf("'%s' @ %8llu : %s => '%s'\n", + EscapeString(key.user_key).c_str(), + static_cast(key.sequence), + type, + EscapeString(iter->value()).c_str()); + } + } + s = iter->status(); + if (!s.ok()) { + printf("iterator error: %s\n", s.ToString().c_str()); + } + + delete iter; + delete table; + delete file; + return true; +} + +bool DumpFile(Env* env, const std::string& fname) { + FileType ftype; + if (!GuessType(fname, &ftype)) { + fprintf(stderr, "%s: unknown file type\n", fname.c_str()); + return false; + } + switch (ftype) { + case kLogFile: return DumpLog(env, fname); + case kDescriptorFile: return DumpDescriptor(env, fname); + case kTableFile: return DumpTable(env, fname); + + default: { + fprintf(stderr, "%s: not a dump-able file type\n", fname.c_str()); + break; + } + } + return false; +} + +bool HandleDumpCommand(Env* env, char** files, int num) { + bool ok = true; + for (int i = 0; i < num; i++) { + ok &= DumpFile(env, files[i]); + } + return ok; +} + +} +} // namespace leveldb + +static void Usage() { + fprintf( + stderr, + "Usage: leveldbutil command...\n" + " dump files... -- dump contents of specified files\n" + ); +} + +int main(int argc, char** argv) { + leveldb::Env* env = leveldb::Env::Default(); + bool ok = true; + if (argc < 2) { + Usage(); + ok = false; + } else { + std::string command = argv[1]; + if (command == "dump") { + ok = leveldb::HandleDumpCommand(env, argv+2, argc-2); + } else { + Usage(); + ok = false; + } + } + return (ok ? 0 : 1); +} diff --git a/src/leveldb-lin/db/log_format.h b/src/leveldb-lin/db/log_format.h new file mode 100644 index 0000000..2690cb9 --- /dev/null +++ b/src/leveldb-lin/db/log_format.h @@ -0,0 +1,35 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Log format information shared by reader and writer. +// See ../doc/log_format.txt for more detail. + +#ifndef STORAGE_LEVELDB_DB_LOG_FORMAT_H_ +#define STORAGE_LEVELDB_DB_LOG_FORMAT_H_ + +namespace leveldb { +namespace log { + +enum RecordType { + // Zero is reserved for preallocated files + kZeroType = 0, + + kFullType = 1, + + // For fragments + kFirstType = 2, + kMiddleType = 3, + kLastType = 4 +}; +static const int kMaxRecordType = kLastType; + +static const int kBlockSize = 32768; + +// Header is checksum (4 bytes), type (1 byte), length (2 bytes). +static const int kHeaderSize = 4 + 1 + 2; + +} // namespace log +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_LOG_FORMAT_H_ diff --git a/src/leveldb-lin/db/log_reader.cc b/src/leveldb-lin/db/log_reader.cc new file mode 100644 index 0000000..b35f115 --- /dev/null +++ b/src/leveldb-lin/db/log_reader.cc @@ -0,0 +1,259 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/log_reader.h" + +#include +#include "leveldb/env.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { +namespace log { + +Reader::Reporter::~Reporter() { +} + +Reader::Reader(SequentialFile* file, Reporter* reporter, bool checksum, + uint64_t initial_offset) + : file_(file), + reporter_(reporter), + checksum_(checksum), + backing_store_(new char[kBlockSize]), + buffer_(), + eof_(false), + last_record_offset_(0), + end_of_buffer_offset_(0), + initial_offset_(initial_offset) { +} + +Reader::~Reader() { + delete[] backing_store_; +} + +bool Reader::SkipToInitialBlock() { + size_t offset_in_block = initial_offset_ % kBlockSize; + uint64_t block_start_location = initial_offset_ - offset_in_block; + + // Don't search a block if we'd be in the trailer + if (offset_in_block > kBlockSize - 6) { + offset_in_block = 0; + block_start_location += kBlockSize; + } + + end_of_buffer_offset_ = block_start_location; + + // Skip to start of first block that can contain the initial record + if (block_start_location > 0) { + Status skip_status = file_->Skip(block_start_location); + if (!skip_status.ok()) { + ReportDrop(block_start_location, skip_status); + return false; + } + } + + return true; +} + +bool Reader::ReadRecord(Slice* record, std::string* scratch) { + if (last_record_offset_ < initial_offset_) { + if (!SkipToInitialBlock()) { + return false; + } + } + + scratch->clear(); + record->clear(); + bool in_fragmented_record = false; + // Record offset of the logical record that we're reading + // 0 is a dummy value to make compilers happy + uint64_t prospective_record_offset = 0; + + Slice fragment; + while (true) { + uint64_t physical_record_offset = end_of_buffer_offset_ - buffer_.size(); + const unsigned int record_type = ReadPhysicalRecord(&fragment); + switch (record_type) { + case kFullType: + if (in_fragmented_record) { + // Handle bug in earlier versions of log::Writer where + // it could emit an empty kFirstType record at the tail end + // of a block followed by a kFullType or kFirstType record + // at the beginning of the next block. + if (scratch->empty()) { + in_fragmented_record = false; + } else { + ReportCorruption(scratch->size(), "partial record without end(1)"); + } + } + prospective_record_offset = physical_record_offset; + scratch->clear(); + *record = fragment; + last_record_offset_ = prospective_record_offset; + return true; + + case kFirstType: + if (in_fragmented_record) { + // Handle bug in earlier versions of log::Writer where + // it could emit an empty kFirstType record at the tail end + // of a block followed by a kFullType or kFirstType record + // at the beginning of the next block. + if (scratch->empty()) { + in_fragmented_record = false; + } else { + ReportCorruption(scratch->size(), "partial record without end(2)"); + } + } + prospective_record_offset = physical_record_offset; + scratch->assign(fragment.data(), fragment.size()); + in_fragmented_record = true; + break; + + case kMiddleType: + if (!in_fragmented_record) { + ReportCorruption(fragment.size(), + "missing start of fragmented record(1)"); + } else { + scratch->append(fragment.data(), fragment.size()); + } + break; + + case kLastType: + if (!in_fragmented_record) { + ReportCorruption(fragment.size(), + "missing start of fragmented record(2)"); + } else { + scratch->append(fragment.data(), fragment.size()); + *record = Slice(*scratch); + last_record_offset_ = prospective_record_offset; + return true; + } + break; + + case kEof: + if (in_fragmented_record) { + ReportCorruption(scratch->size(), "partial record without end(3)"); + scratch->clear(); + } + return false; + + case kBadRecord: + if (in_fragmented_record) { + ReportCorruption(scratch->size(), "error in middle of record"); + in_fragmented_record = false; + scratch->clear(); + } + break; + + default: { + char buf[40]; + snprintf(buf, sizeof(buf), "unknown record type %u", record_type); + ReportCorruption( + (fragment.size() + (in_fragmented_record ? scratch->size() : 0)), + buf); + in_fragmented_record = false; + scratch->clear(); + break; + } + } + } + return false; +} + +uint64_t Reader::LastRecordOffset() { + return last_record_offset_; +} + +void Reader::ReportCorruption(size_t bytes, const char* reason) { + ReportDrop(bytes, Status::Corruption(reason)); +} + +void Reader::ReportDrop(size_t bytes, const Status& reason) { + if (reporter_ != NULL && + end_of_buffer_offset_ - buffer_.size() - bytes >= initial_offset_) { + reporter_->Corruption(bytes, reason); + } +} + +unsigned int Reader::ReadPhysicalRecord(Slice* result) { + while (true) { + if (buffer_.size() < kHeaderSize) { + if (!eof_) { + // Last read was a full read, so this is a trailer to skip + buffer_.clear(); + Status status = file_->Read(kBlockSize, &buffer_, backing_store_); + end_of_buffer_offset_ += buffer_.size(); + if (!status.ok()) { + buffer_.clear(); + ReportDrop(kBlockSize, status); + eof_ = true; + return kEof; + } else if (buffer_.size() < kBlockSize) { + eof_ = true; + } + continue; + } else if (buffer_.size() == 0) { + // End of file + return kEof; + } else { + size_t drop_size = buffer_.size(); + buffer_.clear(); + ReportCorruption(drop_size, "truncated record at end of file"); + return kEof; + } + } + + // Parse the header + const char* header = buffer_.data(); + const uint32_t a = static_cast(header[4]) & 0xff; + const uint32_t b = static_cast(header[5]) & 0xff; + const unsigned int type = header[6]; + const uint32_t length = a | (b << 8); + if (kHeaderSize + length > buffer_.size()) { + size_t drop_size = buffer_.size(); + buffer_.clear(); + ReportCorruption(drop_size, "bad record length"); + return kBadRecord; + } + + if (type == kZeroType && length == 0) { + // Skip zero length record without reporting any drops since + // such records are produced by the mmap based writing code in + // env_posix.cc that preallocates file regions. + buffer_.clear(); + return kBadRecord; + } + + // Check crc + if (checksum_) { + uint32_t expected_crc = crc32c::Unmask(DecodeFixed32(header)); + uint32_t actual_crc = crc32c::Value(header + 6, 1 + length); + if (actual_crc != expected_crc) { + // Drop the rest of the buffer since "length" itself may have + // been corrupted and if we trust it, we could find some + // fragment of a real log record that just happens to look + // like a valid log record. + size_t drop_size = buffer_.size(); + buffer_.clear(); + ReportCorruption(drop_size, "checksum mismatch"); + return kBadRecord; + } + } + + buffer_.remove_prefix(kHeaderSize + length); + + // Skip physical record that started before initial_offset_ + if (end_of_buffer_offset_ - buffer_.size() - kHeaderSize - length < + initial_offset_) { + result->clear(); + return kBadRecord; + } + + *result = Slice(header + kHeaderSize, length); + return type; + } +} + +} // namespace log +} // namespace leveldb diff --git a/src/leveldb-lin/db/log_reader.h b/src/leveldb-lin/db/log_reader.h new file mode 100644 index 0000000..82d4bee --- /dev/null +++ b/src/leveldb-lin/db/log_reader.h @@ -0,0 +1,108 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_LOG_READER_H_ +#define STORAGE_LEVELDB_DB_LOG_READER_H_ + +#include + +#include "db/log_format.h" +#include "leveldb/slice.h" +#include "leveldb/status.h" + +namespace leveldb { + +class SequentialFile; + +namespace log { + +class Reader { + public: + // Interface for reporting errors. + class Reporter { + public: + virtual ~Reporter(); + + // Some corruption was detected. "size" is the approximate number + // of bytes dropped due to the corruption. + virtual void Corruption(size_t bytes, const Status& status) = 0; + }; + + // Create a reader that will return log records from "*file". + // "*file" must remain live while this Reader is in use. + // + // If "reporter" is non-NULL, it is notified whenever some data is + // dropped due to a detected corruption. "*reporter" must remain + // live while this Reader is in use. + // + // If "checksum" is true, verify checksums if available. + // + // The Reader will start reading at the first record located at physical + // position >= initial_offset within the file. + Reader(SequentialFile* file, Reporter* reporter, bool checksum, + uint64_t initial_offset); + + ~Reader(); + + // Read the next record into *record. Returns true if read + // successfully, false if we hit end of the input. May use + // "*scratch" as temporary storage. The contents filled in *record + // will only be valid until the next mutating operation on this + // reader or the next mutation to *scratch. + bool ReadRecord(Slice* record, std::string* scratch); + + // Returns the physical offset of the last record returned by ReadRecord. + // + // Undefined before the first call to ReadRecord. + uint64_t LastRecordOffset(); + + private: + SequentialFile* const file_; + Reporter* const reporter_; + bool const checksum_; + char* const backing_store_; + Slice buffer_; + bool eof_; // Last Read() indicated EOF by returning < kBlockSize + + // Offset of the last record returned by ReadRecord. + uint64_t last_record_offset_; + // Offset of the first location past the end of buffer_. + uint64_t end_of_buffer_offset_; + + // Offset at which to start looking for the first record to return + uint64_t const initial_offset_; + + // Extend record types with the following special values + enum { + kEof = kMaxRecordType + 1, + // Returned whenever we find an invalid physical record. + // Currently there are three situations in which this happens: + // * The record has an invalid CRC (ReadPhysicalRecord reports a drop) + // * The record is a 0-length record (No drop is reported) + // * The record is below constructor's initial_offset (No drop is reported) + kBadRecord = kMaxRecordType + 2 + }; + + // Skips all blocks that are completely before "initial_offset_". + // + // Returns true on success. Handles reporting. + bool SkipToInitialBlock(); + + // Return type, or one of the preceding special values + unsigned int ReadPhysicalRecord(Slice* result); + + // Reports dropped bytes to the reporter. + // buffer_ must be updated to remove the dropped bytes prior to invocation. + void ReportCorruption(size_t bytes, const char* reason); + void ReportDrop(size_t bytes, const Status& reason); + + // No copying allowed + Reader(const Reader&); + void operator=(const Reader&); +}; + +} // namespace log +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_LOG_READER_H_ diff --git a/src/leveldb-lin/db/log_reader.o b/src/leveldb-lin/db/log_reader.o new file mode 100644 index 0000000..cc097bb Binary files /dev/null and b/src/leveldb-lin/db/log_reader.o differ diff --git a/src/leveldb-lin/db/log_test.cc b/src/leveldb-lin/db/log_test.cc new file mode 100644 index 0000000..4c5cf87 --- /dev/null +++ b/src/leveldb-lin/db/log_test.cc @@ -0,0 +1,500 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "leveldb/env.h" +#include "util/coding.h" +#include "util/crc32c.h" +#include "util/random.h" +#include "util/testharness.h" + +namespace leveldb { +namespace log { + +// Construct a string of the specified length made out of the supplied +// partial string. +static std::string BigString(const std::string& partial_string, size_t n) { + std::string result; + while (result.size() < n) { + result.append(partial_string); + } + result.resize(n); + return result; +} + +// Construct a string from a number +static std::string NumberString(int n) { + char buf[50]; + snprintf(buf, sizeof(buf), "%d.", n); + return std::string(buf); +} + +// Return a skewed potentially long string +static std::string RandomSkewedString(int i, Random* rnd) { + return BigString(NumberString(i), rnd->Skewed(17)); +} + +class LogTest { + private: + class StringDest : public WritableFile { + public: + std::string contents_; + + virtual Status Close() { return Status::OK(); } + virtual Status Flush() { return Status::OK(); } + virtual Status Sync() { return Status::OK(); } + virtual Status Append(const Slice& slice) { + contents_.append(slice.data(), slice.size()); + return Status::OK(); + } + }; + + class StringSource : public SequentialFile { + public: + Slice contents_; + bool force_error_; + bool returned_partial_; + StringSource() : force_error_(false), returned_partial_(false) { } + + virtual Status Read(size_t n, Slice* result, char* scratch) { + ASSERT_TRUE(!returned_partial_) << "must not Read() after eof/error"; + + if (force_error_) { + force_error_ = false; + returned_partial_ = true; + return Status::Corruption("read error"); + } + + if (contents_.size() < n) { + n = contents_.size(); + returned_partial_ = true; + } + *result = Slice(contents_.data(), n); + contents_.remove_prefix(n); + return Status::OK(); + } + + virtual Status Skip(uint64_t n) { + if (n > contents_.size()) { + contents_.clear(); + return Status::NotFound("in-memory file skipepd past end"); + } + + contents_.remove_prefix(n); + + return Status::OK(); + } + }; + + class ReportCollector : public Reader::Reporter { + public: + size_t dropped_bytes_; + std::string message_; + + ReportCollector() : dropped_bytes_(0) { } + virtual void Corruption(size_t bytes, const Status& status) { + dropped_bytes_ += bytes; + message_.append(status.ToString()); + } + }; + + StringDest dest_; + StringSource source_; + ReportCollector report_; + bool reading_; + Writer writer_; + Reader reader_; + + // Record metadata for testing initial offset functionality + static size_t initial_offset_record_sizes_[]; + static uint64_t initial_offset_last_record_offsets_[]; + + public: + LogTest() : reading_(false), + writer_(&dest_), + reader_(&source_, &report_, true/*checksum*/, + 0/*initial_offset*/) { + } + + void Write(const std::string& msg) { + ASSERT_TRUE(!reading_) << "Write() after starting to read"; + writer_.AddRecord(Slice(msg)); + } + + size_t WrittenBytes() const { + return dest_.contents_.size(); + } + + std::string Read() { + if (!reading_) { + reading_ = true; + source_.contents_ = Slice(dest_.contents_); + } + std::string scratch; + Slice record; + if (reader_.ReadRecord(&record, &scratch)) { + return record.ToString(); + } else { + return "EOF"; + } + } + + void IncrementByte(int offset, int delta) { + dest_.contents_[offset] += delta; + } + + void SetByte(int offset, char new_byte) { + dest_.contents_[offset] = new_byte; + } + + void ShrinkSize(int bytes) { + dest_.contents_.resize(dest_.contents_.size() - bytes); + } + + void FixChecksum(int header_offset, int len) { + // Compute crc of type/len/data + uint32_t crc = crc32c::Value(&dest_.contents_[header_offset+6], 1 + len); + crc = crc32c::Mask(crc); + EncodeFixed32(&dest_.contents_[header_offset], crc); + } + + void ForceError() { + source_.force_error_ = true; + } + + size_t DroppedBytes() const { + return report_.dropped_bytes_; + } + + std::string ReportMessage() const { + return report_.message_; + } + + // Returns OK iff recorded error message contains "msg" + std::string MatchError(const std::string& msg) const { + if (report_.message_.find(msg) == std::string::npos) { + return report_.message_; + } else { + return "OK"; + } + } + + void WriteInitialOffsetLog() { + for (int i = 0; i < 4; i++) { + std::string record(initial_offset_record_sizes_[i], + static_cast('a' + i)); + Write(record); + } + } + + void CheckOffsetPastEndReturnsNoRecords(uint64_t offset_past_end) { + WriteInitialOffsetLog(); + reading_ = true; + source_.contents_ = Slice(dest_.contents_); + Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/, + WrittenBytes() + offset_past_end); + Slice record; + std::string scratch; + ASSERT_TRUE(!offset_reader->ReadRecord(&record, &scratch)); + delete offset_reader; + } + + void CheckInitialOffsetRecord(uint64_t initial_offset, + int expected_record_offset) { + WriteInitialOffsetLog(); + reading_ = true; + source_.contents_ = Slice(dest_.contents_); + Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/, + initial_offset); + Slice record; + std::string scratch; + ASSERT_TRUE(offset_reader->ReadRecord(&record, &scratch)); + ASSERT_EQ(initial_offset_record_sizes_[expected_record_offset], + record.size()); + ASSERT_EQ(initial_offset_last_record_offsets_[expected_record_offset], + offset_reader->LastRecordOffset()); + ASSERT_EQ((char)('a' + expected_record_offset), record.data()[0]); + delete offset_reader; + } + +}; + +size_t LogTest::initial_offset_record_sizes_[] = + {10000, // Two sizable records in first block + 10000, + 2 * log::kBlockSize - 1000, // Span three blocks + 1}; + +uint64_t LogTest::initial_offset_last_record_offsets_[] = + {0, + kHeaderSize + 10000, + 2 * (kHeaderSize + 10000), + 2 * (kHeaderSize + 10000) + + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize}; + + +TEST(LogTest, Empty) { + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, ReadWrite) { + Write("foo"); + Write("bar"); + Write(""); + Write("xxxx"); + ASSERT_EQ("foo", Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("", Read()); + ASSERT_EQ("xxxx", Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ("EOF", Read()); // Make sure reads at eof work +} + +TEST(LogTest, ManyBlocks) { + for (int i = 0; i < 100000; i++) { + Write(NumberString(i)); + } + for (int i = 0; i < 100000; i++) { + ASSERT_EQ(NumberString(i), Read()); + } + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, Fragmentation) { + Write("small"); + Write(BigString("medium", 50000)); + Write(BigString("large", 100000)); + ASSERT_EQ("small", Read()); + ASSERT_EQ(BigString("medium", 50000), Read()); + ASSERT_EQ(BigString("large", 100000), Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, MarginalTrailer) { + // Make a trailer that is exactly the same length as an empty record. + const int n = kBlockSize - 2*kHeaderSize; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes()); + Write(""); + Write("bar"); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("", Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, MarginalTrailer2) { + // Make a trailer that is exactly the same length as an empty record. + const int n = kBlockSize - 2*kHeaderSize; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes()); + Write("bar"); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(0, DroppedBytes()); + ASSERT_EQ("", ReportMessage()); +} + +TEST(LogTest, ShortTrailer) { + const int n = kBlockSize - 2*kHeaderSize + 4; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes()); + Write(""); + Write("bar"); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("", Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, AlignedEof) { + const int n = kBlockSize - 2*kHeaderSize + 4; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes()); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, RandomRead) { + const int N = 500; + Random write_rnd(301); + for (int i = 0; i < N; i++) { + Write(RandomSkewedString(i, &write_rnd)); + } + Random read_rnd(301); + for (int i = 0; i < N; i++) { + ASSERT_EQ(RandomSkewedString(i, &read_rnd), Read()); + } + ASSERT_EQ("EOF", Read()); +} + +// Tests of all the error paths in log_reader.cc follow: + +TEST(LogTest, ReadError) { + Write("foo"); + ForceError(); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(kBlockSize, DroppedBytes()); + ASSERT_EQ("OK", MatchError("read error")); +} + +TEST(LogTest, BadRecordType) { + Write("foo"); + // Type is stored in header[6] + IncrementByte(6, 100); + FixChecksum(0, 3); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("unknown record type")); +} + +TEST(LogTest, TruncatedTrailingRecord) { + Write("foo"); + ShrinkSize(4); // Drop all payload as well as a header byte + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(kHeaderSize - 1, DroppedBytes()); + ASSERT_EQ("OK", MatchError("truncated record at end of file")); +} + +TEST(LogTest, BadLength) { + Write("foo"); + ShrinkSize(1); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(kHeaderSize + 2, DroppedBytes()); + ASSERT_EQ("OK", MatchError("bad record length")); +} + +TEST(LogTest, ChecksumMismatch) { + Write("foo"); + IncrementByte(0, 10); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(10, DroppedBytes()); + ASSERT_EQ("OK", MatchError("checksum mismatch")); +} + +TEST(LogTest, UnexpectedMiddleType) { + Write("foo"); + SetByte(6, kMiddleType); + FixChecksum(0, 3); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("missing start")); +} + +TEST(LogTest, UnexpectedLastType) { + Write("foo"); + SetByte(6, kLastType); + FixChecksum(0, 3); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("missing start")); +} + +TEST(LogTest, UnexpectedFullType) { + Write("foo"); + Write("bar"); + SetByte(6, kFirstType); + FixChecksum(0, 3); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("partial record without end")); +} + +TEST(LogTest, UnexpectedFirstType) { + Write("foo"); + Write(BigString("bar", 100000)); + SetByte(6, kFirstType); + FixChecksum(0, 3); + ASSERT_EQ(BigString("bar", 100000), Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("partial record without end")); +} + +TEST(LogTest, ErrorJoinsRecords) { + // Consider two fragmented records: + // first(R1) last(R1) first(R2) last(R2) + // where the middle two fragments disappear. We do not want + // first(R1),last(R2) to get joined and returned as a valid record. + + // Write records that span two blocks + Write(BigString("foo", kBlockSize)); + Write(BigString("bar", kBlockSize)); + Write("correct"); + + // Wipe the middle block + for (int offset = kBlockSize; offset < 2*kBlockSize; offset++) { + SetByte(offset, 'x'); + } + + ASSERT_EQ("correct", Read()); + ASSERT_EQ("EOF", Read()); + const int dropped = DroppedBytes(); + ASSERT_LE(dropped, 2*kBlockSize + 100); + ASSERT_GE(dropped, 2*kBlockSize); +} + +TEST(LogTest, ReadStart) { + CheckInitialOffsetRecord(0, 0); +} + +TEST(LogTest, ReadSecondOneOff) { + CheckInitialOffsetRecord(1, 1); +} + +TEST(LogTest, ReadSecondTenThousand) { + CheckInitialOffsetRecord(10000, 1); +} + +TEST(LogTest, ReadSecondStart) { + CheckInitialOffsetRecord(10007, 1); +} + +TEST(LogTest, ReadThirdOneOff) { + CheckInitialOffsetRecord(10008, 2); +} + +TEST(LogTest, ReadThirdStart) { + CheckInitialOffsetRecord(20014, 2); +} + +TEST(LogTest, ReadFourthOneOff) { + CheckInitialOffsetRecord(20015, 3); +} + +TEST(LogTest, ReadFourthFirstBlockTrailer) { + CheckInitialOffsetRecord(log::kBlockSize - 4, 3); +} + +TEST(LogTest, ReadFourthMiddleBlock) { + CheckInitialOffsetRecord(log::kBlockSize + 1, 3); +} + +TEST(LogTest, ReadFourthLastBlock) { + CheckInitialOffsetRecord(2 * log::kBlockSize + 1, 3); +} + +TEST(LogTest, ReadFourthStart) { + CheckInitialOffsetRecord( + 2 * (kHeaderSize + 1000) + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize, + 3); +} + +TEST(LogTest, ReadEnd) { + CheckOffsetPastEndReturnsNoRecords(0); +} + +TEST(LogTest, ReadPastEnd) { + CheckOffsetPastEndReturnsNoRecords(5); +} + +} // namespace log +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/db/log_writer.cc b/src/leveldb-lin/db/log_writer.cc new file mode 100644 index 0000000..2da99ac --- /dev/null +++ b/src/leveldb-lin/db/log_writer.cc @@ -0,0 +1,103 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/log_writer.h" + +#include +#include "leveldb/env.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { +namespace log { + +Writer::Writer(WritableFile* dest) + : dest_(dest), + block_offset_(0) { + for (int i = 0; i <= kMaxRecordType; i++) { + char t = static_cast(i); + type_crc_[i] = crc32c::Value(&t, 1); + } +} + +Writer::~Writer() { +} + +Status Writer::AddRecord(const Slice& slice) { + const char* ptr = slice.data(); + size_t left = slice.size(); + + // Fragment the record if necessary and emit it. Note that if slice + // is empty, we still want to iterate once to emit a single + // zero-length record + Status s; + bool begin = true; + do { + const int leftover = kBlockSize - block_offset_; + assert(leftover >= 0); + if (leftover < kHeaderSize) { + // Switch to a new block + if (leftover > 0) { + // Fill the trailer (literal below relies on kHeaderSize being 7) + assert(kHeaderSize == 7); + dest_->Append(Slice("\x00\x00\x00\x00\x00\x00", leftover)); + } + block_offset_ = 0; + } + + // Invariant: we never leave < kHeaderSize bytes in a block. + assert(kBlockSize - block_offset_ - kHeaderSize >= 0); + + const size_t avail = kBlockSize - block_offset_ - kHeaderSize; + const size_t fragment_length = (left < avail) ? left : avail; + + RecordType type; + const bool end = (left == fragment_length); + if (begin && end) { + type = kFullType; + } else if (begin) { + type = kFirstType; + } else if (end) { + type = kLastType; + } else { + type = kMiddleType; + } + + s = EmitPhysicalRecord(type, ptr, fragment_length); + ptr += fragment_length; + left -= fragment_length; + begin = false; + } while (s.ok() && left > 0); + return s; +} + +Status Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) { + assert(n <= 0xffff); // Must fit in two bytes + assert(block_offset_ + kHeaderSize + n <= kBlockSize); + + // Format the header + char buf[kHeaderSize]; + buf[4] = static_cast(n & 0xff); + buf[5] = static_cast(n >> 8); + buf[6] = static_cast(t); + + // Compute the crc of the record type and the payload. + uint32_t crc = crc32c::Extend(type_crc_[t], ptr, n); + crc = crc32c::Mask(crc); // Adjust for storage + EncodeFixed32(buf, crc); + + // Write the header and the payload + Status s = dest_->Append(Slice(buf, kHeaderSize)); + if (s.ok()) { + s = dest_->Append(Slice(ptr, n)); + if (s.ok()) { + s = dest_->Flush(); + } + } + block_offset_ += kHeaderSize + n; + return s; +} + +} // namespace log +} // namespace leveldb diff --git a/src/leveldb-lin/db/log_writer.h b/src/leveldb-lin/db/log_writer.h new file mode 100644 index 0000000..a3a954d --- /dev/null +++ b/src/leveldb-lin/db/log_writer.h @@ -0,0 +1,48 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_LOG_WRITER_H_ +#define STORAGE_LEVELDB_DB_LOG_WRITER_H_ + +#include +#include "db/log_format.h" +#include "leveldb/slice.h" +#include "leveldb/status.h" + +namespace leveldb { + +class WritableFile; + +namespace log { + +class Writer { + public: + // Create a writer that will append data to "*dest". + // "*dest" must be initially empty. + // "*dest" must remain live while this Writer is in use. + explicit Writer(WritableFile* dest); + ~Writer(); + + Status AddRecord(const Slice& slice); + + private: + WritableFile* dest_; + int block_offset_; // Current offset in block + + // crc32c values for all supported record types. These are + // pre-computed to reduce the overhead of computing the crc of the + // record type stored in the header. + uint32_t type_crc_[kMaxRecordType + 1]; + + Status EmitPhysicalRecord(RecordType type, const char* ptr, size_t length); + + // No copying allowed + Writer(const Writer&); + void operator=(const Writer&); +}; + +} // namespace log +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_LOG_WRITER_H_ diff --git a/src/leveldb-lin/db/log_writer.o b/src/leveldb-lin/db/log_writer.o new file mode 100644 index 0000000..fe28d37 Binary files /dev/null and b/src/leveldb-lin/db/log_writer.o differ diff --git a/src/leveldb-lin/db/memtable.cc b/src/leveldb-lin/db/memtable.cc new file mode 100644 index 0000000..bfec0a7 --- /dev/null +++ b/src/leveldb-lin/db/memtable.cc @@ -0,0 +1,145 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/memtable.h" +#include "db/dbformat.h" +#include "leveldb/comparator.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "util/coding.h" + +namespace leveldb { + +static Slice GetLengthPrefixedSlice(const char* data) { + uint32_t len; + const char* p = data; + p = GetVarint32Ptr(p, p + 5, &len); // +5: we assume "p" is not corrupted + return Slice(p, len); +} + +MemTable::MemTable(const InternalKeyComparator& cmp) + : comparator_(cmp), + refs_(0), + table_(comparator_, &arena_) { +} + +MemTable::~MemTable() { + assert(refs_ == 0); +} + +size_t MemTable::ApproximateMemoryUsage() { return arena_.MemoryUsage(); } + +int MemTable::KeyComparator::operator()(const char* aptr, const char* bptr) + const { + // Internal keys are encoded as length-prefixed strings. + Slice a = GetLengthPrefixedSlice(aptr); + Slice b = GetLengthPrefixedSlice(bptr); + return comparator.Compare(a, b); +} + +// Encode a suitable internal key target for "target" and return it. +// Uses *scratch as scratch space, and the returned pointer will point +// into this scratch space. +static const char* EncodeKey(std::string* scratch, const Slice& target) { + scratch->clear(); + PutVarint32(scratch, target.size()); + scratch->append(target.data(), target.size()); + return scratch->data(); +} + +class MemTableIterator: public Iterator { + public: + explicit MemTableIterator(MemTable::Table* table) : iter_(table) { } + + virtual bool Valid() const { return iter_.Valid(); } + virtual void Seek(const Slice& k) { iter_.Seek(EncodeKey(&tmp_, k)); } + virtual void SeekToFirst() { iter_.SeekToFirst(); } + virtual void SeekToLast() { iter_.SeekToLast(); } + virtual void Next() { iter_.Next(); } + virtual void Prev() { iter_.Prev(); } + virtual Slice key() const { return GetLengthPrefixedSlice(iter_.key()); } + virtual Slice value() const { + Slice key_slice = GetLengthPrefixedSlice(iter_.key()); + return GetLengthPrefixedSlice(key_slice.data() + key_slice.size()); + } + + virtual Status status() const { return Status::OK(); } + + private: + MemTable::Table::Iterator iter_; + std::string tmp_; // For passing to EncodeKey + + // No copying allowed + MemTableIterator(const MemTableIterator&); + void operator=(const MemTableIterator&); +}; + +Iterator* MemTable::NewIterator() { + return new MemTableIterator(&table_); +} + +void MemTable::Add(SequenceNumber s, ValueType type, + const Slice& key, + const Slice& value) { + // Format of an entry is concatenation of: + // key_size : varint32 of internal_key.size() + // key bytes : char[internal_key.size()] + // value_size : varint32 of value.size() + // value bytes : char[value.size()] + size_t key_size = key.size(); + size_t val_size = value.size(); + size_t internal_key_size = key_size + 8; + const size_t encoded_len = + VarintLength(internal_key_size) + internal_key_size + + VarintLength(val_size) + val_size; + char* buf = arena_.Allocate(encoded_len); + char* p = EncodeVarint32(buf, internal_key_size); + memcpy(p, key.data(), key_size); + p += key_size; + EncodeFixed64(p, (s << 8) | type); + p += 8; + p = EncodeVarint32(p, val_size); + memcpy(p, value.data(), val_size); + assert((p + val_size) - buf == encoded_len); + table_.Insert(buf); +} + +bool MemTable::Get(const LookupKey& key, std::string* value, Status* s) { + Slice memkey = key.memtable_key(); + Table::Iterator iter(&table_); + iter.Seek(memkey.data()); + if (iter.Valid()) { + // entry format is: + // klength varint32 + // userkey char[klength] + // tag uint64 + // vlength varint32 + // value char[vlength] + // Check that it belongs to same user key. We do not check the + // sequence number since the Seek() call above should have skipped + // all entries with overly large sequence numbers. + const char* entry = iter.key(); + uint32_t key_length; + const char* key_ptr = GetVarint32Ptr(entry, entry+5, &key_length); + if (comparator_.comparator.user_comparator()->Compare( + Slice(key_ptr, key_length - 8), + key.user_key()) == 0) { + // Correct user key + const uint64_t tag = DecodeFixed64(key_ptr + key_length - 8); + switch (static_cast(tag & 0xff)) { + case kTypeValue: { + Slice v = GetLengthPrefixedSlice(key_ptr + key_length); + value->assign(v.data(), v.size()); + return true; + } + case kTypeDeletion: + *s = Status::NotFound(Slice()); + return true; + } + } + } + return false; +} + +} // namespace leveldb diff --git a/src/leveldb-lin/db/memtable.h b/src/leveldb-lin/db/memtable.h new file mode 100644 index 0000000..92e90bb --- /dev/null +++ b/src/leveldb-lin/db/memtable.h @@ -0,0 +1,91 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_MEMTABLE_H_ +#define STORAGE_LEVELDB_DB_MEMTABLE_H_ + +#include +#include "leveldb/db.h" +#include "db/dbformat.h" +#include "db/skiplist.h" +#include "util/arena.h" + +namespace leveldb { + +class InternalKeyComparator; +class Mutex; +class MemTableIterator; + +class MemTable { + public: + // MemTables are reference counted. The initial reference count + // is zero and the caller must call Ref() at least once. + explicit MemTable(const InternalKeyComparator& comparator); + + // Increase reference count. + void Ref() { ++refs_; } + + // Drop reference count. Delete if no more references exist. + void Unref() { + --refs_; + assert(refs_ >= 0); + if (refs_ <= 0) { + delete this; + } + } + + // Returns an estimate of the number of bytes of data in use by this + // data structure. + // + // REQUIRES: external synchronization to prevent simultaneous + // operations on the same MemTable. + size_t ApproximateMemoryUsage(); + + // Return an iterator that yields the contents of the memtable. + // + // The caller must ensure that the underlying MemTable remains live + // while the returned iterator is live. The keys returned by this + // iterator are internal keys encoded by AppendInternalKey in the + // db/format.{h,cc} module. + Iterator* NewIterator(); + + // Add an entry into memtable that maps key to value at the + // specified sequence number and with the specified type. + // Typically value will be empty if type==kTypeDeletion. + void Add(SequenceNumber seq, ValueType type, + const Slice& key, + const Slice& value); + + // If memtable contains a value for key, store it in *value and return true. + // If memtable contains a deletion for key, store a NotFound() error + // in *status and return true. + // Else, return false. + bool Get(const LookupKey& key, std::string* value, Status* s); + + private: + ~MemTable(); // Private since only Unref() should be used to delete it + + struct KeyComparator { + const InternalKeyComparator comparator; + explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) { } + int operator()(const char* a, const char* b) const; + }; + friend class MemTableIterator; + friend class MemTableBackwardIterator; + + typedef SkipList Table; + + KeyComparator comparator_; + int refs_; + Arena arena_; + Table table_; + + // No copying allowed + MemTable(const MemTable&); + void operator=(const MemTable&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_MEMTABLE_H_ diff --git a/src/leveldb-lin/db/memtable.o b/src/leveldb-lin/db/memtable.o new file mode 100644 index 0000000..805ec8b Binary files /dev/null and b/src/leveldb-lin/db/memtable.o differ diff --git a/src/leveldb-lin/db/repair.cc b/src/leveldb-lin/db/repair.cc new file mode 100644 index 0000000..022d52f --- /dev/null +++ b/src/leveldb-lin/db/repair.cc @@ -0,0 +1,389 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// We recover the contents of the descriptor from the other files we find. +// (1) Any log files are first converted to tables +// (2) We scan every table to compute +// (a) smallest/largest for the table +// (b) largest sequence number in the table +// (3) We generate descriptor contents: +// - log number is set to zero +// - next-file-number is set to 1 + largest file number we found +// - last-sequence-number is set to largest sequence# found across +// all tables (see 2c) +// - compaction pointers are cleared +// - every table file is added at level 0 +// +// Possible optimization 1: +// (a) Compute total size and use to pick appropriate max-level M +// (b) Sort tables by largest sequence# in the table +// (c) For each table: if it overlaps earlier table, place in level-0, +// else place in level-M. +// Possible optimization 2: +// Store per-table metadata (smallest, largest, largest-seq#, ...) +// in the table's meta section to speed up ScanTable. + +#include "db/builder.h" +#include "db/db_impl.h" +#include "db/dbformat.h" +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "db/memtable.h" +#include "db/table_cache.h" +#include "db/version_edit.h" +#include "db/write_batch_internal.h" +#include "leveldb/comparator.h" +#include "leveldb/db.h" +#include "leveldb/env.h" + +namespace leveldb { + +namespace { + +class Repairer { + public: + Repairer(const std::string& dbname, const Options& options) + : dbname_(dbname), + env_(options.env), + icmp_(options.comparator), + ipolicy_(options.filter_policy), + options_(SanitizeOptions(dbname, &icmp_, &ipolicy_, options)), + owns_info_log_(options_.info_log != options.info_log), + owns_cache_(options_.block_cache != options.block_cache), + next_file_number_(1) { + // TableCache can be small since we expect each table to be opened once. + table_cache_ = new TableCache(dbname_, &options_, 10); + } + + ~Repairer() { + delete table_cache_; + if (owns_info_log_) { + delete options_.info_log; + } + if (owns_cache_) { + delete options_.block_cache; + } + } + + Status Run() { + Status status = FindFiles(); + if (status.ok()) { + ConvertLogFilesToTables(); + ExtractMetaData(); + status = WriteDescriptor(); + } + if (status.ok()) { + unsigned long long bytes = 0; + for (size_t i = 0; i < tables_.size(); i++) { + bytes += tables_[i].meta.file_size; + } + Log(options_.info_log, + "**** Repaired leveldb %s; " + "recovered %d files; %llu bytes. " + "Some data may have been lost. " + "****", + dbname_.c_str(), + static_cast(tables_.size()), + bytes); + } + return status; + } + + private: + struct TableInfo { + FileMetaData meta; + SequenceNumber max_sequence; + }; + + std::string const dbname_; + Env* const env_; + InternalKeyComparator const icmp_; + InternalFilterPolicy const ipolicy_; + Options const options_; + bool owns_info_log_; + bool owns_cache_; + TableCache* table_cache_; + VersionEdit edit_; + + std::vector manifests_; + std::vector table_numbers_; + std::vector logs_; + std::vector tables_; + uint64_t next_file_number_; + + Status FindFiles() { + std::vector filenames; + Status status = env_->GetChildren(dbname_, &filenames); + if (!status.ok()) { + return status; + } + if (filenames.empty()) { + return Status::IOError(dbname_, "repair found no files"); + } + + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type)) { + if (type == kDescriptorFile) { + manifests_.push_back(filenames[i]); + } else { + if (number + 1 > next_file_number_) { + next_file_number_ = number + 1; + } + if (type == kLogFile) { + logs_.push_back(number); + } else if (type == kTableFile) { + table_numbers_.push_back(number); + } else { + // Ignore other files + } + } + } + } + return status; + } + + void ConvertLogFilesToTables() { + for (size_t i = 0; i < logs_.size(); i++) { + std::string logname = LogFileName(dbname_, logs_[i]); + Status status = ConvertLogToTable(logs_[i]); + if (!status.ok()) { + Log(options_.info_log, "Log #%llu: ignoring conversion error: %s", + (unsigned long long) logs_[i], + status.ToString().c_str()); + } + ArchiveFile(logname); + } + } + + Status ConvertLogToTable(uint64_t log) { + struct LogReporter : public log::Reader::Reporter { + Env* env; + Logger* info_log; + uint64_t lognum; + virtual void Corruption(size_t bytes, const Status& s) { + // We print error messages for corruption, but continue repairing. + Log(info_log, "Log #%llu: dropping %d bytes; %s", + (unsigned long long) lognum, + static_cast(bytes), + s.ToString().c_str()); + } + }; + + // Open the log file + std::string logname = LogFileName(dbname_, log); + SequentialFile* lfile; + Status status = env_->NewSequentialFile(logname, &lfile); + if (!status.ok()) { + return status; + } + + // Create the log reader. + LogReporter reporter; + reporter.env = env_; + reporter.info_log = options_.info_log; + reporter.lognum = log; + // We intentially make log::Reader do checksumming so that + // corruptions cause entire commits to be skipped instead of + // propagating bad information (like overly large sequence + // numbers). + log::Reader reader(lfile, &reporter, false/*do not checksum*/, + 0/*initial_offset*/); + + // Read all the records and add to a memtable + std::string scratch; + Slice record; + WriteBatch batch; + MemTable* mem = new MemTable(icmp_); + mem->Ref(); + int counter = 0; + while (reader.ReadRecord(&record, &scratch)) { + if (record.size() < 12) { + reporter.Corruption( + record.size(), Status::Corruption("log record too small")); + continue; + } + WriteBatchInternal::SetContents(&batch, record); + status = WriteBatchInternal::InsertInto(&batch, mem); + if (status.ok()) { + counter += WriteBatchInternal::Count(&batch); + } else { + Log(options_.info_log, "Log #%llu: ignoring %s", + (unsigned long long) log, + status.ToString().c_str()); + status = Status::OK(); // Keep going with rest of file + } + } + delete lfile; + + // Do not record a version edit for this conversion to a Table + // since ExtractMetaData() will also generate edits. + FileMetaData meta; + meta.number = next_file_number_++; + Iterator* iter = mem->NewIterator(); + status = BuildTable(dbname_, env_, options_, table_cache_, iter, &meta); + delete iter; + mem->Unref(); + mem = NULL; + if (status.ok()) { + if (meta.file_size > 0) { + table_numbers_.push_back(meta.number); + } + } + Log(options_.info_log, "Log #%llu: %d ops saved to Table #%llu %s", + (unsigned long long) log, + counter, + (unsigned long long) meta.number, + status.ToString().c_str()); + return status; + } + + void ExtractMetaData() { + std::vector kept; + for (size_t i = 0; i < table_numbers_.size(); i++) { + TableInfo t; + t.meta.number = table_numbers_[i]; + Status status = ScanTable(&t); + if (!status.ok()) { + std::string fname = TableFileName(dbname_, table_numbers_[i]); + Log(options_.info_log, "Table #%llu: ignoring %s", + (unsigned long long) table_numbers_[i], + status.ToString().c_str()); + ArchiveFile(fname); + } else { + tables_.push_back(t); + } + } + } + + Status ScanTable(TableInfo* t) { + std::string fname = TableFileName(dbname_, t->meta.number); + int counter = 0; + Status status = env_->GetFileSize(fname, &t->meta.file_size); + if (status.ok()) { + Iterator* iter = table_cache_->NewIterator( + ReadOptions(), t->meta.number, t->meta.file_size); + bool empty = true; + ParsedInternalKey parsed; + t->max_sequence = 0; + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + Slice key = iter->key(); + if (!ParseInternalKey(key, &parsed)) { + Log(options_.info_log, "Table #%llu: unparsable key %s", + (unsigned long long) t->meta.number, + EscapeString(key).c_str()); + continue; + } + + counter++; + if (empty) { + empty = false; + t->meta.smallest.DecodeFrom(key); + } + t->meta.largest.DecodeFrom(key); + if (parsed.sequence > t->max_sequence) { + t->max_sequence = parsed.sequence; + } + } + if (!iter->status().ok()) { + status = iter->status(); + } + delete iter; + } + Log(options_.info_log, "Table #%llu: %d entries %s", + (unsigned long long) t->meta.number, + counter, + status.ToString().c_str()); + return status; + } + + Status WriteDescriptor() { + std::string tmp = TempFileName(dbname_, 1); + WritableFile* file; + Status status = env_->NewWritableFile(tmp, &file); + if (!status.ok()) { + return status; + } + + SequenceNumber max_sequence = 0; + for (size_t i = 0; i < tables_.size(); i++) { + if (max_sequence < tables_[i].max_sequence) { + max_sequence = tables_[i].max_sequence; + } + } + + edit_.SetComparatorName(icmp_.user_comparator()->Name()); + edit_.SetLogNumber(0); + edit_.SetNextFile(next_file_number_); + edit_.SetLastSequence(max_sequence); + + for (size_t i = 0; i < tables_.size(); i++) { + // TODO(opt): separate out into multiple levels + const TableInfo& t = tables_[i]; + edit_.AddFile(0, t.meta.number, t.meta.file_size, + t.meta.smallest, t.meta.largest); + } + + //fprintf(stderr, "NewDescriptor:\n%s\n", edit_.DebugString().c_str()); + { + log::Writer log(file); + std::string record; + edit_.EncodeTo(&record); + status = log.AddRecord(record); + } + if (status.ok()) { + status = file->Close(); + } + delete file; + file = NULL; + + if (!status.ok()) { + env_->DeleteFile(tmp); + } else { + // Discard older manifests + for (size_t i = 0; i < manifests_.size(); i++) { + ArchiveFile(dbname_ + "/" + manifests_[i]); + } + + // Install new manifest + status = env_->RenameFile(tmp, DescriptorFileName(dbname_, 1)); + if (status.ok()) { + status = SetCurrentFile(env_, dbname_, 1); + } else { + env_->DeleteFile(tmp); + } + } + return status; + } + + void ArchiveFile(const std::string& fname) { + // Move into another directory. E.g., for + // dir/foo + // rename to + // dir/lost/foo + const char* slash = strrchr(fname.c_str(), '/'); + std::string new_dir; + if (slash != NULL) { + new_dir.assign(fname.data(), slash - fname.data()); + } + new_dir.append("/lost"); + env_->CreateDir(new_dir); // Ignore error + std::string new_file = new_dir; + new_file.append("/"); + new_file.append((slash == NULL) ? fname.c_str() : slash + 1); + Status s = env_->RenameFile(fname, new_file); + Log(options_.info_log, "Archiving %s: %s\n", + fname.c_str(), s.ToString().c_str()); + } +}; +} // namespace + +Status RepairDB(const std::string& dbname, const Options& options) { + Repairer repairer(dbname, options); + return repairer.Run(); +} + +} // namespace leveldb diff --git a/src/leveldb-lin/db/repair.o b/src/leveldb-lin/db/repair.o new file mode 100644 index 0000000..4d82aca Binary files /dev/null and b/src/leveldb-lin/db/repair.o differ diff --git a/src/leveldb-lin/db/skiplist.h b/src/leveldb-lin/db/skiplist.h new file mode 100644 index 0000000..af85be6 --- /dev/null +++ b/src/leveldb-lin/db/skiplist.h @@ -0,0 +1,379 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Thread safety +// ------------- +// +// Writes require external synchronization, most likely a mutex. +// Reads require a guarantee that the SkipList will not be destroyed +// while the read is in progress. Apart from that, reads progress +// without any internal locking or synchronization. +// +// Invariants: +// +// (1) Allocated nodes are never deleted until the SkipList is +// destroyed. This is trivially guaranteed by the code since we +// never delete any skip list nodes. +// +// (2) The contents of a Node except for the next/prev pointers are +// immutable after the Node has been linked into the SkipList. +// Only Insert() modifies the list, and it is careful to initialize +// a node and use release-stores to publish the nodes in one or +// more lists. +// +// ... prev vs. next pointer ordering ... + +#include +#include +#include "port/port.h" +#include "util/arena.h" +#include "util/random.h" + +namespace leveldb { + +class Arena; + +template +class SkipList { + private: + struct Node; + + public: + // Create a new SkipList object that will use "cmp" for comparing keys, + // and will allocate memory using "*arena". Objects allocated in the arena + // must remain allocated for the lifetime of the skiplist object. + explicit SkipList(Comparator cmp, Arena* arena); + + // Insert key into the list. + // REQUIRES: nothing that compares equal to key is currently in the list. + void Insert(const Key& key); + + // Returns true iff an entry that compares equal to key is in the list. + bool Contains(const Key& key) const; + + // Iteration over the contents of a skip list + class Iterator { + public: + // Initialize an iterator over the specified list. + // The returned iterator is not valid. + explicit Iterator(const SkipList* list); + + // Returns true iff the iterator is positioned at a valid node. + bool Valid() const; + + // Returns the key at the current position. + // REQUIRES: Valid() + const Key& key() const; + + // Advances to the next position. + // REQUIRES: Valid() + void Next(); + + // Advances to the previous position. + // REQUIRES: Valid() + void Prev(); + + // Advance to the first entry with a key >= target + void Seek(const Key& target); + + // Position at the first entry in list. + // Final state of iterator is Valid() iff list is not empty. + void SeekToFirst(); + + // Position at the last entry in list. + // Final state of iterator is Valid() iff list is not empty. + void SeekToLast(); + + private: + const SkipList* list_; + Node* node_; + // Intentionally copyable + }; + + private: + enum { kMaxHeight = 12 }; + + // Immutable after construction + Comparator const compare_; + Arena* const arena_; // Arena used for allocations of nodes + + Node* const head_; + + // Modified only by Insert(). Read racily by readers, but stale + // values are ok. + port::AtomicPointer max_height_; // Height of the entire list + + inline int GetMaxHeight() const { + return static_cast( + reinterpret_cast(max_height_.NoBarrier_Load())); + } + + // Read/written only by Insert(). + Random rnd_; + + Node* NewNode(const Key& key, int height); + int RandomHeight(); + bool Equal(const Key& a, const Key& b) const { return (compare_(a, b) == 0); } + + // Return true if key is greater than the data stored in "n" + bool KeyIsAfterNode(const Key& key, Node* n) const; + + // Return the earliest node that comes at or after key. + // Return NULL if there is no such node. + // + // If prev is non-NULL, fills prev[level] with pointer to previous + // node at "level" for every level in [0..max_height_-1]. + Node* FindGreaterOrEqual(const Key& key, Node** prev) const; + + // Return the latest node with a key < key. + // Return head_ if there is no such node. + Node* FindLessThan(const Key& key) const; + + // Return the last node in the list. + // Return head_ if list is empty. + Node* FindLast() const; + + // No copying allowed + SkipList(const SkipList&); + void operator=(const SkipList&); +}; + +// Implementation details follow +template +struct SkipList::Node { + explicit Node(const Key& k) : key(k) { } + + Key const key; + + // Accessors/mutators for links. Wrapped in methods so we can + // add the appropriate barriers as necessary. + Node* Next(int n) { + assert(n >= 0); + // Use an 'acquire load' so that we observe a fully initialized + // version of the returned Node. + return reinterpret_cast(next_[n].Acquire_Load()); + } + void SetNext(int n, Node* x) { + assert(n >= 0); + // Use a 'release store' so that anybody who reads through this + // pointer observes a fully initialized version of the inserted node. + next_[n].Release_Store(x); + } + + // No-barrier variants that can be safely used in a few locations. + Node* NoBarrier_Next(int n) { + assert(n >= 0); + return reinterpret_cast(next_[n].NoBarrier_Load()); + } + void NoBarrier_SetNext(int n, Node* x) { + assert(n >= 0); + next_[n].NoBarrier_Store(x); + } + + private: + // Array of length equal to the node height. next_[0] is lowest level link. + port::AtomicPointer next_[1]; +}; + +template +typename SkipList::Node* +SkipList::NewNode(const Key& key, int height) { + char* mem = arena_->AllocateAligned( + sizeof(Node) + sizeof(port::AtomicPointer) * (height - 1)); + return new (mem) Node(key); +} + +template +inline SkipList::Iterator::Iterator(const SkipList* list) { + list_ = list; + node_ = NULL; +} + +template +inline bool SkipList::Iterator::Valid() const { + return node_ != NULL; +} + +template +inline const Key& SkipList::Iterator::key() const { + assert(Valid()); + return node_->key; +} + +template +inline void SkipList::Iterator::Next() { + assert(Valid()); + node_ = node_->Next(0); +} + +template +inline void SkipList::Iterator::Prev() { + // Instead of using explicit "prev" links, we just search for the + // last node that falls before key. + assert(Valid()); + node_ = list_->FindLessThan(node_->key); + if (node_ == list_->head_) { + node_ = NULL; + } +} + +template +inline void SkipList::Iterator::Seek(const Key& target) { + node_ = list_->FindGreaterOrEqual(target, NULL); +} + +template +inline void SkipList::Iterator::SeekToFirst() { + node_ = list_->head_->Next(0); +} + +template +inline void SkipList::Iterator::SeekToLast() { + node_ = list_->FindLast(); + if (node_ == list_->head_) { + node_ = NULL; + } +} + +template +int SkipList::RandomHeight() { + // Increase height with probability 1 in kBranching + static const unsigned int kBranching = 4; + int height = 1; + while (height < kMaxHeight && ((rnd_.Next() % kBranching) == 0)) { + height++; + } + assert(height > 0); + assert(height <= kMaxHeight); + return height; +} + +template +bool SkipList::KeyIsAfterNode(const Key& key, Node* n) const { + // NULL n is considered infinite + return (n != NULL) && (compare_(n->key, key) < 0); +} + +template +typename SkipList::Node* SkipList::FindGreaterOrEqual(const Key& key, Node** prev) + const { + Node* x = head_; + int level = GetMaxHeight() - 1; + while (true) { + Node* next = x->Next(level); + if (KeyIsAfterNode(key, next)) { + // Keep searching in this list + x = next; + } else { + if (prev != NULL) prev[level] = x; + if (level == 0) { + return next; + } else { + // Switch to next list + level--; + } + } + } +} + +template +typename SkipList::Node* +SkipList::FindLessThan(const Key& key) const { + Node* x = head_; + int level = GetMaxHeight() - 1; + while (true) { + assert(x == head_ || compare_(x->key, key) < 0); + Node* next = x->Next(level); + if (next == NULL || compare_(next->key, key) >= 0) { + if (level == 0) { + return x; + } else { + // Switch to next list + level--; + } + } else { + x = next; + } + } +} + +template +typename SkipList::Node* SkipList::FindLast() + const { + Node* x = head_; + int level = GetMaxHeight() - 1; + while (true) { + Node* next = x->Next(level); + if (next == NULL) { + if (level == 0) { + return x; + } else { + // Switch to next list + level--; + } + } else { + x = next; + } + } +} + +template +SkipList::SkipList(Comparator cmp, Arena* arena) + : compare_(cmp), + arena_(arena), + head_(NewNode(0 /* any key will do */, kMaxHeight)), + max_height_(reinterpret_cast(1)), + rnd_(0xdeadbeef) { + for (int i = 0; i < kMaxHeight; i++) { + head_->SetNext(i, NULL); + } +} + +template +void SkipList::Insert(const Key& key) { + // TODO(opt): We can use a barrier-free variant of FindGreaterOrEqual() + // here since Insert() is externally synchronized. + Node* prev[kMaxHeight]; + Node* x = FindGreaterOrEqual(key, prev); + + // Our data structure does not allow duplicate insertion + assert(x == NULL || !Equal(key, x->key)); + + int height = RandomHeight(); + if (height > GetMaxHeight()) { + for (int i = GetMaxHeight(); i < height; i++) { + prev[i] = head_; + } + //fprintf(stderr, "Change height from %d to %d\n", max_height_, height); + + // It is ok to mutate max_height_ without any synchronization + // with concurrent readers. A concurrent reader that observes + // the new value of max_height_ will see either the old value of + // new level pointers from head_ (NULL), or a new value set in + // the loop below. In the former case the reader will + // immediately drop to the next level since NULL sorts after all + // keys. In the latter case the reader will use the new node. + max_height_.NoBarrier_Store(reinterpret_cast(height)); + } + + x = NewNode(key, height); + for (int i = 0; i < height; i++) { + // NoBarrier_SetNext() suffices since we will add a barrier when + // we publish a pointer to "x" in prev[i]. + x->NoBarrier_SetNext(i, prev[i]->NoBarrier_Next(i)); + prev[i]->SetNext(i, x); + } +} + +template +bool SkipList::Contains(const Key& key) const { + Node* x = FindGreaterOrEqual(key, NULL); + if (x != NULL && Equal(key, x->key)) { + return true; + } else { + return false; + } +} + +} // namespace leveldb diff --git a/src/leveldb-lin/db/skiplist_test.cc b/src/leveldb-lin/db/skiplist_test.cc new file mode 100644 index 0000000..c78f4b4 --- /dev/null +++ b/src/leveldb-lin/db/skiplist_test.cc @@ -0,0 +1,378 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/skiplist.h" +#include +#include "leveldb/env.h" +#include "util/arena.h" +#include "util/hash.h" +#include "util/random.h" +#include "util/testharness.h" + +namespace leveldb { + +typedef uint64_t Key; + +struct Comparator { + int operator()(const Key& a, const Key& b) const { + if (a < b) { + return -1; + } else if (a > b) { + return +1; + } else { + return 0; + } + } +}; + +class SkipTest { }; + +TEST(SkipTest, Empty) { + Arena arena; + Comparator cmp; + SkipList list(cmp, &arena); + ASSERT_TRUE(!list.Contains(10)); + + SkipList::Iterator iter(&list); + ASSERT_TRUE(!iter.Valid()); + iter.SeekToFirst(); + ASSERT_TRUE(!iter.Valid()); + iter.Seek(100); + ASSERT_TRUE(!iter.Valid()); + iter.SeekToLast(); + ASSERT_TRUE(!iter.Valid()); +} + +TEST(SkipTest, InsertAndLookup) { + const int N = 2000; + const int R = 5000; + Random rnd(1000); + std::set keys; + Arena arena; + Comparator cmp; + SkipList list(cmp, &arena); + for (int i = 0; i < N; i++) { + Key key = rnd.Next() % R; + if (keys.insert(key).second) { + list.Insert(key); + } + } + + for (int i = 0; i < R; i++) { + if (list.Contains(i)) { + ASSERT_EQ(keys.count(i), 1); + } else { + ASSERT_EQ(keys.count(i), 0); + } + } + + // Simple iterator tests + { + SkipList::Iterator iter(&list); + ASSERT_TRUE(!iter.Valid()); + + iter.Seek(0); + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*(keys.begin()), iter.key()); + + iter.SeekToFirst(); + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*(keys.begin()), iter.key()); + + iter.SeekToLast(); + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*(keys.rbegin()), iter.key()); + } + + // Forward iteration test + for (int i = 0; i < R; i++) { + SkipList::Iterator iter(&list); + iter.Seek(i); + + // Compare against model iterator + std::set::iterator model_iter = keys.lower_bound(i); + for (int j = 0; j < 3; j++) { + if (model_iter == keys.end()) { + ASSERT_TRUE(!iter.Valid()); + break; + } else { + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*model_iter, iter.key()); + ++model_iter; + iter.Next(); + } + } + } + + // Backward iteration test + { + SkipList::Iterator iter(&list); + iter.SeekToLast(); + + // Compare against model iterator + for (std::set::reverse_iterator model_iter = keys.rbegin(); + model_iter != keys.rend(); + ++model_iter) { + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*model_iter, iter.key()); + iter.Prev(); + } + ASSERT_TRUE(!iter.Valid()); + } +} + +// We want to make sure that with a single writer and multiple +// concurrent readers (with no synchronization other than when a +// reader's iterator is created), the reader always observes all the +// data that was present in the skip list when the iterator was +// constructor. Because insertions are happening concurrently, we may +// also observe new values that were inserted since the iterator was +// constructed, but we should never miss any values that were present +// at iterator construction time. +// +// We generate multi-part keys: +// +// where: +// key is in range [0..K-1] +// gen is a generation number for key +// hash is hash(key,gen) +// +// The insertion code picks a random key, sets gen to be 1 + the last +// generation number inserted for that key, and sets hash to Hash(key,gen). +// +// At the beginning of a read, we snapshot the last inserted +// generation number for each key. We then iterate, including random +// calls to Next() and Seek(). For every key we encounter, we +// check that it is either expected given the initial snapshot or has +// been concurrently added since the iterator started. +class ConcurrentTest { + private: + static const uint32_t K = 4; + + static uint64_t key(Key key) { return (key >> 40); } + static uint64_t gen(Key key) { return (key >> 8) & 0xffffffffu; } + static uint64_t hash(Key key) { return key & 0xff; } + + static uint64_t HashNumbers(uint64_t k, uint64_t g) { + uint64_t data[2] = { k, g }; + return Hash(reinterpret_cast(data), sizeof(data), 0); + } + + static Key MakeKey(uint64_t k, uint64_t g) { + assert(sizeof(Key) == sizeof(uint64_t)); + assert(k <= K); // We sometimes pass K to seek to the end of the skiplist + assert(g <= 0xffffffffu); + return ((k << 40) | (g << 8) | (HashNumbers(k, g) & 0xff)); + } + + static bool IsValidKey(Key k) { + return hash(k) == (HashNumbers(key(k), gen(k)) & 0xff); + } + + static Key RandomTarget(Random* rnd) { + switch (rnd->Next() % 10) { + case 0: + // Seek to beginning + return MakeKey(0, 0); + case 1: + // Seek to end + return MakeKey(K, 0); + default: + // Seek to middle + return MakeKey(rnd->Next() % K, 0); + } + } + + // Per-key generation + struct State { + port::AtomicPointer generation[K]; + void Set(int k, intptr_t v) { + generation[k].Release_Store(reinterpret_cast(v)); + } + intptr_t Get(int k) { + return reinterpret_cast(generation[k].Acquire_Load()); + } + + State() { + for (int k = 0; k < K; k++) { + Set(k, 0); + } + } + }; + + // Current state of the test + State current_; + + Arena arena_; + + // SkipList is not protected by mu_. We just use a single writer + // thread to modify it. + SkipList list_; + + public: + ConcurrentTest() : list_(Comparator(), &arena_) { } + + // REQUIRES: External synchronization + void WriteStep(Random* rnd) { + const uint32_t k = rnd->Next() % K; + const intptr_t g = current_.Get(k) + 1; + const Key key = MakeKey(k, g); + list_.Insert(key); + current_.Set(k, g); + } + + void ReadStep(Random* rnd) { + // Remember the initial committed state of the skiplist. + State initial_state; + for (int k = 0; k < K; k++) { + initial_state.Set(k, current_.Get(k)); + } + + Key pos = RandomTarget(rnd); + SkipList::Iterator iter(&list_); + iter.Seek(pos); + while (true) { + Key current; + if (!iter.Valid()) { + current = MakeKey(K, 0); + } else { + current = iter.key(); + ASSERT_TRUE(IsValidKey(current)) << current; + } + ASSERT_LE(pos, current) << "should not go backwards"; + + // Verify that everything in [pos,current) was not present in + // initial_state. + while (pos < current) { + ASSERT_LT(key(pos), K) << pos; + + // Note that generation 0 is never inserted, so it is ok if + // <*,0,*> is missing. + ASSERT_TRUE((gen(pos) == 0) || + (gen(pos) > initial_state.Get(key(pos))) + ) << "key: " << key(pos) + << "; gen: " << gen(pos) + << "; initgen: " + << initial_state.Get(key(pos)); + + // Advance to next key in the valid key space + if (key(pos) < key(current)) { + pos = MakeKey(key(pos) + 1, 0); + } else { + pos = MakeKey(key(pos), gen(pos) + 1); + } + } + + if (!iter.Valid()) { + break; + } + + if (rnd->Next() % 2) { + iter.Next(); + pos = MakeKey(key(pos), gen(pos) + 1); + } else { + Key new_target = RandomTarget(rnd); + if (new_target > pos) { + pos = new_target; + iter.Seek(new_target); + } + } + } + } +}; +const uint32_t ConcurrentTest::K; + +// Simple test that does single-threaded testing of the ConcurrentTest +// scaffolding. +TEST(SkipTest, ConcurrentWithoutThreads) { + ConcurrentTest test; + Random rnd(test::RandomSeed()); + for (int i = 0; i < 10000; i++) { + test.ReadStep(&rnd); + test.WriteStep(&rnd); + } +} + +class TestState { + public: + ConcurrentTest t_; + int seed_; + port::AtomicPointer quit_flag_; + + enum ReaderState { + STARTING, + RUNNING, + DONE + }; + + explicit TestState(int s) + : seed_(s), + quit_flag_(NULL), + state_(STARTING), + state_cv_(&mu_) {} + + void Wait(ReaderState s) { + mu_.Lock(); + while (state_ != s) { + state_cv_.Wait(); + } + mu_.Unlock(); + } + + void Change(ReaderState s) { + mu_.Lock(); + state_ = s; + state_cv_.Signal(); + mu_.Unlock(); + } + + private: + port::Mutex mu_; + ReaderState state_; + port::CondVar state_cv_; +}; + +static void ConcurrentReader(void* arg) { + TestState* state = reinterpret_cast(arg); + Random rnd(state->seed_); + int64_t reads = 0; + state->Change(TestState::RUNNING); + while (!state->quit_flag_.Acquire_Load()) { + state->t_.ReadStep(&rnd); + ++reads; + } + state->Change(TestState::DONE); +} + +static void RunConcurrent(int run) { + const int seed = test::RandomSeed() + (run * 100); + Random rnd(seed); + const int N = 1000; + const int kSize = 1000; + for (int i = 0; i < N; i++) { + if ((i % 100) == 0) { + fprintf(stderr, "Run %d of %d\n", i, N); + } + TestState state(seed + 1); + Env::Default()->Schedule(ConcurrentReader, &state); + state.Wait(TestState::RUNNING); + for (int i = 0; i < kSize; i++) { + state.t_.WriteStep(&rnd); + } + state.quit_flag_.Release_Store(&state); // Any non-NULL arg will do + state.Wait(TestState::DONE); + } +} + +TEST(SkipTest, Concurrent1) { RunConcurrent(1); } +TEST(SkipTest, Concurrent2) { RunConcurrent(2); } +TEST(SkipTest, Concurrent3) { RunConcurrent(3); } +TEST(SkipTest, Concurrent4) { RunConcurrent(4); } +TEST(SkipTest, Concurrent5) { RunConcurrent(5); } + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/db/snapshot.h b/src/leveldb-lin/db/snapshot.h new file mode 100644 index 0000000..e7f8fd2 --- /dev/null +++ b/src/leveldb-lin/db/snapshot.h @@ -0,0 +1,66 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_SNAPSHOT_H_ +#define STORAGE_LEVELDB_DB_SNAPSHOT_H_ + +#include "leveldb/db.h" + +namespace leveldb { + +class SnapshotList; + +// Snapshots are kept in a doubly-linked list in the DB. +// Each SnapshotImpl corresponds to a particular sequence number. +class SnapshotImpl : public Snapshot { + public: + SequenceNumber number_; // const after creation + + private: + friend class SnapshotList; + + // SnapshotImpl is kept in a doubly-linked circular list + SnapshotImpl* prev_; + SnapshotImpl* next_; + + SnapshotList* list_; // just for sanity checks +}; + +class SnapshotList { + public: + SnapshotList() { + list_.prev_ = &list_; + list_.next_ = &list_; + } + + bool empty() const { return list_.next_ == &list_; } + SnapshotImpl* oldest() const { assert(!empty()); return list_.next_; } + SnapshotImpl* newest() const { assert(!empty()); return list_.prev_; } + + const SnapshotImpl* New(SequenceNumber seq) { + SnapshotImpl* s = new SnapshotImpl; + s->number_ = seq; + s->list_ = this; + s->next_ = &list_; + s->prev_ = list_.prev_; + s->prev_->next_ = s; + s->next_->prev_ = s; + return s; + } + + void Delete(const SnapshotImpl* s) { + assert(s->list_ == this); + s->prev_->next_ = s->next_; + s->next_->prev_ = s->prev_; + delete s; + } + + private: + // Dummy head of doubly-linked list of snapshots + SnapshotImpl list_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_SNAPSHOT_H_ diff --git a/src/leveldb-lin/db/table_cache.cc b/src/leveldb-lin/db/table_cache.cc new file mode 100644 index 0000000..497db27 --- /dev/null +++ b/src/leveldb-lin/db/table_cache.cc @@ -0,0 +1,121 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/table_cache.h" + +#include "db/filename.h" +#include "leveldb/env.h" +#include "leveldb/table.h" +#include "util/coding.h" + +namespace leveldb { + +struct TableAndFile { + RandomAccessFile* file; + Table* table; +}; + +static void DeleteEntry(const Slice& key, void* value) { + TableAndFile* tf = reinterpret_cast(value); + delete tf->table; + delete tf->file; + delete tf; +} + +static void UnrefEntry(void* arg1, void* arg2) { + Cache* cache = reinterpret_cast(arg1); + Cache::Handle* h = reinterpret_cast(arg2); + cache->Release(h); +} + +TableCache::TableCache(const std::string& dbname, + const Options* options, + int entries) + : env_(options->env), + dbname_(dbname), + options_(options), + cache_(NewLRUCache(entries)) { +} + +TableCache::~TableCache() { + delete cache_; +} + +Status TableCache::FindTable(uint64_t file_number, uint64_t file_size, + Cache::Handle** handle) { + Status s; + char buf[sizeof(file_number)]; + EncodeFixed64(buf, file_number); + Slice key(buf, sizeof(buf)); + *handle = cache_->Lookup(key); + if (*handle == NULL) { + std::string fname = TableFileName(dbname_, file_number); + RandomAccessFile* file = NULL; + Table* table = NULL; + s = env_->NewRandomAccessFile(fname, &file); + if (s.ok()) { + s = Table::Open(*options_, file, file_size, &table); + } + + if (!s.ok()) { + assert(table == NULL); + delete file; + // We do not cache error results so that if the error is transient, + // or somebody repairs the file, we recover automatically. + } else { + TableAndFile* tf = new TableAndFile; + tf->file = file; + tf->table = table; + *handle = cache_->Insert(key, tf, 1, &DeleteEntry); + } + } + return s; +} + +Iterator* TableCache::NewIterator(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + Table** tableptr) { + if (tableptr != NULL) { + *tableptr = NULL; + } + + Cache::Handle* handle = NULL; + Status s = FindTable(file_number, file_size, &handle); + if (!s.ok()) { + return NewErrorIterator(s); + } + + Table* table = reinterpret_cast(cache_->Value(handle))->table; + Iterator* result = table->NewIterator(options); + result->RegisterCleanup(&UnrefEntry, cache_, handle); + if (tableptr != NULL) { + *tableptr = table; + } + return result; +} + +Status TableCache::Get(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + const Slice& k, + void* arg, + void (*saver)(void*, const Slice&, const Slice&)) { + Cache::Handle* handle = NULL; + Status s = FindTable(file_number, file_size, &handle); + if (s.ok()) { + Table* t = reinterpret_cast(cache_->Value(handle))->table; + s = t->InternalGet(options, k, arg, saver); + cache_->Release(handle); + } + return s; +} + +void TableCache::Evict(uint64_t file_number) { + char buf[sizeof(file_number)]; + EncodeFixed64(buf, file_number); + cache_->Erase(Slice(buf, sizeof(buf))); +} + +} // namespace leveldb diff --git a/src/leveldb-lin/db/table_cache.h b/src/leveldb-lin/db/table_cache.h new file mode 100644 index 0000000..8cf4aaf --- /dev/null +++ b/src/leveldb-lin/db/table_cache.h @@ -0,0 +1,61 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Thread-safe (provides internal synchronization) + +#ifndef STORAGE_LEVELDB_DB_TABLE_CACHE_H_ +#define STORAGE_LEVELDB_DB_TABLE_CACHE_H_ + +#include +#include +#include "db/dbformat.h" +#include "leveldb/cache.h" +#include "leveldb/table.h" +#include "port/port.h" + +namespace leveldb { + +class Env; + +class TableCache { + public: + TableCache(const std::string& dbname, const Options* options, int entries); + ~TableCache(); + + // Return an iterator for the specified file number (the corresponding + // file length must be exactly "file_size" bytes). If "tableptr" is + // non-NULL, also sets "*tableptr" to point to the Table object + // underlying the returned iterator, or NULL if no Table object underlies + // the returned iterator. The returned "*tableptr" object is owned by + // the cache and should not be deleted, and is valid for as long as the + // returned iterator is live. + Iterator* NewIterator(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + Table** tableptr = NULL); + + // If a seek to internal key "k" in specified file finds an entry, + // call (*handle_result)(arg, found_key, found_value). + Status Get(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + const Slice& k, + void* arg, + void (*handle_result)(void*, const Slice&, const Slice&)); + + // Evict any entry for the specified file number + void Evict(uint64_t file_number); + + private: + Env* const env_; + const std::string dbname_; + const Options* options_; + Cache* cache_; + + Status FindTable(uint64_t file_number, uint64_t file_size, Cache::Handle**); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_TABLE_CACHE_H_ diff --git a/src/leveldb-lin/db/table_cache.o b/src/leveldb-lin/db/table_cache.o new file mode 100644 index 0000000..2e9d372 Binary files /dev/null and b/src/leveldb-lin/db/table_cache.o differ diff --git a/src/leveldb-lin/db/version_edit.cc b/src/leveldb-lin/db/version_edit.cc new file mode 100644 index 0000000..f10a2d5 --- /dev/null +++ b/src/leveldb-lin/db/version_edit.cc @@ -0,0 +1,266 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/version_edit.h" + +#include "db/version_set.h" +#include "util/coding.h" + +namespace leveldb { + +// Tag numbers for serialized VersionEdit. These numbers are written to +// disk and should not be changed. +enum Tag { + kComparator = 1, + kLogNumber = 2, + kNextFileNumber = 3, + kLastSequence = 4, + kCompactPointer = 5, + kDeletedFile = 6, + kNewFile = 7, + // 8 was used for large value refs + kPrevLogNumber = 9 +}; + +void VersionEdit::Clear() { + comparator_.clear(); + log_number_ = 0; + prev_log_number_ = 0; + last_sequence_ = 0; + next_file_number_ = 0; + has_comparator_ = false; + has_log_number_ = false; + has_prev_log_number_ = false; + has_next_file_number_ = false; + has_last_sequence_ = false; + deleted_files_.clear(); + new_files_.clear(); +} + +void VersionEdit::EncodeTo(std::string* dst) const { + if (has_comparator_) { + PutVarint32(dst, kComparator); + PutLengthPrefixedSlice(dst, comparator_); + } + if (has_log_number_) { + PutVarint32(dst, kLogNumber); + PutVarint64(dst, log_number_); + } + if (has_prev_log_number_) { + PutVarint32(dst, kPrevLogNumber); + PutVarint64(dst, prev_log_number_); + } + if (has_next_file_number_) { + PutVarint32(dst, kNextFileNumber); + PutVarint64(dst, next_file_number_); + } + if (has_last_sequence_) { + PutVarint32(dst, kLastSequence); + PutVarint64(dst, last_sequence_); + } + + for (size_t i = 0; i < compact_pointers_.size(); i++) { + PutVarint32(dst, kCompactPointer); + PutVarint32(dst, compact_pointers_[i].first); // level + PutLengthPrefixedSlice(dst, compact_pointers_[i].second.Encode()); + } + + for (DeletedFileSet::const_iterator iter = deleted_files_.begin(); + iter != deleted_files_.end(); + ++iter) { + PutVarint32(dst, kDeletedFile); + PutVarint32(dst, iter->first); // level + PutVarint64(dst, iter->second); // file number + } + + for (size_t i = 0; i < new_files_.size(); i++) { + const FileMetaData& f = new_files_[i].second; + PutVarint32(dst, kNewFile); + PutVarint32(dst, new_files_[i].first); // level + PutVarint64(dst, f.number); + PutVarint64(dst, f.file_size); + PutLengthPrefixedSlice(dst, f.smallest.Encode()); + PutLengthPrefixedSlice(dst, f.largest.Encode()); + } +} + +static bool GetInternalKey(Slice* input, InternalKey* dst) { + Slice str; + if (GetLengthPrefixedSlice(input, &str)) { + dst->DecodeFrom(str); + return true; + } else { + return false; + } +} + +static bool GetLevel(Slice* input, int* level) { + uint32_t v; + if (GetVarint32(input, &v) && + v < config::kNumLevels) { + *level = v; + return true; + } else { + return false; + } +} + +Status VersionEdit::DecodeFrom(const Slice& src) { + Clear(); + Slice input = src; + const char* msg = NULL; + uint32_t tag; + + // Temporary storage for parsing + int level; + uint64_t number; + FileMetaData f; + Slice str; + InternalKey key; + + while (msg == NULL && GetVarint32(&input, &tag)) { + switch (tag) { + case kComparator: + if (GetLengthPrefixedSlice(&input, &str)) { + comparator_ = str.ToString(); + has_comparator_ = true; + } else { + msg = "comparator name"; + } + break; + + case kLogNumber: + if (GetVarint64(&input, &log_number_)) { + has_log_number_ = true; + } else { + msg = "log number"; + } + break; + + case kPrevLogNumber: + if (GetVarint64(&input, &prev_log_number_)) { + has_prev_log_number_ = true; + } else { + msg = "previous log number"; + } + break; + + case kNextFileNumber: + if (GetVarint64(&input, &next_file_number_)) { + has_next_file_number_ = true; + } else { + msg = "next file number"; + } + break; + + case kLastSequence: + if (GetVarint64(&input, &last_sequence_)) { + has_last_sequence_ = true; + } else { + msg = "last sequence number"; + } + break; + + case kCompactPointer: + if (GetLevel(&input, &level) && + GetInternalKey(&input, &key)) { + compact_pointers_.push_back(std::make_pair(level, key)); + } else { + msg = "compaction pointer"; + } + break; + + case kDeletedFile: + if (GetLevel(&input, &level) && + GetVarint64(&input, &number)) { + deleted_files_.insert(std::make_pair(level, number)); + } else { + msg = "deleted file"; + } + break; + + case kNewFile: + if (GetLevel(&input, &level) && + GetVarint64(&input, &f.number) && + GetVarint64(&input, &f.file_size) && + GetInternalKey(&input, &f.smallest) && + GetInternalKey(&input, &f.largest)) { + new_files_.push_back(std::make_pair(level, f)); + } else { + msg = "new-file entry"; + } + break; + + default: + msg = "unknown tag"; + break; + } + } + + if (msg == NULL && !input.empty()) { + msg = "invalid tag"; + } + + Status result; + if (msg != NULL) { + result = Status::Corruption("VersionEdit", msg); + } + return result; +} + +std::string VersionEdit::DebugString() const { + std::string r; + r.append("VersionEdit {"); + if (has_comparator_) { + r.append("\n Comparator: "); + r.append(comparator_); + } + if (has_log_number_) { + r.append("\n LogNumber: "); + AppendNumberTo(&r, log_number_); + } + if (has_prev_log_number_) { + r.append("\n PrevLogNumber: "); + AppendNumberTo(&r, prev_log_number_); + } + if (has_next_file_number_) { + r.append("\n NextFile: "); + AppendNumberTo(&r, next_file_number_); + } + if (has_last_sequence_) { + r.append("\n LastSeq: "); + AppendNumberTo(&r, last_sequence_); + } + for (size_t i = 0; i < compact_pointers_.size(); i++) { + r.append("\n CompactPointer: "); + AppendNumberTo(&r, compact_pointers_[i].first); + r.append(" "); + r.append(compact_pointers_[i].second.DebugString()); + } + for (DeletedFileSet::const_iterator iter = deleted_files_.begin(); + iter != deleted_files_.end(); + ++iter) { + r.append("\n DeleteFile: "); + AppendNumberTo(&r, iter->first); + r.append(" "); + AppendNumberTo(&r, iter->second); + } + for (size_t i = 0; i < new_files_.size(); i++) { + const FileMetaData& f = new_files_[i].second; + r.append("\n AddFile: "); + AppendNumberTo(&r, new_files_[i].first); + r.append(" "); + AppendNumberTo(&r, f.number); + r.append(" "); + AppendNumberTo(&r, f.file_size); + r.append(" "); + r.append(f.smallest.DebugString()); + r.append(" .. "); + r.append(f.largest.DebugString()); + } + r.append("\n}\n"); + return r; +} + +} // namespace leveldb diff --git a/src/leveldb-lin/db/version_edit.h b/src/leveldb-lin/db/version_edit.h new file mode 100644 index 0000000..eaef77b --- /dev/null +++ b/src/leveldb-lin/db/version_edit.h @@ -0,0 +1,107 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_VERSION_EDIT_H_ +#define STORAGE_LEVELDB_DB_VERSION_EDIT_H_ + +#include +#include +#include +#include "db/dbformat.h" + +namespace leveldb { + +class VersionSet; + +struct FileMetaData { + int refs; + int allowed_seeks; // Seeks allowed until compaction + uint64_t number; + uint64_t file_size; // File size in bytes + InternalKey smallest; // Smallest internal key served by table + InternalKey largest; // Largest internal key served by table + + FileMetaData() : refs(0), allowed_seeks(1 << 30), file_size(0) { } +}; + +class VersionEdit { + public: + VersionEdit() { Clear(); } + ~VersionEdit() { } + + void Clear(); + + void SetComparatorName(const Slice& name) { + has_comparator_ = true; + comparator_ = name.ToString(); + } + void SetLogNumber(uint64_t num) { + has_log_number_ = true; + log_number_ = num; + } + void SetPrevLogNumber(uint64_t num) { + has_prev_log_number_ = true; + prev_log_number_ = num; + } + void SetNextFile(uint64_t num) { + has_next_file_number_ = true; + next_file_number_ = num; + } + void SetLastSequence(SequenceNumber seq) { + has_last_sequence_ = true; + last_sequence_ = seq; + } + void SetCompactPointer(int level, const InternalKey& key) { + compact_pointers_.push_back(std::make_pair(level, key)); + } + + // Add the specified file at the specified number. + // REQUIRES: This version has not been saved (see VersionSet::SaveTo) + // REQUIRES: "smallest" and "largest" are smallest and largest keys in file + void AddFile(int level, uint64_t file, + uint64_t file_size, + const InternalKey& smallest, + const InternalKey& largest) { + FileMetaData f; + f.number = file; + f.file_size = file_size; + f.smallest = smallest; + f.largest = largest; + new_files_.push_back(std::make_pair(level, f)); + } + + // Delete the specified "file" from the specified "level". + void DeleteFile(int level, uint64_t file) { + deleted_files_.insert(std::make_pair(level, file)); + } + + void EncodeTo(std::string* dst) const; + Status DecodeFrom(const Slice& src); + + std::string DebugString() const; + + private: + friend class VersionSet; + + typedef std::set< std::pair > DeletedFileSet; + + std::string comparator_; + uint64_t log_number_; + uint64_t prev_log_number_; + uint64_t next_file_number_; + SequenceNumber last_sequence_; + bool has_comparator_; + bool has_log_number_; + bool has_prev_log_number_; + bool has_next_file_number_; + bool has_last_sequence_; + + std::vector< std::pair > compact_pointers_; + DeletedFileSet deleted_files_; + std::vector< std::pair > new_files_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_VERSION_EDIT_H_ diff --git a/src/leveldb-lin/db/version_edit.o b/src/leveldb-lin/db/version_edit.o new file mode 100644 index 0000000..e8eb4db Binary files /dev/null and b/src/leveldb-lin/db/version_edit.o differ diff --git a/src/leveldb-lin/db/version_edit_test.cc b/src/leveldb-lin/db/version_edit_test.cc new file mode 100644 index 0000000..280310b --- /dev/null +++ b/src/leveldb-lin/db/version_edit_test.cc @@ -0,0 +1,46 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/version_edit.h" +#include "util/testharness.h" + +namespace leveldb { + +static void TestEncodeDecode(const VersionEdit& edit) { + std::string encoded, encoded2; + edit.EncodeTo(&encoded); + VersionEdit parsed; + Status s = parsed.DecodeFrom(encoded); + ASSERT_TRUE(s.ok()) << s.ToString(); + parsed.EncodeTo(&encoded2); + ASSERT_EQ(encoded, encoded2); +} + +class VersionEditTest { }; + +TEST(VersionEditTest, EncodeDecode) { + static const uint64_t kBig = 1ull << 50; + + VersionEdit edit; + for (int i = 0; i < 4; i++) { + TestEncodeDecode(edit); + edit.AddFile(3, kBig + 300 + i, kBig + 400 + i, + InternalKey("foo", kBig + 500 + i, kTypeValue), + InternalKey("zoo", kBig + 600 + i, kTypeDeletion)); + edit.DeleteFile(4, kBig + 700 + i); + edit.SetCompactPointer(i, InternalKey("x", kBig + 900 + i, kTypeValue)); + } + + edit.SetComparatorName("foo"); + edit.SetLogNumber(kBig + 100); + edit.SetNextFile(kBig + 200); + edit.SetLastSequence(kBig + 1000); + TestEncodeDecode(edit); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/db/version_set.cc b/src/leveldb-lin/db/version_set.cc new file mode 100644 index 0000000..7d0a5de --- /dev/null +++ b/src/leveldb-lin/db/version_set.cc @@ -0,0 +1,1438 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/version_set.h" + +#include +#include +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "db/memtable.h" +#include "db/table_cache.h" +#include "leveldb/env.h" +#include "leveldb/table_builder.h" +#include "table/merger.h" +#include "table/two_level_iterator.h" +#include "util/coding.h" +#include "util/logging.h" + +namespace leveldb { + +static const int kTargetFileSize = 2 * 1048576; + +// Maximum bytes of overlaps in grandparent (i.e., level+2) before we +// stop building a single file in a level->level+1 compaction. +static const int64_t kMaxGrandParentOverlapBytes = 10 * kTargetFileSize; + +// Maximum number of bytes in all compacted files. We avoid expanding +// the lower level file set of a compaction if it would make the +// total compaction cover more than this many bytes. +static const int64_t kExpandedCompactionByteSizeLimit = 25 * kTargetFileSize; + +static double MaxBytesForLevel(int level) { + // Note: the result for level zero is not really used since we set + // the level-0 compaction threshold based on number of files. + double result = 10 * 1048576.0; // Result for both level-0 and level-1 + while (level > 1) { + result *= 10; + level--; + } + return result; +} + +static uint64_t MaxFileSizeForLevel(int level) { + return kTargetFileSize; // We could vary per level to reduce number of files? +} + +static int64_t TotalFileSize(const std::vector& files) { + int64_t sum = 0; + for (size_t i = 0; i < files.size(); i++) { + sum += files[i]->file_size; + } + return sum; +} + +namespace { +std::string IntSetToString(const std::set& s) { + std::string result = "{"; + for (std::set::const_iterator it = s.begin(); + it != s.end(); + ++it) { + result += (result.size() > 1) ? "," : ""; + result += NumberToString(*it); + } + result += "}"; + return result; +} +} // namespace + +Version::~Version() { + assert(refs_ == 0); + + // Remove from linked list + prev_->next_ = next_; + next_->prev_ = prev_; + + // Drop references to files + for (int level = 0; level < config::kNumLevels; level++) { + for (size_t i = 0; i < files_[level].size(); i++) { + FileMetaData* f = files_[level][i]; + assert(f->refs > 0); + f->refs--; + if (f->refs <= 0) { + delete f; + } + } + } +} + +int FindFile(const InternalKeyComparator& icmp, + const std::vector& files, + const Slice& key) { + uint32_t left = 0; + uint32_t right = files.size(); + while (left < right) { + uint32_t mid = (left + right) / 2; + const FileMetaData* f = files[mid]; + if (icmp.InternalKeyComparator::Compare(f->largest.Encode(), key) < 0) { + // Key at "mid.largest" is < "target". Therefore all + // files at or before "mid" are uninteresting. + left = mid + 1; + } else { + // Key at "mid.largest" is >= "target". Therefore all files + // after "mid" are uninteresting. + right = mid; + } + } + return right; +} + +static bool AfterFile(const Comparator* ucmp, + const Slice* user_key, const FileMetaData* f) { + // NULL user_key occurs before all keys and is therefore never after *f + return (user_key != NULL && + ucmp->Compare(*user_key, f->largest.user_key()) > 0); +} + +static bool BeforeFile(const Comparator* ucmp, + const Slice* user_key, const FileMetaData* f) { + // NULL user_key occurs after all keys and is therefore never before *f + return (user_key != NULL && + ucmp->Compare(*user_key, f->smallest.user_key()) < 0); +} + +bool SomeFileOverlapsRange( + const InternalKeyComparator& icmp, + bool disjoint_sorted_files, + const std::vector& files, + const Slice* smallest_user_key, + const Slice* largest_user_key) { + const Comparator* ucmp = icmp.user_comparator(); + if (!disjoint_sorted_files) { + // Need to check against all files + for (size_t i = 0; i < files.size(); i++) { + const FileMetaData* f = files[i]; + if (AfterFile(ucmp, smallest_user_key, f) || + BeforeFile(ucmp, largest_user_key, f)) { + // No overlap + } else { + return true; // Overlap + } + } + return false; + } + + // Binary search over file list + uint32_t index = 0; + if (smallest_user_key != NULL) { + // Find the earliest possible internal key for smallest_user_key + InternalKey small(*smallest_user_key, kMaxSequenceNumber,kValueTypeForSeek); + index = FindFile(icmp, files, small.Encode()); + } + + if (index >= files.size()) { + // beginning of range is after all files, so no overlap. + return false; + } + + return !BeforeFile(ucmp, largest_user_key, files[index]); +} + +// An internal iterator. For a given version/level pair, yields +// information about the files in the level. For a given entry, key() +// is the largest key that occurs in the file, and value() is an +// 16-byte value containing the file number and file size, both +// encoded using EncodeFixed64. +class Version::LevelFileNumIterator : public Iterator { + public: + LevelFileNumIterator(const InternalKeyComparator& icmp, + const std::vector* flist) + : icmp_(icmp), + flist_(flist), + index_(flist->size()) { // Marks as invalid + } + virtual bool Valid() const { + return index_ < flist_->size(); + } + virtual void Seek(const Slice& target) { + index_ = FindFile(icmp_, *flist_, target); + } + virtual void SeekToFirst() { index_ = 0; } + virtual void SeekToLast() { + index_ = flist_->empty() ? 0 : flist_->size() - 1; + } + virtual void Next() { + assert(Valid()); + index_++; + } + virtual void Prev() { + assert(Valid()); + if (index_ == 0) { + index_ = flist_->size(); // Marks as invalid + } else { + index_--; + } + } + Slice key() const { + assert(Valid()); + return (*flist_)[index_]->largest.Encode(); + } + Slice value() const { + assert(Valid()); + EncodeFixed64(value_buf_, (*flist_)[index_]->number); + EncodeFixed64(value_buf_+8, (*flist_)[index_]->file_size); + return Slice(value_buf_, sizeof(value_buf_)); + } + virtual Status status() const { return Status::OK(); } + private: + const InternalKeyComparator icmp_; + const std::vector* const flist_; + uint32_t index_; + + // Backing store for value(). Holds the file number and size. + mutable char value_buf_[16]; +}; + +static Iterator* GetFileIterator(void* arg, + const ReadOptions& options, + const Slice& file_value) { + TableCache* cache = reinterpret_cast(arg); + if (file_value.size() != 16) { + return NewErrorIterator( + Status::Corruption("FileReader invoked with unexpected value")); + } else { + return cache->NewIterator(options, + DecodeFixed64(file_value.data()), + DecodeFixed64(file_value.data() + 8)); + } +} + +Iterator* Version::NewConcatenatingIterator(const ReadOptions& options, + int level) const { + return NewTwoLevelIterator( + new LevelFileNumIterator(vset_->icmp_, &files_[level]), + &GetFileIterator, vset_->table_cache_, options); +} + +void Version::AddIterators(const ReadOptions& options, + std::vector* iters) { + // Merge all level zero files together since they may overlap + for (size_t i = 0; i < files_[0].size(); i++) { + iters->push_back( + vset_->table_cache_->NewIterator( + options, files_[0][i]->number, files_[0][i]->file_size)); + } + + // For levels > 0, we can use a concatenating iterator that sequentially + // walks through the non-overlapping files in the level, opening them + // lazily. + for (int level = 1; level < config::kNumLevels; level++) { + if (!files_[level].empty()) { + iters->push_back(NewConcatenatingIterator(options, level)); + } + } +} + +// Callback from TableCache::Get() +namespace { +enum SaverState { + kNotFound, + kFound, + kDeleted, + kCorrupt, +}; +struct Saver { + SaverState state; + const Comparator* ucmp; + Slice user_key; + std::string* value; +}; +} +static void SaveValue(void* arg, const Slice& ikey, const Slice& v) { + Saver* s = reinterpret_cast(arg); + ParsedInternalKey parsed_key; + if (!ParseInternalKey(ikey, &parsed_key)) { + s->state = kCorrupt; + } else { + if (s->ucmp->Compare(parsed_key.user_key, s->user_key) == 0) { + s->state = (parsed_key.type == kTypeValue) ? kFound : kDeleted; + if (s->state == kFound) { + s->value->assign(v.data(), v.size()); + } + } + } +} + +static bool NewestFirst(FileMetaData* a, FileMetaData* b) { + return a->number > b->number; +} + +Status Version::Get(const ReadOptions& options, + const LookupKey& k, + std::string* value, + GetStats* stats) { + Slice ikey = k.internal_key(); + Slice user_key = k.user_key(); + const Comparator* ucmp = vset_->icmp_.user_comparator(); + Status s; + + stats->seek_file = NULL; + stats->seek_file_level = -1; + FileMetaData* last_file_read = NULL; + int last_file_read_level = -1; + + // We can search level-by-level since entries never hop across + // levels. Therefore we are guaranteed that if we find data + // in an smaller level, later levels are irrelevant. + std::vector tmp; + FileMetaData* tmp2; + for (int level = 0; level < config::kNumLevels; level++) { + size_t num_files = files_[level].size(); + if (num_files == 0) continue; + + // Get the list of files to search in this level + FileMetaData* const* files = &files_[level][0]; + if (level == 0) { + // Level-0 files may overlap each other. Find all files that + // overlap user_key and process them in order from newest to oldest. + tmp.reserve(num_files); + for (uint32_t i = 0; i < num_files; i++) { + FileMetaData* f = files[i]; + if (ucmp->Compare(user_key, f->smallest.user_key()) >= 0 && + ucmp->Compare(user_key, f->largest.user_key()) <= 0) { + tmp.push_back(f); + } + } + if (tmp.empty()) continue; + + std::sort(tmp.begin(), tmp.end(), NewestFirst); + files = &tmp[0]; + num_files = tmp.size(); + } else { + // Binary search to find earliest index whose largest key >= ikey. + uint32_t index = FindFile(vset_->icmp_, files_[level], ikey); + if (index >= num_files) { + files = NULL; + num_files = 0; + } else { + tmp2 = files[index]; + if (ucmp->Compare(user_key, tmp2->smallest.user_key()) < 0) { + // All of "tmp2" is past any data for user_key + files = NULL; + num_files = 0; + } else { + files = &tmp2; + num_files = 1; + } + } + } + + for (uint32_t i = 0; i < num_files; ++i) { + if (last_file_read != NULL && stats->seek_file == NULL) { + // We have had more than one seek for this read. Charge the 1st file. + stats->seek_file = last_file_read; + stats->seek_file_level = last_file_read_level; + } + + FileMetaData* f = files[i]; + last_file_read = f; + last_file_read_level = level; + + Saver saver; + saver.state = kNotFound; + saver.ucmp = ucmp; + saver.user_key = user_key; + saver.value = value; + s = vset_->table_cache_->Get(options, f->number, f->file_size, + ikey, &saver, SaveValue); + if (!s.ok()) { + return s; + } + switch (saver.state) { + case kNotFound: + break; // Keep searching in other files + case kFound: + return s; + case kDeleted: + s = Status::NotFound(Slice()); // Use empty error message for speed + return s; + case kCorrupt: + s = Status::Corruption("corrupted key for ", user_key); + return s; + } + } + } + + return Status::NotFound(Slice()); // Use an empty error message for speed +} + +bool Version::UpdateStats(const GetStats& stats) { + FileMetaData* f = stats.seek_file; + if (f != NULL) { + f->allowed_seeks--; + if (f->allowed_seeks <= 0 && file_to_compact_ == NULL) { + file_to_compact_ = f; + file_to_compact_level_ = stats.seek_file_level; + return true; + } + } + return false; +} + +void Version::Ref() { + ++refs_; +} + +void Version::Unref() { + assert(this != &vset_->dummy_versions_); + assert(refs_ >= 1); + --refs_; + if (refs_ == 0) { + delete this; + } +} + +bool Version::OverlapInLevel(int level, + const Slice* smallest_user_key, + const Slice* largest_user_key) { + return SomeFileOverlapsRange(vset_->icmp_, (level > 0), files_[level], + smallest_user_key, largest_user_key); +} + +int Version::PickLevelForMemTableOutput( + const Slice& smallest_user_key, + const Slice& largest_user_key) { + int level = 0; + if (!OverlapInLevel(0, &smallest_user_key, &largest_user_key)) { + // Push to next level if there is no overlap in next level, + // and the #bytes overlapping in the level after that are limited. + InternalKey start(smallest_user_key, kMaxSequenceNumber, kValueTypeForSeek); + InternalKey limit(largest_user_key, 0, static_cast(0)); + std::vector overlaps; + while (level < config::kMaxMemCompactLevel) { + if (OverlapInLevel(level + 1, &smallest_user_key, &largest_user_key)) { + break; + } + GetOverlappingInputs(level + 2, &start, &limit, &overlaps); + const int64_t sum = TotalFileSize(overlaps); + if (sum > kMaxGrandParentOverlapBytes) { + break; + } + level++; + } + } + return level; +} + +// Store in "*inputs" all files in "level" that overlap [begin,end] +void Version::GetOverlappingInputs( + int level, + const InternalKey* begin, + const InternalKey* end, + std::vector* inputs) { + inputs->clear(); + Slice user_begin, user_end; + if (begin != NULL) { + user_begin = begin->user_key(); + } + if (end != NULL) { + user_end = end->user_key(); + } + const Comparator* user_cmp = vset_->icmp_.user_comparator(); + for (size_t i = 0; i < files_[level].size(); ) { + FileMetaData* f = files_[level][i++]; + const Slice file_start = f->smallest.user_key(); + const Slice file_limit = f->largest.user_key(); + if (begin != NULL && user_cmp->Compare(file_limit, user_begin) < 0) { + // "f" is completely before specified range; skip it + } else if (end != NULL && user_cmp->Compare(file_start, user_end) > 0) { + // "f" is completely after specified range; skip it + } else { + inputs->push_back(f); + if (level == 0) { + // Level-0 files may overlap each other. So check if the newly + // added file has expanded the range. If so, restart search. + if (begin != NULL && user_cmp->Compare(file_start, user_begin) < 0) { + user_begin = file_start; + inputs->clear(); + i = 0; + } else if (end != NULL && user_cmp->Compare(file_limit, user_end) > 0) { + user_end = file_limit; + inputs->clear(); + i = 0; + } + } + } + } +} + +std::string Version::DebugString() const { + std::string r; + for (int level = 0; level < config::kNumLevels; level++) { + // E.g., + // --- level 1 --- + // 17:123['a' .. 'd'] + // 20:43['e' .. 'g'] + r.append("--- level "); + AppendNumberTo(&r, level); + r.append(" ---\n"); + const std::vector& files = files_[level]; + for (size_t i = 0; i < files.size(); i++) { + r.push_back(' '); + AppendNumberTo(&r, files[i]->number); + r.push_back(':'); + AppendNumberTo(&r, files[i]->file_size); + r.append("["); + r.append(files[i]->smallest.DebugString()); + r.append(" .. "); + r.append(files[i]->largest.DebugString()); + r.append("]\n"); + } + } + return r; +} + +// A helper class so we can efficiently apply a whole sequence +// of edits to a particular state without creating intermediate +// Versions that contain full copies of the intermediate state. +class VersionSet::Builder { + private: + // Helper to sort by v->files_[file_number].smallest + struct BySmallestKey { + const InternalKeyComparator* internal_comparator; + + bool operator()(FileMetaData* f1, FileMetaData* f2) const { + int r = internal_comparator->Compare(f1->smallest, f2->smallest); + if (r != 0) { + return (r < 0); + } else { + // Break ties by file number + return (f1->number < f2->number); + } + } + }; + + typedef std::set FileSet; + struct LevelState { + std::set deleted_files; + FileSet* added_files; + }; + + VersionSet* vset_; + Version* base_; + LevelState levels_[config::kNumLevels]; + + public: + // Initialize a builder with the files from *base and other info from *vset + Builder(VersionSet* vset, Version* base) + : vset_(vset), + base_(base) { + base_->Ref(); + BySmallestKey cmp; + cmp.internal_comparator = &vset_->icmp_; + for (int level = 0; level < config::kNumLevels; level++) { + levels_[level].added_files = new FileSet(cmp); + } + } + + ~Builder() { + for (int level = 0; level < config::kNumLevels; level++) { + const FileSet* added = levels_[level].added_files; + std::vector to_unref; + to_unref.reserve(added->size()); + for (FileSet::const_iterator it = added->begin(); + it != added->end(); ++it) { + to_unref.push_back(*it); + } + delete added; + for (uint32_t i = 0; i < to_unref.size(); i++) { + FileMetaData* f = to_unref[i]; + f->refs--; + if (f->refs <= 0) { + delete f; + } + } + } + base_->Unref(); + } + + // Apply all of the edits in *edit to the current state. + void Apply(VersionEdit* edit) { + // Update compaction pointers + for (size_t i = 0; i < edit->compact_pointers_.size(); i++) { + const int level = edit->compact_pointers_[i].first; + vset_->compact_pointer_[level] = + edit->compact_pointers_[i].second.Encode().ToString(); + } + + // Delete files + const VersionEdit::DeletedFileSet& del = edit->deleted_files_; + for (VersionEdit::DeletedFileSet::const_iterator iter = del.begin(); + iter != del.end(); + ++iter) { + const int level = iter->first; + const uint64_t number = iter->second; + levels_[level].deleted_files.insert(number); + } + + // Add new files + for (size_t i = 0; i < edit->new_files_.size(); i++) { + const int level = edit->new_files_[i].first; + FileMetaData* f = new FileMetaData(edit->new_files_[i].second); + f->refs = 1; + + // We arrange to automatically compact this file after + // a certain number of seeks. Let's assume: + // (1) One seek costs 10ms + // (2) Writing or reading 1MB costs 10ms (100MB/s) + // (3) A compaction of 1MB does 25MB of IO: + // 1MB read from this level + // 10-12MB read from next level (boundaries may be misaligned) + // 10-12MB written to next level + // This implies that 25 seeks cost the same as the compaction + // of 1MB of data. I.e., one seek costs approximately the + // same as the compaction of 40KB of data. We are a little + // conservative and allow approximately one seek for every 16KB + // of data before triggering a compaction. + f->allowed_seeks = (f->file_size / 16384); + if (f->allowed_seeks < 100) f->allowed_seeks = 100; + + levels_[level].deleted_files.erase(f->number); + levels_[level].added_files->insert(f); + } + } + + // Save the current state in *v. + void SaveTo(Version* v) { + BySmallestKey cmp; + cmp.internal_comparator = &vset_->icmp_; + for (int level = 0; level < config::kNumLevels; level++) { + // Merge the set of added files with the set of pre-existing files. + // Drop any deleted files. Store the result in *v. + const std::vector& base_files = base_->files_[level]; + std::vector::const_iterator base_iter = base_files.begin(); + std::vector::const_iterator base_end = base_files.end(); + const FileSet* added = levels_[level].added_files; + v->files_[level].reserve(base_files.size() + added->size()); + for (FileSet::const_iterator added_iter = added->begin(); + added_iter != added->end(); + ++added_iter) { + // Add all smaller files listed in base_ + for (std::vector::const_iterator bpos + = std::upper_bound(base_iter, base_end, *added_iter, cmp); + base_iter != bpos; + ++base_iter) { + MaybeAddFile(v, level, *base_iter); + } + + MaybeAddFile(v, level, *added_iter); + } + + // Add remaining base files + for (; base_iter != base_end; ++base_iter) { + MaybeAddFile(v, level, *base_iter); + } + +#ifndef NDEBUG + // Make sure there is no overlap in levels > 0 + if (level > 0) { + for (uint32_t i = 1; i < v->files_[level].size(); i++) { + const InternalKey& prev_end = v->files_[level][i-1]->largest; + const InternalKey& this_begin = v->files_[level][i]->smallest; + if (vset_->icmp_.Compare(prev_end, this_begin) >= 0) { + fprintf(stderr, "overlapping ranges in same level %s vs. %s\n", + prev_end.DebugString().c_str(), + this_begin.DebugString().c_str()); + abort(); + } + } + } +#endif + } + } + + void MaybeAddFile(Version* v, int level, FileMetaData* f) { + if (levels_[level].deleted_files.count(f->number) > 0) { + // File is deleted: do nothing + } else { + std::vector* files = &v->files_[level]; + if (level > 0 && !files->empty()) { + // Must not overlap + assert(vset_->icmp_.Compare((*files)[files->size()-1]->largest, + f->smallest) < 0); + } + f->refs++; + files->push_back(f); + } + } +}; + +VersionSet::VersionSet(const std::string& dbname, + const Options* options, + TableCache* table_cache, + const InternalKeyComparator* cmp) + : env_(options->env), + dbname_(dbname), + options_(options), + table_cache_(table_cache), + icmp_(*cmp), + next_file_number_(2), + manifest_file_number_(0), // Filled by Recover() + last_sequence_(0), + log_number_(0), + prev_log_number_(0), + descriptor_file_(NULL), + descriptor_log_(NULL), + dummy_versions_(this), + current_(NULL) { + AppendVersion(new Version(this)); +} + +VersionSet::~VersionSet() { + current_->Unref(); + assert(dummy_versions_.next_ == &dummy_versions_); // List must be empty + delete descriptor_log_; + delete descriptor_file_; +} + +void VersionSet::AppendVersion(Version* v) { + // Make "v" current + assert(v->refs_ == 0); + assert(v != current_); + if (current_ != NULL) { + current_->Unref(); + } + current_ = v; + v->Ref(); + + // Append to linked list + v->prev_ = dummy_versions_.prev_; + v->next_ = &dummy_versions_; + v->prev_->next_ = v; + v->next_->prev_ = v; +} + +Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) { + if (edit->has_log_number_) { + assert(edit->log_number_ >= log_number_); + assert(edit->log_number_ < next_file_number_); + } else { + edit->SetLogNumber(log_number_); + } + + if (!edit->has_prev_log_number_) { + edit->SetPrevLogNumber(prev_log_number_); + } + + edit->SetNextFile(next_file_number_); + edit->SetLastSequence(last_sequence_); + + Version* v = new Version(this); + { + Builder builder(this, current_); + builder.Apply(edit); + builder.SaveTo(v); + } + Finalize(v); + + // Initialize new descriptor log file if necessary by creating + // a temporary file that contains a snapshot of the current version. + std::string new_manifest_file; + Status s; + if (descriptor_log_ == NULL) { + // No reason to unlock *mu here since we only hit this path in the + // first call to LogAndApply (when opening the database). + assert(descriptor_file_ == NULL); + new_manifest_file = DescriptorFileName(dbname_, manifest_file_number_); + edit->SetNextFile(next_file_number_); + s = env_->NewWritableFile(new_manifest_file, &descriptor_file_); + if (s.ok()) { + descriptor_log_ = new log::Writer(descriptor_file_); + s = WriteSnapshot(descriptor_log_); + } + } + + // Unlock during expensive MANIFEST log write + { + mu->Unlock(); + + // Write new record to MANIFEST log + if (s.ok()) { + std::string record; + edit->EncodeTo(&record); + s = descriptor_log_->AddRecord(record); + if (s.ok()) { + s = descriptor_file_->Sync(); + } + if (!s.ok()) { + Log(options_->info_log, "MANIFEST write: %s\n", s.ToString().c_str()); + if (ManifestContains(record)) { + Log(options_->info_log, + "MANIFEST contains log record despite error; advancing to new " + "version to prevent mismatch between in-memory and logged state"); + s = Status::OK(); + } + } + } + + // If we just created a new descriptor file, install it by writing a + // new CURRENT file that points to it. + if (s.ok() && !new_manifest_file.empty()) { + s = SetCurrentFile(env_, dbname_, manifest_file_number_); + // No need to double-check MANIFEST in case of error since it + // will be discarded below. + } + + mu->Lock(); + } + + // Install the new version + if (s.ok()) { + AppendVersion(v); + log_number_ = edit->log_number_; + prev_log_number_ = edit->prev_log_number_; + } else { + delete v; + if (!new_manifest_file.empty()) { + delete descriptor_log_; + delete descriptor_file_; + descriptor_log_ = NULL; + descriptor_file_ = NULL; + env_->DeleteFile(new_manifest_file); + } + } + + return s; +} + +Status VersionSet::Recover() { + struct LogReporter : public log::Reader::Reporter { + Status* status; + virtual void Corruption(size_t bytes, const Status& s) { + if (this->status->ok()) *this->status = s; + } + }; + + // Read "CURRENT" file, which contains a pointer to the current manifest file + std::string current; + Status s = ReadFileToString(env_, CurrentFileName(dbname_), ¤t); + if (!s.ok()) { + return s; + } + if (current.empty() || current[current.size()-1] != '\n') { + return Status::Corruption("CURRENT file does not end with newline"); + } + current.resize(current.size() - 1); + + std::string dscname = dbname_ + "/" + current; + SequentialFile* file; + s = env_->NewSequentialFile(dscname, &file); + if (!s.ok()) { + return s; + } + + bool have_log_number = false; + bool have_prev_log_number = false; + bool have_next_file = false; + bool have_last_sequence = false; + uint64_t next_file = 0; + uint64_t last_sequence = 0; + uint64_t log_number = 0; + uint64_t prev_log_number = 0; + Builder builder(this, current_); + + { + LogReporter reporter; + reporter.status = &s; + log::Reader reader(file, &reporter, true/*checksum*/, 0/*initial_offset*/); + Slice record; + std::string scratch; + while (reader.ReadRecord(&record, &scratch) && s.ok()) { + VersionEdit edit; + s = edit.DecodeFrom(record); + if (s.ok()) { + if (edit.has_comparator_ && + edit.comparator_ != icmp_.user_comparator()->Name()) { + s = Status::InvalidArgument( + edit.comparator_ + " does not match existing comparator ", + icmp_.user_comparator()->Name()); + } + } + + if (s.ok()) { + builder.Apply(&edit); + } + + if (edit.has_log_number_) { + log_number = edit.log_number_; + have_log_number = true; + } + + if (edit.has_prev_log_number_) { + prev_log_number = edit.prev_log_number_; + have_prev_log_number = true; + } + + if (edit.has_next_file_number_) { + next_file = edit.next_file_number_; + have_next_file = true; + } + + if (edit.has_last_sequence_) { + last_sequence = edit.last_sequence_; + have_last_sequence = true; + } + } + } + delete file; + file = NULL; + + if (s.ok()) { + if (!have_next_file) { + s = Status::Corruption("no meta-nextfile entry in descriptor"); + } else if (!have_log_number) { + s = Status::Corruption("no meta-lognumber entry in descriptor"); + } else if (!have_last_sequence) { + s = Status::Corruption("no last-sequence-number entry in descriptor"); + } + + if (!have_prev_log_number) { + prev_log_number = 0; + } + + MarkFileNumberUsed(prev_log_number); + MarkFileNumberUsed(log_number); + } + + if (s.ok()) { + Version* v = new Version(this); + builder.SaveTo(v); + // Install recovered version + Finalize(v); + AppendVersion(v); + manifest_file_number_ = next_file; + next_file_number_ = next_file + 1; + last_sequence_ = last_sequence; + log_number_ = log_number; + prev_log_number_ = prev_log_number; + } + + return s; +} + +void VersionSet::MarkFileNumberUsed(uint64_t number) { + if (next_file_number_ <= number) { + next_file_number_ = number + 1; + } +} + +void VersionSet::Finalize(Version* v) { + // Precomputed best level for next compaction + int best_level = -1; + double best_score = -1; + + for (int level = 0; level < config::kNumLevels-1; level++) { + double score; + if (level == 0) { + // We treat level-0 specially by bounding the number of files + // instead of number of bytes for two reasons: + // + // (1) With larger write-buffer sizes, it is nice not to do too + // many level-0 compactions. + // + // (2) The files in level-0 are merged on every read and + // therefore we wish to avoid too many files when the individual + // file size is small (perhaps because of a small write-buffer + // setting, or very high compression ratios, or lots of + // overwrites/deletions). + score = v->files_[level].size() / + static_cast(config::kL0_CompactionTrigger); + } else { + // Compute the ratio of current size to size limit. + const uint64_t level_bytes = TotalFileSize(v->files_[level]); + score = static_cast(level_bytes) / MaxBytesForLevel(level); + } + + if (score > best_score) { + best_level = level; + best_score = score; + } + } + + v->compaction_level_ = best_level; + v->compaction_score_ = best_score; +} + +Status VersionSet::WriteSnapshot(log::Writer* log) { + // TODO: Break up into multiple records to reduce memory usage on recovery? + + // Save metadata + VersionEdit edit; + edit.SetComparatorName(icmp_.user_comparator()->Name()); + + // Save compaction pointers + for (int level = 0; level < config::kNumLevels; level++) { + if (!compact_pointer_[level].empty()) { + InternalKey key; + key.DecodeFrom(compact_pointer_[level]); + edit.SetCompactPointer(level, key); + } + } + + // Save files + for (int level = 0; level < config::kNumLevels; level++) { + const std::vector& files = current_->files_[level]; + for (size_t i = 0; i < files.size(); i++) { + const FileMetaData* f = files[i]; + edit.AddFile(level, f->number, f->file_size, f->smallest, f->largest); + } + } + + std::string record; + edit.EncodeTo(&record); + return log->AddRecord(record); +} + +int VersionSet::NumLevelFiles(int level) const { + assert(level >= 0); + assert(level < config::kNumLevels); + return current_->files_[level].size(); +} + +const char* VersionSet::LevelSummary(LevelSummaryStorage* scratch) const { + // Update code if kNumLevels changes + assert(config::kNumLevels == 7); + snprintf(scratch->buffer, sizeof(scratch->buffer), + "files[ %d %d %d %d %d %d %d ]", + int(current_->files_[0].size()), + int(current_->files_[1].size()), + int(current_->files_[2].size()), + int(current_->files_[3].size()), + int(current_->files_[4].size()), + int(current_->files_[5].size()), + int(current_->files_[6].size())); + return scratch->buffer; +} + +// Return true iff the manifest contains the specified record. +bool VersionSet::ManifestContains(const std::string& record) const { + std::string fname = DescriptorFileName(dbname_, manifest_file_number_); + Log(options_->info_log, "ManifestContains: checking %s\n", fname.c_str()); + SequentialFile* file = NULL; + Status s = env_->NewSequentialFile(fname, &file); + if (!s.ok()) { + Log(options_->info_log, "ManifestContains: %s\n", s.ToString().c_str()); + return false; + } + log::Reader reader(file, NULL, true/*checksum*/, 0); + Slice r; + std::string scratch; + bool result = false; + while (reader.ReadRecord(&r, &scratch)) { + if (r == Slice(record)) { + result = true; + break; + } + } + delete file; + Log(options_->info_log, "ManifestContains: result = %d\n", result ? 1 : 0); + return result; +} + +uint64_t VersionSet::ApproximateOffsetOf(Version* v, const InternalKey& ikey) { + uint64_t result = 0; + for (int level = 0; level < config::kNumLevels; level++) { + const std::vector& files = v->files_[level]; + for (size_t i = 0; i < files.size(); i++) { + if (icmp_.Compare(files[i]->largest, ikey) <= 0) { + // Entire file is before "ikey", so just add the file size + result += files[i]->file_size; + } else if (icmp_.Compare(files[i]->smallest, ikey) > 0) { + // Entire file is after "ikey", so ignore + if (level > 0) { + // Files other than level 0 are sorted by meta->smallest, so + // no further files in this level will contain data for + // "ikey". + break; + } + } else { + // "ikey" falls in the range for this table. Add the + // approximate offset of "ikey" within the table. + Table* tableptr; + Iterator* iter = table_cache_->NewIterator( + ReadOptions(), files[i]->number, files[i]->file_size, &tableptr); + if (tableptr != NULL) { + result += tableptr->ApproximateOffsetOf(ikey.Encode()); + } + delete iter; + } + } + } + return result; +} + +void VersionSet::AddLiveFiles(std::set* live) { + for (Version* v = dummy_versions_.next_; + v != &dummy_versions_; + v = v->next_) { + for (int level = 0; level < config::kNumLevels; level++) { + const std::vector& files = v->files_[level]; + for (size_t i = 0; i < files.size(); i++) { + live->insert(files[i]->number); + } + } + } +} + +int64_t VersionSet::NumLevelBytes(int level) const { + assert(level >= 0); + assert(level < config::kNumLevels); + return TotalFileSize(current_->files_[level]); +} + +int64_t VersionSet::MaxNextLevelOverlappingBytes() { + int64_t result = 0; + std::vector overlaps; + for (int level = 1; level < config::kNumLevels - 1; level++) { + for (size_t i = 0; i < current_->files_[level].size(); i++) { + const FileMetaData* f = current_->files_[level][i]; + current_->GetOverlappingInputs(level+1, &f->smallest, &f->largest, + &overlaps); + const int64_t sum = TotalFileSize(overlaps); + if (sum > result) { + result = sum; + } + } + } + return result; +} + +// Stores the minimal range that covers all entries in inputs in +// *smallest, *largest. +// REQUIRES: inputs is not empty +void VersionSet::GetRange(const std::vector& inputs, + InternalKey* smallest, + InternalKey* largest) { + assert(!inputs.empty()); + smallest->Clear(); + largest->Clear(); + for (size_t i = 0; i < inputs.size(); i++) { + FileMetaData* f = inputs[i]; + if (i == 0) { + *smallest = f->smallest; + *largest = f->largest; + } else { + if (icmp_.Compare(f->smallest, *smallest) < 0) { + *smallest = f->smallest; + } + if (icmp_.Compare(f->largest, *largest) > 0) { + *largest = f->largest; + } + } + } +} + +// Stores the minimal range that covers all entries in inputs1 and inputs2 +// in *smallest, *largest. +// REQUIRES: inputs is not empty +void VersionSet::GetRange2(const std::vector& inputs1, + const std::vector& inputs2, + InternalKey* smallest, + InternalKey* largest) { + std::vector all = inputs1; + all.insert(all.end(), inputs2.begin(), inputs2.end()); + GetRange(all, smallest, largest); +} + +Iterator* VersionSet::MakeInputIterator(Compaction* c) { + ReadOptions options; + options.verify_checksums = options_->paranoid_checks; + options.fill_cache = false; + + // Level-0 files have to be merged together. For other levels, + // we will make a concatenating iterator per level. + // TODO(opt): use concatenating iterator for level-0 if there is no overlap + const int space = (c->level() == 0 ? c->inputs_[0].size() + 1 : 2); + Iterator** list = new Iterator*[space]; + int num = 0; + for (int which = 0; which < 2; which++) { + if (!c->inputs_[which].empty()) { + if (c->level() + which == 0) { + const std::vector& files = c->inputs_[which]; + for (size_t i = 0; i < files.size(); i++) { + list[num++] = table_cache_->NewIterator( + options, files[i]->number, files[i]->file_size); + } + } else { + // Create concatenating iterator for the files from this level + list[num++] = NewTwoLevelIterator( + new Version::LevelFileNumIterator(icmp_, &c->inputs_[which]), + &GetFileIterator, table_cache_, options); + } + } + } + assert(num <= space); + Iterator* result = NewMergingIterator(&icmp_, list, num); + delete[] list; + return result; +} + +Compaction* VersionSet::PickCompaction() { + Compaction* c; + int level; + + // We prefer compactions triggered by too much data in a level over + // the compactions triggered by seeks. + const bool size_compaction = (current_->compaction_score_ >= 1); + const bool seek_compaction = (current_->file_to_compact_ != NULL); + if (size_compaction) { + level = current_->compaction_level_; + assert(level >= 0); + assert(level+1 < config::kNumLevels); + c = new Compaction(level); + + // Pick the first file that comes after compact_pointer_[level] + for (size_t i = 0; i < current_->files_[level].size(); i++) { + FileMetaData* f = current_->files_[level][i]; + if (compact_pointer_[level].empty() || + icmp_.Compare(f->largest.Encode(), compact_pointer_[level]) > 0) { + c->inputs_[0].push_back(f); + break; + } + } + if (c->inputs_[0].empty()) { + // Wrap-around to the beginning of the key space + c->inputs_[0].push_back(current_->files_[level][0]); + } + } else if (seek_compaction) { + level = current_->file_to_compact_level_; + c = new Compaction(level); + c->inputs_[0].push_back(current_->file_to_compact_); + } else { + return NULL; + } + + c->input_version_ = current_; + c->input_version_->Ref(); + + // Files in level 0 may overlap each other, so pick up all overlapping ones + if (level == 0) { + InternalKey smallest, largest; + GetRange(c->inputs_[0], &smallest, &largest); + // Note that the next call will discard the file we placed in + // c->inputs_[0] earlier and replace it with an overlapping set + // which will include the picked file. + current_->GetOverlappingInputs(0, &smallest, &largest, &c->inputs_[0]); + assert(!c->inputs_[0].empty()); + } + + SetupOtherInputs(c); + + return c; +} + +void VersionSet::SetupOtherInputs(Compaction* c) { + const int level = c->level(); + InternalKey smallest, largest; + GetRange(c->inputs_[0], &smallest, &largest); + + current_->GetOverlappingInputs(level+1, &smallest, &largest, &c->inputs_[1]); + + // Get entire range covered by compaction + InternalKey all_start, all_limit; + GetRange2(c->inputs_[0], c->inputs_[1], &all_start, &all_limit); + + // See if we can grow the number of inputs in "level" without + // changing the number of "level+1" files we pick up. + if (!c->inputs_[1].empty()) { + std::vector expanded0; + current_->GetOverlappingInputs(level, &all_start, &all_limit, &expanded0); + const int64_t inputs0_size = TotalFileSize(c->inputs_[0]); + const int64_t inputs1_size = TotalFileSize(c->inputs_[1]); + const int64_t expanded0_size = TotalFileSize(expanded0); + if (expanded0.size() > c->inputs_[0].size() && + inputs1_size + expanded0_size < kExpandedCompactionByteSizeLimit) { + InternalKey new_start, new_limit; + GetRange(expanded0, &new_start, &new_limit); + std::vector expanded1; + current_->GetOverlappingInputs(level+1, &new_start, &new_limit, + &expanded1); + if (expanded1.size() == c->inputs_[1].size()) { + Log(options_->info_log, + "Expanding@%d %d+%d (%ld+%ld bytes) to %d+%d (%ld+%ld bytes)\n", + level, + int(c->inputs_[0].size()), + int(c->inputs_[1].size()), + long(inputs0_size), long(inputs1_size), + int(expanded0.size()), + int(expanded1.size()), + long(expanded0_size), long(inputs1_size)); + smallest = new_start; + largest = new_limit; + c->inputs_[0] = expanded0; + c->inputs_[1] = expanded1; + GetRange2(c->inputs_[0], c->inputs_[1], &all_start, &all_limit); + } + } + } + + // Compute the set of grandparent files that overlap this compaction + // (parent == level+1; grandparent == level+2) + if (level + 2 < config::kNumLevels) { + current_->GetOverlappingInputs(level + 2, &all_start, &all_limit, + &c->grandparents_); + } + + if (false) { + Log(options_->info_log, "Compacting %d '%s' .. '%s'", + level, + smallest.DebugString().c_str(), + largest.DebugString().c_str()); + } + + // Update the place where we will do the next compaction for this level. + // We update this immediately instead of waiting for the VersionEdit + // to be applied so that if the compaction fails, we will try a different + // key range next time. + compact_pointer_[level] = largest.Encode().ToString(); + c->edit_.SetCompactPointer(level, largest); +} + +Compaction* VersionSet::CompactRange( + int level, + const InternalKey* begin, + const InternalKey* end) { + std::vector inputs; + current_->GetOverlappingInputs(level, begin, end, &inputs); + if (inputs.empty()) { + return NULL; + } + + // Avoid compacting too much in one shot in case the range is large. + const uint64_t limit = MaxFileSizeForLevel(level); + uint64_t total = 0; + for (size_t i = 0; i < inputs.size(); i++) { + uint64_t s = inputs[i]->file_size; + total += s; + if (total >= limit) { + inputs.resize(i + 1); + break; + } + } + + Compaction* c = new Compaction(level); + c->input_version_ = current_; + c->input_version_->Ref(); + c->inputs_[0] = inputs; + SetupOtherInputs(c); + return c; +} + +Compaction::Compaction(int level) + : level_(level), + max_output_file_size_(MaxFileSizeForLevel(level)), + input_version_(NULL), + grandparent_index_(0), + seen_key_(false), + overlapped_bytes_(0) { + for (int i = 0; i < config::kNumLevels; i++) { + level_ptrs_[i] = 0; + } +} + +Compaction::~Compaction() { + if (input_version_ != NULL) { + input_version_->Unref(); + } +} + +bool Compaction::IsTrivialMove() const { + // Avoid a move if there is lots of overlapping grandparent data. + // Otherwise, the move could create a parent file that will require + // a very expensive merge later on. + return (num_input_files(0) == 1 && + num_input_files(1) == 0 && + TotalFileSize(grandparents_) <= kMaxGrandParentOverlapBytes); +} + +void Compaction::AddInputDeletions(VersionEdit* edit) { + for (int which = 0; which < 2; which++) { + for (size_t i = 0; i < inputs_[which].size(); i++) { + edit->DeleteFile(level_ + which, inputs_[which][i]->number); + } + } +} + +bool Compaction::IsBaseLevelForKey(const Slice& user_key) { + // Maybe use binary search to find right entry instead of linear search? + const Comparator* user_cmp = input_version_->vset_->icmp_.user_comparator(); + for (int lvl = level_ + 2; lvl < config::kNumLevels; lvl++) { + const std::vector& files = input_version_->files_[lvl]; + for (; level_ptrs_[lvl] < files.size(); ) { + FileMetaData* f = files[level_ptrs_[lvl]]; + if (user_cmp->Compare(user_key, f->largest.user_key()) <= 0) { + // We've advanced far enough + if (user_cmp->Compare(user_key, f->smallest.user_key()) >= 0) { + // Key falls in this file's range, so definitely not base level + return false; + } + break; + } + level_ptrs_[lvl]++; + } + } + return true; +} + +bool Compaction::ShouldStopBefore(const Slice& internal_key) { + // Scan to find earliest grandparent file that contains key. + const InternalKeyComparator* icmp = &input_version_->vset_->icmp_; + while (grandparent_index_ < grandparents_.size() && + icmp->Compare(internal_key, + grandparents_[grandparent_index_]->largest.Encode()) > 0) { + if (seen_key_) { + overlapped_bytes_ += grandparents_[grandparent_index_]->file_size; + } + grandparent_index_++; + } + seen_key_ = true; + + if (overlapped_bytes_ > kMaxGrandParentOverlapBytes) { + // Too much overlap for current output; start new output + overlapped_bytes_ = 0; + return true; + } else { + return false; + } +} + +void Compaction::ReleaseInputs() { + if (input_version_ != NULL) { + input_version_->Unref(); + input_version_ = NULL; + } +} + +} // namespace leveldb diff --git a/src/leveldb-lin/db/version_set.h b/src/leveldb-lin/db/version_set.h new file mode 100644 index 0000000..9d084fd --- /dev/null +++ b/src/leveldb-lin/db/version_set.h @@ -0,0 +1,383 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// The representation of a DBImpl consists of a set of Versions. The +// newest version is called "current". Older versions may be kept +// around to provide a consistent view to live iterators. +// +// Each Version keeps track of a set of Table files per level. The +// entire set of versions is maintained in a VersionSet. +// +// Version,VersionSet are thread-compatible, but require external +// synchronization on all accesses. + +#ifndef STORAGE_LEVELDB_DB_VERSION_SET_H_ +#define STORAGE_LEVELDB_DB_VERSION_SET_H_ + +#include +#include +#include +#include "db/dbformat.h" +#include "db/version_edit.h" +#include "port/port.h" +#include "port/thread_annotations.h" + +namespace leveldb { + +namespace log { class Writer; } + +class Compaction; +class Iterator; +class MemTable; +class TableBuilder; +class TableCache; +class Version; +class VersionSet; +class WritableFile; + +// Return the smallest index i such that files[i]->largest >= key. +// Return files.size() if there is no such file. +// REQUIRES: "files" contains a sorted list of non-overlapping files. +extern int FindFile(const InternalKeyComparator& icmp, + const std::vector& files, + const Slice& key); + +// Returns true iff some file in "files" overlaps the user key range +// [*smallest,*largest]. +// smallest==NULL represents a key smaller than all keys in the DB. +// largest==NULL represents a key largest than all keys in the DB. +// REQUIRES: If disjoint_sorted_files, files[] contains disjoint ranges +// in sorted order. +extern bool SomeFileOverlapsRange( + const InternalKeyComparator& icmp, + bool disjoint_sorted_files, + const std::vector& files, + const Slice* smallest_user_key, + const Slice* largest_user_key); + +class Version { + public: + // Append to *iters a sequence of iterators that will + // yield the contents of this Version when merged together. + // REQUIRES: This version has been saved (see VersionSet::SaveTo) + void AddIterators(const ReadOptions&, std::vector* iters); + + // Lookup the value for key. If found, store it in *val and + // return OK. Else return a non-OK status. Fills *stats. + // REQUIRES: lock is not held + struct GetStats { + FileMetaData* seek_file; + int seek_file_level; + }; + Status Get(const ReadOptions&, const LookupKey& key, std::string* val, + GetStats* stats); + + // Adds "stats" into the current state. Returns true if a new + // compaction may need to be triggered, false otherwise. + // REQUIRES: lock is held + bool UpdateStats(const GetStats& stats); + + // Reference count management (so Versions do not disappear out from + // under live iterators) + void Ref(); + void Unref(); + + void GetOverlappingInputs( + int level, + const InternalKey* begin, // NULL means before all keys + const InternalKey* end, // NULL means after all keys + std::vector* inputs); + + // Returns true iff some file in the specified level overlaps + // some part of [*smallest_user_key,*largest_user_key]. + // smallest_user_key==NULL represents a key smaller than all keys in the DB. + // largest_user_key==NULL represents a key largest than all keys in the DB. + bool OverlapInLevel(int level, + const Slice* smallest_user_key, + const Slice* largest_user_key); + + // Return the level at which we should place a new memtable compaction + // result that covers the range [smallest_user_key,largest_user_key]. + int PickLevelForMemTableOutput(const Slice& smallest_user_key, + const Slice& largest_user_key); + + int NumFiles(int level) const { return files_[level].size(); } + + // Return a human readable string that describes this version's contents. + std::string DebugString() const; + + private: + friend class Compaction; + friend class VersionSet; + + class LevelFileNumIterator; + Iterator* NewConcatenatingIterator(const ReadOptions&, int level) const; + + VersionSet* vset_; // VersionSet to which this Version belongs + Version* next_; // Next version in linked list + Version* prev_; // Previous version in linked list + int refs_; // Number of live refs to this version + + // List of files per level + std::vector files_[config::kNumLevels]; + + // Next file to compact based on seek stats. + FileMetaData* file_to_compact_; + int file_to_compact_level_; + + // Level that should be compacted next and its compaction score. + // Score < 1 means compaction is not strictly needed. These fields + // are initialized by Finalize(). + double compaction_score_; + int compaction_level_; + + explicit Version(VersionSet* vset) + : vset_(vset), next_(this), prev_(this), refs_(0), + file_to_compact_(NULL), + file_to_compact_level_(-1), + compaction_score_(-1), + compaction_level_(-1) { + } + + ~Version(); + + // No copying allowed + Version(const Version&); + void operator=(const Version&); +}; + +class VersionSet { + public: + VersionSet(const std::string& dbname, + const Options* options, + TableCache* table_cache, + const InternalKeyComparator*); + ~VersionSet(); + + // Apply *edit to the current version to form a new descriptor that + // is both saved to persistent state and installed as the new + // current version. Will release *mu while actually writing to the file. + // REQUIRES: *mu is held on entry. + // REQUIRES: no other thread concurrently calls LogAndApply() + Status LogAndApply(VersionEdit* edit, port::Mutex* mu) + EXCLUSIVE_LOCKS_REQUIRED(mu); + + // Recover the last saved descriptor from persistent storage. + Status Recover(); + + // Return the current version. + Version* current() const { return current_; } + + // Return the current manifest file number + uint64_t ManifestFileNumber() const { return manifest_file_number_; } + + // Allocate and return a new file number + uint64_t NewFileNumber() { return next_file_number_++; } + + // Arrange to reuse "file_number" unless a newer file number has + // already been allocated. + // REQUIRES: "file_number" was returned by a call to NewFileNumber(). + void ReuseFileNumber(uint64_t file_number) { + if (next_file_number_ == file_number + 1) { + next_file_number_ = file_number; + } + } + + // Return the number of Table files at the specified level. + int NumLevelFiles(int level) const; + + // Return the combined file size of all files at the specified level. + int64_t NumLevelBytes(int level) const; + + // Return the last sequence number. + uint64_t LastSequence() const { return last_sequence_; } + + // Set the last sequence number to s. + void SetLastSequence(uint64_t s) { + assert(s >= last_sequence_); + last_sequence_ = s; + } + + // Mark the specified file number as used. + void MarkFileNumberUsed(uint64_t number); + + // Return the current log file number. + uint64_t LogNumber() const { return log_number_; } + + // Return the log file number for the log file that is currently + // being compacted, or zero if there is no such log file. + uint64_t PrevLogNumber() const { return prev_log_number_; } + + // Pick level and inputs for a new compaction. + // Returns NULL if there is no compaction to be done. + // Otherwise returns a pointer to a heap-allocated object that + // describes the compaction. Caller should delete the result. + Compaction* PickCompaction(); + + // Return a compaction object for compacting the range [begin,end] in + // the specified level. Returns NULL if there is nothing in that + // level that overlaps the specified range. Caller should delete + // the result. + Compaction* CompactRange( + int level, + const InternalKey* begin, + const InternalKey* end); + + // Return the maximum overlapping data (in bytes) at next level for any + // file at a level >= 1. + int64_t MaxNextLevelOverlappingBytes(); + + // Create an iterator that reads over the compaction inputs for "*c". + // The caller should delete the iterator when no longer needed. + Iterator* MakeInputIterator(Compaction* c); + + // Returns true iff some level needs a compaction. + bool NeedsCompaction() const { + Version* v = current_; + return (v->compaction_score_ >= 1) || (v->file_to_compact_ != NULL); + } + + // Add all files listed in any live version to *live. + // May also mutate some internal state. + void AddLiveFiles(std::set* live); + + // Return the approximate offset in the database of the data for + // "key" as of version "v". + uint64_t ApproximateOffsetOf(Version* v, const InternalKey& key); + + // Return a human-readable short (single-line) summary of the number + // of files per level. Uses *scratch as backing store. + struct LevelSummaryStorage { + char buffer[100]; + }; + const char* LevelSummary(LevelSummaryStorage* scratch) const; + + private: + class Builder; + + friend class Compaction; + friend class Version; + + void Finalize(Version* v); + + void GetRange(const std::vector& inputs, + InternalKey* smallest, + InternalKey* largest); + + void GetRange2(const std::vector& inputs1, + const std::vector& inputs2, + InternalKey* smallest, + InternalKey* largest); + + void SetupOtherInputs(Compaction* c); + + // Save current contents to *log + Status WriteSnapshot(log::Writer* log); + + void AppendVersion(Version* v); + + bool ManifestContains(const std::string& record) const; + + Env* const env_; + const std::string dbname_; + const Options* const options_; + TableCache* const table_cache_; + const InternalKeyComparator icmp_; + uint64_t next_file_number_; + uint64_t manifest_file_number_; + uint64_t last_sequence_; + uint64_t log_number_; + uint64_t prev_log_number_; // 0 or backing store for memtable being compacted + + // Opened lazily + WritableFile* descriptor_file_; + log::Writer* descriptor_log_; + Version dummy_versions_; // Head of circular doubly-linked list of versions. + Version* current_; // == dummy_versions_.prev_ + + // Per-level key at which the next compaction at that level should start. + // Either an empty string, or a valid InternalKey. + std::string compact_pointer_[config::kNumLevels]; + + // No copying allowed + VersionSet(const VersionSet&); + void operator=(const VersionSet&); +}; + +// A Compaction encapsulates information about a compaction. +class Compaction { + public: + ~Compaction(); + + // Return the level that is being compacted. Inputs from "level" + // and "level+1" will be merged to produce a set of "level+1" files. + int level() const { return level_; } + + // Return the object that holds the edits to the descriptor done + // by this compaction. + VersionEdit* edit() { return &edit_; } + + // "which" must be either 0 or 1 + int num_input_files(int which) const { return inputs_[which].size(); } + + // Return the ith input file at "level()+which" ("which" must be 0 or 1). + FileMetaData* input(int which, int i) const { return inputs_[which][i]; } + + // Maximum size of files to build during this compaction. + uint64_t MaxOutputFileSize() const { return max_output_file_size_; } + + // Is this a trivial compaction that can be implemented by just + // moving a single input file to the next level (no merging or splitting) + bool IsTrivialMove() const; + + // Add all inputs to this compaction as delete operations to *edit. + void AddInputDeletions(VersionEdit* edit); + + // Returns true if the information we have available guarantees that + // the compaction is producing data in "level+1" for which no data exists + // in levels greater than "level+1". + bool IsBaseLevelForKey(const Slice& user_key); + + // Returns true iff we should stop building the current output + // before processing "internal_key". + bool ShouldStopBefore(const Slice& internal_key); + + // Release the input version for the compaction, once the compaction + // is successful. + void ReleaseInputs(); + + private: + friend class Version; + friend class VersionSet; + + explicit Compaction(int level); + + int level_; + uint64_t max_output_file_size_; + Version* input_version_; + VersionEdit edit_; + + // Each compaction reads inputs from "level_" and "level_+1" + std::vector inputs_[2]; // The two sets of inputs + + // State used to check for number of of overlapping grandparent files + // (parent == level_ + 1, grandparent == level_ + 2) + std::vector grandparents_; + size_t grandparent_index_; // Index in grandparent_starts_ + bool seen_key_; // Some output key has been seen + int64_t overlapped_bytes_; // Bytes of overlap between current output + // and grandparent files + + // State for implementing IsBaseLevelForKey + + // level_ptrs_ holds indices into input_version_->levels_: our state + // is that we are positioned at one of the file ranges for each + // higher level than the ones involved in this compaction (i.e. for + // all L >= level_ + 2). + size_t level_ptrs_[config::kNumLevels]; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_VERSION_SET_H_ diff --git a/src/leveldb-lin/db/version_set.o b/src/leveldb-lin/db/version_set.o new file mode 100644 index 0000000..83bc7ef Binary files /dev/null and b/src/leveldb-lin/db/version_set.o differ diff --git a/src/leveldb-lin/db/version_set_test.cc b/src/leveldb-lin/db/version_set_test.cc new file mode 100644 index 0000000..501e34d --- /dev/null +++ b/src/leveldb-lin/db/version_set_test.cc @@ -0,0 +1,179 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/version_set.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +class FindFileTest { + public: + std::vector files_; + bool disjoint_sorted_files_; + + FindFileTest() : disjoint_sorted_files_(true) { } + + ~FindFileTest() { + for (int i = 0; i < files_.size(); i++) { + delete files_[i]; + } + } + + void Add(const char* smallest, const char* largest, + SequenceNumber smallest_seq = 100, + SequenceNumber largest_seq = 100) { + FileMetaData* f = new FileMetaData; + f->number = files_.size() + 1; + f->smallest = InternalKey(smallest, smallest_seq, kTypeValue); + f->largest = InternalKey(largest, largest_seq, kTypeValue); + files_.push_back(f); + } + + int Find(const char* key) { + InternalKey target(key, 100, kTypeValue); + InternalKeyComparator cmp(BytewiseComparator()); + return FindFile(cmp, files_, target.Encode()); + } + + bool Overlaps(const char* smallest, const char* largest) { + InternalKeyComparator cmp(BytewiseComparator()); + Slice s(smallest != NULL ? smallest : ""); + Slice l(largest != NULL ? largest : ""); + return SomeFileOverlapsRange(cmp, disjoint_sorted_files_, files_, + (smallest != NULL ? &s : NULL), + (largest != NULL ? &l : NULL)); + } +}; + +TEST(FindFileTest, Empty) { + ASSERT_EQ(0, Find("foo")); + ASSERT_TRUE(! Overlaps("a", "z")); + ASSERT_TRUE(! Overlaps(NULL, "z")); + ASSERT_TRUE(! Overlaps("a", NULL)); + ASSERT_TRUE(! Overlaps(NULL, NULL)); +} + +TEST(FindFileTest, Single) { + Add("p", "q"); + ASSERT_EQ(0, Find("a")); + ASSERT_EQ(0, Find("p")); + ASSERT_EQ(0, Find("p1")); + ASSERT_EQ(0, Find("q")); + ASSERT_EQ(1, Find("q1")); + ASSERT_EQ(1, Find("z")); + + ASSERT_TRUE(! Overlaps("a", "b")); + ASSERT_TRUE(! Overlaps("z1", "z2")); + ASSERT_TRUE(Overlaps("a", "p")); + ASSERT_TRUE(Overlaps("a", "q")); + ASSERT_TRUE(Overlaps("a", "z")); + ASSERT_TRUE(Overlaps("p", "p1")); + ASSERT_TRUE(Overlaps("p", "q")); + ASSERT_TRUE(Overlaps("p", "z")); + ASSERT_TRUE(Overlaps("p1", "p2")); + ASSERT_TRUE(Overlaps("p1", "z")); + ASSERT_TRUE(Overlaps("q", "q")); + ASSERT_TRUE(Overlaps("q", "q1")); + + ASSERT_TRUE(! Overlaps(NULL, "j")); + ASSERT_TRUE(! Overlaps("r", NULL)); + ASSERT_TRUE(Overlaps(NULL, "p")); + ASSERT_TRUE(Overlaps(NULL, "p1")); + ASSERT_TRUE(Overlaps("q", NULL)); + ASSERT_TRUE(Overlaps(NULL, NULL)); +} + + +TEST(FindFileTest, Multiple) { + Add("150", "200"); + Add("200", "250"); + Add("300", "350"); + Add("400", "450"); + ASSERT_EQ(0, Find("100")); + ASSERT_EQ(0, Find("150")); + ASSERT_EQ(0, Find("151")); + ASSERT_EQ(0, Find("199")); + ASSERT_EQ(0, Find("200")); + ASSERT_EQ(1, Find("201")); + ASSERT_EQ(1, Find("249")); + ASSERT_EQ(1, Find("250")); + ASSERT_EQ(2, Find("251")); + ASSERT_EQ(2, Find("299")); + ASSERT_EQ(2, Find("300")); + ASSERT_EQ(2, Find("349")); + ASSERT_EQ(2, Find("350")); + ASSERT_EQ(3, Find("351")); + ASSERT_EQ(3, Find("400")); + ASSERT_EQ(3, Find("450")); + ASSERT_EQ(4, Find("451")); + + ASSERT_TRUE(! Overlaps("100", "149")); + ASSERT_TRUE(! Overlaps("251", "299")); + ASSERT_TRUE(! Overlaps("451", "500")); + ASSERT_TRUE(! Overlaps("351", "399")); + + ASSERT_TRUE(Overlaps("100", "150")); + ASSERT_TRUE(Overlaps("100", "200")); + ASSERT_TRUE(Overlaps("100", "300")); + ASSERT_TRUE(Overlaps("100", "400")); + ASSERT_TRUE(Overlaps("100", "500")); + ASSERT_TRUE(Overlaps("375", "400")); + ASSERT_TRUE(Overlaps("450", "450")); + ASSERT_TRUE(Overlaps("450", "500")); +} + +TEST(FindFileTest, MultipleNullBoundaries) { + Add("150", "200"); + Add("200", "250"); + Add("300", "350"); + Add("400", "450"); + ASSERT_TRUE(! Overlaps(NULL, "149")); + ASSERT_TRUE(! Overlaps("451", NULL)); + ASSERT_TRUE(Overlaps(NULL, NULL)); + ASSERT_TRUE(Overlaps(NULL, "150")); + ASSERT_TRUE(Overlaps(NULL, "199")); + ASSERT_TRUE(Overlaps(NULL, "200")); + ASSERT_TRUE(Overlaps(NULL, "201")); + ASSERT_TRUE(Overlaps(NULL, "400")); + ASSERT_TRUE(Overlaps(NULL, "800")); + ASSERT_TRUE(Overlaps("100", NULL)); + ASSERT_TRUE(Overlaps("200", NULL)); + ASSERT_TRUE(Overlaps("449", NULL)); + ASSERT_TRUE(Overlaps("450", NULL)); +} + +TEST(FindFileTest, OverlapSequenceChecks) { + Add("200", "200", 5000, 3000); + ASSERT_TRUE(! Overlaps("199", "199")); + ASSERT_TRUE(! Overlaps("201", "300")); + ASSERT_TRUE(Overlaps("200", "200")); + ASSERT_TRUE(Overlaps("190", "200")); + ASSERT_TRUE(Overlaps("200", "210")); +} + +TEST(FindFileTest, OverlappingFiles) { + Add("150", "600"); + Add("400", "500"); + disjoint_sorted_files_ = false; + ASSERT_TRUE(! Overlaps("100", "149")); + ASSERT_TRUE(! Overlaps("601", "700")); + ASSERT_TRUE(Overlaps("100", "150")); + ASSERT_TRUE(Overlaps("100", "200")); + ASSERT_TRUE(Overlaps("100", "300")); + ASSERT_TRUE(Overlaps("100", "400")); + ASSERT_TRUE(Overlaps("100", "500")); + ASSERT_TRUE(Overlaps("375", "400")); + ASSERT_TRUE(Overlaps("450", "450")); + ASSERT_TRUE(Overlaps("450", "500")); + ASSERT_TRUE(Overlaps("450", "700")); + ASSERT_TRUE(Overlaps("600", "700")); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/db/write_batch.cc b/src/leveldb-lin/db/write_batch.cc new file mode 100644 index 0000000..33f4a42 --- /dev/null +++ b/src/leveldb-lin/db/write_batch.cc @@ -0,0 +1,147 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// WriteBatch::rep_ := +// sequence: fixed64 +// count: fixed32 +// data: record[count] +// record := +// kTypeValue varstring varstring | +// kTypeDeletion varstring +// varstring := +// len: varint32 +// data: uint8[len] + +#include "leveldb/write_batch.h" + +#include "leveldb/db.h" +#include "db/dbformat.h" +#include "db/memtable.h" +#include "db/write_batch_internal.h" +#include "util/coding.h" + +namespace leveldb { + +// WriteBatch header has an 8-byte sequence number followed by a 4-byte count. +static const size_t kHeader = 12; + +WriteBatch::WriteBatch() { + Clear(); +} + +WriteBatch::~WriteBatch() { } + +WriteBatch::Handler::~Handler() { } + +void WriteBatch::Clear() { + rep_.clear(); + rep_.resize(kHeader); +} + +Status WriteBatch::Iterate(Handler* handler) const { + Slice input(rep_); + if (input.size() < kHeader) { + return Status::Corruption("malformed WriteBatch (too small)"); + } + + input.remove_prefix(kHeader); + Slice key, value; + int found = 0; + while (!input.empty()) { + found++; + char tag = input[0]; + input.remove_prefix(1); + switch (tag) { + case kTypeValue: + if (GetLengthPrefixedSlice(&input, &key) && + GetLengthPrefixedSlice(&input, &value)) { + handler->Put(key, value); + } else { + return Status::Corruption("bad WriteBatch Put"); + } + break; + case kTypeDeletion: + if (GetLengthPrefixedSlice(&input, &key)) { + handler->Delete(key); + } else { + return Status::Corruption("bad WriteBatch Delete"); + } + break; + default: + return Status::Corruption("unknown WriteBatch tag"); + } + } + if (found != WriteBatchInternal::Count(this)) { + return Status::Corruption("WriteBatch has wrong count"); + } else { + return Status::OK(); + } +} + +int WriteBatchInternal::Count(const WriteBatch* b) { + return DecodeFixed32(b->rep_.data() + 8); +} + +void WriteBatchInternal::SetCount(WriteBatch* b, int n) { + EncodeFixed32(&b->rep_[8], n); +} + +SequenceNumber WriteBatchInternal::Sequence(const WriteBatch* b) { + return SequenceNumber(DecodeFixed64(b->rep_.data())); +} + +void WriteBatchInternal::SetSequence(WriteBatch* b, SequenceNumber seq) { + EncodeFixed64(&b->rep_[0], seq); +} + +void WriteBatch::Put(const Slice& key, const Slice& value) { + WriteBatchInternal::SetCount(this, WriteBatchInternal::Count(this) + 1); + rep_.push_back(static_cast(kTypeValue)); + PutLengthPrefixedSlice(&rep_, key); + PutLengthPrefixedSlice(&rep_, value); +} + +void WriteBatch::Delete(const Slice& key) { + WriteBatchInternal::SetCount(this, WriteBatchInternal::Count(this) + 1); + rep_.push_back(static_cast(kTypeDeletion)); + PutLengthPrefixedSlice(&rep_, key); +} + +namespace { +class MemTableInserter : public WriteBatch::Handler { + public: + SequenceNumber sequence_; + MemTable* mem_; + + virtual void Put(const Slice& key, const Slice& value) { + mem_->Add(sequence_, kTypeValue, key, value); + sequence_++; + } + virtual void Delete(const Slice& key) { + mem_->Add(sequence_, kTypeDeletion, key, Slice()); + sequence_++; + } +}; +} // namespace + +Status WriteBatchInternal::InsertInto(const WriteBatch* b, + MemTable* memtable) { + MemTableInserter inserter; + inserter.sequence_ = WriteBatchInternal::Sequence(b); + inserter.mem_ = memtable; + return b->Iterate(&inserter); +} + +void WriteBatchInternal::SetContents(WriteBatch* b, const Slice& contents) { + assert(contents.size() >= kHeader); + b->rep_.assign(contents.data(), contents.size()); +} + +void WriteBatchInternal::Append(WriteBatch* dst, const WriteBatch* src) { + SetCount(dst, Count(dst) + Count(src)); + assert(src->rep_.size() >= kHeader); + dst->rep_.append(src->rep_.data() + kHeader, src->rep_.size() - kHeader); +} + +} // namespace leveldb diff --git a/src/leveldb-lin/db/write_batch.o b/src/leveldb-lin/db/write_batch.o new file mode 100644 index 0000000..ff6b661 Binary files /dev/null and b/src/leveldb-lin/db/write_batch.o differ diff --git a/src/leveldb-lin/db/write_batch_internal.h b/src/leveldb-lin/db/write_batch_internal.h new file mode 100644 index 0000000..4423a7f --- /dev/null +++ b/src/leveldb-lin/db/write_batch_internal.h @@ -0,0 +1,49 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_ +#define STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_ + +#include "leveldb/write_batch.h" + +namespace leveldb { + +class MemTable; + +// WriteBatchInternal provides static methods for manipulating a +// WriteBatch that we don't want in the public WriteBatch interface. +class WriteBatchInternal { + public: + // Return the number of entries in the batch. + static int Count(const WriteBatch* batch); + + // Set the count for the number of entries in the batch. + static void SetCount(WriteBatch* batch, int n); + + // Return the seqeunce number for the start of this batch. + static SequenceNumber Sequence(const WriteBatch* batch); + + // Store the specified number as the seqeunce number for the start of + // this batch. + static void SetSequence(WriteBatch* batch, SequenceNumber seq); + + static Slice Contents(const WriteBatch* batch) { + return Slice(batch->rep_); + } + + static size_t ByteSize(const WriteBatch* batch) { + return batch->rep_.size(); + } + + static void SetContents(WriteBatch* batch, const Slice& contents); + + static Status InsertInto(const WriteBatch* batch, MemTable* memtable); + + static void Append(WriteBatch* dst, const WriteBatch* src); +}; + +} // namespace leveldb + + +#endif // STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_ diff --git a/src/leveldb-lin/db/write_batch_test.cc b/src/leveldb-lin/db/write_batch_test.cc new file mode 100644 index 0000000..9064e3d --- /dev/null +++ b/src/leveldb-lin/db/write_batch_test.cc @@ -0,0 +1,120 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" + +#include "db/memtable.h" +#include "db/write_batch_internal.h" +#include "leveldb/env.h" +#include "util/logging.h" +#include "util/testharness.h" + +namespace leveldb { + +static std::string PrintContents(WriteBatch* b) { + InternalKeyComparator cmp(BytewiseComparator()); + MemTable* mem = new MemTable(cmp); + mem->Ref(); + std::string state; + Status s = WriteBatchInternal::InsertInto(b, mem); + int count = 0; + Iterator* iter = mem->NewIterator(); + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + ParsedInternalKey ikey; + ASSERT_TRUE(ParseInternalKey(iter->key(), &ikey)); + switch (ikey.type) { + case kTypeValue: + state.append("Put("); + state.append(ikey.user_key.ToString()); + state.append(", "); + state.append(iter->value().ToString()); + state.append(")"); + count++; + break; + case kTypeDeletion: + state.append("Delete("); + state.append(ikey.user_key.ToString()); + state.append(")"); + count++; + break; + } + state.append("@"); + state.append(NumberToString(ikey.sequence)); + } + delete iter; + if (!s.ok()) { + state.append("ParseError()"); + } else if (count != WriteBatchInternal::Count(b)) { + state.append("CountMismatch()"); + } + mem->Unref(); + return state; +} + +class WriteBatchTest { }; + +TEST(WriteBatchTest, Empty) { + WriteBatch batch; + ASSERT_EQ("", PrintContents(&batch)); + ASSERT_EQ(0, WriteBatchInternal::Count(&batch)); +} + +TEST(WriteBatchTest, Multiple) { + WriteBatch batch; + batch.Put(Slice("foo"), Slice("bar")); + batch.Delete(Slice("box")); + batch.Put(Slice("baz"), Slice("boo")); + WriteBatchInternal::SetSequence(&batch, 100); + ASSERT_EQ(100, WriteBatchInternal::Sequence(&batch)); + ASSERT_EQ(3, WriteBatchInternal::Count(&batch)); + ASSERT_EQ("Put(baz, boo)@102" + "Delete(box)@101" + "Put(foo, bar)@100", + PrintContents(&batch)); +} + +TEST(WriteBatchTest, Corruption) { + WriteBatch batch; + batch.Put(Slice("foo"), Slice("bar")); + batch.Delete(Slice("box")); + WriteBatchInternal::SetSequence(&batch, 200); + Slice contents = WriteBatchInternal::Contents(&batch); + WriteBatchInternal::SetContents(&batch, + Slice(contents.data(),contents.size()-1)); + ASSERT_EQ("Put(foo, bar)@200" + "ParseError()", + PrintContents(&batch)); +} + +TEST(WriteBatchTest, Append) { + WriteBatch b1, b2; + WriteBatchInternal::SetSequence(&b1, 200); + WriteBatchInternal::SetSequence(&b2, 300); + WriteBatchInternal::Append(&b1, &b2); + ASSERT_EQ("", + PrintContents(&b1)); + b2.Put("a", "va"); + WriteBatchInternal::Append(&b1, &b2); + ASSERT_EQ("Put(a, va)@200", + PrintContents(&b1)); + b2.Clear(); + b2.Put("b", "vb"); + WriteBatchInternal::Append(&b1, &b2); + ASSERT_EQ("Put(a, va)@200" + "Put(b, vb)@201", + PrintContents(&b1)); + b2.Delete("foo"); + WriteBatchInternal::Append(&b1, &b2); + ASSERT_EQ("Put(a, va)@200" + "Put(b, vb)@202" + "Put(b, vb)@201" + "Delete(foo)@203", + PrintContents(&b1)); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/doc/bench/db_bench_sqlite3.cc b/src/leveldb-lin/doc/bench/db_bench_sqlite3.cc new file mode 100644 index 0000000..e63aaa8 --- /dev/null +++ b/src/leveldb-lin/doc/bench/db_bench_sqlite3.cc @@ -0,0 +1,718 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include +#include "util/histogram.h" +#include "util/random.h" +#include "util/testutil.h" + +// Comma-separated list of operations to run in the specified order +// Actual benchmarks: +// +// fillseq -- write N values in sequential key order in async mode +// fillseqsync -- write N/100 values in sequential key order in sync mode +// fillseqbatch -- batch write N values in sequential key order in async mode +// fillrandom -- write N values in random key order in async mode +// fillrandsync -- write N/100 values in random key order in sync mode +// fillrandbatch -- batch write N values in sequential key order in async mode +// overwrite -- overwrite N values in random key order in async mode +// fillrand100K -- write N/1000 100K values in random order in async mode +// fillseq100K -- write N/1000 100K values in sequential order in async mode +// readseq -- read N times sequentially +// readrandom -- read N times in random order +// readrand100K -- read N/1000 100K values in sequential order in async mode +static const char* FLAGS_benchmarks = + "fillseq," + "fillseqsync," + "fillseqbatch," + "fillrandom," + "fillrandsync," + "fillrandbatch," + "overwrite," + "overwritebatch," + "readrandom," + "readseq," + "fillrand100K," + "fillseq100K," + "readseq," + "readrand100K," + ; + +// Number of key/values to place in database +static int FLAGS_num = 1000000; + +// Number of read operations to do. If negative, do FLAGS_num reads. +static int FLAGS_reads = -1; + +// Size of each value +static int FLAGS_value_size = 100; + +// Print histogram of operation timings +static bool FLAGS_histogram = false; + +// Arrange to generate values that shrink to this fraction of +// their original size after compression +static double FLAGS_compression_ratio = 0.5; + +// Page size. Default 1 KB. +static int FLAGS_page_size = 1024; + +// Number of pages. +// Default cache size = FLAGS_page_size * FLAGS_num_pages = 4 MB. +static int FLAGS_num_pages = 4096; + +// If true, do not destroy the existing database. If you set this +// flag and also specify a benchmark that wants a fresh database, that +// benchmark will fail. +static bool FLAGS_use_existing_db = false; + +// If true, we allow batch writes to occur +static bool FLAGS_transaction = true; + +// If true, we enable Write-Ahead Logging +static bool FLAGS_WAL_enabled = true; + +// Use the db with the following name. +static const char* FLAGS_db = NULL; + +inline +static void ExecErrorCheck(int status, char *err_msg) { + if (status != SQLITE_OK) { + fprintf(stderr, "SQL error: %s\n", err_msg); + sqlite3_free(err_msg); + exit(1); + } +} + +inline +static void StepErrorCheck(int status) { + if (status != SQLITE_DONE) { + fprintf(stderr, "SQL step error: status = %d\n", status); + exit(1); + } +} + +inline +static void ErrorCheck(int status) { + if (status != SQLITE_OK) { + fprintf(stderr, "sqlite3 error: status = %d\n", status); + exit(1); + } +} + +inline +static void WalCheckpoint(sqlite3* db_) { + // Flush all writes to disk + if (FLAGS_WAL_enabled) { + sqlite3_wal_checkpoint_v2(db_, NULL, SQLITE_CHECKPOINT_FULL, NULL, NULL); + } +} + +namespace leveldb { + +// Helper for quickly generating random data. +namespace { +class RandomGenerator { + private: + std::string data_; + int pos_; + + public: + RandomGenerator() { + // We use a limited amount of data over and over again and ensure + // that it is larger than the compression window (32KB), and also + // large enough to serve all typical value sizes we want to write. + Random rnd(301); + std::string piece; + while (data_.size() < 1048576) { + // Add a short fragment that is as compressible as specified + // by FLAGS_compression_ratio. + test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece); + data_.append(piece); + } + pos_ = 0; + } + + Slice Generate(int len) { + if (pos_ + len > data_.size()) { + pos_ = 0; + assert(len < data_.size()); + } + pos_ += len; + return Slice(data_.data() + pos_ - len, len); + } +}; + +static Slice TrimSpace(Slice s) { + int start = 0; + while (start < s.size() && isspace(s[start])) { + start++; + } + int limit = s.size(); + while (limit > start && isspace(s[limit-1])) { + limit--; + } + return Slice(s.data() + start, limit - start); +} + +} // namespace + +class Benchmark { + private: + sqlite3* db_; + int db_num_; + int num_; + int reads_; + double start_; + double last_op_finish_; + int64_t bytes_; + std::string message_; + Histogram hist_; + RandomGenerator gen_; + Random rand_; + + // State kept for progress messages + int done_; + int next_report_; // When to report next + + void PrintHeader() { + const int kKeySize = 16; + PrintEnvironment(); + fprintf(stdout, "Keys: %d bytes each\n", kKeySize); + fprintf(stdout, "Values: %d bytes each\n", FLAGS_value_size); + fprintf(stdout, "Entries: %d\n", num_); + fprintf(stdout, "RawSize: %.1f MB (estimated)\n", + ((static_cast(kKeySize + FLAGS_value_size) * num_) + / 1048576.0)); + PrintWarnings(); + fprintf(stdout, "------------------------------------------------\n"); + } + + void PrintWarnings() { +#if defined(__GNUC__) && !defined(__OPTIMIZE__) + fprintf(stdout, + "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n" + ); +#endif +#ifndef NDEBUG + fprintf(stdout, + "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n"); +#endif + } + + void PrintEnvironment() { + fprintf(stderr, "SQLite: version %s\n", SQLITE_VERSION); + +#if defined(__linux) + time_t now = time(NULL); + fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline + + FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); + if (cpuinfo != NULL) { + char line[1000]; + int num_cpus = 0; + std::string cpu_type; + std::string cache_size; + while (fgets(line, sizeof(line), cpuinfo) != NULL) { + const char* sep = strchr(line, ':'); + if (sep == NULL) { + continue; + } + Slice key = TrimSpace(Slice(line, sep - 1 - line)); + Slice val = TrimSpace(Slice(sep + 1)); + if (key == "model name") { + ++num_cpus; + cpu_type = val.ToString(); + } else if (key == "cache size") { + cache_size = val.ToString(); + } + } + fclose(cpuinfo); + fprintf(stderr, "CPU: %d * %s\n", num_cpus, cpu_type.c_str()); + fprintf(stderr, "CPUCache: %s\n", cache_size.c_str()); + } +#endif + } + + void Start() { + start_ = Env::Default()->NowMicros() * 1e-6; + bytes_ = 0; + message_.clear(); + last_op_finish_ = start_; + hist_.Clear(); + done_ = 0; + next_report_ = 100; + } + + void FinishedSingleOp() { + if (FLAGS_histogram) { + double now = Env::Default()->NowMicros() * 1e-6; + double micros = (now - last_op_finish_) * 1e6; + hist_.Add(micros); + if (micros > 20000) { + fprintf(stderr, "long op: %.1f micros%30s\r", micros, ""); + fflush(stderr); + } + last_op_finish_ = now; + } + + done_++; + if (done_ >= next_report_) { + if (next_report_ < 1000) next_report_ += 100; + else if (next_report_ < 5000) next_report_ += 500; + else if (next_report_ < 10000) next_report_ += 1000; + else if (next_report_ < 50000) next_report_ += 5000; + else if (next_report_ < 100000) next_report_ += 10000; + else if (next_report_ < 500000) next_report_ += 50000; + else next_report_ += 100000; + fprintf(stderr, "... finished %d ops%30s\r", done_, ""); + fflush(stderr); + } + } + + void Stop(const Slice& name) { + double finish = Env::Default()->NowMicros() * 1e-6; + + // Pretend at least one op was done in case we are running a benchmark + // that does not call FinishedSingleOp(). + if (done_ < 1) done_ = 1; + + if (bytes_ > 0) { + char rate[100]; + snprintf(rate, sizeof(rate), "%6.1f MB/s", + (bytes_ / 1048576.0) / (finish - start_)); + if (!message_.empty()) { + message_ = std::string(rate) + " " + message_; + } else { + message_ = rate; + } + } + + fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", + name.ToString().c_str(), + (finish - start_) * 1e6 / done_, + (message_.empty() ? "" : " "), + message_.c_str()); + if (FLAGS_histogram) { + fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str()); + } + fflush(stdout); + } + + public: + enum Order { + SEQUENTIAL, + RANDOM + }; + enum DBState { + FRESH, + EXISTING + }; + + Benchmark() + : db_(NULL), + db_num_(0), + num_(FLAGS_num), + reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), + bytes_(0), + rand_(301) { + std::vector files; + std::string test_dir; + Env::Default()->GetTestDirectory(&test_dir); + Env::Default()->GetChildren(test_dir, &files); + if (!FLAGS_use_existing_db) { + for (int i = 0; i < files.size(); i++) { + if (Slice(files[i]).starts_with("dbbench_sqlite3")) { + std::string file_name(test_dir); + file_name += "/"; + file_name += files[i]; + Env::Default()->DeleteFile(file_name.c_str()); + } + } + } + } + + ~Benchmark() { + int status = sqlite3_close(db_); + ErrorCheck(status); + } + + void Run() { + PrintHeader(); + Open(); + + const char* benchmarks = FLAGS_benchmarks; + while (benchmarks != NULL) { + const char* sep = strchr(benchmarks, ','); + Slice name; + if (sep == NULL) { + name = benchmarks; + benchmarks = NULL; + } else { + name = Slice(benchmarks, sep - benchmarks); + benchmarks = sep + 1; + } + + bytes_ = 0; + Start(); + + bool known = true; + bool write_sync = false; + if (name == Slice("fillseq")) { + Write(write_sync, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1); + WalCheckpoint(db_); + } else if (name == Slice("fillseqbatch")) { + Write(write_sync, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1000); + WalCheckpoint(db_); + } else if (name == Slice("fillrandom")) { + Write(write_sync, RANDOM, FRESH, num_, FLAGS_value_size, 1); + WalCheckpoint(db_); + } else if (name == Slice("fillrandbatch")) { + Write(write_sync, RANDOM, FRESH, num_, FLAGS_value_size, 1000); + WalCheckpoint(db_); + } else if (name == Slice("overwrite")) { + Write(write_sync, RANDOM, EXISTING, num_, FLAGS_value_size, 1); + WalCheckpoint(db_); + } else if (name == Slice("overwritebatch")) { + Write(write_sync, RANDOM, EXISTING, num_, FLAGS_value_size, 1000); + WalCheckpoint(db_); + } else if (name == Slice("fillrandsync")) { + write_sync = true; + Write(write_sync, RANDOM, FRESH, num_ / 100, FLAGS_value_size, 1); + WalCheckpoint(db_); + } else if (name == Slice("fillseqsync")) { + write_sync = true; + Write(write_sync, SEQUENTIAL, FRESH, num_ / 100, FLAGS_value_size, 1); + WalCheckpoint(db_); + } else if (name == Slice("fillrand100K")) { + Write(write_sync, RANDOM, FRESH, num_ / 1000, 100 * 1000, 1); + WalCheckpoint(db_); + } else if (name == Slice("fillseq100K")) { + Write(write_sync, SEQUENTIAL, FRESH, num_ / 1000, 100 * 1000, 1); + WalCheckpoint(db_); + } else if (name == Slice("readseq")) { + ReadSequential(); + } else if (name == Slice("readrandom")) { + Read(RANDOM, 1); + } else if (name == Slice("readrand100K")) { + int n = reads_; + reads_ /= 1000; + Read(RANDOM, 1); + reads_ = n; + } else { + known = false; + if (name != Slice()) { // No error message for empty name + fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str()); + } + } + if (known) { + Stop(name); + } + } + } + + void Open() { + assert(db_ == NULL); + + int status; + char file_name[100]; + char* err_msg = NULL; + db_num_++; + + // Open database + std::string tmp_dir; + Env::Default()->GetTestDirectory(&tmp_dir); + snprintf(file_name, sizeof(file_name), + "%s/dbbench_sqlite3-%d.db", + tmp_dir.c_str(), + db_num_); + status = sqlite3_open(file_name, &db_); + if (status) { + fprintf(stderr, "open error: %s\n", sqlite3_errmsg(db_)); + exit(1); + } + + // Change SQLite cache size + char cache_size[100]; + snprintf(cache_size, sizeof(cache_size), "PRAGMA cache_size = %d", + FLAGS_num_pages); + status = sqlite3_exec(db_, cache_size, NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + + // FLAGS_page_size is defaulted to 1024 + if (FLAGS_page_size != 1024) { + char page_size[100]; + snprintf(page_size, sizeof(page_size), "PRAGMA page_size = %d", + FLAGS_page_size); + status = sqlite3_exec(db_, page_size, NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + } + + // Change journal mode to WAL if WAL enabled flag is on + if (FLAGS_WAL_enabled) { + std::string WAL_stmt = "PRAGMA journal_mode = WAL"; + + // LevelDB's default cache size is a combined 4 MB + std::string WAL_checkpoint = "PRAGMA wal_autocheckpoint = 4096"; + status = sqlite3_exec(db_, WAL_stmt.c_str(), NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + status = sqlite3_exec(db_, WAL_checkpoint.c_str(), NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + } + + // Change locking mode to exclusive and create tables/index for database + std::string locking_stmt = "PRAGMA locking_mode = EXCLUSIVE"; + std::string create_stmt = + "CREATE TABLE test (key blob, value blob, PRIMARY KEY(key))"; + std::string stmt_array[] = { locking_stmt, create_stmt }; + int stmt_array_length = sizeof(stmt_array) / sizeof(std::string); + for (int i = 0; i < stmt_array_length; i++) { + status = sqlite3_exec(db_, stmt_array[i].c_str(), NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + } + } + + void Write(bool write_sync, Order order, DBState state, + int num_entries, int value_size, int entries_per_batch) { + // Create new database if state == FRESH + if (state == FRESH) { + if (FLAGS_use_existing_db) { + message_ = "skipping (--use_existing_db is true)"; + return; + } + sqlite3_close(db_); + db_ = NULL; + Open(); + Start(); + } + + if (num_entries != num_) { + char msg[100]; + snprintf(msg, sizeof(msg), "(%d ops)", num_entries); + message_ = msg; + } + + char* err_msg = NULL; + int status; + + sqlite3_stmt *replace_stmt, *begin_trans_stmt, *end_trans_stmt; + std::string replace_str = "REPLACE INTO test (key, value) VALUES (?, ?)"; + std::string begin_trans_str = "BEGIN TRANSACTION;"; + std::string end_trans_str = "END TRANSACTION;"; + + // Check for synchronous flag in options + std::string sync_stmt = (write_sync) ? "PRAGMA synchronous = FULL" : + "PRAGMA synchronous = OFF"; + status = sqlite3_exec(db_, sync_stmt.c_str(), NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + + // Preparing sqlite3 statements + status = sqlite3_prepare_v2(db_, replace_str.c_str(), -1, + &replace_stmt, NULL); + ErrorCheck(status); + status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1, + &begin_trans_stmt, NULL); + ErrorCheck(status); + status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, + &end_trans_stmt, NULL); + ErrorCheck(status); + + bool transaction = (entries_per_batch > 1); + for (int i = 0; i < num_entries; i += entries_per_batch) { + // Begin write transaction + if (FLAGS_transaction && transaction) { + status = sqlite3_step(begin_trans_stmt); + StepErrorCheck(status); + status = sqlite3_reset(begin_trans_stmt); + ErrorCheck(status); + } + + // Create and execute SQL statements + for (int j = 0; j < entries_per_batch; j++) { + const char* value = gen_.Generate(value_size).data(); + + // Create values for key-value pair + const int k = (order == SEQUENTIAL) ? i + j : + (rand_.Next() % num_entries); + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + + // Bind KV values into replace_stmt + status = sqlite3_bind_blob(replace_stmt, 1, key, 16, SQLITE_STATIC); + ErrorCheck(status); + status = sqlite3_bind_blob(replace_stmt, 2, value, + value_size, SQLITE_STATIC); + ErrorCheck(status); + + // Execute replace_stmt + bytes_ += value_size + strlen(key); + status = sqlite3_step(replace_stmt); + StepErrorCheck(status); + + // Reset SQLite statement for another use + status = sqlite3_clear_bindings(replace_stmt); + ErrorCheck(status); + status = sqlite3_reset(replace_stmt); + ErrorCheck(status); + + FinishedSingleOp(); + } + + // End write transaction + if (FLAGS_transaction && transaction) { + status = sqlite3_step(end_trans_stmt); + StepErrorCheck(status); + status = sqlite3_reset(end_trans_stmt); + ErrorCheck(status); + } + } + + status = sqlite3_finalize(replace_stmt); + ErrorCheck(status); + status = sqlite3_finalize(begin_trans_stmt); + ErrorCheck(status); + status = sqlite3_finalize(end_trans_stmt); + ErrorCheck(status); + } + + void Read(Order order, int entries_per_batch) { + int status; + sqlite3_stmt *read_stmt, *begin_trans_stmt, *end_trans_stmt; + + std::string read_str = "SELECT * FROM test WHERE key = ?"; + std::string begin_trans_str = "BEGIN TRANSACTION;"; + std::string end_trans_str = "END TRANSACTION;"; + + // Preparing sqlite3 statements + status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1, + &begin_trans_stmt, NULL); + ErrorCheck(status); + status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, + &end_trans_stmt, NULL); + ErrorCheck(status); + status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &read_stmt, NULL); + ErrorCheck(status); + + bool transaction = (entries_per_batch > 1); + for (int i = 0; i < reads_; i += entries_per_batch) { + // Begin read transaction + if (FLAGS_transaction && transaction) { + status = sqlite3_step(begin_trans_stmt); + StepErrorCheck(status); + status = sqlite3_reset(begin_trans_stmt); + ErrorCheck(status); + } + + // Create and execute SQL statements + for (int j = 0; j < entries_per_batch; j++) { + // Create key value + char key[100]; + int k = (order == SEQUENTIAL) ? i + j : (rand_.Next() % reads_); + snprintf(key, sizeof(key), "%016d", k); + + // Bind key value into read_stmt + status = sqlite3_bind_blob(read_stmt, 1, key, 16, SQLITE_STATIC); + ErrorCheck(status); + + // Execute read statement + while ((status = sqlite3_step(read_stmt)) == SQLITE_ROW) {} + StepErrorCheck(status); + + // Reset SQLite statement for another use + status = sqlite3_clear_bindings(read_stmt); + ErrorCheck(status); + status = sqlite3_reset(read_stmt); + ErrorCheck(status); + FinishedSingleOp(); + } + + // End read transaction + if (FLAGS_transaction && transaction) { + status = sqlite3_step(end_trans_stmt); + StepErrorCheck(status); + status = sqlite3_reset(end_trans_stmt); + ErrorCheck(status); + } + } + + status = sqlite3_finalize(read_stmt); + ErrorCheck(status); + status = sqlite3_finalize(begin_trans_stmt); + ErrorCheck(status); + status = sqlite3_finalize(end_trans_stmt); + ErrorCheck(status); + } + + void ReadSequential() { + int status; + sqlite3_stmt *pStmt; + std::string read_str = "SELECT * FROM test ORDER BY key"; + + status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &pStmt, NULL); + ErrorCheck(status); + for (int i = 0; i < reads_ && SQLITE_ROW == sqlite3_step(pStmt); i++) { + bytes_ += sqlite3_column_bytes(pStmt, 1) + sqlite3_column_bytes(pStmt, 2); + FinishedSingleOp(); + } + + status = sqlite3_finalize(pStmt); + ErrorCheck(status); + } + +}; + +} // namespace leveldb + +int main(int argc, char** argv) { + std::string default_db_path; + for (int i = 1; i < argc; i++) { + double d; + int n; + char junk; + if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) { + FLAGS_benchmarks = argv[i] + strlen("--benchmarks="); + } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_histogram = n; + } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) { + FLAGS_compression_ratio = d; + } else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_use_existing_db = n; + } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) { + FLAGS_num = n; + } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) { + FLAGS_reads = n; + } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) { + FLAGS_value_size = n; + } else if (leveldb::Slice(argv[i]) == leveldb::Slice("--no_transaction")) { + FLAGS_transaction = false; + } else if (sscanf(argv[i], "--page_size=%d%c", &n, &junk) == 1) { + FLAGS_page_size = n; + } else if (sscanf(argv[i], "--num_pages=%d%c", &n, &junk) == 1) { + FLAGS_num_pages = n; + } else if (sscanf(argv[i], "--WAL_enabled=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_WAL_enabled = n; + } else if (strncmp(argv[i], "--db=", 5) == 0) { + FLAGS_db = argv[i] + 5; + } else { + fprintf(stderr, "Invalid flag '%s'\n", argv[i]); + exit(1); + } + } + + // Choose a location for the test database if none given with --db= + if (FLAGS_db == NULL) { + leveldb::Env::Default()->GetTestDirectory(&default_db_path); + default_db_path += "/dbbench"; + FLAGS_db = default_db_path.c_str(); + } + + leveldb::Benchmark benchmark; + benchmark.Run(); + return 0; +} diff --git a/src/leveldb-lin/doc/bench/db_bench_tree_db.cc b/src/leveldb-lin/doc/bench/db_bench_tree_db.cc new file mode 100644 index 0000000..ed86f03 --- /dev/null +++ b/src/leveldb-lin/doc/bench/db_bench_tree_db.cc @@ -0,0 +1,528 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include +#include "util/histogram.h" +#include "util/random.h" +#include "util/testutil.h" + +// Comma-separated list of operations to run in the specified order +// Actual benchmarks: +// +// fillseq -- write N values in sequential key order in async mode +// fillrandom -- write N values in random key order in async mode +// overwrite -- overwrite N values in random key order in async mode +// fillseqsync -- write N/100 values in sequential key order in sync mode +// fillrandsync -- write N/100 values in random key order in sync mode +// fillrand100K -- write N/1000 100K values in random order in async mode +// fillseq100K -- write N/1000 100K values in seq order in async mode +// readseq -- read N times sequentially +// readseq100K -- read N/1000 100K values in sequential order in async mode +// readrand100K -- read N/1000 100K values in sequential order in async mode +// readrandom -- read N times in random order +static const char* FLAGS_benchmarks = + "fillseq," + "fillseqsync," + "fillrandsync," + "fillrandom," + "overwrite," + "readrandom," + "readseq," + "fillrand100K," + "fillseq100K," + "readseq100K," + "readrand100K," + ; + +// Number of key/values to place in database +static int FLAGS_num = 1000000; + +// Number of read operations to do. If negative, do FLAGS_num reads. +static int FLAGS_reads = -1; + +// Size of each value +static int FLAGS_value_size = 100; + +// Arrange to generate values that shrink to this fraction of +// their original size after compression +static double FLAGS_compression_ratio = 0.5; + +// Print histogram of operation timings +static bool FLAGS_histogram = false; + +// Cache size. Default 4 MB +static int FLAGS_cache_size = 4194304; + +// Page size. Default 1 KB +static int FLAGS_page_size = 1024; + +// If true, do not destroy the existing database. If you set this +// flag and also specify a benchmark that wants a fresh database, that +// benchmark will fail. +static bool FLAGS_use_existing_db = false; + +// Compression flag. If true, compression is on. If false, compression +// is off. +static bool FLAGS_compression = true; + +// Use the db with the following name. +static const char* FLAGS_db = NULL; + +inline +static void DBSynchronize(kyotocabinet::TreeDB* db_) +{ + // Synchronize will flush writes to disk + if (!db_->synchronize()) { + fprintf(stderr, "synchronize error: %s\n", db_->error().name()); + } +} + +namespace leveldb { + +// Helper for quickly generating random data. +namespace { +class RandomGenerator { + private: + std::string data_; + int pos_; + + public: + RandomGenerator() { + // We use a limited amount of data over and over again and ensure + // that it is larger than the compression window (32KB), and also + // large enough to serve all typical value sizes we want to write. + Random rnd(301); + std::string piece; + while (data_.size() < 1048576) { + // Add a short fragment that is as compressible as specified + // by FLAGS_compression_ratio. + test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece); + data_.append(piece); + } + pos_ = 0; + } + + Slice Generate(int len) { + if (pos_ + len > data_.size()) { + pos_ = 0; + assert(len < data_.size()); + } + pos_ += len; + return Slice(data_.data() + pos_ - len, len); + } +}; + +static Slice TrimSpace(Slice s) { + int start = 0; + while (start < s.size() && isspace(s[start])) { + start++; + } + int limit = s.size(); + while (limit > start && isspace(s[limit-1])) { + limit--; + } + return Slice(s.data() + start, limit - start); +} + +} // namespace + +class Benchmark { + private: + kyotocabinet::TreeDB* db_; + int db_num_; + int num_; + int reads_; + double start_; + double last_op_finish_; + int64_t bytes_; + std::string message_; + Histogram hist_; + RandomGenerator gen_; + Random rand_; + kyotocabinet::LZOCompressor comp_; + + // State kept for progress messages + int done_; + int next_report_; // When to report next + + void PrintHeader() { + const int kKeySize = 16; + PrintEnvironment(); + fprintf(stdout, "Keys: %d bytes each\n", kKeySize); + fprintf(stdout, "Values: %d bytes each (%d bytes after compression)\n", + FLAGS_value_size, + static_cast(FLAGS_value_size * FLAGS_compression_ratio + 0.5)); + fprintf(stdout, "Entries: %d\n", num_); + fprintf(stdout, "RawSize: %.1f MB (estimated)\n", + ((static_cast(kKeySize + FLAGS_value_size) * num_) + / 1048576.0)); + fprintf(stdout, "FileSize: %.1f MB (estimated)\n", + (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) + / 1048576.0)); + PrintWarnings(); + fprintf(stdout, "------------------------------------------------\n"); + } + + void PrintWarnings() { +#if defined(__GNUC__) && !defined(__OPTIMIZE__) + fprintf(stdout, + "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n" + ); +#endif +#ifndef NDEBUG + fprintf(stdout, + "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n"); +#endif + } + + void PrintEnvironment() { + fprintf(stderr, "Kyoto Cabinet: version %s, lib ver %d, lib rev %d\n", + kyotocabinet::VERSION, kyotocabinet::LIBVER, kyotocabinet::LIBREV); + +#if defined(__linux) + time_t now = time(NULL); + fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline + + FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); + if (cpuinfo != NULL) { + char line[1000]; + int num_cpus = 0; + std::string cpu_type; + std::string cache_size; + while (fgets(line, sizeof(line), cpuinfo) != NULL) { + const char* sep = strchr(line, ':'); + if (sep == NULL) { + continue; + } + Slice key = TrimSpace(Slice(line, sep - 1 - line)); + Slice val = TrimSpace(Slice(sep + 1)); + if (key == "model name") { + ++num_cpus; + cpu_type = val.ToString(); + } else if (key == "cache size") { + cache_size = val.ToString(); + } + } + fclose(cpuinfo); + fprintf(stderr, "CPU: %d * %s\n", num_cpus, cpu_type.c_str()); + fprintf(stderr, "CPUCache: %s\n", cache_size.c_str()); + } +#endif + } + + void Start() { + start_ = Env::Default()->NowMicros() * 1e-6; + bytes_ = 0; + message_.clear(); + last_op_finish_ = start_; + hist_.Clear(); + done_ = 0; + next_report_ = 100; + } + + void FinishedSingleOp() { + if (FLAGS_histogram) { + double now = Env::Default()->NowMicros() * 1e-6; + double micros = (now - last_op_finish_) * 1e6; + hist_.Add(micros); + if (micros > 20000) { + fprintf(stderr, "long op: %.1f micros%30s\r", micros, ""); + fflush(stderr); + } + last_op_finish_ = now; + } + + done_++; + if (done_ >= next_report_) { + if (next_report_ < 1000) next_report_ += 100; + else if (next_report_ < 5000) next_report_ += 500; + else if (next_report_ < 10000) next_report_ += 1000; + else if (next_report_ < 50000) next_report_ += 5000; + else if (next_report_ < 100000) next_report_ += 10000; + else if (next_report_ < 500000) next_report_ += 50000; + else next_report_ += 100000; + fprintf(stderr, "... finished %d ops%30s\r", done_, ""); + fflush(stderr); + } + } + + void Stop(const Slice& name) { + double finish = Env::Default()->NowMicros() * 1e-6; + + // Pretend at least one op was done in case we are running a benchmark + // that does not call FinishedSingleOp(). + if (done_ < 1) done_ = 1; + + if (bytes_ > 0) { + char rate[100]; + snprintf(rate, sizeof(rate), "%6.1f MB/s", + (bytes_ / 1048576.0) / (finish - start_)); + if (!message_.empty()) { + message_ = std::string(rate) + " " + message_; + } else { + message_ = rate; + } + } + + fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", + name.ToString().c_str(), + (finish - start_) * 1e6 / done_, + (message_.empty() ? "" : " "), + message_.c_str()); + if (FLAGS_histogram) { + fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str()); + } + fflush(stdout); + } + + public: + enum Order { + SEQUENTIAL, + RANDOM + }; + enum DBState { + FRESH, + EXISTING + }; + + Benchmark() + : db_(NULL), + num_(FLAGS_num), + reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), + bytes_(0), + rand_(301) { + std::vector files; + std::string test_dir; + Env::Default()->GetTestDirectory(&test_dir); + Env::Default()->GetChildren(test_dir.c_str(), &files); + if (!FLAGS_use_existing_db) { + for (int i = 0; i < files.size(); i++) { + if (Slice(files[i]).starts_with("dbbench_polyDB")) { + std::string file_name(test_dir); + file_name += "/"; + file_name += files[i]; + Env::Default()->DeleteFile(file_name.c_str()); + } + } + } + } + + ~Benchmark() { + if (!db_->close()) { + fprintf(stderr, "close error: %s\n", db_->error().name()); + } + } + + void Run() { + PrintHeader(); + Open(false); + + const char* benchmarks = FLAGS_benchmarks; + while (benchmarks != NULL) { + const char* sep = strchr(benchmarks, ','); + Slice name; + if (sep == NULL) { + name = benchmarks; + benchmarks = NULL; + } else { + name = Slice(benchmarks, sep - benchmarks); + benchmarks = sep + 1; + } + + Start(); + + bool known = true; + bool write_sync = false; + if (name == Slice("fillseq")) { + Write(write_sync, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1); + + } else if (name == Slice("fillrandom")) { + Write(write_sync, RANDOM, FRESH, num_, FLAGS_value_size, 1); + DBSynchronize(db_); + } else if (name == Slice("overwrite")) { + Write(write_sync, RANDOM, EXISTING, num_, FLAGS_value_size, 1); + DBSynchronize(db_); + } else if (name == Slice("fillrandsync")) { + write_sync = true; + Write(write_sync, RANDOM, FRESH, num_ / 100, FLAGS_value_size, 1); + DBSynchronize(db_); + } else if (name == Slice("fillseqsync")) { + write_sync = true; + Write(write_sync, SEQUENTIAL, FRESH, num_ / 100, FLAGS_value_size, 1); + DBSynchronize(db_); + } else if (name == Slice("fillrand100K")) { + Write(write_sync, RANDOM, FRESH, num_ / 1000, 100 * 1000, 1); + DBSynchronize(db_); + } else if (name == Slice("fillseq100K")) { + Write(write_sync, SEQUENTIAL, FRESH, num_ / 1000, 100 * 1000, 1); + DBSynchronize(db_); + } else if (name == Slice("readseq")) { + ReadSequential(); + } else if (name == Slice("readrandom")) { + ReadRandom(); + } else if (name == Slice("readrand100K")) { + int n = reads_; + reads_ /= 1000; + ReadRandom(); + reads_ = n; + } else if (name == Slice("readseq100K")) { + int n = reads_; + reads_ /= 1000; + ReadSequential(); + reads_ = n; + } else { + known = false; + if (name != Slice()) { // No error message for empty name + fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str()); + } + } + if (known) { + Stop(name); + } + } + } + + private: + void Open(bool sync) { + assert(db_ == NULL); + + // Initialize db_ + db_ = new kyotocabinet::TreeDB(); + char file_name[100]; + db_num_++; + std::string test_dir; + Env::Default()->GetTestDirectory(&test_dir); + snprintf(file_name, sizeof(file_name), + "%s/dbbench_polyDB-%d.kct", + test_dir.c_str(), + db_num_); + + // Create tuning options and open the database + int open_options = kyotocabinet::PolyDB::OWRITER | + kyotocabinet::PolyDB::OCREATE; + int tune_options = kyotocabinet::TreeDB::TSMALL | + kyotocabinet::TreeDB::TLINEAR; + if (FLAGS_compression) { + tune_options |= kyotocabinet::TreeDB::TCOMPRESS; + db_->tune_compressor(&comp_); + } + db_->tune_options(tune_options); + db_->tune_page_cache(FLAGS_cache_size); + db_->tune_page(FLAGS_page_size); + db_->tune_map(256LL<<20); + if (sync) { + open_options |= kyotocabinet::PolyDB::OAUTOSYNC; + } + if (!db_->open(file_name, open_options)) { + fprintf(stderr, "open error: %s\n", db_->error().name()); + } + } + + void Write(bool sync, Order order, DBState state, + int num_entries, int value_size, int entries_per_batch) { + // Create new database if state == FRESH + if (state == FRESH) { + if (FLAGS_use_existing_db) { + message_ = "skipping (--use_existing_db is true)"; + return; + } + delete db_; + db_ = NULL; + Open(sync); + Start(); // Do not count time taken to destroy/open + } + + if (num_entries != num_) { + char msg[100]; + snprintf(msg, sizeof(msg), "(%d ops)", num_entries); + message_ = msg; + } + + // Write to database + for (int i = 0; i < num_entries; i++) + { + const int k = (order == SEQUENTIAL) ? i : (rand_.Next() % num_entries); + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + bytes_ += value_size + strlen(key); + std::string cpp_key = key; + if (!db_->set(cpp_key, gen_.Generate(value_size).ToString())) { + fprintf(stderr, "set error: %s\n", db_->error().name()); + } + FinishedSingleOp(); + } + } + + void ReadSequential() { + kyotocabinet::DB::Cursor* cur = db_->cursor(); + cur->jump(); + std::string ckey, cvalue; + while (cur->get(&ckey, &cvalue, true)) { + bytes_ += ckey.size() + cvalue.size(); + FinishedSingleOp(); + } + delete cur; + } + + void ReadRandom() { + std::string value; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = rand_.Next() % reads_; + snprintf(key, sizeof(key), "%016d", k); + db_->get(key, &value); + FinishedSingleOp(); + } + } +}; + +} // namespace leveldb + +int main(int argc, char** argv) { + std::string default_db_path; + for (int i = 1; i < argc; i++) { + double d; + int n; + char junk; + if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) { + FLAGS_benchmarks = argv[i] + strlen("--benchmarks="); + } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) { + FLAGS_compression_ratio = d; + } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_histogram = n; + } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) { + FLAGS_num = n; + } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) { + FLAGS_reads = n; + } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) { + FLAGS_value_size = n; + } else if (sscanf(argv[i], "--cache_size=%d%c", &n, &junk) == 1) { + FLAGS_cache_size = n; + } else if (sscanf(argv[i], "--page_size=%d%c", &n, &junk) == 1) { + FLAGS_page_size = n; + } else if (sscanf(argv[i], "--compression=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_compression = (n == 1) ? true : false; + } else if (strncmp(argv[i], "--db=", 5) == 0) { + FLAGS_db = argv[i] + 5; + } else { + fprintf(stderr, "Invalid flag '%s'\n", argv[i]); + exit(1); + } + } + + // Choose a location for the test database if none given with --db= + if (FLAGS_db == NULL) { + leveldb::Env::Default()->GetTestDirectory(&default_db_path); + default_db_path += "/dbbench"; + FLAGS_db = default_db_path.c_str(); + } + + leveldb::Benchmark benchmark; + benchmark.Run(); + return 0; +} diff --git a/src/leveldb-lin/doc/benchmark.html b/src/leveldb-lin/doc/benchmark.html new file mode 100644 index 0000000..c463977 --- /dev/null +++ b/src/leveldb-lin/doc/benchmark.html @@ -0,0 +1,459 @@ + + + +LevelDB Benchmarks + + + + +

LevelDB Benchmarks

+

Google, July 2011

+
+ +

In order to test LevelDB's performance, we benchmark it against other well-established database implementations. We compare LevelDB (revision 39) against SQLite3 (version 3.7.6.3) and Kyoto Cabinet's (version 1.2.67) TreeDB (a B+Tree based key-value store). We would like to acknowledge Scott Hess and Mikio Hirabayashi for their suggestions and contributions to the SQLite3 and Kyoto Cabinet benchmarks, respectively.

+ +

Benchmarks were all performed on a six-core Intel(R) Xeon(R) CPU X5650 @ 2.67GHz, with 12288 KB of total L3 cache and 12 GB of DDR3 RAM at 1333 MHz. (Note that LevelDB uses at most two CPUs since the benchmarks are single threaded: one to run the benchmark, and one for background compactions.) We ran the benchmarks on two machines (with identical processors), one with an Ext3 file system and one with an Ext4 file system. The machine with the Ext3 file system has a SATA Hitachi HDS721050CLA362 hard drive. The machine with the Ext4 file system has a SATA Samsung HD502HJ hard drive. Both hard drives spin at 7200 RPM and have hard drive write-caching enabled (using `hdparm -W 1 [device]`). The numbers reported below are the median of three measurements.

+ +

Benchmark Source Code

+

We wrote benchmark tools for SQLite and Kyoto TreeDB based on LevelDB's db_bench. The code for each of the benchmarks resides here:

+ + +

Custom Build Specifications

+
    +
  • LevelDB: LevelDB was compiled with the tcmalloc library and the Snappy compression library (revision 33). Assertions were disabled.
  • +
  • TreeDB: TreeDB was compiled using the LZO compression library (version 2.03). Furthermore, we enabled the TSMALL and TLINEAR options when opening the database in order to reduce the footprint of each record.
  • +
  • SQLite: We tuned SQLite's performance, by setting its locking mode to exclusive. We also enabled SQLite's write-ahead logging.
  • +
+ +

1. Baseline Performance

+

This section gives the baseline performance of all the +databases. Following sections show how performance changes as various +parameters are varied. For the baseline:

+
    +
  • Each database is allowed 4 MB of cache memory.
  • +
  • Databases are opened in asynchronous write mode. + (LevelDB's sync option, TreeDB's OAUTOSYNC option, and + SQLite3's synchronous options are all turned off). I.e., + every write is pushed to the operating system, but the + benchmark does not wait for the write to reach the disk.
  • +
  • Keys are 16 bytes each.
  • +
  • Value are 100 bytes each (with enough redundancy so that + a simple compressor shrinks them to 50% of their original + size).
  • +
  • Sequential reads/writes traverse the key space in increasing order.
  • +
  • Random reads/writes traverse the key space in random order.
  • +
+ +

A. Sequential Reads

+ + + + + + + + + + +
LevelDB4,030,000 ops/sec
 
Kyoto TreeDB1,010,000 ops/sec
 
SQLite3383,000 ops/sec
 
+

B. Random Reads

+ + + + + + + + + + +
LevelDB129,000 ops/sec
 
Kyoto TreeDB151,000 ops/sec
 
SQLite3134,000 ops/sec
 
+

C. Sequential Writes

+ + + + + + + + + + +
LevelDB779,000 ops/sec
 
Kyoto TreeDB342,000 ops/sec
 
SQLite348,600 ops/sec
 
+

D. Random Writes

+ + + + + + + + + + +
LevelDB164,000 ops/sec
 
Kyoto TreeDB88,500 ops/sec
 
SQLite39,860 ops/sec
 
+ +

LevelDB outperforms both SQLite3 and TreeDB in sequential and random write operations and sequential read operations. Kyoto Cabinet has the fastest random read operations.

+ +

2. Write Performance under Different Configurations

+

A. Large Values

+

For this benchmark, we start with an empty database, and write 100,000 byte values (~50% compressible). To keep the benchmark running time reasonable, we stop after writing 1000 values.

+

Sequential Writes

+ + + + + + + + + + +
LevelDB1,100 ops/sec
 
Kyoto TreeDB1,000 ops/sec
 
SQLite31,600 ops/sec
 
+

Random Writes

+ + + + + + + + + + +
LevelDB480 ops/sec
 
Kyoto TreeDB1,100 ops/sec
 
SQLite31,600 ops/sec
 
+

LevelDB doesn't perform as well with large values of 100,000 bytes each. This is because LevelDB writes keys and values at least twice: first time to the transaction log, and second time (during a compaction) to a sorted file. +With larger values, LevelDB's per-operation efficiency is swamped by the +cost of extra copies of large values.

+

B. Batch Writes

+

A batch write is a set of writes that are applied atomically to the underlying database. A single batch of N writes may be significantly faster than N individual writes. The following benchmark writes one thousand batches where each batch contains one thousand 100-byte values. TreeDB does not support batch writes and is omitted from this benchmark.

+

Sequential Writes

+ + + + + + + + + +
LevelDB840,000 entries/sec
 
(1.08x baseline)
SQLite3124,000 entries/sec
 
(2.55x baseline)
+

Random Writes

+ + + + + + + + + +
LevelDB221,000 entries/sec
 
(1.35x baseline)
SQLite322,000 entries/sec
 
(2.23x baseline)
+ +

Because of the way LevelDB persistent storage is organized, batches of +random writes are not much slower (only a factor of 4x) than batches +of sequential writes.

+ +

C. Synchronous Writes

+

In the following benchmark, we enable the synchronous writing modes +of all of the databases. Since this change significantly slows down the +benchmark, we stop after 10,000 writes. For synchronous write tests, we've +disabled hard drive write-caching (using `hdparm -W 0 [device]`).

+
    +
  • For LevelDB, we set WriteOptions.sync = true.
  • +
  • In TreeDB, we enabled TreeDB's OAUTOSYNC option.
  • +
  • For SQLite3, we set "PRAGMA synchronous = FULL".
  • +
+

Sequential Writes

+ + + + + + + + + + + + + +
LevelDB100 ops/sec
 
(0.003x baseline)
Kyoto TreeDB7 ops/sec
 
(0.0004x baseline)
SQLite388 ops/sec
 
(0.002x baseline)
+

Random Writes

+ + + + + + + + + + + + + +
LevelDB100 ops/sec
 
(0.015x baseline)
Kyoto TreeDB8 ops/sec
 
(0.001x baseline)
SQLite388 ops/sec
 
(0.009x baseline)
+ +

Also see the ext4 performance numbers below +since synchronous writes behave significantly differently +on ext3 and ext4.

+ +

D. Turning Compression Off

+ +

In the baseline measurements, LevelDB and TreeDB were using +light-weight compression +(Snappy for LevelDB, +and LZO for +TreeDB). SQLite3, by default does not use compression. The +experiments below show what happens when compression is disabled in +all of the databases (the SQLite3 numbers are just a copy of +its baseline measurements):

+ +

Sequential Writes

+ + + + + + + + + + + + + +
LevelDB594,000 ops/sec
 
(0.76x baseline)
Kyoto TreeDB485,000 ops/sec
 
(1.42x baseline)
SQLite348,600 ops/sec
 
(1.00x baseline)
+

Random Writes

+ + + + + + + + + + + + + +
LevelDB135,000 ops/sec
 
(0.82x baseline)
Kyoto TreeDB159,000 ops/sec
 
(1.80x baseline)
SQLite39,860 ops/sec
 
(1.00x baseline)
+ +

LevelDB's write performance is better with compression than without +since compression decreases the amount of data that has to be written +to disk. Therefore LevelDB users can leave compression enabled in +most scenarios without having worry about a tradeoff between space +usage and performance. TreeDB's performance on the other hand is +better without compression than with compression. Presumably this is +because TreeDB's compression library (LZO) is more expensive than +LevelDB's compression library (Snappy).

+ +

E. Using More Memory

+

We increased the overall cache size for each database to 128 MB. For LevelDB, we partitioned 128 MB into a 120 MB write buffer and 8 MB of cache (up from 2 MB of write buffer and 2 MB of cache). For SQLite3, we kept the page size at 1024 bytes, but increased the number of pages to 131,072 (up from 4096). For TreeDB, we also kept the page size at 1024 bytes, but increased the cache size to 128 MB (up from 4 MB).

+

Sequential Writes

+ + + + + + + + + + + + + +
LevelDB812,000 ops/sec
 
(1.04x baseline)
Kyoto TreeDB321,000 ops/sec
 
(0.94x baseline)
SQLite348,500 ops/sec
 
(1.00x baseline)
+

Random Writes

+ + + + + + + + + + + + + +
LevelDB355,000 ops/sec
 
(2.16x baseline)
Kyoto TreeDB284,000 ops/sec
 
(3.21x baseline)
SQLite39,670 ops/sec
 
(0.98x baseline)
+ +

SQLite's performance does not change substantially when compared to +the baseline, but the random write performance for both LevelDB and +TreeDB increases significantly. LevelDB's performance improves +because a larger write buffer reduces the need to merge sorted files +(since it creates a smaller number of larger sorted files). TreeDB's +performance goes up because the entire database is available in memory +for fast in-place updates.

+ +

3. Read Performance under Different Configurations

+

A. Larger Caches

+

We increased the overall memory usage to 128 MB for each database. +For LevelDB, we allocated 8 MB to LevelDB's write buffer and 120 MB +to LevelDB's cache. The other databases don't differentiate between a +write buffer and a cache, so we simply set their cache size to 128 +MB.

+

Sequential Reads

+ + + + + + + + + + + + + +
LevelDB5,210,000 ops/sec
 
(1.29x baseline)
Kyoto TreeDB1,070,000 ops/sec
 
(1.06x baseline)
SQLite3609,000 ops/sec
 
(1.59x baseline)
+ +

Random Reads

+ + + + + + + + + + + + + +
LevelDB190,000 ops/sec
 
(1.47x baseline)
Kyoto TreeDB463,000 ops/sec
 
(3.07x baseline)
SQLite3186,000 ops/sec
 
(1.39x baseline)
+ +

As expected, the read performance of all of the databases increases +when the caches are enlarged. In particular, TreeDB seems to make +very effective use of a cache that is large enough to hold the entire +database.

+ +

B. No Compression Reads

+

For this benchmark, we populated a database with 1 million entries consisting of 16 byte keys and 100 byte values. We compiled LevelDB and Kyoto Cabinet without compression support, so results that are read out from the database are already uncompressed. We've listed the SQLite3 baseline read performance as a point of comparison.

+

Sequential Reads

+ + + + + + + + + + + + + +
LevelDB4,880,000 ops/sec
 
(1.21x baseline)
Kyoto TreeDB1,230,000 ops/sec
 
(3.60x baseline)
SQLite3383,000 ops/sec
 
(1.00x baseline)
+

Random Reads

+ + + + + + + + + + + + + +
LevelDB149,000 ops/sec
 
(1.16x baseline)
Kyoto TreeDB175,000 ops/sec
 
(1.16x baseline)
SQLite3134,000 ops/sec
 
(1.00x baseline)
+ +

Performance of both LevelDB and TreeDB improves a small amount when +compression is disabled. Note however that under different workloads, +performance may very well be better with compression if it allows more +of the working set to fit in memory.

+ +

Note about Ext4 Filesystems

+

The preceding numbers are for an ext3 file system. Synchronous writes are much slower under ext4 (LevelDB drops to ~31 writes / second and TreeDB drops to ~5 writes / second; SQLite3's synchronous writes do not noticeably drop) due to ext4's different handling of fsync / msync calls. Even LevelDB's asynchronous write performance drops somewhat since it spreads its storage across multiple files and issues fsync calls when switching to a new file.

+ +

Acknowledgements

+

Jeff Dean and Sanjay Ghemawat wrote LevelDB. Kevin Tseng wrote and compiled these benchmarks. Mikio Hirabayashi, Scott Hess, and Gabor Cselle provided help and advice.

+ + diff --git a/src/leveldb-lin/doc/doc.css b/src/leveldb-lin/doc/doc.css new file mode 100644 index 0000000..700c564 --- /dev/null +++ b/src/leveldb-lin/doc/doc.css @@ -0,0 +1,89 @@ +body { + margin-left: 0.5in; + margin-right: 0.5in; + background: white; + color: black; +} + +h1 { + margin-left: -0.2in; + font-size: 14pt; +} +h2 { + margin-left: -0in; + font-size: 12pt; +} +h3 { + margin-left: -0in; +} +h4 { + margin-left: -0in; +} +hr { + margin-left: -0in; +} + +/* Definition lists: definition term bold */ +dt { + font-weight: bold; +} + +address { + text-align: center; +} +code,samp,var { + color: blue; +} +kbd { + color: #600000; +} +div.note p { + float: right; + width: 3in; + margin-right: 0%; + padding: 1px; + border: 2px solid #6060a0; + background-color: #fffff0; +} + +ul { + margin-top: -0em; + margin-bottom: -0em; +} + +ol { + margin-top: -0em; + margin-bottom: -0em; +} + +UL.nobullets { + list-style-type: none; + list-style-image: none; + margin-left: -1em; +} + +p { + margin: 1em 0 1em 0; + padding: 0 0 0 0; +} + +pre { + line-height: 1.3em; + padding: 0.4em 0 0.8em 0; + margin: 0 0 0 0; + border: 0 0 0 0; + color: blue; +} + +.datatable { + margin-left: auto; + margin-right: auto; + margin-top: 2em; + margin-bottom: 2em; + border: 1px solid; +} + +.datatable td,th { + padding: 0 0.5em 0 0.5em; + text-align: right; +} diff --git a/src/leveldb-lin/doc/impl.html b/src/leveldb-lin/doc/impl.html new file mode 100644 index 0000000..e870795 --- /dev/null +++ b/src/leveldb-lin/doc/impl.html @@ -0,0 +1,213 @@ + + + + +Leveldb file layout and compactions + + + + +

Files

+ +The implementation of leveldb is similar in spirit to the +representation of a single + +Bigtable tablet (section 5.3). +However the organization of the files that make up the representation +is somewhat different and is explained below. + +

+Each database is represented by a set of files stored in a directory. +There are several different types of files as documented below: +

+

Log files

+

+A log file (*.log) stores a sequence of recent updates. Each update +is appended to the current log file. When the log file reaches a +pre-determined size (approximately 4MB by default), it is converted +to a sorted table (see below) and a new log file is created for future +updates. +

+A copy of the current log file is kept in an in-memory structure (the +memtable). This copy is consulted on every read so that read +operations reflect all logged updates. +

+

Sorted tables

+

+A sorted table (*.sst) stores a sequence of entries sorted by key. +Each entry is either a value for the key, or a deletion marker for the +key. (Deletion markers are kept around to hide obsolete values +present in older sorted tables). +

+The set of sorted tables are organized into a sequence of levels. The +sorted table generated from a log file is placed in a special young +level (also called level-0). When the number of young files exceeds a +certain threshold (currently four), all of the young files are merged +together with all of the overlapping level-1 files to produce a +sequence of new level-1 files (we create a new level-1 file for every +2MB of data.) +

+Files in the young level may contain overlapping keys. However files +in other levels have distinct non-overlapping key ranges. Consider +level number L where L >= 1. When the combined size of files in +level-L exceeds (10^L) MB (i.e., 10MB for level-1, 100MB for level-2, +...), one file in level-L, and all of the overlapping files in +level-(L+1) are merged to form a set of new files for level-(L+1). +These merges have the effect of gradually migrating new updates from +the young level to the largest level using only bulk reads and writes +(i.e., minimizing expensive seeks). + +

Manifest

+

+A MANIFEST file lists the set of sorted tables that make up each +level, the corresponding key ranges, and other important metadata. +A new MANIFEST file (with a new number embedded in the file name) +is created whenever the database is reopened. The MANIFEST file is +formatted as a log, and changes made to the serving state (as files +are added or removed) are appended to this log. +

+

Current

+

+CURRENT is a simple text file that contains the name of the latest +MANIFEST file. +

+

Info logs

+

+Informational messages are printed to files named LOG and LOG.old. +

+

Others

+

+Other files used for miscellaneous purposes may also be present +(LOCK, *.dbtmp). + +

Level 0

+When the log file grows above a certain size (1MB by default): +
    +
  • Create a brand new memtable and log file and direct future updates here +
  • In the background: +
      +
    • Write the contents of the previous memtable to an sstable +
    • Discard the memtable +
    • Delete the old log file and the old memtable +
    • Add the new sstable to the young (level-0) level. +
    +
+ +

Compactions

+ +

+When the size of level L exceeds its limit, we compact it in a +background thread. The compaction picks a file from level L and all +overlapping files from the next level L+1. Note that if a level-L +file overlaps only part of a level-(L+1) file, the entire file at +level-(L+1) is used as an input to the compaction and will be +discarded after the compaction. Aside: because level-0 is special +(files in it may overlap each other), we treat compactions from +level-0 to level-1 specially: a level-0 compaction may pick more than +one level-0 file in case some of these files overlap each other. + +

+A compaction merges the contents of the picked files to produce a +sequence of level-(L+1) files. We switch to producing a new +level-(L+1) file after the current output file has reached the target +file size (2MB). We also switch to a new output file when the key +range of the current output file has grown enough to overlap more then +ten level-(L+2) files. This last rule ensures that a later compaction +of a level-(L+1) file will not pick up too much data from level-(L+2). + +

+The old files are discarded and the new files are added to the serving +state. + +

+Compactions for a particular level rotate through the key space. In +more detail, for each level L, we remember the ending key of the last +compaction at level L. The next compaction for level L will pick the +first file that starts after this key (wrapping around to the +beginning of the key space if there is no such file). + +

+Compactions drop overwritten values. They also drop deletion markers +if there are no higher numbered levels that contain a file whose range +overlaps the current key. + +

Timing

+ +Level-0 compactions will read up to four 1MB files from level-0, and +at worst all the level-1 files (10MB). I.e., we will read 14MB and +write 14MB. + +

+Other than the special level-0 compactions, we will pick one 2MB file +from level L. In the worst case, this will overlap ~ 12 files from +level L+1 (10 because level-(L+1) is ten times the size of level-L, +and another two at the boundaries since the file ranges at level-L +will usually not be aligned with the file ranges at level-L+1). The +compaction will therefore read 26MB and write 26MB. Assuming a disk +IO rate of 100MB/s (ballpark range for modern drives), the worst +compaction cost will be approximately 0.5 second. + +

+If we throttle the background writing to something small, say 10% of +the full 100MB/s speed, a compaction may take up to 5 seconds. If the +user is writing at 10MB/s, we might build up lots of level-0 files +(~50 to hold the 5*10MB). This may signficantly increase the cost of +reads due to the overhead of merging more files together on every +read. + +

+Solution 1: To reduce this problem, we might want to increase the log +switching threshold when the number of level-0 files is large. Though +the downside is that the larger this threshold, the more memory we will +need to hold the corresponding memtable. + +

+Solution 2: We might want to decrease write rate artificially when the +number of level-0 files goes up. + +

+Solution 3: We work on reducing the cost of very wide merges. +Perhaps most of the level-0 files will have their blocks sitting +uncompressed in the cache and we will only need to worry about the +O(N) complexity in the merging iterator. + +

Number of files

+ +Instead of always making 2MB files, we could make larger files for +larger levels to reduce the total file count, though at the expense of +more bursty compactions. Alternatively, we could shard the set of +files into multiple directories. + +

+An experiment on an ext3 filesystem on Feb 04, 2011 shows +the following timings to do 100K file opens in directories with +varying number of files: + + + + + +
Files in directoryMicroseconds to open a file
10009
1000010
10000016
+So maybe even the sharding is not necessary on modern filesystems? + +

Recovery

+ +
    +
  • Read CURRENT to find name of the latest committed MANIFEST +
  • Read the named MANIFEST file +
  • Clean up stale files +
  • We could open all sstables here, but it is probably better to be lazy... +
  • Convert log chunk to a new level-0 sstable +
  • Start directing new writes to a new log file with recovered sequence# +
+ +

Garbage collection of files

+ +DeleteObsoleteFiles() is called at the end of every +compaction and at the end of recovery. It finds the names of all +files in the database. It deletes all log files that are not the +current log file. It deletes all table files that are not referenced +from some level and are not the output of an active compaction. + + + diff --git a/src/leveldb-lin/doc/index.html b/src/leveldb-lin/doc/index.html new file mode 100644 index 0000000..3ed0ed9 --- /dev/null +++ b/src/leveldb-lin/doc/index.html @@ -0,0 +1,549 @@ + + + + +Leveldb + + + +

Leveldb

+
Jeff Dean, Sanjay Ghemawat
+

+The leveldb library provides a persistent key value store. Keys and +values are arbitrary byte arrays. The keys are ordered within the key +value store according to a user-specified comparator function. + +

+

Opening A Database

+

+A leveldb database has a name which corresponds to a file system +directory. All of the contents of database are stored in this +directory. The following example shows how to open a database, +creating it if necessary: +

+

+  #include <assert>
+  #include "leveldb/db.h"
+
+  leveldb::DB* db;
+  leveldb::Options options;
+  options.create_if_missing = true;
+  leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
+  assert(status.ok());
+  ...
+
+If you want to raise an error if the database already exists, add +the following line before the leveldb::DB::Open call: +
+  options.error_if_exists = true;
+
+

Status

+

+You may have noticed the leveldb::Status type above. Values of this +type are returned by most functions in leveldb that may encounter an +error. You can check if such a result is ok, and also print an +associated error message: +

+

+   leveldb::Status s = ...;
+   if (!s.ok()) cerr << s.ToString() << endl;
+
+

Closing A Database

+

+When you are done with a database, just delete the database object. +Example: +

+

+  ... open the db as described above ...
+  ... do something with db ...
+  delete db;
+
+

Reads And Writes

+

+The database provides Put, Delete, and Get methods to +modify/query the database. For example, the following code +moves the value stored under key1 to key2. +

+  std::string value;
+  leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
+  if (s.ok()) s = db->Put(leveldb::WriteOptions(), key2, value);
+  if (s.ok()) s = db->Delete(leveldb::WriteOptions(), key1);
+
+ +

Atomic Updates

+

+Note that if the process dies after the Put of key2 but before the +delete of key1, the same value may be left stored under multiple keys. +Such problems can be avoided by using the WriteBatch class to +atomically apply a set of updates: +

+

+  #include "leveldb/write_batch.h"
+  ...
+  std::string value;
+  leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
+  if (s.ok()) {
+    leveldb::WriteBatch batch;
+    batch.Delete(key1);
+    batch.Put(key2, value);
+    s = db->Write(leveldb::WriteOptions(), &batch);
+  }
+
+The WriteBatch holds a sequence of edits to be made to the database, +and these edits within the batch are applied in order. Note that we +called Delete before Put so that if key1 is identical to key2, +we do not end up erroneously dropping the value entirely. +

+Apart from its atomicity benefits, WriteBatch may also be used to +speed up bulk updates by placing lots of individual mutations into the +same batch. + +

Synchronous Writes

+By default, each write to leveldb is asynchronous: it +returns after pushing the write from the process into the operating +system. The transfer from operating system memory to the underlying +persistent storage happens asynchronously. The sync flag +can be turned on for a particular write to make the write operation +not return until the data being written has been pushed all the way to +persistent storage. (On Posix systems, this is implemented by calling +either fsync(...) or fdatasync(...) or +msync(..., MS_SYNC) before the write operation returns.) +
+  leveldb::WriteOptions write_options;
+  write_options.sync = true;
+  db->Put(write_options, ...);
+
+Asynchronous writes are often more than a thousand times as fast as +synchronous writes. The downside of asynchronous writes is that a +crash of the machine may cause the last few updates to be lost. Note +that a crash of just the writing process (i.e., not a reboot) will not +cause any loss since even when sync is false, an update +is pushed from the process memory into the operating system before it +is considered done. + +

+Asynchronous writes can often be used safely. For example, when +loading a large amount of data into the database you can handle lost +updates by restarting the bulk load after a crash. A hybrid scheme is +also possible where every Nth write is synchronous, and in the event +of a crash, the bulk load is restarted just after the last synchronous +write finished by the previous run. (The synchronous write can update +a marker that describes where to restart on a crash.) + +

+WriteBatch provides an alternative to asynchronous writes. +Multiple updates may be placed in the same WriteBatch and +applied together using a synchronous write (i.e., +write_options.sync is set to true). The extra cost of +the synchronous write will be amortized across all of the writes in +the batch. + +

+

Concurrency

+

+A database may only be opened by one process at a time. +The leveldb implementation acquires a lock from the +operating system to prevent misuse. Within a single process, the +same leveldb::DB object may be safely shared by multiple +concurrent threads. I.e., different threads may write into or fetch +iterators or call Get on the same database without any +external synchronization (the leveldb implementation will +automatically do the required synchronization). However other objects +(like Iterator and WriteBatch) may require external synchronization. +If two threads share such an object, they must protect access to it +using their own locking protocol. More details are available in +the public header files. +

+

Iteration

+

+The following example demonstrates how to print all key,value pairs +in a database. +

+

+  leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
+  for (it->SeekToFirst(); it->Valid(); it->Next()) {
+    cout << it->key().ToString() << ": "  << it->value().ToString() << endl;
+  }
+  assert(it->status().ok());  // Check for any errors found during the scan
+  delete it;
+
+The following variation shows how to process just the keys in the +range [start,limit): +

+

+  for (it->Seek(start);
+       it->Valid() && it->key().ToString() < limit;
+       it->Next()) {
+    ...
+  }
+
+You can also process entries in reverse order. (Caveat: reverse +iteration may be somewhat slower than forward iteration.) +

+

+  for (it->SeekToLast(); it->Valid(); it->Prev()) {
+    ...
+  }
+
+

Snapshots

+

+Snapshots provide consistent read-only views over the entire state of +the key-value store. ReadOptions::snapshot may be non-NULL to indicate +that a read should operate on a particular version of the DB state. +If ReadOptions::snapshot is NULL, the read will operate on an +implicit snapshot of the current state. +

+Snapshots are created by the DB::GetSnapshot() method: +

+

+  leveldb::ReadOptions options;
+  options.snapshot = db->GetSnapshot();
+  ... apply some updates to db ...
+  leveldb::Iterator* iter = db->NewIterator(options);
+  ... read using iter to view the state when the snapshot was created ...
+  delete iter;
+  db->ReleaseSnapshot(options.snapshot);
+
+Note that when a snapshot is no longer needed, it should be released +using the DB::ReleaseSnapshot interface. This allows the +implementation to get rid of state that was being maintained just to +support reading as of that snapshot. +

Slice

+

+The return value of the it->key() and it->value() calls above +are instances of the leveldb::Slice type. Slice is a simple +structure that contains a length and a pointer to an external byte +array. Returning a Slice is a cheaper alternative to returning a +std::string since we do not need to copy potentially large keys and +values. In addition, leveldb methods do not return null-terminated +C-style strings since leveldb keys and values are allowed to +contain '\0' bytes. +

+C++ strings and null-terminated C-style strings can be easily converted +to a Slice: +

+

+   leveldb::Slice s1 = "hello";
+
+   std::string str("world");
+   leveldb::Slice s2 = str;
+
+A Slice can be easily converted back to a C++ string: +
+   std::string str = s1.ToString();
+   assert(str == std::string("hello"));
+
+Be careful when using Slices since it is up to the caller to ensure that +the external byte array into which the Slice points remains live while +the Slice is in use. For example, the following is buggy: +

+

+   leveldb::Slice slice;
+   if (...) {
+     std::string str = ...;
+     slice = str;
+   }
+   Use(slice);
+
+When the if statement goes out of scope, str will be destroyed and the +backing storage for slice will disappear. +

+

Comparators

+

+The preceding examples used the default ordering function for key, +which orders bytes lexicographically. You can however supply a custom +comparator when opening a database. For example, suppose each +database key consists of two numbers and we should sort by the first +number, breaking ties by the second number. First, define a proper +subclass of leveldb::Comparator that expresses these rules: +

+

+  class TwoPartComparator : public leveldb::Comparator {
+   public:
+    // Three-way comparison function:
+    //   if a < b: negative result
+    //   if a > b: positive result
+    //   else: zero result
+    int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const {
+      int a1, a2, b1, b2;
+      ParseKey(a, &a1, &a2);
+      ParseKey(b, &b1, &b2);
+      if (a1 < b1) return -1;
+      if (a1 > b1) return +1;
+      if (a2 < b2) return -1;
+      if (a2 > b2) return +1;
+      return 0;
+    }
+
+    // Ignore the following methods for now:
+    const char* Name() const { return "TwoPartComparator"; }
+    void FindShortestSeparator(std::string*, const leveldb::Slice&) const { }
+    void FindShortSuccessor(std::string*) const { }
+  };
+
+Now create a database using this custom comparator: +

+

+  TwoPartComparator cmp;
+  leveldb::DB* db;
+  leveldb::Options options;
+  options.create_if_missing = true;
+  options.comparator = &cmp;
+  leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
+  ...
+
+

Backwards compatibility

+

+The result of the comparator's Name method is attached to the +database when it is created, and is checked on every subsequent +database open. If the name changes, the leveldb::DB::Open call will +fail. Therefore, change the name if and only if the new key format +and comparison function are incompatible with existing databases, and +it is ok to discard the contents of all existing databases. +

+You can however still gradually evolve your key format over time with +a little bit of pre-planning. For example, you could store a version +number at the end of each key (one byte should suffice for most uses). +When you wish to switch to a new key format (e.g., adding an optional +third part to the keys processed by TwoPartComparator), +(a) keep the same comparator name (b) increment the version number +for new keys (c) change the comparator function so it uses the +version numbers found in the keys to decide how to interpret them. +

+

Performance

+

+Performance can be tuned by changing the default values of the +types defined in include/leveldb/options.h. + +

+

Block size

+

+leveldb groups adjacent keys together into the same block and such a +block is the unit of transfer to and from persistent storage. The +default block size is approximately 4096 uncompressed bytes. +Applications that mostly do bulk scans over the contents of the +database may wish to increase this size. Applications that do a lot +of point reads of small values may wish to switch to a smaller block +size if performance measurements indicate an improvement. There isn't +much benefit in using blocks smaller than one kilobyte, or larger than +a few megabytes. Also note that compression will be more effective +with larger block sizes. +

+

Compression

+

+Each block is individually compressed before being written to +persistent storage. Compression is on by default since the default +compression method is very fast, and is automatically disabled for +uncompressible data. In rare cases, applications may want to disable +compression entirely, but should only do so if benchmarks show a +performance improvement: +

+

+  leveldb::Options options;
+  options.compression = leveldb::kNoCompression;
+  ... leveldb::DB::Open(options, name, ...) ....
+
+

Cache

+

+The contents of the database are stored in a set of files in the +filesystem and each file stores a sequence of compressed blocks. If +options.cache is non-NULL, it is used to cache frequently used +uncompressed block contents. +

+

+  #include "leveldb/cache.h"
+
+  leveldb::Options options;
+  options.cache = leveldb::NewLRUCache(100 * 1048576);  // 100MB cache
+  leveldb::DB* db;
+  leveldb::DB::Open(options, name, &db);
+  ... use the db ...
+  delete db
+  delete options.cache;
+
+Note that the cache holds uncompressed data, and therefore it should +be sized according to application level data sizes, without any +reduction from compression. (Caching of compressed blocks is left to +the operating system buffer cache, or any custom Env +implementation provided by the client.) +

+When performing a bulk read, the application may wish to disable +caching so that the data processed by the bulk read does not end up +displacing most of the cached contents. A per-iterator option can be +used to achieve this: +

+

+  leveldb::ReadOptions options;
+  options.fill_cache = false;
+  leveldb::Iterator* it = db->NewIterator(options);
+  for (it->SeekToFirst(); it->Valid(); it->Next()) {
+    ...
+  }
+
+

Key Layout

+

+Note that the unit of disk transfer and caching is a block. Adjacent +keys (according to the database sort order) will usually be placed in +the same block. Therefore the application can improve its performance +by placing keys that are accessed together near each other and placing +infrequently used keys in a separate region of the key space. +

+For example, suppose we are implementing a simple file system on top +of leveldb. The types of entries we might wish to store are: +

+

+   filename -> permission-bits, length, list of file_block_ids
+   file_block_id -> data
+
+We might want to prefix filename keys with one letter (say '/') and the +file_block_id keys with a different letter (say '0') so that scans +over just the metadata do not force us to fetch and cache bulky file +contents. +

+

Filters

+

+Because of the way leveldb data is organized on disk, +a single Get() call may involve multiple reads from disk. +The optional FilterPolicy mechanism can be used to reduce +the number of disk reads substantially. +

+   leveldb::Options options;
+   options.filter_policy = NewBloomFilterPolicy(10);
+   leveldb::DB* db;
+   leveldb::DB::Open(options, "/tmp/testdb", &db);
+   ... use the database ...
+   delete db;
+   delete options.filter_policy;
+
+The preceding code associates a +Bloom filter +based filtering policy with the database. Bloom filter based +filtering relies on keeping some number of bits of data in memory per +key (in this case 10 bits per key since that is the argument we passed +to NewBloomFilterPolicy). This filter will reduce the number of unnecessary +disk reads needed for Get() calls by a factor of +approximately a 100. Increasing the bits per key will lead to a +larger reduction at the cost of more memory usage. We recommend that +applications whose working set does not fit in memory and that do a +lot of random reads set a filter policy. +

+If you are using a custom comparator, you should ensure that the filter +policy you are using is compatible with your comparator. For example, +consider a comparator that ignores trailing spaces when comparing keys. +NewBloomFilterPolicy must not be used with such a comparator. +Instead, the application should provide a custom filter policy that +also ignores trailing spaces. For example: +

+  class CustomFilterPolicy : public leveldb::FilterPolicy {
+   private:
+    FilterPolicy* builtin_policy_;
+   public:
+    CustomFilterPolicy() : builtin_policy_(NewBloomFilterPolicy(10)) { }
+    ~CustomFilterPolicy() { delete builtin_policy_; }
+
+    const char* Name() const { return "IgnoreTrailingSpacesFilter"; }
+
+    void CreateFilter(const Slice* keys, int n, std::string* dst) const {
+      // Use builtin bloom filter code after removing trailing spaces
+      std::vector<Slice> trimmed(n);
+      for (int i = 0; i < n; i++) {
+        trimmed[i] = RemoveTrailingSpaces(keys[i]);
+      }
+      return builtin_policy_->CreateFilter(&trimmed[i], n, dst);
+    }
+
+    bool KeyMayMatch(const Slice& key, const Slice& filter) const {
+      // Use builtin bloom filter code after removing trailing spaces
+      return builtin_policy_->KeyMayMatch(RemoveTrailingSpaces(key), filter);
+    }
+  };
+
+

+Advanced applications may provide a filter policy that does not use +a bloom filter but uses some other mechanism for summarizing a set +of keys. See leveldb/filter_policy.h for detail. +

+

Checksums

+

+leveldb associates checksums with all data it stores in the file system. +There are two separate controls provided over how aggressively these +checksums are verified: +

+

    +
  • ReadOptions::verify_checksums may be set to true to force + checksum verification of all data that is read from the file system on + behalf of a particular read. By default, no such verification is + done. +

    +

  • Options::paranoid_checks may be set to true before opening a + database to make the database implementation raise an error as soon as + it detects an internal corruption. Depending on which portion of the + database has been corrupted, the error may be raised when the database + is opened, or later by another database operation. By default, + paranoid checking is off so that the database can be used even if + parts of its persistent storage have been corrupted. +

    + If a database is corrupted (perhaps it cannot be opened when + paranoid checking is turned on), the leveldb::RepairDB function + may be used to recover as much of the data as possible +

    +

+

Approximate Sizes

+

+The GetApproximateSizes method can used to get the approximate +number of bytes of file system space used by one or more key ranges. +

+

+   leveldb::Range ranges[2];
+   ranges[0] = leveldb::Range("a", "c");
+   ranges[1] = leveldb::Range("x", "z");
+   uint64_t sizes[2];
+   leveldb::Status s = db->GetApproximateSizes(ranges, 2, sizes);
+
+The preceding call will set sizes[0] to the approximate number of +bytes of file system space used by the key range [a..c) and +sizes[1] to the approximate number of bytes used by the key range +[x..z). +

+

Environment

+

+All file operations (and other operating system calls) issued by the +leveldb implementation are routed through a leveldb::Env object. +Sophisticated clients may wish to provide their own Env +implementation to get better control. For example, an application may +introduce artificial delays in the file IO paths to limit the impact +of leveldb on other activities in the system. +

+

+  class SlowEnv : public leveldb::Env {
+    .. implementation of the Env interface ...
+  };
+
+  SlowEnv env;
+  leveldb::Options options;
+  options.env = &env;
+  Status s = leveldb::DB::Open(options, ...);
+
+

Porting

+

+leveldb may be ported to a new platform by providing platform +specific implementations of the types/methods/functions exported by +leveldb/port/port.h. See leveldb/port/port_example.h for more +details. +

+In addition, the new platform may need a new default leveldb::Env +implementation. See leveldb/util/env_posix.h for an example. + +

Other Information

+ +

+Details about the leveldb implementation may be found in +the following documents: +

+ + + diff --git a/src/leveldb-lin/doc/log_format.txt b/src/leveldb-lin/doc/log_format.txt new file mode 100644 index 0000000..5228f62 --- /dev/null +++ b/src/leveldb-lin/doc/log_format.txt @@ -0,0 +1,75 @@ +The log file contents are a sequence of 32KB blocks. The only +exception is that the tail of the file may contain a partial block. + +Each block consists of a sequence of records: + block := record* trailer? + record := + checksum: uint32 // crc32c of type and data[] ; little-endian + length: uint16 // little-endian + type: uint8 // One of FULL, FIRST, MIDDLE, LAST + data: uint8[length] + +A record never starts within the last six bytes of a block (since it +won't fit). Any leftover bytes here form the trailer, which must +consist entirely of zero bytes and must be skipped by readers. + +Aside: if exactly seven bytes are left in the current block, and a new +non-zero length record is added, the writer must emit a FIRST record +(which contains zero bytes of user data) to fill up the trailing seven +bytes of the block and then emit all of the user data in subsequent +blocks. + +More types may be added in the future. Some Readers may skip record +types they do not understand, others may report that some data was +skipped. + +FULL == 1 +FIRST == 2 +MIDDLE == 3 +LAST == 4 + +The FULL record contains the contents of an entire user record. + +FIRST, MIDDLE, LAST are types used for user records that have been +split into multiple fragments (typically because of block boundaries). +FIRST is the type of the first fragment of a user record, LAST is the +type of the last fragment of a user record, and MID is the type of all +interior fragments of a user record. + +Example: consider a sequence of user records: + A: length 1000 + B: length 97270 + C: length 8000 +A will be stored as a FULL record in the first block. + +B will be split into three fragments: first fragment occupies the rest +of the first block, second fragment occupies the entirety of the +second block, and the third fragment occupies a prefix of the third +block. This will leave six bytes free in the third block, which will +be left empty as the trailer. + +C will be stored as a FULL record in the fourth block. + +=================== + +Some benefits over the recordio format: + +(1) We do not need any heuristics for resyncing - just go to next +block boundary and scan. If there is a corruption, skip to the next +block. As a side-benefit, we do not get confused when part of the +contents of one log file are embedded as a record inside another log +file. + +(2) Splitting at approximate boundaries (e.g., for mapreduce) is +simple: find the next block boundary and skip records until we +hit a FULL or FIRST record. + +(3) We do not need extra buffering for large records. + +Some downsides compared to recordio format: + +(1) No packing of tiny records. This could be fixed by adding a new +record type, so it is a shortcoming of the current implementation, +not necessarily the format. + +(2) No compression. Again, this could be fixed by adding new record types. diff --git a/src/leveldb-lin/doc/table_format.txt b/src/leveldb-lin/doc/table_format.txt new file mode 100644 index 0000000..ca8f9b4 --- /dev/null +++ b/src/leveldb-lin/doc/table_format.txt @@ -0,0 +1,104 @@ +File format +=========== + + + [data block 1] + [data block 2] + ... + [data block N] + [meta block 1] + ... + [meta block K] + [metaindex block] + [index block] + [Footer] (fixed size; starts at file_size - sizeof(Footer)) + + +The file contains internal pointers. Each such pointer is called +a BlockHandle and contains the following information: + offset: varint64 + size: varint64 +See https://developers.google.com/protocol-buffers/docs/encoding#varints +for an explanation of varint64 format. + +(1) The sequence of key/value pairs in the file are stored in sorted +order and partitioned into a sequence of data blocks. These blocks +come one after another at the beginning of the file. Each data block +is formatted according to the code in block_builder.cc, and then +optionally compressed. + +(2) After the data blocks we store a bunch of meta blocks. The +supported meta block types are described below. More meta block types +may be added in the future. Each meta block is again formatted using +block_builder.cc and then optionally compressed. + +(3) A "metaindex" block. It contains one entry for every other meta +block where the key is the name of the meta block and the value is a +BlockHandle pointing to that meta block. + +(4) An "index" block. This block contains one entry per data block, +where the key is a string >= last key in that data block and before +the first key in the successive data block. The value is the +BlockHandle for the data block. + +(6) At the very end of the file is a fixed length footer that contains +the BlockHandle of the metaindex and index blocks as well as a magic number. + metaindex_handle: char[p]; // Block handle for metaindex + index_handle: char[q]; // Block handle for index + padding: char[40-p-q]; // zeroed bytes to make fixed length + // (40==2*BlockHandle::kMaxEncodedLength) + magic: fixed64; // == 0xdb4775248b80fb57 (little-endian) + +"filter" Meta Block +------------------- + +If a "FilterPolicy" was specified when the database was opened, a +filter block is stored in each table. The "metaindex" block contains +an entry that maps from "filter." to the BlockHandle for the filter +block where "" is the string returned by the filter policy's +"Name()" method. + +The filter block stores a sequence of filters, where filter i contains +the output of FilterPolicy::CreateFilter() on all keys that are stored +in a block whose file offset falls within the range + + [ i*base ... (i+1)*base-1 ] + +Currently, "base" is 2KB. So for example, if blocks X and Y start in +the range [ 0KB .. 2KB-1 ], all of the keys in X and Y will be +converted to a filter by calling FilterPolicy::CreateFilter(), and the +resulting filter will be stored as the first filter in the filter +block. + +The filter block is formatted as follows: + + [filter 0] + [filter 1] + [filter 2] + ... + [filter N-1] + + [offset of filter 0] : 4 bytes + [offset of filter 1] : 4 bytes + [offset of filter 2] : 4 bytes + ... + [offset of filter N-1] : 4 bytes + + [offset of beginning of offset array] : 4 bytes + lg(base) : 1 byte + +The offset array at the end of the filter block allows efficient +mapping from a data block offset to the corresponding filter. + +"stats" Meta Block +------------------ + +This meta block contains a bunch of stats. The key is the name +of the statistic. The value contains the statistic. +TODO(postrelease): record following stats. + data size + index size + key size (uncompressed) + value size (uncompressed) + number of entries + number of data blocks diff --git a/src/leveldb-lin/helpers/memenv/memenv.cc b/src/leveldb-lin/helpers/memenv/memenv.cc new file mode 100644 index 0000000..5879de1 --- /dev/null +++ b/src/leveldb-lin/helpers/memenv/memenv.cc @@ -0,0 +1,384 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "helpers/memenv/memenv.h" + +#include "leveldb/env.h" +#include "leveldb/status.h" +#include "port/port.h" +#include "util/mutexlock.h" +#include +#include +#include +#include + +namespace leveldb { + +namespace { + +class FileState { + public: + // FileStates are reference counted. The initial reference count is zero + // and the caller must call Ref() at least once. + FileState() : refs_(0), size_(0) {} + + // Increase the reference count. + void Ref() { + MutexLock lock(&refs_mutex_); + ++refs_; + } + + // Decrease the reference count. Delete if this is the last reference. + void Unref() { + bool do_delete = false; + + { + MutexLock lock(&refs_mutex_); + --refs_; + assert(refs_ >= 0); + if (refs_ <= 0) { + do_delete = true; + } + } + + if (do_delete) { + delete this; + } + } + + uint64_t Size() const { return size_; } + + Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const { + if (offset > size_) { + return Status::IOError("Offset greater than file size."); + } + const uint64_t available = size_ - offset; + if (n > available) { + n = available; + } + if (n == 0) { + *result = Slice(); + return Status::OK(); + } + + size_t block = offset / kBlockSize; + size_t block_offset = offset % kBlockSize; + + if (n <= kBlockSize - block_offset) { + // The requested bytes are all in the first block. + *result = Slice(blocks_[block] + block_offset, n); + return Status::OK(); + } + + size_t bytes_to_copy = n; + char* dst = scratch; + + while (bytes_to_copy > 0) { + size_t avail = kBlockSize - block_offset; + if (avail > bytes_to_copy) { + avail = bytes_to_copy; + } + memcpy(dst, blocks_[block] + block_offset, avail); + + bytes_to_copy -= avail; + dst += avail; + block++; + block_offset = 0; + } + + *result = Slice(scratch, n); + return Status::OK(); + } + + Status Append(const Slice& data) { + const char* src = data.data(); + size_t src_len = data.size(); + + while (src_len > 0) { + size_t avail; + size_t offset = size_ % kBlockSize; + + if (offset != 0) { + // There is some room in the last block. + avail = kBlockSize - offset; + } else { + // No room in the last block; push new one. + blocks_.push_back(new char[kBlockSize]); + avail = kBlockSize; + } + + if (avail > src_len) { + avail = src_len; + } + memcpy(blocks_.back() + offset, src, avail); + src_len -= avail; + src += avail; + size_ += avail; + } + + return Status::OK(); + } + + private: + // Private since only Unref() should be used to delete it. + ~FileState() { + for (std::vector::iterator i = blocks_.begin(); i != blocks_.end(); + ++i) { + delete [] *i; + } + } + + // No copying allowed. + FileState(const FileState&); + void operator=(const FileState&); + + port::Mutex refs_mutex_; + int refs_; // Protected by refs_mutex_; + + // The following fields are not protected by any mutex. They are only mutable + // while the file is being written, and concurrent access is not allowed + // to writable files. + std::vector blocks_; + uint64_t size_; + + enum { kBlockSize = 8 * 1024 }; +}; + +class SequentialFileImpl : public SequentialFile { + public: + explicit SequentialFileImpl(FileState* file) : file_(file), pos_(0) { + file_->Ref(); + } + + ~SequentialFileImpl() { + file_->Unref(); + } + + virtual Status Read(size_t n, Slice* result, char* scratch) { + Status s = file_->Read(pos_, n, result, scratch); + if (s.ok()) { + pos_ += result->size(); + } + return s; + } + + virtual Status Skip(uint64_t n) { + if (pos_ > file_->Size()) { + return Status::IOError("pos_ > file_->Size()"); + } + const size_t available = file_->Size() - pos_; + if (n > available) { + n = available; + } + pos_ += n; + return Status::OK(); + } + + private: + FileState* file_; + size_t pos_; +}; + +class RandomAccessFileImpl : public RandomAccessFile { + public: + explicit RandomAccessFileImpl(FileState* file) : file_(file) { + file_->Ref(); + } + + ~RandomAccessFileImpl() { + file_->Unref(); + } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + return file_->Read(offset, n, result, scratch); + } + + private: + FileState* file_; +}; + +class WritableFileImpl : public WritableFile { + public: + WritableFileImpl(FileState* file) : file_(file) { + file_->Ref(); + } + + ~WritableFileImpl() { + file_->Unref(); + } + + virtual Status Append(const Slice& data) { + return file_->Append(data); + } + + virtual Status Close() { return Status::OK(); } + virtual Status Flush() { return Status::OK(); } + virtual Status Sync() { return Status::OK(); } + + private: + FileState* file_; +}; + +class NoOpLogger : public Logger { + public: + virtual void Logv(const char* format, va_list ap) { } +}; + +class InMemoryEnv : public EnvWrapper { + public: + explicit InMemoryEnv(Env* base_env) : EnvWrapper(base_env) { } + + virtual ~InMemoryEnv() { + for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){ + i->second->Unref(); + } + } + + // Partial implementation of the Env interface. + virtual Status NewSequentialFile(const std::string& fname, + SequentialFile** result) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) == file_map_.end()) { + *result = NULL; + return Status::IOError(fname, "File not found"); + } + + *result = new SequentialFileImpl(file_map_[fname]); + return Status::OK(); + } + + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) == file_map_.end()) { + *result = NULL; + return Status::IOError(fname, "File not found"); + } + + *result = new RandomAccessFileImpl(file_map_[fname]); + return Status::OK(); + } + + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) != file_map_.end()) { + DeleteFileInternal(fname); + } + + FileState* file = new FileState(); + file->Ref(); + file_map_[fname] = file; + + *result = new WritableFileImpl(file); + return Status::OK(); + } + + virtual bool FileExists(const std::string& fname) { + MutexLock lock(&mutex_); + return file_map_.find(fname) != file_map_.end(); + } + + virtual Status GetChildren(const std::string& dir, + std::vector* result) { + MutexLock lock(&mutex_); + result->clear(); + + for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){ + const std::string& filename = i->first; + + if (filename.size() >= dir.size() + 1 && filename[dir.size()] == '/' && + Slice(filename).starts_with(Slice(dir))) { + result->push_back(filename.substr(dir.size() + 1)); + } + } + + return Status::OK(); + } + + void DeleteFileInternal(const std::string& fname) { + if (file_map_.find(fname) == file_map_.end()) { + return; + } + + file_map_[fname]->Unref(); + file_map_.erase(fname); + } + + virtual Status DeleteFile(const std::string& fname) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) == file_map_.end()) { + return Status::IOError(fname, "File not found"); + } + + DeleteFileInternal(fname); + return Status::OK(); + } + + virtual Status CreateDir(const std::string& dirname) { + return Status::OK(); + } + + virtual Status DeleteDir(const std::string& dirname) { + return Status::OK(); + } + + virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) == file_map_.end()) { + return Status::IOError(fname, "File not found"); + } + + *file_size = file_map_[fname]->Size(); + return Status::OK(); + } + + virtual Status RenameFile(const std::string& src, + const std::string& target) { + MutexLock lock(&mutex_); + if (file_map_.find(src) == file_map_.end()) { + return Status::IOError(src, "File not found"); + } + + DeleteFileInternal(target); + file_map_[target] = file_map_[src]; + file_map_.erase(src); + return Status::OK(); + } + + virtual Status LockFile(const std::string& fname, FileLock** lock) { + *lock = new FileLock; + return Status::OK(); + } + + virtual Status UnlockFile(FileLock* lock) { + delete lock; + return Status::OK(); + } + + virtual Status GetTestDirectory(std::string* path) { + *path = "/test"; + return Status::OK(); + } + + virtual Status NewLogger(const std::string& fname, Logger** result) { + *result = new NoOpLogger; + return Status::OK(); + } + + private: + // Map from filenames to FileState objects, representing a simple file system. + typedef std::map FileSystem; + port::Mutex mutex_; + FileSystem file_map_; // Protected by mutex_. +}; + +} // namespace + +Env* NewMemEnv(Env* base_env) { + return new InMemoryEnv(base_env); +} + +} // namespace leveldb diff --git a/src/leveldb-lin/helpers/memenv/memenv.h b/src/leveldb-lin/helpers/memenv/memenv.h new file mode 100644 index 0000000..03b88de --- /dev/null +++ b/src/leveldb-lin/helpers/memenv/memenv.h @@ -0,0 +1,20 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_ +#define STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_ + +namespace leveldb { + +class Env; + +// Returns a new environment that stores its data in memory and delegates +// all non-file-storage tasks to base_env. The caller must delete the result +// when it is no longer needed. +// *base_env must remain live while the result is in use. +Env* NewMemEnv(Env* base_env); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_ diff --git a/src/leveldb-lin/helpers/memenv/memenv.o b/src/leveldb-lin/helpers/memenv/memenv.o new file mode 100644 index 0000000..420eae0 Binary files /dev/null and b/src/leveldb-lin/helpers/memenv/memenv.o differ diff --git a/src/leveldb-lin/helpers/memenv/memenv_test.cc b/src/leveldb-lin/helpers/memenv/memenv_test.cc new file mode 100644 index 0000000..a44310f --- /dev/null +++ b/src/leveldb-lin/helpers/memenv/memenv_test.cc @@ -0,0 +1,232 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "helpers/memenv/memenv.h" + +#include "db/db_impl.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "util/testharness.h" +#include +#include + +namespace leveldb { + +class MemEnvTest { + public: + Env* env_; + + MemEnvTest() + : env_(NewMemEnv(Env::Default())) { + } + ~MemEnvTest() { + delete env_; + } +}; + +TEST(MemEnvTest, Basics) { + uint64_t file_size; + WritableFile* writable_file; + std::vector children; + + ASSERT_OK(env_->CreateDir("/dir")); + + // Check that the directory is empty. + ASSERT_TRUE(!env_->FileExists("/dir/non_existent")); + ASSERT_TRUE(!env_->GetFileSize("/dir/non_existent", &file_size).ok()); + ASSERT_OK(env_->GetChildren("/dir", &children)); + ASSERT_EQ(0, children.size()); + + // Create a file. + ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + delete writable_file; + + // Check that the file exists. + ASSERT_TRUE(env_->FileExists("/dir/f")); + ASSERT_OK(env_->GetFileSize("/dir/f", &file_size)); + ASSERT_EQ(0, file_size); + ASSERT_OK(env_->GetChildren("/dir", &children)); + ASSERT_EQ(1, children.size()); + ASSERT_EQ("f", children[0]); + + // Write to the file. + ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + ASSERT_OK(writable_file->Append("abc")); + delete writable_file; + + // Check for expected size. + ASSERT_OK(env_->GetFileSize("/dir/f", &file_size)); + ASSERT_EQ(3, file_size); + + // Check that renaming works. + ASSERT_TRUE(!env_->RenameFile("/dir/non_existent", "/dir/g").ok()); + ASSERT_OK(env_->RenameFile("/dir/f", "/dir/g")); + ASSERT_TRUE(!env_->FileExists("/dir/f")); + ASSERT_TRUE(env_->FileExists("/dir/g")); + ASSERT_OK(env_->GetFileSize("/dir/g", &file_size)); + ASSERT_EQ(3, file_size); + + // Check that opening non-existent file fails. + SequentialFile* seq_file; + RandomAccessFile* rand_file; + ASSERT_TRUE(!env_->NewSequentialFile("/dir/non_existent", &seq_file).ok()); + ASSERT_TRUE(!seq_file); + ASSERT_TRUE(!env_->NewRandomAccessFile("/dir/non_existent", &rand_file).ok()); + ASSERT_TRUE(!rand_file); + + // Check that deleting works. + ASSERT_TRUE(!env_->DeleteFile("/dir/non_existent").ok()); + ASSERT_OK(env_->DeleteFile("/dir/g")); + ASSERT_TRUE(!env_->FileExists("/dir/g")); + ASSERT_OK(env_->GetChildren("/dir", &children)); + ASSERT_EQ(0, children.size()); + ASSERT_OK(env_->DeleteDir("/dir")); +} + +TEST(MemEnvTest, ReadWrite) { + WritableFile* writable_file; + SequentialFile* seq_file; + RandomAccessFile* rand_file; + Slice result; + char scratch[100]; + + ASSERT_OK(env_->CreateDir("/dir")); + + ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + ASSERT_OK(writable_file->Append("hello ")); + ASSERT_OK(writable_file->Append("world")); + delete writable_file; + + // Read sequentially. + ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file)); + ASSERT_OK(seq_file->Read(5, &result, scratch)); // Read "hello". + ASSERT_EQ(0, result.compare("hello")); + ASSERT_OK(seq_file->Skip(1)); + ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Read "world". + ASSERT_EQ(0, result.compare("world")); + ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Try reading past EOF. + ASSERT_EQ(0, result.size()); + ASSERT_OK(seq_file->Skip(100)); // Try to skip past end of file. + ASSERT_OK(seq_file->Read(1000, &result, scratch)); + ASSERT_EQ(0, result.size()); + delete seq_file; + + // Random reads. + ASSERT_OK(env_->NewRandomAccessFile("/dir/f", &rand_file)); + ASSERT_OK(rand_file->Read(6, 5, &result, scratch)); // Read "world". + ASSERT_EQ(0, result.compare("world")); + ASSERT_OK(rand_file->Read(0, 5, &result, scratch)); // Read "hello". + ASSERT_EQ(0, result.compare("hello")); + ASSERT_OK(rand_file->Read(10, 100, &result, scratch)); // Read "d". + ASSERT_EQ(0, result.compare("d")); + + // Too high offset. + ASSERT_TRUE(!rand_file->Read(1000, 5, &result, scratch).ok()); + delete rand_file; +} + +TEST(MemEnvTest, Locks) { + FileLock* lock; + + // These are no-ops, but we test they return success. + ASSERT_OK(env_->LockFile("some file", &lock)); + ASSERT_OK(env_->UnlockFile(lock)); +} + +TEST(MemEnvTest, Misc) { + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + ASSERT_TRUE(!test_dir.empty()); + + WritableFile* writable_file; + ASSERT_OK(env_->NewWritableFile("/a/b", &writable_file)); + + // These are no-ops, but we test they return success. + ASSERT_OK(writable_file->Sync()); + ASSERT_OK(writable_file->Flush()); + ASSERT_OK(writable_file->Close()); + delete writable_file; +} + +TEST(MemEnvTest, LargeWrite) { + const size_t kWriteSize = 300 * 1024; + char* scratch = new char[kWriteSize * 2]; + + std::string write_data; + for (size_t i = 0; i < kWriteSize; ++i) { + write_data.append(1, static_cast(i)); + } + + WritableFile* writable_file; + ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + ASSERT_OK(writable_file->Append("foo")); + ASSERT_OK(writable_file->Append(write_data)); + delete writable_file; + + SequentialFile* seq_file; + Slice result; + ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file)); + ASSERT_OK(seq_file->Read(3, &result, scratch)); // Read "foo". + ASSERT_EQ(0, result.compare("foo")); + + size_t read = 0; + std::string read_data; + while (read < kWriteSize) { + ASSERT_OK(seq_file->Read(kWriteSize - read, &result, scratch)); + read_data.append(result.data(), result.size()); + read += result.size(); + } + ASSERT_TRUE(write_data == read_data); + delete seq_file; + delete [] scratch; +} + +TEST(MemEnvTest, DBTest) { + Options options; + options.create_if_missing = true; + options.env = env_; + DB* db; + + const Slice keys[] = {Slice("aaa"), Slice("bbb"), Slice("ccc")}; + const Slice vals[] = {Slice("foo"), Slice("bar"), Slice("baz")}; + + ASSERT_OK(DB::Open(options, "/dir/db", &db)); + for (size_t i = 0; i < 3; ++i) { + ASSERT_OK(db->Put(WriteOptions(), keys[i], vals[i])); + } + + for (size_t i = 0; i < 3; ++i) { + std::string res; + ASSERT_OK(db->Get(ReadOptions(), keys[i], &res)); + ASSERT_TRUE(res == vals[i]); + } + + Iterator* iterator = db->NewIterator(ReadOptions()); + iterator->SeekToFirst(); + for (size_t i = 0; i < 3; ++i) { + ASSERT_TRUE(iterator->Valid()); + ASSERT_TRUE(keys[i] == iterator->key()); + ASSERT_TRUE(vals[i] == iterator->value()); + iterator->Next(); + } + ASSERT_TRUE(!iterator->Valid()); + delete iterator; + + DBImpl* dbi = reinterpret_cast(db); + ASSERT_OK(dbi->TEST_CompactMemTable()); + + for (size_t i = 0; i < 3; ++i) { + std::string res; + ASSERT_OK(db->Get(ReadOptions(), keys[i], &res)); + ASSERT_TRUE(res == vals[i]); + } + + delete db; +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/include/leveldb/c.h b/src/leveldb-lin/include/leveldb/c.h new file mode 100644 index 0000000..1fa5886 --- /dev/null +++ b/src/leveldb-lin/include/leveldb/c.h @@ -0,0 +1,291 @@ +/* Copyright (c) 2011 The LevelDB Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. See the AUTHORS file for names of contributors. + + C bindings for leveldb. May be useful as a stable ABI that can be + used by programs that keep leveldb in a shared library, or for + a JNI api. + + Does not support: + . getters for the option types + . custom comparators that implement key shortening + . capturing post-write-snapshot + . custom iter, db, env, cache implementations using just the C bindings + + Some conventions: + + (1) We expose just opaque struct pointers and functions to clients. + This allows us to change internal representations without having to + recompile clients. + + (2) For simplicity, there is no equivalent to the Slice type. Instead, + the caller has to pass the pointer and length as separate + arguments. + + (3) Errors are represented by a null-terminated c string. NULL + means no error. All operations that can raise an error are passed + a "char** errptr" as the last argument. One of the following must + be true on entry: + *errptr == NULL + *errptr points to a malloc()ed null-terminated error message + (On Windows, *errptr must have been malloc()-ed by this library.) + On success, a leveldb routine leaves *errptr unchanged. + On failure, leveldb frees the old value of *errptr and + set *errptr to a malloc()ed error message. + + (4) Bools have the type unsigned char (0 == false; rest == true) + + (5) All of the pointer arguments must be non-NULL. +*/ + +#ifndef STORAGE_LEVELDB_INCLUDE_C_H_ +#define STORAGE_LEVELDB_INCLUDE_C_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* Exported types */ + +typedef struct leveldb_t leveldb_t; +typedef struct leveldb_cache_t leveldb_cache_t; +typedef struct leveldb_comparator_t leveldb_comparator_t; +typedef struct leveldb_env_t leveldb_env_t; +typedef struct leveldb_filelock_t leveldb_filelock_t; +typedef struct leveldb_filterpolicy_t leveldb_filterpolicy_t; +typedef struct leveldb_iterator_t leveldb_iterator_t; +typedef struct leveldb_logger_t leveldb_logger_t; +typedef struct leveldb_options_t leveldb_options_t; +typedef struct leveldb_randomfile_t leveldb_randomfile_t; +typedef struct leveldb_readoptions_t leveldb_readoptions_t; +typedef struct leveldb_seqfile_t leveldb_seqfile_t; +typedef struct leveldb_snapshot_t leveldb_snapshot_t; +typedef struct leveldb_writablefile_t leveldb_writablefile_t; +typedef struct leveldb_writebatch_t leveldb_writebatch_t; +typedef struct leveldb_writeoptions_t leveldb_writeoptions_t; + +/* DB operations */ + +extern leveldb_t* leveldb_open( + const leveldb_options_t* options, + const char* name, + char** errptr); + +extern void leveldb_close(leveldb_t* db); + +extern void leveldb_put( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + const char* val, size_t vallen, + char** errptr); + +extern void leveldb_delete( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + char** errptr); + +extern void leveldb_write( + leveldb_t* db, + const leveldb_writeoptions_t* options, + leveldb_writebatch_t* batch, + char** errptr); + +/* Returns NULL if not found. A malloc()ed array otherwise. + Stores the length of the array in *vallen. */ +extern char* leveldb_get( + leveldb_t* db, + const leveldb_readoptions_t* options, + const char* key, size_t keylen, + size_t* vallen, + char** errptr); + +extern leveldb_iterator_t* leveldb_create_iterator( + leveldb_t* db, + const leveldb_readoptions_t* options); + +extern const leveldb_snapshot_t* leveldb_create_snapshot( + leveldb_t* db); + +extern void leveldb_release_snapshot( + leveldb_t* db, + const leveldb_snapshot_t* snapshot); + +/* Returns NULL if property name is unknown. + Else returns a pointer to a malloc()-ed null-terminated value. */ +extern char* leveldb_property_value( + leveldb_t* db, + const char* propname); + +extern void leveldb_approximate_sizes( + leveldb_t* db, + int num_ranges, + const char* const* range_start_key, const size_t* range_start_key_len, + const char* const* range_limit_key, const size_t* range_limit_key_len, + uint64_t* sizes); + +extern void leveldb_compact_range( + leveldb_t* db, + const char* start_key, size_t start_key_len, + const char* limit_key, size_t limit_key_len); + +/* Management operations */ + +extern void leveldb_destroy_db( + const leveldb_options_t* options, + const char* name, + char** errptr); + +extern void leveldb_repair_db( + const leveldb_options_t* options, + const char* name, + char** errptr); + +/* Iterator */ + +extern void leveldb_iter_destroy(leveldb_iterator_t*); +extern unsigned char leveldb_iter_valid(const leveldb_iterator_t*); +extern void leveldb_iter_seek_to_first(leveldb_iterator_t*); +extern void leveldb_iter_seek_to_last(leveldb_iterator_t*); +extern void leveldb_iter_seek(leveldb_iterator_t*, const char* k, size_t klen); +extern void leveldb_iter_next(leveldb_iterator_t*); +extern void leveldb_iter_prev(leveldb_iterator_t*); +extern const char* leveldb_iter_key(const leveldb_iterator_t*, size_t* klen); +extern const char* leveldb_iter_value(const leveldb_iterator_t*, size_t* vlen); +extern void leveldb_iter_get_error(const leveldb_iterator_t*, char** errptr); + +/* Write batch */ + +extern leveldb_writebatch_t* leveldb_writebatch_create(); +extern void leveldb_writebatch_destroy(leveldb_writebatch_t*); +extern void leveldb_writebatch_clear(leveldb_writebatch_t*); +extern void leveldb_writebatch_put( + leveldb_writebatch_t*, + const char* key, size_t klen, + const char* val, size_t vlen); +extern void leveldb_writebatch_delete( + leveldb_writebatch_t*, + const char* key, size_t klen); +extern void leveldb_writebatch_iterate( + leveldb_writebatch_t*, + void* state, + void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen), + void (*deleted)(void*, const char* k, size_t klen)); + +/* Options */ + +extern leveldb_options_t* leveldb_options_create(); +extern void leveldb_options_destroy(leveldb_options_t*); +extern void leveldb_options_set_comparator( + leveldb_options_t*, + leveldb_comparator_t*); +extern void leveldb_options_set_filter_policy( + leveldb_options_t*, + leveldb_filterpolicy_t*); +extern void leveldb_options_set_create_if_missing( + leveldb_options_t*, unsigned char); +extern void leveldb_options_set_error_if_exists( + leveldb_options_t*, unsigned char); +extern void leveldb_options_set_paranoid_checks( + leveldb_options_t*, unsigned char); +extern void leveldb_options_set_env(leveldb_options_t*, leveldb_env_t*); +extern void leveldb_options_set_info_log(leveldb_options_t*, leveldb_logger_t*); +extern void leveldb_options_set_write_buffer_size(leveldb_options_t*, size_t); +extern void leveldb_options_set_max_open_files(leveldb_options_t*, int); +extern void leveldb_options_set_cache(leveldb_options_t*, leveldb_cache_t*); +extern void leveldb_options_set_block_size(leveldb_options_t*, size_t); +extern void leveldb_options_set_block_restart_interval(leveldb_options_t*, int); + +enum { + leveldb_no_compression = 0, + leveldb_snappy_compression = 1 +}; +extern void leveldb_options_set_compression(leveldb_options_t*, int); + +/* Comparator */ + +extern leveldb_comparator_t* leveldb_comparator_create( + void* state, + void (*destructor)(void*), + int (*compare)( + void*, + const char* a, size_t alen, + const char* b, size_t blen), + const char* (*name)(void*)); +extern void leveldb_comparator_destroy(leveldb_comparator_t*); + +/* Filter policy */ + +extern leveldb_filterpolicy_t* leveldb_filterpolicy_create( + void* state, + void (*destructor)(void*), + char* (*create_filter)( + void*, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length), + unsigned char (*key_may_match)( + void*, + const char* key, size_t length, + const char* filter, size_t filter_length), + const char* (*name)(void*)); +extern void leveldb_filterpolicy_destroy(leveldb_filterpolicy_t*); + +extern leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom( + int bits_per_key); + +/* Read options */ + +extern leveldb_readoptions_t* leveldb_readoptions_create(); +extern void leveldb_readoptions_destroy(leveldb_readoptions_t*); +extern void leveldb_readoptions_set_verify_checksums( + leveldb_readoptions_t*, + unsigned char); +extern void leveldb_readoptions_set_fill_cache( + leveldb_readoptions_t*, unsigned char); +extern void leveldb_readoptions_set_snapshot( + leveldb_readoptions_t*, + const leveldb_snapshot_t*); + +/* Write options */ + +extern leveldb_writeoptions_t* leveldb_writeoptions_create(); +extern void leveldb_writeoptions_destroy(leveldb_writeoptions_t*); +extern void leveldb_writeoptions_set_sync( + leveldb_writeoptions_t*, unsigned char); + +/* Cache */ + +extern leveldb_cache_t* leveldb_cache_create_lru(size_t capacity); +extern void leveldb_cache_destroy(leveldb_cache_t* cache); + +/* Env */ + +extern leveldb_env_t* leveldb_create_default_env(); +extern void leveldb_env_destroy(leveldb_env_t*); + +/* Utility */ + +/* Calls free(ptr). + REQUIRES: ptr was malloc()-ed and returned by one of the routines + in this file. Note that in certain cases (typically on Windows), you + may need to call this routine instead of free(ptr) to dispose of + malloc()-ed memory returned by this library. */ +extern void leveldb_free(void* ptr); + +/* Return the major version number for this release. */ +extern int leveldb_major_version(); + +/* Return the minor version number for this release. */ +extern int leveldb_minor_version(); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* STORAGE_LEVELDB_INCLUDE_C_H_ */ diff --git a/src/leveldb-lin/include/leveldb/cache.h b/src/leveldb-lin/include/leveldb/cache.h new file mode 100644 index 0000000..5e3b476 --- /dev/null +++ b/src/leveldb-lin/include/leveldb/cache.h @@ -0,0 +1,99 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A Cache is an interface that maps keys to values. It has internal +// synchronization and may be safely accessed concurrently from +// multiple threads. It may automatically evict entries to make room +// for new entries. Values have a specified charge against the cache +// capacity. For example, a cache where the values are variable +// length strings, may use the length of the string as the charge for +// the string. +// +// A builtin cache implementation with a least-recently-used eviction +// policy is provided. Clients may use their own implementations if +// they want something more sophisticated (like scan-resistance, a +// custom eviction policy, variable cache sizing, etc.) + +#ifndef STORAGE_LEVELDB_INCLUDE_CACHE_H_ +#define STORAGE_LEVELDB_INCLUDE_CACHE_H_ + +#include +#include "leveldb/slice.h" + +namespace leveldb { + +class Cache; + +// Create a new cache with a fixed size capacity. This implementation +// of Cache uses a least-recently-used eviction policy. +extern Cache* NewLRUCache(size_t capacity); + +class Cache { + public: + Cache() { } + + // Destroys all existing entries by calling the "deleter" + // function that was passed to the constructor. + virtual ~Cache(); + + // Opaque handle to an entry stored in the cache. + struct Handle { }; + + // Insert a mapping from key->value into the cache and assign it + // the specified charge against the total cache capacity. + // + // Returns a handle that corresponds to the mapping. The caller + // must call this->Release(handle) when the returned mapping is no + // longer needed. + // + // When the inserted entry is no longer needed, the key and + // value will be passed to "deleter". + virtual Handle* Insert(const Slice& key, void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)) = 0; + + // If the cache has no mapping for "key", returns NULL. + // + // Else return a handle that corresponds to the mapping. The caller + // must call this->Release(handle) when the returned mapping is no + // longer needed. + virtual Handle* Lookup(const Slice& key) = 0; + + // Release a mapping returned by a previous Lookup(). + // REQUIRES: handle must not have been released yet. + // REQUIRES: handle must have been returned by a method on *this. + virtual void Release(Handle* handle) = 0; + + // Return the value encapsulated in a handle returned by a + // successful Lookup(). + // REQUIRES: handle must not have been released yet. + // REQUIRES: handle must have been returned by a method on *this. + virtual void* Value(Handle* handle) = 0; + + // If the cache contains entry for key, erase it. Note that the + // underlying entry will be kept around until all existing handles + // to it have been released. + virtual void Erase(const Slice& key) = 0; + + // Return a new numeric id. May be used by multiple clients who are + // sharing the same cache to partition the key space. Typically the + // client will allocate a new id at startup and prepend the id to + // its cache keys. + virtual uint64_t NewId() = 0; + + private: + void LRU_Remove(Handle* e); + void LRU_Append(Handle* e); + void Unref(Handle* e); + + struct Rep; + Rep* rep_; + + // No copying allowed + Cache(const Cache&); + void operator=(const Cache&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_CACHE_H_ diff --git a/src/leveldb-lin/include/leveldb/comparator.h b/src/leveldb-lin/include/leveldb/comparator.h new file mode 100644 index 0000000..556b984 --- /dev/null +++ b/src/leveldb-lin/include/leveldb/comparator.h @@ -0,0 +1,63 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_ +#define STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_ + +#include + +namespace leveldb { + +class Slice; + +// A Comparator object provides a total order across slices that are +// used as keys in an sstable or a database. A Comparator implementation +// must be thread-safe since leveldb may invoke its methods concurrently +// from multiple threads. +class Comparator { + public: + virtual ~Comparator(); + + // Three-way comparison. Returns value: + // < 0 iff "a" < "b", + // == 0 iff "a" == "b", + // > 0 iff "a" > "b" + virtual int Compare(const Slice& a, const Slice& b) const = 0; + + // The name of the comparator. Used to check for comparator + // mismatches (i.e., a DB created with one comparator is + // accessed using a different comparator. + // + // The client of this package should switch to a new name whenever + // the comparator implementation changes in a way that will cause + // the relative ordering of any two keys to change. + // + // Names starting with "leveldb." are reserved and should not be used + // by any clients of this package. + virtual const char* Name() const = 0; + + // Advanced functions: these are used to reduce the space requirements + // for internal data structures like index blocks. + + // If *start < limit, changes *start to a short string in [start,limit). + // Simple comparator implementations may return with *start unchanged, + // i.e., an implementation of this method that does nothing is correct. + virtual void FindShortestSeparator( + std::string* start, + const Slice& limit) const = 0; + + // Changes *key to a short string >= *key. + // Simple comparator implementations may return with *key unchanged, + // i.e., an implementation of this method that does nothing is correct. + virtual void FindShortSuccessor(std::string* key) const = 0; +}; + +// Return a builtin comparator that uses lexicographic byte-wise +// ordering. The result remains the property of this module and +// must not be deleted. +extern const Comparator* BytewiseComparator(); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_ diff --git a/src/leveldb-lin/include/leveldb/db.h b/src/leveldb-lin/include/leveldb/db.h new file mode 100644 index 0000000..29d3674 --- /dev/null +++ b/src/leveldb-lin/include/leveldb/db.h @@ -0,0 +1,161 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_DB_H_ +#define STORAGE_LEVELDB_INCLUDE_DB_H_ + +#include +#include +#include "leveldb/iterator.h" +#include "leveldb/options.h" + +namespace leveldb { + +// Update Makefile if you change these +static const int kMajorVersion = 1; +static const int kMinorVersion = 9; + +struct Options; +struct ReadOptions; +struct WriteOptions; +class WriteBatch; + +// Abstract handle to particular state of a DB. +// A Snapshot is an immutable object and can therefore be safely +// accessed from multiple threads without any external synchronization. +class Snapshot { + protected: + virtual ~Snapshot(); +}; + +// A range of keys +struct Range { + Slice start; // Included in the range + Slice limit; // Not included in the range + + Range() { } + Range(const Slice& s, const Slice& l) : start(s), limit(l) { } +}; + +// A DB is a persistent ordered map from keys to values. +// A DB is safe for concurrent access from multiple threads without +// any external synchronization. +class DB { + public: + // Open the database with the specified "name". + // Stores a pointer to a heap-allocated database in *dbptr and returns + // OK on success. + // Stores NULL in *dbptr and returns a non-OK status on error. + // Caller should delete *dbptr when it is no longer needed. + static Status Open(const Options& options, + const std::string& name, + DB** dbptr); + + DB() { } + virtual ~DB(); + + // Set the database entry for "key" to "value". Returns OK on success, + // and a non-OK status on error. + // Note: consider setting options.sync = true. + virtual Status Put(const WriteOptions& options, + const Slice& key, + const Slice& value) = 0; + + // Remove the database entry (if any) for "key". Returns OK on + // success, and a non-OK status on error. It is not an error if "key" + // did not exist in the database. + // Note: consider setting options.sync = true. + virtual Status Delete(const WriteOptions& options, const Slice& key) = 0; + + // Apply the specified updates to the database. + // Returns OK on success, non-OK on failure. + // Note: consider setting options.sync = true. + virtual Status Write(const WriteOptions& options, WriteBatch* updates) = 0; + + // If the database contains an entry for "key" store the + // corresponding value in *value and return OK. + // + // If there is no entry for "key" leave *value unchanged and return + // a status for which Status::IsNotFound() returns true. + // + // May return some other Status on an error. + virtual Status Get(const ReadOptions& options, + const Slice& key, std::string* value) = 0; + + // Return a heap-allocated iterator over the contents of the database. + // The result of NewIterator() is initially invalid (caller must + // call one of the Seek methods on the iterator before using it). + // + // Caller should delete the iterator when it is no longer needed. + // The returned iterator should be deleted before this db is deleted. + virtual Iterator* NewIterator(const ReadOptions& options) = 0; + + // Return a handle to the current DB state. Iterators created with + // this handle will all observe a stable snapshot of the current DB + // state. The caller must call ReleaseSnapshot(result) when the + // snapshot is no longer needed. + virtual const Snapshot* GetSnapshot() = 0; + + // Release a previously acquired snapshot. The caller must not + // use "snapshot" after this call. + virtual void ReleaseSnapshot(const Snapshot* snapshot) = 0; + + // DB implementations can export properties about their state + // via this method. If "property" is a valid property understood by this + // DB implementation, fills "*value" with its current value and returns + // true. Otherwise returns false. + // + // + // Valid property names include: + // + // "leveldb.num-files-at-level" - return the number of files at level , + // where is an ASCII representation of a level number (e.g. "0"). + // "leveldb.stats" - returns a multi-line string that describes statistics + // about the internal operation of the DB. + // "leveldb.sstables" - returns a multi-line string that describes all + // of the sstables that make up the db contents. + virtual bool GetProperty(const Slice& property, std::string* value) = 0; + + // For each i in [0,n-1], store in "sizes[i]", the approximate + // file system space used by keys in "[range[i].start .. range[i].limit)". + // + // Note that the returned sizes measure file system space usage, so + // if the user data compresses by a factor of ten, the returned + // sizes will be one-tenth the size of the corresponding user data size. + // + // The results may not include the sizes of recently written data. + virtual void GetApproximateSizes(const Range* range, int n, + uint64_t* sizes) = 0; + + // Compact the underlying storage for the key range [*begin,*end]. + // In particular, deleted and overwritten versions are discarded, + // and the data is rearranged to reduce the cost of operations + // needed to access the data. This operation should typically only + // be invoked by users who understand the underlying implementation. + // + // begin==NULL is treated as a key before all keys in the database. + // end==NULL is treated as a key after all keys in the database. + // Therefore the following call will compact the entire database: + // db->CompactRange(NULL, NULL); + virtual void CompactRange(const Slice* begin, const Slice* end) = 0; + + private: + // No copying allowed + DB(const DB&); + void operator=(const DB&); +}; + +// Destroy the contents of the specified database. +// Be very careful using this method. +Status DestroyDB(const std::string& name, const Options& options); + +// If a DB cannot be opened, you may attempt to call this method to +// resurrect as much of the contents of the database as possible. +// Some data may be lost, so be careful when calling this function +// on a database that contains important information. +Status RepairDB(const std::string& dbname, const Options& options); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_DB_H_ diff --git a/src/leveldb-lin/include/leveldb/env.h b/src/leveldb-lin/include/leveldb/env.h new file mode 100644 index 0000000..fa32289 --- /dev/null +++ b/src/leveldb-lin/include/leveldb/env.h @@ -0,0 +1,333 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// An Env is an interface used by the leveldb implementation to access +// operating system functionality like the filesystem etc. Callers +// may wish to provide a custom Env object when opening a database to +// get fine gain control; e.g., to rate limit file system operations. +// +// All Env implementations are safe for concurrent access from +// multiple threads without any external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_ENV_H_ +#define STORAGE_LEVELDB_INCLUDE_ENV_H_ + +#include +#include +#include +#include +#include "leveldb/status.h" + +namespace leveldb { + +class FileLock; +class Logger; +class RandomAccessFile; +class SequentialFile; +class Slice; +class WritableFile; + +class Env { + public: + Env() { } + virtual ~Env(); + + // Return a default environment suitable for the current operating + // system. Sophisticated users may wish to provide their own Env + // implementation instead of relying on this default environment. + // + // The result of Default() belongs to leveldb and must never be deleted. + static Env* Default(); + + // Create a brand new sequentially-readable file with the specified name. + // On success, stores a pointer to the new file in *result and returns OK. + // On failure stores NULL in *result and returns non-OK. If the file does + // not exist, returns a non-OK status. + // + // The returned file will only be accessed by one thread at a time. + virtual Status NewSequentialFile(const std::string& fname, + SequentialFile** result) = 0; + + // Create a brand new random access read-only file with the + // specified name. On success, stores a pointer to the new file in + // *result and returns OK. On failure stores NULL in *result and + // returns non-OK. If the file does not exist, returns a non-OK + // status. + // + // The returned file may be concurrently accessed by multiple threads. + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) = 0; + + // Create an object that writes to a new file with the specified + // name. Deletes any existing file with the same name and creates a + // new file. On success, stores a pointer to the new file in + // *result and returns OK. On failure stores NULL in *result and + // returns non-OK. + // + // The returned file will only be accessed by one thread at a time. + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) = 0; + + // Returns true iff the named file exists. + virtual bool FileExists(const std::string& fname) = 0; + + // Store in *result the names of the children of the specified directory. + // The names are relative to "dir". + // Original contents of *results are dropped. + virtual Status GetChildren(const std::string& dir, + std::vector* result) = 0; + + // Delete the named file. + virtual Status DeleteFile(const std::string& fname) = 0; + + // Create the specified directory. + virtual Status CreateDir(const std::string& dirname) = 0; + + // Delete the specified directory. + virtual Status DeleteDir(const std::string& dirname) = 0; + + // Store the size of fname in *file_size. + virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) = 0; + + // Rename file src to target. + virtual Status RenameFile(const std::string& src, + const std::string& target) = 0; + + // Lock the specified file. Used to prevent concurrent access to + // the same db by multiple processes. On failure, stores NULL in + // *lock and returns non-OK. + // + // On success, stores a pointer to the object that represents the + // acquired lock in *lock and returns OK. The caller should call + // UnlockFile(*lock) to release the lock. If the process exits, + // the lock will be automatically released. + // + // If somebody else already holds the lock, finishes immediately + // with a failure. I.e., this call does not wait for existing locks + // to go away. + // + // May create the named file if it does not already exist. + virtual Status LockFile(const std::string& fname, FileLock** lock) = 0; + + // Release the lock acquired by a previous successful call to LockFile. + // REQUIRES: lock was returned by a successful LockFile() call + // REQUIRES: lock has not already been unlocked. + virtual Status UnlockFile(FileLock* lock) = 0; + + // Arrange to run "(*function)(arg)" once in a background thread. + // + // "function" may run in an unspecified thread. Multiple functions + // added to the same Env may run concurrently in different threads. + // I.e., the caller may not assume that background work items are + // serialized. + virtual void Schedule( + void (*function)(void* arg), + void* arg) = 0; + + // Start a new thread, invoking "function(arg)" within the new thread. + // When "function(arg)" returns, the thread will be destroyed. + virtual void StartThread(void (*function)(void* arg), void* arg) = 0; + + // *path is set to a temporary directory that can be used for testing. It may + // or many not have just been created. The directory may or may not differ + // between runs of the same process, but subsequent calls will return the + // same directory. + virtual Status GetTestDirectory(std::string* path) = 0; + + // Create and return a log file for storing informational messages. + virtual Status NewLogger(const std::string& fname, Logger** result) = 0; + + // Returns the number of micro-seconds since some fixed point in time. Only + // useful for computing deltas of time. + virtual uint64_t NowMicros() = 0; + + // Sleep/delay the thread for the perscribed number of micro-seconds. + virtual void SleepForMicroseconds(int micros) = 0; + + private: + // No copying allowed + Env(const Env&); + void operator=(const Env&); +}; + +// A file abstraction for reading sequentially through a file +class SequentialFile { + public: + SequentialFile() { } + virtual ~SequentialFile(); + + // Read up to "n" bytes from the file. "scratch[0..n-1]" may be + // written by this routine. Sets "*result" to the data that was + // read (including if fewer than "n" bytes were successfully read). + // May set "*result" to point at data in "scratch[0..n-1]", so + // "scratch[0..n-1]" must be live when "*result" is used. + // If an error was encountered, returns a non-OK status. + // + // REQUIRES: External synchronization + virtual Status Read(size_t n, Slice* result, char* scratch) = 0; + + // Skip "n" bytes from the file. This is guaranteed to be no + // slower that reading the same data, but may be faster. + // + // If end of file is reached, skipping will stop at the end of the + // file, and Skip will return OK. + // + // REQUIRES: External synchronization + virtual Status Skip(uint64_t n) = 0; + + private: + // No copying allowed + SequentialFile(const SequentialFile&); + void operator=(const SequentialFile&); +}; + +// A file abstraction for randomly reading the contents of a file. +class RandomAccessFile { + public: + RandomAccessFile() { } + virtual ~RandomAccessFile(); + + // Read up to "n" bytes from the file starting at "offset". + // "scratch[0..n-1]" may be written by this routine. Sets "*result" + // to the data that was read (including if fewer than "n" bytes were + // successfully read). May set "*result" to point at data in + // "scratch[0..n-1]", so "scratch[0..n-1]" must be live when + // "*result" is used. If an error was encountered, returns a non-OK + // status. + // + // Safe for concurrent use by multiple threads. + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const = 0; + + private: + // No copying allowed + RandomAccessFile(const RandomAccessFile&); + void operator=(const RandomAccessFile&); +}; + +// A file abstraction for sequential writing. The implementation +// must provide buffering since callers may append small fragments +// at a time to the file. +class WritableFile { + public: + WritableFile() { } + virtual ~WritableFile(); + + virtual Status Append(const Slice& data) = 0; + virtual Status Close() = 0; + virtual Status Flush() = 0; + virtual Status Sync() = 0; + + private: + // No copying allowed + WritableFile(const WritableFile&); + void operator=(const WritableFile&); +}; + +// An interface for writing log messages. +class Logger { + public: + Logger() { } + virtual ~Logger(); + + // Write an entry to the log file with the specified format. + virtual void Logv(const char* format, va_list ap) = 0; + + private: + // No copying allowed + Logger(const Logger&); + void operator=(const Logger&); +}; + + +// Identifies a locked file. +class FileLock { + public: + FileLock() { } + virtual ~FileLock(); + private: + // No copying allowed + FileLock(const FileLock&); + void operator=(const FileLock&); +}; + +// Log the specified data to *info_log if info_log is non-NULL. +extern void Log(Logger* info_log, const char* format, ...) +# if defined(__GNUC__) || defined(__clang__) + __attribute__((__format__ (__printf__, 2, 3))) +# endif + ; + +// A utility routine: write "data" to the named file. +extern Status WriteStringToFile(Env* env, const Slice& data, + const std::string& fname); + +// A utility routine: read contents of named file into *data +extern Status ReadFileToString(Env* env, const std::string& fname, + std::string* data); + +// An implementation of Env that forwards all calls to another Env. +// May be useful to clients who wish to override just part of the +// functionality of another Env. +class EnvWrapper : public Env { + public: + // Initialize an EnvWrapper that delegates all calls to *t + explicit EnvWrapper(Env* t) : target_(t) { } + virtual ~EnvWrapper(); + + // Return the target to which this Env forwards all calls + Env* target() const { return target_; } + + // The following text is boilerplate that forwards all methods to target() + Status NewSequentialFile(const std::string& f, SequentialFile** r) { + return target_->NewSequentialFile(f, r); + } + Status NewRandomAccessFile(const std::string& f, RandomAccessFile** r) { + return target_->NewRandomAccessFile(f, r); + } + Status NewWritableFile(const std::string& f, WritableFile** r) { + return target_->NewWritableFile(f, r); + } + bool FileExists(const std::string& f) { return target_->FileExists(f); } + Status GetChildren(const std::string& dir, std::vector* r) { + return target_->GetChildren(dir, r); + } + Status DeleteFile(const std::string& f) { return target_->DeleteFile(f); } + Status CreateDir(const std::string& d) { return target_->CreateDir(d); } + Status DeleteDir(const std::string& d) { return target_->DeleteDir(d); } + Status GetFileSize(const std::string& f, uint64_t* s) { + return target_->GetFileSize(f, s); + } + Status RenameFile(const std::string& s, const std::string& t) { + return target_->RenameFile(s, t); + } + Status LockFile(const std::string& f, FileLock** l) { + return target_->LockFile(f, l); + } + Status UnlockFile(FileLock* l) { return target_->UnlockFile(l); } + void Schedule(void (*f)(void*), void* a) { + return target_->Schedule(f, a); + } + void StartThread(void (*f)(void*), void* a) { + return target_->StartThread(f, a); + } + virtual Status GetTestDirectory(std::string* path) { + return target_->GetTestDirectory(path); + } + virtual Status NewLogger(const std::string& fname, Logger** result) { + return target_->NewLogger(fname, result); + } + uint64_t NowMicros() { + return target_->NowMicros(); + } + void SleepForMicroseconds(int micros) { + target_->SleepForMicroseconds(micros); + } + private: + Env* target_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_ENV_H_ diff --git a/src/leveldb-lin/include/leveldb/filter_policy.h b/src/leveldb-lin/include/leveldb/filter_policy.h new file mode 100644 index 0000000..1fba080 --- /dev/null +++ b/src/leveldb-lin/include/leveldb/filter_policy.h @@ -0,0 +1,70 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A database can be configured with a custom FilterPolicy object. +// This object is responsible for creating a small filter from a set +// of keys. These filters are stored in leveldb and are consulted +// automatically by leveldb to decide whether or not to read some +// information from disk. In many cases, a filter can cut down the +// number of disk seeks form a handful to a single disk seek per +// DB::Get() call. +// +// Most people will want to use the builtin bloom filter support (see +// NewBloomFilterPolicy() below). + +#ifndef STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ +#define STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ + +#include + +namespace leveldb { + +class Slice; + +class FilterPolicy { + public: + virtual ~FilterPolicy(); + + // Return the name of this policy. Note that if the filter encoding + // changes in an incompatible way, the name returned by this method + // must be changed. Otherwise, old incompatible filters may be + // passed to methods of this type. + virtual const char* Name() const = 0; + + // keys[0,n-1] contains a list of keys (potentially with duplicates) + // that are ordered according to the user supplied comparator. + // Append a filter that summarizes keys[0,n-1] to *dst. + // + // Warning: do not change the initial contents of *dst. Instead, + // append the newly constructed filter to *dst. + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) + const = 0; + + // "filter" contains the data appended by a preceding call to + // CreateFilter() on this class. This method must return true if + // the key was in the list of keys passed to CreateFilter(). + // This method may return true or false if the key was not on the + // list, but it should aim to return false with a high probability. + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const = 0; +}; + +// Return a new filter policy that uses a bloom filter with approximately +// the specified number of bits per key. A good value for bits_per_key +// is 10, which yields a filter with ~ 1% false positive rate. +// +// Callers must delete the result after any database that is using the +// result has been closed. +// +// Note: if you are using a custom comparator that ignores some parts +// of the keys being compared, you must not use NewBloomFilterPolicy() +// and must provide your own FilterPolicy that also ignores the +// corresponding parts of the keys. For example, if the comparator +// ignores trailing spaces, it would be incorrect to use a +// FilterPolicy (like NewBloomFilterPolicy) that does not ignore +// trailing spaces in keys. +extern const FilterPolicy* NewBloomFilterPolicy(int bits_per_key); + +} + +#endif // STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ diff --git a/src/leveldb-lin/include/leveldb/iterator.h b/src/leveldb-lin/include/leveldb/iterator.h new file mode 100644 index 0000000..ad543eb --- /dev/null +++ b/src/leveldb-lin/include/leveldb/iterator.h @@ -0,0 +1,100 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// An iterator yields a sequence of key/value pairs from a source. +// The following class defines the interface. Multiple implementations +// are provided by this library. In particular, iterators are provided +// to access the contents of a Table or a DB. +// +// Multiple threads can invoke const methods on an Iterator without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same Iterator must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ +#define STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ + +#include "leveldb/slice.h" +#include "leveldb/status.h" + +namespace leveldb { + +class Iterator { + public: + Iterator(); + virtual ~Iterator(); + + // An iterator is either positioned at a key/value pair, or + // not valid. This method returns true iff the iterator is valid. + virtual bool Valid() const = 0; + + // Position at the first key in the source. The iterator is Valid() + // after this call iff the source is not empty. + virtual void SeekToFirst() = 0; + + // Position at the last key in the source. The iterator is + // Valid() after this call iff the source is not empty. + virtual void SeekToLast() = 0; + + // Position at the first key in the source that at or past target + // The iterator is Valid() after this call iff the source contains + // an entry that comes at or past target. + virtual void Seek(const Slice& target) = 0; + + // Moves to the next entry in the source. After this call, Valid() is + // true iff the iterator was not positioned at the last entry in the source. + // REQUIRES: Valid() + virtual void Next() = 0; + + // Moves to the previous entry in the source. After this call, Valid() is + // true iff the iterator was not positioned at the first entry in source. + // REQUIRES: Valid() + virtual void Prev() = 0; + + // Return the key for the current entry. The underlying storage for + // the returned slice is valid only until the next modification of + // the iterator. + // REQUIRES: Valid() + virtual Slice key() const = 0; + + // Return the value for the current entry. The underlying storage for + // the returned slice is valid only until the next modification of + // the iterator. + // REQUIRES: !AtEnd() && !AtStart() + virtual Slice value() const = 0; + + // If an error has occurred, return it. Else return an ok status. + virtual Status status() const = 0; + + // Clients are allowed to register function/arg1/arg2 triples that + // will be invoked when this iterator is destroyed. + // + // Note that unlike all of the preceding methods, this method is + // not abstract and therefore clients should not override it. + typedef void (*CleanupFunction)(void* arg1, void* arg2); + void RegisterCleanup(CleanupFunction function, void* arg1, void* arg2); + + private: + struct Cleanup { + CleanupFunction function; + void* arg1; + void* arg2; + Cleanup* next; + }; + Cleanup cleanup_; + + // No copying allowed + Iterator(const Iterator&); + void operator=(const Iterator&); +}; + +// Return an empty iterator (yields nothing). +extern Iterator* NewEmptyIterator(); + +// Return an empty iterator with the specified status. +extern Iterator* NewErrorIterator(const Status& status); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ diff --git a/src/leveldb-lin/include/leveldb/options.h b/src/leveldb-lin/include/leveldb/options.h new file mode 100644 index 0000000..fdda718 --- /dev/null +++ b/src/leveldb-lin/include/leveldb/options.h @@ -0,0 +1,195 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_OPTIONS_H_ +#define STORAGE_LEVELDB_INCLUDE_OPTIONS_H_ + +#include + +namespace leveldb { + +class Cache; +class Comparator; +class Env; +class FilterPolicy; +class Logger; +class Snapshot; + +// DB contents are stored in a set of blocks, each of which holds a +// sequence of key,value pairs. Each block may be compressed before +// being stored in a file. The following enum describes which +// compression method (if any) is used to compress a block. +enum CompressionType { + // NOTE: do not change the values of existing entries, as these are + // part of the persistent format on disk. + kNoCompression = 0x0, + kSnappyCompression = 0x1 +}; + +// Options to control the behavior of a database (passed to DB::Open) +struct Options { + // ------------------- + // Parameters that affect behavior + + // Comparator used to define the order of keys in the table. + // Default: a comparator that uses lexicographic byte-wise ordering + // + // REQUIRES: The client must ensure that the comparator supplied + // here has the same name and orders keys *exactly* the same as the + // comparator provided to previous open calls on the same DB. + const Comparator* comparator; + + // If true, the database will be created if it is missing. + // Default: false + bool create_if_missing; + + // If true, an error is raised if the database already exists. + // Default: false + bool error_if_exists; + + // If true, the implementation will do aggressive checking of the + // data it is processing and will stop early if it detects any + // errors. This may have unforeseen ramifications: for example, a + // corruption of one DB entry may cause a large number of entries to + // become unreadable or for the entire DB to become unopenable. + // Default: false + bool paranoid_checks; + + // Use the specified object to interact with the environment, + // e.g. to read/write files, schedule background work, etc. + // Default: Env::Default() + Env* env; + + // Any internal progress/error information generated by the db will + // be written to info_log if it is non-NULL, or to a file stored + // in the same directory as the DB contents if info_log is NULL. + // Default: NULL + Logger* info_log; + + // ------------------- + // Parameters that affect performance + + // Amount of data to build up in memory (backed by an unsorted log + // on disk) before converting to a sorted on-disk file. + // + // Larger values increase performance, especially during bulk loads. + // Up to two write buffers may be held in memory at the same time, + // so you may wish to adjust this parameter to control memory usage. + // Also, a larger write buffer will result in a longer recovery time + // the next time the database is opened. + // + // Default: 4MB + size_t write_buffer_size; + + // Number of open files that can be used by the DB. You may need to + // increase this if your database has a large working set (budget + // one open file per 2MB of working set). + // + // Default: 1000 + int max_open_files; + + // Control over blocks (user data is stored in a set of blocks, and + // a block is the unit of reading from disk). + + // If non-NULL, use the specified cache for blocks. + // If NULL, leveldb will automatically create and use an 8MB internal cache. + // Default: NULL + Cache* block_cache; + + // Approximate size of user data packed per block. Note that the + // block size specified here corresponds to uncompressed data. The + // actual size of the unit read from disk may be smaller if + // compression is enabled. This parameter can be changed dynamically. + // + // Default: 4K + size_t block_size; + + // Number of keys between restart points for delta encoding of keys. + // This parameter can be changed dynamically. Most clients should + // leave this parameter alone. + // + // Default: 16 + int block_restart_interval; + + // Compress blocks using the specified compression algorithm. This + // parameter can be changed dynamically. + // + // Default: kSnappyCompression, which gives lightweight but fast + // compression. + // + // Typical speeds of kSnappyCompression on an Intel(R) Core(TM)2 2.4GHz: + // ~200-500MB/s compression + // ~400-800MB/s decompression + // Note that these speeds are significantly faster than most + // persistent storage speeds, and therefore it is typically never + // worth switching to kNoCompression. Even if the input data is + // incompressible, the kSnappyCompression implementation will + // efficiently detect that and will switch to uncompressed mode. + CompressionType compression; + + // If non-NULL, use the specified filter policy to reduce disk reads. + // Many applications will benefit from passing the result of + // NewBloomFilterPolicy() here. + // + // Default: NULL + const FilterPolicy* filter_policy; + + // Create an Options object with default values for all fields. + Options(); +}; + +// Options that control read operations +struct ReadOptions { + // If true, all data read from underlying storage will be + // verified against corresponding checksums. + // Default: false + bool verify_checksums; + + // Should the data read for this iteration be cached in memory? + // Callers may wish to set this field to false for bulk scans. + // Default: true + bool fill_cache; + + // If "snapshot" is non-NULL, read as of the supplied snapshot + // (which must belong to the DB that is being read and which must + // not have been released). If "snapshot" is NULL, use an impliicit + // snapshot of the state at the beginning of this read operation. + // Default: NULL + const Snapshot* snapshot; + + ReadOptions() + : verify_checksums(false), + fill_cache(true), + snapshot(NULL) { + } +}; + +// Options that control write operations +struct WriteOptions { + // If true, the write will be flushed from the operating system + // buffer cache (by calling WritableFile::Sync()) before the write + // is considered complete. If this flag is true, writes will be + // slower. + // + // If this flag is false, and the machine crashes, some recent + // writes may be lost. Note that if it is just the process that + // crashes (i.e., the machine does not reboot), no writes will be + // lost even if sync==false. + // + // In other words, a DB write with sync==false has similar + // crash semantics as the "write()" system call. A DB write + // with sync==true has similar crash semantics to a "write()" + // system call followed by "fsync()". + // + // Default: false + bool sync; + + WriteOptions() + : sync(false) { + } +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_OPTIONS_H_ diff --git a/src/leveldb-lin/include/leveldb/slice.h b/src/leveldb-lin/include/leveldb/slice.h new file mode 100644 index 0000000..74ea8fa --- /dev/null +++ b/src/leveldb-lin/include/leveldb/slice.h @@ -0,0 +1,109 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Slice is a simple structure containing a pointer into some external +// storage and a size. The user of a Slice must ensure that the slice +// is not used after the corresponding external storage has been +// deallocated. +// +// Multiple threads can invoke const methods on a Slice without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same Slice must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_SLICE_H_ +#define STORAGE_LEVELDB_INCLUDE_SLICE_H_ + +#include +#include +#include +#include + +namespace leveldb { + +class Slice { + public: + // Create an empty slice. + Slice() : data_(""), size_(0) { } + + // Create a slice that refers to d[0,n-1]. + Slice(const char* d, size_t n) : data_(d), size_(n) { } + + // Create a slice that refers to the contents of "s" + Slice(const std::string& s) : data_(s.data()), size_(s.size()) { } + + // Create a slice that refers to s[0,strlen(s)-1] + Slice(const char* s) : data_(s), size_(strlen(s)) { } + + // Return a pointer to the beginning of the referenced data + const char* data() const { return data_; } + + // Return the length (in bytes) of the referenced data + size_t size() const { return size_; } + + // Return true iff the length of the referenced data is zero + bool empty() const { return size_ == 0; } + + // Return the ith byte in the referenced data. + // REQUIRES: n < size() + char operator[](size_t n) const { + assert(n < size()); + return data_[n]; + } + + // Change this slice to refer to an empty array + void clear() { data_ = ""; size_ = 0; } + + // Drop the first "n" bytes from this slice. + void remove_prefix(size_t n) { + assert(n <= size()); + data_ += n; + size_ -= n; + } + + // Return a string that contains the copy of the referenced data. + std::string ToString() const { return std::string(data_, size_); } + + // Three-way comparison. Returns value: + // < 0 iff "*this" < "b", + // == 0 iff "*this" == "b", + // > 0 iff "*this" > "b" + int compare(const Slice& b) const; + + // Return true iff "x" is a prefix of "*this" + bool starts_with(const Slice& x) const { + return ((size_ >= x.size_) && + (memcmp(data_, x.data_, x.size_) == 0)); + } + + private: + const char* data_; + size_t size_; + + // Intentionally copyable +}; + +inline bool operator==(const Slice& x, const Slice& y) { + return ((x.size() == y.size()) && + (memcmp(x.data(), y.data(), x.size()) == 0)); +} + +inline bool operator!=(const Slice& x, const Slice& y) { + return !(x == y); +} + +inline int Slice::compare(const Slice& b) const { + const int min_len = (size_ < b.size_) ? size_ : b.size_; + int r = memcmp(data_, b.data_, min_len); + if (r == 0) { + if (size_ < b.size_) r = -1; + else if (size_ > b.size_) r = +1; + } + return r; +} + +} // namespace leveldb + + +#endif // STORAGE_LEVELDB_INCLUDE_SLICE_H_ diff --git a/src/leveldb-lin/include/leveldb/status.h b/src/leveldb-lin/include/leveldb/status.h new file mode 100644 index 0000000..11dbd4b --- /dev/null +++ b/src/leveldb-lin/include/leveldb/status.h @@ -0,0 +1,106 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A Status encapsulates the result of an operation. It may indicate success, +// or it may indicate an error with an associated error message. +// +// Multiple threads can invoke const methods on a Status without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same Status must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_STATUS_H_ +#define STORAGE_LEVELDB_INCLUDE_STATUS_H_ + +#include +#include "leveldb/slice.h" + +namespace leveldb { + +class Status { + public: + // Create a success status. + Status() : state_(NULL) { } + ~Status() { delete[] state_; } + + // Copy the specified status. + Status(const Status& s); + void operator=(const Status& s); + + // Return a success status. + static Status OK() { return Status(); } + + // Return error status of an appropriate type. + static Status NotFound(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kNotFound, msg, msg2); + } + static Status Corruption(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kCorruption, msg, msg2); + } + static Status NotSupported(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kNotSupported, msg, msg2); + } + static Status InvalidArgument(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kInvalidArgument, msg, msg2); + } + static Status IOError(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kIOError, msg, msg2); + } + + // Returns true iff the status indicates success. + bool ok() const { return (state_ == NULL); } + + // Returns true iff the status indicates a NotFound error. + bool IsNotFound() const { return code() == kNotFound; } + + // Returns true iff the status indicates a Corruption error. + bool IsCorruption() const { return code() == kCorruption; } + + // Returns true iff the status indicates an IOError. + bool IsIOError() const { return code() == kIOError; } + + // Return a string representation of this status suitable for printing. + // Returns the string "OK" for success. + std::string ToString() const; + + private: + // OK status has a NULL state_. Otherwise, state_ is a new[] array + // of the following form: + // state_[0..3] == length of message + // state_[4] == code + // state_[5..] == message + const char* state_; + + enum Code { + kOk = 0, + kNotFound = 1, + kCorruption = 2, + kNotSupported = 3, + kInvalidArgument = 4, + kIOError = 5 + }; + + Code code() const { + return (state_ == NULL) ? kOk : static_cast(state_[4]); + } + + Status(Code code, const Slice& msg, const Slice& msg2); + static const char* CopyState(const char* s); +}; + +inline Status::Status(const Status& s) { + state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_); +} +inline void Status::operator=(const Status& s) { + // The following condition catches both aliasing (when this == &s), + // and the common case where both s and *this are ok. + if (state_ != s.state_) { + delete[] state_; + state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_); + } +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_STATUS_H_ diff --git a/src/leveldb-lin/include/leveldb/table.h b/src/leveldb-lin/include/leveldb/table.h new file mode 100644 index 0000000..a9746c3 --- /dev/null +++ b/src/leveldb-lin/include/leveldb/table.h @@ -0,0 +1,85 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_TABLE_H_ +#define STORAGE_LEVELDB_INCLUDE_TABLE_H_ + +#include +#include "leveldb/iterator.h" + +namespace leveldb { + +class Block; +class BlockHandle; +class Footer; +struct Options; +class RandomAccessFile; +struct ReadOptions; +class TableCache; + +// A Table is a sorted map from strings to strings. Tables are +// immutable and persistent. A Table may be safely accessed from +// multiple threads without external synchronization. +class Table { + public: + // Attempt to open the table that is stored in bytes [0..file_size) + // of "file", and read the metadata entries necessary to allow + // retrieving data from the table. + // + // If successful, returns ok and sets "*table" to the newly opened + // table. The client should delete "*table" when no longer needed. + // If there was an error while initializing the table, sets "*table" + // to NULL and returns a non-ok status. Does not take ownership of + // "*source", but the client must ensure that "source" remains live + // for the duration of the returned table's lifetime. + // + // *file must remain live while this Table is in use. + static Status Open(const Options& options, + RandomAccessFile* file, + uint64_t file_size, + Table** table); + + ~Table(); + + // Returns a new iterator over the table contents. + // The result of NewIterator() is initially invalid (caller must + // call one of the Seek methods on the iterator before using it). + Iterator* NewIterator(const ReadOptions&) const; + + // Given a key, return an approximate byte offset in the file where + // the data for that key begins (or would begin if the key were + // present in the file). The returned value is in terms of file + // bytes, and so includes effects like compression of the underlying data. + // E.g., the approximate offset of the last key in the table will + // be close to the file length. + uint64_t ApproximateOffsetOf(const Slice& key) const; + + private: + struct Rep; + Rep* rep_; + + explicit Table(Rep* rep) { rep_ = rep; } + static Iterator* BlockReader(void*, const ReadOptions&, const Slice&); + + // Calls (*handle_result)(arg, ...) with the entry found after a call + // to Seek(key). May not make such a call if filter policy says + // that key is not present. + friend class TableCache; + Status InternalGet( + const ReadOptions&, const Slice& key, + void* arg, + void (*handle_result)(void* arg, const Slice& k, const Slice& v)); + + + void ReadMeta(const Footer& footer); + void ReadFilter(const Slice& filter_handle_value); + + // No copying allowed + Table(const Table&); + void operator=(const Table&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_TABLE_H_ diff --git a/src/leveldb-lin/include/leveldb/table_builder.h b/src/leveldb-lin/include/leveldb/table_builder.h new file mode 100644 index 0000000..5fd1dc7 --- /dev/null +++ b/src/leveldb-lin/include/leveldb/table_builder.h @@ -0,0 +1,92 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// TableBuilder provides the interface used to build a Table +// (an immutable and sorted map from keys to values). +// +// Multiple threads can invoke const methods on a TableBuilder without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same TableBuilder must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_ +#define STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_ + +#include +#include "leveldb/options.h" +#include "leveldb/status.h" + +namespace leveldb { + +class BlockBuilder; +class BlockHandle; +class WritableFile; + +class TableBuilder { + public: + // Create a builder that will store the contents of the table it is + // building in *file. Does not close the file. It is up to the + // caller to close the file after calling Finish(). + TableBuilder(const Options& options, WritableFile* file); + + // REQUIRES: Either Finish() or Abandon() has been called. + ~TableBuilder(); + + // Change the options used by this builder. Note: only some of the + // option fields can be changed after construction. If a field is + // not allowed to change dynamically and its value in the structure + // passed to the constructor is different from its value in the + // structure passed to this method, this method will return an error + // without changing any fields. + Status ChangeOptions(const Options& options); + + // Add key,value to the table being constructed. + // REQUIRES: key is after any previously added key according to comparator. + // REQUIRES: Finish(), Abandon() have not been called + void Add(const Slice& key, const Slice& value); + + // Advanced operation: flush any buffered key/value pairs to file. + // Can be used to ensure that two adjacent entries never live in + // the same data block. Most clients should not need to use this method. + // REQUIRES: Finish(), Abandon() have not been called + void Flush(); + + // Return non-ok iff some error has been detected. + Status status() const; + + // Finish building the table. Stops using the file passed to the + // constructor after this function returns. + // REQUIRES: Finish(), Abandon() have not been called + Status Finish(); + + // Indicate that the contents of this builder should be abandoned. Stops + // using the file passed to the constructor after this function returns. + // If the caller is not going to call Finish(), it must call Abandon() + // before destroying this builder. + // REQUIRES: Finish(), Abandon() have not been called + void Abandon(); + + // Number of calls to Add() so far. + uint64_t NumEntries() const; + + // Size of the file generated so far. If invoked after a successful + // Finish() call, returns the size of the final generated file. + uint64_t FileSize() const; + + private: + bool ok() const { return status().ok(); } + void WriteBlock(BlockBuilder* block, BlockHandle* handle); + void WriteRawBlock(const Slice& data, CompressionType, BlockHandle* handle); + + struct Rep; + Rep* rep_; + + // No copying allowed + TableBuilder(const TableBuilder&); + void operator=(const TableBuilder&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_ diff --git a/src/leveldb-lin/include/leveldb/write_batch.h b/src/leveldb-lin/include/leveldb/write_batch.h new file mode 100644 index 0000000..ee9aab6 --- /dev/null +++ b/src/leveldb-lin/include/leveldb/write_batch.h @@ -0,0 +1,64 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// WriteBatch holds a collection of updates to apply atomically to a DB. +// +// The updates are applied in the order in which they are added +// to the WriteBatch. For example, the value of "key" will be "v3" +// after the following batch is written: +// +// batch.Put("key", "v1"); +// batch.Delete("key"); +// batch.Put("key", "v2"); +// batch.Put("key", "v3"); +// +// Multiple threads can invoke const methods on a WriteBatch without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same WriteBatch must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_WRITE_BATCH_H_ +#define STORAGE_LEVELDB_INCLUDE_WRITE_BATCH_H_ + +#include +#include "leveldb/status.h" + +namespace leveldb { + +class Slice; + +class WriteBatch { + public: + WriteBatch(); + ~WriteBatch(); + + // Store the mapping "key->value" in the database. + void Put(const Slice& key, const Slice& value); + + // If the database contains a mapping for "key", erase it. Else do nothing. + void Delete(const Slice& key); + + // Clear all updates buffered in this batch. + void Clear(); + + // Support for iterating over the contents of a batch. + class Handler { + public: + virtual ~Handler(); + virtual void Put(const Slice& key, const Slice& value) = 0; + virtual void Delete(const Slice& key) = 0; + }; + Status Iterate(Handler* handler) const; + + private: + friend class WriteBatchInternal; + + std::string rep_; // See comment in write_batch.cc for the format of rep_ + + // Intentionally copyable +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_WRITE_BATCH_H_ diff --git a/src/leveldb-lin/leveldb/.gitignore b/src/leveldb-lin/leveldb/.gitignore new file mode 100644 index 0000000..0630251 --- /dev/null +++ b/src/leveldb-lin/leveldb/.gitignore @@ -0,0 +1,9 @@ +build_config.mk +*.a +*.o +*.dylib* +*.so +*.so.* +*_test +db_bench +leveldbutil diff --git a/src/leveldb-lin/leveldb/AUTHORS b/src/leveldb-lin/leveldb/AUTHORS new file mode 100644 index 0000000..2439d7a --- /dev/null +++ b/src/leveldb-lin/leveldb/AUTHORS @@ -0,0 +1,12 @@ +# Names should be added to this file like so: +# Name or Organization + +Google Inc. + +# Initial version authors: +Jeffrey Dean +Sanjay Ghemawat + +# Partial list of contributors: +Kevin Regan +Johan Bilien diff --git a/src/leveldb-lin/leveldb/build_detect_platform b/src/leveldb-lin/leveldb/build_detect_platform new file mode 100644 index 0000000..6e59c6f --- /dev/null +++ b/src/leveldb-lin/leveldb/build_detect_platform @@ -0,0 +1,221 @@ +#!/bin/sh +# +# Detects OS we're compiling on and outputs a file specified by the first +# argument, which in turn gets read while processing Makefile. +# +# The output will set the following variables: +# CC C Compiler path +# CXX C++ Compiler path +# PLATFORM_LDFLAGS Linker flags +# PLATFORM_LIBS Libraries flags +# PLATFORM_SHARED_EXT Extension for shared libraries +# PLATFORM_SHARED_LDFLAGS Flags for building shared library +# This flag is embedded just before the name +# of the shared library without intervening spaces +# PLATFORM_SHARED_CFLAGS Flags for compiling objects for shared library +# PLATFORM_CCFLAGS C compiler flags +# PLATFORM_CXXFLAGS C++ compiler flags. Will contain: +# PLATFORM_SHARED_VERSIONED Set to 'true' if platform supports versioned +# shared libraries, empty otherwise. +# +# The PLATFORM_CCFLAGS and PLATFORM_CXXFLAGS might include the following: +# +# -DLEVELDB_CSTDATOMIC_PRESENT if is present +# -DLEVELDB_PLATFORM_POSIX for Posix-based platforms +# -DSNAPPY if the Snappy library is present +# + +OUTPUT=$1 +PREFIX=$2 +if test -z "$OUTPUT" || test -z "$PREFIX"; then + echo "usage: $0 " >&2 + exit 1 +fi + +# Delete existing output, if it exists +rm -f $OUTPUT +touch $OUTPUT + +if test -z "$CC"; then + CC=cc +fi + +if test -z "$CXX"; then + CXX=g++ +fi + +if test -z "$TMPDIR"; then + TMPDIR=/tmp +fi + +# Detect OS +if test -z "$TARGET_OS"; then + TARGET_OS=`uname -s` +fi + +COMMON_FLAGS= +CROSS_COMPILE= +PLATFORM_CCFLAGS= +PLATFORM_CXXFLAGS= +PLATFORM_LDFLAGS= +PLATFORM_LIBS= +PLATFORM_SHARED_EXT="so" +PLATFORM_SHARED_LDFLAGS="-shared -Wl,-soname -Wl," +PLATFORM_SHARED_CFLAGS="-fPIC" +PLATFORM_SHARED_VERSIONED=true + +MEMCMP_FLAG= +if [ "$CXX" = "g++" ]; then + # Use libc's memcmp instead of GCC's memcmp. This results in ~40% + # performance improvement on readrandom under gcc 4.4.3 on Linux/x86. + MEMCMP_FLAG="-fno-builtin-memcmp" +fi + +case "$TARGET_OS" in + Darwin) + PLATFORM=OS_MACOSX + COMMON_FLAGS="$MEMCMP_FLAG -DOS_MACOSX" + PLATFORM_SHARED_EXT=dylib + [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd` + PLATFORM_SHARED_LDFLAGS="-dynamiclib -install_name $INSTALL_PATH/" + PORT_FILE=port/port_posix.cc + ;; + Linux) + PLATFORM=OS_LINUX + COMMON_FLAGS="$MEMCMP_FLAG -pthread -DOS_LINUX" + PLATFORM_LDFLAGS="-pthread" + PORT_FILE=port/port_posix.cc + ;; + SunOS) + PLATFORM=OS_SOLARIS + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_SOLARIS" + PLATFORM_LIBS="-lpthread -lrt" + PORT_FILE=port/port_posix.cc + ;; + FreeBSD) + PLATFORM=OS_FREEBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_FREEBSD" + PLATFORM_LIBS="-lpthread" + PORT_FILE=port/port_posix.cc + ;; + NetBSD) + PLATFORM=OS_NETBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_NETBSD" + PLATFORM_LIBS="-lpthread -lgcc_s" + PORT_FILE=port/port_posix.cc + ;; + OpenBSD) + PLATFORM=OS_OPENBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_OPENBSD" + PLATFORM_LDFLAGS="-pthread" + PORT_FILE=port/port_posix.cc + ;; + DragonFly) + PLATFORM=OS_DRAGONFLYBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_DRAGONFLYBSD" + PLATFORM_LIBS="-lpthread" + PORT_FILE=port/port_posix.cc + ;; + OS_ANDROID_CROSSCOMPILE) + PLATFORM=OS_ANDROID + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_ANDROID -DLEVELDB_PLATFORM_POSIX" + PLATFORM_LDFLAGS="" # All pthread features are in the Android C library + PORT_FILE=port/port_posix.cc + CROSS_COMPILE=true + ;; + HP-UX) + PLATFORM=OS_HPUX + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_HPUX" + PLATFORM_LDFLAGS="-pthread" + PORT_FILE=port/port_posix.cc + # man ld: +h internal_name + PLATFORM_SHARED_LDFLAGS="-shared -Wl,+h -Wl," + ;; + IOS) + PLATFORM=IOS + COMMON_FLAGS="$MEMCMP_FLAG -DOS_MACOSX" + [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd` + PORT_FILE=port/port_posix.cc + PLATFORM_SHARED_EXT= + PLATFORM_SHARED_LDFLAGS= + PLATFORM_SHARED_CFLAGS= + PLATFORM_SHARED_VERSIONED= + ;; + *) + echo "Unknown platform!" >&2 + exit 1 +esac + +# We want to make a list of all cc files within util, db, table, and helpers +# except for the test and benchmark files. By default, find will output a list +# of all files matching either rule, so we need to append -print to make the +# prune take effect. +DIRS="$PREFIX/db $PREFIX/util $PREFIX/table" + +set -f # temporarily disable globbing so that our patterns aren't expanded +PRUNE_TEST="-name *test*.cc -prune" +PRUNE_BENCH="-name *_bench.cc -prune" +PRUNE_TOOL="-name leveldb_main.cc -prune" +PORTABLE_FILES=`find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o $PRUNE_TOOL -o -name '*.cc' -print | sort | sed "s,^$PREFIX/,," | tr "\n" " "` + +set +f # re-enable globbing + +# The sources consist of the portable files, plus the platform-specific port +# file. +echo "SOURCES=$PORTABLE_FILES $PORT_FILE" >> $OUTPUT +echo "MEMENV_SOURCES=helpers/memenv/memenv.cc" >> $OUTPUT + +if [ "$CROSS_COMPILE" = "true" ]; then + # Cross-compiling; do not try any compilation tests. + true +else + CXXOUTPUT="${TMPDIR}/leveldb_build_detect_platform-cxx.$$" + + # If -std=c++0x works, use . Otherwise use port_posix.h. + $CXX $CXXFLAGS -std=c++0x -x c++ - -o $CXXOUTPUT 2>/dev/null < + int main() {} +EOF + if [ "$?" = 0 ]; then + COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX -DLEVELDB_CSTDATOMIC_PRESENT" + PLATFORM_CXXFLAGS="-std=c++0x" + else + COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX" + fi + + # Test whether Snappy library is installed + # http://code.google.com/p/snappy/ + $CXX $CXXFLAGS -x c++ - -o $CXXOUTPUT 2>/dev/null < + int main() {} +EOF + if [ "$?" = 0 ]; then + COMMON_FLAGS="$COMMON_FLAGS -DSNAPPY" + PLATFORM_LIBS="$PLATFORM_LIBS -lsnappy" + fi + + # Test whether tcmalloc is available + $CXX $CXXFLAGS -x c++ - -o $CXXOUTPUT -ltcmalloc 2>/dev/null </dev/null +fi + +PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS" +PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS" + +echo "CC=$CC" >> $OUTPUT +echo "CXX=$CXX" >> $OUTPUT +echo "PLATFORM=$PLATFORM" >> $OUTPUT +echo "PLATFORM_LDFLAGS=$PLATFORM_LDFLAGS" >> $OUTPUT +echo "PLATFORM_LIBS=$PLATFORM_LIBS" >> $OUTPUT +echo "PLATFORM_CCFLAGS=$PLATFORM_CCFLAGS" >> $OUTPUT +echo "PLATFORM_CXXFLAGS=$PLATFORM_CXXFLAGS" >> $OUTPUT +echo "PLATFORM_SHARED_CFLAGS=$PLATFORM_SHARED_CFLAGS" >> $OUTPUT +echo "PLATFORM_SHARED_EXT=$PLATFORM_SHARED_EXT" >> $OUTPUT +echo "PLATFORM_SHARED_LDFLAGS=$PLATFORM_SHARED_LDFLAGS" >> $OUTPUT +echo "PLATFORM_SHARED_VERSIONED=$PLATFORM_SHARED_VERSIONED" >> $OUTPUT diff --git a/src/leveldb-lin/leveldb/db/autocompact_test.cc b/src/leveldb-lin/leveldb/db/autocompact_test.cc new file mode 100644 index 0000000..d20a236 --- /dev/null +++ b/src/leveldb-lin/leveldb/db/autocompact_test.cc @@ -0,0 +1,118 @@ +// Copyright (c) 2013 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" +#include "db/db_impl.h" +#include "leveldb/cache.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +class AutoCompactTest { + public: + std::string dbname_; + Cache* tiny_cache_; + Options options_; + DB* db_; + + AutoCompactTest() { + dbname_ = test::TmpDir() + "/autocompact_test"; + tiny_cache_ = NewLRUCache(100); + options_.block_cache = tiny_cache_; + DestroyDB(dbname_, options_); + options_.create_if_missing = true; + options_.compression = kNoCompression; + ASSERT_OK(DB::Open(options_, dbname_, &db_)); + } + + ~AutoCompactTest() { + delete db_; + DestroyDB(dbname_, Options()); + delete tiny_cache_; + } + + std::string Key(int i) { + char buf[100]; + snprintf(buf, sizeof(buf), "key%06d", i); + return std::string(buf); + } + + uint64_t Size(const Slice& start, const Slice& limit) { + Range r(start, limit); + uint64_t size; + db_->GetApproximateSizes(&r, 1, &size); + return size; + } + + void DoReads(int n); +}; + +static const int kValueSize = 200 * 1024; +static const int kTotalSize = 100 * 1024 * 1024; +static const int kCount = kTotalSize / kValueSize; + +// Read through the first n keys repeatedly and check that they get +// compacted (verified by checking the size of the key space). +void AutoCompactTest::DoReads(int n) { + std::string value(kValueSize, 'x'); + DBImpl* dbi = reinterpret_cast(db_); + + // Fill database + for (int i = 0; i < kCount; i++) { + ASSERT_OK(db_->Put(WriteOptions(), Key(i), value)); + } + ASSERT_OK(dbi->TEST_CompactMemTable()); + + // Delete everything + for (int i = 0; i < kCount; i++) { + ASSERT_OK(db_->Delete(WriteOptions(), Key(i))); + } + ASSERT_OK(dbi->TEST_CompactMemTable()); + + // Get initial measurement of the space we will be reading. + const int64_t initial_size = Size(Key(0), Key(n)); + const int64_t initial_other_size = Size(Key(n), Key(kCount)); + + // Read until size drops significantly. + std::string limit_key = Key(n); + for (int read = 0; true; read++) { + ASSERT_LT(read, 100) << "Taking too long to compact"; + Iterator* iter = db_->NewIterator(ReadOptions()); + for (iter->SeekToFirst(); + iter->Valid() && iter->key().ToString() < limit_key; + iter->Next()) { + // Drop data + } + delete iter; + // Wait a little bit to allow any triggered compactions to complete. + Env::Default()->SleepForMicroseconds(1000000); + uint64_t size = Size(Key(0), Key(n)); + fprintf(stderr, "iter %3d => %7.3f MB [other %7.3f MB]\n", + read+1, size/1048576.0, Size(Key(n), Key(kCount))/1048576.0); + if (size <= initial_size/10) { + break; + } + } + + // Verify that the size of the key space not touched by the reads + // is pretty much unchanged. + const int64_t final_other_size = Size(Key(n), Key(kCount)); + ASSERT_LE(final_other_size, initial_other_size + 1048576); + ASSERT_GE(final_other_size, initial_other_size/5 - 1048576); +} + +TEST(AutoCompactTest, ReadAll) { + DoReads(kCount); +} + +TEST(AutoCompactTest, ReadHalf) { + DoReads(kCount/2); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/leveldb/db/builder.cc b/src/leveldb-lin/leveldb/db/builder.cc new file mode 100644 index 0000000..f419882 --- /dev/null +++ b/src/leveldb-lin/leveldb/db/builder.cc @@ -0,0 +1,88 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/builder.h" + +#include "db/filename.h" +#include "db/dbformat.h" +#include "db/table_cache.h" +#include "db/version_edit.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" + +namespace leveldb { + +Status BuildTable(const std::string& dbname, + Env* env, + const Options& options, + TableCache* table_cache, + Iterator* iter, + FileMetaData* meta) { + Status s; + meta->file_size = 0; + iter->SeekToFirst(); + + std::string fname = TableFileName(dbname, meta->number); + if (iter->Valid()) { + WritableFile* file; + s = env->NewWritableFile(fname, &file); + if (!s.ok()) { + return s; + } + + TableBuilder* builder = new TableBuilder(options, file); + meta->smallest.DecodeFrom(iter->key()); + for (; iter->Valid(); iter->Next()) { + Slice key = iter->key(); + meta->largest.DecodeFrom(key); + builder->Add(key, iter->value()); + } + + // Finish and check for builder errors + if (s.ok()) { + s = builder->Finish(); + if (s.ok()) { + meta->file_size = builder->FileSize(); + assert(meta->file_size > 0); + } + } else { + builder->Abandon(); + } + delete builder; + + // Finish and check for file errors + if (s.ok()) { + s = file->Sync(); + } + if (s.ok()) { + s = file->Close(); + } + delete file; + file = NULL; + + if (s.ok()) { + // Verify that the table is usable + Iterator* it = table_cache->NewIterator(ReadOptions(), + meta->number, + meta->file_size); + s = it->status(); + delete it; + } + } + + // Check for input iterator errors + if (!iter->status().ok()) { + s = iter->status(); + } + + if (s.ok() && meta->file_size > 0) { + // Keep it + } else { + env->DeleteFile(fname); + } + return s; +} + +} // namespace leveldb diff --git a/src/leveldb-lin/leveldb/db/builder.h b/src/leveldb-lin/leveldb/db/builder.h new file mode 100644 index 0000000..62431fc --- /dev/null +++ b/src/leveldb-lin/leveldb/db/builder.h @@ -0,0 +1,34 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_BUILDER_H_ +#define STORAGE_LEVELDB_DB_BUILDER_H_ + +#include "leveldb/status.h" + +namespace leveldb { + +struct Options; +struct FileMetaData; + +class Env; +class Iterator; +class TableCache; +class VersionEdit; + +// Build a Table file from the contents of *iter. The generated file +// will be named according to meta->number. On success, the rest of +// *meta will be filled with metadata about the generated table. +// If no data is present in *iter, meta->file_size will be set to +// zero, and no Table file will be produced. +extern Status BuildTable(const std::string& dbname, + Env* env, + const Options& options, + TableCache* table_cache, + Iterator* iter, + FileMetaData* meta); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_BUILDER_H_ diff --git a/src/leveldb-lin/leveldb/db/c.cc b/src/leveldb-lin/leveldb/db/c.cc new file mode 100644 index 0000000..08ff0ad --- /dev/null +++ b/src/leveldb-lin/leveldb/db/c.cc @@ -0,0 +1,595 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/c.h" + +#include +#include +#include "leveldb/cache.h" +#include "leveldb/comparator.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/filter_policy.h" +#include "leveldb/iterator.h" +#include "leveldb/options.h" +#include "leveldb/status.h" +#include "leveldb/write_batch.h" + +using leveldb::Cache; +using leveldb::Comparator; +using leveldb::CompressionType; +using leveldb::DB; +using leveldb::Env; +using leveldb::FileLock; +using leveldb::FilterPolicy; +using leveldb::Iterator; +using leveldb::kMajorVersion; +using leveldb::kMinorVersion; +using leveldb::Logger; +using leveldb::NewBloomFilterPolicy; +using leveldb::NewLRUCache; +using leveldb::Options; +using leveldb::RandomAccessFile; +using leveldb::Range; +using leveldb::ReadOptions; +using leveldb::SequentialFile; +using leveldb::Slice; +using leveldb::Snapshot; +using leveldb::Status; +using leveldb::WritableFile; +using leveldb::WriteBatch; +using leveldb::WriteOptions; + +extern "C" { + +struct leveldb_t { DB* rep; }; +struct leveldb_iterator_t { Iterator* rep; }; +struct leveldb_writebatch_t { WriteBatch rep; }; +struct leveldb_snapshot_t { const Snapshot* rep; }; +struct leveldb_readoptions_t { ReadOptions rep; }; +struct leveldb_writeoptions_t { WriteOptions rep; }; +struct leveldb_options_t { Options rep; }; +struct leveldb_cache_t { Cache* rep; }; +struct leveldb_seqfile_t { SequentialFile* rep; }; +struct leveldb_randomfile_t { RandomAccessFile* rep; }; +struct leveldb_writablefile_t { WritableFile* rep; }; +struct leveldb_logger_t { Logger* rep; }; +struct leveldb_filelock_t { FileLock* rep; }; + +struct leveldb_comparator_t : public Comparator { + void* state_; + void (*destructor_)(void*); + int (*compare_)( + void*, + const char* a, size_t alen, + const char* b, size_t blen); + const char* (*name_)(void*); + + virtual ~leveldb_comparator_t() { + (*destructor_)(state_); + } + + virtual int Compare(const Slice& a, const Slice& b) const { + return (*compare_)(state_, a.data(), a.size(), b.data(), b.size()); + } + + virtual const char* Name() const { + return (*name_)(state_); + } + + // No-ops since the C binding does not support key shortening methods. + virtual void FindShortestSeparator(std::string*, const Slice&) const { } + virtual void FindShortSuccessor(std::string* key) const { } +}; + +struct leveldb_filterpolicy_t : public FilterPolicy { + void* state_; + void (*destructor_)(void*); + const char* (*name_)(void*); + char* (*create_)( + void*, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length); + unsigned char (*key_match_)( + void*, + const char* key, size_t length, + const char* filter, size_t filter_length); + + virtual ~leveldb_filterpolicy_t() { + (*destructor_)(state_); + } + + virtual const char* Name() const { + return (*name_)(state_); + } + + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + std::vector key_pointers(n); + std::vector key_sizes(n); + for (int i = 0; i < n; i++) { + key_pointers[i] = keys[i].data(); + key_sizes[i] = keys[i].size(); + } + size_t len; + char* filter = (*create_)(state_, &key_pointers[0], &key_sizes[0], n, &len); + dst->append(filter, len); + free(filter); + } + + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const { + return (*key_match_)(state_, key.data(), key.size(), + filter.data(), filter.size()); + } +}; + +struct leveldb_env_t { + Env* rep; + bool is_default; +}; + +static bool SaveError(char** errptr, const Status& s) { + assert(errptr != NULL); + if (s.ok()) { + return false; + } else if (*errptr == NULL) { + *errptr = strdup(s.ToString().c_str()); + } else { + // TODO(sanjay): Merge with existing error? + free(*errptr); + *errptr = strdup(s.ToString().c_str()); + } + return true; +} + +static char* CopyString(const std::string& str) { + char* result = reinterpret_cast(malloc(sizeof(char) * str.size())); + memcpy(result, str.data(), sizeof(char) * str.size()); + return result; +} + +leveldb_t* leveldb_open( + const leveldb_options_t* options, + const char* name, + char** errptr) { + DB* db; + if (SaveError(errptr, DB::Open(options->rep, std::string(name), &db))) { + return NULL; + } + leveldb_t* result = new leveldb_t; + result->rep = db; + return result; +} + +void leveldb_close(leveldb_t* db) { + delete db->rep; + delete db; +} + +void leveldb_put( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + const char* val, size_t vallen, + char** errptr) { + SaveError(errptr, + db->rep->Put(options->rep, Slice(key, keylen), Slice(val, vallen))); +} + +void leveldb_delete( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + char** errptr) { + SaveError(errptr, db->rep->Delete(options->rep, Slice(key, keylen))); +} + + +void leveldb_write( + leveldb_t* db, + const leveldb_writeoptions_t* options, + leveldb_writebatch_t* batch, + char** errptr) { + SaveError(errptr, db->rep->Write(options->rep, &batch->rep)); +} + +char* leveldb_get( + leveldb_t* db, + const leveldb_readoptions_t* options, + const char* key, size_t keylen, + size_t* vallen, + char** errptr) { + char* result = NULL; + std::string tmp; + Status s = db->rep->Get(options->rep, Slice(key, keylen), &tmp); + if (s.ok()) { + *vallen = tmp.size(); + result = CopyString(tmp); + } else { + *vallen = 0; + if (!s.IsNotFound()) { + SaveError(errptr, s); + } + } + return result; +} + +leveldb_iterator_t* leveldb_create_iterator( + leveldb_t* db, + const leveldb_readoptions_t* options) { + leveldb_iterator_t* result = new leveldb_iterator_t; + result->rep = db->rep->NewIterator(options->rep); + return result; +} + +const leveldb_snapshot_t* leveldb_create_snapshot( + leveldb_t* db) { + leveldb_snapshot_t* result = new leveldb_snapshot_t; + result->rep = db->rep->GetSnapshot(); + return result; +} + +void leveldb_release_snapshot( + leveldb_t* db, + const leveldb_snapshot_t* snapshot) { + db->rep->ReleaseSnapshot(snapshot->rep); + delete snapshot; +} + +char* leveldb_property_value( + leveldb_t* db, + const char* propname) { + std::string tmp; + if (db->rep->GetProperty(Slice(propname), &tmp)) { + // We use strdup() since we expect human readable output. + return strdup(tmp.c_str()); + } else { + return NULL; + } +} + +void leveldb_approximate_sizes( + leveldb_t* db, + int num_ranges, + const char* const* range_start_key, const size_t* range_start_key_len, + const char* const* range_limit_key, const size_t* range_limit_key_len, + uint64_t* sizes) { + Range* ranges = new Range[num_ranges]; + for (int i = 0; i < num_ranges; i++) { + ranges[i].start = Slice(range_start_key[i], range_start_key_len[i]); + ranges[i].limit = Slice(range_limit_key[i], range_limit_key_len[i]); + } + db->rep->GetApproximateSizes(ranges, num_ranges, sizes); + delete[] ranges; +} + +void leveldb_compact_range( + leveldb_t* db, + const char* start_key, size_t start_key_len, + const char* limit_key, size_t limit_key_len) { + Slice a, b; + db->rep->CompactRange( + // Pass NULL Slice if corresponding "const char*" is NULL + (start_key ? (a = Slice(start_key, start_key_len), &a) : NULL), + (limit_key ? (b = Slice(limit_key, limit_key_len), &b) : NULL)); +} + +void leveldb_destroy_db( + const leveldb_options_t* options, + const char* name, + char** errptr) { + SaveError(errptr, DestroyDB(name, options->rep)); +} + +void leveldb_repair_db( + const leveldb_options_t* options, + const char* name, + char** errptr) { + SaveError(errptr, RepairDB(name, options->rep)); +} + +void leveldb_iter_destroy(leveldb_iterator_t* iter) { + delete iter->rep; + delete iter; +} + +unsigned char leveldb_iter_valid(const leveldb_iterator_t* iter) { + return iter->rep->Valid(); +} + +void leveldb_iter_seek_to_first(leveldb_iterator_t* iter) { + iter->rep->SeekToFirst(); +} + +void leveldb_iter_seek_to_last(leveldb_iterator_t* iter) { + iter->rep->SeekToLast(); +} + +void leveldb_iter_seek(leveldb_iterator_t* iter, const char* k, size_t klen) { + iter->rep->Seek(Slice(k, klen)); +} + +void leveldb_iter_next(leveldb_iterator_t* iter) { + iter->rep->Next(); +} + +void leveldb_iter_prev(leveldb_iterator_t* iter) { + iter->rep->Prev(); +} + +const char* leveldb_iter_key(const leveldb_iterator_t* iter, size_t* klen) { + Slice s = iter->rep->key(); + *klen = s.size(); + return s.data(); +} + +const char* leveldb_iter_value(const leveldb_iterator_t* iter, size_t* vlen) { + Slice s = iter->rep->value(); + *vlen = s.size(); + return s.data(); +} + +void leveldb_iter_get_error(const leveldb_iterator_t* iter, char** errptr) { + SaveError(errptr, iter->rep->status()); +} + +leveldb_writebatch_t* leveldb_writebatch_create() { + return new leveldb_writebatch_t; +} + +void leveldb_writebatch_destroy(leveldb_writebatch_t* b) { + delete b; +} + +void leveldb_writebatch_clear(leveldb_writebatch_t* b) { + b->rep.Clear(); +} + +void leveldb_writebatch_put( + leveldb_writebatch_t* b, + const char* key, size_t klen, + const char* val, size_t vlen) { + b->rep.Put(Slice(key, klen), Slice(val, vlen)); +} + +void leveldb_writebatch_delete( + leveldb_writebatch_t* b, + const char* key, size_t klen) { + b->rep.Delete(Slice(key, klen)); +} + +void leveldb_writebatch_iterate( + leveldb_writebatch_t* b, + void* state, + void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen), + void (*deleted)(void*, const char* k, size_t klen)) { + class H : public WriteBatch::Handler { + public: + void* state_; + void (*put_)(void*, const char* k, size_t klen, const char* v, size_t vlen); + void (*deleted_)(void*, const char* k, size_t klen); + virtual void Put(const Slice& key, const Slice& value) { + (*put_)(state_, key.data(), key.size(), value.data(), value.size()); + } + virtual void Delete(const Slice& key) { + (*deleted_)(state_, key.data(), key.size()); + } + }; + H handler; + handler.state_ = state; + handler.put_ = put; + handler.deleted_ = deleted; + b->rep.Iterate(&handler); +} + +leveldb_options_t* leveldb_options_create() { + return new leveldb_options_t; +} + +void leveldb_options_destroy(leveldb_options_t* options) { + delete options; +} + +void leveldb_options_set_comparator( + leveldb_options_t* opt, + leveldb_comparator_t* cmp) { + opt->rep.comparator = cmp; +} + +void leveldb_options_set_filter_policy( + leveldb_options_t* opt, + leveldb_filterpolicy_t* policy) { + opt->rep.filter_policy = policy; +} + +void leveldb_options_set_create_if_missing( + leveldb_options_t* opt, unsigned char v) { + opt->rep.create_if_missing = v; +} + +void leveldb_options_set_error_if_exists( + leveldb_options_t* opt, unsigned char v) { + opt->rep.error_if_exists = v; +} + +void leveldb_options_set_paranoid_checks( + leveldb_options_t* opt, unsigned char v) { + opt->rep.paranoid_checks = v; +} + +void leveldb_options_set_env(leveldb_options_t* opt, leveldb_env_t* env) { + opt->rep.env = (env ? env->rep : NULL); +} + +void leveldb_options_set_info_log(leveldb_options_t* opt, leveldb_logger_t* l) { + opt->rep.info_log = (l ? l->rep : NULL); +} + +void leveldb_options_set_write_buffer_size(leveldb_options_t* opt, size_t s) { + opt->rep.write_buffer_size = s; +} + +void leveldb_options_set_max_open_files(leveldb_options_t* opt, int n) { + opt->rep.max_open_files = n; +} + +void leveldb_options_set_cache(leveldb_options_t* opt, leveldb_cache_t* c) { + opt->rep.block_cache = c->rep; +} + +void leveldb_options_set_block_size(leveldb_options_t* opt, size_t s) { + opt->rep.block_size = s; +} + +void leveldb_options_set_block_restart_interval(leveldb_options_t* opt, int n) { + opt->rep.block_restart_interval = n; +} + +void leveldb_options_set_compression(leveldb_options_t* opt, int t) { + opt->rep.compression = static_cast(t); +} + +leveldb_comparator_t* leveldb_comparator_create( + void* state, + void (*destructor)(void*), + int (*compare)( + void*, + const char* a, size_t alen, + const char* b, size_t blen), + const char* (*name)(void*)) { + leveldb_comparator_t* result = new leveldb_comparator_t; + result->state_ = state; + result->destructor_ = destructor; + result->compare_ = compare; + result->name_ = name; + return result; +} + +void leveldb_comparator_destroy(leveldb_comparator_t* cmp) { + delete cmp; +} + +leveldb_filterpolicy_t* leveldb_filterpolicy_create( + void* state, + void (*destructor)(void*), + char* (*create_filter)( + void*, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length), + unsigned char (*key_may_match)( + void*, + const char* key, size_t length, + const char* filter, size_t filter_length), + const char* (*name)(void*)) { + leveldb_filterpolicy_t* result = new leveldb_filterpolicy_t; + result->state_ = state; + result->destructor_ = destructor; + result->create_ = create_filter; + result->key_match_ = key_may_match; + result->name_ = name; + return result; +} + +void leveldb_filterpolicy_destroy(leveldb_filterpolicy_t* filter) { + delete filter; +} + +leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom(int bits_per_key) { + // Make a leveldb_filterpolicy_t, but override all of its methods so + // they delegate to a NewBloomFilterPolicy() instead of user + // supplied C functions. + struct Wrapper : public leveldb_filterpolicy_t { + const FilterPolicy* rep_; + ~Wrapper() { delete rep_; } + const char* Name() const { return rep_->Name(); } + void CreateFilter(const Slice* keys, int n, std::string* dst) const { + return rep_->CreateFilter(keys, n, dst); + } + bool KeyMayMatch(const Slice& key, const Slice& filter) const { + return rep_->KeyMayMatch(key, filter); + } + static void DoNothing(void*) { } + }; + Wrapper* wrapper = new Wrapper; + wrapper->rep_ = NewBloomFilterPolicy(bits_per_key); + wrapper->state_ = NULL; + wrapper->destructor_ = &Wrapper::DoNothing; + return wrapper; +} + +leveldb_readoptions_t* leveldb_readoptions_create() { + return new leveldb_readoptions_t; +} + +void leveldb_readoptions_destroy(leveldb_readoptions_t* opt) { + delete opt; +} + +void leveldb_readoptions_set_verify_checksums( + leveldb_readoptions_t* opt, + unsigned char v) { + opt->rep.verify_checksums = v; +} + +void leveldb_readoptions_set_fill_cache( + leveldb_readoptions_t* opt, unsigned char v) { + opt->rep.fill_cache = v; +} + +void leveldb_readoptions_set_snapshot( + leveldb_readoptions_t* opt, + const leveldb_snapshot_t* snap) { + opt->rep.snapshot = (snap ? snap->rep : NULL); +} + +leveldb_writeoptions_t* leveldb_writeoptions_create() { + return new leveldb_writeoptions_t; +} + +void leveldb_writeoptions_destroy(leveldb_writeoptions_t* opt) { + delete opt; +} + +void leveldb_writeoptions_set_sync( + leveldb_writeoptions_t* opt, unsigned char v) { + opt->rep.sync = v; +} + +leveldb_cache_t* leveldb_cache_create_lru(size_t capacity) { + leveldb_cache_t* c = new leveldb_cache_t; + c->rep = NewLRUCache(capacity); + return c; +} + +void leveldb_cache_destroy(leveldb_cache_t* cache) { + delete cache->rep; + delete cache; +} + +leveldb_env_t* leveldb_create_default_env() { + leveldb_env_t* result = new leveldb_env_t; + result->rep = Env::Default(); + result->is_default = true; + return result; +} + +void leveldb_env_destroy(leveldb_env_t* env) { + if (!env->is_default) delete env->rep; + delete env; +} + +void leveldb_free(void* ptr) { + free(ptr); +} + +int leveldb_major_version() { + return kMajorVersion; +} + +int leveldb_minor_version() { + return kMinorVersion; +} + +} // end extern "C" diff --git a/src/leveldb-lin/leveldb/db/c_test.c b/src/leveldb-lin/leveldb/db/c_test.c new file mode 100644 index 0000000..7cd5ee0 --- /dev/null +++ b/src/leveldb-lin/leveldb/db/c_test.c @@ -0,0 +1,390 @@ +/* Copyright (c) 2011 The LevelDB Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. See the AUTHORS file for names of contributors. */ + +#include "leveldb/c.h" + +#include +#include +#include +#include +#include +#include + +const char* phase = ""; +static char dbname[200]; + +static void StartPhase(const char* name) { + fprintf(stderr, "=== Test %s\n", name); + phase = name; +} + +static const char* GetTempDir(void) { + const char* ret = getenv("TEST_TMPDIR"); + if (ret == NULL || ret[0] == '\0') + ret = "/tmp"; + return ret; +} + +#define CheckNoError(err) \ + if ((err) != NULL) { \ + fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, (err)); \ + abort(); \ + } + +#define CheckCondition(cond) \ + if (!(cond)) { \ + fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, #cond); \ + abort(); \ + } + +static void CheckEqual(const char* expected, const char* v, size_t n) { + if (expected == NULL && v == NULL) { + // ok + } else if (expected != NULL && v != NULL && n == strlen(expected) && + memcmp(expected, v, n) == 0) { + // ok + return; + } else { + fprintf(stderr, "%s: expected '%s', got '%s'\n", + phase, + (expected ? expected : "(null)"), + (v ? v : "(null")); + abort(); + } +} + +static void Free(char** ptr) { + if (*ptr) { + free(*ptr); + *ptr = NULL; + } +} + +static void CheckGet( + leveldb_t* db, + const leveldb_readoptions_t* options, + const char* key, + const char* expected) { + char* err = NULL; + size_t val_len; + char* val; + val = leveldb_get(db, options, key, strlen(key), &val_len, &err); + CheckNoError(err); + CheckEqual(expected, val, val_len); + Free(&val); +} + +static void CheckIter(leveldb_iterator_t* iter, + const char* key, const char* val) { + size_t len; + const char* str; + str = leveldb_iter_key(iter, &len); + CheckEqual(key, str, len); + str = leveldb_iter_value(iter, &len); + CheckEqual(val, str, len); +} + +// Callback from leveldb_writebatch_iterate() +static void CheckPut(void* ptr, + const char* k, size_t klen, + const char* v, size_t vlen) { + int* state = (int*) ptr; + CheckCondition(*state < 2); + switch (*state) { + case 0: + CheckEqual("bar", k, klen); + CheckEqual("b", v, vlen); + break; + case 1: + CheckEqual("box", k, klen); + CheckEqual("c", v, vlen); + break; + } + (*state)++; +} + +// Callback from leveldb_writebatch_iterate() +static void CheckDel(void* ptr, const char* k, size_t klen) { + int* state = (int*) ptr; + CheckCondition(*state == 2); + CheckEqual("bar", k, klen); + (*state)++; +} + +static void CmpDestroy(void* arg) { } + +static int CmpCompare(void* arg, const char* a, size_t alen, + const char* b, size_t blen) { + int n = (alen < blen) ? alen : blen; + int r = memcmp(a, b, n); + if (r == 0) { + if (alen < blen) r = -1; + else if (alen > blen) r = +1; + } + return r; +} + +static const char* CmpName(void* arg) { + return "foo"; +} + +// Custom filter policy +static unsigned char fake_filter_result = 1; +static void FilterDestroy(void* arg) { } +static const char* FilterName(void* arg) { + return "TestFilter"; +} +static char* FilterCreate( + void* arg, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length) { + *filter_length = 4; + char* result = malloc(4); + memcpy(result, "fake", 4); + return result; +} +unsigned char FilterKeyMatch( + void* arg, + const char* key, size_t length, + const char* filter, size_t filter_length) { + CheckCondition(filter_length == 4); + CheckCondition(memcmp(filter, "fake", 4) == 0); + return fake_filter_result; +} + +int main(int argc, char** argv) { + leveldb_t* db; + leveldb_comparator_t* cmp; + leveldb_cache_t* cache; + leveldb_env_t* env; + leveldb_options_t* options; + leveldb_readoptions_t* roptions; + leveldb_writeoptions_t* woptions; + char* err = NULL; + int run = -1; + + CheckCondition(leveldb_major_version() >= 1); + CheckCondition(leveldb_minor_version() >= 1); + + snprintf(dbname, sizeof(dbname), + "%s/leveldb_c_test-%d", + GetTempDir(), + ((int) geteuid())); + + StartPhase("create_objects"); + cmp = leveldb_comparator_create(NULL, CmpDestroy, CmpCompare, CmpName); + env = leveldb_create_default_env(); + cache = leveldb_cache_create_lru(100000); + + options = leveldb_options_create(); + leveldb_options_set_comparator(options, cmp); + leveldb_options_set_error_if_exists(options, 1); + leveldb_options_set_cache(options, cache); + leveldb_options_set_env(options, env); + leveldb_options_set_info_log(options, NULL); + leveldb_options_set_write_buffer_size(options, 100000); + leveldb_options_set_paranoid_checks(options, 1); + leveldb_options_set_max_open_files(options, 10); + leveldb_options_set_block_size(options, 1024); + leveldb_options_set_block_restart_interval(options, 8); + leveldb_options_set_compression(options, leveldb_no_compression); + + roptions = leveldb_readoptions_create(); + leveldb_readoptions_set_verify_checksums(roptions, 1); + leveldb_readoptions_set_fill_cache(roptions, 0); + + woptions = leveldb_writeoptions_create(); + leveldb_writeoptions_set_sync(woptions, 1); + + StartPhase("destroy"); + leveldb_destroy_db(options, dbname, &err); + Free(&err); + + StartPhase("open_error"); + db = leveldb_open(options, dbname, &err); + CheckCondition(err != NULL); + Free(&err); + + StartPhase("leveldb_free"); + db = leveldb_open(options, dbname, &err); + CheckCondition(err != NULL); + leveldb_free(err); + err = NULL; + + StartPhase("open"); + leveldb_options_set_create_if_missing(options, 1); + db = leveldb_open(options, dbname, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", NULL); + + StartPhase("put"); + leveldb_put(db, woptions, "foo", 3, "hello", 5, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", "hello"); + + StartPhase("compactall"); + leveldb_compact_range(db, NULL, 0, NULL, 0); + CheckGet(db, roptions, "foo", "hello"); + + StartPhase("compactrange"); + leveldb_compact_range(db, "a", 1, "z", 1); + CheckGet(db, roptions, "foo", "hello"); + + StartPhase("writebatch"); + { + leveldb_writebatch_t* wb = leveldb_writebatch_create(); + leveldb_writebatch_put(wb, "foo", 3, "a", 1); + leveldb_writebatch_clear(wb); + leveldb_writebatch_put(wb, "bar", 3, "b", 1); + leveldb_writebatch_put(wb, "box", 3, "c", 1); + leveldb_writebatch_delete(wb, "bar", 3); + leveldb_write(db, woptions, wb, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", "hello"); + CheckGet(db, roptions, "bar", NULL); + CheckGet(db, roptions, "box", "c"); + int pos = 0; + leveldb_writebatch_iterate(wb, &pos, CheckPut, CheckDel); + CheckCondition(pos == 3); + leveldb_writebatch_destroy(wb); + } + + StartPhase("iter"); + { + leveldb_iterator_t* iter = leveldb_create_iterator(db, roptions); + CheckCondition(!leveldb_iter_valid(iter)); + leveldb_iter_seek_to_first(iter); + CheckCondition(leveldb_iter_valid(iter)); + CheckIter(iter, "box", "c"); + leveldb_iter_next(iter); + CheckIter(iter, "foo", "hello"); + leveldb_iter_prev(iter); + CheckIter(iter, "box", "c"); + leveldb_iter_prev(iter); + CheckCondition(!leveldb_iter_valid(iter)); + leveldb_iter_seek_to_last(iter); + CheckIter(iter, "foo", "hello"); + leveldb_iter_seek(iter, "b", 1); + CheckIter(iter, "box", "c"); + leveldb_iter_get_error(iter, &err); + CheckNoError(err); + leveldb_iter_destroy(iter); + } + + StartPhase("approximate_sizes"); + { + int i; + int n = 20000; + char keybuf[100]; + char valbuf[100]; + uint64_t sizes[2]; + const char* start[2] = { "a", "k00000000000000010000" }; + size_t start_len[2] = { 1, 21 }; + const char* limit[2] = { "k00000000000000010000", "z" }; + size_t limit_len[2] = { 21, 1 }; + leveldb_writeoptions_set_sync(woptions, 0); + for (i = 0; i < n; i++) { + snprintf(keybuf, sizeof(keybuf), "k%020d", i); + snprintf(valbuf, sizeof(valbuf), "v%020d", i); + leveldb_put(db, woptions, keybuf, strlen(keybuf), valbuf, strlen(valbuf), + &err); + CheckNoError(err); + } + leveldb_approximate_sizes(db, 2, start, start_len, limit, limit_len, sizes); + CheckCondition(sizes[0] > 0); + CheckCondition(sizes[1] > 0); + } + + StartPhase("property"); + { + char* prop = leveldb_property_value(db, "nosuchprop"); + CheckCondition(prop == NULL); + prop = leveldb_property_value(db, "leveldb.stats"); + CheckCondition(prop != NULL); + Free(&prop); + } + + StartPhase("snapshot"); + { + const leveldb_snapshot_t* snap; + snap = leveldb_create_snapshot(db); + leveldb_delete(db, woptions, "foo", 3, &err); + CheckNoError(err); + leveldb_readoptions_set_snapshot(roptions, snap); + CheckGet(db, roptions, "foo", "hello"); + leveldb_readoptions_set_snapshot(roptions, NULL); + CheckGet(db, roptions, "foo", NULL); + leveldb_release_snapshot(db, snap); + } + + StartPhase("repair"); + { + leveldb_close(db); + leveldb_options_set_create_if_missing(options, 0); + leveldb_options_set_error_if_exists(options, 0); + leveldb_repair_db(options, dbname, &err); + CheckNoError(err); + db = leveldb_open(options, dbname, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", NULL); + CheckGet(db, roptions, "bar", NULL); + CheckGet(db, roptions, "box", "c"); + leveldb_options_set_create_if_missing(options, 1); + leveldb_options_set_error_if_exists(options, 1); + } + + StartPhase("filter"); + for (run = 0; run < 2; run++) { + // First run uses custom filter, second run uses bloom filter + CheckNoError(err); + leveldb_filterpolicy_t* policy; + if (run == 0) { + policy = leveldb_filterpolicy_create( + NULL, FilterDestroy, FilterCreate, FilterKeyMatch, FilterName); + } else { + policy = leveldb_filterpolicy_create_bloom(10); + } + + // Create new database + leveldb_close(db); + leveldb_destroy_db(options, dbname, &err); + leveldb_options_set_filter_policy(options, policy); + db = leveldb_open(options, dbname, &err); + CheckNoError(err); + leveldb_put(db, woptions, "foo", 3, "foovalue", 8, &err); + CheckNoError(err); + leveldb_put(db, woptions, "bar", 3, "barvalue", 8, &err); + CheckNoError(err); + leveldb_compact_range(db, NULL, 0, NULL, 0); + + fake_filter_result = 1; + CheckGet(db, roptions, "foo", "foovalue"); + CheckGet(db, roptions, "bar", "barvalue"); + if (phase == 0) { + // Must not find value when custom filter returns false + fake_filter_result = 0; + CheckGet(db, roptions, "foo", NULL); + CheckGet(db, roptions, "bar", NULL); + fake_filter_result = 1; + + CheckGet(db, roptions, "foo", "foovalue"); + CheckGet(db, roptions, "bar", "barvalue"); + } + leveldb_options_set_filter_policy(options, NULL); + leveldb_filterpolicy_destroy(policy); + } + + StartPhase("cleanup"); + leveldb_close(db); + leveldb_options_destroy(options); + leveldb_readoptions_destroy(roptions); + leveldb_writeoptions_destroy(woptions); + leveldb_cache_destroy(cache); + leveldb_comparator_destroy(cmp); + leveldb_env_destroy(env); + + fprintf(stderr, "PASS\n"); + return 0; +} diff --git a/src/leveldb-lin/leveldb/db/corruption_test.cc b/src/leveldb-lin/leveldb/db/corruption_test.cc new file mode 100644 index 0000000..96afc68 --- /dev/null +++ b/src/leveldb-lin/leveldb/db/corruption_test.cc @@ -0,0 +1,374 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" + +#include +#include +#include +#include +#include "leveldb/cache.h" +#include "leveldb/env.h" +#include "leveldb/table.h" +#include "leveldb/write_batch.h" +#include "db/db_impl.h" +#include "db/filename.h" +#include "db/log_format.h" +#include "db/version_set.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +static const int kValueSize = 1000; + +class CorruptionTest { + public: + test::ErrorEnv env_; + std::string dbname_; + Cache* tiny_cache_; + Options options_; + DB* db_; + + CorruptionTest() { + tiny_cache_ = NewLRUCache(100); + options_.env = &env_; + options_.block_cache = tiny_cache_; + dbname_ = test::TmpDir() + "/db_test"; + DestroyDB(dbname_, options_); + + db_ = NULL; + options_.create_if_missing = true; + Reopen(); + options_.create_if_missing = false; + } + + ~CorruptionTest() { + delete db_; + DestroyDB(dbname_, Options()); + delete tiny_cache_; + } + + Status TryReopen() { + delete db_; + db_ = NULL; + return DB::Open(options_, dbname_, &db_); + } + + void Reopen() { + ASSERT_OK(TryReopen()); + } + + void RepairDB() { + delete db_; + db_ = NULL; + ASSERT_OK(::leveldb::RepairDB(dbname_, options_)); + } + + void Build(int n) { + std::string key_space, value_space; + WriteBatch batch; + for (int i = 0; i < n; i++) { + //if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n); + Slice key = Key(i, &key_space); + batch.Clear(); + batch.Put(key, Value(i, &value_space)); + WriteOptions options; + // Corrupt() doesn't work without this sync on windows; stat reports 0 for + // the file size. + if (i == n - 1) { + options.sync = true; + } + ASSERT_OK(db_->Write(options, &batch)); + } + } + + void Check(int min_expected, int max_expected) { + int next_expected = 0; + int missed = 0; + int bad_keys = 0; + int bad_values = 0; + int correct = 0; + std::string value_space; + Iterator* iter = db_->NewIterator(ReadOptions()); + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + uint64_t key; + Slice in(iter->key()); + if (in == "" || in == "~") { + // Ignore boundary keys. + continue; + } + if (!ConsumeDecimalNumber(&in, &key) || + !in.empty() || + key < next_expected) { + bad_keys++; + continue; + } + missed += (key - next_expected); + next_expected = key + 1; + if (iter->value() != Value(key, &value_space)) { + bad_values++; + } else { + correct++; + } + } + delete iter; + + fprintf(stderr, + "expected=%d..%d; got=%d; bad_keys=%d; bad_values=%d; missed=%d\n", + min_expected, max_expected, correct, bad_keys, bad_values, missed); + ASSERT_LE(min_expected, correct); + ASSERT_GE(max_expected, correct); + } + + void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) { + // Pick file to corrupt + std::vector filenames; + ASSERT_OK(env_.GetChildren(dbname_, &filenames)); + uint64_t number; + FileType type; + std::string fname; + int picked_number = -1; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) && + type == filetype && + int(number) > picked_number) { // Pick latest file + fname = dbname_ + "/" + filenames[i]; + picked_number = number; + } + } + ASSERT_TRUE(!fname.empty()) << filetype; + + struct stat sbuf; + if (stat(fname.c_str(), &sbuf) != 0) { + const char* msg = strerror(errno); + ASSERT_TRUE(false) << fname << ": " << msg; + } + + if (offset < 0) { + // Relative to end of file; make it absolute + if (-offset > sbuf.st_size) { + offset = 0; + } else { + offset = sbuf.st_size + offset; + } + } + if (offset > sbuf.st_size) { + offset = sbuf.st_size; + } + if (offset + bytes_to_corrupt > sbuf.st_size) { + bytes_to_corrupt = sbuf.st_size - offset; + } + + // Do it + std::string contents; + Status s = ReadFileToString(Env::Default(), fname, &contents); + ASSERT_TRUE(s.ok()) << s.ToString(); + for (int i = 0; i < bytes_to_corrupt; i++) { + contents[i + offset] ^= 0x80; + } + s = WriteStringToFile(Env::Default(), contents, fname); + ASSERT_TRUE(s.ok()) << s.ToString(); + } + + int Property(const std::string& name) { + std::string property; + int result; + if (db_->GetProperty(name, &property) && + sscanf(property.c_str(), "%d", &result) == 1) { + return result; + } else { + return -1; + } + } + + // Return the ith key + Slice Key(int i, std::string* storage) { + char buf[100]; + snprintf(buf, sizeof(buf), "%016d", i); + storage->assign(buf, strlen(buf)); + return Slice(*storage); + } + + // Return the value to associate with the specified key + Slice Value(int k, std::string* storage) { + Random r(k); + return test::RandomString(&r, kValueSize, storage); + } +}; + +TEST(CorruptionTest, Recovery) { + Build(100); + Check(100, 100); + Corrupt(kLogFile, 19, 1); // WriteBatch tag for first record + Corrupt(kLogFile, log::kBlockSize + 1000, 1); // Somewhere in second block + Reopen(); + + // The 64 records in the first two log blocks are completely lost. + Check(36, 36); +} + +TEST(CorruptionTest, RecoverWriteError) { + env_.writable_file_error_ = true; + Status s = TryReopen(); + ASSERT_TRUE(!s.ok()); +} + +TEST(CorruptionTest, NewFileErrorDuringWrite) { + // Do enough writing to force minor compaction + env_.writable_file_error_ = true; + const int num = 3 + (Options().write_buffer_size / kValueSize); + std::string value_storage; + Status s; + for (int i = 0; s.ok() && i < num; i++) { + WriteBatch batch; + batch.Put("a", Value(100, &value_storage)); + s = db_->Write(WriteOptions(), &batch); + } + ASSERT_TRUE(!s.ok()); + ASSERT_GE(env_.num_writable_file_errors_, 1); + env_.writable_file_error_ = false; + Reopen(); +} + +TEST(CorruptionTest, TableFile) { + Build(100); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + dbi->TEST_CompactRange(0, NULL, NULL); + dbi->TEST_CompactRange(1, NULL, NULL); + + Corrupt(kTableFile, 100, 1); + Check(90, 99); +} + +TEST(CorruptionTest, TableFileRepair) { + options_.block_size = 2 * kValueSize; // Limit scope of corruption + options_.paranoid_checks = true; + Reopen(); + Build(100); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + dbi->TEST_CompactRange(0, NULL, NULL); + dbi->TEST_CompactRange(1, NULL, NULL); + + Corrupt(kTableFile, 100, 1); + RepairDB(); + Reopen(); + Check(95, 99); +} + +TEST(CorruptionTest, TableFileIndexData) { + Build(10000); // Enough to build multiple Tables + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + + Corrupt(kTableFile, -2000, 500); + Reopen(); + Check(5000, 9999); +} + +TEST(CorruptionTest, MissingDescriptor) { + Build(1000); + RepairDB(); + Reopen(); + Check(1000, 1000); +} + +TEST(CorruptionTest, SequenceNumberRecovery) { + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v3")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v4")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v5")); + RepairDB(); + Reopen(); + std::string v; + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("v5", v); + // Write something. If sequence number was not recovered properly, + // it will be hidden by an earlier write. + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v6")); + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("v6", v); + Reopen(); + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("v6", v); +} + +TEST(CorruptionTest, CorruptedDescriptor) { + ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello")); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + dbi->TEST_CompactRange(0, NULL, NULL); + + Corrupt(kDescriptorFile, 0, 1000); + Status s = TryReopen(); + ASSERT_TRUE(!s.ok()); + + RepairDB(); + Reopen(); + std::string v; + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("hello", v); +} + +TEST(CorruptionTest, CompactionInputError) { + Build(10); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(1, Property("leveldb.num-files-at-level" + NumberToString(last))); + + Corrupt(kTableFile, 100, 1); + Check(5, 9); + + // Force compactions by writing lots of values + Build(10000); + Check(10000, 10000); +} + +TEST(CorruptionTest, CompactionInputErrorParanoid) { + options_.paranoid_checks = true; + options_.write_buffer_size = 512 << 10; + Reopen(); + DBImpl* dbi = reinterpret_cast(db_); + + // Make multiple inputs so we need to compact. + for (int i = 0; i < 2; i++) { + Build(10); + dbi->TEST_CompactMemTable(); + Corrupt(kTableFile, 100, 1); + env_.SleepForMicroseconds(100000); + } + dbi->CompactRange(NULL, NULL); + + // Write must fail because of corrupted table + std::string tmp1, tmp2; + Status s = db_->Put(WriteOptions(), Key(5, &tmp1), Value(5, &tmp2)); + ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db"; +} + +TEST(CorruptionTest, UnrelatedKeys) { + Build(10); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + Corrupt(kTableFile, 100, 1); + + std::string tmp1, tmp2; + ASSERT_OK(db_->Put(WriteOptions(), Key(1000, &tmp1), Value(1000, &tmp2))); + std::string v; + ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v)); + ASSERT_EQ(Value(1000, &tmp2).ToString(), v); + dbi->TEST_CompactMemTable(); + ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v)); + ASSERT_EQ(Value(1000, &tmp2).ToString(), v); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/leveldb/db/db_bench.cc b/src/leveldb-lin/leveldb/db/db_bench.cc new file mode 100644 index 0000000..fc46d89 --- /dev/null +++ b/src/leveldb-lin/leveldb/db/db_bench.cc @@ -0,0 +1,979 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include +#include "db/db_impl.h" +#include "db/version_set.h" +#include "leveldb/cache.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/write_batch.h" +#include "port/port.h" +#include "util/crc32c.h" +#include "util/histogram.h" +#include "util/mutexlock.h" +#include "util/random.h" +#include "util/testutil.h" + +// Comma-separated list of operations to run in the specified order +// Actual benchmarks: +// fillseq -- write N values in sequential key order in async mode +// fillrandom -- write N values in random key order in async mode +// overwrite -- overwrite N values in random key order in async mode +// fillsync -- write N/100 values in random key order in sync mode +// fill100K -- write N/1000 100K values in random order in async mode +// deleteseq -- delete N keys in sequential order +// deleterandom -- delete N keys in random order +// readseq -- read N times sequentially +// readreverse -- read N times in reverse order +// readrandom -- read N times in random order +// readmissing -- read N missing keys in random order +// readhot -- read N times in random order from 1% section of DB +// seekrandom -- N random seeks +// crc32c -- repeated crc32c of 4K of data +// acquireload -- load N*1000 times +// Meta operations: +// compact -- Compact the entire DB +// stats -- Print DB stats +// sstables -- Print sstable info +// heapprofile -- Dump a heap profile (if supported by this port) +static const char* FLAGS_benchmarks = + "fillseq," + "fillsync," + "fillrandom," + "overwrite," + "readrandom," + "readrandom," // Extra run to allow previous compactions to quiesce + "readseq," + "readreverse," + "compact," + "readrandom," + "readseq," + "readreverse," + "fill100K," + "crc32c," + "snappycomp," + "snappyuncomp," + "acquireload," + ; + +// Number of key/values to place in database +static int FLAGS_num = 1000000; + +// Number of read operations to do. If negative, do FLAGS_num reads. +static int FLAGS_reads = -1; + +// Number of concurrent threads to run. +static int FLAGS_threads = 1; + +// Size of each value +static int FLAGS_value_size = 100; + +// Arrange to generate values that shrink to this fraction of +// their original size after compression +static double FLAGS_compression_ratio = 0.5; + +// Print histogram of operation timings +static bool FLAGS_histogram = false; + +// Number of bytes to buffer in memtable before compacting +// (initialized to default value by "main") +static int FLAGS_write_buffer_size = 0; + +// Number of bytes to use as a cache of uncompressed data. +// Negative means use default settings. +static int FLAGS_cache_size = -1; + +// Maximum number of files to keep open at the same time (use default if == 0) +static int FLAGS_open_files = 0; + +// Bloom filter bits per key. +// Negative means use default settings. +static int FLAGS_bloom_bits = -1; + +// If true, do not destroy the existing database. If you set this +// flag and also specify a benchmark that wants a fresh database, that +// benchmark will fail. +static bool FLAGS_use_existing_db = false; + +// Use the db with the following name. +static const char* FLAGS_db = NULL; + +namespace leveldb { + +namespace { + +// Helper for quickly generating random data. +class RandomGenerator { + private: + std::string data_; + int pos_; + + public: + RandomGenerator() { + // We use a limited amount of data over and over again and ensure + // that it is larger than the compression window (32KB), and also + // large enough to serve all typical value sizes we want to write. + Random rnd(301); + std::string piece; + while (data_.size() < 1048576) { + // Add a short fragment that is as compressible as specified + // by FLAGS_compression_ratio. + test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece); + data_.append(piece); + } + pos_ = 0; + } + + Slice Generate(size_t len) { + if (pos_ + len > data_.size()) { + pos_ = 0; + assert(len < data_.size()); + } + pos_ += len; + return Slice(data_.data() + pos_ - len, len); + } +}; + +static Slice TrimSpace(Slice s) { + size_t start = 0; + while (start < s.size() && isspace(s[start])) { + start++; + } + size_t limit = s.size(); + while (limit > start && isspace(s[limit-1])) { + limit--; + } + return Slice(s.data() + start, limit - start); +} + +static void AppendWithSpace(std::string* str, Slice msg) { + if (msg.empty()) return; + if (!str->empty()) { + str->push_back(' '); + } + str->append(msg.data(), msg.size()); +} + +class Stats { + private: + double start_; + double finish_; + double seconds_; + int done_; + int next_report_; + int64_t bytes_; + double last_op_finish_; + Histogram hist_; + std::string message_; + + public: + Stats() { Start(); } + + void Start() { + next_report_ = 100; + last_op_finish_ = start_; + hist_.Clear(); + done_ = 0; + bytes_ = 0; + seconds_ = 0; + start_ = Env::Default()->NowMicros(); + finish_ = start_; + message_.clear(); + } + + void Merge(const Stats& other) { + hist_.Merge(other.hist_); + done_ += other.done_; + bytes_ += other.bytes_; + seconds_ += other.seconds_; + if (other.start_ < start_) start_ = other.start_; + if (other.finish_ > finish_) finish_ = other.finish_; + + // Just keep the messages from one thread + if (message_.empty()) message_ = other.message_; + } + + void Stop() { + finish_ = Env::Default()->NowMicros(); + seconds_ = (finish_ - start_) * 1e-6; + } + + void AddMessage(Slice msg) { + AppendWithSpace(&message_, msg); + } + + void FinishedSingleOp() { + if (FLAGS_histogram) { + double now = Env::Default()->NowMicros(); + double micros = now - last_op_finish_; + hist_.Add(micros); + if (micros > 20000) { + fprintf(stderr, "long op: %.1f micros%30s\r", micros, ""); + fflush(stderr); + } + last_op_finish_ = now; + } + + done_++; + if (done_ >= next_report_) { + if (next_report_ < 1000) next_report_ += 100; + else if (next_report_ < 5000) next_report_ += 500; + else if (next_report_ < 10000) next_report_ += 1000; + else if (next_report_ < 50000) next_report_ += 5000; + else if (next_report_ < 100000) next_report_ += 10000; + else if (next_report_ < 500000) next_report_ += 50000; + else next_report_ += 100000; + fprintf(stderr, "... finished %d ops%30s\r", done_, ""); + fflush(stderr); + } + } + + void AddBytes(int64_t n) { + bytes_ += n; + } + + void Report(const Slice& name) { + // Pretend at least one op was done in case we are running a benchmark + // that does not call FinishedSingleOp(). + if (done_ < 1) done_ = 1; + + std::string extra; + if (bytes_ > 0) { + // Rate is computed on actual elapsed time, not the sum of per-thread + // elapsed times. + double elapsed = (finish_ - start_) * 1e-6; + char rate[100]; + snprintf(rate, sizeof(rate), "%6.1f MB/s", + (bytes_ / 1048576.0) / elapsed); + extra = rate; + } + AppendWithSpace(&extra, message_); + + fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", + name.ToString().c_str(), + seconds_ * 1e6 / done_, + (extra.empty() ? "" : " "), + extra.c_str()); + if (FLAGS_histogram) { + fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str()); + } + fflush(stdout); + } +}; + +// State shared by all concurrent executions of the same benchmark. +struct SharedState { + port::Mutex mu; + port::CondVar cv; + int total; + + // Each thread goes through the following states: + // (1) initializing + // (2) waiting for others to be initialized + // (3) running + // (4) done + + int num_initialized; + int num_done; + bool start; + + SharedState() : cv(&mu) { } +}; + +// Per-thread state for concurrent executions of the same benchmark. +struct ThreadState { + int tid; // 0..n-1 when running in n threads + Random rand; // Has different seeds for different threads + Stats stats; + SharedState* shared; + + ThreadState(int index) + : tid(index), + rand(1000 + index) { + } +}; + +} // namespace + +class Benchmark { + private: + Cache* cache_; + const FilterPolicy* filter_policy_; + DB* db_; + int num_; + int value_size_; + int entries_per_batch_; + WriteOptions write_options_; + int reads_; + int heap_counter_; + + void PrintHeader() { + const int kKeySize = 16; + PrintEnvironment(); + fprintf(stdout, "Keys: %d bytes each\n", kKeySize); + fprintf(stdout, "Values: %d bytes each (%d bytes after compression)\n", + FLAGS_value_size, + static_cast(FLAGS_value_size * FLAGS_compression_ratio + 0.5)); + fprintf(stdout, "Entries: %d\n", num_); + fprintf(stdout, "RawSize: %.1f MB (estimated)\n", + ((static_cast(kKeySize + FLAGS_value_size) * num_) + / 1048576.0)); + fprintf(stdout, "FileSize: %.1f MB (estimated)\n", + (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) + / 1048576.0)); + PrintWarnings(); + fprintf(stdout, "------------------------------------------------\n"); + } + + void PrintWarnings() { +#if defined(__GNUC__) && !defined(__OPTIMIZE__) + fprintf(stdout, + "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n" + ); +#endif +#ifndef NDEBUG + fprintf(stdout, + "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n"); +#endif + + // See if snappy is working by attempting to compress a compressible string + const char text[] = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"; + std::string compressed; + if (!port::Snappy_Compress(text, sizeof(text), &compressed)) { + fprintf(stdout, "WARNING: Snappy compression is not enabled\n"); + } else if (compressed.size() >= sizeof(text)) { + fprintf(stdout, "WARNING: Snappy compression is not effective\n"); + } + } + + void PrintEnvironment() { + fprintf(stderr, "LevelDB: version %d.%d\n", + kMajorVersion, kMinorVersion); + +#if defined(__linux) + time_t now = time(NULL); + fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline + + FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); + if (cpuinfo != NULL) { + char line[1000]; + int num_cpus = 0; + std::string cpu_type; + std::string cache_size; + while (fgets(line, sizeof(line), cpuinfo) != NULL) { + const char* sep = strchr(line, ':'); + if (sep == NULL) { + continue; + } + Slice key = TrimSpace(Slice(line, sep - 1 - line)); + Slice val = TrimSpace(Slice(sep + 1)); + if (key == "model name") { + ++num_cpus; + cpu_type = val.ToString(); + } else if (key == "cache size") { + cache_size = val.ToString(); + } + } + fclose(cpuinfo); + fprintf(stderr, "CPU: %d * %s\n", num_cpus, cpu_type.c_str()); + fprintf(stderr, "CPUCache: %s\n", cache_size.c_str()); + } +#endif + } + + public: + Benchmark() + : cache_(FLAGS_cache_size >= 0 ? NewLRUCache(FLAGS_cache_size) : NULL), + filter_policy_(FLAGS_bloom_bits >= 0 + ? NewBloomFilterPolicy(FLAGS_bloom_bits) + : NULL), + db_(NULL), + num_(FLAGS_num), + value_size_(FLAGS_value_size), + entries_per_batch_(1), + reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), + heap_counter_(0) { + std::vector files; + Env::Default()->GetChildren(FLAGS_db, &files); + for (size_t i = 0; i < files.size(); i++) { + if (Slice(files[i]).starts_with("heap-")) { + Env::Default()->DeleteFile(std::string(FLAGS_db) + "/" + files[i]); + } + } + if (!FLAGS_use_existing_db) { + DestroyDB(FLAGS_db, Options()); + } + } + + ~Benchmark() { + delete db_; + delete cache_; + delete filter_policy_; + } + + void Run() { + PrintHeader(); + Open(); + + const char* benchmarks = FLAGS_benchmarks; + while (benchmarks != NULL) { + const char* sep = strchr(benchmarks, ','); + Slice name; + if (sep == NULL) { + name = benchmarks; + benchmarks = NULL; + } else { + name = Slice(benchmarks, sep - benchmarks); + benchmarks = sep + 1; + } + + // Reset parameters that may be overriddden bwlow + num_ = FLAGS_num; + reads_ = (FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads); + value_size_ = FLAGS_value_size; + entries_per_batch_ = 1; + write_options_ = WriteOptions(); + + void (Benchmark::*method)(ThreadState*) = NULL; + bool fresh_db = false; + int num_threads = FLAGS_threads; + + if (name == Slice("fillseq")) { + fresh_db = true; + method = &Benchmark::WriteSeq; + } else if (name == Slice("fillbatch")) { + fresh_db = true; + entries_per_batch_ = 1000; + method = &Benchmark::WriteSeq; + } else if (name == Slice("fillrandom")) { + fresh_db = true; + method = &Benchmark::WriteRandom; + } else if (name == Slice("overwrite")) { + fresh_db = false; + method = &Benchmark::WriteRandom; + } else if (name == Slice("fillsync")) { + fresh_db = true; + num_ /= 1000; + write_options_.sync = true; + method = &Benchmark::WriteRandom; + } else if (name == Slice("fill100K")) { + fresh_db = true; + num_ /= 1000; + value_size_ = 100 * 1000; + method = &Benchmark::WriteRandom; + } else if (name == Slice("readseq")) { + method = &Benchmark::ReadSequential; + } else if (name == Slice("readreverse")) { + method = &Benchmark::ReadReverse; + } else if (name == Slice("readrandom")) { + method = &Benchmark::ReadRandom; + } else if (name == Slice("readmissing")) { + method = &Benchmark::ReadMissing; + } else if (name == Slice("seekrandom")) { + method = &Benchmark::SeekRandom; + } else if (name == Slice("readhot")) { + method = &Benchmark::ReadHot; + } else if (name == Slice("readrandomsmall")) { + reads_ /= 1000; + method = &Benchmark::ReadRandom; + } else if (name == Slice("deleteseq")) { + method = &Benchmark::DeleteSeq; + } else if (name == Slice("deleterandom")) { + method = &Benchmark::DeleteRandom; + } else if (name == Slice("readwhilewriting")) { + num_threads++; // Add extra thread for writing + method = &Benchmark::ReadWhileWriting; + } else if (name == Slice("compact")) { + method = &Benchmark::Compact; + } else if (name == Slice("crc32c")) { + method = &Benchmark::Crc32c; + } else if (name == Slice("acquireload")) { + method = &Benchmark::AcquireLoad; + } else if (name == Slice("snappycomp")) { + method = &Benchmark::SnappyCompress; + } else if (name == Slice("snappyuncomp")) { + method = &Benchmark::SnappyUncompress; + } else if (name == Slice("heapprofile")) { + HeapProfile(); + } else if (name == Slice("stats")) { + PrintStats("leveldb.stats"); + } else if (name == Slice("sstables")) { + PrintStats("leveldb.sstables"); + } else { + if (name != Slice()) { // No error message for empty name + fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str()); + } + } + + if (fresh_db) { + if (FLAGS_use_existing_db) { + fprintf(stdout, "%-12s : skipped (--use_existing_db is true)\n", + name.ToString().c_str()); + method = NULL; + } else { + delete db_; + db_ = NULL; + DestroyDB(FLAGS_db, Options()); + Open(); + } + } + + if (method != NULL) { + RunBenchmark(num_threads, name, method); + } + } + } + + private: + struct ThreadArg { + Benchmark* bm; + SharedState* shared; + ThreadState* thread; + void (Benchmark::*method)(ThreadState*); + }; + + static void ThreadBody(void* v) { + ThreadArg* arg = reinterpret_cast(v); + SharedState* shared = arg->shared; + ThreadState* thread = arg->thread; + { + MutexLock l(&shared->mu); + shared->num_initialized++; + if (shared->num_initialized >= shared->total) { + shared->cv.SignalAll(); + } + while (!shared->start) { + shared->cv.Wait(); + } + } + + thread->stats.Start(); + (arg->bm->*(arg->method))(thread); + thread->stats.Stop(); + + { + MutexLock l(&shared->mu); + shared->num_done++; + if (shared->num_done >= shared->total) { + shared->cv.SignalAll(); + } + } + } + + void RunBenchmark(int n, Slice name, + void (Benchmark::*method)(ThreadState*)) { + SharedState shared; + shared.total = n; + shared.num_initialized = 0; + shared.num_done = 0; + shared.start = false; + + ThreadArg* arg = new ThreadArg[n]; + for (int i = 0; i < n; i++) { + arg[i].bm = this; + arg[i].method = method; + arg[i].shared = &shared; + arg[i].thread = new ThreadState(i); + arg[i].thread->shared = &shared; + Env::Default()->StartThread(ThreadBody, &arg[i]); + } + + shared.mu.Lock(); + while (shared.num_initialized < n) { + shared.cv.Wait(); + } + + shared.start = true; + shared.cv.SignalAll(); + while (shared.num_done < n) { + shared.cv.Wait(); + } + shared.mu.Unlock(); + + for (int i = 1; i < n; i++) { + arg[0].thread->stats.Merge(arg[i].thread->stats); + } + arg[0].thread->stats.Report(name); + + for (int i = 0; i < n; i++) { + delete arg[i].thread; + } + delete[] arg; + } + + void Crc32c(ThreadState* thread) { + // Checksum about 500MB of data total + const int size = 4096; + const char* label = "(4K per op)"; + std::string data(size, 'x'); + int64_t bytes = 0; + uint32_t crc = 0; + while (bytes < 500 * 1048576) { + crc = crc32c::Value(data.data(), size); + thread->stats.FinishedSingleOp(); + bytes += size; + } + // Print so result is not dead + fprintf(stderr, "... crc=0x%x\r", static_cast(crc)); + + thread->stats.AddBytes(bytes); + thread->stats.AddMessage(label); + } + + void AcquireLoad(ThreadState* thread) { + int dummy; + port::AtomicPointer ap(&dummy); + int count = 0; + void *ptr = NULL; + thread->stats.AddMessage("(each op is 1000 loads)"); + while (count < 100000) { + for (int i = 0; i < 1000; i++) { + ptr = ap.Acquire_Load(); + } + count++; + thread->stats.FinishedSingleOp(); + } + if (ptr == NULL) exit(1); // Disable unused variable warning. + } + + void SnappyCompress(ThreadState* thread) { + RandomGenerator gen; + Slice input = gen.Generate(Options().block_size); + int64_t bytes = 0; + int64_t produced = 0; + bool ok = true; + std::string compressed; + while (ok && bytes < 1024 * 1048576) { // Compress 1G + ok = port::Snappy_Compress(input.data(), input.size(), &compressed); + produced += compressed.size(); + bytes += input.size(); + thread->stats.FinishedSingleOp(); + } + + if (!ok) { + thread->stats.AddMessage("(snappy failure)"); + } else { + char buf[100]; + snprintf(buf, sizeof(buf), "(output: %.1f%%)", + (produced * 100.0) / bytes); + thread->stats.AddMessage(buf); + thread->stats.AddBytes(bytes); + } + } + + void SnappyUncompress(ThreadState* thread) { + RandomGenerator gen; + Slice input = gen.Generate(Options().block_size); + std::string compressed; + bool ok = port::Snappy_Compress(input.data(), input.size(), &compressed); + int64_t bytes = 0; + char* uncompressed = new char[input.size()]; + while (ok && bytes < 1024 * 1048576) { // Compress 1G + ok = port::Snappy_Uncompress(compressed.data(), compressed.size(), + uncompressed); + bytes += input.size(); + thread->stats.FinishedSingleOp(); + } + delete[] uncompressed; + + if (!ok) { + thread->stats.AddMessage("(snappy failure)"); + } else { + thread->stats.AddBytes(bytes); + } + } + + void Open() { + assert(db_ == NULL); + Options options; + options.create_if_missing = !FLAGS_use_existing_db; + options.block_cache = cache_; + options.write_buffer_size = FLAGS_write_buffer_size; + options.max_open_files = FLAGS_open_files; + options.filter_policy = filter_policy_; + Status s = DB::Open(options, FLAGS_db, &db_); + if (!s.ok()) { + fprintf(stderr, "open error: %s\n", s.ToString().c_str()); + exit(1); + } + } + + void WriteSeq(ThreadState* thread) { + DoWrite(thread, true); + } + + void WriteRandom(ThreadState* thread) { + DoWrite(thread, false); + } + + void DoWrite(ThreadState* thread, bool seq) { + if (num_ != FLAGS_num) { + char msg[100]; + snprintf(msg, sizeof(msg), "(%d ops)", num_); + thread->stats.AddMessage(msg); + } + + RandomGenerator gen; + WriteBatch batch; + Status s; + int64_t bytes = 0; + for (int i = 0; i < num_; i += entries_per_batch_) { + batch.Clear(); + for (int j = 0; j < entries_per_batch_; j++) { + const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num); + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + batch.Put(key, gen.Generate(value_size_)); + bytes += value_size_ + strlen(key); + thread->stats.FinishedSingleOp(); + } + s = db_->Write(write_options_, &batch); + if (!s.ok()) { + fprintf(stderr, "put error: %s\n", s.ToString().c_str()); + exit(1); + } + } + thread->stats.AddBytes(bytes); + } + + void ReadSequential(ThreadState* thread) { + Iterator* iter = db_->NewIterator(ReadOptions()); + int i = 0; + int64_t bytes = 0; + for (iter->SeekToFirst(); i < reads_ && iter->Valid(); iter->Next()) { + bytes += iter->key().size() + iter->value().size(); + thread->stats.FinishedSingleOp(); + ++i; + } + delete iter; + thread->stats.AddBytes(bytes); + } + + void ReadReverse(ThreadState* thread) { + Iterator* iter = db_->NewIterator(ReadOptions()); + int i = 0; + int64_t bytes = 0; + for (iter->SeekToLast(); i < reads_ && iter->Valid(); iter->Prev()) { + bytes += iter->key().size() + iter->value().size(); + thread->stats.FinishedSingleOp(); + ++i; + } + delete iter; + thread->stats.AddBytes(bytes); + } + + void ReadRandom(ThreadState* thread) { + ReadOptions options; + std::string value; + int found = 0; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = thread->rand.Next() % FLAGS_num; + snprintf(key, sizeof(key), "%016d", k); + if (db_->Get(options, key, &value).ok()) { + found++; + } + thread->stats.FinishedSingleOp(); + } + char msg[100]; + snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_); + thread->stats.AddMessage(msg); + } + + void ReadMissing(ThreadState* thread) { + ReadOptions options; + std::string value; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = thread->rand.Next() % FLAGS_num; + snprintf(key, sizeof(key), "%016d.", k); + db_->Get(options, key, &value); + thread->stats.FinishedSingleOp(); + } + } + + void ReadHot(ThreadState* thread) { + ReadOptions options; + std::string value; + const int range = (FLAGS_num + 99) / 100; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = thread->rand.Next() % range; + snprintf(key, sizeof(key), "%016d", k); + db_->Get(options, key, &value); + thread->stats.FinishedSingleOp(); + } + } + + void SeekRandom(ThreadState* thread) { + ReadOptions options; + std::string value; + int found = 0; + for (int i = 0; i < reads_; i++) { + Iterator* iter = db_->NewIterator(options); + char key[100]; + const int k = thread->rand.Next() % FLAGS_num; + snprintf(key, sizeof(key), "%016d", k); + iter->Seek(key); + if (iter->Valid() && iter->key() == key) found++; + delete iter; + thread->stats.FinishedSingleOp(); + } + char msg[100]; + snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_); + thread->stats.AddMessage(msg); + } + + void DoDelete(ThreadState* thread, bool seq) { + RandomGenerator gen; + WriteBatch batch; + Status s; + for (int i = 0; i < num_; i += entries_per_batch_) { + batch.Clear(); + for (int j = 0; j < entries_per_batch_; j++) { + const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num); + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + batch.Delete(key); + thread->stats.FinishedSingleOp(); + } + s = db_->Write(write_options_, &batch); + if (!s.ok()) { + fprintf(stderr, "del error: %s\n", s.ToString().c_str()); + exit(1); + } + } + } + + void DeleteSeq(ThreadState* thread) { + DoDelete(thread, true); + } + + void DeleteRandom(ThreadState* thread) { + DoDelete(thread, false); + } + + void ReadWhileWriting(ThreadState* thread) { + if (thread->tid > 0) { + ReadRandom(thread); + } else { + // Special thread that keeps writing until other threads are done. + RandomGenerator gen; + while (true) { + { + MutexLock l(&thread->shared->mu); + if (thread->shared->num_done + 1 >= thread->shared->num_initialized) { + // Other threads have finished + break; + } + } + + const int k = thread->rand.Next() % FLAGS_num; + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + Status s = db_->Put(write_options_, key, gen.Generate(value_size_)); + if (!s.ok()) { + fprintf(stderr, "put error: %s\n", s.ToString().c_str()); + exit(1); + } + } + + // Do not count any of the preceding work/delay in stats. + thread->stats.Start(); + } + } + + void Compact(ThreadState* thread) { + db_->CompactRange(NULL, NULL); + } + + void PrintStats(const char* key) { + std::string stats; + if (!db_->GetProperty(key, &stats)) { + stats = "(failed)"; + } + fprintf(stdout, "\n%s\n", stats.c_str()); + } + + static void WriteToFile(void* arg, const char* buf, int n) { + reinterpret_cast(arg)->Append(Slice(buf, n)); + } + + void HeapProfile() { + char fname[100]; + snprintf(fname, sizeof(fname), "%s/heap-%04d", FLAGS_db, ++heap_counter_); + WritableFile* file; + Status s = Env::Default()->NewWritableFile(fname, &file); + if (!s.ok()) { + fprintf(stderr, "%s\n", s.ToString().c_str()); + return; + } + bool ok = port::GetHeapProfile(WriteToFile, file); + delete file; + if (!ok) { + fprintf(stderr, "heap profiling not supported\n"); + Env::Default()->DeleteFile(fname); + } + } +}; + +} // namespace leveldb + +int main(int argc, char** argv) { + FLAGS_write_buffer_size = leveldb::Options().write_buffer_size; + FLAGS_open_files = leveldb::Options().max_open_files; + std::string default_db_path; + + for (int i = 1; i < argc; i++) { + double d; + int n; + char junk; + if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) { + FLAGS_benchmarks = argv[i] + strlen("--benchmarks="); + } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) { + FLAGS_compression_ratio = d; + } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_histogram = n; + } else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_use_existing_db = n; + } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) { + FLAGS_num = n; + } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) { + FLAGS_reads = n; + } else if (sscanf(argv[i], "--threads=%d%c", &n, &junk) == 1) { + FLAGS_threads = n; + } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) { + FLAGS_value_size = n; + } else if (sscanf(argv[i], "--write_buffer_size=%d%c", &n, &junk) == 1) { + FLAGS_write_buffer_size = n; + } else if (sscanf(argv[i], "--cache_size=%d%c", &n, &junk) == 1) { + FLAGS_cache_size = n; + } else if (sscanf(argv[i], "--bloom_bits=%d%c", &n, &junk) == 1) { + FLAGS_bloom_bits = n; + } else if (sscanf(argv[i], "--open_files=%d%c", &n, &junk) == 1) { + FLAGS_open_files = n; + } else if (strncmp(argv[i], "--db=", 5) == 0) { + FLAGS_db = argv[i] + 5; + } else { + fprintf(stderr, "Invalid flag '%s'\n", argv[i]); + exit(1); + } + } + + // Choose a location for the test database if none given with --db= + if (FLAGS_db == NULL) { + leveldb::Env::Default()->GetTestDirectory(&default_db_path); + default_db_path += "/dbbench"; + FLAGS_db = default_db_path.c_str(); + } + + leveldb::Benchmark benchmark; + benchmark.Run(); + return 0; +} diff --git a/src/leveldb-lin/leveldb/db/db_impl.cc b/src/leveldb-lin/leveldb/db/db_impl.cc new file mode 100644 index 0000000..faf5e7d --- /dev/null +++ b/src/leveldb-lin/leveldb/db/db_impl.cc @@ -0,0 +1,1513 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/db_impl.h" + +#include +#include +#include +#include +#include +#include +#include "db/builder.h" +#include "db/db_iter.h" +#include "db/dbformat.h" +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "db/memtable.h" +#include "db/table_cache.h" +#include "db/version_set.h" +#include "db/write_batch_internal.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/status.h" +#include "leveldb/table.h" +#include "leveldb/table_builder.h" +#include "port/port.h" +#include "table/block.h" +#include "table/merger.h" +#include "table/two_level_iterator.h" +#include "util/coding.h" +#include "util/logging.h" +#include "util/mutexlock.h" + +namespace leveldb { + +const int kNumNonTableCacheFiles = 10; + +// Information kept for every waiting writer +struct DBImpl::Writer { + Status status; + WriteBatch* batch; + bool sync; + bool done; + port::CondVar cv; + + explicit Writer(port::Mutex* mu) : cv(mu) { } +}; + +struct DBImpl::CompactionState { + Compaction* const compaction; + + // Sequence numbers < smallest_snapshot are not significant since we + // will never have to service a snapshot below smallest_snapshot. + // Therefore if we have seen a sequence number S <= smallest_snapshot, + // we can drop all entries for the same key with sequence numbers < S. + SequenceNumber smallest_snapshot; + + // Files produced by compaction + struct Output { + uint64_t number; + uint64_t file_size; + InternalKey smallest, largest; + }; + std::vector outputs; + + // State kept for output being generated + WritableFile* outfile; + TableBuilder* builder; + + uint64_t total_bytes; + + Output* current_output() { return &outputs[outputs.size()-1]; } + + explicit CompactionState(Compaction* c) + : compaction(c), + outfile(NULL), + builder(NULL), + total_bytes(0) { + } +}; + +// Fix user-supplied options to be reasonable +template +static void ClipToRange(T* ptr, V minvalue, V maxvalue) { + if (static_cast(*ptr) > maxvalue) *ptr = maxvalue; + if (static_cast(*ptr) < minvalue) *ptr = minvalue; +} +Options SanitizeOptions(const std::string& dbname, + const InternalKeyComparator* icmp, + const InternalFilterPolicy* ipolicy, + const Options& src) { + Options result = src; + result.comparator = icmp; + result.filter_policy = (src.filter_policy != NULL) ? ipolicy : NULL; + ClipToRange(&result.max_open_files, 64 + kNumNonTableCacheFiles, 50000); + ClipToRange(&result.write_buffer_size, 64<<10, 1<<30); + ClipToRange(&result.block_size, 1<<10, 4<<20); + if (result.info_log == NULL) { + // Open a log file in the same directory as the db + src.env->CreateDir(dbname); // In case it does not exist + src.env->RenameFile(InfoLogFileName(dbname), OldInfoLogFileName(dbname)); + Status s = src.env->NewLogger(InfoLogFileName(dbname), &result.info_log); + if (!s.ok()) { + // No place suitable for logging + result.info_log = NULL; + } + } + if (result.block_cache == NULL) { + result.block_cache = NewLRUCache(8 << 20); + } + return result; +} + +DBImpl::DBImpl(const Options& raw_options, const std::string& dbname) + : env_(raw_options.env), + internal_comparator_(raw_options.comparator), + internal_filter_policy_(raw_options.filter_policy), + options_(SanitizeOptions(dbname, &internal_comparator_, + &internal_filter_policy_, raw_options)), + owns_info_log_(options_.info_log != raw_options.info_log), + owns_cache_(options_.block_cache != raw_options.block_cache), + dbname_(dbname), + db_lock_(NULL), + shutting_down_(NULL), + bg_cv_(&mutex_), + mem_(new MemTable(internal_comparator_)), + imm_(NULL), + logfile_(NULL), + logfile_number_(0), + log_(NULL), + seed_(0), + tmp_batch_(new WriteBatch), + bg_compaction_scheduled_(false), + manual_compaction_(NULL) { + mem_->Ref(); + has_imm_.Release_Store(NULL); + + // Reserve ten files or so for other uses and give the rest to TableCache. + const int table_cache_size = options_.max_open_files - kNumNonTableCacheFiles; + table_cache_ = new TableCache(dbname_, &options_, table_cache_size); + + versions_ = new VersionSet(dbname_, &options_, table_cache_, + &internal_comparator_); +} + +DBImpl::~DBImpl() { + // Wait for background work to finish + mutex_.Lock(); + shutting_down_.Release_Store(this); // Any non-NULL value is ok + while (bg_compaction_scheduled_) { + bg_cv_.Wait(); + } + mutex_.Unlock(); + + if (db_lock_ != NULL) { + env_->UnlockFile(db_lock_); + } + + delete versions_; + if (mem_ != NULL) mem_->Unref(); + if (imm_ != NULL) imm_->Unref(); + delete tmp_batch_; + delete log_; + delete logfile_; + delete table_cache_; + + if (owns_info_log_) { + delete options_.info_log; + } + if (owns_cache_) { + delete options_.block_cache; + } +} + +Status DBImpl::NewDB() { + VersionEdit new_db; + new_db.SetComparatorName(user_comparator()->Name()); + new_db.SetLogNumber(0); + new_db.SetNextFile(2); + new_db.SetLastSequence(0); + + const std::string manifest = DescriptorFileName(dbname_, 1); + WritableFile* file; + Status s = env_->NewWritableFile(manifest, &file); + if (!s.ok()) { + return s; + } + { + log::Writer log(file); + std::string record; + new_db.EncodeTo(&record); + s = log.AddRecord(record); + if (s.ok()) { + s = file->Close(); + } + } + delete file; + if (s.ok()) { + // Make "CURRENT" file that points to the new manifest file. + s = SetCurrentFile(env_, dbname_, 1); + } else { + env_->DeleteFile(manifest); + } + return s; +} + +void DBImpl::MaybeIgnoreError(Status* s) const { + if (s->ok() || options_.paranoid_checks) { + // No change needed + } else { + Log(options_.info_log, "Ignoring error %s", s->ToString().c_str()); + *s = Status::OK(); + } +} + +void DBImpl::DeleteObsoleteFiles() { + if (!bg_error_.ok()) { + // After a background error, we don't know whether a new version may + // or may not have been committed, so we cannot safely garbage collect. + return; + } + + // Make a set of all of the live files + std::set live = pending_outputs_; + versions_->AddLiveFiles(&live); + + std::vector filenames; + env_->GetChildren(dbname_, &filenames); // Ignoring errors on purpose + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type)) { + bool keep = true; + switch (type) { + case kLogFile: + keep = ((number >= versions_->LogNumber()) || + (number == versions_->PrevLogNumber())); + break; + case kDescriptorFile: + // Keep my manifest file, and any newer incarnations' + // (in case there is a race that allows other incarnations) + keep = (number >= versions_->ManifestFileNumber()); + break; + case kTableFile: + keep = (live.find(number) != live.end()); + break; + case kTempFile: + // Any temp files that are currently being written to must + // be recorded in pending_outputs_, which is inserted into "live" + keep = (live.find(number) != live.end()); + break; + case kCurrentFile: + case kDBLockFile: + case kInfoLogFile: + keep = true; + break; + } + + if (!keep) { + if (type == kTableFile) { + table_cache_->Evict(number); + } + Log(options_.info_log, "Delete type=%d #%lld\n", + int(type), + static_cast(number)); + env_->DeleteFile(dbname_ + "/" + filenames[i]); + } + } + } +} + +Status DBImpl::Recover(VersionEdit* edit) { + mutex_.AssertHeld(); + + // Ignore error from CreateDir since the creation of the DB is + // committed only when the descriptor is created, and this directory + // may already exist from a previous failed creation attempt. + env_->CreateDir(dbname_); + assert(db_lock_ == NULL); + Status s = env_->LockFile(LockFileName(dbname_), &db_lock_); + if (!s.ok()) { + return s; + } + + if (!env_->FileExists(CurrentFileName(dbname_))) { + if (options_.create_if_missing) { + s = NewDB(); + if (!s.ok()) { + return s; + } + } else { + return Status::InvalidArgument( + dbname_, "does not exist (create_if_missing is false)"); + } + } else { + if (options_.error_if_exists) { + return Status::InvalidArgument( + dbname_, "exists (error_if_exists is true)"); + } + } + + s = versions_->Recover(); + if (s.ok()) { + SequenceNumber max_sequence(0); + + // Recover from all newer log files than the ones named in the + // descriptor (new log files may have been added by the previous + // incarnation without registering them in the descriptor). + // + // Note that PrevLogNumber() is no longer used, but we pay + // attention to it in case we are recovering a database + // produced by an older version of leveldb. + const uint64_t min_log = versions_->LogNumber(); + const uint64_t prev_log = versions_->PrevLogNumber(); + std::vector filenames; + s = env_->GetChildren(dbname_, &filenames); + if (!s.ok()) { + return s; + } + std::set expected; + versions_->AddLiveFiles(&expected); + uint64_t number; + FileType type; + std::vector logs; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type)) { + expected.erase(number); + if (type == kLogFile && ((number >= min_log) || (number == prev_log))) + logs.push_back(number); + } + } + if (!expected.empty()) { + char buf[50]; + snprintf(buf, sizeof(buf), "%d missing files; e.g.", + static_cast(expected.size())); + return Status::Corruption(buf, TableFileName(dbname_, *(expected.begin()))); + } + + // Recover in the order in which the logs were generated + std::sort(logs.begin(), logs.end()); + for (size_t i = 0; i < logs.size(); i++) { + s = RecoverLogFile(logs[i], edit, &max_sequence); + + // The previous incarnation may not have written any MANIFEST + // records after allocating this log number. So we manually + // update the file number allocation counter in VersionSet. + versions_->MarkFileNumberUsed(logs[i]); + } + + if (s.ok()) { + if (versions_->LastSequence() < max_sequence) { + versions_->SetLastSequence(max_sequence); + } + } + } + + return s; +} + +Status DBImpl::RecoverLogFile(uint64_t log_number, + VersionEdit* edit, + SequenceNumber* max_sequence) { + struct LogReporter : public log::Reader::Reporter { + Env* env; + Logger* info_log; + const char* fname; + Status* status; // NULL if options_.paranoid_checks==false + virtual void Corruption(size_t bytes, const Status& s) { + Log(info_log, "%s%s: dropping %d bytes; %s", + (this->status == NULL ? "(ignoring error) " : ""), + fname, static_cast(bytes), s.ToString().c_str()); + if (this->status != NULL && this->status->ok()) *this->status = s; + } + }; + + mutex_.AssertHeld(); + + // Open the log file + std::string fname = LogFileName(dbname_, log_number); + SequentialFile* file; + Status status = env_->NewSequentialFile(fname, &file); + if (!status.ok()) { + MaybeIgnoreError(&status); + return status; + } + + // Create the log reader. + LogReporter reporter; + reporter.env = env_; + reporter.info_log = options_.info_log; + reporter.fname = fname.c_str(); + reporter.status = (options_.paranoid_checks ? &status : NULL); + // We intentially make log::Reader do checksumming even if + // paranoid_checks==false so that corruptions cause entire commits + // to be skipped instead of propagating bad information (like overly + // large sequence numbers). + log::Reader reader(file, &reporter, true/*checksum*/, + 0/*initial_offset*/); + Log(options_.info_log, "Recovering log #%llu", + (unsigned long long) log_number); + + // Read all the records and add to a memtable + std::string scratch; + Slice record; + WriteBatch batch; + MemTable* mem = NULL; + while (reader.ReadRecord(&record, &scratch) && + status.ok()) { + if (record.size() < 12) { + reporter.Corruption( + record.size(), Status::Corruption("log record too small")); + continue; + } + WriteBatchInternal::SetContents(&batch, record); + + if (mem == NULL) { + mem = new MemTable(internal_comparator_); + mem->Ref(); + } + status = WriteBatchInternal::InsertInto(&batch, mem); + MaybeIgnoreError(&status); + if (!status.ok()) { + break; + } + const SequenceNumber last_seq = + WriteBatchInternal::Sequence(&batch) + + WriteBatchInternal::Count(&batch) - 1; + if (last_seq > *max_sequence) { + *max_sequence = last_seq; + } + + if (mem->ApproximateMemoryUsage() > options_.write_buffer_size) { + status = WriteLevel0Table(mem, edit, NULL); + if (!status.ok()) { + // Reflect errors immediately so that conditions like full + // file-systems cause the DB::Open() to fail. + break; + } + mem->Unref(); + mem = NULL; + } + } + + if (status.ok() && mem != NULL) { + status = WriteLevel0Table(mem, edit, NULL); + // Reflect errors immediately so that conditions like full + // file-systems cause the DB::Open() to fail. + } + + if (mem != NULL) mem->Unref(); + delete file; + return status; +} + +Status DBImpl::WriteLevel0Table(MemTable* mem, VersionEdit* edit, + Version* base) { + mutex_.AssertHeld(); + const uint64_t start_micros = env_->NowMicros(); + FileMetaData meta; + meta.number = versions_->NewFileNumber(); + pending_outputs_.insert(meta.number); + Iterator* iter = mem->NewIterator(); + Log(options_.info_log, "Level-0 table #%llu: started", + (unsigned long long) meta.number); + + Status s; + { + mutex_.Unlock(); + s = BuildTable(dbname_, env_, options_, table_cache_, iter, &meta); + mutex_.Lock(); + } + + Log(options_.info_log, "Level-0 table #%llu: %lld bytes %s", + (unsigned long long) meta.number, + (unsigned long long) meta.file_size, + s.ToString().c_str()); + delete iter; + pending_outputs_.erase(meta.number); + + + // Note that if file_size is zero, the file has been deleted and + // should not be added to the manifest. + int level = 0; + if (s.ok() && meta.file_size > 0) { + const Slice min_user_key = meta.smallest.user_key(); + const Slice max_user_key = meta.largest.user_key(); + if (base != NULL) { + level = base->PickLevelForMemTableOutput(min_user_key, max_user_key); + } + edit->AddFile(level, meta.number, meta.file_size, + meta.smallest, meta.largest); + } + + CompactionStats stats; + stats.micros = env_->NowMicros() - start_micros; + stats.bytes_written = meta.file_size; + stats_[level].Add(stats); + return s; +} + +void DBImpl::CompactMemTable() { + mutex_.AssertHeld(); + assert(imm_ != NULL); + + // Save the contents of the memtable as a new Table + VersionEdit edit; + Version* base = versions_->current(); + base->Ref(); + Status s = WriteLevel0Table(imm_, &edit, base); + base->Unref(); + + if (s.ok() && shutting_down_.Acquire_Load()) { + s = Status::IOError("Deleting DB during memtable compaction"); + } + + // Replace immutable memtable with the generated Table + if (s.ok()) { + edit.SetPrevLogNumber(0); + edit.SetLogNumber(logfile_number_); // Earlier logs no longer needed + s = versions_->LogAndApply(&edit, &mutex_); + } + + if (s.ok()) { + // Commit to the new state + imm_->Unref(); + imm_ = NULL; + has_imm_.Release_Store(NULL); + DeleteObsoleteFiles(); + } else { + RecordBackgroundError(s); + } +} + +void DBImpl::CompactRange(const Slice* begin, const Slice* end) { + int max_level_with_files = 1; + { + MutexLock l(&mutex_); + Version* base = versions_->current(); + for (int level = 1; level < config::kNumLevels; level++) { + if (base->OverlapInLevel(level, begin, end)) { + max_level_with_files = level; + } + } + } + TEST_CompactMemTable(); // TODO(sanjay): Skip if memtable does not overlap + for (int level = 0; level < max_level_with_files; level++) { + TEST_CompactRange(level, begin, end); + } +} + +void DBImpl::TEST_CompactRange(int level, const Slice* begin,const Slice* end) { + assert(level >= 0); + assert(level + 1 < config::kNumLevels); + + InternalKey begin_storage, end_storage; + + ManualCompaction manual; + manual.level = level; + manual.done = false; + if (begin == NULL) { + manual.begin = NULL; + } else { + begin_storage = InternalKey(*begin, kMaxSequenceNumber, kValueTypeForSeek); + manual.begin = &begin_storage; + } + if (end == NULL) { + manual.end = NULL; + } else { + end_storage = InternalKey(*end, 0, static_cast(0)); + manual.end = &end_storage; + } + + MutexLock l(&mutex_); + while (!manual.done && !shutting_down_.Acquire_Load() && bg_error_.ok()) { + if (manual_compaction_ == NULL) { // Idle + manual_compaction_ = &manual; + MaybeScheduleCompaction(); + } else { // Running either my compaction or another compaction. + bg_cv_.Wait(); + } + } + if (manual_compaction_ == &manual) { + // Cancel my manual compaction since we aborted early for some reason. + manual_compaction_ = NULL; + } +} + +Status DBImpl::TEST_CompactMemTable() { + // NULL batch means just wait for earlier writes to be done + Status s = Write(WriteOptions(), NULL); + if (s.ok()) { + // Wait until the compaction completes + MutexLock l(&mutex_); + while (imm_ != NULL && bg_error_.ok()) { + bg_cv_.Wait(); + } + if (imm_ != NULL) { + s = bg_error_; + } + } + return s; +} + +void DBImpl::RecordBackgroundError(const Status& s) { + mutex_.AssertHeld(); + if (bg_error_.ok()) { + bg_error_ = s; + bg_cv_.SignalAll(); + } +} + +void DBImpl::MaybeScheduleCompaction() { + mutex_.AssertHeld(); + if (bg_compaction_scheduled_) { + // Already scheduled + } else if (shutting_down_.Acquire_Load()) { + // DB is being deleted; no more background compactions + } else if (!bg_error_.ok()) { + // Already got an error; no more changes + } else if (imm_ == NULL && + manual_compaction_ == NULL && + !versions_->NeedsCompaction()) { + // No work to be done + } else { + bg_compaction_scheduled_ = true; + env_->Schedule(&DBImpl::BGWork, this); + } +} + +void DBImpl::BGWork(void* db) { + reinterpret_cast(db)->BackgroundCall(); +} + +void DBImpl::BackgroundCall() { + MutexLock l(&mutex_); + assert(bg_compaction_scheduled_); + if (shutting_down_.Acquire_Load()) { + // No more background work when shutting down. + } else if (!bg_error_.ok()) { + // No more background work after a background error. + } else { + BackgroundCompaction(); + } + + bg_compaction_scheduled_ = false; + + // Previous compaction may have produced too many files in a level, + // so reschedule another compaction if needed. + MaybeScheduleCompaction(); + bg_cv_.SignalAll(); +} + +void DBImpl::BackgroundCompaction() { + mutex_.AssertHeld(); + + if (imm_ != NULL) { + CompactMemTable(); + return; + } + + Compaction* c; + bool is_manual = (manual_compaction_ != NULL); + InternalKey manual_end; + if (is_manual) { + ManualCompaction* m = manual_compaction_; + c = versions_->CompactRange(m->level, m->begin, m->end); + m->done = (c == NULL); + if (c != NULL) { + manual_end = c->input(0, c->num_input_files(0) - 1)->largest; + } + Log(options_.info_log, + "Manual compaction at level-%d from %s .. %s; will stop at %s\n", + m->level, + (m->begin ? m->begin->DebugString().c_str() : "(begin)"), + (m->end ? m->end->DebugString().c_str() : "(end)"), + (m->done ? "(end)" : manual_end.DebugString().c_str())); + } else { + c = versions_->PickCompaction(); + } + + Status status; + if (c == NULL) { + // Nothing to do + } else if (!is_manual && c->IsTrivialMove()) { + // Move file to next level + assert(c->num_input_files(0) == 1); + FileMetaData* f = c->input(0, 0); + c->edit()->DeleteFile(c->level(), f->number); + c->edit()->AddFile(c->level() + 1, f->number, f->file_size, + f->smallest, f->largest); + status = versions_->LogAndApply(c->edit(), &mutex_); + if (!status.ok()) { + RecordBackgroundError(status); + } + VersionSet::LevelSummaryStorage tmp; + Log(options_.info_log, "Moved #%lld to level-%d %lld bytes %s: %s\n", + static_cast(f->number), + c->level() + 1, + static_cast(f->file_size), + status.ToString().c_str(), + versions_->LevelSummary(&tmp)); + } else { + CompactionState* compact = new CompactionState(c); + status = DoCompactionWork(compact); + if (!status.ok()) { + RecordBackgroundError(status); + } + CleanupCompaction(compact); + c->ReleaseInputs(); + DeleteObsoleteFiles(); + } + delete c; + + if (status.ok()) { + // Done + } else if (shutting_down_.Acquire_Load()) { + // Ignore compaction errors found during shutting down + } else { + Log(options_.info_log, + "Compaction error: %s", status.ToString().c_str()); + } + + if (is_manual) { + ManualCompaction* m = manual_compaction_; + if (!status.ok()) { + m->done = true; + } + if (!m->done) { + // We only compacted part of the requested range. Update *m + // to the range that is left to be compacted. + m->tmp_storage = manual_end; + m->begin = &m->tmp_storage; + } + manual_compaction_ = NULL; + } +} + +void DBImpl::CleanupCompaction(CompactionState* compact) { + mutex_.AssertHeld(); + if (compact->builder != NULL) { + // May happen if we get a shutdown call in the middle of compaction + compact->builder->Abandon(); + delete compact->builder; + } else { + assert(compact->outfile == NULL); + } + delete compact->outfile; + for (size_t i = 0; i < compact->outputs.size(); i++) { + const CompactionState::Output& out = compact->outputs[i]; + pending_outputs_.erase(out.number); + } + delete compact; +} + +Status DBImpl::OpenCompactionOutputFile(CompactionState* compact) { + assert(compact != NULL); + assert(compact->builder == NULL); + uint64_t file_number; + { + mutex_.Lock(); + file_number = versions_->NewFileNumber(); + pending_outputs_.insert(file_number); + CompactionState::Output out; + out.number = file_number; + out.smallest.Clear(); + out.largest.Clear(); + compact->outputs.push_back(out); + mutex_.Unlock(); + } + + // Make the output file + std::string fname = TableFileName(dbname_, file_number); + Status s = env_->NewWritableFile(fname, &compact->outfile); + if (s.ok()) { + compact->builder = new TableBuilder(options_, compact->outfile); + } + return s; +} + +Status DBImpl::FinishCompactionOutputFile(CompactionState* compact, + Iterator* input) { + assert(compact != NULL); + assert(compact->outfile != NULL); + assert(compact->builder != NULL); + + const uint64_t output_number = compact->current_output()->number; + assert(output_number != 0); + + // Check for iterator errors + Status s = input->status(); + const uint64_t current_entries = compact->builder->NumEntries(); + if (s.ok()) { + s = compact->builder->Finish(); + } else { + compact->builder->Abandon(); + } + const uint64_t current_bytes = compact->builder->FileSize(); + compact->current_output()->file_size = current_bytes; + compact->total_bytes += current_bytes; + delete compact->builder; + compact->builder = NULL; + + // Finish and check for file errors + if (s.ok()) { + s = compact->outfile->Sync(); + } + if (s.ok()) { + s = compact->outfile->Close(); + } + delete compact->outfile; + compact->outfile = NULL; + + if (s.ok() && current_entries > 0) { + // Verify that the table is usable + Iterator* iter = table_cache_->NewIterator(ReadOptions(), + output_number, + current_bytes); + s = iter->status(); + delete iter; + if (s.ok()) { + Log(options_.info_log, + "Generated table #%llu: %lld keys, %lld bytes", + (unsigned long long) output_number, + (unsigned long long) current_entries, + (unsigned long long) current_bytes); + } + } + return s; +} + + +Status DBImpl::InstallCompactionResults(CompactionState* compact) { + mutex_.AssertHeld(); + Log(options_.info_log, "Compacted %d@%d + %d@%d files => %lld bytes", + compact->compaction->num_input_files(0), + compact->compaction->level(), + compact->compaction->num_input_files(1), + compact->compaction->level() + 1, + static_cast(compact->total_bytes)); + + // Add compaction outputs + compact->compaction->AddInputDeletions(compact->compaction->edit()); + const int level = compact->compaction->level(); + for (size_t i = 0; i < compact->outputs.size(); i++) { + const CompactionState::Output& out = compact->outputs[i]; + compact->compaction->edit()->AddFile( + level + 1, + out.number, out.file_size, out.smallest, out.largest); + } + return versions_->LogAndApply(compact->compaction->edit(), &mutex_); +} + +Status DBImpl::DoCompactionWork(CompactionState* compact) { + const uint64_t start_micros = env_->NowMicros(); + int64_t imm_micros = 0; // Micros spent doing imm_ compactions + + Log(options_.info_log, "Compacting %d@%d + %d@%d files", + compact->compaction->num_input_files(0), + compact->compaction->level(), + compact->compaction->num_input_files(1), + compact->compaction->level() + 1); + + assert(versions_->NumLevelFiles(compact->compaction->level()) > 0); + assert(compact->builder == NULL); + assert(compact->outfile == NULL); + if (snapshots_.empty()) { + compact->smallest_snapshot = versions_->LastSequence(); + } else { + compact->smallest_snapshot = snapshots_.oldest()->number_; + } + + // Release mutex while we're actually doing the compaction work + mutex_.Unlock(); + + Iterator* input = versions_->MakeInputIterator(compact->compaction); + input->SeekToFirst(); + Status status; + ParsedInternalKey ikey; + std::string current_user_key; + bool has_current_user_key = false; + SequenceNumber last_sequence_for_key = kMaxSequenceNumber; + for (; input->Valid() && !shutting_down_.Acquire_Load(); ) { + // Prioritize immutable compaction work + if (has_imm_.NoBarrier_Load() != NULL) { + const uint64_t imm_start = env_->NowMicros(); + mutex_.Lock(); + if (imm_ != NULL) { + CompactMemTable(); + bg_cv_.SignalAll(); // Wakeup MakeRoomForWrite() if necessary + } + mutex_.Unlock(); + imm_micros += (env_->NowMicros() - imm_start); + } + + Slice key = input->key(); + if (compact->compaction->ShouldStopBefore(key) && + compact->builder != NULL) { + status = FinishCompactionOutputFile(compact, input); + if (!status.ok()) { + break; + } + } + + // Handle key/value, add to state, etc. + bool drop = false; + if (!ParseInternalKey(key, &ikey)) { + // Do not hide error keys + current_user_key.clear(); + has_current_user_key = false; + last_sequence_for_key = kMaxSequenceNumber; + } else { + if (!has_current_user_key || + user_comparator()->Compare(ikey.user_key, + Slice(current_user_key)) != 0) { + // First occurrence of this user key + current_user_key.assign(ikey.user_key.data(), ikey.user_key.size()); + has_current_user_key = true; + last_sequence_for_key = kMaxSequenceNumber; + } + + if (last_sequence_for_key <= compact->smallest_snapshot) { + // Hidden by an newer entry for same user key + drop = true; // (A) + } else if (ikey.type == kTypeDeletion && + ikey.sequence <= compact->smallest_snapshot && + compact->compaction->IsBaseLevelForKey(ikey.user_key)) { + // For this user key: + // (1) there is no data in higher levels + // (2) data in lower levels will have larger sequence numbers + // (3) data in layers that are being compacted here and have + // smaller sequence numbers will be dropped in the next + // few iterations of this loop (by rule (A) above). + // Therefore this deletion marker is obsolete and can be dropped. + drop = true; + } + + last_sequence_for_key = ikey.sequence; + } +#if 0 + Log(options_.info_log, + " Compact: %s, seq %d, type: %d %d, drop: %d, is_base: %d, " + "%d smallest_snapshot: %d", + ikey.user_key.ToString().c_str(), + (int)ikey.sequence, ikey.type, kTypeValue, drop, + compact->compaction->IsBaseLevelForKey(ikey.user_key), + (int)last_sequence_for_key, (int)compact->smallest_snapshot); +#endif + + if (!drop) { + // Open output file if necessary + if (compact->builder == NULL) { + status = OpenCompactionOutputFile(compact); + if (!status.ok()) { + break; + } + } + if (compact->builder->NumEntries() == 0) { + compact->current_output()->smallest.DecodeFrom(key); + } + compact->current_output()->largest.DecodeFrom(key); + compact->builder->Add(key, input->value()); + + // Close output file if it is big enough + if (compact->builder->FileSize() >= + compact->compaction->MaxOutputFileSize()) { + status = FinishCompactionOutputFile(compact, input); + if (!status.ok()) { + break; + } + } + } + + input->Next(); + } + + if (status.ok() && shutting_down_.Acquire_Load()) { + status = Status::IOError("Deleting DB during compaction"); + } + if (status.ok() && compact->builder != NULL) { + status = FinishCompactionOutputFile(compact, input); + } + if (status.ok()) { + status = input->status(); + } + delete input; + input = NULL; + + CompactionStats stats; + stats.micros = env_->NowMicros() - start_micros - imm_micros; + for (int which = 0; which < 2; which++) { + for (int i = 0; i < compact->compaction->num_input_files(which); i++) { + stats.bytes_read += compact->compaction->input(which, i)->file_size; + } + } + for (size_t i = 0; i < compact->outputs.size(); i++) { + stats.bytes_written += compact->outputs[i].file_size; + } + + mutex_.Lock(); + stats_[compact->compaction->level() + 1].Add(stats); + + if (status.ok()) { + status = InstallCompactionResults(compact); + } + if (!status.ok()) { + RecordBackgroundError(status); + } + VersionSet::LevelSummaryStorage tmp; + Log(options_.info_log, + "compacted to: %s", versions_->LevelSummary(&tmp)); + return status; +} + +namespace { +struct IterState { + port::Mutex* mu; + Version* version; + MemTable* mem; + MemTable* imm; +}; + +static void CleanupIteratorState(void* arg1, void* arg2) { + IterState* state = reinterpret_cast(arg1); + state->mu->Lock(); + state->mem->Unref(); + if (state->imm != NULL) state->imm->Unref(); + state->version->Unref(); + state->mu->Unlock(); + delete state; +} +} // namespace + +Iterator* DBImpl::NewInternalIterator(const ReadOptions& options, + SequenceNumber* latest_snapshot, + uint32_t* seed) { + IterState* cleanup = new IterState; + mutex_.Lock(); + *latest_snapshot = versions_->LastSequence(); + + // Collect together all needed child iterators + std::vector list; + list.push_back(mem_->NewIterator()); + mem_->Ref(); + if (imm_ != NULL) { + list.push_back(imm_->NewIterator()); + imm_->Ref(); + } + versions_->current()->AddIterators(options, &list); + Iterator* internal_iter = + NewMergingIterator(&internal_comparator_, &list[0], list.size()); + versions_->current()->Ref(); + + cleanup->mu = &mutex_; + cleanup->mem = mem_; + cleanup->imm = imm_; + cleanup->version = versions_->current(); + internal_iter->RegisterCleanup(CleanupIteratorState, cleanup, NULL); + + *seed = ++seed_; + mutex_.Unlock(); + return internal_iter; +} + +Iterator* DBImpl::TEST_NewInternalIterator() { + SequenceNumber ignored; + uint32_t ignored_seed; + return NewInternalIterator(ReadOptions(), &ignored, &ignored_seed); +} + +int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes() { + MutexLock l(&mutex_); + return versions_->MaxNextLevelOverlappingBytes(); +} + +Status DBImpl::Get(const ReadOptions& options, + const Slice& key, + std::string* value) { + Status s; + MutexLock l(&mutex_); + SequenceNumber snapshot; + if (options.snapshot != NULL) { + snapshot = reinterpret_cast(options.snapshot)->number_; + } else { + snapshot = versions_->LastSequence(); + } + + MemTable* mem = mem_; + MemTable* imm = imm_; + Version* current = versions_->current(); + mem->Ref(); + if (imm != NULL) imm->Ref(); + current->Ref(); + + bool have_stat_update = false; + Version::GetStats stats; + + // Unlock while reading from files and memtables + { + mutex_.Unlock(); + // First look in the memtable, then in the immutable memtable (if any). + LookupKey lkey(key, snapshot); + if (mem->Get(lkey, value, &s)) { + // Done + } else if (imm != NULL && imm->Get(lkey, value, &s)) { + // Done + } else { + s = current->Get(options, lkey, value, &stats); + have_stat_update = true; + } + mutex_.Lock(); + } + + if (have_stat_update && current->UpdateStats(stats)) { + MaybeScheduleCompaction(); + } + mem->Unref(); + if (imm != NULL) imm->Unref(); + current->Unref(); + return s; +} + +Iterator* DBImpl::NewIterator(const ReadOptions& options) { + SequenceNumber latest_snapshot; + uint32_t seed; + Iterator* iter = NewInternalIterator(options, &latest_snapshot, &seed); + return NewDBIterator( + this, user_comparator(), iter, + (options.snapshot != NULL + ? reinterpret_cast(options.snapshot)->number_ + : latest_snapshot), + seed); +} + +void DBImpl::RecordReadSample(Slice key) { + MutexLock l(&mutex_); + if (versions_->current()->RecordReadSample(key)) { + MaybeScheduleCompaction(); + } +} + +const Snapshot* DBImpl::GetSnapshot() { + MutexLock l(&mutex_); + return snapshots_.New(versions_->LastSequence()); +} + +void DBImpl::ReleaseSnapshot(const Snapshot* s) { + MutexLock l(&mutex_); + snapshots_.Delete(reinterpret_cast(s)); +} + +// Convenience methods +Status DBImpl::Put(const WriteOptions& o, const Slice& key, const Slice& val) { + return DB::Put(o, key, val); +} + +Status DBImpl::Delete(const WriteOptions& options, const Slice& key) { + return DB::Delete(options, key); +} + +Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) { + Writer w(&mutex_); + w.batch = my_batch; + w.sync = options.sync; + w.done = false; + + MutexLock l(&mutex_); + writers_.push_back(&w); + while (!w.done && &w != writers_.front()) { + w.cv.Wait(); + } + if (w.done) { + return w.status; + } + + // May temporarily unlock and wait. + Status status = MakeRoomForWrite(my_batch == NULL); + uint64_t last_sequence = versions_->LastSequence(); + Writer* last_writer = &w; + if (status.ok() && my_batch != NULL) { // NULL batch is for compactions + WriteBatch* updates = BuildBatchGroup(&last_writer); + WriteBatchInternal::SetSequence(updates, last_sequence + 1); + last_sequence += WriteBatchInternal::Count(updates); + + // Add to log and apply to memtable. We can release the lock + // during this phase since &w is currently responsible for logging + // and protects against concurrent loggers and concurrent writes + // into mem_. + { + mutex_.Unlock(); + status = log_->AddRecord(WriteBatchInternal::Contents(updates)); + bool sync_error = false; + if (status.ok() && options.sync) { + status = logfile_->Sync(); + if (!status.ok()) { + sync_error = true; + } + } + if (status.ok()) { + status = WriteBatchInternal::InsertInto(updates, mem_); + } + mutex_.Lock(); + if (sync_error) { + // The state of the log file is indeterminate: the log record we + // just added may or may not show up when the DB is re-opened. + // So we force the DB into a mode where all future writes fail. + RecordBackgroundError(status); + } + } + if (updates == tmp_batch_) tmp_batch_->Clear(); + + versions_->SetLastSequence(last_sequence); + } + + while (true) { + Writer* ready = writers_.front(); + writers_.pop_front(); + if (ready != &w) { + ready->status = status; + ready->done = true; + ready->cv.Signal(); + } + if (ready == last_writer) break; + } + + // Notify new head of write queue + if (!writers_.empty()) { + writers_.front()->cv.Signal(); + } + + return status; +} + +// REQUIRES: Writer list must be non-empty +// REQUIRES: First writer must have a non-NULL batch +WriteBatch* DBImpl::BuildBatchGroup(Writer** last_writer) { + assert(!writers_.empty()); + Writer* first = writers_.front(); + WriteBatch* result = first->batch; + assert(result != NULL); + + size_t size = WriteBatchInternal::ByteSize(first->batch); + + // Allow the group to grow up to a maximum size, but if the + // original write is small, limit the growth so we do not slow + // down the small write too much. + size_t max_size = 1 << 20; + if (size <= (128<<10)) { + max_size = size + (128<<10); + } + + *last_writer = first; + std::deque::iterator iter = writers_.begin(); + ++iter; // Advance past "first" + for (; iter != writers_.end(); ++iter) { + Writer* w = *iter; + if (w->sync && !first->sync) { + // Do not include a sync write into a batch handled by a non-sync write. + break; + } + + if (w->batch != NULL) { + size += WriteBatchInternal::ByteSize(w->batch); + if (size > max_size) { + // Do not make batch too big + break; + } + + // Append to *reuslt + if (result == first->batch) { + // Switch to temporary batch instead of disturbing caller's batch + result = tmp_batch_; + assert(WriteBatchInternal::Count(result) == 0); + WriteBatchInternal::Append(result, first->batch); + } + WriteBatchInternal::Append(result, w->batch); + } + *last_writer = w; + } + return result; +} + +// REQUIRES: mutex_ is held +// REQUIRES: this thread is currently at the front of the writer queue +Status DBImpl::MakeRoomForWrite(bool force) { + mutex_.AssertHeld(); + assert(!writers_.empty()); + bool allow_delay = !force; + Status s; + while (true) { + if (!bg_error_.ok()) { + // Yield previous error + s = bg_error_; + break; + } else if ( + allow_delay && + versions_->NumLevelFiles(0) >= config::kL0_SlowdownWritesTrigger) { + // We are getting close to hitting a hard limit on the number of + // L0 files. Rather than delaying a single write by several + // seconds when we hit the hard limit, start delaying each + // individual write by 1ms to reduce latency variance. Also, + // this delay hands over some CPU to the compaction thread in + // case it is sharing the same core as the writer. + mutex_.Unlock(); + env_->SleepForMicroseconds(1000); + allow_delay = false; // Do not delay a single write more than once + mutex_.Lock(); + } else if (!force && + (mem_->ApproximateMemoryUsage() <= options_.write_buffer_size)) { + // There is room in current memtable + break; + } else if (imm_ != NULL) { + // We have filled up the current memtable, but the previous + // one is still being compacted, so we wait. + Log(options_.info_log, "Current memtable full; waiting...\n"); + bg_cv_.Wait(); + } else if (versions_->NumLevelFiles(0) >= config::kL0_StopWritesTrigger) { + // There are too many level-0 files. + Log(options_.info_log, "Too many L0 files; waiting...\n"); + bg_cv_.Wait(); + } else { + // Attempt to switch to a new memtable and trigger compaction of old + assert(versions_->PrevLogNumber() == 0); + uint64_t new_log_number = versions_->NewFileNumber(); + WritableFile* lfile = NULL; + s = env_->NewWritableFile(LogFileName(dbname_, new_log_number), &lfile); + if (!s.ok()) { + // Avoid chewing through file number space in a tight loop. + versions_->ReuseFileNumber(new_log_number); + break; + } + delete log_; + delete logfile_; + logfile_ = lfile; + logfile_number_ = new_log_number; + log_ = new log::Writer(lfile); + imm_ = mem_; + has_imm_.Release_Store(imm_); + mem_ = new MemTable(internal_comparator_); + mem_->Ref(); + force = false; // Do not force another compaction if have room + MaybeScheduleCompaction(); + } + } + return s; +} + +bool DBImpl::GetProperty(const Slice& property, std::string* value) { + value->clear(); + + MutexLock l(&mutex_); + Slice in = property; + Slice prefix("leveldb."); + if (!in.starts_with(prefix)) return false; + in.remove_prefix(prefix.size()); + + if (in.starts_with("num-files-at-level")) { + in.remove_prefix(strlen("num-files-at-level")); + uint64_t level; + bool ok = ConsumeDecimalNumber(&in, &level) && in.empty(); + if (!ok || level >= config::kNumLevels) { + return false; + } else { + char buf[100]; + snprintf(buf, sizeof(buf), "%d", + versions_->NumLevelFiles(static_cast(level))); + *value = buf; + return true; + } + } else if (in == "stats") { + char buf[200]; + snprintf(buf, sizeof(buf), + " Compactions\n" + "Level Files Size(MB) Time(sec) Read(MB) Write(MB)\n" + "--------------------------------------------------\n" + ); + value->append(buf); + for (int level = 0; level < config::kNumLevels; level++) { + int files = versions_->NumLevelFiles(level); + if (stats_[level].micros > 0 || files > 0) { + snprintf( + buf, sizeof(buf), + "%3d %8d %8.0f %9.0f %8.0f %9.0f\n", + level, + files, + versions_->NumLevelBytes(level) / 1048576.0, + stats_[level].micros / 1e6, + stats_[level].bytes_read / 1048576.0, + stats_[level].bytes_written / 1048576.0); + value->append(buf); + } + } + return true; + } else if (in == "sstables") { + *value = versions_->current()->DebugString(); + return true; + } + + return false; +} + +void DBImpl::GetApproximateSizes( + const Range* range, int n, + uint64_t* sizes) { + // TODO(opt): better implementation + Version* v; + { + MutexLock l(&mutex_); + versions_->current()->Ref(); + v = versions_->current(); + } + + for (int i = 0; i < n; i++) { + // Convert user_key into a corresponding internal key. + InternalKey k1(range[i].start, kMaxSequenceNumber, kValueTypeForSeek); + InternalKey k2(range[i].limit, kMaxSequenceNumber, kValueTypeForSeek); + uint64_t start = versions_->ApproximateOffsetOf(v, k1); + uint64_t limit = versions_->ApproximateOffsetOf(v, k2); + sizes[i] = (limit >= start ? limit - start : 0); + } + + { + MutexLock l(&mutex_); + v->Unref(); + } +} + +// Default implementations of convenience methods that subclasses of DB +// can call if they wish +Status DB::Put(const WriteOptions& opt, const Slice& key, const Slice& value) { + WriteBatch batch; + batch.Put(key, value); + return Write(opt, &batch); +} + +Status DB::Delete(const WriteOptions& opt, const Slice& key) { + WriteBatch batch; + batch.Delete(key); + return Write(opt, &batch); +} + +DB::~DB() { } + +Status DB::Open(const Options& options, const std::string& dbname, + DB** dbptr) { + *dbptr = NULL; + + DBImpl* impl = new DBImpl(options, dbname); + impl->mutex_.Lock(); + VersionEdit edit; + Status s = impl->Recover(&edit); // Handles create_if_missing, error_if_exists + if (s.ok()) { + uint64_t new_log_number = impl->versions_->NewFileNumber(); + WritableFile* lfile; + s = options.env->NewWritableFile(LogFileName(dbname, new_log_number), + &lfile); + if (s.ok()) { + edit.SetLogNumber(new_log_number); + impl->logfile_ = lfile; + impl->logfile_number_ = new_log_number; + impl->log_ = new log::Writer(lfile); + s = impl->versions_->LogAndApply(&edit, &impl->mutex_); + } + if (s.ok()) { + impl->DeleteObsoleteFiles(); + impl->MaybeScheduleCompaction(); + } + } + impl->mutex_.Unlock(); + if (s.ok()) { + *dbptr = impl; + } else { + delete impl; + } + return s; +} + +Snapshot::~Snapshot() { +} + +Status DestroyDB(const std::string& dbname, const Options& options) { + Env* env = options.env; + std::vector filenames; + // Ignore error in case directory does not exist + env->GetChildren(dbname, &filenames); + if (filenames.empty()) { + return Status::OK(); + } + + FileLock* lock; + const std::string lockname = LockFileName(dbname); + Status result = env->LockFile(lockname, &lock); + if (result.ok()) { + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) && + type != kDBLockFile) { // Lock file will be deleted at end + Status del = env->DeleteFile(dbname + "/" + filenames[i]); + if (result.ok() && !del.ok()) { + result = del; + } + } + } + env->UnlockFile(lock); // Ignore error since state is already gone + env->DeleteFile(lockname); + env->DeleteDir(dbname); // Ignore error in case dir contains other files + } + return result; +} + +} // namespace leveldb diff --git a/src/leveldb-lin/leveldb/db/db_impl.h b/src/leveldb-lin/leveldb/db/db_impl.h new file mode 100644 index 0000000..cfc9981 --- /dev/null +++ b/src/leveldb-lin/leveldb/db/db_impl.h @@ -0,0 +1,211 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_DB_IMPL_H_ +#define STORAGE_LEVELDB_DB_DB_IMPL_H_ + +#include +#include +#include "db/dbformat.h" +#include "db/log_writer.h" +#include "db/snapshot.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "port/port.h" +#include "port/thread_annotations.h" + +namespace leveldb { + +class MemTable; +class TableCache; +class Version; +class VersionEdit; +class VersionSet; + +class DBImpl : public DB { + public: + DBImpl(const Options& options, const std::string& dbname); + virtual ~DBImpl(); + + // Implementations of the DB interface + virtual Status Put(const WriteOptions&, const Slice& key, const Slice& value); + virtual Status Delete(const WriteOptions&, const Slice& key); + virtual Status Write(const WriteOptions& options, WriteBatch* updates); + virtual Status Get(const ReadOptions& options, + const Slice& key, + std::string* value); + virtual Iterator* NewIterator(const ReadOptions&); + virtual const Snapshot* GetSnapshot(); + virtual void ReleaseSnapshot(const Snapshot* snapshot); + virtual bool GetProperty(const Slice& property, std::string* value); + virtual void GetApproximateSizes(const Range* range, int n, uint64_t* sizes); + virtual void CompactRange(const Slice* begin, const Slice* end); + + // Extra methods (for testing) that are not in the public DB interface + + // Compact any files in the named level that overlap [*begin,*end] + void TEST_CompactRange(int level, const Slice* begin, const Slice* end); + + // Force current memtable contents to be compacted. + Status TEST_CompactMemTable(); + + // Return an internal iterator over the current state of the database. + // The keys of this iterator are internal keys (see format.h). + // The returned iterator should be deleted when no longer needed. + Iterator* TEST_NewInternalIterator(); + + // Return the maximum overlapping data (in bytes) at next level for any + // file at a level >= 1. + int64_t TEST_MaxNextLevelOverlappingBytes(); + + // Record a sample of bytes read at the specified internal key. + // Samples are taken approximately once every config::kReadBytesPeriod + // bytes. + void RecordReadSample(Slice key); + + private: + friend class DB; + struct CompactionState; + struct Writer; + + Iterator* NewInternalIterator(const ReadOptions&, + SequenceNumber* latest_snapshot, + uint32_t* seed); + + Status NewDB(); + + // Recover the descriptor from persistent storage. May do a significant + // amount of work to recover recently logged updates. Any changes to + // be made to the descriptor are added to *edit. + Status Recover(VersionEdit* edit) EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + void MaybeIgnoreError(Status* s) const; + + // Delete any unneeded files and stale in-memory entries. + void DeleteObsoleteFiles(); + + // Compact the in-memory write buffer to disk. Switches to a new + // log-file/memtable and writes a new descriptor iff successful. + // Errors are recorded in bg_error_. + void CompactMemTable() EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Status RecoverLogFile(uint64_t log_number, + VersionEdit* edit, + SequenceNumber* max_sequence) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Status WriteLevel0Table(MemTable* mem, VersionEdit* edit, Version* base) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Status MakeRoomForWrite(bool force /* compact even if there is room? */) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + WriteBatch* BuildBatchGroup(Writer** last_writer); + + void RecordBackgroundError(const Status& s); + + void MaybeScheduleCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_); + static void BGWork(void* db); + void BackgroundCall(); + void BackgroundCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_); + void CleanupCompaction(CompactionState* compact) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + Status DoCompactionWork(CompactionState* compact) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Status OpenCompactionOutputFile(CompactionState* compact); + Status FinishCompactionOutputFile(CompactionState* compact, Iterator* input); + Status InstallCompactionResults(CompactionState* compact) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Constant after construction + Env* const env_; + const InternalKeyComparator internal_comparator_; + const InternalFilterPolicy internal_filter_policy_; + const Options options_; // options_.comparator == &internal_comparator_ + bool owns_info_log_; + bool owns_cache_; + const std::string dbname_; + + // table_cache_ provides its own synchronization + TableCache* table_cache_; + + // Lock over the persistent DB state. Non-NULL iff successfully acquired. + FileLock* db_lock_; + + // State below is protected by mutex_ + port::Mutex mutex_; + port::AtomicPointer shutting_down_; + port::CondVar bg_cv_; // Signalled when background work finishes + MemTable* mem_; + MemTable* imm_; // Memtable being compacted + port::AtomicPointer has_imm_; // So bg thread can detect non-NULL imm_ + WritableFile* logfile_; + uint64_t logfile_number_; + log::Writer* log_; + uint32_t seed_; // For sampling. + + // Queue of writers. + std::deque writers_; + WriteBatch* tmp_batch_; + + SnapshotList snapshots_; + + // Set of table files to protect from deletion because they are + // part of ongoing compactions. + std::set pending_outputs_; + + // Has a background compaction been scheduled or is running? + bool bg_compaction_scheduled_; + + // Information for a manual compaction + struct ManualCompaction { + int level; + bool done; + const InternalKey* begin; // NULL means beginning of key range + const InternalKey* end; // NULL means end of key range + InternalKey tmp_storage; // Used to keep track of compaction progress + }; + ManualCompaction* manual_compaction_; + + VersionSet* versions_; + + // Have we encountered a background error in paranoid mode? + Status bg_error_; + + // Per level compaction stats. stats_[level] stores the stats for + // compactions that produced data for the specified "level". + struct CompactionStats { + int64_t micros; + int64_t bytes_read; + int64_t bytes_written; + + CompactionStats() : micros(0), bytes_read(0), bytes_written(0) { } + + void Add(const CompactionStats& c) { + this->micros += c.micros; + this->bytes_read += c.bytes_read; + this->bytes_written += c.bytes_written; + } + }; + CompactionStats stats_[config::kNumLevels]; + + // No copying allowed + DBImpl(const DBImpl&); + void operator=(const DBImpl&); + + const Comparator* user_comparator() const { + return internal_comparator_.user_comparator(); + } +}; + +// Sanitize db options. The caller should delete result.info_log if +// it is not equal to src.info_log. +extern Options SanitizeOptions(const std::string& db, + const InternalKeyComparator* icmp, + const InternalFilterPolicy* ipolicy, + const Options& src); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_DB_IMPL_H_ diff --git a/src/leveldb-lin/leveldb/db/db_iter.cc b/src/leveldb-lin/leveldb/db/db_iter.cc new file mode 100644 index 0000000..3b2035e --- /dev/null +++ b/src/leveldb-lin/leveldb/db/db_iter.cc @@ -0,0 +1,317 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/db_iter.h" + +#include "db/filename.h" +#include "db/db_impl.h" +#include "db/dbformat.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "port/port.h" +#include "util/logging.h" +#include "util/mutexlock.h" +#include "util/random.h" + +namespace leveldb { + +#if 0 +static void DumpInternalIter(Iterator* iter) { + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + ParsedInternalKey k; + if (!ParseInternalKey(iter->key(), &k)) { + fprintf(stderr, "Corrupt '%s'\n", EscapeString(iter->key()).c_str()); + } else { + fprintf(stderr, "@ '%s'\n", k.DebugString().c_str()); + } + } +} +#endif + +namespace { + +// Memtables and sstables that make the DB representation contain +// (userkey,seq,type) => uservalue entries. DBIter +// combines multiple entries for the same userkey found in the DB +// representation into a single entry while accounting for sequence +// numbers, deletion markers, overwrites, etc. +class DBIter: public Iterator { + public: + // Which direction is the iterator currently moving? + // (1) When moving forward, the internal iterator is positioned at + // the exact entry that yields this->key(), this->value() + // (2) When moving backwards, the internal iterator is positioned + // just before all entries whose user key == this->key(). + enum Direction { + kForward, + kReverse + }; + + DBIter(DBImpl* db, const Comparator* cmp, Iterator* iter, SequenceNumber s, + uint32_t seed) + : db_(db), + user_comparator_(cmp), + iter_(iter), + sequence_(s), + direction_(kForward), + valid_(false), + rnd_(seed), + bytes_counter_(RandomPeriod()) { + } + virtual ~DBIter() { + delete iter_; + } + virtual bool Valid() const { return valid_; } + virtual Slice key() const { + assert(valid_); + return (direction_ == kForward) ? ExtractUserKey(iter_->key()) : saved_key_; + } + virtual Slice value() const { + assert(valid_); + return (direction_ == kForward) ? iter_->value() : saved_value_; + } + virtual Status status() const { + if (status_.ok()) { + return iter_->status(); + } else { + return status_; + } + } + + virtual void Next(); + virtual void Prev(); + virtual void Seek(const Slice& target); + virtual void SeekToFirst(); + virtual void SeekToLast(); + + private: + void FindNextUserEntry(bool skipping, std::string* skip); + void FindPrevUserEntry(); + bool ParseKey(ParsedInternalKey* key); + + inline void SaveKey(const Slice& k, std::string* dst) { + dst->assign(k.data(), k.size()); + } + + inline void ClearSavedValue() { + if (saved_value_.capacity() > 1048576) { + std::string empty; + swap(empty, saved_value_); + } else { + saved_value_.clear(); + } + } + + // Pick next gap with average value of config::kReadBytesPeriod. + ssize_t RandomPeriod() { + return rnd_.Uniform(2*config::kReadBytesPeriod); + } + + DBImpl* db_; + const Comparator* const user_comparator_; + Iterator* const iter_; + SequenceNumber const sequence_; + + Status status_; + std::string saved_key_; // == current key when direction_==kReverse + std::string saved_value_; // == current raw value when direction_==kReverse + Direction direction_; + bool valid_; + + Random rnd_; + ssize_t bytes_counter_; + + // No copying allowed + DBIter(const DBIter&); + void operator=(const DBIter&); +}; + +inline bool DBIter::ParseKey(ParsedInternalKey* ikey) { + Slice k = iter_->key(); + ssize_t n = k.size() + iter_->value().size(); + bytes_counter_ -= n; + while (bytes_counter_ < 0) { + bytes_counter_ += RandomPeriod(); + db_->RecordReadSample(k); + } + if (!ParseInternalKey(k, ikey)) { + status_ = Status::Corruption("corrupted internal key in DBIter"); + return false; + } else { + return true; + } +} + +void DBIter::Next() { + assert(valid_); + + if (direction_ == kReverse) { // Switch directions? + direction_ = kForward; + // iter_ is pointing just before the entries for this->key(), + // so advance into the range of entries for this->key() and then + // use the normal skipping code below. + if (!iter_->Valid()) { + iter_->SeekToFirst(); + } else { + iter_->Next(); + } + if (!iter_->Valid()) { + valid_ = false; + saved_key_.clear(); + return; + } + // saved_key_ already contains the key to skip past. + } else { + // Store in saved_key_ the current key so we skip it below. + SaveKey(ExtractUserKey(iter_->key()), &saved_key_); + } + + FindNextUserEntry(true, &saved_key_); +} + +void DBIter::FindNextUserEntry(bool skipping, std::string* skip) { + // Loop until we hit an acceptable entry to yield + assert(iter_->Valid()); + assert(direction_ == kForward); + do { + ParsedInternalKey ikey; + if (ParseKey(&ikey) && ikey.sequence <= sequence_) { + switch (ikey.type) { + case kTypeDeletion: + // Arrange to skip all upcoming entries for this key since + // they are hidden by this deletion. + SaveKey(ikey.user_key, skip); + skipping = true; + break; + case kTypeValue: + if (skipping && + user_comparator_->Compare(ikey.user_key, *skip) <= 0) { + // Entry hidden + } else { + valid_ = true; + saved_key_.clear(); + return; + } + break; + } + } + iter_->Next(); + } while (iter_->Valid()); + saved_key_.clear(); + valid_ = false; +} + +void DBIter::Prev() { + assert(valid_); + + if (direction_ == kForward) { // Switch directions? + // iter_ is pointing at the current entry. Scan backwards until + // the key changes so we can use the normal reverse scanning code. + assert(iter_->Valid()); // Otherwise valid_ would have been false + SaveKey(ExtractUserKey(iter_->key()), &saved_key_); + while (true) { + iter_->Prev(); + if (!iter_->Valid()) { + valid_ = false; + saved_key_.clear(); + ClearSavedValue(); + return; + } + if (user_comparator_->Compare(ExtractUserKey(iter_->key()), + saved_key_) < 0) { + break; + } + } + direction_ = kReverse; + } + + FindPrevUserEntry(); +} + +void DBIter::FindPrevUserEntry() { + assert(direction_ == kReverse); + + ValueType value_type = kTypeDeletion; + if (iter_->Valid()) { + do { + ParsedInternalKey ikey; + if (ParseKey(&ikey) && ikey.sequence <= sequence_) { + if ((value_type != kTypeDeletion) && + user_comparator_->Compare(ikey.user_key, saved_key_) < 0) { + // We encountered a non-deleted value in entries for previous keys, + break; + } + value_type = ikey.type; + if (value_type == kTypeDeletion) { + saved_key_.clear(); + ClearSavedValue(); + } else { + Slice raw_value = iter_->value(); + if (saved_value_.capacity() > raw_value.size() + 1048576) { + std::string empty; + swap(empty, saved_value_); + } + SaveKey(ExtractUserKey(iter_->key()), &saved_key_); + saved_value_.assign(raw_value.data(), raw_value.size()); + } + } + iter_->Prev(); + } while (iter_->Valid()); + } + + if (value_type == kTypeDeletion) { + // End + valid_ = false; + saved_key_.clear(); + ClearSavedValue(); + direction_ = kForward; + } else { + valid_ = true; + } +} + +void DBIter::Seek(const Slice& target) { + direction_ = kForward; + ClearSavedValue(); + saved_key_.clear(); + AppendInternalKey( + &saved_key_, ParsedInternalKey(target, sequence_, kValueTypeForSeek)); + iter_->Seek(saved_key_); + if (iter_->Valid()) { + FindNextUserEntry(false, &saved_key_ /* temporary storage */); + } else { + valid_ = false; + } +} + +void DBIter::SeekToFirst() { + direction_ = kForward; + ClearSavedValue(); + iter_->SeekToFirst(); + if (iter_->Valid()) { + FindNextUserEntry(false, &saved_key_ /* temporary storage */); + } else { + valid_ = false; + } +} + +void DBIter::SeekToLast() { + direction_ = kReverse; + ClearSavedValue(); + iter_->SeekToLast(); + FindPrevUserEntry(); +} + +} // anonymous namespace + +Iterator* NewDBIterator( + DBImpl* db, + const Comparator* user_key_comparator, + Iterator* internal_iter, + SequenceNumber sequence, + uint32_t seed) { + return new DBIter(db, user_key_comparator, internal_iter, sequence, seed); +} + +} // namespace leveldb diff --git a/src/leveldb-lin/leveldb/db/db_iter.h b/src/leveldb-lin/leveldb/db/db_iter.h new file mode 100644 index 0000000..04927e9 --- /dev/null +++ b/src/leveldb-lin/leveldb/db/db_iter.h @@ -0,0 +1,28 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_DB_ITER_H_ +#define STORAGE_LEVELDB_DB_DB_ITER_H_ + +#include +#include "leveldb/db.h" +#include "db/dbformat.h" + +namespace leveldb { + +class DBImpl; + +// Return a new iterator that converts internal keys (yielded by +// "*internal_iter") that were live at the specified "sequence" number +// into appropriate user keys. +extern Iterator* NewDBIterator( + DBImpl* db, + const Comparator* user_key_comparator, + Iterator* internal_iter, + SequenceNumber sequence, + uint32_t seed); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_DB_ITER_H_ diff --git a/src/leveldb-lin/leveldb/db/db_test.cc b/src/leveldb-lin/leveldb/db/db_test.cc new file mode 100644 index 0000000..280b01c --- /dev/null +++ b/src/leveldb-lin/leveldb/db/db_test.cc @@ -0,0 +1,2128 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" +#include "leveldb/filter_policy.h" +#include "db/db_impl.h" +#include "db/filename.h" +#include "db/version_set.h" +#include "db/write_batch_internal.h" +#include "leveldb/cache.h" +#include "leveldb/env.h" +#include "leveldb/table.h" +#include "util/hash.h" +#include "util/logging.h" +#include "util/mutexlock.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +static std::string RandomString(Random* rnd, int len) { + std::string r; + test::RandomString(rnd, len, &r); + return r; +} + +namespace { +class AtomicCounter { + private: + port::Mutex mu_; + int count_; + public: + AtomicCounter() : count_(0) { } + void Increment() { + IncrementBy(1); + } + void IncrementBy(int count) { + MutexLock l(&mu_); + count_ += count; + } + int Read() { + MutexLock l(&mu_); + return count_; + } + void Reset() { + MutexLock l(&mu_); + count_ = 0; + } +}; + +void DelayMilliseconds(int millis) { + Env::Default()->SleepForMicroseconds(millis * 1000); +} +} + +// Special Env used to delay background operations +class SpecialEnv : public EnvWrapper { + public: + // sstable/log Sync() calls are blocked while this pointer is non-NULL. + port::AtomicPointer delay_data_sync_; + + // sstable/log Sync() calls return an error. + port::AtomicPointer data_sync_error_; + + // Simulate no-space errors while this pointer is non-NULL. + port::AtomicPointer no_space_; + + // Simulate non-writable file system while this pointer is non-NULL + port::AtomicPointer non_writable_; + + // Force sync of manifest files to fail while this pointer is non-NULL + port::AtomicPointer manifest_sync_error_; + + // Force write to manifest files to fail while this pointer is non-NULL + port::AtomicPointer manifest_write_error_; + + bool count_random_reads_; + AtomicCounter random_read_counter_; + + explicit SpecialEnv(Env* base) : EnvWrapper(base) { + delay_data_sync_.Release_Store(NULL); + data_sync_error_.Release_Store(NULL); + no_space_.Release_Store(NULL); + non_writable_.Release_Store(NULL); + count_random_reads_ = false; + manifest_sync_error_.Release_Store(NULL); + manifest_write_error_.Release_Store(NULL); + } + + Status NewWritableFile(const std::string& f, WritableFile** r) { + class DataFile : public WritableFile { + private: + SpecialEnv* env_; + WritableFile* base_; + + public: + DataFile(SpecialEnv* env, WritableFile* base) + : env_(env), + base_(base) { + } + ~DataFile() { delete base_; } + Status Append(const Slice& data) { + if (env_->no_space_.Acquire_Load() != NULL) { + // Drop writes on the floor + return Status::OK(); + } else { + return base_->Append(data); + } + } + Status Close() { return base_->Close(); } + Status Flush() { return base_->Flush(); } + Status Sync() { + if (env_->data_sync_error_.Acquire_Load() != NULL) { + return Status::IOError("simulated data sync error"); + } + while (env_->delay_data_sync_.Acquire_Load() != NULL) { + DelayMilliseconds(100); + } + return base_->Sync(); + } + }; + class ManifestFile : public WritableFile { + private: + SpecialEnv* env_; + WritableFile* base_; + public: + ManifestFile(SpecialEnv* env, WritableFile* b) : env_(env), base_(b) { } + ~ManifestFile() { delete base_; } + Status Append(const Slice& data) { + if (env_->manifest_write_error_.Acquire_Load() != NULL) { + return Status::IOError("simulated writer error"); + } else { + return base_->Append(data); + } + } + Status Close() { return base_->Close(); } + Status Flush() { return base_->Flush(); } + Status Sync() { + if (env_->manifest_sync_error_.Acquire_Load() != NULL) { + return Status::IOError("simulated sync error"); + } else { + return base_->Sync(); + } + } + }; + + if (non_writable_.Acquire_Load() != NULL) { + return Status::IOError("simulated write error"); + } + + Status s = target()->NewWritableFile(f, r); + if (s.ok()) { + if (strstr(f.c_str(), ".ldb") != NULL || + strstr(f.c_str(), ".log") != NULL) { + *r = new DataFile(this, *r); + } else if (strstr(f.c_str(), "MANIFEST") != NULL) { + *r = new ManifestFile(this, *r); + } + } + return s; + } + + Status NewRandomAccessFile(const std::string& f, RandomAccessFile** r) { + class CountingFile : public RandomAccessFile { + private: + RandomAccessFile* target_; + AtomicCounter* counter_; + public: + CountingFile(RandomAccessFile* target, AtomicCounter* counter) + : target_(target), counter_(counter) { + } + virtual ~CountingFile() { delete target_; } + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + counter_->Increment(); + return target_->Read(offset, n, result, scratch); + } + }; + + Status s = target()->NewRandomAccessFile(f, r); + if (s.ok() && count_random_reads_) { + *r = new CountingFile(*r, &random_read_counter_); + } + return s; + } +}; + +class DBTest { + private: + const FilterPolicy* filter_policy_; + + // Sequence of option configurations to try + enum OptionConfig { + kDefault, + kFilter, + kUncompressed, + kEnd + }; + int option_config_; + + public: + std::string dbname_; + SpecialEnv* env_; + DB* db_; + + Options last_options_; + + DBTest() : option_config_(kDefault), + env_(new SpecialEnv(Env::Default())) { + filter_policy_ = NewBloomFilterPolicy(10); + dbname_ = test::TmpDir() + "/db_test"; + DestroyDB(dbname_, Options()); + db_ = NULL; + Reopen(); + } + + ~DBTest() { + delete db_; + DestroyDB(dbname_, Options()); + delete env_; + delete filter_policy_; + } + + // Switch to a fresh database with the next option configuration to + // test. Return false if there are no more configurations to test. + bool ChangeOptions() { + option_config_++; + if (option_config_ >= kEnd) { + return false; + } else { + DestroyAndReopen(); + return true; + } + } + + // Return the current option configuration. + Options CurrentOptions() { + Options options; + switch (option_config_) { + case kFilter: + options.filter_policy = filter_policy_; + break; + case kUncompressed: + options.compression = kNoCompression; + break; + default: + break; + } + return options; + } + + DBImpl* dbfull() { + return reinterpret_cast(db_); + } + + void Reopen(Options* options = NULL) { + ASSERT_OK(TryReopen(options)); + } + + void Close() { + delete db_; + db_ = NULL; + } + + void DestroyAndReopen(Options* options = NULL) { + delete db_; + db_ = NULL; + DestroyDB(dbname_, Options()); + ASSERT_OK(TryReopen(options)); + } + + Status TryReopen(Options* options) { + delete db_; + db_ = NULL; + Options opts; + if (options != NULL) { + opts = *options; + } else { + opts = CurrentOptions(); + opts.create_if_missing = true; + } + last_options_ = opts; + + return DB::Open(opts, dbname_, &db_); + } + + Status Put(const std::string& k, const std::string& v) { + return db_->Put(WriteOptions(), k, v); + } + + Status Delete(const std::string& k) { + return db_->Delete(WriteOptions(), k); + } + + std::string Get(const std::string& k, const Snapshot* snapshot = NULL) { + ReadOptions options; + options.snapshot = snapshot; + std::string result; + Status s = db_->Get(options, k, &result); + if (s.IsNotFound()) { + result = "NOT_FOUND"; + } else if (!s.ok()) { + result = s.ToString(); + } + return result; + } + + // Return a string that contains all key,value pairs in order, + // formatted like "(k1->v1)(k2->v2)". + std::string Contents() { + std::vector forward; + std::string result; + Iterator* iter = db_->NewIterator(ReadOptions()); + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + std::string s = IterStatus(iter); + result.push_back('('); + result.append(s); + result.push_back(')'); + forward.push_back(s); + } + + // Check reverse iteration results are the reverse of forward results + size_t matched = 0; + for (iter->SeekToLast(); iter->Valid(); iter->Prev()) { + ASSERT_LT(matched, forward.size()); + ASSERT_EQ(IterStatus(iter), forward[forward.size() - matched - 1]); + matched++; + } + ASSERT_EQ(matched, forward.size()); + + delete iter; + return result; + } + + std::string AllEntriesFor(const Slice& user_key) { + Iterator* iter = dbfull()->TEST_NewInternalIterator(); + InternalKey target(user_key, kMaxSequenceNumber, kTypeValue); + iter->Seek(target.Encode()); + std::string result; + if (!iter->status().ok()) { + result = iter->status().ToString(); + } else { + result = "[ "; + bool first = true; + while (iter->Valid()) { + ParsedInternalKey ikey; + if (!ParseInternalKey(iter->key(), &ikey)) { + result += "CORRUPTED"; + } else { + if (last_options_.comparator->Compare(ikey.user_key, user_key) != 0) { + break; + } + if (!first) { + result += ", "; + } + first = false; + switch (ikey.type) { + case kTypeValue: + result += iter->value().ToString(); + break; + case kTypeDeletion: + result += "DEL"; + break; + } + } + iter->Next(); + } + if (!first) { + result += " "; + } + result += "]"; + } + delete iter; + return result; + } + + int NumTableFilesAtLevel(int level) { + std::string property; + ASSERT_TRUE( + db_->GetProperty("leveldb.num-files-at-level" + NumberToString(level), + &property)); + return atoi(property.c_str()); + } + + int TotalTableFiles() { + int result = 0; + for (int level = 0; level < config::kNumLevels; level++) { + result += NumTableFilesAtLevel(level); + } + return result; + } + + // Return spread of files per level + std::string FilesPerLevel() { + std::string result; + int last_non_zero_offset = 0; + for (int level = 0; level < config::kNumLevels; level++) { + int f = NumTableFilesAtLevel(level); + char buf[100]; + snprintf(buf, sizeof(buf), "%s%d", (level ? "," : ""), f); + result += buf; + if (f > 0) { + last_non_zero_offset = result.size(); + } + } + result.resize(last_non_zero_offset); + return result; + } + + int CountFiles() { + std::vector files; + env_->GetChildren(dbname_, &files); + return static_cast(files.size()); + } + + uint64_t Size(const Slice& start, const Slice& limit) { + Range r(start, limit); + uint64_t size; + db_->GetApproximateSizes(&r, 1, &size); + return size; + } + + void Compact(const Slice& start, const Slice& limit) { + db_->CompactRange(&start, &limit); + } + + // Do n memtable compactions, each of which produces an sstable + // covering the range [small,large]. + void MakeTables(int n, const std::string& small, const std::string& large) { + for (int i = 0; i < n; i++) { + Put(small, "begin"); + Put(large, "end"); + dbfull()->TEST_CompactMemTable(); + } + } + + // Prevent pushing of new sstables into deeper levels by adding + // tables that cover a specified range to all levels. + void FillLevels(const std::string& smallest, const std::string& largest) { + MakeTables(config::kNumLevels, smallest, largest); + } + + void DumpFileCounts(const char* label) { + fprintf(stderr, "---\n%s:\n", label); + fprintf(stderr, "maxoverlap: %lld\n", + static_cast( + dbfull()->TEST_MaxNextLevelOverlappingBytes())); + for (int level = 0; level < config::kNumLevels; level++) { + int num = NumTableFilesAtLevel(level); + if (num > 0) { + fprintf(stderr, " level %3d : %d files\n", level, num); + } + } + } + + std::string DumpSSTableList() { + std::string property; + db_->GetProperty("leveldb.sstables", &property); + return property; + } + + std::string IterStatus(Iterator* iter) { + std::string result; + if (iter->Valid()) { + result = iter->key().ToString() + "->" + iter->value().ToString(); + } else { + result = "(invalid)"; + } + return result; + } + + bool DeleteAnSSTFile() { + std::vector filenames; + ASSERT_OK(env_->GetChildren(dbname_, &filenames)); + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) && type == kTableFile) { + ASSERT_OK(env_->DeleteFile(TableFileName(dbname_, number))); + return true; + } + } + return false; + } + + // Returns number of files renamed. + int RenameLDBToSST() { + std::vector filenames; + ASSERT_OK(env_->GetChildren(dbname_, &filenames)); + uint64_t number; + FileType type; + int files_renamed = 0; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) && type == kTableFile) { + const std::string from = TableFileName(dbname_, number); + const std::string to = SSTTableFileName(dbname_, number); + ASSERT_OK(env_->RenameFile(from, to)); + files_renamed++; + } + } + return files_renamed; + } +}; + +TEST(DBTest, Empty) { + do { + ASSERT_TRUE(db_ != NULL); + ASSERT_EQ("NOT_FOUND", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, ReadWrite) { + do { + ASSERT_OK(Put("foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + ASSERT_OK(Put("bar", "v2")); + ASSERT_OK(Put("foo", "v3")); + ASSERT_EQ("v3", Get("foo")); + ASSERT_EQ("v2", Get("bar")); + } while (ChangeOptions()); +} + +TEST(DBTest, PutDeleteGet) { + do { + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2")); + ASSERT_EQ("v2", Get("foo")); + ASSERT_OK(db_->Delete(WriteOptions(), "foo")); + ASSERT_EQ("NOT_FOUND", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetFromImmutableLayer) { + do { + Options options = CurrentOptions(); + options.env = env_; + options.write_buffer_size = 100000; // Small write buffer + Reopen(&options); + + ASSERT_OK(Put("foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + + env_->delay_data_sync_.Release_Store(env_); // Block sync calls + Put("k1", std::string(100000, 'x')); // Fill memtable + Put("k2", std::string(100000, 'y')); // Trigger compaction + ASSERT_EQ("v1", Get("foo")); + env_->delay_data_sync_.Release_Store(NULL); // Release sync calls + } while (ChangeOptions()); +} + +TEST(DBTest, GetFromVersions) { + do { + ASSERT_OK(Put("foo", "v1")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v1", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetSnapshot) { + do { + // Try with both a short key and a long key + for (int i = 0; i < 2; i++) { + std::string key = (i == 0) ? std::string("foo") : std::string(200, 'x'); + ASSERT_OK(Put(key, "v1")); + const Snapshot* s1 = db_->GetSnapshot(); + ASSERT_OK(Put(key, "v2")); + ASSERT_EQ("v2", Get(key)); + ASSERT_EQ("v1", Get(key, s1)); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v2", Get(key)); + ASSERT_EQ("v1", Get(key, s1)); + db_->ReleaseSnapshot(s1); + } + } while (ChangeOptions()); +} + +TEST(DBTest, GetLevel0Ordering) { + do { + // Check that we process level-0 files in correct order. The code + // below generates two level-0 files where the earlier one comes + // before the later one in the level-0 file list since the earlier + // one has a smaller "smallest" key. + ASSERT_OK(Put("bar", "b")); + ASSERT_OK(Put("foo", "v1")); + dbfull()->TEST_CompactMemTable(); + ASSERT_OK(Put("foo", "v2")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v2", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetOrderedByLevels) { + do { + ASSERT_OK(Put("foo", "v1")); + Compact("a", "z"); + ASSERT_EQ("v1", Get("foo")); + ASSERT_OK(Put("foo", "v2")); + ASSERT_EQ("v2", Get("foo")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v2", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetPicksCorrectFile) { + do { + // Arrange to have multiple files in a non-level-0 level. + ASSERT_OK(Put("a", "va")); + Compact("a", "b"); + ASSERT_OK(Put("x", "vx")); + Compact("x", "y"); + ASSERT_OK(Put("f", "vf")); + Compact("f", "g"); + ASSERT_EQ("va", Get("a")); + ASSERT_EQ("vf", Get("f")); + ASSERT_EQ("vx", Get("x")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetEncountersEmptyLevel) { + do { + // Arrange for the following to happen: + // * sstable A in level 0 + // * nothing in level 1 + // * sstable B in level 2 + // Then do enough Get() calls to arrange for an automatic compaction + // of sstable A. A bug would cause the compaction to be marked as + // occuring at level 1 (instead of the correct level 0). + + // Step 1: First place sstables in levels 0 and 2 + int compaction_count = 0; + while (NumTableFilesAtLevel(0) == 0 || + NumTableFilesAtLevel(2) == 0) { + ASSERT_LE(compaction_count, 100) << "could not fill levels 0 and 2"; + compaction_count++; + Put("a", "begin"); + Put("z", "end"); + dbfull()->TEST_CompactMemTable(); + } + + // Step 2: clear level 1 if necessary. + dbfull()->TEST_CompactRange(1, NULL, NULL); + ASSERT_EQ(NumTableFilesAtLevel(0), 1); + ASSERT_EQ(NumTableFilesAtLevel(1), 0); + ASSERT_EQ(NumTableFilesAtLevel(2), 1); + + // Step 3: read a bunch of times + for (int i = 0; i < 1000; i++) { + ASSERT_EQ("NOT_FOUND", Get("missing")); + } + + // Step 4: Wait for compaction to finish + DelayMilliseconds(1000); + + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + } while (ChangeOptions()); +} + +TEST(DBTest, IterEmpty) { + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek("foo"); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterSingle) { + ASSERT_OK(Put("a", "va")); + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek(""); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek("a"); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek("b"); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterMulti) { + ASSERT_OK(Put("a", "va")); + ASSERT_OK(Put("b", "vb")); + ASSERT_OK(Put("c", "vc")); + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek(""); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Seek("a"); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Seek("ax"); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Seek("b"); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Seek("z"); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + // Switch from reverse to forward + iter->SeekToLast(); + iter->Prev(); + iter->Prev(); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + + // Switch from forward to reverse + iter->SeekToFirst(); + iter->Next(); + iter->Next(); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + + // Make sure iter stays at snapshot + ASSERT_OK(Put("a", "va2")); + ASSERT_OK(Put("a2", "va3")); + ASSERT_OK(Put("b", "vb2")); + ASSERT_OK(Put("c", "vc2")); + ASSERT_OK(Delete("b")); + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterSmallAndLargeMix) { + ASSERT_OK(Put("a", "va")); + ASSERT_OK(Put("b", std::string(100000, 'b'))); + ASSERT_OK(Put("c", "vc")); + ASSERT_OK(Put("d", std::string(100000, 'd'))); + ASSERT_OK(Put("e", std::string(100000, 'e'))); + + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b')); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd')); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e')); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e')); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd')); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b')); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterMultiWithDelete) { + do { + ASSERT_OK(Put("a", "va")); + ASSERT_OK(Put("b", "vb")); + ASSERT_OK(Put("c", "vc")); + ASSERT_OK(Delete("b")); + ASSERT_EQ("NOT_FOUND", Get("b")); + + Iterator* iter = db_->NewIterator(ReadOptions()); + iter->Seek("c"); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + delete iter; + } while (ChangeOptions()); +} + +TEST(DBTest, Recover) { + do { + ASSERT_OK(Put("foo", "v1")); + ASSERT_OK(Put("baz", "v5")); + + Reopen(); + ASSERT_EQ("v1", Get("foo")); + + ASSERT_EQ("v1", Get("foo")); + ASSERT_EQ("v5", Get("baz")); + ASSERT_OK(Put("bar", "v2")); + ASSERT_OK(Put("foo", "v3")); + + Reopen(); + ASSERT_EQ("v3", Get("foo")); + ASSERT_OK(Put("foo", "v4")); + ASSERT_EQ("v4", Get("foo")); + ASSERT_EQ("v2", Get("bar")); + ASSERT_EQ("v5", Get("baz")); + } while (ChangeOptions()); +} + +TEST(DBTest, RecoveryWithEmptyLog) { + do { + ASSERT_OK(Put("foo", "v1")); + ASSERT_OK(Put("foo", "v2")); + Reopen(); + Reopen(); + ASSERT_OK(Put("foo", "v3")); + Reopen(); + ASSERT_EQ("v3", Get("foo")); + } while (ChangeOptions()); +} + +// Check that writes done during a memtable compaction are recovered +// if the database is shutdown during the memtable compaction. +TEST(DBTest, RecoverDuringMemtableCompaction) { + do { + Options options = CurrentOptions(); + options.env = env_; + options.write_buffer_size = 1000000; + Reopen(&options); + + // Trigger a long memtable compaction and reopen the database during it + ASSERT_OK(Put("foo", "v1")); // Goes to 1st log file + ASSERT_OK(Put("big1", std::string(10000000, 'x'))); // Fills memtable + ASSERT_OK(Put("big2", std::string(1000, 'y'))); // Triggers compaction + ASSERT_OK(Put("bar", "v2")); // Goes to new log file + + Reopen(&options); + ASSERT_EQ("v1", Get("foo")); + ASSERT_EQ("v2", Get("bar")); + ASSERT_EQ(std::string(10000000, 'x'), Get("big1")); + ASSERT_EQ(std::string(1000, 'y'), Get("big2")); + } while (ChangeOptions()); +} + +static std::string Key(int i) { + char buf[100]; + snprintf(buf, sizeof(buf), "key%06d", i); + return std::string(buf); +} + +TEST(DBTest, MinorCompactionsHappen) { + Options options = CurrentOptions(); + options.write_buffer_size = 10000; + Reopen(&options); + + const int N = 500; + + int starting_num_tables = TotalTableFiles(); + for (int i = 0; i < N; i++) { + ASSERT_OK(Put(Key(i), Key(i) + std::string(1000, 'v'))); + } + int ending_num_tables = TotalTableFiles(); + ASSERT_GT(ending_num_tables, starting_num_tables); + + for (int i = 0; i < N; i++) { + ASSERT_EQ(Key(i) + std::string(1000, 'v'), Get(Key(i))); + } + + Reopen(); + + for (int i = 0; i < N; i++) { + ASSERT_EQ(Key(i) + std::string(1000, 'v'), Get(Key(i))); + } +} + +TEST(DBTest, RecoverWithLargeLog) { + { + Options options = CurrentOptions(); + Reopen(&options); + ASSERT_OK(Put("big1", std::string(200000, '1'))); + ASSERT_OK(Put("big2", std::string(200000, '2'))); + ASSERT_OK(Put("small3", std::string(10, '3'))); + ASSERT_OK(Put("small4", std::string(10, '4'))); + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + } + + // Make sure that if we re-open with a small write buffer size that + // we flush table files in the middle of a large log file. + Options options = CurrentOptions(); + options.write_buffer_size = 100000; + Reopen(&options); + ASSERT_EQ(NumTableFilesAtLevel(0), 3); + ASSERT_EQ(std::string(200000, '1'), Get("big1")); + ASSERT_EQ(std::string(200000, '2'), Get("big2")); + ASSERT_EQ(std::string(10, '3'), Get("small3")); + ASSERT_EQ(std::string(10, '4'), Get("small4")); + ASSERT_GT(NumTableFilesAtLevel(0), 1); +} + +TEST(DBTest, CompactionsGenerateMultipleFiles) { + Options options = CurrentOptions(); + options.write_buffer_size = 100000000; // Large write buffer + Reopen(&options); + + Random rnd(301); + + // Write 8MB (80 values, each 100K) + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + std::vector values; + for (int i = 0; i < 80; i++) { + values.push_back(RandomString(&rnd, 100000)); + ASSERT_OK(Put(Key(i), values[i])); + } + + // Reopening moves updates to level-0 + Reopen(&options); + dbfull()->TEST_CompactRange(0, NULL, NULL); + + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + ASSERT_GT(NumTableFilesAtLevel(1), 1); + for (int i = 0; i < 80; i++) { + ASSERT_EQ(Get(Key(i)), values[i]); + } +} + +TEST(DBTest, RepeatedWritesToSameKey) { + Options options = CurrentOptions(); + options.env = env_; + options.write_buffer_size = 100000; // Small write buffer + Reopen(&options); + + // We must have at most one file per level except for level-0, + // which may have up to kL0_StopWritesTrigger files. + const int kMaxFiles = config::kNumLevels + config::kL0_StopWritesTrigger; + + Random rnd(301); + std::string value = RandomString(&rnd, 2 * options.write_buffer_size); + for (int i = 0; i < 5 * kMaxFiles; i++) { + Put("key", value); + ASSERT_LE(TotalTableFiles(), kMaxFiles); + fprintf(stderr, "after %d: %d files\n", int(i+1), TotalTableFiles()); + } +} + +TEST(DBTest, SparseMerge) { + Options options = CurrentOptions(); + options.compression = kNoCompression; + Reopen(&options); + + FillLevels("A", "Z"); + + // Suppose there is: + // small amount of data with prefix A + // large amount of data with prefix B + // small amount of data with prefix C + // and that recent updates have made small changes to all three prefixes. + // Check that we do not do a compaction that merges all of B in one shot. + const std::string value(1000, 'x'); + Put("A", "va"); + // Write approximately 100MB of "B" values + for (int i = 0; i < 100000; i++) { + char key[100]; + snprintf(key, sizeof(key), "B%010d", i); + Put(key, value); + } + Put("C", "vc"); + dbfull()->TEST_CompactMemTable(); + dbfull()->TEST_CompactRange(0, NULL, NULL); + + // Make sparse update + Put("A", "va2"); + Put("B100", "bvalue2"); + Put("C", "vc2"); + dbfull()->TEST_CompactMemTable(); + + // Compactions should not cause us to create a situation where + // a file overlaps too much data at the next level. + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); + dbfull()->TEST_CompactRange(0, NULL, NULL); + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); + dbfull()->TEST_CompactRange(1, NULL, NULL); + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); +} + +static bool Between(uint64_t val, uint64_t low, uint64_t high) { + bool result = (val >= low) && (val <= high); + if (!result) { + fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n", + (unsigned long long)(val), + (unsigned long long)(low), + (unsigned long long)(high)); + } + return result; +} + +TEST(DBTest, ApproximateSizes) { + do { + Options options = CurrentOptions(); + options.write_buffer_size = 100000000; // Large write buffer + options.compression = kNoCompression; + DestroyAndReopen(); + + ASSERT_TRUE(Between(Size("", "xyz"), 0, 0)); + Reopen(&options); + ASSERT_TRUE(Between(Size("", "xyz"), 0, 0)); + + // Write 8MB (80 values, each 100K) + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + const int N = 80; + static const int S1 = 100000; + static const int S2 = 105000; // Allow some expansion from metadata + Random rnd(301); + for (int i = 0; i < N; i++) { + ASSERT_OK(Put(Key(i), RandomString(&rnd, S1))); + } + + // 0 because GetApproximateSizes() does not account for memtable space + ASSERT_TRUE(Between(Size("", Key(50)), 0, 0)); + + // Check sizes across recovery by reopening a few times + for (int run = 0; run < 3; run++) { + Reopen(&options); + + for (int compact_start = 0; compact_start < N; compact_start += 10) { + for (int i = 0; i < N; i += 10) { + ASSERT_TRUE(Between(Size("", Key(i)), S1*i, S2*i)); + ASSERT_TRUE(Between(Size("", Key(i)+".suffix"), S1*(i+1), S2*(i+1))); + ASSERT_TRUE(Between(Size(Key(i), Key(i+10)), S1*10, S2*10)); + } + ASSERT_TRUE(Between(Size("", Key(50)), S1*50, S2*50)); + ASSERT_TRUE(Between(Size("", Key(50)+".suffix"), S1*50, S2*50)); + + std::string cstart_str = Key(compact_start); + std::string cend_str = Key(compact_start + 9); + Slice cstart = cstart_str; + Slice cend = cend_str; + dbfull()->TEST_CompactRange(0, &cstart, &cend); + } + + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + ASSERT_GT(NumTableFilesAtLevel(1), 0); + } + } while (ChangeOptions()); +} + +TEST(DBTest, ApproximateSizes_MixOfSmallAndLarge) { + do { + Options options = CurrentOptions(); + options.compression = kNoCompression; + Reopen(); + + Random rnd(301); + std::string big1 = RandomString(&rnd, 100000); + ASSERT_OK(Put(Key(0), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(1), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(2), big1)); + ASSERT_OK(Put(Key(3), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(4), big1)); + ASSERT_OK(Put(Key(5), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(6), RandomString(&rnd, 300000))); + ASSERT_OK(Put(Key(7), RandomString(&rnd, 10000))); + + // Check sizes across recovery by reopening a few times + for (int run = 0; run < 3; run++) { + Reopen(&options); + + ASSERT_TRUE(Between(Size("", Key(0)), 0, 0)); + ASSERT_TRUE(Between(Size("", Key(1)), 10000, 11000)); + ASSERT_TRUE(Between(Size("", Key(2)), 20000, 21000)); + ASSERT_TRUE(Between(Size("", Key(3)), 120000, 121000)); + ASSERT_TRUE(Between(Size("", Key(4)), 130000, 131000)); + ASSERT_TRUE(Between(Size("", Key(5)), 230000, 231000)); + ASSERT_TRUE(Between(Size("", Key(6)), 240000, 241000)); + ASSERT_TRUE(Between(Size("", Key(7)), 540000, 541000)); + ASSERT_TRUE(Between(Size("", Key(8)), 550000, 560000)); + + ASSERT_TRUE(Between(Size(Key(3), Key(5)), 110000, 111000)); + + dbfull()->TEST_CompactRange(0, NULL, NULL); + } + } while (ChangeOptions()); +} + +TEST(DBTest, IteratorPinsRef) { + Put("foo", "hello"); + + // Get iterator that will yield the current contents of the DB. + Iterator* iter = db_->NewIterator(ReadOptions()); + + // Write to force compactions + Put("foo", "newvalue1"); + for (int i = 0; i < 100; i++) { + ASSERT_OK(Put(Key(i), Key(i) + std::string(100000, 'v'))); // 100K values + } + Put("foo", "newvalue2"); + + iter->SeekToFirst(); + ASSERT_TRUE(iter->Valid()); + ASSERT_EQ("foo", iter->key().ToString()); + ASSERT_EQ("hello", iter->value().ToString()); + iter->Next(); + ASSERT_TRUE(!iter->Valid()); + delete iter; +} + +TEST(DBTest, Snapshot) { + do { + Put("foo", "v1"); + const Snapshot* s1 = db_->GetSnapshot(); + Put("foo", "v2"); + const Snapshot* s2 = db_->GetSnapshot(); + Put("foo", "v3"); + const Snapshot* s3 = db_->GetSnapshot(); + + Put("foo", "v4"); + ASSERT_EQ("v1", Get("foo", s1)); + ASSERT_EQ("v2", Get("foo", s2)); + ASSERT_EQ("v3", Get("foo", s3)); + ASSERT_EQ("v4", Get("foo")); + + db_->ReleaseSnapshot(s3); + ASSERT_EQ("v1", Get("foo", s1)); + ASSERT_EQ("v2", Get("foo", s2)); + ASSERT_EQ("v4", Get("foo")); + + db_->ReleaseSnapshot(s1); + ASSERT_EQ("v2", Get("foo", s2)); + ASSERT_EQ("v4", Get("foo")); + + db_->ReleaseSnapshot(s2); + ASSERT_EQ("v4", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, HiddenValuesAreRemoved) { + do { + Random rnd(301); + FillLevels("a", "z"); + + std::string big = RandomString(&rnd, 50000); + Put("foo", big); + Put("pastfoo", "v"); + const Snapshot* snapshot = db_->GetSnapshot(); + Put("foo", "tiny"); + Put("pastfoo2", "v2"); // Advance sequence number one more + + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + ASSERT_GT(NumTableFilesAtLevel(0), 0); + + ASSERT_EQ(big, Get("foo", snapshot)); + ASSERT_TRUE(Between(Size("", "pastfoo"), 50000, 60000)); + db_->ReleaseSnapshot(snapshot); + ASSERT_EQ(AllEntriesFor("foo"), "[ tiny, " + big + " ]"); + Slice x("x"); + dbfull()->TEST_CompactRange(0, NULL, &x); + ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]"); + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + ASSERT_GE(NumTableFilesAtLevel(1), 1); + dbfull()->TEST_CompactRange(1, NULL, &x); + ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]"); + + ASSERT_TRUE(Between(Size("", "pastfoo"), 0, 1000)); + } while (ChangeOptions()); +} + +TEST(DBTest, DeletionMarkers1) { + Put("foo", "v1"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level + + // Place a table at level last-1 to prevent merging with preceding mutation + Put("a", "begin"); + Put("z", "end"); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ(NumTableFilesAtLevel(last), 1); + ASSERT_EQ(NumTableFilesAtLevel(last-1), 1); + + Delete("foo"); + Put("foo", "v2"); + ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2 + ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]"); + Slice z("z"); + dbfull()->TEST_CompactRange(last-2, NULL, &z); + // DEL eliminated, but v1 remains because we aren't compacting that level + // (DEL can be eliminated because v2 hides v1). + ASSERT_EQ(AllEntriesFor("foo"), "[ v2, v1 ]"); + dbfull()->TEST_CompactRange(last-1, NULL, NULL); + // Merging last-1 w/ last, so we are the base level for "foo", so + // DEL is removed. (as is v1). + ASSERT_EQ(AllEntriesFor("foo"), "[ v2 ]"); +} + +TEST(DBTest, DeletionMarkers2) { + Put("foo", "v1"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level + + // Place a table at level last-1 to prevent merging with preceding mutation + Put("a", "begin"); + Put("z", "end"); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ(NumTableFilesAtLevel(last), 1); + ASSERT_EQ(NumTableFilesAtLevel(last-1), 1); + + Delete("foo"); + ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2 + ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); + dbfull()->TEST_CompactRange(last-2, NULL, NULL); + // DEL kept: "last" file overlaps + ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); + dbfull()->TEST_CompactRange(last-1, NULL, NULL); + // Merging last-1 w/ last, so we are the base level for "foo", so + // DEL is removed. (as is v1). + ASSERT_EQ(AllEntriesFor("foo"), "[ ]"); +} + +TEST(DBTest, OverlapInLevel0) { + do { + ASSERT_EQ(config::kMaxMemCompactLevel, 2) << "Fix test to match config"; + + // Fill levels 1 and 2 to disable the pushing of new memtables to levels > 0. + ASSERT_OK(Put("100", "v100")); + ASSERT_OK(Put("999", "v999")); + dbfull()->TEST_CompactMemTable(); + ASSERT_OK(Delete("100")); + ASSERT_OK(Delete("999")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("0,1,1", FilesPerLevel()); + + // Make files spanning the following ranges in level-0: + // files[0] 200 .. 900 + // files[1] 300 .. 500 + // Note that files are sorted by smallest key. + ASSERT_OK(Put("300", "v300")); + ASSERT_OK(Put("500", "v500")); + dbfull()->TEST_CompactMemTable(); + ASSERT_OK(Put("200", "v200")); + ASSERT_OK(Put("600", "v600")); + ASSERT_OK(Put("900", "v900")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("2,1,1", FilesPerLevel()); + + // Compact away the placeholder files we created initially + dbfull()->TEST_CompactRange(1, NULL, NULL); + dbfull()->TEST_CompactRange(2, NULL, NULL); + ASSERT_EQ("2", FilesPerLevel()); + + // Do a memtable compaction. Before bug-fix, the compaction would + // not detect the overlap with level-0 files and would incorrectly place + // the deletion in a deeper level. + ASSERT_OK(Delete("600")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("3", FilesPerLevel()); + ASSERT_EQ("NOT_FOUND", Get("600")); + } while (ChangeOptions()); +} + +TEST(DBTest, L0_CompactionBug_Issue44_a) { + Reopen(); + ASSERT_OK(Put("b", "v")); + Reopen(); + ASSERT_OK(Delete("b")); + ASSERT_OK(Delete("a")); + Reopen(); + ASSERT_OK(Delete("a")); + Reopen(); + ASSERT_OK(Put("a", "v")); + Reopen(); + Reopen(); + ASSERT_EQ("(a->v)", Contents()); + DelayMilliseconds(1000); // Wait for compaction to finish + ASSERT_EQ("(a->v)", Contents()); +} + +TEST(DBTest, L0_CompactionBug_Issue44_b) { + Reopen(); + Put("",""); + Reopen(); + Delete("e"); + Put("",""); + Reopen(); + Put("c", "cv"); + Reopen(); + Put("",""); + Reopen(); + Put("",""); + DelayMilliseconds(1000); // Wait for compaction to finish + Reopen(); + Put("d","dv"); + Reopen(); + Put("",""); + Reopen(); + Delete("d"); + Delete("b"); + Reopen(); + ASSERT_EQ("(->)(c->cv)", Contents()); + DelayMilliseconds(1000); // Wait for compaction to finish + ASSERT_EQ("(->)(c->cv)", Contents()); +} + +TEST(DBTest, ComparatorCheck) { + class NewComparator : public Comparator { + public: + virtual const char* Name() const { return "leveldb.NewComparator"; } + virtual int Compare(const Slice& a, const Slice& b) const { + return BytewiseComparator()->Compare(a, b); + } + virtual void FindShortestSeparator(std::string* s, const Slice& l) const { + BytewiseComparator()->FindShortestSeparator(s, l); + } + virtual void FindShortSuccessor(std::string* key) const { + BytewiseComparator()->FindShortSuccessor(key); + } + }; + NewComparator cmp; + Options new_options = CurrentOptions(); + new_options.comparator = &cmp; + Status s = TryReopen(&new_options); + ASSERT_TRUE(!s.ok()); + ASSERT_TRUE(s.ToString().find("comparator") != std::string::npos) + << s.ToString(); +} + +TEST(DBTest, CustomComparator) { + class NumberComparator : public Comparator { + public: + virtual const char* Name() const { return "test.NumberComparator"; } + virtual int Compare(const Slice& a, const Slice& b) const { + return ToNumber(a) - ToNumber(b); + } + virtual void FindShortestSeparator(std::string* s, const Slice& l) const { + ToNumber(*s); // Check format + ToNumber(l); // Check format + } + virtual void FindShortSuccessor(std::string* key) const { + ToNumber(*key); // Check format + } + private: + static int ToNumber(const Slice& x) { + // Check that there are no extra characters. + ASSERT_TRUE(x.size() >= 2 && x[0] == '[' && x[x.size()-1] == ']') + << EscapeString(x); + int val; + char ignored; + ASSERT_TRUE(sscanf(x.ToString().c_str(), "[%i]%c", &val, &ignored) == 1) + << EscapeString(x); + return val; + } + }; + NumberComparator cmp; + Options new_options = CurrentOptions(); + new_options.create_if_missing = true; + new_options.comparator = &cmp; + new_options.filter_policy = NULL; // Cannot use bloom filters + new_options.write_buffer_size = 1000; // Compact more often + DestroyAndReopen(&new_options); + ASSERT_OK(Put("[10]", "ten")); + ASSERT_OK(Put("[0x14]", "twenty")); + for (int i = 0; i < 2; i++) { + ASSERT_EQ("ten", Get("[10]")); + ASSERT_EQ("ten", Get("[0xa]")); + ASSERT_EQ("twenty", Get("[20]")); + ASSERT_EQ("twenty", Get("[0x14]")); + ASSERT_EQ("NOT_FOUND", Get("[15]")); + ASSERT_EQ("NOT_FOUND", Get("[0xf]")); + Compact("[0]", "[9999]"); + } + + for (int run = 0; run < 2; run++) { + for (int i = 0; i < 1000; i++) { + char buf[100]; + snprintf(buf, sizeof(buf), "[%d]", i*10); + ASSERT_OK(Put(buf, buf)); + } + Compact("[0]", "[1000000]"); + } +} + +TEST(DBTest, ManualCompaction) { + ASSERT_EQ(config::kMaxMemCompactLevel, 2) + << "Need to update this test to match kMaxMemCompactLevel"; + + MakeTables(3, "p", "q"); + ASSERT_EQ("1,1,1", FilesPerLevel()); + + // Compaction range falls before files + Compact("", "c"); + ASSERT_EQ("1,1,1", FilesPerLevel()); + + // Compaction range falls after files + Compact("r", "z"); + ASSERT_EQ("1,1,1", FilesPerLevel()); + + // Compaction range overlaps files + Compact("p1", "p9"); + ASSERT_EQ("0,0,1", FilesPerLevel()); + + // Populate a different range + MakeTables(3, "c", "e"); + ASSERT_EQ("1,1,2", FilesPerLevel()); + + // Compact just the new range + Compact("b", "f"); + ASSERT_EQ("0,0,2", FilesPerLevel()); + + // Compact all + MakeTables(1, "a", "z"); + ASSERT_EQ("0,1,2", FilesPerLevel()); + db_->CompactRange(NULL, NULL); + ASSERT_EQ("0,0,1", FilesPerLevel()); +} + +TEST(DBTest, DBOpen_Options) { + std::string dbname = test::TmpDir() + "/db_options_test"; + DestroyDB(dbname, Options()); + + // Does not exist, and create_if_missing == false: error + DB* db = NULL; + Options opts; + opts.create_if_missing = false; + Status s = DB::Open(opts, dbname, &db); + ASSERT_TRUE(strstr(s.ToString().c_str(), "does not exist") != NULL); + ASSERT_TRUE(db == NULL); + + // Does not exist, and create_if_missing == true: OK + opts.create_if_missing = true; + s = DB::Open(opts, dbname, &db); + ASSERT_OK(s); + ASSERT_TRUE(db != NULL); + + delete db; + db = NULL; + + // Does exist, and error_if_exists == true: error + opts.create_if_missing = false; + opts.error_if_exists = true; + s = DB::Open(opts, dbname, &db); + ASSERT_TRUE(strstr(s.ToString().c_str(), "exists") != NULL); + ASSERT_TRUE(db == NULL); + + // Does exist, and error_if_exists == false: OK + opts.create_if_missing = true; + opts.error_if_exists = false; + s = DB::Open(opts, dbname, &db); + ASSERT_OK(s); + ASSERT_TRUE(db != NULL); + + delete db; + db = NULL; +} + +TEST(DBTest, Locking) { + DB* db2 = NULL; + Status s = DB::Open(CurrentOptions(), dbname_, &db2); + ASSERT_TRUE(!s.ok()) << "Locking did not prevent re-opening db"; +} + +// Check that number of files does not grow when we are out of space +TEST(DBTest, NoSpace) { + Options options = CurrentOptions(); + options.env = env_; + Reopen(&options); + + ASSERT_OK(Put("foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + Compact("a", "z"); + const int num_files = CountFiles(); + env_->no_space_.Release_Store(env_); // Force out-of-space errors + for (int i = 0; i < 10; i++) { + for (int level = 0; level < config::kNumLevels-1; level++) { + dbfull()->TEST_CompactRange(level, NULL, NULL); + } + } + env_->no_space_.Release_Store(NULL); + ASSERT_LT(CountFiles(), num_files + 3); +} + +TEST(DBTest, NonWritableFileSystem) { + Options options = CurrentOptions(); + options.write_buffer_size = 1000; + options.env = env_; + Reopen(&options); + ASSERT_OK(Put("foo", "v1")); + env_->non_writable_.Release_Store(env_); // Force errors for new files + std::string big(100000, 'x'); + int errors = 0; + for (int i = 0; i < 20; i++) { + fprintf(stderr, "iter %d; errors %d\n", i, errors); + if (!Put("foo", big).ok()) { + errors++; + DelayMilliseconds(100); + } + } + ASSERT_GT(errors, 0); + env_->non_writable_.Release_Store(NULL); +} + +TEST(DBTest, WriteSyncError) { + // Check that log sync errors cause the DB to disallow future writes. + + // (a) Cause log sync calls to fail + Options options = CurrentOptions(); + options.env = env_; + Reopen(&options); + env_->data_sync_error_.Release_Store(env_); + + // (b) Normal write should succeed + WriteOptions w; + ASSERT_OK(db_->Put(w, "k1", "v1")); + ASSERT_EQ("v1", Get("k1")); + + // (c) Do a sync write; should fail + w.sync = true; + ASSERT_TRUE(!db_->Put(w, "k2", "v2").ok()); + ASSERT_EQ("v1", Get("k1")); + ASSERT_EQ("NOT_FOUND", Get("k2")); + + // (d) make sync behave normally + env_->data_sync_error_.Release_Store(NULL); + + // (e) Do a non-sync write; should fail + w.sync = false; + ASSERT_TRUE(!db_->Put(w, "k3", "v3").ok()); + ASSERT_EQ("v1", Get("k1")); + ASSERT_EQ("NOT_FOUND", Get("k2")); + ASSERT_EQ("NOT_FOUND", Get("k3")); +} + +TEST(DBTest, ManifestWriteError) { + // Test for the following problem: + // (a) Compaction produces file F + // (b) Log record containing F is written to MANIFEST file, but Sync() fails + // (c) GC deletes F + // (d) After reopening DB, reads fail since deleted F is named in log record + + // We iterate twice. In the second iteration, everything is the + // same except the log record never makes it to the MANIFEST file. + for (int iter = 0; iter < 2; iter++) { + port::AtomicPointer* error_type = (iter == 0) + ? &env_->manifest_sync_error_ + : &env_->manifest_write_error_; + + // Insert foo=>bar mapping + Options options = CurrentOptions(); + options.env = env_; + options.create_if_missing = true; + options.error_if_exists = false; + DestroyAndReopen(&options); + ASSERT_OK(Put("foo", "bar")); + ASSERT_EQ("bar", Get("foo")); + + // Memtable compaction (will succeed) + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("bar", Get("foo")); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo=>bar is now in last level + + // Merging compaction (will fail) + error_type->Release_Store(env_); + dbfull()->TEST_CompactRange(last, NULL, NULL); // Should fail + ASSERT_EQ("bar", Get("foo")); + + // Recovery: should not lose data + error_type->Release_Store(NULL); + Reopen(&options); + ASSERT_EQ("bar", Get("foo")); + } +} + +TEST(DBTest, MissingSSTFile) { + ASSERT_OK(Put("foo", "bar")); + ASSERT_EQ("bar", Get("foo")); + + // Dump the memtable to disk. + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("bar", Get("foo")); + + Close(); + ASSERT_TRUE(DeleteAnSSTFile()); + Options options = CurrentOptions(); + options.paranoid_checks = true; + Status s = TryReopen(&options); + ASSERT_TRUE(!s.ok()); + ASSERT_TRUE(s.ToString().find("issing") != std::string::npos) + << s.ToString(); +} + +TEST(DBTest, StillReadSST) { + ASSERT_OK(Put("foo", "bar")); + ASSERT_EQ("bar", Get("foo")); + + // Dump the memtable to disk. + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("bar", Get("foo")); + Close(); + ASSERT_GT(RenameLDBToSST(), 0); + Options options = CurrentOptions(); + options.paranoid_checks = true; + Status s = TryReopen(&options); + ASSERT_TRUE(s.ok()); + ASSERT_EQ("bar", Get("foo")); +} + +TEST(DBTest, FilesDeletedAfterCompaction) { + ASSERT_OK(Put("foo", "v2")); + Compact("a", "z"); + const int num_files = CountFiles(); + for (int i = 0; i < 10; i++) { + ASSERT_OK(Put("foo", "v2")); + Compact("a", "z"); + } + ASSERT_EQ(CountFiles(), num_files); +} + +TEST(DBTest, BloomFilter) { + env_->count_random_reads_ = true; + Options options = CurrentOptions(); + options.env = env_; + options.block_cache = NewLRUCache(0); // Prevent cache hits + options.filter_policy = NewBloomFilterPolicy(10); + Reopen(&options); + + // Populate multiple layers + const int N = 10000; + for (int i = 0; i < N; i++) { + ASSERT_OK(Put(Key(i), Key(i))); + } + Compact("a", "z"); + for (int i = 0; i < N; i += 100) { + ASSERT_OK(Put(Key(i), Key(i))); + } + dbfull()->TEST_CompactMemTable(); + + // Prevent auto compactions triggered by seeks + env_->delay_data_sync_.Release_Store(env_); + + // Lookup present keys. Should rarely read from small sstable. + env_->random_read_counter_.Reset(); + for (int i = 0; i < N; i++) { + ASSERT_EQ(Key(i), Get(Key(i))); + } + int reads = env_->random_read_counter_.Read(); + fprintf(stderr, "%d present => %d reads\n", N, reads); + ASSERT_GE(reads, N); + ASSERT_LE(reads, N + 2*N/100); + + // Lookup present keys. Should rarely read from either sstable. + env_->random_read_counter_.Reset(); + for (int i = 0; i < N; i++) { + ASSERT_EQ("NOT_FOUND", Get(Key(i) + ".missing")); + } + reads = env_->random_read_counter_.Read(); + fprintf(stderr, "%d missing => %d reads\n", N, reads); + ASSERT_LE(reads, 3*N/100); + + env_->delay_data_sync_.Release_Store(NULL); + Close(); + delete options.block_cache; + delete options.filter_policy; +} + +// Multi-threaded test: +namespace { + +static const int kNumThreads = 4; +static const int kTestSeconds = 10; +static const int kNumKeys = 1000; + +struct MTState { + DBTest* test; + port::AtomicPointer stop; + port::AtomicPointer counter[kNumThreads]; + port::AtomicPointer thread_done[kNumThreads]; +}; + +struct MTThread { + MTState* state; + int id; +}; + +static void MTThreadBody(void* arg) { + MTThread* t = reinterpret_cast(arg); + int id = t->id; + DB* db = t->state->test->db_; + uintptr_t counter = 0; + fprintf(stderr, "... starting thread %d\n", id); + Random rnd(1000 + id); + std::string value; + char valbuf[1500]; + while (t->state->stop.Acquire_Load() == NULL) { + t->state->counter[id].Release_Store(reinterpret_cast(counter)); + + int key = rnd.Uniform(kNumKeys); + char keybuf[20]; + snprintf(keybuf, sizeof(keybuf), "%016d", key); + + if (rnd.OneIn(2)) { + // Write values of the form . + // We add some padding for force compactions. + snprintf(valbuf, sizeof(valbuf), "%d.%d.%-1000d", + key, id, static_cast(counter)); + ASSERT_OK(db->Put(WriteOptions(), Slice(keybuf), Slice(valbuf))); + } else { + // Read a value and verify that it matches the pattern written above. + Status s = db->Get(ReadOptions(), Slice(keybuf), &value); + if (s.IsNotFound()) { + // Key has not yet been written + } else { + // Check that the writer thread counter is >= the counter in the value + ASSERT_OK(s); + int k, w, c; + ASSERT_EQ(3, sscanf(value.c_str(), "%d.%d.%d", &k, &w, &c)) << value; + ASSERT_EQ(k, key); + ASSERT_GE(w, 0); + ASSERT_LT(w, kNumThreads); + ASSERT_LE(static_cast(c), reinterpret_cast( + t->state->counter[w].Acquire_Load())); + } + } + counter++; + } + t->state->thread_done[id].Release_Store(t); + fprintf(stderr, "... stopping thread %d after %d ops\n", id, int(counter)); +} + +} // namespace + +TEST(DBTest, MultiThreaded) { + do { + // Initialize state + MTState mt; + mt.test = this; + mt.stop.Release_Store(0); + for (int id = 0; id < kNumThreads; id++) { + mt.counter[id].Release_Store(0); + mt.thread_done[id].Release_Store(0); + } + + // Start threads + MTThread thread[kNumThreads]; + for (int id = 0; id < kNumThreads; id++) { + thread[id].state = &mt; + thread[id].id = id; + env_->StartThread(MTThreadBody, &thread[id]); + } + + // Let them run for a while + DelayMilliseconds(kTestSeconds * 1000); + + // Stop the threads and wait for them to finish + mt.stop.Release_Store(&mt); + for (int id = 0; id < kNumThreads; id++) { + while (mt.thread_done[id].Acquire_Load() == NULL) { + DelayMilliseconds(100); + } + } + } while (ChangeOptions()); +} + +namespace { +typedef std::map KVMap; +} + +class ModelDB: public DB { + public: + class ModelSnapshot : public Snapshot { + public: + KVMap map_; + }; + + explicit ModelDB(const Options& options): options_(options) { } + ~ModelDB() { } + virtual Status Put(const WriteOptions& o, const Slice& k, const Slice& v) { + return DB::Put(o, k, v); + } + virtual Status Delete(const WriteOptions& o, const Slice& key) { + return DB::Delete(o, key); + } + virtual Status Get(const ReadOptions& options, + const Slice& key, std::string* value) { + assert(false); // Not implemented + return Status::NotFound(key); + } + virtual Iterator* NewIterator(const ReadOptions& options) { + if (options.snapshot == NULL) { + KVMap* saved = new KVMap; + *saved = map_; + return new ModelIter(saved, true); + } else { + const KVMap* snapshot_state = + &(reinterpret_cast(options.snapshot)->map_); + return new ModelIter(snapshot_state, false); + } + } + virtual const Snapshot* GetSnapshot() { + ModelSnapshot* snapshot = new ModelSnapshot; + snapshot->map_ = map_; + return snapshot; + } + + virtual void ReleaseSnapshot(const Snapshot* snapshot) { + delete reinterpret_cast(snapshot); + } + virtual Status Write(const WriteOptions& options, WriteBatch* batch) { + class Handler : public WriteBatch::Handler { + public: + KVMap* map_; + virtual void Put(const Slice& key, const Slice& value) { + (*map_)[key.ToString()] = value.ToString(); + } + virtual void Delete(const Slice& key) { + map_->erase(key.ToString()); + } + }; + Handler handler; + handler.map_ = &map_; + return batch->Iterate(&handler); + } + + virtual bool GetProperty(const Slice& property, std::string* value) { + return false; + } + virtual void GetApproximateSizes(const Range* r, int n, uint64_t* sizes) { + for (int i = 0; i < n; i++) { + sizes[i] = 0; + } + } + virtual void CompactRange(const Slice* start, const Slice* end) { + } + + private: + class ModelIter: public Iterator { + public: + ModelIter(const KVMap* map, bool owned) + : map_(map), owned_(owned), iter_(map_->end()) { + } + ~ModelIter() { + if (owned_) delete map_; + } + virtual bool Valid() const { return iter_ != map_->end(); } + virtual void SeekToFirst() { iter_ = map_->begin(); } + virtual void SeekToLast() { + if (map_->empty()) { + iter_ = map_->end(); + } else { + iter_ = map_->find(map_->rbegin()->first); + } + } + virtual void Seek(const Slice& k) { + iter_ = map_->lower_bound(k.ToString()); + } + virtual void Next() { ++iter_; } + virtual void Prev() { --iter_; } + virtual Slice key() const { return iter_->first; } + virtual Slice value() const { return iter_->second; } + virtual Status status() const { return Status::OK(); } + private: + const KVMap* const map_; + const bool owned_; // Do we own map_ + KVMap::const_iterator iter_; + }; + const Options options_; + KVMap map_; +}; + +static std::string RandomKey(Random* rnd) { + int len = (rnd->OneIn(3) + ? 1 // Short sometimes to encourage collisions + : (rnd->OneIn(100) ? rnd->Skewed(10) : rnd->Uniform(10))); + return test::RandomKey(rnd, len); +} + +static bool CompareIterators(int step, + DB* model, + DB* db, + const Snapshot* model_snap, + const Snapshot* db_snap) { + ReadOptions options; + options.snapshot = model_snap; + Iterator* miter = model->NewIterator(options); + options.snapshot = db_snap; + Iterator* dbiter = db->NewIterator(options); + bool ok = true; + int count = 0; + for (miter->SeekToFirst(), dbiter->SeekToFirst(); + ok && miter->Valid() && dbiter->Valid(); + miter->Next(), dbiter->Next()) { + count++; + if (miter->key().compare(dbiter->key()) != 0) { + fprintf(stderr, "step %d: Key mismatch: '%s' vs. '%s'\n", + step, + EscapeString(miter->key()).c_str(), + EscapeString(dbiter->key()).c_str()); + ok = false; + break; + } + + if (miter->value().compare(dbiter->value()) != 0) { + fprintf(stderr, "step %d: Value mismatch for key '%s': '%s' vs. '%s'\n", + step, + EscapeString(miter->key()).c_str(), + EscapeString(miter->value()).c_str(), + EscapeString(miter->value()).c_str()); + ok = false; + } + } + + if (ok) { + if (miter->Valid() != dbiter->Valid()) { + fprintf(stderr, "step %d: Mismatch at end of iterators: %d vs. %d\n", + step, miter->Valid(), dbiter->Valid()); + ok = false; + } + } + fprintf(stderr, "%d entries compared: ok=%d\n", count, ok); + delete miter; + delete dbiter; + return ok; +} + +TEST(DBTest, Randomized) { + Random rnd(test::RandomSeed()); + do { + ModelDB model(CurrentOptions()); + const int N = 10000; + const Snapshot* model_snap = NULL; + const Snapshot* db_snap = NULL; + std::string k, v; + for (int step = 0; step < N; step++) { + if (step % 100 == 0) { + fprintf(stderr, "Step %d of %d\n", step, N); + } + // TODO(sanjay): Test Get() works + int p = rnd.Uniform(100); + if (p < 45) { // Put + k = RandomKey(&rnd); + v = RandomString(&rnd, + rnd.OneIn(20) + ? 100 + rnd.Uniform(100) + : rnd.Uniform(8)); + ASSERT_OK(model.Put(WriteOptions(), k, v)); + ASSERT_OK(db_->Put(WriteOptions(), k, v)); + + } else if (p < 90) { // Delete + k = RandomKey(&rnd); + ASSERT_OK(model.Delete(WriteOptions(), k)); + ASSERT_OK(db_->Delete(WriteOptions(), k)); + + + } else { // Multi-element batch + WriteBatch b; + const int num = rnd.Uniform(8); + for (int i = 0; i < num; i++) { + if (i == 0 || !rnd.OneIn(10)) { + k = RandomKey(&rnd); + } else { + // Periodically re-use the same key from the previous iter, so + // we have multiple entries in the write batch for the same key + } + if (rnd.OneIn(2)) { + v = RandomString(&rnd, rnd.Uniform(10)); + b.Put(k, v); + } else { + b.Delete(k); + } + } + ASSERT_OK(model.Write(WriteOptions(), &b)); + ASSERT_OK(db_->Write(WriteOptions(), &b)); + } + + if ((step % 100) == 0) { + ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL)); + ASSERT_TRUE(CompareIterators(step, &model, db_, model_snap, db_snap)); + // Save a snapshot from each DB this time that we'll use next + // time we compare things, to make sure the current state is + // preserved with the snapshot + if (model_snap != NULL) model.ReleaseSnapshot(model_snap); + if (db_snap != NULL) db_->ReleaseSnapshot(db_snap); + + Reopen(); + ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL)); + + model_snap = model.GetSnapshot(); + db_snap = db_->GetSnapshot(); + } + } + if (model_snap != NULL) model.ReleaseSnapshot(model_snap); + if (db_snap != NULL) db_->ReleaseSnapshot(db_snap); + } while (ChangeOptions()); +} + +std::string MakeKey(unsigned int num) { + char buf[30]; + snprintf(buf, sizeof(buf), "%016u", num); + return std::string(buf); +} + +void BM_LogAndApply(int iters, int num_base_files) { + std::string dbname = test::TmpDir() + "/leveldb_test_benchmark"; + DestroyDB(dbname, Options()); + + DB* db = NULL; + Options opts; + opts.create_if_missing = true; + Status s = DB::Open(opts, dbname, &db); + ASSERT_OK(s); + ASSERT_TRUE(db != NULL); + + delete db; + db = NULL; + + Env* env = Env::Default(); + + port::Mutex mu; + MutexLock l(&mu); + + InternalKeyComparator cmp(BytewiseComparator()); + Options options; + VersionSet vset(dbname, &options, NULL, &cmp); + ASSERT_OK(vset.Recover()); + VersionEdit vbase; + uint64_t fnum = 1; + for (int i = 0; i < num_base_files; i++) { + InternalKey start(MakeKey(2*fnum), 1, kTypeValue); + InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion); + vbase.AddFile(2, fnum++, 1 /* file size */, start, limit); + } + ASSERT_OK(vset.LogAndApply(&vbase, &mu)); + + uint64_t start_micros = env->NowMicros(); + + for (int i = 0; i < iters; i++) { + VersionEdit vedit; + vedit.DeleteFile(2, fnum); + InternalKey start(MakeKey(2*fnum), 1, kTypeValue); + InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion); + vedit.AddFile(2, fnum++, 1 /* file size */, start, limit); + vset.LogAndApply(&vedit, &mu); + } + uint64_t stop_micros = env->NowMicros(); + unsigned int us = stop_micros - start_micros; + char buf[16]; + snprintf(buf, sizeof(buf), "%d", num_base_files); + fprintf(stderr, + "BM_LogAndApply/%-6s %8d iters : %9u us (%7.0f us / iter)\n", + buf, iters, us, ((float)us) / iters); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + if (argc > 1 && std::string(argv[1]) == "--benchmark") { + leveldb::BM_LogAndApply(1000, 1); + leveldb::BM_LogAndApply(1000, 100); + leveldb::BM_LogAndApply(1000, 10000); + leveldb::BM_LogAndApply(100, 100000); + return 0; + } + + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/leveldb/db/dbformat.cc b/src/leveldb-lin/leveldb/db/dbformat.cc new file mode 100644 index 0000000..20a7ca4 --- /dev/null +++ b/src/leveldb-lin/leveldb/db/dbformat.cc @@ -0,0 +1,140 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include "db/dbformat.h" +#include "port/port.h" +#include "util/coding.h" + +namespace leveldb { + +static uint64_t PackSequenceAndType(uint64_t seq, ValueType t) { + assert(seq <= kMaxSequenceNumber); + assert(t <= kValueTypeForSeek); + return (seq << 8) | t; +} + +void AppendInternalKey(std::string* result, const ParsedInternalKey& key) { + result->append(key.user_key.data(), key.user_key.size()); + PutFixed64(result, PackSequenceAndType(key.sequence, key.type)); +} + +std::string ParsedInternalKey::DebugString() const { + char buf[50]; + snprintf(buf, sizeof(buf), "' @ %llu : %d", + (unsigned long long) sequence, + int(type)); + std::string result = "'"; + result += EscapeString(user_key.ToString()); + result += buf; + return result; +} + +std::string InternalKey::DebugString() const { + std::string result; + ParsedInternalKey parsed; + if (ParseInternalKey(rep_, &parsed)) { + result = parsed.DebugString(); + } else { + result = "(bad)"; + result.append(EscapeString(rep_)); + } + return result; +} + +const char* InternalKeyComparator::Name() const { + return "leveldb.InternalKeyComparator"; +} + +int InternalKeyComparator::Compare(const Slice& akey, const Slice& bkey) const { + // Order by: + // increasing user key (according to user-supplied comparator) + // decreasing sequence number + // decreasing type (though sequence# should be enough to disambiguate) + int r = user_comparator_->Compare(ExtractUserKey(akey), ExtractUserKey(bkey)); + if (r == 0) { + const uint64_t anum = DecodeFixed64(akey.data() + akey.size() - 8); + const uint64_t bnum = DecodeFixed64(bkey.data() + bkey.size() - 8); + if (anum > bnum) { + r = -1; + } else if (anum < bnum) { + r = +1; + } + } + return r; +} + +void InternalKeyComparator::FindShortestSeparator( + std::string* start, + const Slice& limit) const { + // Attempt to shorten the user portion of the key + Slice user_start = ExtractUserKey(*start); + Slice user_limit = ExtractUserKey(limit); + std::string tmp(user_start.data(), user_start.size()); + user_comparator_->FindShortestSeparator(&tmp, user_limit); + if (tmp.size() < user_start.size() && + user_comparator_->Compare(user_start, tmp) < 0) { + // User key has become shorter physically, but larger logically. + // Tack on the earliest possible number to the shortened user key. + PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek)); + assert(this->Compare(*start, tmp) < 0); + assert(this->Compare(tmp, limit) < 0); + start->swap(tmp); + } +} + +void InternalKeyComparator::FindShortSuccessor(std::string* key) const { + Slice user_key = ExtractUserKey(*key); + std::string tmp(user_key.data(), user_key.size()); + user_comparator_->FindShortSuccessor(&tmp); + if (tmp.size() < user_key.size() && + user_comparator_->Compare(user_key, tmp) < 0) { + // User key has become shorter physically, but larger logically. + // Tack on the earliest possible number to the shortened user key. + PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek)); + assert(this->Compare(*key, tmp) < 0); + key->swap(tmp); + } +} + +const char* InternalFilterPolicy::Name() const { + return user_policy_->Name(); +} + +void InternalFilterPolicy::CreateFilter(const Slice* keys, int n, + std::string* dst) const { + // We rely on the fact that the code in table.cc does not mind us + // adjusting keys[]. + Slice* mkey = const_cast(keys); + for (int i = 0; i < n; i++) { + mkey[i] = ExtractUserKey(keys[i]); + // TODO(sanjay): Suppress dups? + } + user_policy_->CreateFilter(keys, n, dst); +} + +bool InternalFilterPolicy::KeyMayMatch(const Slice& key, const Slice& f) const { + return user_policy_->KeyMayMatch(ExtractUserKey(key), f); +} + +LookupKey::LookupKey(const Slice& user_key, SequenceNumber s) { + size_t usize = user_key.size(); + size_t needed = usize + 13; // A conservative estimate + char* dst; + if (needed <= sizeof(space_)) { + dst = space_; + } else { + dst = new char[needed]; + } + start_ = dst; + dst = EncodeVarint32(dst, usize + 8); + kstart_ = dst; + memcpy(dst, user_key.data(), usize); + dst += usize; + EncodeFixed64(dst, PackSequenceAndType(s, kValueTypeForSeek)); + dst += 8; + end_ = dst; +} + +} // namespace leveldb diff --git a/src/leveldb-lin/leveldb/db/dbformat.h b/src/leveldb-lin/leveldb/db/dbformat.h new file mode 100644 index 0000000..5d8a032 --- /dev/null +++ b/src/leveldb-lin/leveldb/db/dbformat.h @@ -0,0 +1,230 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_FORMAT_H_ +#define STORAGE_LEVELDB_DB_FORMAT_H_ + +#include +#include "leveldb/comparator.h" +#include "leveldb/db.h" +#include "leveldb/filter_policy.h" +#include "leveldb/slice.h" +#include "leveldb/table_builder.h" +#include "util/coding.h" +#include "util/logging.h" + +namespace leveldb { + +// Grouping of constants. We may want to make some of these +// parameters set via options. +namespace config { +static const int kNumLevels = 7; + +// Level-0 compaction is started when we hit this many files. +static const int kL0_CompactionTrigger = 4; + +// Soft limit on number of level-0 files. We slow down writes at this point. +static const int kL0_SlowdownWritesTrigger = 8; + +// Maximum number of level-0 files. We stop writes at this point. +static const int kL0_StopWritesTrigger = 12; + +// Maximum level to which a new compacted memtable is pushed if it +// does not create overlap. We try to push to level 2 to avoid the +// relatively expensive level 0=>1 compactions and to avoid some +// expensive manifest file operations. We do not push all the way to +// the largest level since that can generate a lot of wasted disk +// space if the same key space is being repeatedly overwritten. +static const int kMaxMemCompactLevel = 2; + +// Approximate gap in bytes between samples of data read during iteration. +static const int kReadBytesPeriod = 1048576; + +} // namespace config + +class InternalKey; + +// Value types encoded as the last component of internal keys. +// DO NOT CHANGE THESE ENUM VALUES: they are embedded in the on-disk +// data structures. +enum ValueType { + kTypeDeletion = 0x0, + kTypeValue = 0x1 +}; +// kValueTypeForSeek defines the ValueType that should be passed when +// constructing a ParsedInternalKey object for seeking to a particular +// sequence number (since we sort sequence numbers in decreasing order +// and the value type is embedded as the low 8 bits in the sequence +// number in internal keys, we need to use the highest-numbered +// ValueType, not the lowest). +static const ValueType kValueTypeForSeek = kTypeValue; + +typedef uint64_t SequenceNumber; + +// We leave eight bits empty at the bottom so a type and sequence# +// can be packed together into 64-bits. +static const SequenceNumber kMaxSequenceNumber = + ((0x1ull << 56) - 1); + +struct ParsedInternalKey { + Slice user_key; + SequenceNumber sequence; + ValueType type; + + ParsedInternalKey() { } // Intentionally left uninitialized (for speed) + ParsedInternalKey(const Slice& u, const SequenceNumber& seq, ValueType t) + : user_key(u), sequence(seq), type(t) { } + std::string DebugString() const; +}; + +// Return the length of the encoding of "key". +inline size_t InternalKeyEncodingLength(const ParsedInternalKey& key) { + return key.user_key.size() + 8; +} + +// Append the serialization of "key" to *result. +extern void AppendInternalKey(std::string* result, + const ParsedInternalKey& key); + +// Attempt to parse an internal key from "internal_key". On success, +// stores the parsed data in "*result", and returns true. +// +// On error, returns false, leaves "*result" in an undefined state. +extern bool ParseInternalKey(const Slice& internal_key, + ParsedInternalKey* result); + +// Returns the user key portion of an internal key. +inline Slice ExtractUserKey(const Slice& internal_key) { + assert(internal_key.size() >= 8); + return Slice(internal_key.data(), internal_key.size() - 8); +} + +inline ValueType ExtractValueType(const Slice& internal_key) { + assert(internal_key.size() >= 8); + const size_t n = internal_key.size(); + uint64_t num = DecodeFixed64(internal_key.data() + n - 8); + unsigned char c = num & 0xff; + return static_cast(c); +} + +// A comparator for internal keys that uses a specified comparator for +// the user key portion and breaks ties by decreasing sequence number. +class InternalKeyComparator : public Comparator { + private: + const Comparator* user_comparator_; + public: + explicit InternalKeyComparator(const Comparator* c) : user_comparator_(c) { } + virtual const char* Name() const; + virtual int Compare(const Slice& a, const Slice& b) const; + virtual void FindShortestSeparator( + std::string* start, + const Slice& limit) const; + virtual void FindShortSuccessor(std::string* key) const; + + const Comparator* user_comparator() const { return user_comparator_; } + + int Compare(const InternalKey& a, const InternalKey& b) const; +}; + +// Filter policy wrapper that converts from internal keys to user keys +class InternalFilterPolicy : public FilterPolicy { + private: + const FilterPolicy* const user_policy_; + public: + explicit InternalFilterPolicy(const FilterPolicy* p) : user_policy_(p) { } + virtual const char* Name() const; + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const; + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const; +}; + +// Modules in this directory should keep internal keys wrapped inside +// the following class instead of plain strings so that we do not +// incorrectly use string comparisons instead of an InternalKeyComparator. +class InternalKey { + private: + std::string rep_; + public: + InternalKey() { } // Leave rep_ as empty to indicate it is invalid + InternalKey(const Slice& user_key, SequenceNumber s, ValueType t) { + AppendInternalKey(&rep_, ParsedInternalKey(user_key, s, t)); + } + + void DecodeFrom(const Slice& s) { rep_.assign(s.data(), s.size()); } + Slice Encode() const { + assert(!rep_.empty()); + return rep_; + } + + Slice user_key() const { return ExtractUserKey(rep_); } + + void SetFrom(const ParsedInternalKey& p) { + rep_.clear(); + AppendInternalKey(&rep_, p); + } + + void Clear() { rep_.clear(); } + + std::string DebugString() const; +}; + +inline int InternalKeyComparator::Compare( + const InternalKey& a, const InternalKey& b) const { + return Compare(a.Encode(), b.Encode()); +} + +inline bool ParseInternalKey(const Slice& internal_key, + ParsedInternalKey* result) { + const size_t n = internal_key.size(); + if (n < 8) return false; + uint64_t num = DecodeFixed64(internal_key.data() + n - 8); + unsigned char c = num & 0xff; + result->sequence = num >> 8; + result->type = static_cast(c); + result->user_key = Slice(internal_key.data(), n - 8); + return (c <= static_cast(kTypeValue)); +} + +// A helper class useful for DBImpl::Get() +class LookupKey { + public: + // Initialize *this for looking up user_key at a snapshot with + // the specified sequence number. + LookupKey(const Slice& user_key, SequenceNumber sequence); + + ~LookupKey(); + + // Return a key suitable for lookup in a MemTable. + Slice memtable_key() const { return Slice(start_, end_ - start_); } + + // Return an internal key (suitable for passing to an internal iterator) + Slice internal_key() const { return Slice(kstart_, end_ - kstart_); } + + // Return the user key + Slice user_key() const { return Slice(kstart_, end_ - kstart_ - 8); } + + private: + // We construct a char array of the form: + // klength varint32 <-- start_ + // userkey char[klength] <-- kstart_ + // tag uint64 + // <-- end_ + // The array is a suitable MemTable key. + // The suffix starting with "userkey" can be used as an InternalKey. + const char* start_; + const char* kstart_; + const char* end_; + char space_[200]; // Avoid allocation for short keys + + // No copying allowed + LookupKey(const LookupKey&); + void operator=(const LookupKey&); +}; + +inline LookupKey::~LookupKey() { + if (start_ != space_) delete[] start_; +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_FORMAT_H_ diff --git a/src/leveldb-lin/leveldb/db/dbformat_test.cc b/src/leveldb-lin/leveldb/db/dbformat_test.cc new file mode 100644 index 0000000..5d82f5d --- /dev/null +++ b/src/leveldb-lin/leveldb/db/dbformat_test.cc @@ -0,0 +1,112 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/dbformat.h" +#include "util/logging.h" +#include "util/testharness.h" + +namespace leveldb { + +static std::string IKey(const std::string& user_key, + uint64_t seq, + ValueType vt) { + std::string encoded; + AppendInternalKey(&encoded, ParsedInternalKey(user_key, seq, vt)); + return encoded; +} + +static std::string Shorten(const std::string& s, const std::string& l) { + std::string result = s; + InternalKeyComparator(BytewiseComparator()).FindShortestSeparator(&result, l); + return result; +} + +static std::string ShortSuccessor(const std::string& s) { + std::string result = s; + InternalKeyComparator(BytewiseComparator()).FindShortSuccessor(&result); + return result; +} + +static void TestKey(const std::string& key, + uint64_t seq, + ValueType vt) { + std::string encoded = IKey(key, seq, vt); + + Slice in(encoded); + ParsedInternalKey decoded("", 0, kTypeValue); + + ASSERT_TRUE(ParseInternalKey(in, &decoded)); + ASSERT_EQ(key, decoded.user_key.ToString()); + ASSERT_EQ(seq, decoded.sequence); + ASSERT_EQ(vt, decoded.type); + + ASSERT_TRUE(!ParseInternalKey(Slice("bar"), &decoded)); +} + +class FormatTest { }; + +TEST(FormatTest, InternalKey_EncodeDecode) { + const char* keys[] = { "", "k", "hello", "longggggggggggggggggggggg" }; + const uint64_t seq[] = { + 1, 2, 3, + (1ull << 8) - 1, 1ull << 8, (1ull << 8) + 1, + (1ull << 16) - 1, 1ull << 16, (1ull << 16) + 1, + (1ull << 32) - 1, 1ull << 32, (1ull << 32) + 1 + }; + for (int k = 0; k < sizeof(keys) / sizeof(keys[0]); k++) { + for (int s = 0; s < sizeof(seq) / sizeof(seq[0]); s++) { + TestKey(keys[k], seq[s], kTypeValue); + TestKey("hello", 1, kTypeDeletion); + } + } +} + +TEST(FormatTest, InternalKeyShortSeparator) { + // When user keys are same + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 99, kTypeValue))); + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 101, kTypeValue))); + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 100, kTypeValue))); + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 100, kTypeDeletion))); + + // When user keys are misordered + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("bar", 99, kTypeValue))); + + // When user keys are different, but correctly ordered + ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek), + Shorten(IKey("foo", 100, kTypeValue), + IKey("hello", 200, kTypeValue))); + + // When start user key is prefix of limit user key + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foobar", 200, kTypeValue))); + + // When limit user key is prefix of start user key + ASSERT_EQ(IKey("foobar", 100, kTypeValue), + Shorten(IKey("foobar", 100, kTypeValue), + IKey("foo", 200, kTypeValue))); +} + +TEST(FormatTest, InternalKeyShortestSuccessor) { + ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek), + ShortSuccessor(IKey("foo", 100, kTypeValue))); + ASSERT_EQ(IKey("\xff\xff", 100, kTypeValue), + ShortSuccessor(IKey("\xff\xff", 100, kTypeValue))); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/leveldb/db/filename.cc b/src/leveldb-lin/leveldb/db/filename.cc new file mode 100644 index 0000000..da32946 --- /dev/null +++ b/src/leveldb-lin/leveldb/db/filename.cc @@ -0,0 +1,144 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include "db/filename.h" +#include "db/dbformat.h" +#include "leveldb/env.h" +#include "util/logging.h" + +namespace leveldb { + +// A utility routine: write "data" to the named file and Sync() it. +extern Status WriteStringToFileSync(Env* env, const Slice& data, + const std::string& fname); + +static std::string MakeFileName(const std::string& name, uint64_t number, + const char* suffix) { + char buf[100]; + snprintf(buf, sizeof(buf), "/%06llu.%s", + static_cast(number), + suffix); + return name + buf; +} + +std::string LogFileName(const std::string& name, uint64_t number) { + assert(number > 0); + return MakeFileName(name, number, "log"); +} + +std::string TableFileName(const std::string& name, uint64_t number) { + assert(number > 0); + return MakeFileName(name, number, "ldb"); +} + +std::string SSTTableFileName(const std::string& name, uint64_t number) { + assert(number > 0); + return MakeFileName(name, number, "sst"); +} + +std::string DescriptorFileName(const std::string& dbname, uint64_t number) { + assert(number > 0); + char buf[100]; + snprintf(buf, sizeof(buf), "/MANIFEST-%06llu", + static_cast(number)); + return dbname + buf; +} + +std::string CurrentFileName(const std::string& dbname) { + return dbname + "/CURRENT"; +} + +std::string LockFileName(const std::string& dbname) { + return dbname + "/LOCK"; +} + +std::string TempFileName(const std::string& dbname, uint64_t number) { + assert(number > 0); + return MakeFileName(dbname, number, "dbtmp"); +} + +std::string InfoLogFileName(const std::string& dbname) { + return dbname + "/LOG"; +} + +// Return the name of the old info log file for "dbname". +std::string OldInfoLogFileName(const std::string& dbname) { + return dbname + "/LOG.old"; +} + + +// Owned filenames have the form: +// dbname/CURRENT +// dbname/LOCK +// dbname/LOG +// dbname/LOG.old +// dbname/MANIFEST-[0-9]+ +// dbname/[0-9]+.(log|sst|ldb) +bool ParseFileName(const std::string& fname, + uint64_t* number, + FileType* type) { + Slice rest(fname); + if (rest == "CURRENT") { + *number = 0; + *type = kCurrentFile; + } else if (rest == "LOCK") { + *number = 0; + *type = kDBLockFile; + } else if (rest == "LOG" || rest == "LOG.old") { + *number = 0; + *type = kInfoLogFile; + } else if (rest.starts_with("MANIFEST-")) { + rest.remove_prefix(strlen("MANIFEST-")); + uint64_t num; + if (!ConsumeDecimalNumber(&rest, &num)) { + return false; + } + if (!rest.empty()) { + return false; + } + *type = kDescriptorFile; + *number = num; + } else { + // Avoid strtoull() to keep filename format independent of the + // current locale + uint64_t num; + if (!ConsumeDecimalNumber(&rest, &num)) { + return false; + } + Slice suffix = rest; + if (suffix == Slice(".log")) { + *type = kLogFile; + } else if (suffix == Slice(".sst") || suffix == Slice(".ldb")) { + *type = kTableFile; + } else if (suffix == Slice(".dbtmp")) { + *type = kTempFile; + } else { + return false; + } + *number = num; + } + return true; +} + +Status SetCurrentFile(Env* env, const std::string& dbname, + uint64_t descriptor_number) { + // Remove leading "dbname/" and add newline to manifest file name + std::string manifest = DescriptorFileName(dbname, descriptor_number); + Slice contents = manifest; + assert(contents.starts_with(dbname + "/")); + contents.remove_prefix(dbname.size() + 1); + std::string tmp = TempFileName(dbname, descriptor_number); + Status s = WriteStringToFileSync(env, contents.ToString() + "\n", tmp); + if (s.ok()) { + s = env->RenameFile(tmp, CurrentFileName(dbname)); + } + if (!s.ok()) { + env->DeleteFile(tmp); + } + return s; +} + +} // namespace leveldb diff --git a/src/leveldb-lin/leveldb/db/filename.h b/src/leveldb-lin/leveldb/db/filename.h new file mode 100644 index 0000000..87a7526 --- /dev/null +++ b/src/leveldb-lin/leveldb/db/filename.h @@ -0,0 +1,85 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// File names used by DB code + +#ifndef STORAGE_LEVELDB_DB_FILENAME_H_ +#define STORAGE_LEVELDB_DB_FILENAME_H_ + +#include +#include +#include "leveldb/slice.h" +#include "leveldb/status.h" +#include "port/port.h" + +namespace leveldb { + +class Env; + +enum FileType { + kLogFile, + kDBLockFile, + kTableFile, + kDescriptorFile, + kCurrentFile, + kTempFile, + kInfoLogFile // Either the current one, or an old one +}; + +// Return the name of the log file with the specified number +// in the db named by "dbname". The result will be prefixed with +// "dbname". +extern std::string LogFileName(const std::string& dbname, uint64_t number); + +// Return the name of the sstable with the specified number +// in the db named by "dbname". The result will be prefixed with +// "dbname". +extern std::string TableFileName(const std::string& dbname, uint64_t number); + +// Return the legacy file name for an sstable with the specified number +// in the db named by "dbname". The result will be prefixed with +// "dbname". +extern std::string SSTTableFileName(const std::string& dbname, uint64_t number); + +// Return the name of the descriptor file for the db named by +// "dbname" and the specified incarnation number. The result will be +// prefixed with "dbname". +extern std::string DescriptorFileName(const std::string& dbname, + uint64_t number); + +// Return the name of the current file. This file contains the name +// of the current manifest file. The result will be prefixed with +// "dbname". +extern std::string CurrentFileName(const std::string& dbname); + +// Return the name of the lock file for the db named by +// "dbname". The result will be prefixed with "dbname". +extern std::string LockFileName(const std::string& dbname); + +// Return the name of a temporary file owned by the db named "dbname". +// The result will be prefixed with "dbname". +extern std::string TempFileName(const std::string& dbname, uint64_t number); + +// Return the name of the info log file for "dbname". +extern std::string InfoLogFileName(const std::string& dbname); + +// Return the name of the old info log file for "dbname". +extern std::string OldInfoLogFileName(const std::string& dbname); + +// If filename is a leveldb file, store the type of the file in *type. +// The number encoded in the filename is stored in *number. If the +// filename was successfully parsed, returns true. Else return false. +extern bool ParseFileName(const std::string& filename, + uint64_t* number, + FileType* type); + +// Make the CURRENT file point to the descriptor file with the +// specified number. +extern Status SetCurrentFile(Env* env, const std::string& dbname, + uint64_t descriptor_number); + + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_FILENAME_H_ diff --git a/src/leveldb-lin/leveldb/db/filename_test.cc b/src/leveldb-lin/leveldb/db/filename_test.cc new file mode 100644 index 0000000..a32556d --- /dev/null +++ b/src/leveldb-lin/leveldb/db/filename_test.cc @@ -0,0 +1,123 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/filename.h" + +#include "db/dbformat.h" +#include "port/port.h" +#include "util/logging.h" +#include "util/testharness.h" + +namespace leveldb { + +class FileNameTest { }; + +TEST(FileNameTest, Parse) { + Slice db; + FileType type; + uint64_t number; + + // Successful parses + static struct { + const char* fname; + uint64_t number; + FileType type; + } cases[] = { + { "100.log", 100, kLogFile }, + { "0.log", 0, kLogFile }, + { "0.sst", 0, kTableFile }, + { "0.ldb", 0, kTableFile }, + { "CURRENT", 0, kCurrentFile }, + { "LOCK", 0, kDBLockFile }, + { "MANIFEST-2", 2, kDescriptorFile }, + { "MANIFEST-7", 7, kDescriptorFile }, + { "LOG", 0, kInfoLogFile }, + { "LOG.old", 0, kInfoLogFile }, + { "18446744073709551615.log", 18446744073709551615ull, kLogFile }, + }; + for (int i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + std::string f = cases[i].fname; + ASSERT_TRUE(ParseFileName(f, &number, &type)) << f; + ASSERT_EQ(cases[i].type, type) << f; + ASSERT_EQ(cases[i].number, number) << f; + } + + // Errors + static const char* errors[] = { + "", + "foo", + "foo-dx-100.log", + ".log", + "", + "manifest", + "CURREN", + "CURRENTX", + "MANIFES", + "MANIFEST", + "MANIFEST-", + "XMANIFEST-3", + "MANIFEST-3x", + "LOC", + "LOCKx", + "LO", + "LOGx", + "18446744073709551616.log", + "184467440737095516150.log", + "100", + "100.", + "100.lop" + }; + for (int i = 0; i < sizeof(errors) / sizeof(errors[0]); i++) { + std::string f = errors[i]; + ASSERT_TRUE(!ParseFileName(f, &number, &type)) << f; + } +} + +TEST(FileNameTest, Construction) { + uint64_t number; + FileType type; + std::string fname; + + fname = CurrentFileName("foo"); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(0, number); + ASSERT_EQ(kCurrentFile, type); + + fname = LockFileName("foo"); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(0, number); + ASSERT_EQ(kDBLockFile, type); + + fname = LogFileName("foo", 192); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(192, number); + ASSERT_EQ(kLogFile, type); + + fname = TableFileName("bar", 200); + ASSERT_EQ("bar/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(200, number); + ASSERT_EQ(kTableFile, type); + + fname = DescriptorFileName("bar", 100); + ASSERT_EQ("bar/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(100, number); + ASSERT_EQ(kDescriptorFile, type); + + fname = TempFileName("tmp", 999); + ASSERT_EQ("tmp/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(999, number); + ASSERT_EQ(kTempFile, type); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/leveldb/db/leveldb_main.cc b/src/leveldb-lin/leveldb/db/leveldb_main.cc new file mode 100644 index 0000000..995d761 --- /dev/null +++ b/src/leveldb-lin/leveldb/db/leveldb_main.cc @@ -0,0 +1,238 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include "db/dbformat.h" +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/version_edit.h" +#include "db/write_batch_internal.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "leveldb/options.h" +#include "leveldb/status.h" +#include "leveldb/table.h" +#include "leveldb/write_batch.h" +#include "util/logging.h" + +namespace leveldb { + +namespace { + +bool GuessType(const std::string& fname, FileType* type) { + size_t pos = fname.rfind('/'); + std::string basename; + if (pos == std::string::npos) { + basename = fname; + } else { + basename = std::string(fname.data() + pos + 1, fname.size() - pos - 1); + } + uint64_t ignored; + return ParseFileName(basename, &ignored, type); +} + +// Notified when log reader encounters corruption. +class CorruptionReporter : public log::Reader::Reporter { + public: + virtual void Corruption(size_t bytes, const Status& status) { + printf("corruption: %d bytes; %s\n", + static_cast(bytes), + status.ToString().c_str()); + } +}; + +// Print contents of a log file. (*func)() is called on every record. +bool PrintLogContents(Env* env, const std::string& fname, + void (*func)(Slice)) { + SequentialFile* file; + Status s = env->NewSequentialFile(fname, &file); + if (!s.ok()) { + fprintf(stderr, "%s\n", s.ToString().c_str()); + return false; + } + CorruptionReporter reporter; + log::Reader reader(file, &reporter, true, 0); + Slice record; + std::string scratch; + while (reader.ReadRecord(&record, &scratch)) { + printf("--- offset %llu; ", + static_cast(reader.LastRecordOffset())); + (*func)(record); + } + delete file; + return true; +} + +// Called on every item found in a WriteBatch. +class WriteBatchItemPrinter : public WriteBatch::Handler { + public: + uint64_t offset_; + uint64_t sequence_; + + virtual void Put(const Slice& key, const Slice& value) { + printf(" put '%s' '%s'\n", + EscapeString(key).c_str(), + EscapeString(value).c_str()); + } + virtual void Delete(const Slice& key) { + printf(" del '%s'\n", + EscapeString(key).c_str()); + } +}; + + +// Called on every log record (each one of which is a WriteBatch) +// found in a kLogFile. +static void WriteBatchPrinter(Slice record) { + if (record.size() < 12) { + printf("log record length %d is too small\n", + static_cast(record.size())); + return; + } + WriteBatch batch; + WriteBatchInternal::SetContents(&batch, record); + printf("sequence %llu\n", + static_cast(WriteBatchInternal::Sequence(&batch))); + WriteBatchItemPrinter batch_item_printer; + Status s = batch.Iterate(&batch_item_printer); + if (!s.ok()) { + printf(" error: %s\n", s.ToString().c_str()); + } +} + +bool DumpLog(Env* env, const std::string& fname) { + return PrintLogContents(env, fname, WriteBatchPrinter); +} + +// Called on every log record (each one of which is a WriteBatch) +// found in a kDescriptorFile. +static void VersionEditPrinter(Slice record) { + VersionEdit edit; + Status s = edit.DecodeFrom(record); + if (!s.ok()) { + printf("%s\n", s.ToString().c_str()); + return; + } + printf("%s", edit.DebugString().c_str()); +} + +bool DumpDescriptor(Env* env, const std::string& fname) { + return PrintLogContents(env, fname, VersionEditPrinter); +} + +bool DumpTable(Env* env, const std::string& fname) { + uint64_t file_size; + RandomAccessFile* file = NULL; + Table* table = NULL; + Status s = env->GetFileSize(fname, &file_size); + if (s.ok()) { + s = env->NewRandomAccessFile(fname, &file); + } + if (s.ok()) { + // We use the default comparator, which may or may not match the + // comparator used in this database. However this should not cause + // problems since we only use Table operations that do not require + // any comparisons. In particular, we do not call Seek or Prev. + s = Table::Open(Options(), file, file_size, &table); + } + if (!s.ok()) { + fprintf(stderr, "%s\n", s.ToString().c_str()); + delete table; + delete file; + return false; + } + + ReadOptions ro; + ro.fill_cache = false; + Iterator* iter = table->NewIterator(ro); + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + ParsedInternalKey key; + if (!ParseInternalKey(iter->key(), &key)) { + printf("badkey '%s' => '%s'\n", + EscapeString(iter->key()).c_str(), + EscapeString(iter->value()).c_str()); + } else { + char kbuf[20]; + const char* type; + if (key.type == kTypeDeletion) { + type = "del"; + } else if (key.type == kTypeValue) { + type = "val"; + } else { + snprintf(kbuf, sizeof(kbuf), "%d", static_cast(key.type)); + type = kbuf; + } + printf("'%s' @ %8llu : %s => '%s'\n", + EscapeString(key.user_key).c_str(), + static_cast(key.sequence), + type, + EscapeString(iter->value()).c_str()); + } + } + s = iter->status(); + if (!s.ok()) { + printf("iterator error: %s\n", s.ToString().c_str()); + } + + delete iter; + delete table; + delete file; + return true; +} + +bool DumpFile(Env* env, const std::string& fname) { + FileType ftype; + if (!GuessType(fname, &ftype)) { + fprintf(stderr, "%s: unknown file type\n", fname.c_str()); + return false; + } + switch (ftype) { + case kLogFile: return DumpLog(env, fname); + case kDescriptorFile: return DumpDescriptor(env, fname); + case kTableFile: return DumpTable(env, fname); + + default: { + fprintf(stderr, "%s: not a dump-able file type\n", fname.c_str()); + break; + } + } + return false; +} + +bool HandleDumpCommand(Env* env, char** files, int num) { + bool ok = true; + for (int i = 0; i < num; i++) { + ok &= DumpFile(env, files[i]); + } + return ok; +} + +} +} // namespace leveldb + +static void Usage() { + fprintf( + stderr, + "Usage: leveldbutil command...\n" + " dump files... -- dump contents of specified files\n" + ); +} + +int main(int argc, char** argv) { + leveldb::Env* env = leveldb::Env::Default(); + bool ok = true; + if (argc < 2) { + Usage(); + ok = false; + } else { + std::string command = argv[1]; + if (command == "dump") { + ok = leveldb::HandleDumpCommand(env, argv+2, argc-2); + } else { + Usage(); + ok = false; + } + } + return (ok ? 0 : 1); +} diff --git a/src/leveldb-lin/leveldb/db/log_format.h b/src/leveldb-lin/leveldb/db/log_format.h new file mode 100644 index 0000000..2690cb9 --- /dev/null +++ b/src/leveldb-lin/leveldb/db/log_format.h @@ -0,0 +1,35 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Log format information shared by reader and writer. +// See ../doc/log_format.txt for more detail. + +#ifndef STORAGE_LEVELDB_DB_LOG_FORMAT_H_ +#define STORAGE_LEVELDB_DB_LOG_FORMAT_H_ + +namespace leveldb { +namespace log { + +enum RecordType { + // Zero is reserved for preallocated files + kZeroType = 0, + + kFullType = 1, + + // For fragments + kFirstType = 2, + kMiddleType = 3, + kLastType = 4 +}; +static const int kMaxRecordType = kLastType; + +static const int kBlockSize = 32768; + +// Header is checksum (4 bytes), type (1 byte), length (2 bytes). +static const int kHeaderSize = 4 + 1 + 2; + +} // namespace log +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_LOG_FORMAT_H_ diff --git a/src/leveldb-lin/leveldb/db/log_reader.cc b/src/leveldb-lin/leveldb/db/log_reader.cc new file mode 100644 index 0000000..b35f115 --- /dev/null +++ b/src/leveldb-lin/leveldb/db/log_reader.cc @@ -0,0 +1,259 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/log_reader.h" + +#include +#include "leveldb/env.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { +namespace log { + +Reader::Reporter::~Reporter() { +} + +Reader::Reader(SequentialFile* file, Reporter* reporter, bool checksum, + uint64_t initial_offset) + : file_(file), + reporter_(reporter), + checksum_(checksum), + backing_store_(new char[kBlockSize]), + buffer_(), + eof_(false), + last_record_offset_(0), + end_of_buffer_offset_(0), + initial_offset_(initial_offset) { +} + +Reader::~Reader() { + delete[] backing_store_; +} + +bool Reader::SkipToInitialBlock() { + size_t offset_in_block = initial_offset_ % kBlockSize; + uint64_t block_start_location = initial_offset_ - offset_in_block; + + // Don't search a block if we'd be in the trailer + if (offset_in_block > kBlockSize - 6) { + offset_in_block = 0; + block_start_location += kBlockSize; + } + + end_of_buffer_offset_ = block_start_location; + + // Skip to start of first block that can contain the initial record + if (block_start_location > 0) { + Status skip_status = file_->Skip(block_start_location); + if (!skip_status.ok()) { + ReportDrop(block_start_location, skip_status); + return false; + } + } + + return true; +} + +bool Reader::ReadRecord(Slice* record, std::string* scratch) { + if (last_record_offset_ < initial_offset_) { + if (!SkipToInitialBlock()) { + return false; + } + } + + scratch->clear(); + record->clear(); + bool in_fragmented_record = false; + // Record offset of the logical record that we're reading + // 0 is a dummy value to make compilers happy + uint64_t prospective_record_offset = 0; + + Slice fragment; + while (true) { + uint64_t physical_record_offset = end_of_buffer_offset_ - buffer_.size(); + const unsigned int record_type = ReadPhysicalRecord(&fragment); + switch (record_type) { + case kFullType: + if (in_fragmented_record) { + // Handle bug in earlier versions of log::Writer where + // it could emit an empty kFirstType record at the tail end + // of a block followed by a kFullType or kFirstType record + // at the beginning of the next block. + if (scratch->empty()) { + in_fragmented_record = false; + } else { + ReportCorruption(scratch->size(), "partial record without end(1)"); + } + } + prospective_record_offset = physical_record_offset; + scratch->clear(); + *record = fragment; + last_record_offset_ = prospective_record_offset; + return true; + + case kFirstType: + if (in_fragmented_record) { + // Handle bug in earlier versions of log::Writer where + // it could emit an empty kFirstType record at the tail end + // of a block followed by a kFullType or kFirstType record + // at the beginning of the next block. + if (scratch->empty()) { + in_fragmented_record = false; + } else { + ReportCorruption(scratch->size(), "partial record without end(2)"); + } + } + prospective_record_offset = physical_record_offset; + scratch->assign(fragment.data(), fragment.size()); + in_fragmented_record = true; + break; + + case kMiddleType: + if (!in_fragmented_record) { + ReportCorruption(fragment.size(), + "missing start of fragmented record(1)"); + } else { + scratch->append(fragment.data(), fragment.size()); + } + break; + + case kLastType: + if (!in_fragmented_record) { + ReportCorruption(fragment.size(), + "missing start of fragmented record(2)"); + } else { + scratch->append(fragment.data(), fragment.size()); + *record = Slice(*scratch); + last_record_offset_ = prospective_record_offset; + return true; + } + break; + + case kEof: + if (in_fragmented_record) { + ReportCorruption(scratch->size(), "partial record without end(3)"); + scratch->clear(); + } + return false; + + case kBadRecord: + if (in_fragmented_record) { + ReportCorruption(scratch->size(), "error in middle of record"); + in_fragmented_record = false; + scratch->clear(); + } + break; + + default: { + char buf[40]; + snprintf(buf, sizeof(buf), "unknown record type %u", record_type); + ReportCorruption( + (fragment.size() + (in_fragmented_record ? scratch->size() : 0)), + buf); + in_fragmented_record = false; + scratch->clear(); + break; + } + } + } + return false; +} + +uint64_t Reader::LastRecordOffset() { + return last_record_offset_; +} + +void Reader::ReportCorruption(size_t bytes, const char* reason) { + ReportDrop(bytes, Status::Corruption(reason)); +} + +void Reader::ReportDrop(size_t bytes, const Status& reason) { + if (reporter_ != NULL && + end_of_buffer_offset_ - buffer_.size() - bytes >= initial_offset_) { + reporter_->Corruption(bytes, reason); + } +} + +unsigned int Reader::ReadPhysicalRecord(Slice* result) { + while (true) { + if (buffer_.size() < kHeaderSize) { + if (!eof_) { + // Last read was a full read, so this is a trailer to skip + buffer_.clear(); + Status status = file_->Read(kBlockSize, &buffer_, backing_store_); + end_of_buffer_offset_ += buffer_.size(); + if (!status.ok()) { + buffer_.clear(); + ReportDrop(kBlockSize, status); + eof_ = true; + return kEof; + } else if (buffer_.size() < kBlockSize) { + eof_ = true; + } + continue; + } else if (buffer_.size() == 0) { + // End of file + return kEof; + } else { + size_t drop_size = buffer_.size(); + buffer_.clear(); + ReportCorruption(drop_size, "truncated record at end of file"); + return kEof; + } + } + + // Parse the header + const char* header = buffer_.data(); + const uint32_t a = static_cast(header[4]) & 0xff; + const uint32_t b = static_cast(header[5]) & 0xff; + const unsigned int type = header[6]; + const uint32_t length = a | (b << 8); + if (kHeaderSize + length > buffer_.size()) { + size_t drop_size = buffer_.size(); + buffer_.clear(); + ReportCorruption(drop_size, "bad record length"); + return kBadRecord; + } + + if (type == kZeroType && length == 0) { + // Skip zero length record without reporting any drops since + // such records are produced by the mmap based writing code in + // env_posix.cc that preallocates file regions. + buffer_.clear(); + return kBadRecord; + } + + // Check crc + if (checksum_) { + uint32_t expected_crc = crc32c::Unmask(DecodeFixed32(header)); + uint32_t actual_crc = crc32c::Value(header + 6, 1 + length); + if (actual_crc != expected_crc) { + // Drop the rest of the buffer since "length" itself may have + // been corrupted and if we trust it, we could find some + // fragment of a real log record that just happens to look + // like a valid log record. + size_t drop_size = buffer_.size(); + buffer_.clear(); + ReportCorruption(drop_size, "checksum mismatch"); + return kBadRecord; + } + } + + buffer_.remove_prefix(kHeaderSize + length); + + // Skip physical record that started before initial_offset_ + if (end_of_buffer_offset_ - buffer_.size() - kHeaderSize - length < + initial_offset_) { + result->clear(); + return kBadRecord; + } + + *result = Slice(header + kHeaderSize, length); + return type; + } +} + +} // namespace log +} // namespace leveldb diff --git a/src/leveldb-lin/leveldb/db/log_reader.h b/src/leveldb-lin/leveldb/db/log_reader.h new file mode 100644 index 0000000..82d4bee --- /dev/null +++ b/src/leveldb-lin/leveldb/db/log_reader.h @@ -0,0 +1,108 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_LOG_READER_H_ +#define STORAGE_LEVELDB_DB_LOG_READER_H_ + +#include + +#include "db/log_format.h" +#include "leveldb/slice.h" +#include "leveldb/status.h" + +namespace leveldb { + +class SequentialFile; + +namespace log { + +class Reader { + public: + // Interface for reporting errors. + class Reporter { + public: + virtual ~Reporter(); + + // Some corruption was detected. "size" is the approximate number + // of bytes dropped due to the corruption. + virtual void Corruption(size_t bytes, const Status& status) = 0; + }; + + // Create a reader that will return log records from "*file". + // "*file" must remain live while this Reader is in use. + // + // If "reporter" is non-NULL, it is notified whenever some data is + // dropped due to a detected corruption. "*reporter" must remain + // live while this Reader is in use. + // + // If "checksum" is true, verify checksums if available. + // + // The Reader will start reading at the first record located at physical + // position >= initial_offset within the file. + Reader(SequentialFile* file, Reporter* reporter, bool checksum, + uint64_t initial_offset); + + ~Reader(); + + // Read the next record into *record. Returns true if read + // successfully, false if we hit end of the input. May use + // "*scratch" as temporary storage. The contents filled in *record + // will only be valid until the next mutating operation on this + // reader or the next mutation to *scratch. + bool ReadRecord(Slice* record, std::string* scratch); + + // Returns the physical offset of the last record returned by ReadRecord. + // + // Undefined before the first call to ReadRecord. + uint64_t LastRecordOffset(); + + private: + SequentialFile* const file_; + Reporter* const reporter_; + bool const checksum_; + char* const backing_store_; + Slice buffer_; + bool eof_; // Last Read() indicated EOF by returning < kBlockSize + + // Offset of the last record returned by ReadRecord. + uint64_t last_record_offset_; + // Offset of the first location past the end of buffer_. + uint64_t end_of_buffer_offset_; + + // Offset at which to start looking for the first record to return + uint64_t const initial_offset_; + + // Extend record types with the following special values + enum { + kEof = kMaxRecordType + 1, + // Returned whenever we find an invalid physical record. + // Currently there are three situations in which this happens: + // * The record has an invalid CRC (ReadPhysicalRecord reports a drop) + // * The record is a 0-length record (No drop is reported) + // * The record is below constructor's initial_offset (No drop is reported) + kBadRecord = kMaxRecordType + 2 + }; + + // Skips all blocks that are completely before "initial_offset_". + // + // Returns true on success. Handles reporting. + bool SkipToInitialBlock(); + + // Return type, or one of the preceding special values + unsigned int ReadPhysicalRecord(Slice* result); + + // Reports dropped bytes to the reporter. + // buffer_ must be updated to remove the dropped bytes prior to invocation. + void ReportCorruption(size_t bytes, const char* reason); + void ReportDrop(size_t bytes, const Status& reason); + + // No copying allowed + Reader(const Reader&); + void operator=(const Reader&); +}; + +} // namespace log +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_LOG_READER_H_ diff --git a/src/leveldb-lin/leveldb/db/log_test.cc b/src/leveldb-lin/leveldb/db/log_test.cc new file mode 100644 index 0000000..4c5cf87 --- /dev/null +++ b/src/leveldb-lin/leveldb/db/log_test.cc @@ -0,0 +1,500 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "leveldb/env.h" +#include "util/coding.h" +#include "util/crc32c.h" +#include "util/random.h" +#include "util/testharness.h" + +namespace leveldb { +namespace log { + +// Construct a string of the specified length made out of the supplied +// partial string. +static std::string BigString(const std::string& partial_string, size_t n) { + std::string result; + while (result.size() < n) { + result.append(partial_string); + } + result.resize(n); + return result; +} + +// Construct a string from a number +static std::string NumberString(int n) { + char buf[50]; + snprintf(buf, sizeof(buf), "%d.", n); + return std::string(buf); +} + +// Return a skewed potentially long string +static std::string RandomSkewedString(int i, Random* rnd) { + return BigString(NumberString(i), rnd->Skewed(17)); +} + +class LogTest { + private: + class StringDest : public WritableFile { + public: + std::string contents_; + + virtual Status Close() { return Status::OK(); } + virtual Status Flush() { return Status::OK(); } + virtual Status Sync() { return Status::OK(); } + virtual Status Append(const Slice& slice) { + contents_.append(slice.data(), slice.size()); + return Status::OK(); + } + }; + + class StringSource : public SequentialFile { + public: + Slice contents_; + bool force_error_; + bool returned_partial_; + StringSource() : force_error_(false), returned_partial_(false) { } + + virtual Status Read(size_t n, Slice* result, char* scratch) { + ASSERT_TRUE(!returned_partial_) << "must not Read() after eof/error"; + + if (force_error_) { + force_error_ = false; + returned_partial_ = true; + return Status::Corruption("read error"); + } + + if (contents_.size() < n) { + n = contents_.size(); + returned_partial_ = true; + } + *result = Slice(contents_.data(), n); + contents_.remove_prefix(n); + return Status::OK(); + } + + virtual Status Skip(uint64_t n) { + if (n > contents_.size()) { + contents_.clear(); + return Status::NotFound("in-memory file skipepd past end"); + } + + contents_.remove_prefix(n); + + return Status::OK(); + } + }; + + class ReportCollector : public Reader::Reporter { + public: + size_t dropped_bytes_; + std::string message_; + + ReportCollector() : dropped_bytes_(0) { } + virtual void Corruption(size_t bytes, const Status& status) { + dropped_bytes_ += bytes; + message_.append(status.ToString()); + } + }; + + StringDest dest_; + StringSource source_; + ReportCollector report_; + bool reading_; + Writer writer_; + Reader reader_; + + // Record metadata for testing initial offset functionality + static size_t initial_offset_record_sizes_[]; + static uint64_t initial_offset_last_record_offsets_[]; + + public: + LogTest() : reading_(false), + writer_(&dest_), + reader_(&source_, &report_, true/*checksum*/, + 0/*initial_offset*/) { + } + + void Write(const std::string& msg) { + ASSERT_TRUE(!reading_) << "Write() after starting to read"; + writer_.AddRecord(Slice(msg)); + } + + size_t WrittenBytes() const { + return dest_.contents_.size(); + } + + std::string Read() { + if (!reading_) { + reading_ = true; + source_.contents_ = Slice(dest_.contents_); + } + std::string scratch; + Slice record; + if (reader_.ReadRecord(&record, &scratch)) { + return record.ToString(); + } else { + return "EOF"; + } + } + + void IncrementByte(int offset, int delta) { + dest_.contents_[offset] += delta; + } + + void SetByte(int offset, char new_byte) { + dest_.contents_[offset] = new_byte; + } + + void ShrinkSize(int bytes) { + dest_.contents_.resize(dest_.contents_.size() - bytes); + } + + void FixChecksum(int header_offset, int len) { + // Compute crc of type/len/data + uint32_t crc = crc32c::Value(&dest_.contents_[header_offset+6], 1 + len); + crc = crc32c::Mask(crc); + EncodeFixed32(&dest_.contents_[header_offset], crc); + } + + void ForceError() { + source_.force_error_ = true; + } + + size_t DroppedBytes() const { + return report_.dropped_bytes_; + } + + std::string ReportMessage() const { + return report_.message_; + } + + // Returns OK iff recorded error message contains "msg" + std::string MatchError(const std::string& msg) const { + if (report_.message_.find(msg) == std::string::npos) { + return report_.message_; + } else { + return "OK"; + } + } + + void WriteInitialOffsetLog() { + for (int i = 0; i < 4; i++) { + std::string record(initial_offset_record_sizes_[i], + static_cast('a' + i)); + Write(record); + } + } + + void CheckOffsetPastEndReturnsNoRecords(uint64_t offset_past_end) { + WriteInitialOffsetLog(); + reading_ = true; + source_.contents_ = Slice(dest_.contents_); + Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/, + WrittenBytes() + offset_past_end); + Slice record; + std::string scratch; + ASSERT_TRUE(!offset_reader->ReadRecord(&record, &scratch)); + delete offset_reader; + } + + void CheckInitialOffsetRecord(uint64_t initial_offset, + int expected_record_offset) { + WriteInitialOffsetLog(); + reading_ = true; + source_.contents_ = Slice(dest_.contents_); + Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/, + initial_offset); + Slice record; + std::string scratch; + ASSERT_TRUE(offset_reader->ReadRecord(&record, &scratch)); + ASSERT_EQ(initial_offset_record_sizes_[expected_record_offset], + record.size()); + ASSERT_EQ(initial_offset_last_record_offsets_[expected_record_offset], + offset_reader->LastRecordOffset()); + ASSERT_EQ((char)('a' + expected_record_offset), record.data()[0]); + delete offset_reader; + } + +}; + +size_t LogTest::initial_offset_record_sizes_[] = + {10000, // Two sizable records in first block + 10000, + 2 * log::kBlockSize - 1000, // Span three blocks + 1}; + +uint64_t LogTest::initial_offset_last_record_offsets_[] = + {0, + kHeaderSize + 10000, + 2 * (kHeaderSize + 10000), + 2 * (kHeaderSize + 10000) + + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize}; + + +TEST(LogTest, Empty) { + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, ReadWrite) { + Write("foo"); + Write("bar"); + Write(""); + Write("xxxx"); + ASSERT_EQ("foo", Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("", Read()); + ASSERT_EQ("xxxx", Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ("EOF", Read()); // Make sure reads at eof work +} + +TEST(LogTest, ManyBlocks) { + for (int i = 0; i < 100000; i++) { + Write(NumberString(i)); + } + for (int i = 0; i < 100000; i++) { + ASSERT_EQ(NumberString(i), Read()); + } + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, Fragmentation) { + Write("small"); + Write(BigString("medium", 50000)); + Write(BigString("large", 100000)); + ASSERT_EQ("small", Read()); + ASSERT_EQ(BigString("medium", 50000), Read()); + ASSERT_EQ(BigString("large", 100000), Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, MarginalTrailer) { + // Make a trailer that is exactly the same length as an empty record. + const int n = kBlockSize - 2*kHeaderSize; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes()); + Write(""); + Write("bar"); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("", Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, MarginalTrailer2) { + // Make a trailer that is exactly the same length as an empty record. + const int n = kBlockSize - 2*kHeaderSize; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes()); + Write("bar"); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(0, DroppedBytes()); + ASSERT_EQ("", ReportMessage()); +} + +TEST(LogTest, ShortTrailer) { + const int n = kBlockSize - 2*kHeaderSize + 4; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes()); + Write(""); + Write("bar"); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("", Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, AlignedEof) { + const int n = kBlockSize - 2*kHeaderSize + 4; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes()); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, RandomRead) { + const int N = 500; + Random write_rnd(301); + for (int i = 0; i < N; i++) { + Write(RandomSkewedString(i, &write_rnd)); + } + Random read_rnd(301); + for (int i = 0; i < N; i++) { + ASSERT_EQ(RandomSkewedString(i, &read_rnd), Read()); + } + ASSERT_EQ("EOF", Read()); +} + +// Tests of all the error paths in log_reader.cc follow: + +TEST(LogTest, ReadError) { + Write("foo"); + ForceError(); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(kBlockSize, DroppedBytes()); + ASSERT_EQ("OK", MatchError("read error")); +} + +TEST(LogTest, BadRecordType) { + Write("foo"); + // Type is stored in header[6] + IncrementByte(6, 100); + FixChecksum(0, 3); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("unknown record type")); +} + +TEST(LogTest, TruncatedTrailingRecord) { + Write("foo"); + ShrinkSize(4); // Drop all payload as well as a header byte + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(kHeaderSize - 1, DroppedBytes()); + ASSERT_EQ("OK", MatchError("truncated record at end of file")); +} + +TEST(LogTest, BadLength) { + Write("foo"); + ShrinkSize(1); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(kHeaderSize + 2, DroppedBytes()); + ASSERT_EQ("OK", MatchError("bad record length")); +} + +TEST(LogTest, ChecksumMismatch) { + Write("foo"); + IncrementByte(0, 10); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(10, DroppedBytes()); + ASSERT_EQ("OK", MatchError("checksum mismatch")); +} + +TEST(LogTest, UnexpectedMiddleType) { + Write("foo"); + SetByte(6, kMiddleType); + FixChecksum(0, 3); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("missing start")); +} + +TEST(LogTest, UnexpectedLastType) { + Write("foo"); + SetByte(6, kLastType); + FixChecksum(0, 3); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("missing start")); +} + +TEST(LogTest, UnexpectedFullType) { + Write("foo"); + Write("bar"); + SetByte(6, kFirstType); + FixChecksum(0, 3); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("partial record without end")); +} + +TEST(LogTest, UnexpectedFirstType) { + Write("foo"); + Write(BigString("bar", 100000)); + SetByte(6, kFirstType); + FixChecksum(0, 3); + ASSERT_EQ(BigString("bar", 100000), Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("partial record without end")); +} + +TEST(LogTest, ErrorJoinsRecords) { + // Consider two fragmented records: + // first(R1) last(R1) first(R2) last(R2) + // where the middle two fragments disappear. We do not want + // first(R1),last(R2) to get joined and returned as a valid record. + + // Write records that span two blocks + Write(BigString("foo", kBlockSize)); + Write(BigString("bar", kBlockSize)); + Write("correct"); + + // Wipe the middle block + for (int offset = kBlockSize; offset < 2*kBlockSize; offset++) { + SetByte(offset, 'x'); + } + + ASSERT_EQ("correct", Read()); + ASSERT_EQ("EOF", Read()); + const int dropped = DroppedBytes(); + ASSERT_LE(dropped, 2*kBlockSize + 100); + ASSERT_GE(dropped, 2*kBlockSize); +} + +TEST(LogTest, ReadStart) { + CheckInitialOffsetRecord(0, 0); +} + +TEST(LogTest, ReadSecondOneOff) { + CheckInitialOffsetRecord(1, 1); +} + +TEST(LogTest, ReadSecondTenThousand) { + CheckInitialOffsetRecord(10000, 1); +} + +TEST(LogTest, ReadSecondStart) { + CheckInitialOffsetRecord(10007, 1); +} + +TEST(LogTest, ReadThirdOneOff) { + CheckInitialOffsetRecord(10008, 2); +} + +TEST(LogTest, ReadThirdStart) { + CheckInitialOffsetRecord(20014, 2); +} + +TEST(LogTest, ReadFourthOneOff) { + CheckInitialOffsetRecord(20015, 3); +} + +TEST(LogTest, ReadFourthFirstBlockTrailer) { + CheckInitialOffsetRecord(log::kBlockSize - 4, 3); +} + +TEST(LogTest, ReadFourthMiddleBlock) { + CheckInitialOffsetRecord(log::kBlockSize + 1, 3); +} + +TEST(LogTest, ReadFourthLastBlock) { + CheckInitialOffsetRecord(2 * log::kBlockSize + 1, 3); +} + +TEST(LogTest, ReadFourthStart) { + CheckInitialOffsetRecord( + 2 * (kHeaderSize + 1000) + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize, + 3); +} + +TEST(LogTest, ReadEnd) { + CheckOffsetPastEndReturnsNoRecords(0); +} + +TEST(LogTest, ReadPastEnd) { + CheckOffsetPastEndReturnsNoRecords(5); +} + +} // namespace log +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/leveldb/db/log_writer.cc b/src/leveldb-lin/leveldb/db/log_writer.cc new file mode 100644 index 0000000..2da99ac --- /dev/null +++ b/src/leveldb-lin/leveldb/db/log_writer.cc @@ -0,0 +1,103 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/log_writer.h" + +#include +#include "leveldb/env.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { +namespace log { + +Writer::Writer(WritableFile* dest) + : dest_(dest), + block_offset_(0) { + for (int i = 0; i <= kMaxRecordType; i++) { + char t = static_cast(i); + type_crc_[i] = crc32c::Value(&t, 1); + } +} + +Writer::~Writer() { +} + +Status Writer::AddRecord(const Slice& slice) { + const char* ptr = slice.data(); + size_t left = slice.size(); + + // Fragment the record if necessary and emit it. Note that if slice + // is empty, we still want to iterate once to emit a single + // zero-length record + Status s; + bool begin = true; + do { + const int leftover = kBlockSize - block_offset_; + assert(leftover >= 0); + if (leftover < kHeaderSize) { + // Switch to a new block + if (leftover > 0) { + // Fill the trailer (literal below relies on kHeaderSize being 7) + assert(kHeaderSize == 7); + dest_->Append(Slice("\x00\x00\x00\x00\x00\x00", leftover)); + } + block_offset_ = 0; + } + + // Invariant: we never leave < kHeaderSize bytes in a block. + assert(kBlockSize - block_offset_ - kHeaderSize >= 0); + + const size_t avail = kBlockSize - block_offset_ - kHeaderSize; + const size_t fragment_length = (left < avail) ? left : avail; + + RecordType type; + const bool end = (left == fragment_length); + if (begin && end) { + type = kFullType; + } else if (begin) { + type = kFirstType; + } else if (end) { + type = kLastType; + } else { + type = kMiddleType; + } + + s = EmitPhysicalRecord(type, ptr, fragment_length); + ptr += fragment_length; + left -= fragment_length; + begin = false; + } while (s.ok() && left > 0); + return s; +} + +Status Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) { + assert(n <= 0xffff); // Must fit in two bytes + assert(block_offset_ + kHeaderSize + n <= kBlockSize); + + // Format the header + char buf[kHeaderSize]; + buf[4] = static_cast(n & 0xff); + buf[5] = static_cast(n >> 8); + buf[6] = static_cast(t); + + // Compute the crc of the record type and the payload. + uint32_t crc = crc32c::Extend(type_crc_[t], ptr, n); + crc = crc32c::Mask(crc); // Adjust for storage + EncodeFixed32(buf, crc); + + // Write the header and the payload + Status s = dest_->Append(Slice(buf, kHeaderSize)); + if (s.ok()) { + s = dest_->Append(Slice(ptr, n)); + if (s.ok()) { + s = dest_->Flush(); + } + } + block_offset_ += kHeaderSize + n; + return s; +} + +} // namespace log +} // namespace leveldb diff --git a/src/leveldb-lin/leveldb/db/log_writer.h b/src/leveldb-lin/leveldb/db/log_writer.h new file mode 100644 index 0000000..a3a954d --- /dev/null +++ b/src/leveldb-lin/leveldb/db/log_writer.h @@ -0,0 +1,48 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_LOG_WRITER_H_ +#define STORAGE_LEVELDB_DB_LOG_WRITER_H_ + +#include +#include "db/log_format.h" +#include "leveldb/slice.h" +#include "leveldb/status.h" + +namespace leveldb { + +class WritableFile; + +namespace log { + +class Writer { + public: + // Create a writer that will append data to "*dest". + // "*dest" must be initially empty. + // "*dest" must remain live while this Writer is in use. + explicit Writer(WritableFile* dest); + ~Writer(); + + Status AddRecord(const Slice& slice); + + private: + WritableFile* dest_; + int block_offset_; // Current offset in block + + // crc32c values for all supported record types. These are + // pre-computed to reduce the overhead of computing the crc of the + // record type stored in the header. + uint32_t type_crc_[kMaxRecordType + 1]; + + Status EmitPhysicalRecord(RecordType type, const char* ptr, size_t length); + + // No copying allowed + Writer(const Writer&); + void operator=(const Writer&); +}; + +} // namespace log +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_LOG_WRITER_H_ diff --git a/src/leveldb-lin/leveldb/db/memtable.cc b/src/leveldb-lin/leveldb/db/memtable.cc new file mode 100644 index 0000000..bfec0a7 --- /dev/null +++ b/src/leveldb-lin/leveldb/db/memtable.cc @@ -0,0 +1,145 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/memtable.h" +#include "db/dbformat.h" +#include "leveldb/comparator.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "util/coding.h" + +namespace leveldb { + +static Slice GetLengthPrefixedSlice(const char* data) { + uint32_t len; + const char* p = data; + p = GetVarint32Ptr(p, p + 5, &len); // +5: we assume "p" is not corrupted + return Slice(p, len); +} + +MemTable::MemTable(const InternalKeyComparator& cmp) + : comparator_(cmp), + refs_(0), + table_(comparator_, &arena_) { +} + +MemTable::~MemTable() { + assert(refs_ == 0); +} + +size_t MemTable::ApproximateMemoryUsage() { return arena_.MemoryUsage(); } + +int MemTable::KeyComparator::operator()(const char* aptr, const char* bptr) + const { + // Internal keys are encoded as length-prefixed strings. + Slice a = GetLengthPrefixedSlice(aptr); + Slice b = GetLengthPrefixedSlice(bptr); + return comparator.Compare(a, b); +} + +// Encode a suitable internal key target for "target" and return it. +// Uses *scratch as scratch space, and the returned pointer will point +// into this scratch space. +static const char* EncodeKey(std::string* scratch, const Slice& target) { + scratch->clear(); + PutVarint32(scratch, target.size()); + scratch->append(target.data(), target.size()); + return scratch->data(); +} + +class MemTableIterator: public Iterator { + public: + explicit MemTableIterator(MemTable::Table* table) : iter_(table) { } + + virtual bool Valid() const { return iter_.Valid(); } + virtual void Seek(const Slice& k) { iter_.Seek(EncodeKey(&tmp_, k)); } + virtual void SeekToFirst() { iter_.SeekToFirst(); } + virtual void SeekToLast() { iter_.SeekToLast(); } + virtual void Next() { iter_.Next(); } + virtual void Prev() { iter_.Prev(); } + virtual Slice key() const { return GetLengthPrefixedSlice(iter_.key()); } + virtual Slice value() const { + Slice key_slice = GetLengthPrefixedSlice(iter_.key()); + return GetLengthPrefixedSlice(key_slice.data() + key_slice.size()); + } + + virtual Status status() const { return Status::OK(); } + + private: + MemTable::Table::Iterator iter_; + std::string tmp_; // For passing to EncodeKey + + // No copying allowed + MemTableIterator(const MemTableIterator&); + void operator=(const MemTableIterator&); +}; + +Iterator* MemTable::NewIterator() { + return new MemTableIterator(&table_); +} + +void MemTable::Add(SequenceNumber s, ValueType type, + const Slice& key, + const Slice& value) { + // Format of an entry is concatenation of: + // key_size : varint32 of internal_key.size() + // key bytes : char[internal_key.size()] + // value_size : varint32 of value.size() + // value bytes : char[value.size()] + size_t key_size = key.size(); + size_t val_size = value.size(); + size_t internal_key_size = key_size + 8; + const size_t encoded_len = + VarintLength(internal_key_size) + internal_key_size + + VarintLength(val_size) + val_size; + char* buf = arena_.Allocate(encoded_len); + char* p = EncodeVarint32(buf, internal_key_size); + memcpy(p, key.data(), key_size); + p += key_size; + EncodeFixed64(p, (s << 8) | type); + p += 8; + p = EncodeVarint32(p, val_size); + memcpy(p, value.data(), val_size); + assert((p + val_size) - buf == encoded_len); + table_.Insert(buf); +} + +bool MemTable::Get(const LookupKey& key, std::string* value, Status* s) { + Slice memkey = key.memtable_key(); + Table::Iterator iter(&table_); + iter.Seek(memkey.data()); + if (iter.Valid()) { + // entry format is: + // klength varint32 + // userkey char[klength] + // tag uint64 + // vlength varint32 + // value char[vlength] + // Check that it belongs to same user key. We do not check the + // sequence number since the Seek() call above should have skipped + // all entries with overly large sequence numbers. + const char* entry = iter.key(); + uint32_t key_length; + const char* key_ptr = GetVarint32Ptr(entry, entry+5, &key_length); + if (comparator_.comparator.user_comparator()->Compare( + Slice(key_ptr, key_length - 8), + key.user_key()) == 0) { + // Correct user key + const uint64_t tag = DecodeFixed64(key_ptr + key_length - 8); + switch (static_cast(tag & 0xff)) { + case kTypeValue: { + Slice v = GetLengthPrefixedSlice(key_ptr + key_length); + value->assign(v.data(), v.size()); + return true; + } + case kTypeDeletion: + *s = Status::NotFound(Slice()); + return true; + } + } + } + return false; +} + +} // namespace leveldb diff --git a/src/leveldb-lin/leveldb/db/memtable.h b/src/leveldb-lin/leveldb/db/memtable.h new file mode 100644 index 0000000..92e90bb --- /dev/null +++ b/src/leveldb-lin/leveldb/db/memtable.h @@ -0,0 +1,91 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_MEMTABLE_H_ +#define STORAGE_LEVELDB_DB_MEMTABLE_H_ + +#include +#include "leveldb/db.h" +#include "db/dbformat.h" +#include "db/skiplist.h" +#include "util/arena.h" + +namespace leveldb { + +class InternalKeyComparator; +class Mutex; +class MemTableIterator; + +class MemTable { + public: + // MemTables are reference counted. The initial reference count + // is zero and the caller must call Ref() at least once. + explicit MemTable(const InternalKeyComparator& comparator); + + // Increase reference count. + void Ref() { ++refs_; } + + // Drop reference count. Delete if no more references exist. + void Unref() { + --refs_; + assert(refs_ >= 0); + if (refs_ <= 0) { + delete this; + } + } + + // Returns an estimate of the number of bytes of data in use by this + // data structure. + // + // REQUIRES: external synchronization to prevent simultaneous + // operations on the same MemTable. + size_t ApproximateMemoryUsage(); + + // Return an iterator that yields the contents of the memtable. + // + // The caller must ensure that the underlying MemTable remains live + // while the returned iterator is live. The keys returned by this + // iterator are internal keys encoded by AppendInternalKey in the + // db/format.{h,cc} module. + Iterator* NewIterator(); + + // Add an entry into memtable that maps key to value at the + // specified sequence number and with the specified type. + // Typically value will be empty if type==kTypeDeletion. + void Add(SequenceNumber seq, ValueType type, + const Slice& key, + const Slice& value); + + // If memtable contains a value for key, store it in *value and return true. + // If memtable contains a deletion for key, store a NotFound() error + // in *status and return true. + // Else, return false. + bool Get(const LookupKey& key, std::string* value, Status* s); + + private: + ~MemTable(); // Private since only Unref() should be used to delete it + + struct KeyComparator { + const InternalKeyComparator comparator; + explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) { } + int operator()(const char* a, const char* b) const; + }; + friend class MemTableIterator; + friend class MemTableBackwardIterator; + + typedef SkipList Table; + + KeyComparator comparator_; + int refs_; + Arena arena_; + Table table_; + + // No copying allowed + MemTable(const MemTable&); + void operator=(const MemTable&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_MEMTABLE_H_ diff --git a/src/leveldb-lin/leveldb/db/repair.cc b/src/leveldb-lin/leveldb/db/repair.cc new file mode 100644 index 0000000..96c9b37 --- /dev/null +++ b/src/leveldb-lin/leveldb/db/repair.cc @@ -0,0 +1,462 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// We recover the contents of the descriptor from the other files we find. +// (1) Any log files are first converted to tables +// (2) We scan every table to compute +// (a) smallest/largest for the table +// (b) largest sequence number in the table +// (3) We generate descriptor contents: +// - log number is set to zero +// - next-file-number is set to 1 + largest file number we found +// - last-sequence-number is set to largest sequence# found across +// all tables (see 2c) +// - compaction pointers are cleared +// - every table file is added at level 0 +// +// Possible optimization 1: +// (a) Compute total size and use to pick appropriate max-level M +// (b) Sort tables by largest sequence# in the table +// (c) For each table: if it overlaps earlier table, place in level-0, +// else place in level-M. +// Possible optimization 2: +// Store per-table metadata (smallest, largest, largest-seq#, ...) +// in the table's meta section to speed up ScanTable. + +#include "db/builder.h" +#include "db/db_impl.h" +#include "db/dbformat.h" +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "db/memtable.h" +#include "db/table_cache.h" +#include "db/version_edit.h" +#include "db/write_batch_internal.h" +#include "leveldb/comparator.h" +#include "leveldb/db.h" +#include "leveldb/env.h" + +namespace leveldb { + +namespace { + +class Repairer { + public: + Repairer(const std::string& dbname, const Options& options) + : dbname_(dbname), + env_(options.env), + icmp_(options.comparator), + ipolicy_(options.filter_policy), + options_(SanitizeOptions(dbname, &icmp_, &ipolicy_, options)), + owns_info_log_(options_.info_log != options.info_log), + owns_cache_(options_.block_cache != options.block_cache), + next_file_number_(1) { + // TableCache can be small since we expect each table to be opened once. + table_cache_ = new TableCache(dbname_, &options_, 10); + } + + ~Repairer() { + delete table_cache_; + if (owns_info_log_) { + delete options_.info_log; + } + if (owns_cache_) { + delete options_.block_cache; + } + } + + Status Run() { + Status status = FindFiles(); + if (status.ok()) { + ConvertLogFilesToTables(); + ExtractMetaData(); + status = WriteDescriptor(); + } + if (status.ok()) { + unsigned long long bytes = 0; + for (size_t i = 0; i < tables_.size(); i++) { + bytes += tables_[i].meta.file_size; + } + Log(options_.info_log, + "**** Repaired leveldb %s; " + "recovered %d files; %llu bytes. " + "Some data may have been lost. " + "****", + dbname_.c_str(), + static_cast(tables_.size()), + bytes); + } + return status; + } + + private: + struct TableInfo { + FileMetaData meta; + SequenceNumber max_sequence; + }; + + std::string const dbname_; + Env* const env_; + InternalKeyComparator const icmp_; + InternalFilterPolicy const ipolicy_; + Options const options_; + bool owns_info_log_; + bool owns_cache_; + TableCache* table_cache_; + VersionEdit edit_; + + std::vector manifests_; + std::vector table_numbers_; + std::vector logs_; + std::vector tables_; + uint64_t next_file_number_; + + Status FindFiles() { + std::vector filenames; + Status status = env_->GetChildren(dbname_, &filenames); + if (!status.ok()) { + return status; + } + if (filenames.empty()) { + return Status::IOError(dbname_, "repair found no files"); + } + + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type)) { + if (type == kDescriptorFile) { + manifests_.push_back(filenames[i]); + } else { + if (number + 1 > next_file_number_) { + next_file_number_ = number + 1; + } + if (type == kLogFile) { + logs_.push_back(number); + } else if (type == kTableFile) { + table_numbers_.push_back(number); + } else { + // Ignore other files + } + } + } + } + return status; + } + + void ConvertLogFilesToTables() { + for (size_t i = 0; i < logs_.size(); i++) { + std::string logname = LogFileName(dbname_, logs_[i]); + Status status = ConvertLogToTable(logs_[i]); + if (!status.ok()) { + Log(options_.info_log, "Log #%llu: ignoring conversion error: %s", + (unsigned long long) logs_[i], + status.ToString().c_str()); + } + ArchiveFile(logname); + } + } + + Status ConvertLogToTable(uint64_t log) { + struct LogReporter : public log::Reader::Reporter { + Env* env; + Logger* info_log; + uint64_t lognum; + virtual void Corruption(size_t bytes, const Status& s) { + // We print error messages for corruption, but continue repairing. + Log(info_log, "Log #%llu: dropping %d bytes; %s", + (unsigned long long) lognum, + static_cast(bytes), + s.ToString().c_str()); + } + }; + + // Open the log file + std::string logname = LogFileName(dbname_, log); + SequentialFile* lfile; + Status status = env_->NewSequentialFile(logname, &lfile); + if (!status.ok()) { + return status; + } + + // Create the log reader. + LogReporter reporter; + reporter.env = env_; + reporter.info_log = options_.info_log; + reporter.lognum = log; + // We intentially make log::Reader do checksumming so that + // corruptions cause entire commits to be skipped instead of + // propagating bad information (like overly large sequence + // numbers). + log::Reader reader(lfile, &reporter, false/*do not checksum*/, + 0/*initial_offset*/); + + // Read all the records and add to a memtable + std::string scratch; + Slice record; + WriteBatch batch; + MemTable* mem = new MemTable(icmp_); + mem->Ref(); + int counter = 0; + while (reader.ReadRecord(&record, &scratch)) { + if (record.size() < 12) { + reporter.Corruption( + record.size(), Status::Corruption("log record too small")); + continue; + } + WriteBatchInternal::SetContents(&batch, record); + status = WriteBatchInternal::InsertInto(&batch, mem); + if (status.ok()) { + counter += WriteBatchInternal::Count(&batch); + } else { + Log(options_.info_log, "Log #%llu: ignoring %s", + (unsigned long long) log, + status.ToString().c_str()); + status = Status::OK(); // Keep going with rest of file + } + } + delete lfile; + + // Do not record a version edit for this conversion to a Table + // since ExtractMetaData() will also generate edits. + FileMetaData meta; + meta.number = next_file_number_++; + Iterator* iter = mem->NewIterator(); + status = BuildTable(dbname_, env_, options_, table_cache_, iter, &meta); + delete iter; + mem->Unref(); + mem = NULL; + if (status.ok()) { + if (meta.file_size > 0) { + table_numbers_.push_back(meta.number); + } + } + Log(options_.info_log, "Log #%llu: %d ops saved to Table #%llu %s", + (unsigned long long) log, + counter, + (unsigned long long) meta.number, + status.ToString().c_str()); + return status; + } + + void ExtractMetaData() { + std::vector kept; + for (size_t i = 0; i < table_numbers_.size(); i++) { + ScanTable(table_numbers_[i]); + } + } + + Iterator* NewTableIterator(const FileMetaData& meta) { + // Same as compaction iterators: if paranoid_checks are on, turn + // on checksum verification. + ReadOptions r; + r.verify_checksums = options_.paranoid_checks; + return table_cache_->NewIterator(r, meta.number, meta.file_size); + } + + void ScanTable(uint64_t number) { + TableInfo t; + t.meta.number = number; + std::string fname = TableFileName(dbname_, number); + Status status = env_->GetFileSize(fname, &t.meta.file_size); + if (!status.ok()) { + // Try alternate file name. + fname = SSTTableFileName(dbname_, number); + Status s2 = env_->GetFileSize(fname, &t.meta.file_size); + if (s2.ok()) { + status = Status::OK(); + } + } + if (!status.ok()) { + ArchiveFile(TableFileName(dbname_, number)); + ArchiveFile(SSTTableFileName(dbname_, number)); + Log(options_.info_log, "Table #%llu: dropped: %s", + (unsigned long long) t.meta.number, + status.ToString().c_str()); + return; + } + + // Extract metadata by scanning through table. + int counter = 0; + Iterator* iter = NewTableIterator(t.meta); + bool empty = true; + ParsedInternalKey parsed; + t.max_sequence = 0; + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + Slice key = iter->key(); + if (!ParseInternalKey(key, &parsed)) { + Log(options_.info_log, "Table #%llu: unparsable key %s", + (unsigned long long) t.meta.number, + EscapeString(key).c_str()); + continue; + } + + counter++; + if (empty) { + empty = false; + t.meta.smallest.DecodeFrom(key); + } + t.meta.largest.DecodeFrom(key); + if (parsed.sequence > t.max_sequence) { + t.max_sequence = parsed.sequence; + } + } + if (!iter->status().ok()) { + status = iter->status(); + } + delete iter; + Log(options_.info_log, "Table #%llu: %d entries %s", + (unsigned long long) t.meta.number, + counter, + status.ToString().c_str()); + + if (status.ok()) { + tables_.push_back(t); + } else { + RepairTable(fname, t); // RepairTable archives input file. + } + } + + void RepairTable(const std::string& src, TableInfo t) { + // We will copy src contents to a new table and then rename the + // new table over the source. + + // Create builder. + std::string copy = TableFileName(dbname_, next_file_number_++); + WritableFile* file; + Status s = env_->NewWritableFile(copy, &file); + if (!s.ok()) { + return; + } + TableBuilder* builder = new TableBuilder(options_, file); + + // Copy data. + Iterator* iter = NewTableIterator(t.meta); + int counter = 0; + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + builder->Add(iter->key(), iter->value()); + counter++; + } + delete iter; + + ArchiveFile(src); + if (counter == 0) { + builder->Abandon(); // Nothing to save + } else { + s = builder->Finish(); + if (s.ok()) { + t.meta.file_size = builder->FileSize(); + } + } + delete builder; + builder = NULL; + + if (s.ok()) { + s = file->Close(); + } + delete file; + file = NULL; + + if (counter > 0 && s.ok()) { + std::string orig = TableFileName(dbname_, t.meta.number); + s = env_->RenameFile(copy, orig); + if (s.ok()) { + Log(options_.info_log, "Table #%llu: %d entries repaired", + (unsigned long long) t.meta.number, counter); + tables_.push_back(t); + } + } + if (!s.ok()) { + env_->DeleteFile(copy); + } + } + + Status WriteDescriptor() { + std::string tmp = TempFileName(dbname_, 1); + WritableFile* file; + Status status = env_->NewWritableFile(tmp, &file); + if (!status.ok()) { + return status; + } + + SequenceNumber max_sequence = 0; + for (size_t i = 0; i < tables_.size(); i++) { + if (max_sequence < tables_[i].max_sequence) { + max_sequence = tables_[i].max_sequence; + } + } + + edit_.SetComparatorName(icmp_.user_comparator()->Name()); + edit_.SetLogNumber(0); + edit_.SetNextFile(next_file_number_); + edit_.SetLastSequence(max_sequence); + + for (size_t i = 0; i < tables_.size(); i++) { + // TODO(opt): separate out into multiple levels + const TableInfo& t = tables_[i]; + edit_.AddFile(0, t.meta.number, t.meta.file_size, + t.meta.smallest, t.meta.largest); + } + + //fprintf(stderr, "NewDescriptor:\n%s\n", edit_.DebugString().c_str()); + { + log::Writer log(file); + std::string record; + edit_.EncodeTo(&record); + status = log.AddRecord(record); + } + if (status.ok()) { + status = file->Close(); + } + delete file; + file = NULL; + + if (!status.ok()) { + env_->DeleteFile(tmp); + } else { + // Discard older manifests + for (size_t i = 0; i < manifests_.size(); i++) { + ArchiveFile(dbname_ + "/" + manifests_[i]); + } + + // Install new manifest + status = env_->RenameFile(tmp, DescriptorFileName(dbname_, 1)); + if (status.ok()) { + status = SetCurrentFile(env_, dbname_, 1); + } else { + env_->DeleteFile(tmp); + } + } + return status; + } + + void ArchiveFile(const std::string& fname) { + // Move into another directory. E.g., for + // dir/foo + // rename to + // dir/lost/foo + const char* slash = strrchr(fname.c_str(), '/'); + std::string new_dir; + if (slash != NULL) { + new_dir.assign(fname.data(), slash - fname.data()); + } + new_dir.append("/lost"); + env_->CreateDir(new_dir); // Ignore error + std::string new_file = new_dir; + new_file.append("/"); + new_file.append((slash == NULL) ? fname.c_str() : slash + 1); + Status s = env_->RenameFile(fname, new_file); + Log(options_.info_log, "Archiving %s: %s\n", + fname.c_str(), s.ToString().c_str()); + } +}; +} // namespace + +Status RepairDB(const std::string& dbname, const Options& options) { + Repairer repairer(dbname, options); + return repairer.Run(); +} + +} // namespace leveldb diff --git a/src/leveldb-lin/leveldb/db/skiplist.h b/src/leveldb-lin/leveldb/db/skiplist.h new file mode 100644 index 0000000..af85be6 --- /dev/null +++ b/src/leveldb-lin/leveldb/db/skiplist.h @@ -0,0 +1,379 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Thread safety +// ------------- +// +// Writes require external synchronization, most likely a mutex. +// Reads require a guarantee that the SkipList will not be destroyed +// while the read is in progress. Apart from that, reads progress +// without any internal locking or synchronization. +// +// Invariants: +// +// (1) Allocated nodes are never deleted until the SkipList is +// destroyed. This is trivially guaranteed by the code since we +// never delete any skip list nodes. +// +// (2) The contents of a Node except for the next/prev pointers are +// immutable after the Node has been linked into the SkipList. +// Only Insert() modifies the list, and it is careful to initialize +// a node and use release-stores to publish the nodes in one or +// more lists. +// +// ... prev vs. next pointer ordering ... + +#include +#include +#include "port/port.h" +#include "util/arena.h" +#include "util/random.h" + +namespace leveldb { + +class Arena; + +template +class SkipList { + private: + struct Node; + + public: + // Create a new SkipList object that will use "cmp" for comparing keys, + // and will allocate memory using "*arena". Objects allocated in the arena + // must remain allocated for the lifetime of the skiplist object. + explicit SkipList(Comparator cmp, Arena* arena); + + // Insert key into the list. + // REQUIRES: nothing that compares equal to key is currently in the list. + void Insert(const Key& key); + + // Returns true iff an entry that compares equal to key is in the list. + bool Contains(const Key& key) const; + + // Iteration over the contents of a skip list + class Iterator { + public: + // Initialize an iterator over the specified list. + // The returned iterator is not valid. + explicit Iterator(const SkipList* list); + + // Returns true iff the iterator is positioned at a valid node. + bool Valid() const; + + // Returns the key at the current position. + // REQUIRES: Valid() + const Key& key() const; + + // Advances to the next position. + // REQUIRES: Valid() + void Next(); + + // Advances to the previous position. + // REQUIRES: Valid() + void Prev(); + + // Advance to the first entry with a key >= target + void Seek(const Key& target); + + // Position at the first entry in list. + // Final state of iterator is Valid() iff list is not empty. + void SeekToFirst(); + + // Position at the last entry in list. + // Final state of iterator is Valid() iff list is not empty. + void SeekToLast(); + + private: + const SkipList* list_; + Node* node_; + // Intentionally copyable + }; + + private: + enum { kMaxHeight = 12 }; + + // Immutable after construction + Comparator const compare_; + Arena* const arena_; // Arena used for allocations of nodes + + Node* const head_; + + // Modified only by Insert(). Read racily by readers, but stale + // values are ok. + port::AtomicPointer max_height_; // Height of the entire list + + inline int GetMaxHeight() const { + return static_cast( + reinterpret_cast(max_height_.NoBarrier_Load())); + } + + // Read/written only by Insert(). + Random rnd_; + + Node* NewNode(const Key& key, int height); + int RandomHeight(); + bool Equal(const Key& a, const Key& b) const { return (compare_(a, b) == 0); } + + // Return true if key is greater than the data stored in "n" + bool KeyIsAfterNode(const Key& key, Node* n) const; + + // Return the earliest node that comes at or after key. + // Return NULL if there is no such node. + // + // If prev is non-NULL, fills prev[level] with pointer to previous + // node at "level" for every level in [0..max_height_-1]. + Node* FindGreaterOrEqual(const Key& key, Node** prev) const; + + // Return the latest node with a key < key. + // Return head_ if there is no such node. + Node* FindLessThan(const Key& key) const; + + // Return the last node in the list. + // Return head_ if list is empty. + Node* FindLast() const; + + // No copying allowed + SkipList(const SkipList&); + void operator=(const SkipList&); +}; + +// Implementation details follow +template +struct SkipList::Node { + explicit Node(const Key& k) : key(k) { } + + Key const key; + + // Accessors/mutators for links. Wrapped in methods so we can + // add the appropriate barriers as necessary. + Node* Next(int n) { + assert(n >= 0); + // Use an 'acquire load' so that we observe a fully initialized + // version of the returned Node. + return reinterpret_cast(next_[n].Acquire_Load()); + } + void SetNext(int n, Node* x) { + assert(n >= 0); + // Use a 'release store' so that anybody who reads through this + // pointer observes a fully initialized version of the inserted node. + next_[n].Release_Store(x); + } + + // No-barrier variants that can be safely used in a few locations. + Node* NoBarrier_Next(int n) { + assert(n >= 0); + return reinterpret_cast(next_[n].NoBarrier_Load()); + } + void NoBarrier_SetNext(int n, Node* x) { + assert(n >= 0); + next_[n].NoBarrier_Store(x); + } + + private: + // Array of length equal to the node height. next_[0] is lowest level link. + port::AtomicPointer next_[1]; +}; + +template +typename SkipList::Node* +SkipList::NewNode(const Key& key, int height) { + char* mem = arena_->AllocateAligned( + sizeof(Node) + sizeof(port::AtomicPointer) * (height - 1)); + return new (mem) Node(key); +} + +template +inline SkipList::Iterator::Iterator(const SkipList* list) { + list_ = list; + node_ = NULL; +} + +template +inline bool SkipList::Iterator::Valid() const { + return node_ != NULL; +} + +template +inline const Key& SkipList::Iterator::key() const { + assert(Valid()); + return node_->key; +} + +template +inline void SkipList::Iterator::Next() { + assert(Valid()); + node_ = node_->Next(0); +} + +template +inline void SkipList::Iterator::Prev() { + // Instead of using explicit "prev" links, we just search for the + // last node that falls before key. + assert(Valid()); + node_ = list_->FindLessThan(node_->key); + if (node_ == list_->head_) { + node_ = NULL; + } +} + +template +inline void SkipList::Iterator::Seek(const Key& target) { + node_ = list_->FindGreaterOrEqual(target, NULL); +} + +template +inline void SkipList::Iterator::SeekToFirst() { + node_ = list_->head_->Next(0); +} + +template +inline void SkipList::Iterator::SeekToLast() { + node_ = list_->FindLast(); + if (node_ == list_->head_) { + node_ = NULL; + } +} + +template +int SkipList::RandomHeight() { + // Increase height with probability 1 in kBranching + static const unsigned int kBranching = 4; + int height = 1; + while (height < kMaxHeight && ((rnd_.Next() % kBranching) == 0)) { + height++; + } + assert(height > 0); + assert(height <= kMaxHeight); + return height; +} + +template +bool SkipList::KeyIsAfterNode(const Key& key, Node* n) const { + // NULL n is considered infinite + return (n != NULL) && (compare_(n->key, key) < 0); +} + +template +typename SkipList::Node* SkipList::FindGreaterOrEqual(const Key& key, Node** prev) + const { + Node* x = head_; + int level = GetMaxHeight() - 1; + while (true) { + Node* next = x->Next(level); + if (KeyIsAfterNode(key, next)) { + // Keep searching in this list + x = next; + } else { + if (prev != NULL) prev[level] = x; + if (level == 0) { + return next; + } else { + // Switch to next list + level--; + } + } + } +} + +template +typename SkipList::Node* +SkipList::FindLessThan(const Key& key) const { + Node* x = head_; + int level = GetMaxHeight() - 1; + while (true) { + assert(x == head_ || compare_(x->key, key) < 0); + Node* next = x->Next(level); + if (next == NULL || compare_(next->key, key) >= 0) { + if (level == 0) { + return x; + } else { + // Switch to next list + level--; + } + } else { + x = next; + } + } +} + +template +typename SkipList::Node* SkipList::FindLast() + const { + Node* x = head_; + int level = GetMaxHeight() - 1; + while (true) { + Node* next = x->Next(level); + if (next == NULL) { + if (level == 0) { + return x; + } else { + // Switch to next list + level--; + } + } else { + x = next; + } + } +} + +template +SkipList::SkipList(Comparator cmp, Arena* arena) + : compare_(cmp), + arena_(arena), + head_(NewNode(0 /* any key will do */, kMaxHeight)), + max_height_(reinterpret_cast(1)), + rnd_(0xdeadbeef) { + for (int i = 0; i < kMaxHeight; i++) { + head_->SetNext(i, NULL); + } +} + +template +void SkipList::Insert(const Key& key) { + // TODO(opt): We can use a barrier-free variant of FindGreaterOrEqual() + // here since Insert() is externally synchronized. + Node* prev[kMaxHeight]; + Node* x = FindGreaterOrEqual(key, prev); + + // Our data structure does not allow duplicate insertion + assert(x == NULL || !Equal(key, x->key)); + + int height = RandomHeight(); + if (height > GetMaxHeight()) { + for (int i = GetMaxHeight(); i < height; i++) { + prev[i] = head_; + } + //fprintf(stderr, "Change height from %d to %d\n", max_height_, height); + + // It is ok to mutate max_height_ without any synchronization + // with concurrent readers. A concurrent reader that observes + // the new value of max_height_ will see either the old value of + // new level pointers from head_ (NULL), or a new value set in + // the loop below. In the former case the reader will + // immediately drop to the next level since NULL sorts after all + // keys. In the latter case the reader will use the new node. + max_height_.NoBarrier_Store(reinterpret_cast(height)); + } + + x = NewNode(key, height); + for (int i = 0; i < height; i++) { + // NoBarrier_SetNext() suffices since we will add a barrier when + // we publish a pointer to "x" in prev[i]. + x->NoBarrier_SetNext(i, prev[i]->NoBarrier_Next(i)); + prev[i]->SetNext(i, x); + } +} + +template +bool SkipList::Contains(const Key& key) const { + Node* x = FindGreaterOrEqual(key, NULL); + if (x != NULL && Equal(key, x->key)) { + return true; + } else { + return false; + } +} + +} // namespace leveldb diff --git a/src/leveldb-lin/leveldb/db/skiplist_test.cc b/src/leveldb-lin/leveldb/db/skiplist_test.cc new file mode 100644 index 0000000..c78f4b4 --- /dev/null +++ b/src/leveldb-lin/leveldb/db/skiplist_test.cc @@ -0,0 +1,378 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/skiplist.h" +#include +#include "leveldb/env.h" +#include "util/arena.h" +#include "util/hash.h" +#include "util/random.h" +#include "util/testharness.h" + +namespace leveldb { + +typedef uint64_t Key; + +struct Comparator { + int operator()(const Key& a, const Key& b) const { + if (a < b) { + return -1; + } else if (a > b) { + return +1; + } else { + return 0; + } + } +}; + +class SkipTest { }; + +TEST(SkipTest, Empty) { + Arena arena; + Comparator cmp; + SkipList list(cmp, &arena); + ASSERT_TRUE(!list.Contains(10)); + + SkipList::Iterator iter(&list); + ASSERT_TRUE(!iter.Valid()); + iter.SeekToFirst(); + ASSERT_TRUE(!iter.Valid()); + iter.Seek(100); + ASSERT_TRUE(!iter.Valid()); + iter.SeekToLast(); + ASSERT_TRUE(!iter.Valid()); +} + +TEST(SkipTest, InsertAndLookup) { + const int N = 2000; + const int R = 5000; + Random rnd(1000); + std::set keys; + Arena arena; + Comparator cmp; + SkipList list(cmp, &arena); + for (int i = 0; i < N; i++) { + Key key = rnd.Next() % R; + if (keys.insert(key).second) { + list.Insert(key); + } + } + + for (int i = 0; i < R; i++) { + if (list.Contains(i)) { + ASSERT_EQ(keys.count(i), 1); + } else { + ASSERT_EQ(keys.count(i), 0); + } + } + + // Simple iterator tests + { + SkipList::Iterator iter(&list); + ASSERT_TRUE(!iter.Valid()); + + iter.Seek(0); + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*(keys.begin()), iter.key()); + + iter.SeekToFirst(); + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*(keys.begin()), iter.key()); + + iter.SeekToLast(); + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*(keys.rbegin()), iter.key()); + } + + // Forward iteration test + for (int i = 0; i < R; i++) { + SkipList::Iterator iter(&list); + iter.Seek(i); + + // Compare against model iterator + std::set::iterator model_iter = keys.lower_bound(i); + for (int j = 0; j < 3; j++) { + if (model_iter == keys.end()) { + ASSERT_TRUE(!iter.Valid()); + break; + } else { + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*model_iter, iter.key()); + ++model_iter; + iter.Next(); + } + } + } + + // Backward iteration test + { + SkipList::Iterator iter(&list); + iter.SeekToLast(); + + // Compare against model iterator + for (std::set::reverse_iterator model_iter = keys.rbegin(); + model_iter != keys.rend(); + ++model_iter) { + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*model_iter, iter.key()); + iter.Prev(); + } + ASSERT_TRUE(!iter.Valid()); + } +} + +// We want to make sure that with a single writer and multiple +// concurrent readers (with no synchronization other than when a +// reader's iterator is created), the reader always observes all the +// data that was present in the skip list when the iterator was +// constructor. Because insertions are happening concurrently, we may +// also observe new values that were inserted since the iterator was +// constructed, but we should never miss any values that were present +// at iterator construction time. +// +// We generate multi-part keys: +// +// where: +// key is in range [0..K-1] +// gen is a generation number for key +// hash is hash(key,gen) +// +// The insertion code picks a random key, sets gen to be 1 + the last +// generation number inserted for that key, and sets hash to Hash(key,gen). +// +// At the beginning of a read, we snapshot the last inserted +// generation number for each key. We then iterate, including random +// calls to Next() and Seek(). For every key we encounter, we +// check that it is either expected given the initial snapshot or has +// been concurrently added since the iterator started. +class ConcurrentTest { + private: + static const uint32_t K = 4; + + static uint64_t key(Key key) { return (key >> 40); } + static uint64_t gen(Key key) { return (key >> 8) & 0xffffffffu; } + static uint64_t hash(Key key) { return key & 0xff; } + + static uint64_t HashNumbers(uint64_t k, uint64_t g) { + uint64_t data[2] = { k, g }; + return Hash(reinterpret_cast(data), sizeof(data), 0); + } + + static Key MakeKey(uint64_t k, uint64_t g) { + assert(sizeof(Key) == sizeof(uint64_t)); + assert(k <= K); // We sometimes pass K to seek to the end of the skiplist + assert(g <= 0xffffffffu); + return ((k << 40) | (g << 8) | (HashNumbers(k, g) & 0xff)); + } + + static bool IsValidKey(Key k) { + return hash(k) == (HashNumbers(key(k), gen(k)) & 0xff); + } + + static Key RandomTarget(Random* rnd) { + switch (rnd->Next() % 10) { + case 0: + // Seek to beginning + return MakeKey(0, 0); + case 1: + // Seek to end + return MakeKey(K, 0); + default: + // Seek to middle + return MakeKey(rnd->Next() % K, 0); + } + } + + // Per-key generation + struct State { + port::AtomicPointer generation[K]; + void Set(int k, intptr_t v) { + generation[k].Release_Store(reinterpret_cast(v)); + } + intptr_t Get(int k) { + return reinterpret_cast(generation[k].Acquire_Load()); + } + + State() { + for (int k = 0; k < K; k++) { + Set(k, 0); + } + } + }; + + // Current state of the test + State current_; + + Arena arena_; + + // SkipList is not protected by mu_. We just use a single writer + // thread to modify it. + SkipList list_; + + public: + ConcurrentTest() : list_(Comparator(), &arena_) { } + + // REQUIRES: External synchronization + void WriteStep(Random* rnd) { + const uint32_t k = rnd->Next() % K; + const intptr_t g = current_.Get(k) + 1; + const Key key = MakeKey(k, g); + list_.Insert(key); + current_.Set(k, g); + } + + void ReadStep(Random* rnd) { + // Remember the initial committed state of the skiplist. + State initial_state; + for (int k = 0; k < K; k++) { + initial_state.Set(k, current_.Get(k)); + } + + Key pos = RandomTarget(rnd); + SkipList::Iterator iter(&list_); + iter.Seek(pos); + while (true) { + Key current; + if (!iter.Valid()) { + current = MakeKey(K, 0); + } else { + current = iter.key(); + ASSERT_TRUE(IsValidKey(current)) << current; + } + ASSERT_LE(pos, current) << "should not go backwards"; + + // Verify that everything in [pos,current) was not present in + // initial_state. + while (pos < current) { + ASSERT_LT(key(pos), K) << pos; + + // Note that generation 0 is never inserted, so it is ok if + // <*,0,*> is missing. + ASSERT_TRUE((gen(pos) == 0) || + (gen(pos) > initial_state.Get(key(pos))) + ) << "key: " << key(pos) + << "; gen: " << gen(pos) + << "; initgen: " + << initial_state.Get(key(pos)); + + // Advance to next key in the valid key space + if (key(pos) < key(current)) { + pos = MakeKey(key(pos) + 1, 0); + } else { + pos = MakeKey(key(pos), gen(pos) + 1); + } + } + + if (!iter.Valid()) { + break; + } + + if (rnd->Next() % 2) { + iter.Next(); + pos = MakeKey(key(pos), gen(pos) + 1); + } else { + Key new_target = RandomTarget(rnd); + if (new_target > pos) { + pos = new_target; + iter.Seek(new_target); + } + } + } + } +}; +const uint32_t ConcurrentTest::K; + +// Simple test that does single-threaded testing of the ConcurrentTest +// scaffolding. +TEST(SkipTest, ConcurrentWithoutThreads) { + ConcurrentTest test; + Random rnd(test::RandomSeed()); + for (int i = 0; i < 10000; i++) { + test.ReadStep(&rnd); + test.WriteStep(&rnd); + } +} + +class TestState { + public: + ConcurrentTest t_; + int seed_; + port::AtomicPointer quit_flag_; + + enum ReaderState { + STARTING, + RUNNING, + DONE + }; + + explicit TestState(int s) + : seed_(s), + quit_flag_(NULL), + state_(STARTING), + state_cv_(&mu_) {} + + void Wait(ReaderState s) { + mu_.Lock(); + while (state_ != s) { + state_cv_.Wait(); + } + mu_.Unlock(); + } + + void Change(ReaderState s) { + mu_.Lock(); + state_ = s; + state_cv_.Signal(); + mu_.Unlock(); + } + + private: + port::Mutex mu_; + ReaderState state_; + port::CondVar state_cv_; +}; + +static void ConcurrentReader(void* arg) { + TestState* state = reinterpret_cast(arg); + Random rnd(state->seed_); + int64_t reads = 0; + state->Change(TestState::RUNNING); + while (!state->quit_flag_.Acquire_Load()) { + state->t_.ReadStep(&rnd); + ++reads; + } + state->Change(TestState::DONE); +} + +static void RunConcurrent(int run) { + const int seed = test::RandomSeed() + (run * 100); + Random rnd(seed); + const int N = 1000; + const int kSize = 1000; + for (int i = 0; i < N; i++) { + if ((i % 100) == 0) { + fprintf(stderr, "Run %d of %d\n", i, N); + } + TestState state(seed + 1); + Env::Default()->Schedule(ConcurrentReader, &state); + state.Wait(TestState::RUNNING); + for (int i = 0; i < kSize; i++) { + state.t_.WriteStep(&rnd); + } + state.quit_flag_.Release_Store(&state); // Any non-NULL arg will do + state.Wait(TestState::DONE); + } +} + +TEST(SkipTest, Concurrent1) { RunConcurrent(1); } +TEST(SkipTest, Concurrent2) { RunConcurrent(2); } +TEST(SkipTest, Concurrent3) { RunConcurrent(3); } +TEST(SkipTest, Concurrent4) { RunConcurrent(4); } +TEST(SkipTest, Concurrent5) { RunConcurrent(5); } + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/leveldb/db/snapshot.h b/src/leveldb-lin/leveldb/db/snapshot.h new file mode 100644 index 0000000..e7f8fd2 --- /dev/null +++ b/src/leveldb-lin/leveldb/db/snapshot.h @@ -0,0 +1,66 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_SNAPSHOT_H_ +#define STORAGE_LEVELDB_DB_SNAPSHOT_H_ + +#include "leveldb/db.h" + +namespace leveldb { + +class SnapshotList; + +// Snapshots are kept in a doubly-linked list in the DB. +// Each SnapshotImpl corresponds to a particular sequence number. +class SnapshotImpl : public Snapshot { + public: + SequenceNumber number_; // const after creation + + private: + friend class SnapshotList; + + // SnapshotImpl is kept in a doubly-linked circular list + SnapshotImpl* prev_; + SnapshotImpl* next_; + + SnapshotList* list_; // just for sanity checks +}; + +class SnapshotList { + public: + SnapshotList() { + list_.prev_ = &list_; + list_.next_ = &list_; + } + + bool empty() const { return list_.next_ == &list_; } + SnapshotImpl* oldest() const { assert(!empty()); return list_.next_; } + SnapshotImpl* newest() const { assert(!empty()); return list_.prev_; } + + const SnapshotImpl* New(SequenceNumber seq) { + SnapshotImpl* s = new SnapshotImpl; + s->number_ = seq; + s->list_ = this; + s->next_ = &list_; + s->prev_ = list_.prev_; + s->prev_->next_ = s; + s->next_->prev_ = s; + return s; + } + + void Delete(const SnapshotImpl* s) { + assert(s->list_ == this); + s->prev_->next_ = s->next_; + s->next_->prev_ = s->prev_; + delete s; + } + + private: + // Dummy head of doubly-linked list of snapshots + SnapshotImpl list_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_SNAPSHOT_H_ diff --git a/src/leveldb-lin/leveldb/db/table_cache.cc b/src/leveldb-lin/leveldb/db/table_cache.cc new file mode 100644 index 0000000..e3d82cd --- /dev/null +++ b/src/leveldb-lin/leveldb/db/table_cache.cc @@ -0,0 +1,127 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/table_cache.h" + +#include "db/filename.h" +#include "leveldb/env.h" +#include "leveldb/table.h" +#include "util/coding.h" + +namespace leveldb { + +struct TableAndFile { + RandomAccessFile* file; + Table* table; +}; + +static void DeleteEntry(const Slice& key, void* value) { + TableAndFile* tf = reinterpret_cast(value); + delete tf->table; + delete tf->file; + delete tf; +} + +static void UnrefEntry(void* arg1, void* arg2) { + Cache* cache = reinterpret_cast(arg1); + Cache::Handle* h = reinterpret_cast(arg2); + cache->Release(h); +} + +TableCache::TableCache(const std::string& dbname, + const Options* options, + int entries) + : env_(options->env), + dbname_(dbname), + options_(options), + cache_(NewLRUCache(entries)) { +} + +TableCache::~TableCache() { + delete cache_; +} + +Status TableCache::FindTable(uint64_t file_number, uint64_t file_size, + Cache::Handle** handle) { + Status s; + char buf[sizeof(file_number)]; + EncodeFixed64(buf, file_number); + Slice key(buf, sizeof(buf)); + *handle = cache_->Lookup(key); + if (*handle == NULL) { + std::string fname = TableFileName(dbname_, file_number); + RandomAccessFile* file = NULL; + Table* table = NULL; + s = env_->NewRandomAccessFile(fname, &file); + if (!s.ok()) { + std::string old_fname = SSTTableFileName(dbname_, file_number); + if (env_->NewRandomAccessFile(old_fname, &file).ok()) { + s = Status::OK(); + } + } + if (s.ok()) { + s = Table::Open(*options_, file, file_size, &table); + } + + if (!s.ok()) { + assert(table == NULL); + delete file; + // We do not cache error results so that if the error is transient, + // or somebody repairs the file, we recover automatically. + } else { + TableAndFile* tf = new TableAndFile; + tf->file = file; + tf->table = table; + *handle = cache_->Insert(key, tf, 1, &DeleteEntry); + } + } + return s; +} + +Iterator* TableCache::NewIterator(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + Table** tableptr) { + if (tableptr != NULL) { + *tableptr = NULL; + } + + Cache::Handle* handle = NULL; + Status s = FindTable(file_number, file_size, &handle); + if (!s.ok()) { + return NewErrorIterator(s); + } + + Table* table = reinterpret_cast(cache_->Value(handle))->table; + Iterator* result = table->NewIterator(options); + result->RegisterCleanup(&UnrefEntry, cache_, handle); + if (tableptr != NULL) { + *tableptr = table; + } + return result; +} + +Status TableCache::Get(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + const Slice& k, + void* arg, + void (*saver)(void*, const Slice&, const Slice&)) { + Cache::Handle* handle = NULL; + Status s = FindTable(file_number, file_size, &handle); + if (s.ok()) { + Table* t = reinterpret_cast(cache_->Value(handle))->table; + s = t->InternalGet(options, k, arg, saver); + cache_->Release(handle); + } + return s; +} + +void TableCache::Evict(uint64_t file_number) { + char buf[sizeof(file_number)]; + EncodeFixed64(buf, file_number); + cache_->Erase(Slice(buf, sizeof(buf))); +} + +} // namespace leveldb diff --git a/src/leveldb-lin/leveldb/db/table_cache.h b/src/leveldb-lin/leveldb/db/table_cache.h new file mode 100644 index 0000000..8cf4aaf --- /dev/null +++ b/src/leveldb-lin/leveldb/db/table_cache.h @@ -0,0 +1,61 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Thread-safe (provides internal synchronization) + +#ifndef STORAGE_LEVELDB_DB_TABLE_CACHE_H_ +#define STORAGE_LEVELDB_DB_TABLE_CACHE_H_ + +#include +#include +#include "db/dbformat.h" +#include "leveldb/cache.h" +#include "leveldb/table.h" +#include "port/port.h" + +namespace leveldb { + +class Env; + +class TableCache { + public: + TableCache(const std::string& dbname, const Options* options, int entries); + ~TableCache(); + + // Return an iterator for the specified file number (the corresponding + // file length must be exactly "file_size" bytes). If "tableptr" is + // non-NULL, also sets "*tableptr" to point to the Table object + // underlying the returned iterator, or NULL if no Table object underlies + // the returned iterator. The returned "*tableptr" object is owned by + // the cache and should not be deleted, and is valid for as long as the + // returned iterator is live. + Iterator* NewIterator(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + Table** tableptr = NULL); + + // If a seek to internal key "k" in specified file finds an entry, + // call (*handle_result)(arg, found_key, found_value). + Status Get(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + const Slice& k, + void* arg, + void (*handle_result)(void*, const Slice&, const Slice&)); + + // Evict any entry for the specified file number + void Evict(uint64_t file_number); + + private: + Env* const env_; + const std::string dbname_; + const Options* options_; + Cache* cache_; + + Status FindTable(uint64_t file_number, uint64_t file_size, Cache::Handle**); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_TABLE_CACHE_H_ diff --git a/src/leveldb-lin/leveldb/db/version_edit.cc b/src/leveldb-lin/leveldb/db/version_edit.cc new file mode 100644 index 0000000..f10a2d5 --- /dev/null +++ b/src/leveldb-lin/leveldb/db/version_edit.cc @@ -0,0 +1,266 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/version_edit.h" + +#include "db/version_set.h" +#include "util/coding.h" + +namespace leveldb { + +// Tag numbers for serialized VersionEdit. These numbers are written to +// disk and should not be changed. +enum Tag { + kComparator = 1, + kLogNumber = 2, + kNextFileNumber = 3, + kLastSequence = 4, + kCompactPointer = 5, + kDeletedFile = 6, + kNewFile = 7, + // 8 was used for large value refs + kPrevLogNumber = 9 +}; + +void VersionEdit::Clear() { + comparator_.clear(); + log_number_ = 0; + prev_log_number_ = 0; + last_sequence_ = 0; + next_file_number_ = 0; + has_comparator_ = false; + has_log_number_ = false; + has_prev_log_number_ = false; + has_next_file_number_ = false; + has_last_sequence_ = false; + deleted_files_.clear(); + new_files_.clear(); +} + +void VersionEdit::EncodeTo(std::string* dst) const { + if (has_comparator_) { + PutVarint32(dst, kComparator); + PutLengthPrefixedSlice(dst, comparator_); + } + if (has_log_number_) { + PutVarint32(dst, kLogNumber); + PutVarint64(dst, log_number_); + } + if (has_prev_log_number_) { + PutVarint32(dst, kPrevLogNumber); + PutVarint64(dst, prev_log_number_); + } + if (has_next_file_number_) { + PutVarint32(dst, kNextFileNumber); + PutVarint64(dst, next_file_number_); + } + if (has_last_sequence_) { + PutVarint32(dst, kLastSequence); + PutVarint64(dst, last_sequence_); + } + + for (size_t i = 0; i < compact_pointers_.size(); i++) { + PutVarint32(dst, kCompactPointer); + PutVarint32(dst, compact_pointers_[i].first); // level + PutLengthPrefixedSlice(dst, compact_pointers_[i].second.Encode()); + } + + for (DeletedFileSet::const_iterator iter = deleted_files_.begin(); + iter != deleted_files_.end(); + ++iter) { + PutVarint32(dst, kDeletedFile); + PutVarint32(dst, iter->first); // level + PutVarint64(dst, iter->second); // file number + } + + for (size_t i = 0; i < new_files_.size(); i++) { + const FileMetaData& f = new_files_[i].second; + PutVarint32(dst, kNewFile); + PutVarint32(dst, new_files_[i].first); // level + PutVarint64(dst, f.number); + PutVarint64(dst, f.file_size); + PutLengthPrefixedSlice(dst, f.smallest.Encode()); + PutLengthPrefixedSlice(dst, f.largest.Encode()); + } +} + +static bool GetInternalKey(Slice* input, InternalKey* dst) { + Slice str; + if (GetLengthPrefixedSlice(input, &str)) { + dst->DecodeFrom(str); + return true; + } else { + return false; + } +} + +static bool GetLevel(Slice* input, int* level) { + uint32_t v; + if (GetVarint32(input, &v) && + v < config::kNumLevels) { + *level = v; + return true; + } else { + return false; + } +} + +Status VersionEdit::DecodeFrom(const Slice& src) { + Clear(); + Slice input = src; + const char* msg = NULL; + uint32_t tag; + + // Temporary storage for parsing + int level; + uint64_t number; + FileMetaData f; + Slice str; + InternalKey key; + + while (msg == NULL && GetVarint32(&input, &tag)) { + switch (tag) { + case kComparator: + if (GetLengthPrefixedSlice(&input, &str)) { + comparator_ = str.ToString(); + has_comparator_ = true; + } else { + msg = "comparator name"; + } + break; + + case kLogNumber: + if (GetVarint64(&input, &log_number_)) { + has_log_number_ = true; + } else { + msg = "log number"; + } + break; + + case kPrevLogNumber: + if (GetVarint64(&input, &prev_log_number_)) { + has_prev_log_number_ = true; + } else { + msg = "previous log number"; + } + break; + + case kNextFileNumber: + if (GetVarint64(&input, &next_file_number_)) { + has_next_file_number_ = true; + } else { + msg = "next file number"; + } + break; + + case kLastSequence: + if (GetVarint64(&input, &last_sequence_)) { + has_last_sequence_ = true; + } else { + msg = "last sequence number"; + } + break; + + case kCompactPointer: + if (GetLevel(&input, &level) && + GetInternalKey(&input, &key)) { + compact_pointers_.push_back(std::make_pair(level, key)); + } else { + msg = "compaction pointer"; + } + break; + + case kDeletedFile: + if (GetLevel(&input, &level) && + GetVarint64(&input, &number)) { + deleted_files_.insert(std::make_pair(level, number)); + } else { + msg = "deleted file"; + } + break; + + case kNewFile: + if (GetLevel(&input, &level) && + GetVarint64(&input, &f.number) && + GetVarint64(&input, &f.file_size) && + GetInternalKey(&input, &f.smallest) && + GetInternalKey(&input, &f.largest)) { + new_files_.push_back(std::make_pair(level, f)); + } else { + msg = "new-file entry"; + } + break; + + default: + msg = "unknown tag"; + break; + } + } + + if (msg == NULL && !input.empty()) { + msg = "invalid tag"; + } + + Status result; + if (msg != NULL) { + result = Status::Corruption("VersionEdit", msg); + } + return result; +} + +std::string VersionEdit::DebugString() const { + std::string r; + r.append("VersionEdit {"); + if (has_comparator_) { + r.append("\n Comparator: "); + r.append(comparator_); + } + if (has_log_number_) { + r.append("\n LogNumber: "); + AppendNumberTo(&r, log_number_); + } + if (has_prev_log_number_) { + r.append("\n PrevLogNumber: "); + AppendNumberTo(&r, prev_log_number_); + } + if (has_next_file_number_) { + r.append("\n NextFile: "); + AppendNumberTo(&r, next_file_number_); + } + if (has_last_sequence_) { + r.append("\n LastSeq: "); + AppendNumberTo(&r, last_sequence_); + } + for (size_t i = 0; i < compact_pointers_.size(); i++) { + r.append("\n CompactPointer: "); + AppendNumberTo(&r, compact_pointers_[i].first); + r.append(" "); + r.append(compact_pointers_[i].second.DebugString()); + } + for (DeletedFileSet::const_iterator iter = deleted_files_.begin(); + iter != deleted_files_.end(); + ++iter) { + r.append("\n DeleteFile: "); + AppendNumberTo(&r, iter->first); + r.append(" "); + AppendNumberTo(&r, iter->second); + } + for (size_t i = 0; i < new_files_.size(); i++) { + const FileMetaData& f = new_files_[i].second; + r.append("\n AddFile: "); + AppendNumberTo(&r, new_files_[i].first); + r.append(" "); + AppendNumberTo(&r, f.number); + r.append(" "); + AppendNumberTo(&r, f.file_size); + r.append(" "); + r.append(f.smallest.DebugString()); + r.append(" .. "); + r.append(f.largest.DebugString()); + } + r.append("\n}\n"); + return r; +} + +} // namespace leveldb diff --git a/src/leveldb-lin/leveldb/db/version_edit.h b/src/leveldb-lin/leveldb/db/version_edit.h new file mode 100644 index 0000000..eaef77b --- /dev/null +++ b/src/leveldb-lin/leveldb/db/version_edit.h @@ -0,0 +1,107 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_VERSION_EDIT_H_ +#define STORAGE_LEVELDB_DB_VERSION_EDIT_H_ + +#include +#include +#include +#include "db/dbformat.h" + +namespace leveldb { + +class VersionSet; + +struct FileMetaData { + int refs; + int allowed_seeks; // Seeks allowed until compaction + uint64_t number; + uint64_t file_size; // File size in bytes + InternalKey smallest; // Smallest internal key served by table + InternalKey largest; // Largest internal key served by table + + FileMetaData() : refs(0), allowed_seeks(1 << 30), file_size(0) { } +}; + +class VersionEdit { + public: + VersionEdit() { Clear(); } + ~VersionEdit() { } + + void Clear(); + + void SetComparatorName(const Slice& name) { + has_comparator_ = true; + comparator_ = name.ToString(); + } + void SetLogNumber(uint64_t num) { + has_log_number_ = true; + log_number_ = num; + } + void SetPrevLogNumber(uint64_t num) { + has_prev_log_number_ = true; + prev_log_number_ = num; + } + void SetNextFile(uint64_t num) { + has_next_file_number_ = true; + next_file_number_ = num; + } + void SetLastSequence(SequenceNumber seq) { + has_last_sequence_ = true; + last_sequence_ = seq; + } + void SetCompactPointer(int level, const InternalKey& key) { + compact_pointers_.push_back(std::make_pair(level, key)); + } + + // Add the specified file at the specified number. + // REQUIRES: This version has not been saved (see VersionSet::SaveTo) + // REQUIRES: "smallest" and "largest" are smallest and largest keys in file + void AddFile(int level, uint64_t file, + uint64_t file_size, + const InternalKey& smallest, + const InternalKey& largest) { + FileMetaData f; + f.number = file; + f.file_size = file_size; + f.smallest = smallest; + f.largest = largest; + new_files_.push_back(std::make_pair(level, f)); + } + + // Delete the specified "file" from the specified "level". + void DeleteFile(int level, uint64_t file) { + deleted_files_.insert(std::make_pair(level, file)); + } + + void EncodeTo(std::string* dst) const; + Status DecodeFrom(const Slice& src); + + std::string DebugString() const; + + private: + friend class VersionSet; + + typedef std::set< std::pair > DeletedFileSet; + + std::string comparator_; + uint64_t log_number_; + uint64_t prev_log_number_; + uint64_t next_file_number_; + SequenceNumber last_sequence_; + bool has_comparator_; + bool has_log_number_; + bool has_prev_log_number_; + bool has_next_file_number_; + bool has_last_sequence_; + + std::vector< std::pair > compact_pointers_; + DeletedFileSet deleted_files_; + std::vector< std::pair > new_files_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_VERSION_EDIT_H_ diff --git a/src/leveldb-lin/libleveldb.a b/src/leveldb-lin/libleveldb.a new file mode 100644 index 0000000..4b9649f Binary files /dev/null and b/src/leveldb-lin/libleveldb.a differ diff --git a/src/leveldb-lin/libleveldb.so b/src/leveldb-lin/libleveldb.so new file mode 100644 index 0000000..a8b0057 Binary files /dev/null and b/src/leveldb-lin/libleveldb.so differ diff --git a/src/leveldb-lin/libleveldb.so.1 b/src/leveldb-lin/libleveldb.so.1 new file mode 100644 index 0000000..a8b0057 Binary files /dev/null and b/src/leveldb-lin/libleveldb.so.1 differ diff --git a/src/leveldb-lin/libleveldb.so.1.9 b/src/leveldb-lin/libleveldb.so.1.9 new file mode 100644 index 0000000..a8b0057 Binary files /dev/null and b/src/leveldb-lin/libleveldb.so.1.9 differ diff --git a/src/leveldb-lin/libmemenv.a b/src/leveldb-lin/libmemenv.a new file mode 100644 index 0000000..db525c1 Binary files /dev/null and b/src/leveldb-lin/libmemenv.a differ diff --git a/src/leveldb-lin/port/README b/src/leveldb-lin/port/README new file mode 100644 index 0000000..422563e --- /dev/null +++ b/src/leveldb-lin/port/README @@ -0,0 +1,10 @@ +This directory contains interfaces and implementations that isolate the +rest of the package from platform details. + +Code in the rest of the package includes "port.h" from this directory. +"port.h" in turn includes a platform specific "port_.h" file +that provides the platform specific implementation. + +See port_posix.h for an example of what must be provided in a platform +specific header file. + diff --git a/src/leveldb-lin/port/atomic_pointer.h b/src/leveldb-lin/port/atomic_pointer.h new file mode 100644 index 0000000..e17bf43 --- /dev/null +++ b/src/leveldb-lin/port/atomic_pointer.h @@ -0,0 +1,224 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// AtomicPointer provides storage for a lock-free pointer. +// Platform-dependent implementation of AtomicPointer: +// - If the platform provides a cheap barrier, we use it with raw pointers +// - If cstdatomic is present (on newer versions of gcc, it is), we use +// a cstdatomic-based AtomicPointer. However we prefer the memory +// barrier based version, because at least on a gcc 4.4 32-bit build +// on linux, we have encountered a buggy +// implementation. Also, some implementations are much +// slower than a memory-barrier based implementation (~16ns for +// based acquire-load vs. ~1ns for a barrier based +// acquire-load). +// This code is based on atomicops-internals-* in Google's perftools: +// http://code.google.com/p/google-perftools/source/browse/#svn%2Ftrunk%2Fsrc%2Fbase + +#ifndef PORT_ATOMIC_POINTER_H_ +#define PORT_ATOMIC_POINTER_H_ + +#include +#ifdef LEVELDB_CSTDATOMIC_PRESENT +#include +#endif +#ifdef OS_WIN +#include +#endif +#ifdef OS_MACOSX +#include +#endif + +#if defined(_M_X64) || defined(__x86_64__) +#define ARCH_CPU_X86_FAMILY 1 +#elif defined(_M_IX86) || defined(__i386__) || defined(__i386) +#define ARCH_CPU_X86_FAMILY 1 +#elif defined(__ARMEL__) +#define ARCH_CPU_ARM_FAMILY 1 +#elif defined(__ppc__) || defined(__powerpc__) || defined(__powerpc64__) +#define ARCH_CPU_PPC_FAMILY 1 +#endif + +namespace leveldb { +namespace port { + +// Define MemoryBarrier() if available +// Windows on x86 +#if defined(OS_WIN) && defined(COMPILER_MSVC) && defined(ARCH_CPU_X86_FAMILY) +// windows.h already provides a MemoryBarrier(void) macro +// http://msdn.microsoft.com/en-us/library/ms684208(v=vs.85).aspx +#define LEVELDB_HAVE_MEMORY_BARRIER + +// Gcc on x86 +#elif defined(ARCH_CPU_X86_FAMILY) && defined(__GNUC__) +inline void MemoryBarrier() { + // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on + // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering. + __asm__ __volatile__("" : : : "memory"); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +// Sun Studio +#elif defined(ARCH_CPU_X86_FAMILY) && defined(__SUNPRO_CC) +inline void MemoryBarrier() { + // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on + // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering. + asm volatile("" : : : "memory"); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +// Mac OS +#elif defined(OS_MACOSX) +inline void MemoryBarrier() { + OSMemoryBarrier(); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +// ARM Linux +#elif defined(ARCH_CPU_ARM_FAMILY) && defined(__linux__) +typedef void (*LinuxKernelMemoryBarrierFunc)(void); +// The Linux ARM kernel provides a highly optimized device-specific memory +// barrier function at a fixed memory address that is mapped in every +// user-level process. +// +// This beats using CPU-specific instructions which are, on single-core +// devices, un-necessary and very costly (e.g. ARMv7-A "dmb" takes more +// than 180ns on a Cortex-A8 like the one on a Nexus One). Benchmarking +// shows that the extra function call cost is completely negligible on +// multi-core devices. +// +inline void MemoryBarrier() { + (*(LinuxKernelMemoryBarrierFunc)0xffff0fa0)(); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +// PPC +#elif defined(ARCH_CPU_PPC_FAMILY) && defined(__GNUC__) +inline void MemoryBarrier() { + // TODO for some powerpc expert: is there a cheaper suitable variant? + // Perhaps by having separate barriers for acquire and release ops. + asm volatile("sync" : : : "memory"); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +#endif + +// AtomicPointer built using platform-specific MemoryBarrier() +#if defined(LEVELDB_HAVE_MEMORY_BARRIER) +class AtomicPointer { + private: + void* rep_; + public: + AtomicPointer() { } + explicit AtomicPointer(void* p) : rep_(p) {} + inline void* NoBarrier_Load() const { return rep_; } + inline void NoBarrier_Store(void* v) { rep_ = v; } + inline void* Acquire_Load() const { + void* result = rep_; + MemoryBarrier(); + return result; + } + inline void Release_Store(void* v) { + MemoryBarrier(); + rep_ = v; + } +}; + +// AtomicPointer based on +#elif defined(LEVELDB_CSTDATOMIC_PRESENT) +class AtomicPointer { + private: + std::atomic rep_; + public: + AtomicPointer() { } + explicit AtomicPointer(void* v) : rep_(v) { } + inline void* Acquire_Load() const { + return rep_.load(std::memory_order_acquire); + } + inline void Release_Store(void* v) { + rep_.store(v, std::memory_order_release); + } + inline void* NoBarrier_Load() const { + return rep_.load(std::memory_order_relaxed); + } + inline void NoBarrier_Store(void* v) { + rep_.store(v, std::memory_order_relaxed); + } +}; + +// Atomic pointer based on sparc memory barriers +#elif defined(__sparcv9) && defined(__GNUC__) +class AtomicPointer { + private: + void* rep_; + public: + AtomicPointer() { } + explicit AtomicPointer(void* v) : rep_(v) { } + inline void* Acquire_Load() const { + void* val; + __asm__ __volatile__ ( + "ldx [%[rep_]], %[val] \n\t" + "membar #LoadLoad|#LoadStore \n\t" + : [val] "=r" (val) + : [rep_] "r" (&rep_) + : "memory"); + return val; + } + inline void Release_Store(void* v) { + __asm__ __volatile__ ( + "membar #LoadStore|#StoreStore \n\t" + "stx %[v], [%[rep_]] \n\t" + : + : [rep_] "r" (&rep_), [v] "r" (v) + : "memory"); + } + inline void* NoBarrier_Load() const { return rep_; } + inline void NoBarrier_Store(void* v) { rep_ = v; } +}; + +// Atomic pointer based on ia64 acq/rel +#elif defined(__ia64) && defined(__GNUC__) +class AtomicPointer { + private: + void* rep_; + public: + AtomicPointer() { } + explicit AtomicPointer(void* v) : rep_(v) { } + inline void* Acquire_Load() const { + void* val ; + __asm__ __volatile__ ( + "ld8.acq %[val] = [%[rep_]] \n\t" + : [val] "=r" (val) + : [rep_] "r" (&rep_) + : "memory" + ); + return val; + } + inline void Release_Store(void* v) { + __asm__ __volatile__ ( + "st8.rel [%[rep_]] = %[v] \n\t" + : + : [rep_] "r" (&rep_), [v] "r" (v) + : "memory" + ); + } + inline void* NoBarrier_Load() const { return rep_; } + inline void NoBarrier_Store(void* v) { rep_ = v; } +}; + +// We have neither MemoryBarrier(), nor +#else +#error Please implement AtomicPointer for this platform. + +#endif + +#undef LEVELDB_HAVE_MEMORY_BARRIER +#undef ARCH_CPU_X86_FAMILY +#undef ARCH_CPU_ARM_FAMILY +#undef ARCH_CPU_PPC_FAMILY + +} // namespace port +} // namespace leveldb + +#endif // PORT_ATOMIC_POINTER_H_ diff --git a/src/leveldb-lin/port/port.h b/src/leveldb-lin/port/port.h new file mode 100644 index 0000000..4baafa8 --- /dev/null +++ b/src/leveldb-lin/port/port.h @@ -0,0 +1,21 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_PORT_PORT_H_ +#define STORAGE_LEVELDB_PORT_PORT_H_ + +#include + +// Include the appropriate platform specific file below. If you are +// porting to a new platform, see "port_example.h" for documentation +// of what the new port_.h file must provide. +#if defined(LEVELDB_PLATFORM_POSIX) +# include "port/port_posix.h" +#elif defined(LEVELDB_PLATFORM_CHROMIUM) +# include "port/port_chromium.h" +#elif defined(LEVELDB_PLATFORM_WINDOWS) +# include "port/port_win.h" +#endif + +#endif // STORAGE_LEVELDB_PORT_PORT_H_ diff --git a/src/leveldb-lin/port/port_example.h b/src/leveldb-lin/port/port_example.h new file mode 100644 index 0000000..ab9e489 --- /dev/null +++ b/src/leveldb-lin/port/port_example.h @@ -0,0 +1,135 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// This file contains the specification, but not the implementations, +// of the types/operations/etc. that should be defined by a platform +// specific port_.h file. Use this file as a reference for +// how to port this package to a new platform. + +#ifndef STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_ +#define STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_ + +namespace leveldb { +namespace port { + +// TODO(jorlow): Many of these belong more in the environment class rather than +// here. We should try moving them and see if it affects perf. + +// The following boolean constant must be true on a little-endian machine +// and false otherwise. +static const bool kLittleEndian = true /* or some other expression */; + +// ------------------ Threading ------------------- + +// A Mutex represents an exclusive lock. +class Mutex { + public: + Mutex(); + ~Mutex(); + + // Lock the mutex. Waits until other lockers have exited. + // Will deadlock if the mutex is already locked by this thread. + void Lock(); + + // Unlock the mutex. + // REQUIRES: This mutex was locked by this thread. + void Unlock(); + + // Optionally crash if this thread does not hold this mutex. + // The implementation must be fast, especially if NDEBUG is + // defined. The implementation is allowed to skip all checks. + void AssertHeld(); +}; + +class CondVar { + public: + explicit CondVar(Mutex* mu); + ~CondVar(); + + // Atomically release *mu and block on this condition variable until + // either a call to SignalAll(), or a call to Signal() that picks + // this thread to wakeup. + // REQUIRES: this thread holds *mu + void Wait(); + + // If there are some threads waiting, wake up at least one of them. + void Signal(); + + // Wake up all waiting threads. + void SignallAll(); +}; + +// Thread-safe initialization. +// Used as follows: +// static port::OnceType init_control = LEVELDB_ONCE_INIT; +// static void Initializer() { ... do something ...; } +// ... +// port::InitOnce(&init_control, &Initializer); +typedef intptr_t OnceType; +#define LEVELDB_ONCE_INIT 0 +extern void InitOnce(port::OnceType*, void (*initializer)()); + +// A type that holds a pointer that can be read or written atomically +// (i.e., without word-tearing.) +class AtomicPointer { + private: + intptr_t rep_; + public: + // Initialize to arbitrary value + AtomicPointer(); + + // Initialize to hold v + explicit AtomicPointer(void* v) : rep_(v) { } + + // Read and return the stored pointer with the guarantee that no + // later memory access (read or write) by this thread can be + // reordered ahead of this read. + void* Acquire_Load() const; + + // Set v as the stored pointer with the guarantee that no earlier + // memory access (read or write) by this thread can be reordered + // after this store. + void Release_Store(void* v); + + // Read the stored pointer with no ordering guarantees. + void* NoBarrier_Load() const; + + // Set va as the stored pointer with no ordering guarantees. + void NoBarrier_Store(void* v); +}; + +// ------------------ Compression ------------------- + +// Store the snappy compression of "input[0,input_length-1]" in *output. +// Returns false if snappy is not supported by this port. +extern bool Snappy_Compress(const char* input, size_t input_length, + std::string* output); + +// If input[0,input_length-1] looks like a valid snappy compressed +// buffer, store the size of the uncompressed data in *result and +// return true. Else return false. +extern bool Snappy_GetUncompressedLength(const char* input, size_t length, + size_t* result); + +// Attempt to snappy uncompress input[0,input_length-1] into *output. +// Returns true if successful, false if the input is invalid lightweight +// compressed data. +// +// REQUIRES: at least the first "n" bytes of output[] must be writable +// where "n" is the result of a successful call to +// Snappy_GetUncompressedLength. +extern bool Snappy_Uncompress(const char* input_data, size_t input_length, + char* output); + +// ------------------ Miscellaneous ------------------- + +// If heap profiling is not supported, returns false. +// Else repeatedly calls (*func)(arg, data, n) and then returns true. +// The concatenation of all "data[0,n-1]" fragments is the heap profile. +extern bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg); + +} // namespace port +} // namespace leveldb + +#endif // STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_ diff --git a/src/leveldb-lin/port/port_posix.cc b/src/leveldb-lin/port/port_posix.cc new file mode 100644 index 0000000..5ba127a --- /dev/null +++ b/src/leveldb-lin/port/port_posix.cc @@ -0,0 +1,54 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "port/port_posix.h" + +#include +#include +#include +#include "util/logging.h" + +namespace leveldb { +namespace port { + +static void PthreadCall(const char* label, int result) { + if (result != 0) { + fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); + abort(); + } +} + +Mutex::Mutex() { PthreadCall("init mutex", pthread_mutex_init(&mu_, NULL)); } + +Mutex::~Mutex() { PthreadCall("destroy mutex", pthread_mutex_destroy(&mu_)); } + +void Mutex::Lock() { PthreadCall("lock", pthread_mutex_lock(&mu_)); } + +void Mutex::Unlock() { PthreadCall("unlock", pthread_mutex_unlock(&mu_)); } + +CondVar::CondVar(Mutex* mu) + : mu_(mu) { + PthreadCall("init cv", pthread_cond_init(&cv_, NULL)); +} + +CondVar::~CondVar() { PthreadCall("destroy cv", pthread_cond_destroy(&cv_)); } + +void CondVar::Wait() { + PthreadCall("wait", pthread_cond_wait(&cv_, &mu_->mu_)); +} + +void CondVar::Signal() { + PthreadCall("signal", pthread_cond_signal(&cv_)); +} + +void CondVar::SignalAll() { + PthreadCall("broadcast", pthread_cond_broadcast(&cv_)); +} + +void InitOnce(OnceType* once, void (*initializer)()) { + PthreadCall("once", pthread_once(once, initializer)); +} + +} // namespace port +} // namespace leveldb diff --git a/src/leveldb-lin/port/port_posix.h b/src/leveldb-lin/port/port_posix.h new file mode 100644 index 0000000..f2b89bf --- /dev/null +++ b/src/leveldb-lin/port/port_posix.h @@ -0,0 +1,157 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// See port_example.h for documentation for the following types/functions. + +#ifndef STORAGE_LEVELDB_PORT_PORT_POSIX_H_ +#define STORAGE_LEVELDB_PORT_PORT_POSIX_H_ + +#undef PLATFORM_IS_LITTLE_ENDIAN +#if defined(OS_MACOSX) + #include + #if defined(__DARWIN_LITTLE_ENDIAN) && defined(__DARWIN_BYTE_ORDER) + #define PLATFORM_IS_LITTLE_ENDIAN \ + (__DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN) + #endif +#elif defined(OS_SOLARIS) + #include + #ifdef _LITTLE_ENDIAN + #define PLATFORM_IS_LITTLE_ENDIAN true + #else + #define PLATFORM_IS_LITTLE_ENDIAN false + #endif +#elif defined(OS_FREEBSD) + #include + #include + #define PLATFORM_IS_LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN) +#elif defined(OS_OPENBSD) || defined(OS_NETBSD) ||\ + defined(OS_DRAGONFLYBSD) + #include + #include +#elif defined(OS_HPUX) + #define PLATFORM_IS_LITTLE_ENDIAN false +#elif defined(OS_ANDROID) + // Due to a bug in the NDK x86 definition, + // _BYTE_ORDER must be used instead of __BYTE_ORDER on Android. + // See http://code.google.com/p/android/issues/detail?id=39824 + #include + #define PLATFORM_IS_LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN) +#else + #include +#endif + +#include +#ifdef SNAPPY +#include +#endif +#include +#include +#include "port/atomic_pointer.h" + +#ifndef PLATFORM_IS_LITTLE_ENDIAN +#define PLATFORM_IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN) +#endif + +#if defined(OS_MACOSX) || defined(OS_SOLARIS) || defined(OS_FREEBSD) ||\ + defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) ||\ + defined(OS_ANDROID) || defined(OS_HPUX) +// Use fread/fwrite/fflush on platforms without _unlocked variants +#define fread_unlocked fread +#define fwrite_unlocked fwrite +#define fflush_unlocked fflush +#endif + +#if defined(OS_MACOSX) || defined(OS_FREEBSD) ||\ + defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) +// Use fsync() on platforms without fdatasync() +#define fdatasync fsync +#endif + +#if defined(OS_ANDROID) && __ANDROID_API__ < 9 +// fdatasync() was only introduced in API level 9 on Android. Use fsync() +// when targetting older platforms. +#define fdatasync fsync +#endif + +namespace leveldb { +namespace port { + +static const bool kLittleEndian = PLATFORM_IS_LITTLE_ENDIAN; +#undef PLATFORM_IS_LITTLE_ENDIAN + +class CondVar; + +class Mutex { + public: + Mutex(); + ~Mutex(); + + void Lock(); + void Unlock(); + void AssertHeld() { } + + private: + friend class CondVar; + pthread_mutex_t mu_; + + // No copying + Mutex(const Mutex&); + void operator=(const Mutex&); +}; + +class CondVar { + public: + explicit CondVar(Mutex* mu); + ~CondVar(); + void Wait(); + void Signal(); + void SignalAll(); + private: + pthread_cond_t cv_; + Mutex* mu_; +}; + +typedef pthread_once_t OnceType; +#define LEVELDB_ONCE_INIT PTHREAD_ONCE_INIT +extern void InitOnce(OnceType* once, void (*initializer)()); + +inline bool Snappy_Compress(const char* input, size_t length, + ::std::string* output) { +#ifdef SNAPPY + output->resize(snappy::MaxCompressedLength(length)); + size_t outlen; + snappy::RawCompress(input, length, &(*output)[0], &outlen); + output->resize(outlen); + return true; +#endif + + return false; +} + +inline bool Snappy_GetUncompressedLength(const char* input, size_t length, + size_t* result) { +#ifdef SNAPPY + return snappy::GetUncompressedLength(input, length, result); +#else + return false; +#endif +} + +inline bool Snappy_Uncompress(const char* input, size_t length, + char* output) { +#ifdef SNAPPY + return snappy::RawUncompress(input, length, output); +#else + return false; +#endif +} + +inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { + return false; +} + +} // namespace port +} // namespace leveldb + +#endif // STORAGE_LEVELDB_PORT_PORT_POSIX_H_ diff --git a/src/leveldb-lin/port/port_posix.o b/src/leveldb-lin/port/port_posix.o new file mode 100644 index 0000000..65740e6 Binary files /dev/null and b/src/leveldb-lin/port/port_posix.o differ diff --git a/src/leveldb-lin/port/port_win.cc b/src/leveldb-lin/port/port_win.cc new file mode 100644 index 0000000..99c1d8e --- /dev/null +++ b/src/leveldb-lin/port/port_win.cc @@ -0,0 +1,149 @@ +// LevelDB Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// See port_example.h for documentation for the following types/functions. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the University of California, Berkeley nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "port/port_win.h" + +#include +#include + +namespace leveldb { +namespace port { + +Mutex::Mutex() : + cs_(NULL) { + assert(!cs_); + cs_ = static_cast(new CRITICAL_SECTION()); + ::InitializeCriticalSection(static_cast(cs_)); + assert(cs_); +} + +Mutex::~Mutex() { + assert(cs_); + ::DeleteCriticalSection(static_cast(cs_)); + delete static_cast(cs_); + cs_ = NULL; + assert(!cs_); +} + +void Mutex::Lock() { + assert(cs_); + ::EnterCriticalSection(static_cast(cs_)); +} + +void Mutex::Unlock() { + assert(cs_); + ::LeaveCriticalSection(static_cast(cs_)); +} + +void Mutex::AssertHeld() { + assert(cs_); + assert(1); +} + +CondVar::CondVar(Mutex* mu) : + waiting_(0), + mu_(mu), + sem1_(::CreateSemaphore(NULL, 0, 10000, NULL)), + sem2_(::CreateSemaphore(NULL, 0, 10000, NULL)) { + assert(mu_); +} + +CondVar::~CondVar() { + ::CloseHandle(sem1_); + ::CloseHandle(sem2_); +} + +void CondVar::Wait() { + mu_->AssertHeld(); + + wait_mtx_.Lock(); + ++waiting_; + wait_mtx_.Unlock(); + + mu_->Unlock(); + + // initiate handshake + ::WaitForSingleObject(sem1_, INFINITE); + ::ReleaseSemaphore(sem2_, 1, NULL); + mu_->Lock(); +} + +void CondVar::Signal() { + wait_mtx_.Lock(); + if (waiting_ > 0) { + --waiting_; + + // finalize handshake + ::ReleaseSemaphore(sem1_, 1, NULL); + ::WaitForSingleObject(sem2_, INFINITE); + } + wait_mtx_.Unlock(); +} + +void CondVar::SignalAll() { + wait_mtx_.Lock(); + for(long i = 0; i < waiting_; ++i) { + ::ReleaseSemaphore(sem1_, 1, NULL); + while(waiting_ > 0) { + --waiting_; + ::WaitForSingleObject(sem2_, INFINITE); + } + } + wait_mtx_.Unlock(); +} + +AtomicPointer::AtomicPointer(void* v) { + Release_Store(v); +} + +void InitOnce(OnceType* once, void (*initializer)()) { + once->InitOnce(initializer); +} + +void* AtomicPointer::Acquire_Load() const { + void * p = NULL; + InterlockedExchangePointer(&p, rep_); + return p; +} + +void AtomicPointer::Release_Store(void* v) { + InterlockedExchangePointer(&rep_, v); +} + +void* AtomicPointer::NoBarrier_Load() const { + return rep_; +} + +void AtomicPointer::NoBarrier_Store(void* v) { + rep_ = v; +} + +} +} diff --git a/src/leveldb-lin/port/port_win.h b/src/leveldb-lin/port/port_win.h new file mode 100644 index 0000000..45bf2f0 --- /dev/null +++ b/src/leveldb-lin/port/port_win.h @@ -0,0 +1,174 @@ +// LevelDB Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// See port_example.h for documentation for the following types/functions. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the University of California, Berkeley nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef STORAGE_LEVELDB_PORT_PORT_WIN_H_ +#define STORAGE_LEVELDB_PORT_PORT_WIN_H_ + +#ifdef _MSC_VER +#define snprintf _snprintf +#define close _close +#define fread_unlocked _fread_nolock +#endif + +#include +#include +#ifdef SNAPPY +#include +#endif + +namespace leveldb { +namespace port { + +// Windows is little endian (for now :p) +static const bool kLittleEndian = true; + +class CondVar; + +class Mutex { + public: + Mutex(); + ~Mutex(); + + void Lock(); + void Unlock(); + void AssertHeld(); + + private: + friend class CondVar; + // critical sections are more efficient than mutexes + // but they are not recursive and can only be used to synchronize threads within the same process + // we use opaque void * to avoid including windows.h in port_win.h + void * cs_; + + // No copying + Mutex(const Mutex&); + void operator=(const Mutex&); +}; + +// the Win32 API offers a dependable condition variable mechanism, but only starting with +// Windows 2008 and Vista +// no matter what we will implement our own condition variable with a semaphore +// implementation as described in a paper written by Andrew D. Birrell in 2003 +class CondVar { + public: + explicit CondVar(Mutex* mu); + ~CondVar(); + void Wait(); + void Signal(); + void SignalAll(); + private: + Mutex* mu_; + + Mutex wait_mtx_; + long waiting_; + + void * sem1_; + void * sem2_; + + +}; + +class OnceType { +public: +// OnceType() : init_(false) {} + OnceType(const OnceType &once) : init_(once.init_) {} + OnceType(bool f) : init_(f) {} + void InitOnce(void (*initializer)()) { + mutex_.Lock(); + if (!init_) { + init_ = true; + initializer(); + } + mutex_.Unlock(); + } + +private: + bool init_; + Mutex mutex_; +}; + +#define LEVELDB_ONCE_INIT false +extern void InitOnce(port::OnceType*, void (*initializer)()); + +// Storage for a lock-free pointer +class AtomicPointer { + private: + void * rep_; + public: + AtomicPointer() : rep_(NULL) { } + explicit AtomicPointer(void* v); + void* Acquire_Load() const; + + void Release_Store(void* v); + + void* NoBarrier_Load() const; + + void NoBarrier_Store(void* v); +}; + +inline bool Snappy_Compress(const char* input, size_t length, + ::std::string* output) { +#ifdef SNAPPY + output->resize(snappy::MaxCompressedLength(length)); + size_t outlen; + snappy::RawCompress(input, length, &(*output)[0], &outlen); + output->resize(outlen); + return true; +#endif + + return false; +} + +inline bool Snappy_GetUncompressedLength(const char* input, size_t length, + size_t* result) { +#ifdef SNAPPY + return snappy::GetUncompressedLength(input, length, result); +#else + return false; +#endif +} + +inline bool Snappy_Uncompress(const char* input, size_t length, + char* output) { +#ifdef SNAPPY + return snappy::RawUncompress(input, length, output); +#else + return false; +#endif +} + +inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { + return false; +} + +} +} + +#endif // STORAGE_LEVELDB_PORT_PORT_WIN_H_ diff --git a/src/leveldb-lin/port/thread_annotations.h b/src/leveldb-lin/port/thread_annotations.h new file mode 100644 index 0000000..6f9b6a7 --- /dev/null +++ b/src/leveldb-lin/port/thread_annotations.h @@ -0,0 +1,59 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H + +// Some environments provide custom macros to aid in static thread-safety +// analysis. Provide empty definitions of such macros unless they are already +// defined. + +#ifndef EXCLUSIVE_LOCKS_REQUIRED +#define EXCLUSIVE_LOCKS_REQUIRED(...) +#endif + +#ifndef SHARED_LOCKS_REQUIRED +#define SHARED_LOCKS_REQUIRED(...) +#endif + +#ifndef LOCKS_EXCLUDED +#define LOCKS_EXCLUDED(...) +#endif + +#ifndef LOCK_RETURNED +#define LOCK_RETURNED(x) +#endif + +#ifndef LOCKABLE +#define LOCKABLE +#endif + +#ifndef SCOPED_LOCKABLE +#define SCOPED_LOCKABLE +#endif + +#ifndef EXCLUSIVE_LOCK_FUNCTION +#define EXCLUSIVE_LOCK_FUNCTION(...) +#endif + +#ifndef SHARED_LOCK_FUNCTION +#define SHARED_LOCK_FUNCTION(...) +#endif + +#ifndef EXCLUSIVE_TRYLOCK_FUNCTION +#define EXCLUSIVE_TRYLOCK_FUNCTION(...) +#endif + +#ifndef SHARED_TRYLOCK_FUNCTION +#define SHARED_TRYLOCK_FUNCTION(...) +#endif + +#ifndef UNLOCK_FUNCTION +#define UNLOCK_FUNCTION(...) +#endif + +#ifndef NO_THREAD_SAFETY_ANALYSIS +#define NO_THREAD_SAFETY_ANALYSIS +#endif + +#endif // STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H diff --git a/src/leveldb-lin/port/win/stdint.h b/src/leveldb-lin/port/win/stdint.h new file mode 100644 index 0000000..39edd0d --- /dev/null +++ b/src/leveldb-lin/port/win/stdint.h @@ -0,0 +1,24 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// MSVC didn't ship with this file until the 2010 version. + +#ifndef STORAGE_LEVELDB_PORT_WIN_STDINT_H_ +#define STORAGE_LEVELDB_PORT_WIN_STDINT_H_ + +#if !defined(_MSC_VER) +#error This file should only be included when compiling with MSVC. +#endif + +// Define C99 equivalent types. +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long long int64_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +#endif // STORAGE_LEVELDB_PORT_WIN_STDINT_H_ diff --git a/src/leveldb-lin/table/block.cc b/src/leveldb-lin/table/block.cc new file mode 100644 index 0000000..ab83c11 --- /dev/null +++ b/src/leveldb-lin/table/block.cc @@ -0,0 +1,267 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Decodes the blocks generated by block_builder.cc. + +#include "table/block.h" + +#include +#include +#include "leveldb/comparator.h" +#include "table/format.h" +#include "util/coding.h" +#include "util/logging.h" + +namespace leveldb { + +inline uint32_t Block::NumRestarts() const { + assert(size_ >= 2*sizeof(uint32_t)); + return DecodeFixed32(data_ + size_ - sizeof(uint32_t)); +} + +Block::Block(const BlockContents& contents) + : data_(contents.data.data()), + size_(contents.data.size()), + owned_(contents.heap_allocated) { + if (size_ < sizeof(uint32_t)) { + size_ = 0; // Error marker + } else { + restart_offset_ = size_ - (1 + NumRestarts()) * sizeof(uint32_t); + if (restart_offset_ > size_ - sizeof(uint32_t)) { + // The size is too small for NumRestarts() and therefore + // restart_offset_ wrapped around. + size_ = 0; + } + } +} + +Block::~Block() { + if (owned_) { + delete[] data_; + } +} + +// Helper routine: decode the next block entry starting at "p", +// storing the number of shared key bytes, non_shared key bytes, +// and the length of the value in "*shared", "*non_shared", and +// "*value_length", respectively. Will not derefence past "limit". +// +// If any errors are detected, returns NULL. Otherwise, returns a +// pointer to the key delta (just past the three decoded values). +static inline const char* DecodeEntry(const char* p, const char* limit, + uint32_t* shared, + uint32_t* non_shared, + uint32_t* value_length) { + if (limit - p < 3) return NULL; + *shared = reinterpret_cast(p)[0]; + *non_shared = reinterpret_cast(p)[1]; + *value_length = reinterpret_cast(p)[2]; + if ((*shared | *non_shared | *value_length) < 128) { + // Fast path: all three values are encoded in one byte each + p += 3; + } else { + if ((p = GetVarint32Ptr(p, limit, shared)) == NULL) return NULL; + if ((p = GetVarint32Ptr(p, limit, non_shared)) == NULL) return NULL; + if ((p = GetVarint32Ptr(p, limit, value_length)) == NULL) return NULL; + } + + if (static_cast(limit - p) < (*non_shared + *value_length)) { + return NULL; + } + return p; +} + +class Block::Iter : public Iterator { + private: + const Comparator* const comparator_; + const char* const data_; // underlying block contents + uint32_t const restarts_; // Offset of restart array (list of fixed32) + uint32_t const num_restarts_; // Number of uint32_t entries in restart array + + // current_ is offset in data_ of current entry. >= restarts_ if !Valid + uint32_t current_; + uint32_t restart_index_; // Index of restart block in which current_ falls + std::string key_; + Slice value_; + Status status_; + + inline int Compare(const Slice& a, const Slice& b) const { + return comparator_->Compare(a, b); + } + + // Return the offset in data_ just past the end of the current entry. + inline uint32_t NextEntryOffset() const { + return (value_.data() + value_.size()) - data_; + } + + uint32_t GetRestartPoint(uint32_t index) { + assert(index < num_restarts_); + return DecodeFixed32(data_ + restarts_ + index * sizeof(uint32_t)); + } + + void SeekToRestartPoint(uint32_t index) { + key_.clear(); + restart_index_ = index; + // current_ will be fixed by ParseNextKey(); + + // ParseNextKey() starts at the end of value_, so set value_ accordingly + uint32_t offset = GetRestartPoint(index); + value_ = Slice(data_ + offset, 0); + } + + public: + Iter(const Comparator* comparator, + const char* data, + uint32_t restarts, + uint32_t num_restarts) + : comparator_(comparator), + data_(data), + restarts_(restarts), + num_restarts_(num_restarts), + current_(restarts_), + restart_index_(num_restarts_) { + assert(num_restarts_ > 0); + } + + virtual bool Valid() const { return current_ < restarts_; } + virtual Status status() const { return status_; } + virtual Slice key() const { + assert(Valid()); + return key_; + } + virtual Slice value() const { + assert(Valid()); + return value_; + } + + virtual void Next() { + assert(Valid()); + ParseNextKey(); + } + + virtual void Prev() { + assert(Valid()); + + // Scan backwards to a restart point before current_ + const uint32_t original = current_; + while (GetRestartPoint(restart_index_) >= original) { + if (restart_index_ == 0) { + // No more entries + current_ = restarts_; + restart_index_ = num_restarts_; + return; + } + restart_index_--; + } + + SeekToRestartPoint(restart_index_); + do { + // Loop until end of current entry hits the start of original entry + } while (ParseNextKey() && NextEntryOffset() < original); + } + + virtual void Seek(const Slice& target) { + // Binary search in restart array to find the last restart point + // with a key < target + uint32_t left = 0; + uint32_t right = num_restarts_ - 1; + while (left < right) { + uint32_t mid = (left + right + 1) / 2; + uint32_t region_offset = GetRestartPoint(mid); + uint32_t shared, non_shared, value_length; + const char* key_ptr = DecodeEntry(data_ + region_offset, + data_ + restarts_, + &shared, &non_shared, &value_length); + if (key_ptr == NULL || (shared != 0)) { + CorruptionError(); + return; + } + Slice mid_key(key_ptr, non_shared); + if (Compare(mid_key, target) < 0) { + // Key at "mid" is smaller than "target". Therefore all + // blocks before "mid" are uninteresting. + left = mid; + } else { + // Key at "mid" is >= "target". Therefore all blocks at or + // after "mid" are uninteresting. + right = mid - 1; + } + } + + // Linear search (within restart block) for first key >= target + SeekToRestartPoint(left); + while (true) { + if (!ParseNextKey()) { + return; + } + if (Compare(key_, target) >= 0) { + return; + } + } + } + + virtual void SeekToFirst() { + SeekToRestartPoint(0); + ParseNextKey(); + } + + virtual void SeekToLast() { + SeekToRestartPoint(num_restarts_ - 1); + while (ParseNextKey() && NextEntryOffset() < restarts_) { + // Keep skipping + } + } + + private: + void CorruptionError() { + current_ = restarts_; + restart_index_ = num_restarts_; + status_ = Status::Corruption("bad entry in block"); + key_.clear(); + value_.clear(); + } + + bool ParseNextKey() { + current_ = NextEntryOffset(); + const char* p = data_ + current_; + const char* limit = data_ + restarts_; // Restarts come right after data + if (p >= limit) { + // No more entries to return. Mark as invalid. + current_ = restarts_; + restart_index_ = num_restarts_; + return false; + } + + // Decode next entry + uint32_t shared, non_shared, value_length; + p = DecodeEntry(p, limit, &shared, &non_shared, &value_length); + if (p == NULL || key_.size() < shared) { + CorruptionError(); + return false; + } else { + key_.resize(shared); + key_.append(p, non_shared); + value_ = Slice(p + non_shared, value_length); + while (restart_index_ + 1 < num_restarts_ && + GetRestartPoint(restart_index_ + 1) < current_) { + ++restart_index_; + } + return true; + } + } +}; + +Iterator* Block::NewIterator(const Comparator* cmp) { + if (size_ < 2*sizeof(uint32_t)) { + return NewErrorIterator(Status::Corruption("bad block contents")); + } + const uint32_t num_restarts = NumRestarts(); + if (num_restarts == 0) { + return NewEmptyIterator(); + } else { + return new Iter(cmp, data_, restart_offset_, num_restarts); + } +} + +} // namespace leveldb diff --git a/src/leveldb-lin/table/block.h b/src/leveldb-lin/table/block.h new file mode 100644 index 0000000..2493eb9 --- /dev/null +++ b/src/leveldb-lin/table/block.h @@ -0,0 +1,44 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_BLOCK_H_ +#define STORAGE_LEVELDB_TABLE_BLOCK_H_ + +#include +#include +#include "leveldb/iterator.h" + +namespace leveldb { + +struct BlockContents; +class Comparator; + +class Block { + public: + // Initialize the block with the specified contents. + explicit Block(const BlockContents& contents); + + ~Block(); + + size_t size() const { return size_; } + Iterator* NewIterator(const Comparator* comparator); + + private: + uint32_t NumRestarts() const; + + const char* data_; + size_t size_; + uint32_t restart_offset_; // Offset in data_ of restart array + bool owned_; // Block owns data_[] + + // No copying allowed + Block(const Block&); + void operator=(const Block&); + + class Iter; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_BLOCK_H_ diff --git a/src/leveldb-lin/table/block.o b/src/leveldb-lin/table/block.o new file mode 100644 index 0000000..e024354 Binary files /dev/null and b/src/leveldb-lin/table/block.o differ diff --git a/src/leveldb-lin/table/block_builder.cc b/src/leveldb-lin/table/block_builder.cc new file mode 100644 index 0000000..db660cd --- /dev/null +++ b/src/leveldb-lin/table/block_builder.cc @@ -0,0 +1,109 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// BlockBuilder generates blocks where keys are prefix-compressed: +// +// When we store a key, we drop the prefix shared with the previous +// string. This helps reduce the space requirement significantly. +// Furthermore, once every K keys, we do not apply the prefix +// compression and store the entire key. We call this a "restart +// point". The tail end of the block stores the offsets of all of the +// restart points, and can be used to do a binary search when looking +// for a particular key. Values are stored as-is (without compression) +// immediately following the corresponding key. +// +// An entry for a particular key-value pair has the form: +// shared_bytes: varint32 +// unshared_bytes: varint32 +// value_length: varint32 +// key_delta: char[unshared_bytes] +// value: char[value_length] +// shared_bytes == 0 for restart points. +// +// The trailer of the block has the form: +// restarts: uint32[num_restarts] +// num_restarts: uint32 +// restarts[i] contains the offset within the block of the ith restart point. + +#include "table/block_builder.h" + +#include +#include +#include "leveldb/comparator.h" +#include "leveldb/table_builder.h" +#include "util/coding.h" + +namespace leveldb { + +BlockBuilder::BlockBuilder(const Options* options) + : options_(options), + restarts_(), + counter_(0), + finished_(false) { + assert(options->block_restart_interval >= 1); + restarts_.push_back(0); // First restart point is at offset 0 +} + +void BlockBuilder::Reset() { + buffer_.clear(); + restarts_.clear(); + restarts_.push_back(0); // First restart point is at offset 0 + counter_ = 0; + finished_ = false; + last_key_.clear(); +} + +size_t BlockBuilder::CurrentSizeEstimate() const { + return (buffer_.size() + // Raw data buffer + restarts_.size() * sizeof(uint32_t) + // Restart array + sizeof(uint32_t)); // Restart array length +} + +Slice BlockBuilder::Finish() { + // Append restart array + for (size_t i = 0; i < restarts_.size(); i++) { + PutFixed32(&buffer_, restarts_[i]); + } + PutFixed32(&buffer_, restarts_.size()); + finished_ = true; + return Slice(buffer_); +} + +void BlockBuilder::Add(const Slice& key, const Slice& value) { + Slice last_key_piece(last_key_); + assert(!finished_); + assert(counter_ <= options_->block_restart_interval); + assert(buffer_.empty() // No values yet? + || options_->comparator->Compare(key, last_key_piece) > 0); + size_t shared = 0; + if (counter_ < options_->block_restart_interval) { + // See how much sharing to do with previous string + const size_t min_length = std::min(last_key_piece.size(), key.size()); + while ((shared < min_length) && (last_key_piece[shared] == key[shared])) { + shared++; + } + } else { + // Restart compression + restarts_.push_back(buffer_.size()); + counter_ = 0; + } + const size_t non_shared = key.size() - shared; + + // Add "" to buffer_ + PutVarint32(&buffer_, shared); + PutVarint32(&buffer_, non_shared); + PutVarint32(&buffer_, value.size()); + + // Add string delta to buffer_ followed by value + buffer_.append(key.data() + shared, non_shared); + buffer_.append(value.data(), value.size()); + + // Update state + last_key_.resize(shared); + last_key_.append(key.data() + shared, non_shared); + assert(Slice(last_key_) == key); + counter_++; +} + +} // namespace leveldb diff --git a/src/leveldb-lin/table/block_builder.h b/src/leveldb-lin/table/block_builder.h new file mode 100644 index 0000000..5b545bd --- /dev/null +++ b/src/leveldb-lin/table/block_builder.h @@ -0,0 +1,57 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_ +#define STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_ + +#include + +#include +#include "leveldb/slice.h" + +namespace leveldb { + +struct Options; + +class BlockBuilder { + public: + explicit BlockBuilder(const Options* options); + + // Reset the contents as if the BlockBuilder was just constructed. + void Reset(); + + // REQUIRES: Finish() has not been callled since the last call to Reset(). + // REQUIRES: key is larger than any previously added key + void Add(const Slice& key, const Slice& value); + + // Finish building the block and return a slice that refers to the + // block contents. The returned slice will remain valid for the + // lifetime of this builder or until Reset() is called. + Slice Finish(); + + // Returns an estimate of the current (uncompressed) size of the block + // we are building. + size_t CurrentSizeEstimate() const; + + // Return true iff no entries have been added since the last Reset() + bool empty() const { + return buffer_.empty(); + } + + private: + const Options* options_; + std::string buffer_; // Destination buffer + std::vector restarts_; // Restart points + int counter_; // Number of entries emitted since restart + bool finished_; // Has Finish() been called? + std::string last_key_; + + // No copying allowed + BlockBuilder(const BlockBuilder&); + void operator=(const BlockBuilder&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_ diff --git a/src/leveldb-lin/table/block_builder.o b/src/leveldb-lin/table/block_builder.o new file mode 100644 index 0000000..c2ead87 Binary files /dev/null and b/src/leveldb-lin/table/block_builder.o differ diff --git a/src/leveldb-lin/table/filter_block.cc b/src/leveldb-lin/table/filter_block.cc new file mode 100644 index 0000000..203e15c --- /dev/null +++ b/src/leveldb-lin/table/filter_block.cc @@ -0,0 +1,111 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/filter_block.h" + +#include "leveldb/filter_policy.h" +#include "util/coding.h" + +namespace leveldb { + +// See doc/table_format.txt for an explanation of the filter block format. + +// Generate new filter every 2KB of data +static const size_t kFilterBaseLg = 11; +static const size_t kFilterBase = 1 << kFilterBaseLg; + +FilterBlockBuilder::FilterBlockBuilder(const FilterPolicy* policy) + : policy_(policy) { +} + +void FilterBlockBuilder::StartBlock(uint64_t block_offset) { + uint64_t filter_index = (block_offset / kFilterBase); + assert(filter_index >= filter_offsets_.size()); + while (filter_index > filter_offsets_.size()) { + GenerateFilter(); + } +} + +void FilterBlockBuilder::AddKey(const Slice& key) { + Slice k = key; + start_.push_back(keys_.size()); + keys_.append(k.data(), k.size()); +} + +Slice FilterBlockBuilder::Finish() { + if (!start_.empty()) { + GenerateFilter(); + } + + // Append array of per-filter offsets + const uint32_t array_offset = result_.size(); + for (size_t i = 0; i < filter_offsets_.size(); i++) { + PutFixed32(&result_, filter_offsets_[i]); + } + + PutFixed32(&result_, array_offset); + result_.push_back(kFilterBaseLg); // Save encoding parameter in result + return Slice(result_); +} + +void FilterBlockBuilder::GenerateFilter() { + const size_t num_keys = start_.size(); + if (num_keys == 0) { + // Fast path if there are no keys for this filter + filter_offsets_.push_back(result_.size()); + return; + } + + // Make list of keys from flattened key structure + start_.push_back(keys_.size()); // Simplify length computation + tmp_keys_.resize(num_keys); + for (size_t i = 0; i < num_keys; i++) { + const char* base = keys_.data() + start_[i]; + size_t length = start_[i+1] - start_[i]; + tmp_keys_[i] = Slice(base, length); + } + + // Generate filter for current set of keys and append to result_. + filter_offsets_.push_back(result_.size()); + policy_->CreateFilter(&tmp_keys_[0], num_keys, &result_); + + tmp_keys_.clear(); + keys_.clear(); + start_.clear(); +} + +FilterBlockReader::FilterBlockReader(const FilterPolicy* policy, + const Slice& contents) + : policy_(policy), + data_(NULL), + offset_(NULL), + num_(0), + base_lg_(0) { + size_t n = contents.size(); + if (n < 5) return; // 1 byte for base_lg_ and 4 for start of offset array + base_lg_ = contents[n-1]; + uint32_t last_word = DecodeFixed32(contents.data() + n - 5); + if (last_word > n - 5) return; + data_ = contents.data(); + offset_ = data_ + last_word; + num_ = (n - 5 - last_word) / 4; +} + +bool FilterBlockReader::KeyMayMatch(uint64_t block_offset, const Slice& key) { + uint64_t index = block_offset >> base_lg_; + if (index < num_) { + uint32_t start = DecodeFixed32(offset_ + index*4); + uint32_t limit = DecodeFixed32(offset_ + index*4 + 4); + if (start <= limit && limit <= (offset_ - data_)) { + Slice filter = Slice(data_ + start, limit - start); + return policy_->KeyMayMatch(key, filter); + } else if (start == limit) { + // Empty filters do not match any keys + return false; + } + } + return true; // Errors are treated as potential matches +} + +} diff --git a/src/leveldb-lin/table/filter_block.h b/src/leveldb-lin/table/filter_block.h new file mode 100644 index 0000000..c67d010 --- /dev/null +++ b/src/leveldb-lin/table/filter_block.h @@ -0,0 +1,68 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A filter block is stored near the end of a Table file. It contains +// filters (e.g., bloom filters) for all data blocks in the table combined +// into a single filter block. + +#ifndef STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_ +#define STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_ + +#include +#include +#include +#include +#include "leveldb/slice.h" +#include "util/hash.h" + +namespace leveldb { + +class FilterPolicy; + +// A FilterBlockBuilder is used to construct all of the filters for a +// particular Table. It generates a single string which is stored as +// a special block in the Table. +// +// The sequence of calls to FilterBlockBuilder must match the regexp: +// (StartBlock AddKey*)* Finish +class FilterBlockBuilder { + public: + explicit FilterBlockBuilder(const FilterPolicy*); + + void StartBlock(uint64_t block_offset); + void AddKey(const Slice& key); + Slice Finish(); + + private: + void GenerateFilter(); + + const FilterPolicy* policy_; + std::string keys_; // Flattened key contents + std::vector start_; // Starting index in keys_ of each key + std::string result_; // Filter data computed so far + std::vector tmp_keys_; // policy_->CreateFilter() argument + std::vector filter_offsets_; + + // No copying allowed + FilterBlockBuilder(const FilterBlockBuilder&); + void operator=(const FilterBlockBuilder&); +}; + +class FilterBlockReader { + public: + // REQUIRES: "contents" and *policy must stay live while *this is live. + FilterBlockReader(const FilterPolicy* policy, const Slice& contents); + bool KeyMayMatch(uint64_t block_offset, const Slice& key); + + private: + const FilterPolicy* policy_; + const char* data_; // Pointer to filter data (at block-start) + const char* offset_; // Pointer to beginning of offset array (at block-end) + size_t num_; // Number of entries in offset array + size_t base_lg_; // Encoding parameter (see kFilterBaseLg in .cc file) +}; + +} + +#endif // STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_ diff --git a/src/leveldb-lin/table/filter_block.o b/src/leveldb-lin/table/filter_block.o new file mode 100644 index 0000000..21465b8 Binary files /dev/null and b/src/leveldb-lin/table/filter_block.o differ diff --git a/src/leveldb-lin/table/filter_block_test.cc b/src/leveldb-lin/table/filter_block_test.cc new file mode 100644 index 0000000..3a2a07c --- /dev/null +++ b/src/leveldb-lin/table/filter_block_test.cc @@ -0,0 +1,128 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/filter_block.h" + +#include "leveldb/filter_policy.h" +#include "util/coding.h" +#include "util/hash.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +// For testing: emit an array with one hash value per key +class TestHashFilter : public FilterPolicy { + public: + virtual const char* Name() const { + return "TestHashFilter"; + } + + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + for (int i = 0; i < n; i++) { + uint32_t h = Hash(keys[i].data(), keys[i].size(), 1); + PutFixed32(dst, h); + } + } + + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const { + uint32_t h = Hash(key.data(), key.size(), 1); + for (int i = 0; i + 4 <= filter.size(); i += 4) { + if (h == DecodeFixed32(filter.data() + i)) { + return true; + } + } + return false; + } +}; + +class FilterBlockTest { + public: + TestHashFilter policy_; +}; + +TEST(FilterBlockTest, EmptyBuilder) { + FilterBlockBuilder builder(&policy_); + Slice block = builder.Finish(); + ASSERT_EQ("\\x00\\x00\\x00\\x00\\x0b", EscapeString(block)); + FilterBlockReader reader(&policy_, block); + ASSERT_TRUE(reader.KeyMayMatch(0, "foo")); + ASSERT_TRUE(reader.KeyMayMatch(100000, "foo")); +} + +TEST(FilterBlockTest, SingleChunk) { + FilterBlockBuilder builder(&policy_); + builder.StartBlock(100); + builder.AddKey("foo"); + builder.AddKey("bar"); + builder.AddKey("box"); + builder.StartBlock(200); + builder.AddKey("box"); + builder.StartBlock(300); + builder.AddKey("hello"); + Slice block = builder.Finish(); + FilterBlockReader reader(&policy_, block); + ASSERT_TRUE(reader.KeyMayMatch(100, "foo")); + ASSERT_TRUE(reader.KeyMayMatch(100, "bar")); + ASSERT_TRUE(reader.KeyMayMatch(100, "box")); + ASSERT_TRUE(reader.KeyMayMatch(100, "hello")); + ASSERT_TRUE(reader.KeyMayMatch(100, "foo")); + ASSERT_TRUE(! reader.KeyMayMatch(100, "missing")); + ASSERT_TRUE(! reader.KeyMayMatch(100, "other")); +} + +TEST(FilterBlockTest, MultiChunk) { + FilterBlockBuilder builder(&policy_); + + // First filter + builder.StartBlock(0); + builder.AddKey("foo"); + builder.StartBlock(2000); + builder.AddKey("bar"); + + // Second filter + builder.StartBlock(3100); + builder.AddKey("box"); + + // Third filter is empty + + // Last filter + builder.StartBlock(9000); + builder.AddKey("box"); + builder.AddKey("hello"); + + Slice block = builder.Finish(); + FilterBlockReader reader(&policy_, block); + + // Check first filter + ASSERT_TRUE(reader.KeyMayMatch(0, "foo")); + ASSERT_TRUE(reader.KeyMayMatch(2000, "bar")); + ASSERT_TRUE(! reader.KeyMayMatch(0, "box")); + ASSERT_TRUE(! reader.KeyMayMatch(0, "hello")); + + // Check second filter + ASSERT_TRUE(reader.KeyMayMatch(3100, "box")); + ASSERT_TRUE(! reader.KeyMayMatch(3100, "foo")); + ASSERT_TRUE(! reader.KeyMayMatch(3100, "bar")); + ASSERT_TRUE(! reader.KeyMayMatch(3100, "hello")); + + // Check third filter (empty) + ASSERT_TRUE(! reader.KeyMayMatch(4100, "foo")); + ASSERT_TRUE(! reader.KeyMayMatch(4100, "bar")); + ASSERT_TRUE(! reader.KeyMayMatch(4100, "box")); + ASSERT_TRUE(! reader.KeyMayMatch(4100, "hello")); + + // Check last filter + ASSERT_TRUE(reader.KeyMayMatch(9000, "box")); + ASSERT_TRUE(reader.KeyMayMatch(9000, "hello")); + ASSERT_TRUE(! reader.KeyMayMatch(9000, "foo")); + ASSERT_TRUE(! reader.KeyMayMatch(9000, "bar")); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/table/format.cc b/src/leveldb-lin/table/format.cc new file mode 100644 index 0000000..cda1dec --- /dev/null +++ b/src/leveldb-lin/table/format.cc @@ -0,0 +1,145 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/format.h" + +#include "leveldb/env.h" +#include "port/port.h" +#include "table/block.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { + +void BlockHandle::EncodeTo(std::string* dst) const { + // Sanity check that all fields have been set + assert(offset_ != ~static_cast(0)); + assert(size_ != ~static_cast(0)); + PutVarint64(dst, offset_); + PutVarint64(dst, size_); +} + +Status BlockHandle::DecodeFrom(Slice* input) { + if (GetVarint64(input, &offset_) && + GetVarint64(input, &size_)) { + return Status::OK(); + } else { + return Status::Corruption("bad block handle"); + } +} + +void Footer::EncodeTo(std::string* dst) const { +#ifndef NDEBUG + const size_t original_size = dst->size(); +#endif + metaindex_handle_.EncodeTo(dst); + index_handle_.EncodeTo(dst); + dst->resize(2 * BlockHandle::kMaxEncodedLength); // Padding + PutFixed32(dst, static_cast(kTableMagicNumber & 0xffffffffu)); + PutFixed32(dst, static_cast(kTableMagicNumber >> 32)); + assert(dst->size() == original_size + kEncodedLength); +} + +Status Footer::DecodeFrom(Slice* input) { + const char* magic_ptr = input->data() + kEncodedLength - 8; + const uint32_t magic_lo = DecodeFixed32(magic_ptr); + const uint32_t magic_hi = DecodeFixed32(magic_ptr + 4); + const uint64_t magic = ((static_cast(magic_hi) << 32) | + (static_cast(magic_lo))); + if (magic != kTableMagicNumber) { + return Status::InvalidArgument("not an sstable (bad magic number)"); + } + + Status result = metaindex_handle_.DecodeFrom(input); + if (result.ok()) { + result = index_handle_.DecodeFrom(input); + } + if (result.ok()) { + // We skip over any leftover data (just padding for now) in "input" + const char* end = magic_ptr + 8; + *input = Slice(end, input->data() + input->size() - end); + } + return result; +} + +Status ReadBlock(RandomAccessFile* file, + const ReadOptions& options, + const BlockHandle& handle, + BlockContents* result) { + result->data = Slice(); + result->cachable = false; + result->heap_allocated = false; + + // Read the block contents as well as the type/crc footer. + // See table_builder.cc for the code that built this structure. + size_t n = static_cast(handle.size()); + char* buf = new char[n + kBlockTrailerSize]; + Slice contents; + Status s = file->Read(handle.offset(), n + kBlockTrailerSize, &contents, buf); + if (!s.ok()) { + delete[] buf; + return s; + } + if (contents.size() != n + kBlockTrailerSize) { + delete[] buf; + return Status::Corruption("truncated block read"); + } + + // Check the crc of the type and the block contents + const char* data = contents.data(); // Pointer to where Read put the data + if (options.verify_checksums) { + const uint32_t crc = crc32c::Unmask(DecodeFixed32(data + n + 1)); + const uint32_t actual = crc32c::Value(data, n + 1); + if (actual != crc) { + delete[] buf; + s = Status::Corruption("block checksum mismatch"); + return s; + } + } + + switch (data[n]) { + case kNoCompression: + if (data != buf) { + // File implementation gave us pointer to some other data. + // Use it directly under the assumption that it will be live + // while the file is open. + delete[] buf; + result->data = Slice(data, n); + result->heap_allocated = false; + result->cachable = false; // Do not double-cache + } else { + result->data = Slice(buf, n); + result->heap_allocated = true; + result->cachable = true; + } + + // Ok + break; + case kSnappyCompression: { + size_t ulength = 0; + if (!port::Snappy_GetUncompressedLength(data, n, &ulength)) { + delete[] buf; + return Status::Corruption("corrupted compressed block contents"); + } + char* ubuf = new char[ulength]; + if (!port::Snappy_Uncompress(data, n, ubuf)) { + delete[] buf; + delete[] ubuf; + return Status::Corruption("corrupted compressed block contents"); + } + delete[] buf; + result->data = Slice(ubuf, ulength); + result->heap_allocated = true; + result->cachable = true; + break; + } + default: + delete[] buf; + return Status::Corruption("bad block type"); + } + + return Status::OK(); +} + +} // namespace leveldb diff --git a/src/leveldb-lin/table/format.h b/src/leveldb-lin/table/format.h new file mode 100644 index 0000000..6c0b80c --- /dev/null +++ b/src/leveldb-lin/table/format.h @@ -0,0 +1,108 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_FORMAT_H_ +#define STORAGE_LEVELDB_TABLE_FORMAT_H_ + +#include +#include +#include "leveldb/slice.h" +#include "leveldb/status.h" +#include "leveldb/table_builder.h" + +namespace leveldb { + +class Block; +class RandomAccessFile; +struct ReadOptions; + +// BlockHandle is a pointer to the extent of a file that stores a data +// block or a meta block. +class BlockHandle { + public: + BlockHandle(); + + // The offset of the block in the file. + uint64_t offset() const { return offset_; } + void set_offset(uint64_t offset) { offset_ = offset; } + + // The size of the stored block + uint64_t size() const { return size_; } + void set_size(uint64_t size) { size_ = size; } + + void EncodeTo(std::string* dst) const; + Status DecodeFrom(Slice* input); + + // Maximum encoding length of a BlockHandle + enum { kMaxEncodedLength = 10 + 10 }; + + private: + uint64_t offset_; + uint64_t size_; +}; + +// Footer encapsulates the fixed information stored at the tail +// end of every table file. +class Footer { + public: + Footer() { } + + // The block handle for the metaindex block of the table + const BlockHandle& metaindex_handle() const { return metaindex_handle_; } + void set_metaindex_handle(const BlockHandle& h) { metaindex_handle_ = h; } + + // The block handle for the index block of the table + const BlockHandle& index_handle() const { + return index_handle_; + } + void set_index_handle(const BlockHandle& h) { + index_handle_ = h; + } + + void EncodeTo(std::string* dst) const; + Status DecodeFrom(Slice* input); + + // Encoded length of a Footer. Note that the serialization of a + // Footer will always occupy exactly this many bytes. It consists + // of two block handles and a magic number. + enum { + kEncodedLength = 2*BlockHandle::kMaxEncodedLength + 8 + }; + + private: + BlockHandle metaindex_handle_; + BlockHandle index_handle_; +}; + +// kTableMagicNumber was picked by running +// echo http://code.google.com/p/leveldb/ | sha1sum +// and taking the leading 64 bits. +static const uint64_t kTableMagicNumber = 0xdb4775248b80fb57ull; + +// 1-byte type + 32-bit crc +static const size_t kBlockTrailerSize = 5; + +struct BlockContents { + Slice data; // Actual contents of data + bool cachable; // True iff data can be cached + bool heap_allocated; // True iff caller should delete[] data.data() +}; + +// Read the block identified by "handle" from "file". On failure +// return non-OK. On success fill *result and return OK. +extern Status ReadBlock(RandomAccessFile* file, + const ReadOptions& options, + const BlockHandle& handle, + BlockContents* result); + +// Implementation details follow. Clients should ignore, + +inline BlockHandle::BlockHandle() + : offset_(~static_cast(0)), + size_(~static_cast(0)) { +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_FORMAT_H_ diff --git a/src/leveldb-lin/table/format.o b/src/leveldb-lin/table/format.o new file mode 100644 index 0000000..a6a782f Binary files /dev/null and b/src/leveldb-lin/table/format.o differ diff --git a/src/leveldb-lin/table/iterator.cc b/src/leveldb-lin/table/iterator.cc new file mode 100644 index 0000000..3d1c87f --- /dev/null +++ b/src/leveldb-lin/table/iterator.cc @@ -0,0 +1,67 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/iterator.h" + +namespace leveldb { + +Iterator::Iterator() { + cleanup_.function = NULL; + cleanup_.next = NULL; +} + +Iterator::~Iterator() { + if (cleanup_.function != NULL) { + (*cleanup_.function)(cleanup_.arg1, cleanup_.arg2); + for (Cleanup* c = cleanup_.next; c != NULL; ) { + (*c->function)(c->arg1, c->arg2); + Cleanup* next = c->next; + delete c; + c = next; + } + } +} + +void Iterator::RegisterCleanup(CleanupFunction func, void* arg1, void* arg2) { + assert(func != NULL); + Cleanup* c; + if (cleanup_.function == NULL) { + c = &cleanup_; + } else { + c = new Cleanup; + c->next = cleanup_.next; + cleanup_.next = c; + } + c->function = func; + c->arg1 = arg1; + c->arg2 = arg2; +} + +namespace { +class EmptyIterator : public Iterator { + public: + EmptyIterator(const Status& s) : status_(s) { } + virtual bool Valid() const { return false; } + virtual void Seek(const Slice& target) { } + virtual void SeekToFirst() { } + virtual void SeekToLast() { } + virtual void Next() { assert(false); } + virtual void Prev() { assert(false); } + Slice key() const { assert(false); return Slice(); } + Slice value() const { assert(false); return Slice(); } + virtual Status status() const { return status_; } + private: + Status status_; +}; +} // namespace + +Iterator* NewEmptyIterator() { + return new EmptyIterator(Status::OK()); +} + +Iterator* NewErrorIterator(const Status& status) { + return new EmptyIterator(status); +} + +} // namespace leveldb diff --git a/src/leveldb-lin/table/iterator.o b/src/leveldb-lin/table/iterator.o new file mode 100644 index 0000000..712bb7a Binary files /dev/null and b/src/leveldb-lin/table/iterator.o differ diff --git a/src/leveldb-lin/table/iterator_wrapper.h b/src/leveldb-lin/table/iterator_wrapper.h new file mode 100644 index 0000000..9e16b3d --- /dev/null +++ b/src/leveldb-lin/table/iterator_wrapper.h @@ -0,0 +1,63 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_ +#define STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_ + +namespace leveldb { + +// A internal wrapper class with an interface similar to Iterator that +// caches the valid() and key() results for an underlying iterator. +// This can help avoid virtual function calls and also gives better +// cache locality. +class IteratorWrapper { + public: + IteratorWrapper(): iter_(NULL), valid_(false) { } + explicit IteratorWrapper(Iterator* iter): iter_(NULL) { + Set(iter); + } + ~IteratorWrapper() { delete iter_; } + Iterator* iter() const { return iter_; } + + // Takes ownership of "iter" and will delete it when destroyed, or + // when Set() is invoked again. + void Set(Iterator* iter) { + delete iter_; + iter_ = iter; + if (iter_ == NULL) { + valid_ = false; + } else { + Update(); + } + } + + + // Iterator interface methods + bool Valid() const { return valid_; } + Slice key() const { assert(Valid()); return key_; } + Slice value() const { assert(Valid()); return iter_->value(); } + // Methods below require iter() != NULL + Status status() const { assert(iter_); return iter_->status(); } + void Next() { assert(iter_); iter_->Next(); Update(); } + void Prev() { assert(iter_); iter_->Prev(); Update(); } + void Seek(const Slice& k) { assert(iter_); iter_->Seek(k); Update(); } + void SeekToFirst() { assert(iter_); iter_->SeekToFirst(); Update(); } + void SeekToLast() { assert(iter_); iter_->SeekToLast(); Update(); } + + private: + void Update() { + valid_ = iter_->Valid(); + if (valid_) { + key_ = iter_->key(); + } + } + + Iterator* iter_; + bool valid_; + Slice key_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_ diff --git a/src/leveldb-lin/table/merger.cc b/src/leveldb-lin/table/merger.cc new file mode 100644 index 0000000..2dde4dc --- /dev/null +++ b/src/leveldb-lin/table/merger.cc @@ -0,0 +1,197 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/merger.h" + +#include "leveldb/comparator.h" +#include "leveldb/iterator.h" +#include "table/iterator_wrapper.h" + +namespace leveldb { + +namespace { +class MergingIterator : public Iterator { + public: + MergingIterator(const Comparator* comparator, Iterator** children, int n) + : comparator_(comparator), + children_(new IteratorWrapper[n]), + n_(n), + current_(NULL), + direction_(kForward) { + for (int i = 0; i < n; i++) { + children_[i].Set(children[i]); + } + } + + virtual ~MergingIterator() { + delete[] children_; + } + + virtual bool Valid() const { + return (current_ != NULL); + } + + virtual void SeekToFirst() { + for (int i = 0; i < n_; i++) { + children_[i].SeekToFirst(); + } + FindSmallest(); + direction_ = kForward; + } + + virtual void SeekToLast() { + for (int i = 0; i < n_; i++) { + children_[i].SeekToLast(); + } + FindLargest(); + direction_ = kReverse; + } + + virtual void Seek(const Slice& target) { + for (int i = 0; i < n_; i++) { + children_[i].Seek(target); + } + FindSmallest(); + direction_ = kForward; + } + + virtual void Next() { + assert(Valid()); + + // Ensure that all children are positioned after key(). + // If we are moving in the forward direction, it is already + // true for all of the non-current_ children since current_ is + // the smallest child and key() == current_->key(). Otherwise, + // we explicitly position the non-current_ children. + if (direction_ != kForward) { + for (int i = 0; i < n_; i++) { + IteratorWrapper* child = &children_[i]; + if (child != current_) { + child->Seek(key()); + if (child->Valid() && + comparator_->Compare(key(), child->key()) == 0) { + child->Next(); + } + } + } + direction_ = kForward; + } + + current_->Next(); + FindSmallest(); + } + + virtual void Prev() { + assert(Valid()); + + // Ensure that all children are positioned before key(). + // If we are moving in the reverse direction, it is already + // true for all of the non-current_ children since current_ is + // the largest child and key() == current_->key(). Otherwise, + // we explicitly position the non-current_ children. + if (direction_ != kReverse) { + for (int i = 0; i < n_; i++) { + IteratorWrapper* child = &children_[i]; + if (child != current_) { + child->Seek(key()); + if (child->Valid()) { + // Child is at first entry >= key(). Step back one to be < key() + child->Prev(); + } else { + // Child has no entries >= key(). Position at last entry. + child->SeekToLast(); + } + } + } + direction_ = kReverse; + } + + current_->Prev(); + FindLargest(); + } + + virtual Slice key() const { + assert(Valid()); + return current_->key(); + } + + virtual Slice value() const { + assert(Valid()); + return current_->value(); + } + + virtual Status status() const { + Status status; + for (int i = 0; i < n_; i++) { + status = children_[i].status(); + if (!status.ok()) { + break; + } + } + return status; + } + + private: + void FindSmallest(); + void FindLargest(); + + // We might want to use a heap in case there are lots of children. + // For now we use a simple array since we expect a very small number + // of children in leveldb. + const Comparator* comparator_; + IteratorWrapper* children_; + int n_; + IteratorWrapper* current_; + + // Which direction is the iterator moving? + enum Direction { + kForward, + kReverse + }; + Direction direction_; +}; + +void MergingIterator::FindSmallest() { + IteratorWrapper* smallest = NULL; + for (int i = 0; i < n_; i++) { + IteratorWrapper* child = &children_[i]; + if (child->Valid()) { + if (smallest == NULL) { + smallest = child; + } else if (comparator_->Compare(child->key(), smallest->key()) < 0) { + smallest = child; + } + } + } + current_ = smallest; +} + +void MergingIterator::FindLargest() { + IteratorWrapper* largest = NULL; + for (int i = n_-1; i >= 0; i--) { + IteratorWrapper* child = &children_[i]; + if (child->Valid()) { + if (largest == NULL) { + largest = child; + } else if (comparator_->Compare(child->key(), largest->key()) > 0) { + largest = child; + } + } + } + current_ = largest; +} +} // namespace + +Iterator* NewMergingIterator(const Comparator* cmp, Iterator** list, int n) { + assert(n >= 0); + if (n == 0) { + return NewEmptyIterator(); + } else if (n == 1) { + return list[0]; + } else { + return new MergingIterator(cmp, list, n); + } +} + +} // namespace leveldb diff --git a/src/leveldb-lin/table/merger.h b/src/leveldb-lin/table/merger.h new file mode 100644 index 0000000..91ddd80 --- /dev/null +++ b/src/leveldb-lin/table/merger.h @@ -0,0 +1,26 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_MERGER_H_ +#define STORAGE_LEVELDB_TABLE_MERGER_H_ + +namespace leveldb { + +class Comparator; +class Iterator; + +// Return an iterator that provided the union of the data in +// children[0,n-1]. Takes ownership of the child iterators and +// will delete them when the result iterator is deleted. +// +// The result does no duplicate suppression. I.e., if a particular +// key is present in K child iterators, it will be yielded K times. +// +// REQUIRES: n >= 0 +extern Iterator* NewMergingIterator( + const Comparator* comparator, Iterator** children, int n); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_MERGER_H_ diff --git a/src/leveldb-lin/table/merger.o b/src/leveldb-lin/table/merger.o new file mode 100644 index 0000000..87c4db1 Binary files /dev/null and b/src/leveldb-lin/table/merger.o differ diff --git a/src/leveldb-lin/table/table.cc b/src/leveldb-lin/table/table.cc new file mode 100644 index 0000000..dbd6d3a --- /dev/null +++ b/src/leveldb-lin/table/table.cc @@ -0,0 +1,276 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/table.h" + +#include "leveldb/cache.h" +#include "leveldb/comparator.h" +#include "leveldb/env.h" +#include "leveldb/filter_policy.h" +#include "leveldb/options.h" +#include "table/block.h" +#include "table/filter_block.h" +#include "table/format.h" +#include "table/two_level_iterator.h" +#include "util/coding.h" + +namespace leveldb { + +struct Table::Rep { + ~Rep() { + delete filter; + delete [] filter_data; + delete index_block; + } + + Options options; + Status status; + RandomAccessFile* file; + uint64_t cache_id; + FilterBlockReader* filter; + const char* filter_data; + + BlockHandle metaindex_handle; // Handle to metaindex_block: saved from footer + Block* index_block; +}; + +Status Table::Open(const Options& options, + RandomAccessFile* file, + uint64_t size, + Table** table) { + *table = NULL; + if (size < Footer::kEncodedLength) { + return Status::InvalidArgument("file is too short to be an sstable"); + } + + char footer_space[Footer::kEncodedLength]; + Slice footer_input; + Status s = file->Read(size - Footer::kEncodedLength, Footer::kEncodedLength, + &footer_input, footer_space); + if (!s.ok()) return s; + + Footer footer; + s = footer.DecodeFrom(&footer_input); + if (!s.ok()) return s; + + // Read the index block + BlockContents contents; + Block* index_block = NULL; + if (s.ok()) { + s = ReadBlock(file, ReadOptions(), footer.index_handle(), &contents); + if (s.ok()) { + index_block = new Block(contents); + } + } + + if (s.ok()) { + // We've successfully read the footer and the index block: we're + // ready to serve requests. + Rep* rep = new Table::Rep; + rep->options = options; + rep->file = file; + rep->metaindex_handle = footer.metaindex_handle(); + rep->index_block = index_block; + rep->cache_id = (options.block_cache ? options.block_cache->NewId() : 0); + rep->filter_data = NULL; + rep->filter = NULL; + *table = new Table(rep); + (*table)->ReadMeta(footer); + } else { + if (index_block) delete index_block; + } + + return s; +} + +void Table::ReadMeta(const Footer& footer) { + if (rep_->options.filter_policy == NULL) { + return; // Do not need any metadata + } + + // TODO(sanjay): Skip this if footer.metaindex_handle() size indicates + // it is an empty block. + ReadOptions opt; + BlockContents contents; + if (!ReadBlock(rep_->file, opt, footer.metaindex_handle(), &contents).ok()) { + // Do not propagate errors since meta info is not needed for operation + return; + } + Block* meta = new Block(contents); + + Iterator* iter = meta->NewIterator(BytewiseComparator()); + std::string key = "filter."; + key.append(rep_->options.filter_policy->Name()); + iter->Seek(key); + if (iter->Valid() && iter->key() == Slice(key)) { + ReadFilter(iter->value()); + } + delete iter; + delete meta; +} + +void Table::ReadFilter(const Slice& filter_handle_value) { + Slice v = filter_handle_value; + BlockHandle filter_handle; + if (!filter_handle.DecodeFrom(&v).ok()) { + return; + } + + // We might want to unify with ReadBlock() if we start + // requiring checksum verification in Table::Open. + ReadOptions opt; + BlockContents block; + if (!ReadBlock(rep_->file, opt, filter_handle, &block).ok()) { + return; + } + if (block.heap_allocated) { + rep_->filter_data = block.data.data(); // Will need to delete later + } + rep_->filter = new FilterBlockReader(rep_->options.filter_policy, block.data); +} + +Table::~Table() { + delete rep_; +} + +static void DeleteBlock(void* arg, void* ignored) { + delete reinterpret_cast(arg); +} + +static void DeleteCachedBlock(const Slice& key, void* value) { + Block* block = reinterpret_cast(value); + delete block; +} + +static void ReleaseBlock(void* arg, void* h) { + Cache* cache = reinterpret_cast(arg); + Cache::Handle* handle = reinterpret_cast(h); + cache->Release(handle); +} + +// Convert an index iterator value (i.e., an encoded BlockHandle) +// into an iterator over the contents of the corresponding block. +Iterator* Table::BlockReader(void* arg, + const ReadOptions& options, + const Slice& index_value) { + Table* table = reinterpret_cast(arg); + Cache* block_cache = table->rep_->options.block_cache; + Block* block = NULL; + Cache::Handle* cache_handle = NULL; + + BlockHandle handle; + Slice input = index_value; + Status s = handle.DecodeFrom(&input); + // We intentionally allow extra stuff in index_value so that we + // can add more features in the future. + + if (s.ok()) { + BlockContents contents; + if (block_cache != NULL) { + char cache_key_buffer[16]; + EncodeFixed64(cache_key_buffer, table->rep_->cache_id); + EncodeFixed64(cache_key_buffer+8, handle.offset()); + Slice key(cache_key_buffer, sizeof(cache_key_buffer)); + cache_handle = block_cache->Lookup(key); + if (cache_handle != NULL) { + block = reinterpret_cast(block_cache->Value(cache_handle)); + } else { + s = ReadBlock(table->rep_->file, options, handle, &contents); + if (s.ok()) { + block = new Block(contents); + if (contents.cachable && options.fill_cache) { + cache_handle = block_cache->Insert( + key, block, block->size(), &DeleteCachedBlock); + } + } + } + } else { + s = ReadBlock(table->rep_->file, options, handle, &contents); + if (s.ok()) { + block = new Block(contents); + } + } + } + + Iterator* iter; + if (block != NULL) { + iter = block->NewIterator(table->rep_->options.comparator); + if (cache_handle == NULL) { + iter->RegisterCleanup(&DeleteBlock, block, NULL); + } else { + iter->RegisterCleanup(&ReleaseBlock, block_cache, cache_handle); + } + } else { + iter = NewErrorIterator(s); + } + return iter; +} + +Iterator* Table::NewIterator(const ReadOptions& options) const { + return NewTwoLevelIterator( + rep_->index_block->NewIterator(rep_->options.comparator), + &Table::BlockReader, const_cast(this), options); +} + +Status Table::InternalGet(const ReadOptions& options, const Slice& k, + void* arg, + void (*saver)(void*, const Slice&, const Slice&)) { + Status s; + Iterator* iiter = rep_->index_block->NewIterator(rep_->options.comparator); + iiter->Seek(k); + if (iiter->Valid()) { + Slice handle_value = iiter->value(); + FilterBlockReader* filter = rep_->filter; + BlockHandle handle; + if (filter != NULL && + handle.DecodeFrom(&handle_value).ok() && + !filter->KeyMayMatch(handle.offset(), k)) { + // Not found + } else { + Slice handle = iiter->value(); + Iterator* block_iter = BlockReader(this, options, iiter->value()); + block_iter->Seek(k); + if (block_iter->Valid()) { + (*saver)(arg, block_iter->key(), block_iter->value()); + } + s = block_iter->status(); + delete block_iter; + } + } + if (s.ok()) { + s = iiter->status(); + } + delete iiter; + return s; +} + + +uint64_t Table::ApproximateOffsetOf(const Slice& key) const { + Iterator* index_iter = + rep_->index_block->NewIterator(rep_->options.comparator); + index_iter->Seek(key); + uint64_t result; + if (index_iter->Valid()) { + BlockHandle handle; + Slice input = index_iter->value(); + Status s = handle.DecodeFrom(&input); + if (s.ok()) { + result = handle.offset(); + } else { + // Strange: we can't decode the block handle in the index block. + // We'll just return the offset of the metaindex block, which is + // close to the whole file size for this case. + result = rep_->metaindex_handle.offset(); + } + } else { + // key is past the last key in the file. Approximate the offset + // by returning the offset of the metaindex block (which is + // right near the end of the file). + result = rep_->metaindex_handle.offset(); + } + delete index_iter; + return result; +} + +} // namespace leveldb diff --git a/src/leveldb-lin/table/table.o b/src/leveldb-lin/table/table.o new file mode 100644 index 0000000..2855187 Binary files /dev/null and b/src/leveldb-lin/table/table.o differ diff --git a/src/leveldb-lin/table/table_builder.cc b/src/leveldb-lin/table/table_builder.cc new file mode 100644 index 0000000..62002c8 --- /dev/null +++ b/src/leveldb-lin/table/table_builder.cc @@ -0,0 +1,270 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/table_builder.h" + +#include +#include "leveldb/comparator.h" +#include "leveldb/env.h" +#include "leveldb/filter_policy.h" +#include "leveldb/options.h" +#include "table/block_builder.h" +#include "table/filter_block.h" +#include "table/format.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { + +struct TableBuilder::Rep { + Options options; + Options index_block_options; + WritableFile* file; + uint64_t offset; + Status status; + BlockBuilder data_block; + BlockBuilder index_block; + std::string last_key; + int64_t num_entries; + bool closed; // Either Finish() or Abandon() has been called. + FilterBlockBuilder* filter_block; + + // We do not emit the index entry for a block until we have seen the + // first key for the next data block. This allows us to use shorter + // keys in the index block. For example, consider a block boundary + // between the keys "the quick brown fox" and "the who". We can use + // "the r" as the key for the index block entry since it is >= all + // entries in the first block and < all entries in subsequent + // blocks. + // + // Invariant: r->pending_index_entry is true only if data_block is empty. + bool pending_index_entry; + BlockHandle pending_handle; // Handle to add to index block + + std::string compressed_output; + + Rep(const Options& opt, WritableFile* f) + : options(opt), + index_block_options(opt), + file(f), + offset(0), + data_block(&options), + index_block(&index_block_options), + num_entries(0), + closed(false), + filter_block(opt.filter_policy == NULL ? NULL + : new FilterBlockBuilder(opt.filter_policy)), + pending_index_entry(false) { + index_block_options.block_restart_interval = 1; + } +}; + +TableBuilder::TableBuilder(const Options& options, WritableFile* file) + : rep_(new Rep(options, file)) { + if (rep_->filter_block != NULL) { + rep_->filter_block->StartBlock(0); + } +} + +TableBuilder::~TableBuilder() { + assert(rep_->closed); // Catch errors where caller forgot to call Finish() + delete rep_->filter_block; + delete rep_; +} + +Status TableBuilder::ChangeOptions(const Options& options) { + // Note: if more fields are added to Options, update + // this function to catch changes that should not be allowed to + // change in the middle of building a Table. + if (options.comparator != rep_->options.comparator) { + return Status::InvalidArgument("changing comparator while building table"); + } + + // Note that any live BlockBuilders point to rep_->options and therefore + // will automatically pick up the updated options. + rep_->options = options; + rep_->index_block_options = options; + rep_->index_block_options.block_restart_interval = 1; + return Status::OK(); +} + +void TableBuilder::Add(const Slice& key, const Slice& value) { + Rep* r = rep_; + assert(!r->closed); + if (!ok()) return; + if (r->num_entries > 0) { + assert(r->options.comparator->Compare(key, Slice(r->last_key)) > 0); + } + + if (r->pending_index_entry) { + assert(r->data_block.empty()); + r->options.comparator->FindShortestSeparator(&r->last_key, key); + std::string handle_encoding; + r->pending_handle.EncodeTo(&handle_encoding); + r->index_block.Add(r->last_key, Slice(handle_encoding)); + r->pending_index_entry = false; + } + + if (r->filter_block != NULL) { + r->filter_block->AddKey(key); + } + + r->last_key.assign(key.data(), key.size()); + r->num_entries++; + r->data_block.Add(key, value); + + const size_t estimated_block_size = r->data_block.CurrentSizeEstimate(); + if (estimated_block_size >= r->options.block_size) { + Flush(); + } +} + +void TableBuilder::Flush() { + Rep* r = rep_; + assert(!r->closed); + if (!ok()) return; + if (r->data_block.empty()) return; + assert(!r->pending_index_entry); + WriteBlock(&r->data_block, &r->pending_handle); + if (ok()) { + r->pending_index_entry = true; + r->status = r->file->Flush(); + } + if (r->filter_block != NULL) { + r->filter_block->StartBlock(r->offset); + } +} + +void TableBuilder::WriteBlock(BlockBuilder* block, BlockHandle* handle) { + // File format contains a sequence of blocks where each block has: + // block_data: uint8[n] + // type: uint8 + // crc: uint32 + assert(ok()); + Rep* r = rep_; + Slice raw = block->Finish(); + + Slice block_contents; + CompressionType type = r->options.compression; + // TODO(postrelease): Support more compression options: zlib? + switch (type) { + case kNoCompression: + block_contents = raw; + break; + + case kSnappyCompression: { + std::string* compressed = &r->compressed_output; + if (port::Snappy_Compress(raw.data(), raw.size(), compressed) && + compressed->size() < raw.size() - (raw.size() / 8u)) { + block_contents = *compressed; + } else { + // Snappy not supported, or compressed less than 12.5%, so just + // store uncompressed form + block_contents = raw; + type = kNoCompression; + } + break; + } + } + WriteRawBlock(block_contents, type, handle); + r->compressed_output.clear(); + block->Reset(); +} + +void TableBuilder::WriteRawBlock(const Slice& block_contents, + CompressionType type, + BlockHandle* handle) { + Rep* r = rep_; + handle->set_offset(r->offset); + handle->set_size(block_contents.size()); + r->status = r->file->Append(block_contents); + if (r->status.ok()) { + char trailer[kBlockTrailerSize]; + trailer[0] = type; + uint32_t crc = crc32c::Value(block_contents.data(), block_contents.size()); + crc = crc32c::Extend(crc, trailer, 1); // Extend crc to cover block type + EncodeFixed32(trailer+1, crc32c::Mask(crc)); + r->status = r->file->Append(Slice(trailer, kBlockTrailerSize)); + if (r->status.ok()) { + r->offset += block_contents.size() + kBlockTrailerSize; + } + } +} + +Status TableBuilder::status() const { + return rep_->status; +} + +Status TableBuilder::Finish() { + Rep* r = rep_; + Flush(); + assert(!r->closed); + r->closed = true; + + BlockHandle filter_block_handle, metaindex_block_handle, index_block_handle; + + // Write filter block + if (ok() && r->filter_block != NULL) { + WriteRawBlock(r->filter_block->Finish(), kNoCompression, + &filter_block_handle); + } + + // Write metaindex block + if (ok()) { + BlockBuilder meta_index_block(&r->options); + if (r->filter_block != NULL) { + // Add mapping from "filter.Name" to location of filter data + std::string key = "filter."; + key.append(r->options.filter_policy->Name()); + std::string handle_encoding; + filter_block_handle.EncodeTo(&handle_encoding); + meta_index_block.Add(key, handle_encoding); + } + + // TODO(postrelease): Add stats and other meta blocks + WriteBlock(&meta_index_block, &metaindex_block_handle); + } + + // Write index block + if (ok()) { + if (r->pending_index_entry) { + r->options.comparator->FindShortSuccessor(&r->last_key); + std::string handle_encoding; + r->pending_handle.EncodeTo(&handle_encoding); + r->index_block.Add(r->last_key, Slice(handle_encoding)); + r->pending_index_entry = false; + } + WriteBlock(&r->index_block, &index_block_handle); + } + + // Write footer + if (ok()) { + Footer footer; + footer.set_metaindex_handle(metaindex_block_handle); + footer.set_index_handle(index_block_handle); + std::string footer_encoding; + footer.EncodeTo(&footer_encoding); + r->status = r->file->Append(footer_encoding); + if (r->status.ok()) { + r->offset += footer_encoding.size(); + } + } + return r->status; +} + +void TableBuilder::Abandon() { + Rep* r = rep_; + assert(!r->closed); + r->closed = true; +} + +uint64_t TableBuilder::NumEntries() const { + return rep_->num_entries; +} + +uint64_t TableBuilder::FileSize() const { + return rep_->offset; +} + +} // namespace leveldb diff --git a/src/leveldb-lin/table/table_builder.o b/src/leveldb-lin/table/table_builder.o new file mode 100644 index 0000000..dc291a7 Binary files /dev/null and b/src/leveldb-lin/table/table_builder.o differ diff --git a/src/leveldb-lin/table/table_test.cc b/src/leveldb-lin/table/table_test.cc new file mode 100644 index 0000000..57cea25 --- /dev/null +++ b/src/leveldb-lin/table/table_test.cc @@ -0,0 +1,838 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/table.h" + +#include +#include +#include "db/dbformat.h" +#include "db/memtable.h" +#include "db/write_batch_internal.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "leveldb/table_builder.h" +#include "table/block.h" +#include "table/block_builder.h" +#include "table/format.h" +#include "util/random.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +// Return reverse of "key". +// Used to test non-lexicographic comparators. +static std::string Reverse(const Slice& key) { + std::string str(key.ToString()); + std::string rev(""); + for (std::string::reverse_iterator rit = str.rbegin(); + rit != str.rend(); ++rit) { + rev.push_back(*rit); + } + return rev; +} + +namespace { +class ReverseKeyComparator : public Comparator { + public: + virtual const char* Name() const { + return "leveldb.ReverseBytewiseComparator"; + } + + virtual int Compare(const Slice& a, const Slice& b) const { + return BytewiseComparator()->Compare(Reverse(a), Reverse(b)); + } + + virtual void FindShortestSeparator( + std::string* start, + const Slice& limit) const { + std::string s = Reverse(*start); + std::string l = Reverse(limit); + BytewiseComparator()->FindShortestSeparator(&s, l); + *start = Reverse(s); + } + + virtual void FindShortSuccessor(std::string* key) const { + std::string s = Reverse(*key); + BytewiseComparator()->FindShortSuccessor(&s); + *key = Reverse(s); + } +}; +} // namespace +static ReverseKeyComparator reverse_key_comparator; + +static void Increment(const Comparator* cmp, std::string* key) { + if (cmp == BytewiseComparator()) { + key->push_back('\0'); + } else { + assert(cmp == &reverse_key_comparator); + std::string rev = Reverse(*key); + rev.push_back('\0'); + *key = Reverse(rev); + } +} + +// An STL comparator that uses a Comparator +namespace { +struct STLLessThan { + const Comparator* cmp; + + STLLessThan() : cmp(BytewiseComparator()) { } + STLLessThan(const Comparator* c) : cmp(c) { } + bool operator()(const std::string& a, const std::string& b) const { + return cmp->Compare(Slice(a), Slice(b)) < 0; + } +}; +} // namespace + +class StringSink: public WritableFile { + public: + ~StringSink() { } + + const std::string& contents() const { return contents_; } + + virtual Status Close() { return Status::OK(); } + virtual Status Flush() { return Status::OK(); } + virtual Status Sync() { return Status::OK(); } + + virtual Status Append(const Slice& data) { + contents_.append(data.data(), data.size()); + return Status::OK(); + } + + private: + std::string contents_; +}; + + +class StringSource: public RandomAccessFile { + public: + StringSource(const Slice& contents) + : contents_(contents.data(), contents.size()) { + } + + virtual ~StringSource() { } + + uint64_t Size() const { return contents_.size(); } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + if (offset > contents_.size()) { + return Status::InvalidArgument("invalid Read offset"); + } + if (offset + n > contents_.size()) { + n = contents_.size() - offset; + } + memcpy(scratch, &contents_[offset], n); + *result = Slice(scratch, n); + return Status::OK(); + } + + private: + std::string contents_; +}; + +typedef std::map KVMap; + +// Helper class for tests to unify the interface between +// BlockBuilder/TableBuilder and Block/Table. +class Constructor { + public: + explicit Constructor(const Comparator* cmp) : data_(STLLessThan(cmp)) { } + virtual ~Constructor() { } + + void Add(const std::string& key, const Slice& value) { + data_[key] = value.ToString(); + } + + // Finish constructing the data structure with all the keys that have + // been added so far. Returns the keys in sorted order in "*keys" + // and stores the key/value pairs in "*kvmap" + void Finish(const Options& options, + std::vector* keys, + KVMap* kvmap) { + *kvmap = data_; + keys->clear(); + for (KVMap::const_iterator it = data_.begin(); + it != data_.end(); + ++it) { + keys->push_back(it->first); + } + data_.clear(); + Status s = FinishImpl(options, *kvmap); + ASSERT_TRUE(s.ok()) << s.ToString(); + } + + // Construct the data structure from the data in "data" + virtual Status FinishImpl(const Options& options, const KVMap& data) = 0; + + virtual Iterator* NewIterator() const = 0; + + virtual const KVMap& data() { return data_; } + + virtual DB* db() const { return NULL; } // Overridden in DBConstructor + + private: + KVMap data_; +}; + +class BlockConstructor: public Constructor { + public: + explicit BlockConstructor(const Comparator* cmp) + : Constructor(cmp), + comparator_(cmp), + block_(NULL) { } + ~BlockConstructor() { + delete block_; + } + virtual Status FinishImpl(const Options& options, const KVMap& data) { + delete block_; + block_ = NULL; + BlockBuilder builder(&options); + + for (KVMap::const_iterator it = data.begin(); + it != data.end(); + ++it) { + builder.Add(it->first, it->second); + } + // Open the block + data_ = builder.Finish().ToString(); + BlockContents contents; + contents.data = data_; + contents.cachable = false; + contents.heap_allocated = false; + block_ = new Block(contents); + return Status::OK(); + } + virtual Iterator* NewIterator() const { + return block_->NewIterator(comparator_); + } + + private: + const Comparator* comparator_; + std::string data_; + Block* block_; + + BlockConstructor(); +}; + +class TableConstructor: public Constructor { + public: + TableConstructor(const Comparator* cmp) + : Constructor(cmp), + source_(NULL), table_(NULL) { + } + ~TableConstructor() { + Reset(); + } + virtual Status FinishImpl(const Options& options, const KVMap& data) { + Reset(); + StringSink sink; + TableBuilder builder(options, &sink); + + for (KVMap::const_iterator it = data.begin(); + it != data.end(); + ++it) { + builder.Add(it->first, it->second); + ASSERT_TRUE(builder.status().ok()); + } + Status s = builder.Finish(); + ASSERT_TRUE(s.ok()) << s.ToString(); + + ASSERT_EQ(sink.contents().size(), builder.FileSize()); + + // Open the table + source_ = new StringSource(sink.contents()); + Options table_options; + table_options.comparator = options.comparator; + return Table::Open(table_options, source_, sink.contents().size(), &table_); + } + + virtual Iterator* NewIterator() const { + return table_->NewIterator(ReadOptions()); + } + + uint64_t ApproximateOffsetOf(const Slice& key) const { + return table_->ApproximateOffsetOf(key); + } + + private: + void Reset() { + delete table_; + delete source_; + table_ = NULL; + source_ = NULL; + } + + StringSource* source_; + Table* table_; + + TableConstructor(); +}; + +// A helper class that converts internal format keys into user keys +class KeyConvertingIterator: public Iterator { + public: + explicit KeyConvertingIterator(Iterator* iter) : iter_(iter) { } + virtual ~KeyConvertingIterator() { delete iter_; } + virtual bool Valid() const { return iter_->Valid(); } + virtual void Seek(const Slice& target) { + ParsedInternalKey ikey(target, kMaxSequenceNumber, kTypeValue); + std::string encoded; + AppendInternalKey(&encoded, ikey); + iter_->Seek(encoded); + } + virtual void SeekToFirst() { iter_->SeekToFirst(); } + virtual void SeekToLast() { iter_->SeekToLast(); } + virtual void Next() { iter_->Next(); } + virtual void Prev() { iter_->Prev(); } + + virtual Slice key() const { + assert(Valid()); + ParsedInternalKey key; + if (!ParseInternalKey(iter_->key(), &key)) { + status_ = Status::Corruption("malformed internal key"); + return Slice("corrupted key"); + } + return key.user_key; + } + + virtual Slice value() const { return iter_->value(); } + virtual Status status() const { + return status_.ok() ? iter_->status() : status_; + } + + private: + mutable Status status_; + Iterator* iter_; + + // No copying allowed + KeyConvertingIterator(const KeyConvertingIterator&); + void operator=(const KeyConvertingIterator&); +}; + +class MemTableConstructor: public Constructor { + public: + explicit MemTableConstructor(const Comparator* cmp) + : Constructor(cmp), + internal_comparator_(cmp) { + memtable_ = new MemTable(internal_comparator_); + memtable_->Ref(); + } + ~MemTableConstructor() { + memtable_->Unref(); + } + virtual Status FinishImpl(const Options& options, const KVMap& data) { + memtable_->Unref(); + memtable_ = new MemTable(internal_comparator_); + memtable_->Ref(); + int seq = 1; + for (KVMap::const_iterator it = data.begin(); + it != data.end(); + ++it) { + memtable_->Add(seq, kTypeValue, it->first, it->second); + seq++; + } + return Status::OK(); + } + virtual Iterator* NewIterator() const { + return new KeyConvertingIterator(memtable_->NewIterator()); + } + + private: + InternalKeyComparator internal_comparator_; + MemTable* memtable_; +}; + +class DBConstructor: public Constructor { + public: + explicit DBConstructor(const Comparator* cmp) + : Constructor(cmp), + comparator_(cmp) { + db_ = NULL; + NewDB(); + } + ~DBConstructor() { + delete db_; + } + virtual Status FinishImpl(const Options& options, const KVMap& data) { + delete db_; + db_ = NULL; + NewDB(); + for (KVMap::const_iterator it = data.begin(); + it != data.end(); + ++it) { + WriteBatch batch; + batch.Put(it->first, it->second); + ASSERT_TRUE(db_->Write(WriteOptions(), &batch).ok()); + } + return Status::OK(); + } + virtual Iterator* NewIterator() const { + return db_->NewIterator(ReadOptions()); + } + + virtual DB* db() const { return db_; } + + private: + void NewDB() { + std::string name = test::TmpDir() + "/table_testdb"; + + Options options; + options.comparator = comparator_; + Status status = DestroyDB(name, options); + ASSERT_TRUE(status.ok()) << status.ToString(); + + options.create_if_missing = true; + options.error_if_exists = true; + options.write_buffer_size = 10000; // Something small to force merging + status = DB::Open(options, name, &db_); + ASSERT_TRUE(status.ok()) << status.ToString(); + } + + const Comparator* comparator_; + DB* db_; +}; + +enum TestType { + TABLE_TEST, + BLOCK_TEST, + MEMTABLE_TEST, + DB_TEST +}; + +struct TestArgs { + TestType type; + bool reverse_compare; + int restart_interval; +}; + +static const TestArgs kTestArgList[] = { + { TABLE_TEST, false, 16 }, + { TABLE_TEST, false, 1 }, + { TABLE_TEST, false, 1024 }, + { TABLE_TEST, true, 16 }, + { TABLE_TEST, true, 1 }, + { TABLE_TEST, true, 1024 }, + + { BLOCK_TEST, false, 16 }, + { BLOCK_TEST, false, 1 }, + { BLOCK_TEST, false, 1024 }, + { BLOCK_TEST, true, 16 }, + { BLOCK_TEST, true, 1 }, + { BLOCK_TEST, true, 1024 }, + + // Restart interval does not matter for memtables + { MEMTABLE_TEST, false, 16 }, + { MEMTABLE_TEST, true, 16 }, + + // Do not bother with restart interval variations for DB + { DB_TEST, false, 16 }, + { DB_TEST, true, 16 }, +}; +static const int kNumTestArgs = sizeof(kTestArgList) / sizeof(kTestArgList[0]); + +class Harness { + public: + Harness() : constructor_(NULL) { } + + void Init(const TestArgs& args) { + delete constructor_; + constructor_ = NULL; + options_ = Options(); + + options_.block_restart_interval = args.restart_interval; + // Use shorter block size for tests to exercise block boundary + // conditions more. + options_.block_size = 256; + if (args.reverse_compare) { + options_.comparator = &reverse_key_comparator; + } + switch (args.type) { + case TABLE_TEST: + constructor_ = new TableConstructor(options_.comparator); + break; + case BLOCK_TEST: + constructor_ = new BlockConstructor(options_.comparator); + break; + case MEMTABLE_TEST: + constructor_ = new MemTableConstructor(options_.comparator); + break; + case DB_TEST: + constructor_ = new DBConstructor(options_.comparator); + break; + } + } + + ~Harness() { + delete constructor_; + } + + void Add(const std::string& key, const std::string& value) { + constructor_->Add(key, value); + } + + void Test(Random* rnd) { + std::vector keys; + KVMap data; + constructor_->Finish(options_, &keys, &data); + + TestForwardScan(keys, data); + TestBackwardScan(keys, data); + TestRandomAccess(rnd, keys, data); + } + + void TestForwardScan(const std::vector& keys, + const KVMap& data) { + Iterator* iter = constructor_->NewIterator(); + ASSERT_TRUE(!iter->Valid()); + iter->SeekToFirst(); + for (KVMap::const_iterator model_iter = data.begin(); + model_iter != data.end(); + ++model_iter) { + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + iter->Next(); + } + ASSERT_TRUE(!iter->Valid()); + delete iter; + } + + void TestBackwardScan(const std::vector& keys, + const KVMap& data) { + Iterator* iter = constructor_->NewIterator(); + ASSERT_TRUE(!iter->Valid()); + iter->SeekToLast(); + for (KVMap::const_reverse_iterator model_iter = data.rbegin(); + model_iter != data.rend(); + ++model_iter) { + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + iter->Prev(); + } + ASSERT_TRUE(!iter->Valid()); + delete iter; + } + + void TestRandomAccess(Random* rnd, + const std::vector& keys, + const KVMap& data) { + static const bool kVerbose = false; + Iterator* iter = constructor_->NewIterator(); + ASSERT_TRUE(!iter->Valid()); + KVMap::const_iterator model_iter = data.begin(); + if (kVerbose) fprintf(stderr, "---\n"); + for (int i = 0; i < 200; i++) { + const int toss = rnd->Uniform(5); + switch (toss) { + case 0: { + if (iter->Valid()) { + if (kVerbose) fprintf(stderr, "Next\n"); + iter->Next(); + ++model_iter; + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + } + break; + } + + case 1: { + if (kVerbose) fprintf(stderr, "SeekToFirst\n"); + iter->SeekToFirst(); + model_iter = data.begin(); + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + break; + } + + case 2: { + std::string key = PickRandomKey(rnd, keys); + model_iter = data.lower_bound(key); + if (kVerbose) fprintf(stderr, "Seek '%s'\n", + EscapeString(key).c_str()); + iter->Seek(Slice(key)); + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + break; + } + + case 3: { + if (iter->Valid()) { + if (kVerbose) fprintf(stderr, "Prev\n"); + iter->Prev(); + if (model_iter == data.begin()) { + model_iter = data.end(); // Wrap around to invalid value + } else { + --model_iter; + } + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + } + break; + } + + case 4: { + if (kVerbose) fprintf(stderr, "SeekToLast\n"); + iter->SeekToLast(); + if (keys.empty()) { + model_iter = data.end(); + } else { + std::string last = data.rbegin()->first; + model_iter = data.lower_bound(last); + } + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + break; + } + } + } + delete iter; + } + + std::string ToString(const KVMap& data, const KVMap::const_iterator& it) { + if (it == data.end()) { + return "END"; + } else { + return "'" + it->first + "->" + it->second + "'"; + } + } + + std::string ToString(const KVMap& data, + const KVMap::const_reverse_iterator& it) { + if (it == data.rend()) { + return "END"; + } else { + return "'" + it->first + "->" + it->second + "'"; + } + } + + std::string ToString(const Iterator* it) { + if (!it->Valid()) { + return "END"; + } else { + return "'" + it->key().ToString() + "->" + it->value().ToString() + "'"; + } + } + + std::string PickRandomKey(Random* rnd, const std::vector& keys) { + if (keys.empty()) { + return "foo"; + } else { + const int index = rnd->Uniform(keys.size()); + std::string result = keys[index]; + switch (rnd->Uniform(3)) { + case 0: + // Return an existing key + break; + case 1: { + // Attempt to return something smaller than an existing key + if (result.size() > 0 && result[result.size()-1] > '\0') { + result[result.size()-1]--; + } + break; + } + case 2: { + // Return something larger than an existing key + Increment(options_.comparator, &result); + break; + } + } + return result; + } + } + + // Returns NULL if not running against a DB + DB* db() const { return constructor_->db(); } + + private: + Options options_; + Constructor* constructor_; +}; + +// Test the empty key +TEST(Harness, SimpleEmptyKey) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 1); + Add("", "v"); + Test(&rnd); + } +} + +TEST(Harness, SimpleSingle) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 2); + Add("abc", "v"); + Test(&rnd); + } +} + +TEST(Harness, SimpleMulti) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 3); + Add("abc", "v"); + Add("abcd", "v"); + Add("ac", "v2"); + Test(&rnd); + } +} + +TEST(Harness, SimpleSpecialKey) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 4); + Add("\xff\xff", "v3"); + Test(&rnd); + } +} + +TEST(Harness, Randomized) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 5); + for (int num_entries = 0; num_entries < 2000; + num_entries += (num_entries < 50 ? 1 : 200)) { + if ((num_entries % 10) == 0) { + fprintf(stderr, "case %d of %d: num_entries = %d\n", + (i + 1), int(kNumTestArgs), num_entries); + } + for (int e = 0; e < num_entries; e++) { + std::string v; + Add(test::RandomKey(&rnd, rnd.Skewed(4)), + test::RandomString(&rnd, rnd.Skewed(5), &v).ToString()); + } + Test(&rnd); + } + } +} + +TEST(Harness, RandomizedLongDB) { + Random rnd(test::RandomSeed()); + TestArgs args = { DB_TEST, false, 16 }; + Init(args); + int num_entries = 100000; + for (int e = 0; e < num_entries; e++) { + std::string v; + Add(test::RandomKey(&rnd, rnd.Skewed(4)), + test::RandomString(&rnd, rnd.Skewed(5), &v).ToString()); + } + Test(&rnd); + + // We must have created enough data to force merging + int files = 0; + for (int level = 0; level < config::kNumLevels; level++) { + std::string value; + char name[100]; + snprintf(name, sizeof(name), "leveldb.num-files-at-level%d", level); + ASSERT_TRUE(db()->GetProperty(name, &value)); + files += atoi(value.c_str()); + } + ASSERT_GT(files, 0); +} + +class MemTableTest { }; + +TEST(MemTableTest, Simple) { + InternalKeyComparator cmp(BytewiseComparator()); + MemTable* memtable = new MemTable(cmp); + memtable->Ref(); + WriteBatch batch; + WriteBatchInternal::SetSequence(&batch, 100); + batch.Put(std::string("k1"), std::string("v1")); + batch.Put(std::string("k2"), std::string("v2")); + batch.Put(std::string("k3"), std::string("v3")); + batch.Put(std::string("largekey"), std::string("vlarge")); + ASSERT_TRUE(WriteBatchInternal::InsertInto(&batch, memtable).ok()); + + Iterator* iter = memtable->NewIterator(); + iter->SeekToFirst(); + while (iter->Valid()) { + fprintf(stderr, "key: '%s' -> '%s'\n", + iter->key().ToString().c_str(), + iter->value().ToString().c_str()); + iter->Next(); + } + + delete iter; + memtable->Unref(); +} + +static bool Between(uint64_t val, uint64_t low, uint64_t high) { + bool result = (val >= low) && (val <= high); + if (!result) { + fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n", + (unsigned long long)(val), + (unsigned long long)(low), + (unsigned long long)(high)); + } + return result; +} + +class TableTest { }; + +TEST(TableTest, ApproximateOffsetOfPlain) { + TableConstructor c(BytewiseComparator()); + c.Add("k01", "hello"); + c.Add("k02", "hello2"); + c.Add("k03", std::string(10000, 'x')); + c.Add("k04", std::string(200000, 'x')); + c.Add("k05", std::string(300000, 'x')); + c.Add("k06", "hello3"); + c.Add("k07", std::string(100000, 'x')); + std::vector keys; + KVMap kvmap; + Options options; + options.block_size = 1024; + options.compression = kNoCompression; + c.Finish(options, &keys, &kvmap); + + ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01a"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 10000, 11000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04a"), 210000, 211000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k05"), 210000, 211000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k06"), 510000, 511000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k07"), 510000, 511000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 610000, 612000)); + +} + +static bool SnappyCompressionSupported() { + std::string out; + Slice in = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + return port::Snappy_Compress(in.data(), in.size(), &out); +} + +TEST(TableTest, ApproximateOffsetOfCompressed) { + if (!SnappyCompressionSupported()) { + fprintf(stderr, "skipping compression tests\n"); + return; + } + + Random rnd(301); + TableConstructor c(BytewiseComparator()); + std::string tmp; + c.Add("k01", "hello"); + c.Add("k02", test::CompressibleString(&rnd, 0.25, 10000, &tmp)); + c.Add("k03", "hello3"); + c.Add("k04", test::CompressibleString(&rnd, 0.25, 10000, &tmp)); + std::vector keys; + KVMap kvmap; + Options options; + options.block_size = 1024; + options.compression = kSnappyCompression; + c.Finish(options, &keys, &kvmap); + + ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 2000, 3000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 2000, 3000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 4000, 6000)); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/table/two_level_iterator.cc b/src/leveldb-lin/table/two_level_iterator.cc new file mode 100644 index 0000000..7822eba --- /dev/null +++ b/src/leveldb-lin/table/two_level_iterator.cc @@ -0,0 +1,182 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/two_level_iterator.h" + +#include "leveldb/table.h" +#include "table/block.h" +#include "table/format.h" +#include "table/iterator_wrapper.h" + +namespace leveldb { + +namespace { + +typedef Iterator* (*BlockFunction)(void*, const ReadOptions&, const Slice&); + +class TwoLevelIterator: public Iterator { + public: + TwoLevelIterator( + Iterator* index_iter, + BlockFunction block_function, + void* arg, + const ReadOptions& options); + + virtual ~TwoLevelIterator(); + + virtual void Seek(const Slice& target); + virtual void SeekToFirst(); + virtual void SeekToLast(); + virtual void Next(); + virtual void Prev(); + + virtual bool Valid() const { + return data_iter_.Valid(); + } + virtual Slice key() const { + assert(Valid()); + return data_iter_.key(); + } + virtual Slice value() const { + assert(Valid()); + return data_iter_.value(); + } + virtual Status status() const { + // It'd be nice if status() returned a const Status& instead of a Status + if (!index_iter_.status().ok()) { + return index_iter_.status(); + } else if (data_iter_.iter() != NULL && !data_iter_.status().ok()) { + return data_iter_.status(); + } else { + return status_; + } + } + + private: + void SaveError(const Status& s) { + if (status_.ok() && !s.ok()) status_ = s; + } + void SkipEmptyDataBlocksForward(); + void SkipEmptyDataBlocksBackward(); + void SetDataIterator(Iterator* data_iter); + void InitDataBlock(); + + BlockFunction block_function_; + void* arg_; + const ReadOptions options_; + Status status_; + IteratorWrapper index_iter_; + IteratorWrapper data_iter_; // May be NULL + // If data_iter_ is non-NULL, then "data_block_handle_" holds the + // "index_value" passed to block_function_ to create the data_iter_. + std::string data_block_handle_; +}; + +TwoLevelIterator::TwoLevelIterator( + Iterator* index_iter, + BlockFunction block_function, + void* arg, + const ReadOptions& options) + : block_function_(block_function), + arg_(arg), + options_(options), + index_iter_(index_iter), + data_iter_(NULL) { +} + +TwoLevelIterator::~TwoLevelIterator() { +} + +void TwoLevelIterator::Seek(const Slice& target) { + index_iter_.Seek(target); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.Seek(target); + SkipEmptyDataBlocksForward(); +} + +void TwoLevelIterator::SeekToFirst() { + index_iter_.SeekToFirst(); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.SeekToFirst(); + SkipEmptyDataBlocksForward(); +} + +void TwoLevelIterator::SeekToLast() { + index_iter_.SeekToLast(); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.SeekToLast(); + SkipEmptyDataBlocksBackward(); +} + +void TwoLevelIterator::Next() { + assert(Valid()); + data_iter_.Next(); + SkipEmptyDataBlocksForward(); +} + +void TwoLevelIterator::Prev() { + assert(Valid()); + data_iter_.Prev(); + SkipEmptyDataBlocksBackward(); +} + + +void TwoLevelIterator::SkipEmptyDataBlocksForward() { + while (data_iter_.iter() == NULL || !data_iter_.Valid()) { + // Move to next block + if (!index_iter_.Valid()) { + SetDataIterator(NULL); + return; + } + index_iter_.Next(); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.SeekToFirst(); + } +} + +void TwoLevelIterator::SkipEmptyDataBlocksBackward() { + while (data_iter_.iter() == NULL || !data_iter_.Valid()) { + // Move to next block + if (!index_iter_.Valid()) { + SetDataIterator(NULL); + return; + } + index_iter_.Prev(); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.SeekToLast(); + } +} + +void TwoLevelIterator::SetDataIterator(Iterator* data_iter) { + if (data_iter_.iter() != NULL) SaveError(data_iter_.status()); + data_iter_.Set(data_iter); +} + +void TwoLevelIterator::InitDataBlock() { + if (!index_iter_.Valid()) { + SetDataIterator(NULL); + } else { + Slice handle = index_iter_.value(); + if (data_iter_.iter() != NULL && handle.compare(data_block_handle_) == 0) { + // data_iter_ is already constructed with this iterator, so + // no need to change anything + } else { + Iterator* iter = (*block_function_)(arg_, options_, handle); + data_block_handle_.assign(handle.data(), handle.size()); + SetDataIterator(iter); + } + } +} + +} // namespace + +Iterator* NewTwoLevelIterator( + Iterator* index_iter, + BlockFunction block_function, + void* arg, + const ReadOptions& options) { + return new TwoLevelIterator(index_iter, block_function, arg, options); +} + +} // namespace leveldb diff --git a/src/leveldb-lin/table/two_level_iterator.h b/src/leveldb-lin/table/two_level_iterator.h new file mode 100644 index 0000000..629ca34 --- /dev/null +++ b/src/leveldb-lin/table/two_level_iterator.h @@ -0,0 +1,34 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_TWO_LEVEL_ITERATOR_H_ +#define STORAGE_LEVELDB_TABLE_TWO_LEVEL_ITERATOR_H_ + +#include "leveldb/iterator.h" + +namespace leveldb { + +struct ReadOptions; + +// Return a new two level iterator. A two-level iterator contains an +// index iterator whose values point to a sequence of blocks where +// each block is itself a sequence of key,value pairs. The returned +// two-level iterator yields the concatenation of all key/value pairs +// in the sequence of blocks. Takes ownership of "index_iter" and +// will delete it when no longer needed. +// +// Uses a supplied function to convert an index_iter value into +// an iterator over the contents of the corresponding block. +extern Iterator* NewTwoLevelIterator( + Iterator* index_iter, + Iterator* (*block_function)( + void* arg, + const ReadOptions& options, + const Slice& index_value), + void* arg, + const ReadOptions& options); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_TWO_LEVEL_ITERATOR_H_ diff --git a/src/leveldb-lin/table/two_level_iterator.o b/src/leveldb-lin/table/two_level_iterator.o new file mode 100644 index 0000000..4c3829a Binary files /dev/null and b/src/leveldb-lin/table/two_level_iterator.o differ diff --git a/src/leveldb-lin/util/arena.cc b/src/leveldb-lin/util/arena.cc new file mode 100644 index 0000000..9551d6a --- /dev/null +++ b/src/leveldb-lin/util/arena.cc @@ -0,0 +1,68 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/arena.h" +#include + +namespace leveldb { + +static const int kBlockSize = 4096; + +Arena::Arena() { + blocks_memory_ = 0; + alloc_ptr_ = NULL; // First allocation will allocate a block + alloc_bytes_remaining_ = 0; +} + +Arena::~Arena() { + for (size_t i = 0; i < blocks_.size(); i++) { + delete[] blocks_[i]; + } +} + +char* Arena::AllocateFallback(size_t bytes) { + if (bytes > kBlockSize / 4) { + // Object is more than a quarter of our block size. Allocate it separately + // to avoid wasting too much space in leftover bytes. + char* result = AllocateNewBlock(bytes); + return result; + } + + // We waste the remaining space in the current block. + alloc_ptr_ = AllocateNewBlock(kBlockSize); + alloc_bytes_remaining_ = kBlockSize; + + char* result = alloc_ptr_; + alloc_ptr_ += bytes; + alloc_bytes_remaining_ -= bytes; + return result; +} + +char* Arena::AllocateAligned(size_t bytes) { + const int align = sizeof(void*); // We'll align to pointer size + assert((align & (align-1)) == 0); // Pointer size should be a power of 2 + size_t current_mod = reinterpret_cast(alloc_ptr_) & (align-1); + size_t slop = (current_mod == 0 ? 0 : align - current_mod); + size_t needed = bytes + slop; + char* result; + if (needed <= alloc_bytes_remaining_) { + result = alloc_ptr_ + slop; + alloc_ptr_ += needed; + alloc_bytes_remaining_ -= needed; + } else { + // AllocateFallback always returned aligned memory + result = AllocateFallback(bytes); + } + assert((reinterpret_cast(result) & (align-1)) == 0); + return result; +} + +char* Arena::AllocateNewBlock(size_t block_bytes) { + char* result = new char[block_bytes]; + blocks_memory_ += block_bytes; + blocks_.push_back(result); + return result; +} + +} // namespace leveldb diff --git a/src/leveldb-lin/util/arena.h b/src/leveldb-lin/util/arena.h new file mode 100644 index 0000000..8f7dde2 --- /dev/null +++ b/src/leveldb-lin/util/arena.h @@ -0,0 +1,68 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_ARENA_H_ +#define STORAGE_LEVELDB_UTIL_ARENA_H_ + +#include +#include +#include +#include + +namespace leveldb { + +class Arena { + public: + Arena(); + ~Arena(); + + // Return a pointer to a newly allocated memory block of "bytes" bytes. + char* Allocate(size_t bytes); + + // Allocate memory with the normal alignment guarantees provided by malloc + char* AllocateAligned(size_t bytes); + + // Returns an estimate of the total memory usage of data allocated + // by the arena (including space allocated but not yet used for user + // allocations). + size_t MemoryUsage() const { + return blocks_memory_ + blocks_.capacity() * sizeof(char*); + } + + private: + char* AllocateFallback(size_t bytes); + char* AllocateNewBlock(size_t block_bytes); + + // Allocation state + char* alloc_ptr_; + size_t alloc_bytes_remaining_; + + // Array of new[] allocated memory blocks + std::vector blocks_; + + // Bytes of memory in blocks allocated so far + size_t blocks_memory_; + + // No copying allowed + Arena(const Arena&); + void operator=(const Arena&); +}; + +inline char* Arena::Allocate(size_t bytes) { + // The semantics of what to return are a bit messy if we allow + // 0-byte allocations, so we disallow them here (we don't need + // them for our internal use). + assert(bytes > 0); + if (bytes <= alloc_bytes_remaining_) { + char* result = alloc_ptr_; + alloc_ptr_ += bytes; + alloc_bytes_remaining_ -= bytes; + return result; + } + return AllocateFallback(bytes); +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_ARENA_H_ diff --git a/src/leveldb-lin/util/arena.o b/src/leveldb-lin/util/arena.o new file mode 100644 index 0000000..72a871e Binary files /dev/null and b/src/leveldb-lin/util/arena.o differ diff --git a/src/leveldb-lin/util/arena_test.cc b/src/leveldb-lin/util/arena_test.cc new file mode 100644 index 0000000..63d1778 --- /dev/null +++ b/src/leveldb-lin/util/arena_test.cc @@ -0,0 +1,68 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/arena.h" + +#include "util/random.h" +#include "util/testharness.h" + +namespace leveldb { + +class ArenaTest { }; + +TEST(ArenaTest, Empty) { + Arena arena; +} + +TEST(ArenaTest, Simple) { + std::vector > allocated; + Arena arena; + const int N = 100000; + size_t bytes = 0; + Random rnd(301); + for (int i = 0; i < N; i++) { + size_t s; + if (i % (N / 10) == 0) { + s = i; + } else { + s = rnd.OneIn(4000) ? rnd.Uniform(6000) : + (rnd.OneIn(10) ? rnd.Uniform(100) : rnd.Uniform(20)); + } + if (s == 0) { + // Our arena disallows size 0 allocations. + s = 1; + } + char* r; + if (rnd.OneIn(10)) { + r = arena.AllocateAligned(s); + } else { + r = arena.Allocate(s); + } + + for (int b = 0; b < s; b++) { + // Fill the "i"th allocation with a known bit pattern + r[b] = i % 256; + } + bytes += s; + allocated.push_back(std::make_pair(s, r)); + ASSERT_GE(arena.MemoryUsage(), bytes); + if (i > N/10) { + ASSERT_LE(arena.MemoryUsage(), bytes * 1.10); + } + } + for (int i = 0; i < allocated.size(); i++) { + size_t num_bytes = allocated[i].first; + const char* p = allocated[i].second; + for (int b = 0; b < num_bytes; b++) { + // Check the "i"th allocation for the known bit pattern + ASSERT_EQ(int(p[b]) & 0xff, i % 256); + } + } +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/util/bloom.cc b/src/leveldb-lin/util/bloom.cc new file mode 100644 index 0000000..d7941cd --- /dev/null +++ b/src/leveldb-lin/util/bloom.cc @@ -0,0 +1,95 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/filter_policy.h" + +#include "leveldb/slice.h" +#include "util/hash.h" + +namespace leveldb { + +namespace { +static uint32_t BloomHash(const Slice& key) { + return Hash(key.data(), key.size(), 0xbc9f1d34); +} + +class BloomFilterPolicy : public FilterPolicy { + private: + size_t bits_per_key_; + size_t k_; + + public: + explicit BloomFilterPolicy(int bits_per_key) + : bits_per_key_(bits_per_key) { + // We intentionally round down to reduce probing cost a little bit + k_ = static_cast(bits_per_key * 0.69); // 0.69 =~ ln(2) + if (k_ < 1) k_ = 1; + if (k_ > 30) k_ = 30; + } + + virtual const char* Name() const { + return "leveldb.BuiltinBloomFilter"; + } + + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + // Compute bloom filter size (in both bits and bytes) + size_t bits = n * bits_per_key_; + + // For small n, we can see a very high false positive rate. Fix it + // by enforcing a minimum bloom filter length. + if (bits < 64) bits = 64; + + size_t bytes = (bits + 7) / 8; + bits = bytes * 8; + + const size_t init_size = dst->size(); + dst->resize(init_size + bytes, 0); + dst->push_back(static_cast(k_)); // Remember # of probes in filter + char* array = &(*dst)[init_size]; + for (size_t i = 0; i < n; i++) { + // Use double-hashing to generate a sequence of hash values. + // See analysis in [Kirsch,Mitzenmacher 2006]. + uint32_t h = BloomHash(keys[i]); + const uint32_t delta = (h >> 17) | (h << 15); // Rotate right 17 bits + for (size_t j = 0; j < k_; j++) { + const uint32_t bitpos = h % bits; + array[bitpos/8] |= (1 << (bitpos % 8)); + h += delta; + } + } + } + + virtual bool KeyMayMatch(const Slice& key, const Slice& bloom_filter) const { + const size_t len = bloom_filter.size(); + if (len < 2) return false; + + const char* array = bloom_filter.data(); + const size_t bits = (len - 1) * 8; + + // Use the encoded k so that we can read filters generated by + // bloom filters created using different parameters. + const size_t k = array[len-1]; + if (k > 30) { + // Reserved for potentially new encodings for short bloom filters. + // Consider it a match. + return true; + } + + uint32_t h = BloomHash(key); + const uint32_t delta = (h >> 17) | (h << 15); // Rotate right 17 bits + for (size_t j = 0; j < k; j++) { + const uint32_t bitpos = h % bits; + if ((array[bitpos/8] & (1 << (bitpos % 8))) == 0) return false; + h += delta; + } + return true; + } +}; +} + +const FilterPolicy* NewBloomFilterPolicy(int bits_per_key) { + return new BloomFilterPolicy(bits_per_key); +} + +} // namespace leveldb diff --git a/src/leveldb-lin/util/bloom.o b/src/leveldb-lin/util/bloom.o new file mode 100644 index 0000000..be07731 Binary files /dev/null and b/src/leveldb-lin/util/bloom.o differ diff --git a/src/leveldb-lin/util/bloom_test.cc b/src/leveldb-lin/util/bloom_test.cc new file mode 100644 index 0000000..0bf8e8d --- /dev/null +++ b/src/leveldb-lin/util/bloom_test.cc @@ -0,0 +1,160 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/filter_policy.h" + +#include "util/coding.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +static const int kVerbose = 1; + +static Slice Key(int i, char* buffer) { + EncodeFixed32(buffer, i); + return Slice(buffer, sizeof(uint32_t)); +} + +class BloomTest { + private: + const FilterPolicy* policy_; + std::string filter_; + std::vector keys_; + + public: + BloomTest() : policy_(NewBloomFilterPolicy(10)) { } + + ~BloomTest() { + delete policy_; + } + + void Reset() { + keys_.clear(); + filter_.clear(); + } + + void Add(const Slice& s) { + keys_.push_back(s.ToString()); + } + + void Build() { + std::vector key_slices; + for (size_t i = 0; i < keys_.size(); i++) { + key_slices.push_back(Slice(keys_[i])); + } + filter_.clear(); + policy_->CreateFilter(&key_slices[0], key_slices.size(), &filter_); + keys_.clear(); + if (kVerbose >= 2) DumpFilter(); + } + + size_t FilterSize() const { + return filter_.size(); + } + + void DumpFilter() { + fprintf(stderr, "F("); + for (size_t i = 0; i+1 < filter_.size(); i++) { + const unsigned int c = static_cast(filter_[i]); + for (int j = 0; j < 8; j++) { + fprintf(stderr, "%c", (c & (1 <KeyMayMatch(s, filter_); + } + + double FalsePositiveRate() { + char buffer[sizeof(int)]; + int result = 0; + for (int i = 0; i < 10000; i++) { + if (Matches(Key(i + 1000000000, buffer))) { + result++; + } + } + return result / 10000.0; + } +}; + +TEST(BloomTest, EmptyFilter) { + ASSERT_TRUE(! Matches("hello")); + ASSERT_TRUE(! Matches("world")); +} + +TEST(BloomTest, Small) { + Add("hello"); + Add("world"); + ASSERT_TRUE(Matches("hello")); + ASSERT_TRUE(Matches("world")); + ASSERT_TRUE(! Matches("x")); + ASSERT_TRUE(! Matches("foo")); +} + +static int NextLength(int length) { + if (length < 10) { + length += 1; + } else if (length < 100) { + length += 10; + } else if (length < 1000) { + length += 100; + } else { + length += 1000; + } + return length; +} + +TEST(BloomTest, VaryingLengths) { + char buffer[sizeof(int)]; + + // Count number of filters that significantly exceed the false positive rate + int mediocre_filters = 0; + int good_filters = 0; + + for (int length = 1; length <= 10000; length = NextLength(length)) { + Reset(); + for (int i = 0; i < length; i++) { + Add(Key(i, buffer)); + } + Build(); + + ASSERT_LE(FilterSize(), (length * 10 / 8) + 40) << length; + + // All added keys must match + for (int i = 0; i < length; i++) { + ASSERT_TRUE(Matches(Key(i, buffer))) + << "Length " << length << "; key " << i; + } + + // Check false positive rate + double rate = FalsePositiveRate(); + if (kVerbose >= 1) { + fprintf(stderr, "False positives: %5.2f%% @ length = %6d ; bytes = %6d\n", + rate*100.0, length, static_cast(FilterSize())); + } + ASSERT_LE(rate, 0.02); // Must not be over 2% + if (rate > 0.0125) mediocre_filters++; // Allowed, but not too often + else good_filters++; + } + if (kVerbose >= 1) { + fprintf(stderr, "Filters: %d good, %d mediocre\n", + good_filters, mediocre_filters); + } + ASSERT_LE(mediocre_filters, good_filters/5); +} + +// Different bits-per-byte + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/util/cache.cc b/src/leveldb-lin/util/cache.cc new file mode 100644 index 0000000..24f1f63 --- /dev/null +++ b/src/leveldb-lin/util/cache.cc @@ -0,0 +1,328 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include + +#include "leveldb/cache.h" +#include "port/port.h" +#include "util/hash.h" +#include "util/mutexlock.h" + +namespace leveldb { + +Cache::~Cache() { +} + +namespace { + +// LRU cache implementation + +// An entry is a variable length heap-allocated structure. Entries +// are kept in a circular doubly linked list ordered by access time. +struct LRUHandle { + void* value; + void (*deleter)(const Slice&, void* value); + LRUHandle* next_hash; + LRUHandle* next; + LRUHandle* prev; + size_t charge; // TODO(opt): Only allow uint32_t? + size_t key_length; + uint32_t refs; + uint32_t hash; // Hash of key(); used for fast sharding and comparisons + char key_data[1]; // Beginning of key + + Slice key() const { + // For cheaper lookups, we allow a temporary Handle object + // to store a pointer to a key in "value". + if (next == this) { + return *(reinterpret_cast(value)); + } else { + return Slice(key_data, key_length); + } + } +}; + +// We provide our own simple hash table since it removes a whole bunch +// of porting hacks and is also faster than some of the built-in hash +// table implementations in some of the compiler/runtime combinations +// we have tested. E.g., readrandom speeds up by ~5% over the g++ +// 4.4.3's builtin hashtable. +class HandleTable { + public: + HandleTable() : length_(0), elems_(0), list_(NULL) { Resize(); } + ~HandleTable() { delete[] list_; } + + LRUHandle* Lookup(const Slice& key, uint32_t hash) { + return *FindPointer(key, hash); + } + + LRUHandle* Insert(LRUHandle* h) { + LRUHandle** ptr = FindPointer(h->key(), h->hash); + LRUHandle* old = *ptr; + h->next_hash = (old == NULL ? NULL : old->next_hash); + *ptr = h; + if (old == NULL) { + ++elems_; + if (elems_ > length_) { + // Since each cache entry is fairly large, we aim for a small + // average linked list length (<= 1). + Resize(); + } + } + return old; + } + + LRUHandle* Remove(const Slice& key, uint32_t hash) { + LRUHandle** ptr = FindPointer(key, hash); + LRUHandle* result = *ptr; + if (result != NULL) { + *ptr = result->next_hash; + --elems_; + } + return result; + } + + private: + // The table consists of an array of buckets where each bucket is + // a linked list of cache entries that hash into the bucket. + uint32_t length_; + uint32_t elems_; + LRUHandle** list_; + + // Return a pointer to slot that points to a cache entry that + // matches key/hash. If there is no such cache entry, return a + // pointer to the trailing slot in the corresponding linked list. + LRUHandle** FindPointer(const Slice& key, uint32_t hash) { + LRUHandle** ptr = &list_[hash & (length_ - 1)]; + while (*ptr != NULL && + ((*ptr)->hash != hash || key != (*ptr)->key())) { + ptr = &(*ptr)->next_hash; + } + return ptr; + } + + void Resize() { + uint32_t new_length = 4; + while (new_length < elems_) { + new_length *= 2; + } + LRUHandle** new_list = new LRUHandle*[new_length]; + memset(new_list, 0, sizeof(new_list[0]) * new_length); + uint32_t count = 0; + for (uint32_t i = 0; i < length_; i++) { + LRUHandle* h = list_[i]; + while (h != NULL) { + LRUHandle* next = h->next_hash; + Slice key = h->key(); + uint32_t hash = h->hash; + LRUHandle** ptr = &new_list[hash & (new_length - 1)]; + h->next_hash = *ptr; + *ptr = h; + h = next; + count++; + } + } + assert(elems_ == count); + delete[] list_; + list_ = new_list; + length_ = new_length; + } +}; + +// A single shard of sharded cache. +class LRUCache { + public: + LRUCache(); + ~LRUCache(); + + // Separate from constructor so caller can easily make an array of LRUCache + void SetCapacity(size_t capacity) { capacity_ = capacity; } + + // Like Cache methods, but with an extra "hash" parameter. + Cache::Handle* Insert(const Slice& key, uint32_t hash, + void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)); + Cache::Handle* Lookup(const Slice& key, uint32_t hash); + void Release(Cache::Handle* handle); + void Erase(const Slice& key, uint32_t hash); + + private: + void LRU_Remove(LRUHandle* e); + void LRU_Append(LRUHandle* e); + void Unref(LRUHandle* e); + + // Initialized before use. + size_t capacity_; + + // mutex_ protects the following state. + port::Mutex mutex_; + size_t usage_; + uint64_t last_id_; + + // Dummy head of LRU list. + // lru.prev is newest entry, lru.next is oldest entry. + LRUHandle lru_; + + HandleTable table_; +}; + +LRUCache::LRUCache() + : usage_(0), + last_id_(0) { + // Make empty circular linked list + lru_.next = &lru_; + lru_.prev = &lru_; +} + +LRUCache::~LRUCache() { + for (LRUHandle* e = lru_.next; e != &lru_; ) { + LRUHandle* next = e->next; + assert(e->refs == 1); // Error if caller has an unreleased handle + Unref(e); + e = next; + } +} + +void LRUCache::Unref(LRUHandle* e) { + assert(e->refs > 0); + e->refs--; + if (e->refs <= 0) { + usage_ -= e->charge; + (*e->deleter)(e->key(), e->value); + free(e); + } +} + +void LRUCache::LRU_Remove(LRUHandle* e) { + e->next->prev = e->prev; + e->prev->next = e->next; +} + +void LRUCache::LRU_Append(LRUHandle* e) { + // Make "e" newest entry by inserting just before lru_ + e->next = &lru_; + e->prev = lru_.prev; + e->prev->next = e; + e->next->prev = e; +} + +Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) { + MutexLock l(&mutex_); + LRUHandle* e = table_.Lookup(key, hash); + if (e != NULL) { + e->refs++; + LRU_Remove(e); + LRU_Append(e); + } + return reinterpret_cast(e); +} + +void LRUCache::Release(Cache::Handle* handle) { + MutexLock l(&mutex_); + Unref(reinterpret_cast(handle)); +} + +Cache::Handle* LRUCache::Insert( + const Slice& key, uint32_t hash, void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)) { + MutexLock l(&mutex_); + + LRUHandle* e = reinterpret_cast( + malloc(sizeof(LRUHandle)-1 + key.size())); + e->value = value; + e->deleter = deleter; + e->charge = charge; + e->key_length = key.size(); + e->hash = hash; + e->refs = 2; // One from LRUCache, one for the returned handle + memcpy(e->key_data, key.data(), key.size()); + LRU_Append(e); + usage_ += charge; + + LRUHandle* old = table_.Insert(e); + if (old != NULL) { + LRU_Remove(old); + Unref(old); + } + + while (usage_ > capacity_ && lru_.next != &lru_) { + LRUHandle* old = lru_.next; + LRU_Remove(old); + table_.Remove(old->key(), old->hash); + Unref(old); + } + + return reinterpret_cast(e); +} + +void LRUCache::Erase(const Slice& key, uint32_t hash) { + MutexLock l(&mutex_); + LRUHandle* e = table_.Remove(key, hash); + if (e != NULL) { + LRU_Remove(e); + Unref(e); + } +} + +static const int kNumShardBits = 4; +static const int kNumShards = 1 << kNumShardBits; + +class ShardedLRUCache : public Cache { + private: + LRUCache shard_[kNumShards]; + port::Mutex id_mutex_; + uint64_t last_id_; + + static inline uint32_t HashSlice(const Slice& s) { + return Hash(s.data(), s.size(), 0); + } + + static uint32_t Shard(uint32_t hash) { + return hash >> (32 - kNumShardBits); + } + + public: + explicit ShardedLRUCache(size_t capacity) + : last_id_(0) { + const size_t per_shard = (capacity + (kNumShards - 1)) / kNumShards; + for (int s = 0; s < kNumShards; s++) { + shard_[s].SetCapacity(per_shard); + } + } + virtual ~ShardedLRUCache() { } + virtual Handle* Insert(const Slice& key, void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)) { + const uint32_t hash = HashSlice(key); + return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter); + } + virtual Handle* Lookup(const Slice& key) { + const uint32_t hash = HashSlice(key); + return shard_[Shard(hash)].Lookup(key, hash); + } + virtual void Release(Handle* handle) { + LRUHandle* h = reinterpret_cast(handle); + shard_[Shard(h->hash)].Release(handle); + } + virtual void Erase(const Slice& key) { + const uint32_t hash = HashSlice(key); + shard_[Shard(hash)].Erase(key, hash); + } + virtual void* Value(Handle* handle) { + return reinterpret_cast(handle)->value; + } + virtual uint64_t NewId() { + MutexLock l(&id_mutex_); + return ++(last_id_); + } +}; + +} // end anonymous namespace + +Cache* NewLRUCache(size_t capacity) { + return new ShardedLRUCache(capacity); +} + +} // namespace leveldb diff --git a/src/leveldb-lin/util/cache.o b/src/leveldb-lin/util/cache.o new file mode 100644 index 0000000..e414943 Binary files /dev/null and b/src/leveldb-lin/util/cache.o differ diff --git a/src/leveldb-lin/util/cache_test.cc b/src/leveldb-lin/util/cache_test.cc new file mode 100644 index 0000000..4371671 --- /dev/null +++ b/src/leveldb-lin/util/cache_test.cc @@ -0,0 +1,186 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/cache.h" + +#include +#include "util/coding.h" +#include "util/testharness.h" + +namespace leveldb { + +// Conversions between numeric keys/values and the types expected by Cache. +static std::string EncodeKey(int k) { + std::string result; + PutFixed32(&result, k); + return result; +} +static int DecodeKey(const Slice& k) { + assert(k.size() == 4); + return DecodeFixed32(k.data()); +} +static void* EncodeValue(uintptr_t v) { return reinterpret_cast(v); } +static int DecodeValue(void* v) { return reinterpret_cast(v); } + +class CacheTest { + public: + static CacheTest* current_; + + static void Deleter(const Slice& key, void* v) { + current_->deleted_keys_.push_back(DecodeKey(key)); + current_->deleted_values_.push_back(DecodeValue(v)); + } + + static const int kCacheSize = 1000; + std::vector deleted_keys_; + std::vector deleted_values_; + Cache* cache_; + + CacheTest() : cache_(NewLRUCache(kCacheSize)) { + current_ = this; + } + + ~CacheTest() { + delete cache_; + } + + int Lookup(int key) { + Cache::Handle* handle = cache_->Lookup(EncodeKey(key)); + const int r = (handle == NULL) ? -1 : DecodeValue(cache_->Value(handle)); + if (handle != NULL) { + cache_->Release(handle); + } + return r; + } + + void Insert(int key, int value, int charge = 1) { + cache_->Release(cache_->Insert(EncodeKey(key), EncodeValue(value), charge, + &CacheTest::Deleter)); + } + + void Erase(int key) { + cache_->Erase(EncodeKey(key)); + } +}; +CacheTest* CacheTest::current_; + +TEST(CacheTest, HitAndMiss) { + ASSERT_EQ(-1, Lookup(100)); + + Insert(100, 101); + ASSERT_EQ(101, Lookup(100)); + ASSERT_EQ(-1, Lookup(200)); + ASSERT_EQ(-1, Lookup(300)); + + Insert(200, 201); + ASSERT_EQ(101, Lookup(100)); + ASSERT_EQ(201, Lookup(200)); + ASSERT_EQ(-1, Lookup(300)); + + Insert(100, 102); + ASSERT_EQ(102, Lookup(100)); + ASSERT_EQ(201, Lookup(200)); + ASSERT_EQ(-1, Lookup(300)); + + ASSERT_EQ(1, deleted_keys_.size()); + ASSERT_EQ(100, deleted_keys_[0]); + ASSERT_EQ(101, deleted_values_[0]); +} + +TEST(CacheTest, Erase) { + Erase(200); + ASSERT_EQ(0, deleted_keys_.size()); + + Insert(100, 101); + Insert(200, 201); + Erase(100); + ASSERT_EQ(-1, Lookup(100)); + ASSERT_EQ(201, Lookup(200)); + ASSERT_EQ(1, deleted_keys_.size()); + ASSERT_EQ(100, deleted_keys_[0]); + ASSERT_EQ(101, deleted_values_[0]); + + Erase(100); + ASSERT_EQ(-1, Lookup(100)); + ASSERT_EQ(201, Lookup(200)); + ASSERT_EQ(1, deleted_keys_.size()); +} + +TEST(CacheTest, EntriesArePinned) { + Insert(100, 101); + Cache::Handle* h1 = cache_->Lookup(EncodeKey(100)); + ASSERT_EQ(101, DecodeValue(cache_->Value(h1))); + + Insert(100, 102); + Cache::Handle* h2 = cache_->Lookup(EncodeKey(100)); + ASSERT_EQ(102, DecodeValue(cache_->Value(h2))); + ASSERT_EQ(0, deleted_keys_.size()); + + cache_->Release(h1); + ASSERT_EQ(1, deleted_keys_.size()); + ASSERT_EQ(100, deleted_keys_[0]); + ASSERT_EQ(101, deleted_values_[0]); + + Erase(100); + ASSERT_EQ(-1, Lookup(100)); + ASSERT_EQ(1, deleted_keys_.size()); + + cache_->Release(h2); + ASSERT_EQ(2, deleted_keys_.size()); + ASSERT_EQ(100, deleted_keys_[1]); + ASSERT_EQ(102, deleted_values_[1]); +} + +TEST(CacheTest, EvictionPolicy) { + Insert(100, 101); + Insert(200, 201); + + // Frequently used entry must be kept around + for (int i = 0; i < kCacheSize + 100; i++) { + Insert(1000+i, 2000+i); + ASSERT_EQ(2000+i, Lookup(1000+i)); + ASSERT_EQ(101, Lookup(100)); + } + ASSERT_EQ(101, Lookup(100)); + ASSERT_EQ(-1, Lookup(200)); +} + +TEST(CacheTest, HeavyEntries) { + // Add a bunch of light and heavy entries and then count the combined + // size of items still in the cache, which must be approximately the + // same as the total capacity. + const int kLight = 1; + const int kHeavy = 10; + int added = 0; + int index = 0; + while (added < 2*kCacheSize) { + const int weight = (index & 1) ? kLight : kHeavy; + Insert(index, 1000+index, weight); + added += weight; + index++; + } + + int cached_weight = 0; + for (int i = 0; i < index; i++) { + const int weight = (i & 1 ? kLight : kHeavy); + int r = Lookup(i); + if (r >= 0) { + cached_weight += weight; + ASSERT_EQ(1000+i, r); + } + } + ASSERT_LE(cached_weight, kCacheSize + kCacheSize/10); +} + +TEST(CacheTest, NewId) { + uint64_t a = cache_->NewId(); + uint64_t b = cache_->NewId(); + ASSERT_NE(a, b); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/util/coding.cc b/src/leveldb-lin/util/coding.cc new file mode 100644 index 0000000..21e3186 --- /dev/null +++ b/src/leveldb-lin/util/coding.cc @@ -0,0 +1,194 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/coding.h" + +namespace leveldb { + +void EncodeFixed32(char* buf, uint32_t value) { + if (port::kLittleEndian) { + memcpy(buf, &value, sizeof(value)); + } else { + buf[0] = value & 0xff; + buf[1] = (value >> 8) & 0xff; + buf[2] = (value >> 16) & 0xff; + buf[3] = (value >> 24) & 0xff; + } +} + +void EncodeFixed64(char* buf, uint64_t value) { + if (port::kLittleEndian) { + memcpy(buf, &value, sizeof(value)); + } else { + buf[0] = value & 0xff; + buf[1] = (value >> 8) & 0xff; + buf[2] = (value >> 16) & 0xff; + buf[3] = (value >> 24) & 0xff; + buf[4] = (value >> 32) & 0xff; + buf[5] = (value >> 40) & 0xff; + buf[6] = (value >> 48) & 0xff; + buf[7] = (value >> 56) & 0xff; + } +} + +void PutFixed32(std::string* dst, uint32_t value) { + char buf[sizeof(value)]; + EncodeFixed32(buf, value); + dst->append(buf, sizeof(buf)); +} + +void PutFixed64(std::string* dst, uint64_t value) { + char buf[sizeof(value)]; + EncodeFixed64(buf, value); + dst->append(buf, sizeof(buf)); +} + +char* EncodeVarint32(char* dst, uint32_t v) { + // Operate on characters as unsigneds + unsigned char* ptr = reinterpret_cast(dst); + static const int B = 128; + if (v < (1<<7)) { + *(ptr++) = v; + } else if (v < (1<<14)) { + *(ptr++) = v | B; + *(ptr++) = v>>7; + } else if (v < (1<<21)) { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = v>>14; + } else if (v < (1<<28)) { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = (v>>14) | B; + *(ptr++) = v>>21; + } else { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = (v>>14) | B; + *(ptr++) = (v>>21) | B; + *(ptr++) = v>>28; + } + return reinterpret_cast(ptr); +} + +void PutVarint32(std::string* dst, uint32_t v) { + char buf[5]; + char* ptr = EncodeVarint32(buf, v); + dst->append(buf, ptr - buf); +} + +char* EncodeVarint64(char* dst, uint64_t v) { + static const int B = 128; + unsigned char* ptr = reinterpret_cast(dst); + while (v >= B) { + *(ptr++) = (v & (B-1)) | B; + v >>= 7; + } + *(ptr++) = static_cast(v); + return reinterpret_cast(ptr); +} + +void PutVarint64(std::string* dst, uint64_t v) { + char buf[10]; + char* ptr = EncodeVarint64(buf, v); + dst->append(buf, ptr - buf); +} + +void PutLengthPrefixedSlice(std::string* dst, const Slice& value) { + PutVarint32(dst, value.size()); + dst->append(value.data(), value.size()); +} + +int VarintLength(uint64_t v) { + int len = 1; + while (v >= 128) { + v >>= 7; + len++; + } + return len; +} + +const char* GetVarint32PtrFallback(const char* p, + const char* limit, + uint32_t* value) { + uint32_t result = 0; + for (uint32_t shift = 0; shift <= 28 && p < limit; shift += 7) { + uint32_t byte = *(reinterpret_cast(p)); + p++; + if (byte & 128) { + // More bytes are present + result |= ((byte & 127) << shift); + } else { + result |= (byte << shift); + *value = result; + return reinterpret_cast(p); + } + } + return NULL; +} + +bool GetVarint32(Slice* input, uint32_t* value) { + const char* p = input->data(); + const char* limit = p + input->size(); + const char* q = GetVarint32Ptr(p, limit, value); + if (q == NULL) { + return false; + } else { + *input = Slice(q, limit - q); + return true; + } +} + +const char* GetVarint64Ptr(const char* p, const char* limit, uint64_t* value) { + uint64_t result = 0; + for (uint32_t shift = 0; shift <= 63 && p < limit; shift += 7) { + uint64_t byte = *(reinterpret_cast(p)); + p++; + if (byte & 128) { + // More bytes are present + result |= ((byte & 127) << shift); + } else { + result |= (byte << shift); + *value = result; + return reinterpret_cast(p); + } + } + return NULL; +} + +bool GetVarint64(Slice* input, uint64_t* value) { + const char* p = input->data(); + const char* limit = p + input->size(); + const char* q = GetVarint64Ptr(p, limit, value); + if (q == NULL) { + return false; + } else { + *input = Slice(q, limit - q); + return true; + } +} + +const char* GetLengthPrefixedSlice(const char* p, const char* limit, + Slice* result) { + uint32_t len; + p = GetVarint32Ptr(p, limit, &len); + if (p == NULL) return NULL; + if (p + len > limit) return NULL; + *result = Slice(p, len); + return p + len; +} + +bool GetLengthPrefixedSlice(Slice* input, Slice* result) { + uint32_t len; + if (GetVarint32(input, &len) && + input->size() >= len) { + *result = Slice(input->data(), len); + input->remove_prefix(len); + return true; + } else { + return false; + } +} + +} // namespace leveldb diff --git a/src/leveldb-lin/util/coding.h b/src/leveldb-lin/util/coding.h new file mode 100644 index 0000000..3993c4a --- /dev/null +++ b/src/leveldb-lin/util/coding.h @@ -0,0 +1,104 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Endian-neutral encoding: +// * Fixed-length numbers are encoded with least-significant byte first +// * In addition we support variable length "varint" encoding +// * Strings are encoded prefixed by their length in varint format + +#ifndef STORAGE_LEVELDB_UTIL_CODING_H_ +#define STORAGE_LEVELDB_UTIL_CODING_H_ + +#include +#include +#include +#include "leveldb/slice.h" +#include "port/port.h" + +namespace leveldb { + +// Standard Put... routines append to a string +extern void PutFixed32(std::string* dst, uint32_t value); +extern void PutFixed64(std::string* dst, uint64_t value); +extern void PutVarint32(std::string* dst, uint32_t value); +extern void PutVarint64(std::string* dst, uint64_t value); +extern void PutLengthPrefixedSlice(std::string* dst, const Slice& value); + +// Standard Get... routines parse a value from the beginning of a Slice +// and advance the slice past the parsed value. +extern bool GetVarint32(Slice* input, uint32_t* value); +extern bool GetVarint64(Slice* input, uint64_t* value); +extern bool GetLengthPrefixedSlice(Slice* input, Slice* result); + +// Pointer-based variants of GetVarint... These either store a value +// in *v and return a pointer just past the parsed value, or return +// NULL on error. These routines only look at bytes in the range +// [p..limit-1] +extern const char* GetVarint32Ptr(const char* p,const char* limit, uint32_t* v); +extern const char* GetVarint64Ptr(const char* p,const char* limit, uint64_t* v); + +// Returns the length of the varint32 or varint64 encoding of "v" +extern int VarintLength(uint64_t v); + +// Lower-level versions of Put... that write directly into a character buffer +// REQUIRES: dst has enough space for the value being written +extern void EncodeFixed32(char* dst, uint32_t value); +extern void EncodeFixed64(char* dst, uint64_t value); + +// Lower-level versions of Put... that write directly into a character buffer +// and return a pointer just past the last byte written. +// REQUIRES: dst has enough space for the value being written +extern char* EncodeVarint32(char* dst, uint32_t value); +extern char* EncodeVarint64(char* dst, uint64_t value); + +// Lower-level versions of Get... that read directly from a character buffer +// without any bounds checking. + +inline uint32_t DecodeFixed32(const char* ptr) { + if (port::kLittleEndian) { + // Load the raw bytes + uint32_t result; + memcpy(&result, ptr, sizeof(result)); // gcc optimizes this to a plain load + return result; + } else { + return ((static_cast(static_cast(ptr[0]))) + | (static_cast(static_cast(ptr[1])) << 8) + | (static_cast(static_cast(ptr[2])) << 16) + | (static_cast(static_cast(ptr[3])) << 24)); + } +} + +inline uint64_t DecodeFixed64(const char* ptr) { + if (port::kLittleEndian) { + // Load the raw bytes + uint64_t result; + memcpy(&result, ptr, sizeof(result)); // gcc optimizes this to a plain load + return result; + } else { + uint64_t lo = DecodeFixed32(ptr); + uint64_t hi = DecodeFixed32(ptr + 4); + return (hi << 32) | lo; + } +} + +// Internal routine for use by fallback path of GetVarint32Ptr +extern const char* GetVarint32PtrFallback(const char* p, + const char* limit, + uint32_t* value); +inline const char* GetVarint32Ptr(const char* p, + const char* limit, + uint32_t* value) { + if (p < limit) { + uint32_t result = *(reinterpret_cast(p)); + if ((result & 128) == 0) { + *value = result; + return p + 1; + } + } + return GetVarint32PtrFallback(p, limit, value); +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_CODING_H_ diff --git a/src/leveldb-lin/util/coding.o b/src/leveldb-lin/util/coding.o new file mode 100644 index 0000000..44581d3 Binary files /dev/null and b/src/leveldb-lin/util/coding.o differ diff --git a/src/leveldb-lin/util/coding_test.cc b/src/leveldb-lin/util/coding_test.cc new file mode 100644 index 0000000..2c52b17 --- /dev/null +++ b/src/leveldb-lin/util/coding_test.cc @@ -0,0 +1,196 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/coding.h" + +#include "util/testharness.h" + +namespace leveldb { + +class Coding { }; + +TEST(Coding, Fixed32) { + std::string s; + for (uint32_t v = 0; v < 100000; v++) { + PutFixed32(&s, v); + } + + const char* p = s.data(); + for (uint32_t v = 0; v < 100000; v++) { + uint32_t actual = DecodeFixed32(p); + ASSERT_EQ(v, actual); + p += sizeof(uint32_t); + } +} + +TEST(Coding, Fixed64) { + std::string s; + for (int power = 0; power <= 63; power++) { + uint64_t v = static_cast(1) << power; + PutFixed64(&s, v - 1); + PutFixed64(&s, v + 0); + PutFixed64(&s, v + 1); + } + + const char* p = s.data(); + for (int power = 0; power <= 63; power++) { + uint64_t v = static_cast(1) << power; + uint64_t actual; + actual = DecodeFixed64(p); + ASSERT_EQ(v-1, actual); + p += sizeof(uint64_t); + + actual = DecodeFixed64(p); + ASSERT_EQ(v+0, actual); + p += sizeof(uint64_t); + + actual = DecodeFixed64(p); + ASSERT_EQ(v+1, actual); + p += sizeof(uint64_t); + } +} + +// Test that encoding routines generate little-endian encodings +TEST(Coding, EncodingOutput) { + std::string dst; + PutFixed32(&dst, 0x04030201); + ASSERT_EQ(4, dst.size()); + ASSERT_EQ(0x01, static_cast(dst[0])); + ASSERT_EQ(0x02, static_cast(dst[1])); + ASSERT_EQ(0x03, static_cast(dst[2])); + ASSERT_EQ(0x04, static_cast(dst[3])); + + dst.clear(); + PutFixed64(&dst, 0x0807060504030201ull); + ASSERT_EQ(8, dst.size()); + ASSERT_EQ(0x01, static_cast(dst[0])); + ASSERT_EQ(0x02, static_cast(dst[1])); + ASSERT_EQ(0x03, static_cast(dst[2])); + ASSERT_EQ(0x04, static_cast(dst[3])); + ASSERT_EQ(0x05, static_cast(dst[4])); + ASSERT_EQ(0x06, static_cast(dst[5])); + ASSERT_EQ(0x07, static_cast(dst[6])); + ASSERT_EQ(0x08, static_cast(dst[7])); +} + +TEST(Coding, Varint32) { + std::string s; + for (uint32_t i = 0; i < (32 * 32); i++) { + uint32_t v = (i / 32) << (i % 32); + PutVarint32(&s, v); + } + + const char* p = s.data(); + const char* limit = p + s.size(); + for (uint32_t i = 0; i < (32 * 32); i++) { + uint32_t expected = (i / 32) << (i % 32); + uint32_t actual; + const char* start = p; + p = GetVarint32Ptr(p, limit, &actual); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(expected, actual); + ASSERT_EQ(VarintLength(actual), p - start); + } + ASSERT_EQ(p, s.data() + s.size()); +} + +TEST(Coding, Varint64) { + // Construct the list of values to check + std::vector values; + // Some special values + values.push_back(0); + values.push_back(100); + values.push_back(~static_cast(0)); + values.push_back(~static_cast(0) - 1); + for (uint32_t k = 0; k < 64; k++) { + // Test values near powers of two + const uint64_t power = 1ull << k; + values.push_back(power); + values.push_back(power-1); + values.push_back(power+1); + }; + + std::string s; + for (int i = 0; i < values.size(); i++) { + PutVarint64(&s, values[i]); + } + + const char* p = s.data(); + const char* limit = p + s.size(); + for (int i = 0; i < values.size(); i++) { + ASSERT_TRUE(p < limit); + uint64_t actual; + const char* start = p; + p = GetVarint64Ptr(p, limit, &actual); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(values[i], actual); + ASSERT_EQ(VarintLength(actual), p - start); + } + ASSERT_EQ(p, limit); + +} + +TEST(Coding, Varint32Overflow) { + uint32_t result; + std::string input("\x81\x82\x83\x84\x85\x11"); + ASSERT_TRUE(GetVarint32Ptr(input.data(), input.data() + input.size(), &result) + == NULL); +} + +TEST(Coding, Varint32Truncation) { + uint32_t large_value = (1u << 31) + 100; + std::string s; + PutVarint32(&s, large_value); + uint32_t result; + for (int len = 0; len < s.size() - 1; len++) { + ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + len, &result) == NULL); + } + ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + s.size(), &result) != NULL); + ASSERT_EQ(large_value, result); +} + +TEST(Coding, Varint64Overflow) { + uint64_t result; + std::string input("\x81\x82\x83\x84\x85\x81\x82\x83\x84\x85\x11"); + ASSERT_TRUE(GetVarint64Ptr(input.data(), input.data() + input.size(), &result) + == NULL); +} + +TEST(Coding, Varint64Truncation) { + uint64_t large_value = (1ull << 63) + 100ull; + std::string s; + PutVarint64(&s, large_value); + uint64_t result; + for (int len = 0; len < s.size() - 1; len++) { + ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + len, &result) == NULL); + } + ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + s.size(), &result) != NULL); + ASSERT_EQ(large_value, result); +} + +TEST(Coding, Strings) { + std::string s; + PutLengthPrefixedSlice(&s, Slice("")); + PutLengthPrefixedSlice(&s, Slice("foo")); + PutLengthPrefixedSlice(&s, Slice("bar")); + PutLengthPrefixedSlice(&s, Slice(std::string(200, 'x'))); + + Slice input(s); + Slice v; + ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v)); + ASSERT_EQ("", v.ToString()); + ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v)); + ASSERT_EQ("foo", v.ToString()); + ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v)); + ASSERT_EQ("bar", v.ToString()); + ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v)); + ASSERT_EQ(std::string(200, 'x'), v.ToString()); + ASSERT_EQ("", input.ToString()); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/util/comparator.cc b/src/leveldb-lin/util/comparator.cc new file mode 100644 index 0000000..4b7b572 --- /dev/null +++ b/src/leveldb-lin/util/comparator.cc @@ -0,0 +1,81 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include "leveldb/comparator.h" +#include "leveldb/slice.h" +#include "port/port.h" +#include "util/logging.h" + +namespace leveldb { + +Comparator::~Comparator() { } + +namespace { +class BytewiseComparatorImpl : public Comparator { + public: + BytewiseComparatorImpl() { } + + virtual const char* Name() const { + return "leveldb.BytewiseComparator"; + } + + virtual int Compare(const Slice& a, const Slice& b) const { + return a.compare(b); + } + + virtual void FindShortestSeparator( + std::string* start, + const Slice& limit) const { + // Find length of common prefix + size_t min_length = std::min(start->size(), limit.size()); + size_t diff_index = 0; + while ((diff_index < min_length) && + ((*start)[diff_index] == limit[diff_index])) { + diff_index++; + } + + if (diff_index >= min_length) { + // Do not shorten if one string is a prefix of the other + } else { + uint8_t diff_byte = static_cast((*start)[diff_index]); + if (diff_byte < static_cast(0xff) && + diff_byte + 1 < static_cast(limit[diff_index])) { + (*start)[diff_index]++; + start->resize(diff_index + 1); + assert(Compare(*start, limit) < 0); + } + } + } + + virtual void FindShortSuccessor(std::string* key) const { + // Find first character that can be incremented + size_t n = key->size(); + for (size_t i = 0; i < n; i++) { + const uint8_t byte = (*key)[i]; + if (byte != static_cast(0xff)) { + (*key)[i] = byte + 1; + key->resize(i+1); + return; + } + } + // *key is a run of 0xffs. Leave it alone. + } +}; +} // namespace + +static port::OnceType once = LEVELDB_ONCE_INIT; +static const Comparator* bytewise; + +static void InitModule() { + bytewise = new BytewiseComparatorImpl; +} + +const Comparator* BytewiseComparator() { + port::InitOnce(&once, InitModule); + return bytewise; +} + +} // namespace leveldb diff --git a/src/leveldb-lin/util/comparator.o b/src/leveldb-lin/util/comparator.o new file mode 100644 index 0000000..03b6027 Binary files /dev/null and b/src/leveldb-lin/util/comparator.o differ diff --git a/src/leveldb-lin/util/crc32c.cc b/src/leveldb-lin/util/crc32c.cc new file mode 100644 index 0000000..6db9e77 --- /dev/null +++ b/src/leveldb-lin/util/crc32c.cc @@ -0,0 +1,332 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A portable implementation of crc32c, optimized to handle +// four bytes at a time. + +#include "util/crc32c.h" + +#include +#include "util/coding.h" + +namespace leveldb { +namespace crc32c { + +static const uint32_t table0_[256] = { + 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, + 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb, + 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, + 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, + 0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b, + 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384, + 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, + 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b, + 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, + 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, + 0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5, + 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa, + 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, + 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a, + 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, + 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, + 0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48, + 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957, + 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, + 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198, + 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, + 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, + 0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8, + 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7, + 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, + 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789, + 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, + 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, + 0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9, + 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6, + 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, + 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829, + 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, + 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, + 0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043, + 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c, + 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, + 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc, + 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, + 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, + 0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652, + 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d, + 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, + 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982, + 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, + 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, + 0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2, + 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed, + 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, + 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f, + 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, + 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, + 0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f, + 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540, + 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, + 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f, + 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, + 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, + 0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321, + 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e, + 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, + 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e, + 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, + 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351 +}; +static const uint32_t table1_[256] = { + 0x00000000, 0x13a29877, 0x274530ee, 0x34e7a899, + 0x4e8a61dc, 0x5d28f9ab, 0x69cf5132, 0x7a6dc945, + 0x9d14c3b8, 0x8eb65bcf, 0xba51f356, 0xa9f36b21, + 0xd39ea264, 0xc03c3a13, 0xf4db928a, 0xe7790afd, + 0x3fc5f181, 0x2c6769f6, 0x1880c16f, 0x0b225918, + 0x714f905d, 0x62ed082a, 0x560aa0b3, 0x45a838c4, + 0xa2d13239, 0xb173aa4e, 0x859402d7, 0x96369aa0, + 0xec5b53e5, 0xfff9cb92, 0xcb1e630b, 0xd8bcfb7c, + 0x7f8be302, 0x6c297b75, 0x58ced3ec, 0x4b6c4b9b, + 0x310182de, 0x22a31aa9, 0x1644b230, 0x05e62a47, + 0xe29f20ba, 0xf13db8cd, 0xc5da1054, 0xd6788823, + 0xac154166, 0xbfb7d911, 0x8b507188, 0x98f2e9ff, + 0x404e1283, 0x53ec8af4, 0x670b226d, 0x74a9ba1a, + 0x0ec4735f, 0x1d66eb28, 0x298143b1, 0x3a23dbc6, + 0xdd5ad13b, 0xcef8494c, 0xfa1fe1d5, 0xe9bd79a2, + 0x93d0b0e7, 0x80722890, 0xb4958009, 0xa737187e, + 0xff17c604, 0xecb55e73, 0xd852f6ea, 0xcbf06e9d, + 0xb19da7d8, 0xa23f3faf, 0x96d89736, 0x857a0f41, + 0x620305bc, 0x71a19dcb, 0x45463552, 0x56e4ad25, + 0x2c896460, 0x3f2bfc17, 0x0bcc548e, 0x186eccf9, + 0xc0d23785, 0xd370aff2, 0xe797076b, 0xf4359f1c, + 0x8e585659, 0x9dface2e, 0xa91d66b7, 0xbabffec0, + 0x5dc6f43d, 0x4e646c4a, 0x7a83c4d3, 0x69215ca4, + 0x134c95e1, 0x00ee0d96, 0x3409a50f, 0x27ab3d78, + 0x809c2506, 0x933ebd71, 0xa7d915e8, 0xb47b8d9f, + 0xce1644da, 0xddb4dcad, 0xe9537434, 0xfaf1ec43, + 0x1d88e6be, 0x0e2a7ec9, 0x3acdd650, 0x296f4e27, + 0x53028762, 0x40a01f15, 0x7447b78c, 0x67e52ffb, + 0xbf59d487, 0xacfb4cf0, 0x981ce469, 0x8bbe7c1e, + 0xf1d3b55b, 0xe2712d2c, 0xd69685b5, 0xc5341dc2, + 0x224d173f, 0x31ef8f48, 0x050827d1, 0x16aabfa6, + 0x6cc776e3, 0x7f65ee94, 0x4b82460d, 0x5820de7a, + 0xfbc3faf9, 0xe861628e, 0xdc86ca17, 0xcf245260, + 0xb5499b25, 0xa6eb0352, 0x920cabcb, 0x81ae33bc, + 0x66d73941, 0x7575a136, 0x419209af, 0x523091d8, + 0x285d589d, 0x3bffc0ea, 0x0f186873, 0x1cbaf004, + 0xc4060b78, 0xd7a4930f, 0xe3433b96, 0xf0e1a3e1, + 0x8a8c6aa4, 0x992ef2d3, 0xadc95a4a, 0xbe6bc23d, + 0x5912c8c0, 0x4ab050b7, 0x7e57f82e, 0x6df56059, + 0x1798a91c, 0x043a316b, 0x30dd99f2, 0x237f0185, + 0x844819fb, 0x97ea818c, 0xa30d2915, 0xb0afb162, + 0xcac27827, 0xd960e050, 0xed8748c9, 0xfe25d0be, + 0x195cda43, 0x0afe4234, 0x3e19eaad, 0x2dbb72da, + 0x57d6bb9f, 0x447423e8, 0x70938b71, 0x63311306, + 0xbb8de87a, 0xa82f700d, 0x9cc8d894, 0x8f6a40e3, + 0xf50789a6, 0xe6a511d1, 0xd242b948, 0xc1e0213f, + 0x26992bc2, 0x353bb3b5, 0x01dc1b2c, 0x127e835b, + 0x68134a1e, 0x7bb1d269, 0x4f567af0, 0x5cf4e287, + 0x04d43cfd, 0x1776a48a, 0x23910c13, 0x30339464, + 0x4a5e5d21, 0x59fcc556, 0x6d1b6dcf, 0x7eb9f5b8, + 0x99c0ff45, 0x8a626732, 0xbe85cfab, 0xad2757dc, + 0xd74a9e99, 0xc4e806ee, 0xf00fae77, 0xe3ad3600, + 0x3b11cd7c, 0x28b3550b, 0x1c54fd92, 0x0ff665e5, + 0x759baca0, 0x663934d7, 0x52de9c4e, 0x417c0439, + 0xa6050ec4, 0xb5a796b3, 0x81403e2a, 0x92e2a65d, + 0xe88f6f18, 0xfb2df76f, 0xcfca5ff6, 0xdc68c781, + 0x7b5fdfff, 0x68fd4788, 0x5c1aef11, 0x4fb87766, + 0x35d5be23, 0x26772654, 0x12908ecd, 0x013216ba, + 0xe64b1c47, 0xf5e98430, 0xc10e2ca9, 0xd2acb4de, + 0xa8c17d9b, 0xbb63e5ec, 0x8f844d75, 0x9c26d502, + 0x449a2e7e, 0x5738b609, 0x63df1e90, 0x707d86e7, + 0x0a104fa2, 0x19b2d7d5, 0x2d557f4c, 0x3ef7e73b, + 0xd98eedc6, 0xca2c75b1, 0xfecbdd28, 0xed69455f, + 0x97048c1a, 0x84a6146d, 0xb041bcf4, 0xa3e32483 +}; +static const uint32_t table2_[256] = { + 0x00000000, 0xa541927e, 0x4f6f520d, 0xea2ec073, + 0x9edea41a, 0x3b9f3664, 0xd1b1f617, 0x74f06469, + 0x38513ec5, 0x9d10acbb, 0x773e6cc8, 0xd27ffeb6, + 0xa68f9adf, 0x03ce08a1, 0xe9e0c8d2, 0x4ca15aac, + 0x70a27d8a, 0xd5e3eff4, 0x3fcd2f87, 0x9a8cbdf9, + 0xee7cd990, 0x4b3d4bee, 0xa1138b9d, 0x045219e3, + 0x48f3434f, 0xedb2d131, 0x079c1142, 0xa2dd833c, + 0xd62de755, 0x736c752b, 0x9942b558, 0x3c032726, + 0xe144fb14, 0x4405696a, 0xae2ba919, 0x0b6a3b67, + 0x7f9a5f0e, 0xdadbcd70, 0x30f50d03, 0x95b49f7d, + 0xd915c5d1, 0x7c5457af, 0x967a97dc, 0x333b05a2, + 0x47cb61cb, 0xe28af3b5, 0x08a433c6, 0xade5a1b8, + 0x91e6869e, 0x34a714e0, 0xde89d493, 0x7bc846ed, + 0x0f382284, 0xaa79b0fa, 0x40577089, 0xe516e2f7, + 0xa9b7b85b, 0x0cf62a25, 0xe6d8ea56, 0x43997828, + 0x37691c41, 0x92288e3f, 0x78064e4c, 0xdd47dc32, + 0xc76580d9, 0x622412a7, 0x880ad2d4, 0x2d4b40aa, + 0x59bb24c3, 0xfcfab6bd, 0x16d476ce, 0xb395e4b0, + 0xff34be1c, 0x5a752c62, 0xb05bec11, 0x151a7e6f, + 0x61ea1a06, 0xc4ab8878, 0x2e85480b, 0x8bc4da75, + 0xb7c7fd53, 0x12866f2d, 0xf8a8af5e, 0x5de93d20, + 0x29195949, 0x8c58cb37, 0x66760b44, 0xc337993a, + 0x8f96c396, 0x2ad751e8, 0xc0f9919b, 0x65b803e5, + 0x1148678c, 0xb409f5f2, 0x5e273581, 0xfb66a7ff, + 0x26217bcd, 0x8360e9b3, 0x694e29c0, 0xcc0fbbbe, + 0xb8ffdfd7, 0x1dbe4da9, 0xf7908dda, 0x52d11fa4, + 0x1e704508, 0xbb31d776, 0x511f1705, 0xf45e857b, + 0x80aee112, 0x25ef736c, 0xcfc1b31f, 0x6a802161, + 0x56830647, 0xf3c29439, 0x19ec544a, 0xbcadc634, + 0xc85da25d, 0x6d1c3023, 0x8732f050, 0x2273622e, + 0x6ed23882, 0xcb93aafc, 0x21bd6a8f, 0x84fcf8f1, + 0xf00c9c98, 0x554d0ee6, 0xbf63ce95, 0x1a225ceb, + 0x8b277743, 0x2e66e53d, 0xc448254e, 0x6109b730, + 0x15f9d359, 0xb0b84127, 0x5a968154, 0xffd7132a, + 0xb3764986, 0x1637dbf8, 0xfc191b8b, 0x595889f5, + 0x2da8ed9c, 0x88e97fe2, 0x62c7bf91, 0xc7862def, + 0xfb850ac9, 0x5ec498b7, 0xb4ea58c4, 0x11abcaba, + 0x655baed3, 0xc01a3cad, 0x2a34fcde, 0x8f756ea0, + 0xc3d4340c, 0x6695a672, 0x8cbb6601, 0x29faf47f, + 0x5d0a9016, 0xf84b0268, 0x1265c21b, 0xb7245065, + 0x6a638c57, 0xcf221e29, 0x250cde5a, 0x804d4c24, + 0xf4bd284d, 0x51fcba33, 0xbbd27a40, 0x1e93e83e, + 0x5232b292, 0xf77320ec, 0x1d5de09f, 0xb81c72e1, + 0xccec1688, 0x69ad84f6, 0x83834485, 0x26c2d6fb, + 0x1ac1f1dd, 0xbf8063a3, 0x55aea3d0, 0xf0ef31ae, + 0x841f55c7, 0x215ec7b9, 0xcb7007ca, 0x6e3195b4, + 0x2290cf18, 0x87d15d66, 0x6dff9d15, 0xc8be0f6b, + 0xbc4e6b02, 0x190ff97c, 0xf321390f, 0x5660ab71, + 0x4c42f79a, 0xe90365e4, 0x032da597, 0xa66c37e9, + 0xd29c5380, 0x77ddc1fe, 0x9df3018d, 0x38b293f3, + 0x7413c95f, 0xd1525b21, 0x3b7c9b52, 0x9e3d092c, + 0xeacd6d45, 0x4f8cff3b, 0xa5a23f48, 0x00e3ad36, + 0x3ce08a10, 0x99a1186e, 0x738fd81d, 0xd6ce4a63, + 0xa23e2e0a, 0x077fbc74, 0xed517c07, 0x4810ee79, + 0x04b1b4d5, 0xa1f026ab, 0x4bdee6d8, 0xee9f74a6, + 0x9a6f10cf, 0x3f2e82b1, 0xd50042c2, 0x7041d0bc, + 0xad060c8e, 0x08479ef0, 0xe2695e83, 0x4728ccfd, + 0x33d8a894, 0x96993aea, 0x7cb7fa99, 0xd9f668e7, + 0x9557324b, 0x3016a035, 0xda386046, 0x7f79f238, + 0x0b899651, 0xaec8042f, 0x44e6c45c, 0xe1a75622, + 0xdda47104, 0x78e5e37a, 0x92cb2309, 0x378ab177, + 0x437ad51e, 0xe63b4760, 0x0c158713, 0xa954156d, + 0xe5f54fc1, 0x40b4ddbf, 0xaa9a1dcc, 0x0fdb8fb2, + 0x7b2bebdb, 0xde6a79a5, 0x3444b9d6, 0x91052ba8 +}; +static const uint32_t table3_[256] = { + 0x00000000, 0xdd45aab8, 0xbf672381, 0x62228939, + 0x7b2231f3, 0xa6679b4b, 0xc4451272, 0x1900b8ca, + 0xf64463e6, 0x2b01c95e, 0x49234067, 0x9466eadf, + 0x8d665215, 0x5023f8ad, 0x32017194, 0xef44db2c, + 0xe964b13d, 0x34211b85, 0x560392bc, 0x8b463804, + 0x924680ce, 0x4f032a76, 0x2d21a34f, 0xf06409f7, + 0x1f20d2db, 0xc2657863, 0xa047f15a, 0x7d025be2, + 0x6402e328, 0xb9474990, 0xdb65c0a9, 0x06206a11, + 0xd725148b, 0x0a60be33, 0x6842370a, 0xb5079db2, + 0xac072578, 0x71428fc0, 0x136006f9, 0xce25ac41, + 0x2161776d, 0xfc24ddd5, 0x9e0654ec, 0x4343fe54, + 0x5a43469e, 0x8706ec26, 0xe524651f, 0x3861cfa7, + 0x3e41a5b6, 0xe3040f0e, 0x81268637, 0x5c632c8f, + 0x45639445, 0x98263efd, 0xfa04b7c4, 0x27411d7c, + 0xc805c650, 0x15406ce8, 0x7762e5d1, 0xaa274f69, + 0xb327f7a3, 0x6e625d1b, 0x0c40d422, 0xd1057e9a, + 0xaba65fe7, 0x76e3f55f, 0x14c17c66, 0xc984d6de, + 0xd0846e14, 0x0dc1c4ac, 0x6fe34d95, 0xb2a6e72d, + 0x5de23c01, 0x80a796b9, 0xe2851f80, 0x3fc0b538, + 0x26c00df2, 0xfb85a74a, 0x99a72e73, 0x44e284cb, + 0x42c2eeda, 0x9f874462, 0xfda5cd5b, 0x20e067e3, + 0x39e0df29, 0xe4a57591, 0x8687fca8, 0x5bc25610, + 0xb4868d3c, 0x69c32784, 0x0be1aebd, 0xd6a40405, + 0xcfa4bccf, 0x12e11677, 0x70c39f4e, 0xad8635f6, + 0x7c834b6c, 0xa1c6e1d4, 0xc3e468ed, 0x1ea1c255, + 0x07a17a9f, 0xdae4d027, 0xb8c6591e, 0x6583f3a6, + 0x8ac7288a, 0x57828232, 0x35a00b0b, 0xe8e5a1b3, + 0xf1e51979, 0x2ca0b3c1, 0x4e823af8, 0x93c79040, + 0x95e7fa51, 0x48a250e9, 0x2a80d9d0, 0xf7c57368, + 0xeec5cba2, 0x3380611a, 0x51a2e823, 0x8ce7429b, + 0x63a399b7, 0xbee6330f, 0xdcc4ba36, 0x0181108e, + 0x1881a844, 0xc5c402fc, 0xa7e68bc5, 0x7aa3217d, + 0x52a0c93f, 0x8fe56387, 0xedc7eabe, 0x30824006, + 0x2982f8cc, 0xf4c75274, 0x96e5db4d, 0x4ba071f5, + 0xa4e4aad9, 0x79a10061, 0x1b838958, 0xc6c623e0, + 0xdfc69b2a, 0x02833192, 0x60a1b8ab, 0xbde41213, + 0xbbc47802, 0x6681d2ba, 0x04a35b83, 0xd9e6f13b, + 0xc0e649f1, 0x1da3e349, 0x7f816a70, 0xa2c4c0c8, + 0x4d801be4, 0x90c5b15c, 0xf2e73865, 0x2fa292dd, + 0x36a22a17, 0xebe780af, 0x89c50996, 0x5480a32e, + 0x8585ddb4, 0x58c0770c, 0x3ae2fe35, 0xe7a7548d, + 0xfea7ec47, 0x23e246ff, 0x41c0cfc6, 0x9c85657e, + 0x73c1be52, 0xae8414ea, 0xcca69dd3, 0x11e3376b, + 0x08e38fa1, 0xd5a62519, 0xb784ac20, 0x6ac10698, + 0x6ce16c89, 0xb1a4c631, 0xd3864f08, 0x0ec3e5b0, + 0x17c35d7a, 0xca86f7c2, 0xa8a47efb, 0x75e1d443, + 0x9aa50f6f, 0x47e0a5d7, 0x25c22cee, 0xf8878656, + 0xe1873e9c, 0x3cc29424, 0x5ee01d1d, 0x83a5b7a5, + 0xf90696d8, 0x24433c60, 0x4661b559, 0x9b241fe1, + 0x8224a72b, 0x5f610d93, 0x3d4384aa, 0xe0062e12, + 0x0f42f53e, 0xd2075f86, 0xb025d6bf, 0x6d607c07, + 0x7460c4cd, 0xa9256e75, 0xcb07e74c, 0x16424df4, + 0x106227e5, 0xcd278d5d, 0xaf050464, 0x7240aedc, + 0x6b401616, 0xb605bcae, 0xd4273597, 0x09629f2f, + 0xe6264403, 0x3b63eebb, 0x59416782, 0x8404cd3a, + 0x9d0475f0, 0x4041df48, 0x22635671, 0xff26fcc9, + 0x2e238253, 0xf36628eb, 0x9144a1d2, 0x4c010b6a, + 0x5501b3a0, 0x88441918, 0xea669021, 0x37233a99, + 0xd867e1b5, 0x05224b0d, 0x6700c234, 0xba45688c, + 0xa345d046, 0x7e007afe, 0x1c22f3c7, 0xc167597f, + 0xc747336e, 0x1a0299d6, 0x782010ef, 0xa565ba57, + 0xbc65029d, 0x6120a825, 0x0302211c, 0xde478ba4, + 0x31035088, 0xec46fa30, 0x8e647309, 0x5321d9b1, + 0x4a21617b, 0x9764cbc3, 0xf54642fa, 0x2803e842 +}; + +// Used to fetch a naturally-aligned 32-bit word in little endian byte-order +static inline uint32_t LE_LOAD32(const uint8_t *p) { + return DecodeFixed32(reinterpret_cast(p)); +} + +uint32_t Extend(uint32_t crc, const char* buf, size_t size) { + const uint8_t *p = reinterpret_cast(buf); + const uint8_t *e = p + size; + uint32_t l = crc ^ 0xffffffffu; + +#define STEP1 do { \ + int c = (l & 0xff) ^ *p++; \ + l = table0_[c] ^ (l >> 8); \ +} while (0) +#define STEP4 do { \ + uint32_t c = l ^ LE_LOAD32(p); \ + p += 4; \ + l = table3_[c & 0xff] ^ \ + table2_[(c >> 8) & 0xff] ^ \ + table1_[(c >> 16) & 0xff] ^ \ + table0_[c >> 24]; \ +} while (0) + + // Point x at first 4-byte aligned byte in string. This might be + // just past the end of the string. + const uintptr_t pval = reinterpret_cast(p); + const uint8_t* x = reinterpret_cast(((pval + 3) >> 2) << 2); + if (x <= e) { + // Process bytes until finished or p is 4-byte aligned + while (p != x) { + STEP1; + } + } + // Process bytes 16 at a time + while ((e-p) >= 16) { + STEP4; STEP4; STEP4; STEP4; + } + // Process bytes 4 at a time + while ((e-p) >= 4) { + STEP4; + } + // Process the last few bytes + while (p != e) { + STEP1; + } +#undef STEP4 +#undef STEP1 + return l ^ 0xffffffffu; +} + +} // namespace crc32c +} // namespace leveldb diff --git a/src/leveldb-lin/util/crc32c.h b/src/leveldb-lin/util/crc32c.h new file mode 100644 index 0000000..1d7e5c0 --- /dev/null +++ b/src/leveldb-lin/util/crc32c.h @@ -0,0 +1,45 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_CRC32C_H_ +#define STORAGE_LEVELDB_UTIL_CRC32C_H_ + +#include +#include + +namespace leveldb { +namespace crc32c { + +// Return the crc32c of concat(A, data[0,n-1]) where init_crc is the +// crc32c of some string A. Extend() is often used to maintain the +// crc32c of a stream of data. +extern uint32_t Extend(uint32_t init_crc, const char* data, size_t n); + +// Return the crc32c of data[0,n-1] +inline uint32_t Value(const char* data, size_t n) { + return Extend(0, data, n); +} + +static const uint32_t kMaskDelta = 0xa282ead8ul; + +// Return a masked representation of crc. +// +// Motivation: it is problematic to compute the CRC of a string that +// contains embedded CRCs. Therefore we recommend that CRCs stored +// somewhere (e.g., in files) should be masked before being stored. +inline uint32_t Mask(uint32_t crc) { + // Rotate right by 15 bits and add a constant. + return ((crc >> 15) | (crc << 17)) + kMaskDelta; +} + +// Return the crc whose masked representation is masked_crc. +inline uint32_t Unmask(uint32_t masked_crc) { + uint32_t rot = masked_crc - kMaskDelta; + return ((rot >> 17) | (rot << 15)); +} + +} // namespace crc32c +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_CRC32C_H_ diff --git a/src/leveldb-lin/util/crc32c.o b/src/leveldb-lin/util/crc32c.o new file mode 100644 index 0000000..27eb2d4 Binary files /dev/null and b/src/leveldb-lin/util/crc32c.o differ diff --git a/src/leveldb-lin/util/crc32c_test.cc b/src/leveldb-lin/util/crc32c_test.cc new file mode 100644 index 0000000..4b957ee --- /dev/null +++ b/src/leveldb-lin/util/crc32c_test.cc @@ -0,0 +1,72 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/crc32c.h" +#include "util/testharness.h" + +namespace leveldb { +namespace crc32c { + +class CRC { }; + +TEST(CRC, StandardResults) { + // From rfc3720 section B.4. + char buf[32]; + + memset(buf, 0, sizeof(buf)); + ASSERT_EQ(0x8a9136aa, Value(buf, sizeof(buf))); + + memset(buf, 0xff, sizeof(buf)); + ASSERT_EQ(0x62a8ab43, Value(buf, sizeof(buf))); + + for (int i = 0; i < 32; i++) { + buf[i] = i; + } + ASSERT_EQ(0x46dd794e, Value(buf, sizeof(buf))); + + for (int i = 0; i < 32; i++) { + buf[i] = 31 - i; + } + ASSERT_EQ(0x113fdb5c, Value(buf, sizeof(buf))); + + unsigned char data[48] = { + 0x01, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x18, + 0x28, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + ASSERT_EQ(0xd9963a56, Value(reinterpret_cast(data), sizeof(data))); +} + +TEST(CRC, Values) { + ASSERT_NE(Value("a", 1), Value("foo", 3)); +} + +TEST(CRC, Extend) { + ASSERT_EQ(Value("hello world", 11), + Extend(Value("hello ", 6), "world", 5)); +} + +TEST(CRC, Mask) { + uint32_t crc = Value("foo", 3); + ASSERT_NE(crc, Mask(crc)); + ASSERT_NE(crc, Mask(Mask(crc))); + ASSERT_EQ(crc, Unmask(Mask(crc))); + ASSERT_EQ(crc, Unmask(Unmask(Mask(Mask(crc))))); +} + +} // namespace crc32c +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/util/env.cc b/src/leveldb-lin/util/env.cc new file mode 100644 index 0000000..c2600e9 --- /dev/null +++ b/src/leveldb-lin/util/env.cc @@ -0,0 +1,96 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/env.h" + +namespace leveldb { + +Env::~Env() { +} + +SequentialFile::~SequentialFile() { +} + +RandomAccessFile::~RandomAccessFile() { +} + +WritableFile::~WritableFile() { +} + +Logger::~Logger() { +} + +FileLock::~FileLock() { +} + +void Log(Logger* info_log, const char* format, ...) { + if (info_log != NULL) { + va_list ap; + va_start(ap, format); + info_log->Logv(format, ap); + va_end(ap); + } +} + +static Status DoWriteStringToFile(Env* env, const Slice& data, + const std::string& fname, + bool should_sync) { + WritableFile* file; + Status s = env->NewWritableFile(fname, &file); + if (!s.ok()) { + return s; + } + s = file->Append(data); + if (s.ok() && should_sync) { + s = file->Sync(); + } + if (s.ok()) { + s = file->Close(); + } + delete file; // Will auto-close if we did not close above + if (!s.ok()) { + env->DeleteFile(fname); + } + return s; +} + +Status WriteStringToFile(Env* env, const Slice& data, + const std::string& fname) { + return DoWriteStringToFile(env, data, fname, false); +} + +Status WriteStringToFileSync(Env* env, const Slice& data, + const std::string& fname) { + return DoWriteStringToFile(env, data, fname, true); +} + +Status ReadFileToString(Env* env, const std::string& fname, std::string* data) { + data->clear(); + SequentialFile* file; + Status s = env->NewSequentialFile(fname, &file); + if (!s.ok()) { + return s; + } + static const int kBufferSize = 8192; + char* space = new char[kBufferSize]; + while (true) { + Slice fragment; + s = file->Read(kBufferSize, &fragment, space); + if (!s.ok()) { + break; + } + data->append(fragment.data(), fragment.size()); + if (fragment.empty()) { + break; + } + } + delete[] space; + delete file; + return s; +} + +EnvWrapper::~EnvWrapper() { +} + +} // namespace leveldb diff --git a/src/leveldb-lin/util/env.o b/src/leveldb-lin/util/env.o new file mode 100644 index 0000000..c594922 Binary files /dev/null and b/src/leveldb-lin/util/env.o differ diff --git a/src/leveldb-lin/util/env_posix.cc b/src/leveldb-lin/util/env_posix.cc new file mode 100644 index 0000000..db81f56 --- /dev/null +++ b/src/leveldb-lin/util/env_posix.cc @@ -0,0 +1,701 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +#if !defined(LEVELDB_PLATFORM_WINDOWS) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(LEVELDB_PLATFORM_ANDROID) +#include +#endif +#include "leveldb/env.h" +#include "leveldb/slice.h" +#include "port/port.h" +#include "util/logging.h" +#include "util/mutexlock.h" +#include "util/posix_logger.h" + +namespace leveldb { + +namespace { + +static Status IOError(const std::string& context, int err_number) { + return Status::IOError(context, strerror(err_number)); +} + +class PosixSequentialFile: public SequentialFile { + private: + std::string filename_; + FILE* file_; + + public: + PosixSequentialFile(const std::string& fname, FILE* f) + : filename_(fname), file_(f) { } + virtual ~PosixSequentialFile() { fclose(file_); } + + virtual Status Read(size_t n, Slice* result, char* scratch) { + Status s; + size_t r = fread_unlocked(scratch, 1, n, file_); + *result = Slice(scratch, r); + if (r < n) { + if (feof(file_)) { + // We leave status as ok if we hit the end of the file + } else { + // A partial read with an error: return a non-ok status + s = IOError(filename_, errno); + } + } + return s; + } + + virtual Status Skip(uint64_t n) { + if (fseek(file_, n, SEEK_CUR)) { + return IOError(filename_, errno); + } + return Status::OK(); + } +}; + +// pread() based random-access +class PosixRandomAccessFile: public RandomAccessFile { + private: + std::string filename_; + int fd_; + + public: + PosixRandomAccessFile(const std::string& fname, int fd) + : filename_(fname), fd_(fd) { } + virtual ~PosixRandomAccessFile() { close(fd_); } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + Status s; + ssize_t r = pread(fd_, scratch, n, static_cast(offset)); + *result = Slice(scratch, (r < 0) ? 0 : r); + if (r < 0) { + // An error: return a non-ok status + s = IOError(filename_, errno); + } + return s; + } +}; + +// Helper class to limit mmap file usage so that we do not end up +// running out virtual memory or running into kernel performance +// problems for very large databases. +class MmapLimiter { + public: + // Up to 1000 mmaps for 64-bit binaries; none for smaller pointer sizes. + MmapLimiter() { + SetAllowed(sizeof(void*) >= 8 ? 1000 : 0); + } + + // If another mmap slot is available, acquire it and return true. + // Else return false. + bool Acquire() { + if (GetAllowed() <= 0) { + return false; + } + MutexLock l(&mu_); + intptr_t x = GetAllowed(); + if (x <= 0) { + return false; + } else { + SetAllowed(x - 1); + return true; + } + } + + // Release a slot acquired by a previous call to Acquire() that returned true. + void Release() { + MutexLock l(&mu_); + SetAllowed(GetAllowed() + 1); + } + + private: + port::Mutex mu_; + port::AtomicPointer allowed_; + + intptr_t GetAllowed() const { + return reinterpret_cast(allowed_.Acquire_Load()); + } + + // REQUIRES: mu_ must be held + void SetAllowed(intptr_t v) { + allowed_.Release_Store(reinterpret_cast(v)); + } + + MmapLimiter(const MmapLimiter&); + void operator=(const MmapLimiter&); +}; + +// mmap() based random-access +class PosixMmapReadableFile: public RandomAccessFile { + private: + std::string filename_; + void* mmapped_region_; + size_t length_; + MmapLimiter* limiter_; + + public: + // base[0,length-1] contains the mmapped contents of the file. + PosixMmapReadableFile(const std::string& fname, void* base, size_t length, + MmapLimiter* limiter) + : filename_(fname), mmapped_region_(base), length_(length), + limiter_(limiter) { + } + + virtual ~PosixMmapReadableFile() { + munmap(mmapped_region_, length_); + limiter_->Release(); + } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + Status s; + if (offset + n > length_) { + *result = Slice(); + s = IOError(filename_, EINVAL); + } else { + *result = Slice(reinterpret_cast(mmapped_region_) + offset, n); + } + return s; + } +}; + +// We preallocate up to an extra megabyte and use memcpy to append new +// data to the file. This is safe since we either properly close the +// file before reading from it, or for log files, the reading code +// knows enough to skip zero suffixes. +class PosixMmapFile : public WritableFile { + private: + std::string filename_; + int fd_; + size_t page_size_; + size_t map_size_; // How much extra memory to map at a time + char* base_; // The mapped region + char* limit_; // Limit of the mapped region + char* dst_; // Where to write next (in range [base_,limit_]) + char* last_sync_; // Where have we synced up to + uint64_t file_offset_; // Offset of base_ in file + + // Have we done an munmap of unsynced data? + bool pending_sync_; + + // Roundup x to a multiple of y + static size_t Roundup(size_t x, size_t y) { + return ((x + y - 1) / y) * y; + } + + size_t TruncateToPageBoundary(size_t s) { + s -= (s & (page_size_ - 1)); + assert((s % page_size_) == 0); + return s; + } + + bool UnmapCurrentRegion() { + bool result = true; + if (base_ != NULL) { + if (last_sync_ < limit_) { + // Defer syncing this data until next Sync() call, if any + pending_sync_ = true; + } + if (munmap(base_, limit_ - base_) != 0) { + result = false; + } + file_offset_ += limit_ - base_; + base_ = NULL; + limit_ = NULL; + last_sync_ = NULL; + dst_ = NULL; + + // Increase the amount we map the next time, but capped at 1MB + if (map_size_ < (1<<20)) { + map_size_ *= 2; + } + } + return result; + } + + bool MapNewRegion() { + assert(base_ == NULL); + if (ftruncate(fd_, file_offset_ + map_size_) < 0) { + return false; + } + void* ptr = mmap(NULL, map_size_, PROT_READ | PROT_WRITE, MAP_SHARED, + fd_, file_offset_); + if (ptr == MAP_FAILED) { + return false; + } + base_ = reinterpret_cast(ptr); + limit_ = base_ + map_size_; + dst_ = base_; + last_sync_ = base_; + return true; + } + + public: + PosixMmapFile(const std::string& fname, int fd, size_t page_size) + : filename_(fname), + fd_(fd), + page_size_(page_size), + map_size_(Roundup(65536, page_size)), + base_(NULL), + limit_(NULL), + dst_(NULL), + last_sync_(NULL), + file_offset_(0), + pending_sync_(false) { + assert((page_size & (page_size - 1)) == 0); + } + + + ~PosixMmapFile() { + if (fd_ >= 0) { + PosixMmapFile::Close(); + } + } + + virtual Status Append(const Slice& data) { + const char* src = data.data(); + size_t left = data.size(); + while (left > 0) { + assert(base_ <= dst_); + assert(dst_ <= limit_); + size_t avail = limit_ - dst_; + if (avail == 0) { + if (!UnmapCurrentRegion() || + !MapNewRegion()) { + return IOError(filename_, errno); + } + } + + size_t n = (left <= avail) ? left : avail; + memcpy(dst_, src, n); + dst_ += n; + src += n; + left -= n; + } + return Status::OK(); + } + + virtual Status Close() { + Status s; + size_t unused = limit_ - dst_; + if (!UnmapCurrentRegion()) { + s = IOError(filename_, errno); + } else if (unused > 0) { + // Trim the extra space at the end of the file + if (ftruncate(fd_, file_offset_ - unused) < 0) { + s = IOError(filename_, errno); + } + } + + if (close(fd_) < 0) { + if (s.ok()) { + s = IOError(filename_, errno); + } + } + + fd_ = -1; + base_ = NULL; + limit_ = NULL; + return s; + } + + virtual Status Flush() { + return Status::OK(); + } + + virtual Status Sync() { + Status s; + + if (pending_sync_) { + // Some unmapped data was not synced + pending_sync_ = false; + if (fdatasync(fd_) < 0) { + s = IOError(filename_, errno); + } + } + + if (dst_ > last_sync_) { + // Find the beginnings of the pages that contain the first and last + // bytes to be synced. + size_t p1 = TruncateToPageBoundary(last_sync_ - base_); + size_t p2 = TruncateToPageBoundary(dst_ - base_ - 1); + last_sync_ = dst_; + if (msync(base_ + p1, p2 - p1 + page_size_, MS_SYNC) < 0) { + s = IOError(filename_, errno); + } + } + + return s; + } +}; + +static int LockOrUnlock(int fd, bool lock) { + errno = 0; + struct flock f; + memset(&f, 0, sizeof(f)); + f.l_type = (lock ? F_WRLCK : F_UNLCK); + f.l_whence = SEEK_SET; + f.l_start = 0; + f.l_len = 0; // Lock/unlock entire file + return fcntl(fd, F_SETLK, &f); +} + +class PosixFileLock : public FileLock { + public: + int fd_; + std::string name_; +}; + +// Set of locked files. We keep a separate set instead of just +// relying on fcntrl(F_SETLK) since fcntl(F_SETLK) does not provide +// any protection against multiple uses from the same process. +class PosixLockTable { + private: + port::Mutex mu_; + std::set locked_files_; + public: + bool Insert(const std::string& fname) { + MutexLock l(&mu_); + return locked_files_.insert(fname).second; + } + void Remove(const std::string& fname) { + MutexLock l(&mu_); + locked_files_.erase(fname); + } +}; + +class PosixEnv : public Env { + public: + PosixEnv(); + virtual ~PosixEnv() { + fprintf(stderr, "Destroying Env::Default()\n"); + exit(1); + } + + virtual Status NewSequentialFile(const std::string& fname, + SequentialFile** result) { + FILE* f = fopen(fname.c_str(), "r"); + if (f == NULL) { + *result = NULL; + return IOError(fname, errno); + } else { + *result = new PosixSequentialFile(fname, f); + return Status::OK(); + } + } + + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) { + *result = NULL; + Status s; + int fd = open(fname.c_str(), O_RDONLY); + if (fd < 0) { + s = IOError(fname, errno); + } else if (mmap_limit_.Acquire()) { + uint64_t size; + s = GetFileSize(fname, &size); + if (s.ok()) { + void* base = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (base != MAP_FAILED) { + *result = new PosixMmapReadableFile(fname, base, size, &mmap_limit_); + } else { + s = IOError(fname, errno); + } + } + close(fd); + if (!s.ok()) { + mmap_limit_.Release(); + } + } else { + *result = new PosixRandomAccessFile(fname, fd); + } + return s; + } + + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) { + Status s; + const int fd = open(fname.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0644); + if (fd < 0) { + *result = NULL; + s = IOError(fname, errno); + } else { + *result = new PosixMmapFile(fname, fd, page_size_); + } + return s; + } + + virtual bool FileExists(const std::string& fname) { + return access(fname.c_str(), F_OK) == 0; + } + + virtual Status GetChildren(const std::string& dir, + std::vector* result) { + result->clear(); + DIR* d = opendir(dir.c_str()); + if (d == NULL) { + return IOError(dir, errno); + } + struct dirent* entry; + while ((entry = readdir(d)) != NULL) { + result->push_back(entry->d_name); + } + closedir(d); + return Status::OK(); + } + + virtual Status DeleteFile(const std::string& fname) { + Status result; + if (unlink(fname.c_str()) != 0) { + result = IOError(fname, errno); + } + return result; + }; + + virtual Status CreateDir(const std::string& name) { + Status result; + if (mkdir(name.c_str(), 0755) != 0) { + result = IOError(name, errno); + } + return result; + }; + + virtual Status DeleteDir(const std::string& name) { + Status result; + if (rmdir(name.c_str()) != 0) { + result = IOError(name, errno); + } + return result; + }; + + virtual Status GetFileSize(const std::string& fname, uint64_t* size) { + Status s; + struct stat sbuf; + if (stat(fname.c_str(), &sbuf) != 0) { + *size = 0; + s = IOError(fname, errno); + } else { + *size = sbuf.st_size; + } + return s; + } + + virtual Status RenameFile(const std::string& src, const std::string& target) { + Status result; + if (rename(src.c_str(), target.c_str()) != 0) { + result = IOError(src, errno); + } + return result; + } + + virtual Status LockFile(const std::string& fname, FileLock** lock) { + *lock = NULL; + Status result; + int fd = open(fname.c_str(), O_RDWR | O_CREAT, 0644); + if (fd < 0) { + result = IOError(fname, errno); + } else if (!locks_.Insert(fname)) { + close(fd); + result = Status::IOError("lock " + fname, "already held by process"); + } else if (LockOrUnlock(fd, true) == -1) { + result = IOError("lock " + fname, errno); + close(fd); + locks_.Remove(fname); + } else { + PosixFileLock* my_lock = new PosixFileLock; + my_lock->fd_ = fd; + my_lock->name_ = fname; + *lock = my_lock; + } + return result; + } + + virtual Status UnlockFile(FileLock* lock) { + PosixFileLock* my_lock = reinterpret_cast(lock); + Status result; + if (LockOrUnlock(my_lock->fd_, false) == -1) { + result = IOError("unlock", errno); + } + locks_.Remove(my_lock->name_); + close(my_lock->fd_); + delete my_lock; + return result; + } + + virtual void Schedule(void (*function)(void*), void* arg); + + virtual void StartThread(void (*function)(void* arg), void* arg); + + virtual Status GetTestDirectory(std::string* result) { + const char* env = getenv("TEST_TMPDIR"); + if (env && env[0] != '\0') { + *result = env; + } else { + char buf[100]; + snprintf(buf, sizeof(buf), "/tmp/leveldbtest-%d", int(geteuid())); + *result = buf; + } + // Directory may already exist + CreateDir(*result); + return Status::OK(); + } + + static uint64_t gettid() { + pthread_t tid = pthread_self(); + uint64_t thread_id = 0; + memcpy(&thread_id, &tid, std::min(sizeof(thread_id), sizeof(tid))); + return thread_id; + } + + virtual Status NewLogger(const std::string& fname, Logger** result) { + FILE* f = fopen(fname.c_str(), "w"); + if (f == NULL) { + *result = NULL; + return IOError(fname, errno); + } else { + *result = new PosixLogger(f, &PosixEnv::gettid); + return Status::OK(); + } + } + + virtual uint64_t NowMicros() { + struct timeval tv; + gettimeofday(&tv, NULL); + return static_cast(tv.tv_sec) * 1000000 + tv.tv_usec; + } + + virtual void SleepForMicroseconds(int micros) { + usleep(micros); + } + + private: + void PthreadCall(const char* label, int result) { + if (result != 0) { + fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); + exit(1); + } + } + + // BGThread() is the body of the background thread + void BGThread(); + static void* BGThreadWrapper(void* arg) { + reinterpret_cast(arg)->BGThread(); + return NULL; + } + + size_t page_size_; + pthread_mutex_t mu_; + pthread_cond_t bgsignal_; + pthread_t bgthread_; + bool started_bgthread_; + + // Entry per Schedule() call + struct BGItem { void* arg; void (*function)(void*); }; + typedef std::deque BGQueue; + BGQueue queue_; + + PosixLockTable locks_; + MmapLimiter mmap_limit_; +}; + +PosixEnv::PosixEnv() : page_size_(getpagesize()), + started_bgthread_(false) { + PthreadCall("mutex_init", pthread_mutex_init(&mu_, NULL)); + PthreadCall("cvar_init", pthread_cond_init(&bgsignal_, NULL)); +} + +void PosixEnv::Schedule(void (*function)(void*), void* arg) { + PthreadCall("lock", pthread_mutex_lock(&mu_)); + + // Start background thread if necessary + if (!started_bgthread_) { + started_bgthread_ = true; + PthreadCall( + "create thread", + pthread_create(&bgthread_, NULL, &PosixEnv::BGThreadWrapper, this)); + } + + // If the queue is currently empty, the background thread may currently be + // waiting. + if (queue_.empty()) { + PthreadCall("signal", pthread_cond_signal(&bgsignal_)); + } + + // Add to priority queue + queue_.push_back(BGItem()); + queue_.back().function = function; + queue_.back().arg = arg; + + PthreadCall("unlock", pthread_mutex_unlock(&mu_)); +} + +void PosixEnv::BGThread() { + while (true) { + // Wait until there is an item that is ready to run + PthreadCall("lock", pthread_mutex_lock(&mu_)); + while (queue_.empty()) { + PthreadCall("wait", pthread_cond_wait(&bgsignal_, &mu_)); + } + + void (*function)(void*) = queue_.front().function; + void* arg = queue_.front().arg; + queue_.pop_front(); + + PthreadCall("unlock", pthread_mutex_unlock(&mu_)); + (*function)(arg); + } +} + +namespace { +struct StartThreadState { + void (*user_function)(void*); + void* arg; +}; +} +static void* StartThreadWrapper(void* arg) { + StartThreadState* state = reinterpret_cast(arg); + state->user_function(state->arg); + delete state; + return NULL; +} + +void PosixEnv::StartThread(void (*function)(void* arg), void* arg) { + pthread_t t; + StartThreadState* state = new StartThreadState; + state->user_function = function; + state->arg = arg; + PthreadCall("start thread", + pthread_create(&t, NULL, &StartThreadWrapper, state)); +} + +} // namespace + +static pthread_once_t once = PTHREAD_ONCE_INIT; +static Env* default_env; +static void InitDefaultEnv() { default_env = new PosixEnv; } + +Env* Env::Default() { + pthread_once(&once, InitDefaultEnv); + return default_env; +} + +} // namespace leveldb + +#endif diff --git a/src/leveldb-lin/util/env_posix.o b/src/leveldb-lin/util/env_posix.o new file mode 100644 index 0000000..2558ab9 Binary files /dev/null and b/src/leveldb-lin/util/env_posix.o differ diff --git a/src/leveldb-lin/util/env_test.cc b/src/leveldb-lin/util/env_test.cc new file mode 100644 index 0000000..b72cb44 --- /dev/null +++ b/src/leveldb-lin/util/env_test.cc @@ -0,0 +1,104 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/env.h" + +#include "port/port.h" +#include "util/testharness.h" + +namespace leveldb { + +static const int kDelayMicros = 100000; + +class EnvPosixTest { + private: + port::Mutex mu_; + std::string events_; + + public: + Env* env_; + EnvPosixTest() : env_(Env::Default()) { } +}; + +static void SetBool(void* ptr) { + reinterpret_cast(ptr)->NoBarrier_Store(ptr); +} + +TEST(EnvPosixTest, RunImmediately) { + port::AtomicPointer called (NULL); + env_->Schedule(&SetBool, &called); + Env::Default()->SleepForMicroseconds(kDelayMicros); + ASSERT_TRUE(called.NoBarrier_Load() != NULL); +} + +TEST(EnvPosixTest, RunMany) { + port::AtomicPointer last_id (NULL); + + struct CB { + port::AtomicPointer* last_id_ptr; // Pointer to shared slot + uintptr_t id; // Order# for the execution of this callback + + CB(port::AtomicPointer* p, int i) : last_id_ptr(p), id(i) { } + + static void Run(void* v) { + CB* cb = reinterpret_cast(v); + void* cur = cb->last_id_ptr->NoBarrier_Load(); + ASSERT_EQ(cb->id-1, reinterpret_cast(cur)); + cb->last_id_ptr->Release_Store(reinterpret_cast(cb->id)); + } + }; + + // Schedule in different order than start time + CB cb1(&last_id, 1); + CB cb2(&last_id, 2); + CB cb3(&last_id, 3); + CB cb4(&last_id, 4); + env_->Schedule(&CB::Run, &cb1); + env_->Schedule(&CB::Run, &cb2); + env_->Schedule(&CB::Run, &cb3); + env_->Schedule(&CB::Run, &cb4); + + Env::Default()->SleepForMicroseconds(kDelayMicros); + void* cur = last_id.Acquire_Load(); + ASSERT_EQ(4, reinterpret_cast(cur)); +} + +struct State { + port::Mutex mu; + int val; + int num_running; +}; + +static void ThreadBody(void* arg) { + State* s = reinterpret_cast(arg); + s->mu.Lock(); + s->val += 1; + s->num_running -= 1; + s->mu.Unlock(); +} + +TEST(EnvPosixTest, StartThread) { + State state; + state.val = 0; + state.num_running = 3; + for (int i = 0; i < 3; i++) { + env_->StartThread(&ThreadBody, &state); + } + while (true) { + state.mu.Lock(); + int num = state.num_running; + state.mu.Unlock(); + if (num == 0) { + break; + } + Env::Default()->SleepForMicroseconds(kDelayMicros); + } + ASSERT_EQ(state.val, 3); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb-lin/util/env_win.cc b/src/leveldb-lin/util/env_win.cc new file mode 100644 index 0000000..ef2ecae --- /dev/null +++ b/src/leveldb-lin/util/env_win.cc @@ -0,0 +1,1031 @@ +// This file contains source that originates from: +// http://code.google.com/p/leveldbwin/source/browse/trunk/win32_impl_src/env_win32.h +// http://code.google.com/p/leveldbwin/source/browse/trunk/win32_impl_src/port_win32.cc +// Those files dont' have any explict license headers but the +// project (http://code.google.com/p/leveldbwin/) lists the 'New BSD License' +// as the license. +#if defined(LEVELDB_PLATFORM_WINDOWS) +#include + + +#include "leveldb/env.h" + +#include "port/port.h" +#include "leveldb/slice.h" +#include "util/logging.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef max +#undef max +#endif + +#ifndef va_copy +#define va_copy(d,s) ((d) = (s)) +#endif + +#if defined DeleteFile +#undef DeleteFile +#endif + +//Declarations +namespace leveldb +{ + +namespace Win32 +{ + +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +std::string GetCurrentDir(); +std::wstring GetCurrentDirW(); + +static const std::string CurrentDir = GetCurrentDir(); +static const std::wstring CurrentDirW = GetCurrentDirW(); + +std::string& ModifyPath(std::string& path); +std::wstring& ModifyPath(std::wstring& path); + +std::string GetLastErrSz(); +std::wstring GetLastErrSzW(); + +size_t GetPageSize(); + +typedef void (*ScheduleProc)(void*) ; + +struct WorkItemWrapper +{ + WorkItemWrapper(ScheduleProc proc_,void* content_); + ScheduleProc proc; + void* pContent; +}; + +DWORD WINAPI WorkItemWrapperProc(LPVOID pContent); + +class Win32SequentialFile : public SequentialFile +{ +public: + friend class Win32Env; + virtual ~Win32SequentialFile(); + virtual Status Read(size_t n, Slice* result, char* scratch); + virtual Status Skip(uint64_t n); + BOOL isEnable(); +private: + BOOL _Init(); + void _CleanUp(); + Win32SequentialFile(const std::string& fname); + std::string _filename; + ::HANDLE _hFile; + DISALLOW_COPY_AND_ASSIGN(Win32SequentialFile); +}; + +class Win32RandomAccessFile : public RandomAccessFile +{ +public: + friend class Win32Env; + virtual ~Win32RandomAccessFile(); + virtual Status Read(uint64_t offset, size_t n, Slice* result,char* scratch) const; + BOOL isEnable(); +private: + BOOL _Init(LPCWSTR path); + void _CleanUp(); + Win32RandomAccessFile(const std::string& fname); + HANDLE _hFile; + const std::string _filename; + DISALLOW_COPY_AND_ASSIGN(Win32RandomAccessFile); +}; + +class Win32MapFile : public WritableFile +{ +public: + Win32MapFile(const std::string& fname); + + ~Win32MapFile(); + virtual Status Append(const Slice& data); + virtual Status Close(); + virtual Status Flush(); + virtual Status Sync(); + BOOL isEnable(); +private: + std::string _filename; + HANDLE _hFile; + size_t _page_size; + size_t _map_size; // How much extra memory to map at a time + char* _base; // The mapped region + HANDLE _base_handle; + char* _limit; // Limit of the mapped region + char* _dst; // Where to write next (in range [base_,limit_]) + char* _last_sync; // Where have we synced up to + uint64_t _file_offset; // Offset of base_ in file + //LARGE_INTEGER file_offset_; + // Have we done an munmap of unsynced data? + bool _pending_sync; + + // Roundup x to a multiple of y + static size_t _Roundup(size_t x, size_t y); + size_t _TruncateToPageBoundary(size_t s); + bool _UnmapCurrentRegion(); + bool _MapNewRegion(); + DISALLOW_COPY_AND_ASSIGN(Win32MapFile); + BOOL _Init(LPCWSTR Path); +}; + +class Win32FileLock : public FileLock +{ +public: + friend class Win32Env; + virtual ~Win32FileLock(); + BOOL isEnable(); +private: + BOOL _Init(LPCWSTR path); + void _CleanUp(); + Win32FileLock(const std::string& fname); + HANDLE _hFile; + std::string _filename; + DISALLOW_COPY_AND_ASSIGN(Win32FileLock); +}; + +class Win32Logger : public Logger +{ +public: + friend class Win32Env; + virtual ~Win32Logger(); + virtual void Logv(const char* format, va_list ap); +private: + explicit Win32Logger(WritableFile* pFile); + WritableFile* _pFileProxy; + DISALLOW_COPY_AND_ASSIGN(Win32Logger); +}; + +class Win32Env : public Env +{ +public: + Win32Env(); + virtual ~Win32Env(); + virtual Status NewSequentialFile(const std::string& fname, + SequentialFile** result); + + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result); + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result); + + virtual bool FileExists(const std::string& fname); + + virtual Status GetChildren(const std::string& dir, + std::vector* result); + + virtual Status DeleteFile(const std::string& fname); + + virtual Status CreateDir(const std::string& dirname); + + virtual Status DeleteDir(const std::string& dirname); + + virtual Status GetFileSize(const std::string& fname, uint64_t* file_size); + + virtual Status RenameFile(const std::string& src, + const std::string& target); + + virtual Status LockFile(const std::string& fname, FileLock** lock); + + virtual Status UnlockFile(FileLock* lock); + + virtual void Schedule( + void (*function)(void* arg), + void* arg); + + virtual void StartThread(void (*function)(void* arg), void* arg); + + virtual Status GetTestDirectory(std::string* path); + + //virtual void Logv(WritableFile* log, const char* format, va_list ap); + + virtual Status NewLogger(const std::string& fname, Logger** result); + + virtual uint64_t NowMicros(); + + virtual void SleepForMicroseconds(int micros); +}; + +void ToWidePath(const std::string& value, std::wstring& target) { + wchar_t buffer[MAX_PATH]; + MultiByteToWideChar(CP_ACP, 0, value.c_str(), -1, buffer, MAX_PATH); + target = buffer; +} + +void ToNarrowPath(const std::wstring& value, std::string& target) { + char buffer[MAX_PATH]; + WideCharToMultiByte(CP_ACP, 0, value.c_str(), -1, buffer, MAX_PATH, NULL, NULL); + target = buffer; +} + +std::string GetCurrentDir() +{ + CHAR path[MAX_PATH]; + ::GetModuleFileNameA(::GetModuleHandleA(NULL),path,MAX_PATH); + *strrchr(path,'\\') = 0; + return std::string(path); +} + +std::wstring GetCurrentDirW() +{ + WCHAR path[MAX_PATH]; + ::GetModuleFileNameW(::GetModuleHandleW(NULL),path,MAX_PATH); + *wcsrchr(path,L'\\') = 0; + return std::wstring(path); +} + +std::string& ModifyPath(std::string& path) +{ + if(path[0] == '/' || path[0] == '\\'){ + path = CurrentDir + path; + } + std::replace(path.begin(),path.end(),'/','\\'); + + return path; +} + +std::wstring& ModifyPath(std::wstring& path) +{ + if(path[0] == L'/' || path[0] == L'\\'){ + path = CurrentDirW + path; + } + std::replace(path.begin(),path.end(),L'/',L'\\'); + return path; +} + +std::string GetLastErrSz() +{ + LPWSTR lpMsgBuf; + FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + 0, // Default language + (LPWSTR) &lpMsgBuf, + 0, + NULL + ); + std::string Err; + ToNarrowPath(lpMsgBuf, Err); + LocalFree( lpMsgBuf ); + return Err; +} + +std::wstring GetLastErrSzW() +{ + LPVOID lpMsgBuf; + FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + 0, // Default language + (LPWSTR) &lpMsgBuf, + 0, + NULL + ); + std::wstring Err = (LPCWSTR)lpMsgBuf; + LocalFree(lpMsgBuf); + return Err; +} + +WorkItemWrapper::WorkItemWrapper( ScheduleProc proc_,void* content_ ) : + proc(proc_),pContent(content_) +{ + +} + +DWORD WINAPI WorkItemWrapperProc(LPVOID pContent) +{ + WorkItemWrapper* item = static_cast(pContent); + ScheduleProc TempProc = item->proc; + void* arg = item->pContent; + delete item; + TempProc(arg); + return 0; +} + +size_t GetPageSize() +{ + SYSTEM_INFO si; + GetSystemInfo(&si); + return std::max(si.dwPageSize,si.dwAllocationGranularity); +} + +const size_t g_PageSize = GetPageSize(); + + +Win32SequentialFile::Win32SequentialFile( const std::string& fname ) : + _filename(fname),_hFile(NULL) +{ + _Init(); +} + +Win32SequentialFile::~Win32SequentialFile() +{ + _CleanUp(); +} + +Status Win32SequentialFile::Read( size_t n, Slice* result, char* scratch ) +{ + Status sRet; + DWORD hasRead = 0; + if(_hFile && ReadFile(_hFile,scratch,n,&hasRead,NULL) ){ + *result = Slice(scratch,hasRead); + } else { + sRet = Status::IOError(_filename, Win32::GetLastErrSz() ); + } + return sRet; +} + +Status Win32SequentialFile::Skip( uint64_t n ) +{ + Status sRet; + LARGE_INTEGER Move,NowPointer; + Move.QuadPart = n; + if(!SetFilePointerEx(_hFile,Move,&NowPointer,FILE_CURRENT)){ + sRet = Status::IOError(_filename,Win32::GetLastErrSz()); + } + return sRet; +} + +BOOL Win32SequentialFile::isEnable() +{ + return _hFile ? TRUE : FALSE; +} + +BOOL Win32SequentialFile::_Init() +{ + std::wstring path; + ToWidePath(_filename, path); + _hFile = CreateFileW(path.c_str(), + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + return _hFile ? TRUE : FALSE; +} + +void Win32SequentialFile::_CleanUp() +{ + if(_hFile){ + CloseHandle(_hFile); + _hFile = NULL; + } +} + +Win32RandomAccessFile::Win32RandomAccessFile( const std::string& fname ) : + _filename(fname),_hFile(NULL) +{ + std::wstring path; + ToWidePath(fname, path); + _Init( path.c_str() ); +} + +Win32RandomAccessFile::~Win32RandomAccessFile() +{ + _CleanUp(); +} + +Status Win32RandomAccessFile::Read(uint64_t offset,size_t n,Slice* result,char* scratch) const +{ + Status sRet; + OVERLAPPED ol = {0}; + ZeroMemory(&ol,sizeof(ol)); + ol.Offset = (DWORD)offset; + ol.OffsetHigh = (DWORD)(offset >> 32); + DWORD hasRead = 0; + if(!ReadFile(_hFile,scratch,n,&hasRead,&ol)) + sRet = Status::IOError(_filename,Win32::GetLastErrSz()); + else + *result = Slice(scratch,hasRead); + return sRet; +} + +BOOL Win32RandomAccessFile::_Init( LPCWSTR path ) +{ + BOOL bRet = FALSE; + if(!_hFile) + _hFile = ::CreateFileW(path,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,NULL); + if(!_hFile || _hFile == INVALID_HANDLE_VALUE ) + _hFile = NULL; + else + bRet = TRUE; + return bRet; +} + +BOOL Win32RandomAccessFile::isEnable() +{ + return _hFile ? TRUE : FALSE; +} + +void Win32RandomAccessFile::_CleanUp() +{ + if(_hFile){ + ::CloseHandle(_hFile); + _hFile = NULL; + } +} + +size_t Win32MapFile::_Roundup( size_t x, size_t y ) +{ + return ((x + y - 1) / y) * y; +} + +size_t Win32MapFile::_TruncateToPageBoundary( size_t s ) +{ + s -= (s & (_page_size - 1)); + assert((s % _page_size) == 0); + return s; +} + +bool Win32MapFile::_UnmapCurrentRegion() +{ + bool result = true; + if (_base != NULL) { + if (_last_sync < _limit) { + // Defer syncing this data until next Sync() call, if any + _pending_sync = true; + } + if (!UnmapViewOfFile(_base) || !CloseHandle(_base_handle)) + result = false; + _file_offset += _limit - _base; + _base = NULL; + _base_handle = NULL; + _limit = NULL; + _last_sync = NULL; + _dst = NULL; + // Increase the amount we map the next time, but capped at 1MB + if (_map_size < (1<<20)) { + _map_size *= 2; + } + } + return result; +} + +bool Win32MapFile::_MapNewRegion() +{ + assert(_base == NULL); + //LONG newSizeHigh = (LONG)((file_offset_ + map_size_) >> 32); + //LONG newSizeLow = (LONG)((file_offset_ + map_size_) & 0xFFFFFFFF); + DWORD off_hi = (DWORD)(_file_offset >> 32); + DWORD off_lo = (DWORD)(_file_offset & 0xFFFFFFFF); + LARGE_INTEGER newSize; + newSize.QuadPart = _file_offset + _map_size; + SetFilePointerEx(_hFile, newSize, NULL, FILE_BEGIN); + SetEndOfFile(_hFile); + + _base_handle = CreateFileMappingA( + _hFile, + NULL, + PAGE_READWRITE, + 0, + 0, + 0); + if (_base_handle != NULL) { + _base = (char*) MapViewOfFile(_base_handle, + FILE_MAP_ALL_ACCESS, + off_hi, + off_lo, + _map_size); + if (_base != NULL) { + _limit = _base + _map_size; + _dst = _base; + _last_sync = _base; + return true; + } + } + return false; +} + +Win32MapFile::Win32MapFile( const std::string& fname) : + _filename(fname), + _hFile(NULL), + _page_size(Win32::g_PageSize), + _map_size(_Roundup(65536, Win32::g_PageSize)), + _base(NULL), + _base_handle(NULL), + _limit(NULL), + _dst(NULL), + _last_sync(NULL), + _file_offset(0), + _pending_sync(false) +{ + std::wstring path; + ToWidePath(fname, path); + _Init(path.c_str()); + assert((Win32::g_PageSize & (Win32::g_PageSize - 1)) == 0); +} + +Status Win32MapFile::Append( const Slice& data ) +{ + const char* src = data.data(); + size_t left = data.size(); + Status s; + while (left > 0) { + assert(_base <= _dst); + assert(_dst <= _limit); + size_t avail = _limit - _dst; + if (avail == 0) { + if (!_UnmapCurrentRegion() || + !_MapNewRegion()) { + return Status::IOError("WinMmapFile.Append::UnmapCurrentRegion or MapNewRegion: ", Win32::GetLastErrSz()); + } + } + size_t n = (left <= avail) ? left : avail; + memcpy(_dst, src, n); + _dst += n; + src += n; + left -= n; + } + return s; +} + +Status Win32MapFile::Close() +{ + Status s; + size_t unused = _limit - _dst; + if (!_UnmapCurrentRegion()) { + s = Status::IOError("WinMmapFile.Close::UnmapCurrentRegion: ",Win32::GetLastErrSz()); + } else if (unused > 0) { + // Trim the extra space at the end of the file + LARGE_INTEGER newSize; + newSize.QuadPart = _file_offset - unused; + if (!SetFilePointerEx(_hFile, newSize, NULL, FILE_BEGIN)) { + s = Status::IOError("WinMmapFile.Close::SetFilePointer: ",Win32::GetLastErrSz()); + } else + SetEndOfFile(_hFile); + } + if (!CloseHandle(_hFile)) { + if (s.ok()) { + s = Status::IOError("WinMmapFile.Close::CloseHandle: ", Win32::GetLastErrSz()); + } + } + _hFile = INVALID_HANDLE_VALUE; + _base = NULL; + _base_handle = NULL; + _limit = NULL; + + return s; +} + +Status Win32MapFile::Sync() +{ + Status s; + if (_pending_sync) { + // Some unmapped data was not synced + _pending_sync = false; + if (!FlushFileBuffers(_hFile)) { + s = Status::IOError("WinMmapFile.Sync::FlushFileBuffers: ",Win32::GetLastErrSz()); + } + } + if (_dst > _last_sync) { + // Find the beginnings of the pages that contain the first and last + // bytes to be synced. + size_t p1 = _TruncateToPageBoundary(_last_sync - _base); + size_t p2 = _TruncateToPageBoundary(_dst - _base - 1); + _last_sync = _dst; + if (!FlushViewOfFile(_base + p1, p2 - p1 + _page_size)) { + s = Status::IOError("WinMmapFile.Sync::FlushViewOfFile: ",Win32::GetLastErrSz()); + } + } + return s; +} + +Status Win32MapFile::Flush() +{ + return Status::OK(); +} + +Win32MapFile::~Win32MapFile() +{ + if (_hFile != INVALID_HANDLE_VALUE) { + Win32MapFile::Close(); + } +} + +BOOL Win32MapFile::_Init( LPCWSTR Path ) +{ + DWORD Flag = PathFileExistsW(Path) ? OPEN_EXISTING : CREATE_ALWAYS; + _hFile = CreateFileW(Path, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE, + NULL, + Flag, + FILE_ATTRIBUTE_NORMAL, + NULL); + if(!_hFile || _hFile == INVALID_HANDLE_VALUE) + return FALSE; + else + return TRUE; +} + +BOOL Win32MapFile::isEnable() +{ + return _hFile ? TRUE : FALSE; +} + +Win32FileLock::Win32FileLock( const std::string& fname ) : + _hFile(NULL),_filename(fname) +{ + std::wstring path; + ToWidePath(fname, path); + _Init(path.c_str()); +} + +Win32FileLock::~Win32FileLock() +{ + _CleanUp(); +} + +BOOL Win32FileLock::_Init( LPCWSTR path ) +{ + BOOL bRet = FALSE; + if(!_hFile) + _hFile = ::CreateFileW(path,0,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); + if(!_hFile || _hFile == INVALID_HANDLE_VALUE ){ + _hFile = NULL; + } + else + bRet = TRUE; + return bRet; +} + +void Win32FileLock::_CleanUp() +{ + ::CloseHandle(_hFile); + _hFile = NULL; +} + +BOOL Win32FileLock::isEnable() +{ + return _hFile ? TRUE : FALSE; +} + +Win32Logger::Win32Logger(WritableFile* pFile) : _pFileProxy(pFile) +{ + assert(_pFileProxy); +} + +Win32Logger::~Win32Logger() +{ + if(_pFileProxy) + delete _pFileProxy; +} + +void Win32Logger::Logv( const char* format, va_list ap ) +{ + uint64_t thread_id = ::GetCurrentThreadId(); + + // We try twice: the first time with a fixed-size stack allocated buffer, + // and the second time with a much larger dynamically allocated buffer. + char buffer[500]; + for (int iter = 0; iter < 2; iter++) { + char* base; + int bufsize; + if (iter == 0) { + bufsize = sizeof(buffer); + base = buffer; + } else { + bufsize = 30000; + base = new char[bufsize]; + } + char* p = base; + char* limit = base + bufsize; + + SYSTEMTIME st; + GetLocalTime(&st); + p += snprintf(p, limit - p, + "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ", + int(st.wYear), + int(st.wMonth), + int(st.wDay), + int(st.wHour), + int(st.wMinute), + int(st.wMinute), + int(st.wMilliseconds), + static_cast(thread_id)); + + // Print the message + if (p < limit) { + va_list backup_ap; + va_copy(backup_ap, ap); + p += vsnprintf(p, limit - p, format, backup_ap); + va_end(backup_ap); + } + + // Truncate to available space if necessary + if (p >= limit) { + if (iter == 0) { + continue; // Try again with larger buffer + } else { + p = limit - 1; + } + } + + // Add newline if necessary + if (p == base || p[-1] != '\n') { + *p++ = '\n'; + } + + assert(p <= limit); + DWORD hasWritten = 0; + if(_pFileProxy){ + _pFileProxy->Append(Slice(base, p - base)); + _pFileProxy->Flush(); + } + if (base != buffer) { + delete[] base; + } + break; + } +} + +bool Win32Env::FileExists(const std::string& fname) +{ + std::string path = fname; + std::wstring wpath; + ToWidePath(ModifyPath(path), wpath); + return ::PathFileExistsW(wpath.c_str()) ? true : false; +} + +Status Win32Env::GetChildren(const std::string& dir, std::vector* result) +{ + Status sRet; + ::WIN32_FIND_DATAW wfd; + std::string path = dir; + ModifyPath(path); + path += "\\*.*"; + std::wstring wpath; + ToWidePath(path, wpath); + + ::HANDLE hFind = ::FindFirstFileW(wpath.c_str() ,&wfd); + if(hFind && hFind != INVALID_HANDLE_VALUE){ + BOOL hasNext = TRUE; + std::string child; + while(hasNext){ + ToNarrowPath(wfd.cFileName, child); + if(child != ".." && child != ".") { + result->push_back(child); + } + hasNext = ::FindNextFileW(hFind,&wfd); + } + ::FindClose(hFind); + } + else + sRet = Status::IOError(dir,"Could not get children."); + return sRet; +} + +void Win32Env::SleepForMicroseconds( int micros ) +{ + ::Sleep((micros + 999) /1000); +} + + +Status Win32Env::DeleteFile( const std::string& fname ) +{ + Status sRet; + std::string path = fname; + std::wstring wpath; + ToWidePath(ModifyPath(path), wpath); + + if(!::DeleteFileW(wpath.c_str())) { + sRet = Status::IOError(path, "Could not delete file."); + } + return sRet; +} + +Status Win32Env::GetFileSize( const std::string& fname, uint64_t* file_size ) +{ + Status sRet; + std::string path = fname; + std::wstring wpath; + ToWidePath(ModifyPath(path), wpath); + + HANDLE file = ::CreateFileW(wpath.c_str(), + GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); + LARGE_INTEGER li; + if(::GetFileSizeEx(file,&li)){ + *file_size = (uint64_t)li.QuadPart; + }else + sRet = Status::IOError(path,"Could not get the file size."); + CloseHandle(file); + return sRet; +} + +Status Win32Env::RenameFile( const std::string& src, const std::string& target ) +{ + Status sRet; + std::string src_path = src; + std::wstring wsrc_path; + ToWidePath(ModifyPath(src_path), wsrc_path); + std::string target_path = target; + std::wstring wtarget_path; + ToWidePath(ModifyPath(target_path), wtarget_path); + + if(!MoveFileW(wsrc_path.c_str(), wtarget_path.c_str() ) ){ + DWORD err = GetLastError(); + if(err == 0x000000b7){ + if(!::DeleteFileW(wtarget_path.c_str() ) ) + sRet = Status::IOError(src, "Could not rename file."); + else if(!::MoveFileW(wsrc_path.c_str(), + wtarget_path.c_str() ) ) + sRet = Status::IOError(src, "Could not rename file."); + } + } + return sRet; +} + +Status Win32Env::LockFile( const std::string& fname, FileLock** lock ) +{ + Status sRet; + std::string path = fname; + ModifyPath(path); + Win32FileLock* _lock = new Win32FileLock(path); + if(!_lock->isEnable()){ + delete _lock; + *lock = NULL; + sRet = Status::IOError(path, "Could not lock file."); + } + else + *lock = _lock; + return sRet; +} + +Status Win32Env::UnlockFile( FileLock* lock ) +{ + Status sRet; + delete lock; + return sRet; +} + +void Win32Env::Schedule( void (*function)(void* arg), void* arg ) +{ + QueueUserWorkItem(Win32::WorkItemWrapperProc, + new Win32::WorkItemWrapper(function,arg), + WT_EXECUTEDEFAULT); +} + +void Win32Env::StartThread( void (*function)(void* arg), void* arg ) +{ + ::_beginthread(function,0,arg); +} + +Status Win32Env::GetTestDirectory( std::string* path ) +{ + Status sRet; + WCHAR TempPath[MAX_PATH]; + ::GetTempPathW(MAX_PATH,TempPath); + ToNarrowPath(TempPath, *path); + path->append("leveldb\\test\\"); + ModifyPath(*path); + return sRet; +} + +uint64_t Win32Env::NowMicros() +{ +#ifndef USE_VISTA_API +#define GetTickCount64 GetTickCount +#endif + return (uint64_t)(GetTickCount64()*1000); +} + +static Status CreateDirInner( const std::string& dirname ) +{ + Status sRet; + DWORD attr = ::GetFileAttributes(dirname.c_str()); + if (attr == INVALID_FILE_ATTRIBUTES) { // doesn't exist: + std::size_t slash = dirname.find_last_of("\\"); + if (slash != std::string::npos){ + sRet = CreateDirInner(dirname.substr(0, slash)); + if (!sRet.ok()) return sRet; + } + BOOL result = ::CreateDirectory(dirname.c_str(), NULL); + if (result == FALSE) { + sRet = Status::IOError(dirname, "Could not create directory."); + return sRet; + } + } + return sRet; +} + +Status Win32Env::CreateDir( const std::string& dirname ) +{ + std::string path = dirname; + if(path[path.length() - 1] != '\\'){ + path += '\\'; + } + ModifyPath(path); + + return CreateDirInner(path); +} + +Status Win32Env::DeleteDir( const std::string& dirname ) +{ + Status sRet; + std::wstring path; + ToWidePath(dirname, path); + ModifyPath(path); + if(!::RemoveDirectoryW( path.c_str() ) ){ + sRet = Status::IOError(dirname, "Could not delete directory."); + } + return sRet; +} + +Status Win32Env::NewSequentialFile( const std::string& fname, SequentialFile** result ) +{ + Status sRet; + std::string path = fname; + ModifyPath(path); + Win32SequentialFile* pFile = new Win32SequentialFile(path); + if(pFile->isEnable()){ + *result = pFile; + }else { + delete pFile; + sRet = Status::IOError(path, Win32::GetLastErrSz()); + } + return sRet; +} + +Status Win32Env::NewRandomAccessFile( const std::string& fname, RandomAccessFile** result ) +{ + Status sRet; + std::string path = fname; + Win32RandomAccessFile* pFile = new Win32RandomAccessFile(ModifyPath(path)); + if(!pFile->isEnable()){ + delete pFile; + *result = NULL; + sRet = Status::IOError(path, Win32::GetLastErrSz()); + }else + *result = pFile; + return sRet; +} + +Status Win32Env::NewLogger( const std::string& fname, Logger** result ) +{ + Status sRet; + std::string path = fname; + Win32MapFile* pMapFile = new Win32MapFile(ModifyPath(path)); + if(!pMapFile->isEnable()){ + delete pMapFile; + *result = NULL; + sRet = Status::IOError(path,"could not create a logger."); + }else + *result = new Win32Logger(pMapFile); + return sRet; +} + +Status Win32Env::NewWritableFile( const std::string& fname, WritableFile** result ) +{ + Status sRet; + std::string path = fname; + Win32MapFile* pFile = new Win32MapFile(ModifyPath(path)); + if(!pFile->isEnable()){ + *result = NULL; + sRet = Status::IOError(fname,Win32::GetLastErrSz()); + }else + *result = pFile; + return sRet; +} + +Win32Env::Win32Env() +{ + +} + +Win32Env::~Win32Env() +{ + +} + + +} // Win32 namespace + +static port::OnceType once = LEVELDB_ONCE_INIT; +static Env* default_env; +static void InitDefaultEnv() { default_env = new Win32::Win32Env(); } + +Env* Env::Default() { + port::InitOnce(&once, InitDefaultEnv); + return default_env; +} + +} // namespace leveldb + +#endif // defined(LEVELDB_PLATFORM_WINDOWS) diff --git a/src/leveldb-lin/util/env_win.o b/src/leveldb-lin/util/env_win.o new file mode 100644 index 0000000..d4f04db Binary files /dev/null and b/src/leveldb-lin/util/env_win.o differ diff --git a/src/leveldb-lin/util/filter_policy.cc b/src/leveldb-lin/util/filter_policy.cc new file mode 100644 index 0000000..7b045c8 --- /dev/null +++ b/src/leveldb-lin/util/filter_policy.cc @@ -0,0 +1,11 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/filter_policy.h" + +namespace leveldb { + +FilterPolicy::~FilterPolicy() { } + +} // namespace leveldb diff --git a/src/leveldb-lin/util/filter_policy.o b/src/leveldb-lin/util/filter_policy.o new file mode 100644 index 0000000..089ac35 Binary files /dev/null and b/src/leveldb-lin/util/filter_policy.o differ diff --git a/src/leveldb-lin/util/hash.cc b/src/leveldb-lin/util/hash.cc new file mode 100644 index 0000000..ba18180 --- /dev/null +++ b/src/leveldb-lin/util/hash.cc @@ -0,0 +1,45 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include "util/coding.h" +#include "util/hash.h" + +namespace leveldb { + +uint32_t Hash(const char* data, size_t n, uint32_t seed) { + // Similar to murmur hash + const uint32_t m = 0xc6a4a793; + const uint32_t r = 24; + const char* limit = data + n; + uint32_t h = seed ^ (n * m); + + // Pick up four bytes at a time + while (data + 4 <= limit) { + uint32_t w = DecodeFixed32(data); + data += 4; + h += w; + h *= m; + h ^= (h >> 16); + } + + // Pick up remaining bytes + switch (limit - data) { + case 3: + h += data[2] << 16; + // fall through + case 2: + h += data[1] << 8; + // fall through + case 1: + h += data[0]; + h *= m; + h ^= (h >> r); + break; + } + return h; +} + + +} // namespace leveldb diff --git a/src/leveldb-lin/util/hash.h b/src/leveldb-lin/util/hash.h new file mode 100644 index 0000000..8889d56 --- /dev/null +++ b/src/leveldb-lin/util/hash.h @@ -0,0 +1,19 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Simple hash function used for internal data structures + +#ifndef STORAGE_LEVELDB_UTIL_HASH_H_ +#define STORAGE_LEVELDB_UTIL_HASH_H_ + +#include +#include + +namespace leveldb { + +extern uint32_t Hash(const char* data, size_t n, uint32_t seed); + +} + +#endif // STORAGE_LEVELDB_UTIL_HASH_H_ diff --git a/src/leveldb-lin/util/hash.o b/src/leveldb-lin/util/hash.o new file mode 100644 index 0000000..8ed4bab Binary files /dev/null and b/src/leveldb-lin/util/hash.o differ diff --git a/src/leveldb-lin/util/histogram.cc b/src/leveldb-lin/util/histogram.cc new file mode 100644 index 0000000..bb95f58 --- /dev/null +++ b/src/leveldb-lin/util/histogram.cc @@ -0,0 +1,139 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include "port/port.h" +#include "util/histogram.h" + +namespace leveldb { + +const double Histogram::kBucketLimit[kNumBuckets] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20, 25, 30, 35, 40, 45, + 50, 60, 70, 80, 90, 100, 120, 140, 160, 180, 200, 250, 300, 350, 400, 450, + 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000, 2500, 3000, + 3500, 4000, 4500, 5000, 6000, 7000, 8000, 9000, 10000, 12000, 14000, + 16000, 18000, 20000, 25000, 30000, 35000, 40000, 45000, 50000, 60000, + 70000, 80000, 90000, 100000, 120000, 140000, 160000, 180000, 200000, + 250000, 300000, 350000, 400000, 450000, 500000, 600000, 700000, 800000, + 900000, 1000000, 1200000, 1400000, 1600000, 1800000, 2000000, 2500000, + 3000000, 3500000, 4000000, 4500000, 5000000, 6000000, 7000000, 8000000, + 9000000, 10000000, 12000000, 14000000, 16000000, 18000000, 20000000, + 25000000, 30000000, 35000000, 40000000, 45000000, 50000000, 60000000, + 70000000, 80000000, 90000000, 100000000, 120000000, 140000000, 160000000, + 180000000, 200000000, 250000000, 300000000, 350000000, 400000000, + 450000000, 500000000, 600000000, 700000000, 800000000, 900000000, + 1000000000, 1200000000, 1400000000, 1600000000, 1800000000, 2000000000, + 2500000000.0, 3000000000.0, 3500000000.0, 4000000000.0, 4500000000.0, + 5000000000.0, 6000000000.0, 7000000000.0, 8000000000.0, 9000000000.0, + 1e200, +}; + +void Histogram::Clear() { + min_ = kBucketLimit[kNumBuckets-1]; + max_ = 0; + num_ = 0; + sum_ = 0; + sum_squares_ = 0; + for (int i = 0; i < kNumBuckets; i++) { + buckets_[i] = 0; + } +} + +void Histogram::Add(double value) { + // Linear search is fast enough for our usage in db_bench + int b = 0; + while (b < kNumBuckets - 1 && kBucketLimit[b] <= value) { + b++; + } + buckets_[b] += 1.0; + if (min_ > value) min_ = value; + if (max_ < value) max_ = value; + num_++; + sum_ += value; + sum_squares_ += (value * value); +} + +void Histogram::Merge(const Histogram& other) { + if (other.min_ < min_) min_ = other.min_; + if (other.max_ > max_) max_ = other.max_; + num_ += other.num_; + sum_ += other.sum_; + sum_squares_ += other.sum_squares_; + for (int b = 0; b < kNumBuckets; b++) { + buckets_[b] += other.buckets_[b]; + } +} + +double Histogram::Median() const { + return Percentile(50.0); +} + +double Histogram::Percentile(double p) const { + double threshold = num_ * (p / 100.0); + double sum = 0; + for (int b = 0; b < kNumBuckets; b++) { + sum += buckets_[b]; + if (sum >= threshold) { + // Scale linearly within this bucket + double left_point = (b == 0) ? 0 : kBucketLimit[b-1]; + double right_point = kBucketLimit[b]; + double left_sum = sum - buckets_[b]; + double right_sum = sum; + double pos = (threshold - left_sum) / (right_sum - left_sum); + double r = left_point + (right_point - left_point) * pos; + if (r < min_) r = min_; + if (r > max_) r = max_; + return r; + } + } + return max_; +} + +double Histogram::Average() const { + if (num_ == 0.0) return 0; + return sum_ / num_; +} + +double Histogram::StandardDeviation() const { + if (num_ == 0.0) return 0; + double variance = (sum_squares_ * num_ - sum_ * sum_) / (num_ * num_); + return sqrt(variance); +} + +std::string Histogram::ToString() const { + std::string r; + char buf[200]; + snprintf(buf, sizeof(buf), + "Count: %.0f Average: %.4f StdDev: %.2f\n", + num_, Average(), StandardDeviation()); + r.append(buf); + snprintf(buf, sizeof(buf), + "Min: %.4f Median: %.4f Max: %.4f\n", + (num_ == 0.0 ? 0.0 : min_), Median(), max_); + r.append(buf); + r.append("------------------------------------------------------\n"); + const double mult = 100.0 / num_; + double sum = 0; + for (int b = 0; b < kNumBuckets; b++) { + if (buckets_[b] <= 0.0) continue; + sum += buckets_[b]; + snprintf(buf, sizeof(buf), + "[ %7.0f, %7.0f ) %7.0f %7.3f%% %7.3f%% ", + ((b == 0) ? 0.0 : kBucketLimit[b-1]), // left + kBucketLimit[b], // right + buckets_[b], // count + mult * buckets_[b], // percentage + mult * sum); // cumulative percentage + r.append(buf); + + // Add hash marks based on percentage; 20 marks for 100%. + int marks = static_cast(20*(buckets_[b] / num_) + 0.5); + r.append(marks, '#'); + r.push_back('\n'); + } + return r; +} + +} // namespace leveldb diff --git a/src/leveldb-lin/util/histogram.h b/src/leveldb-lin/util/histogram.h new file mode 100644 index 0000000..1ef9f3c --- /dev/null +++ b/src/leveldb-lin/util/histogram.h @@ -0,0 +1,42 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_HISTOGRAM_H_ +#define STORAGE_LEVELDB_UTIL_HISTOGRAM_H_ + +#include + +namespace leveldb { + +class Histogram { + public: + Histogram() { } + ~Histogram() { } + + void Clear(); + void Add(double value); + void Merge(const Histogram& other); + + std::string ToString() const; + + private: + double min_; + double max_; + double num_; + double sum_; + double sum_squares_; + + enum { kNumBuckets = 154 }; + static const double kBucketLimit[kNumBuckets]; + double buckets_[kNumBuckets]; + + double Median() const; + double Percentile(double p) const; + double Average() const; + double StandardDeviation() const; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_HISTOGRAM_H_ diff --git a/src/leveldb-lin/util/histogram.o b/src/leveldb-lin/util/histogram.o new file mode 100644 index 0000000..b933cf1 Binary files /dev/null and b/src/leveldb-lin/util/histogram.o differ diff --git a/src/leveldb-lin/util/logging.cc b/src/leveldb-lin/util/logging.cc new file mode 100644 index 0000000..22cf278 --- /dev/null +++ b/src/leveldb-lin/util/logging.cc @@ -0,0 +1,81 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/logging.h" + +#include +#include +#include +#include +#include "leveldb/env.h" +#include "leveldb/slice.h" + +namespace leveldb { + +void AppendNumberTo(std::string* str, uint64_t num) { + char buf[30]; + snprintf(buf, sizeof(buf), "%llu", (unsigned long long) num); + str->append(buf); +} + +void AppendEscapedStringTo(std::string* str, const Slice& value) { + for (size_t i = 0; i < value.size(); i++) { + char c = value[i]; + if (c >= ' ' && c <= '~') { + str->push_back(c); + } else { + char buf[10]; + snprintf(buf, sizeof(buf), "\\x%02x", + static_cast(c) & 0xff); + str->append(buf); + } + } +} + +std::string NumberToString(uint64_t num) { + std::string r; + AppendNumberTo(&r, num); + return r; +} + +std::string EscapeString(const Slice& value) { + std::string r; + AppendEscapedStringTo(&r, value); + return r; +} + +bool ConsumeChar(Slice* in, char c) { + if (!in->empty() && (*in)[0] == c) { + in->remove_prefix(1); + return true; + } else { + return false; + } +} + +bool ConsumeDecimalNumber(Slice* in, uint64_t* val) { + uint64_t v = 0; + int digits = 0; + while (!in->empty()) { + char c = (*in)[0]; + if (c >= '0' && c <= '9') { + ++digits; + const int delta = (c - '0'); + static const uint64_t kMaxUint64 = ~static_cast(0); + if (v > kMaxUint64/10 || + (v == kMaxUint64/10 && delta > kMaxUint64%10)) { + // Overflow + return false; + } + v = (v * 10) + delta; + in->remove_prefix(1); + } else { + break; + } + } + *val = v; + return (digits > 0); +} + +} // namespace leveldb diff --git a/src/leveldb-lin/util/logging.h b/src/leveldb-lin/util/logging.h new file mode 100644 index 0000000..b0c5da8 --- /dev/null +++ b/src/leveldb-lin/util/logging.h @@ -0,0 +1,47 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Must not be included from any .h files to avoid polluting the namespace +// with macros. + +#ifndef STORAGE_LEVELDB_UTIL_LOGGING_H_ +#define STORAGE_LEVELDB_UTIL_LOGGING_H_ + +#include +#include +#include +#include "port/port.h" + +namespace leveldb { + +class Slice; +class WritableFile; + +// Append a human-readable printout of "num" to *str +extern void AppendNumberTo(std::string* str, uint64_t num); + +// Append a human-readable printout of "value" to *str. +// Escapes any non-printable characters found in "value". +extern void AppendEscapedStringTo(std::string* str, const Slice& value); + +// Return a human-readable printout of "num" +extern std::string NumberToString(uint64_t num); + +// Return a human-readable version of "value". +// Escapes any non-printable characters found in "value". +extern std::string EscapeString(const Slice& value); + +// If *in starts with "c", advances *in past the first character and +// returns true. Otherwise, returns false. +extern bool ConsumeChar(Slice* in, char c); + +// Parse a human-readable number from "*in" into *value. On success, +// advances "*in" past the consumed number and sets "*val" to the +// numeric value. Otherwise, returns false and leaves *in in an +// unspecified state. +extern bool ConsumeDecimalNumber(Slice* in, uint64_t* val); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_LOGGING_H_ diff --git a/src/leveldb-lin/util/logging.o b/src/leveldb-lin/util/logging.o new file mode 100644 index 0000000..0d8a4b1 Binary files /dev/null and b/src/leveldb-lin/util/logging.o differ diff --git a/src/leveldb-lin/util/mutexlock.h b/src/leveldb-lin/util/mutexlock.h new file mode 100644 index 0000000..1ff5a9e --- /dev/null +++ b/src/leveldb-lin/util/mutexlock.h @@ -0,0 +1,41 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_ +#define STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_ + +#include "port/port.h" +#include "port/thread_annotations.h" + +namespace leveldb { + +// Helper class that locks a mutex on construction and unlocks the mutex when +// the destructor of the MutexLock object is invoked. +// +// Typical usage: +// +// void MyClass::MyMethod() { +// MutexLock l(&mu_); // mu_ is an instance variable +// ... some complex code, possibly with multiple return paths ... +// } + +class SCOPED_LOCKABLE MutexLock { + public: + explicit MutexLock(port::Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu) + : mu_(mu) { + this->mu_->Lock(); + } + ~MutexLock() UNLOCK_FUNCTION() { this->mu_->Unlock(); } + + private: + port::Mutex *const mu_; + // No copying allowed + MutexLock(const MutexLock&); + void operator=(const MutexLock&); +}; + +} // namespace leveldb + + +#endif // STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_ diff --git a/src/leveldb-lin/util/options.cc b/src/leveldb-lin/util/options.cc new file mode 100644 index 0000000..76af5b9 --- /dev/null +++ b/src/leveldb-lin/util/options.cc @@ -0,0 +1,29 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/options.h" + +#include "leveldb/comparator.h" +#include "leveldb/env.h" + +namespace leveldb { + +Options::Options() + : comparator(BytewiseComparator()), + create_if_missing(false), + error_if_exists(false), + paranoid_checks(false), + env(Env::Default()), + info_log(NULL), + write_buffer_size(4<<20), + max_open_files(1000), + block_cache(NULL), + block_size(4096), + block_restart_interval(16), + compression(kSnappyCompression), + filter_policy(NULL) { +} + + +} // namespace leveldb diff --git a/src/leveldb-lin/util/options.o b/src/leveldb-lin/util/options.o new file mode 100644 index 0000000..81318ea Binary files /dev/null and b/src/leveldb-lin/util/options.o differ diff --git a/src/leveldb-lin/util/posix_logger.h b/src/leveldb-lin/util/posix_logger.h new file mode 100644 index 0000000..c063c2b --- /dev/null +++ b/src/leveldb-lin/util/posix_logger.h @@ -0,0 +1,98 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Logger implementation that can be shared by all environments +// where enough Posix functionality is available. + +#ifndef STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ +#define STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ + +#include +#include +#include +#include +#include "leveldb/env.h" + +namespace leveldb { + +class PosixLogger : public Logger { + private: + FILE* file_; + uint64_t (*gettid_)(); // Return the thread id for the current thread + public: + PosixLogger(FILE* f, uint64_t (*gettid)()) : file_(f), gettid_(gettid) { } + virtual ~PosixLogger() { + fclose(file_); + } + virtual void Logv(const char* format, va_list ap) { + const uint64_t thread_id = (*gettid_)(); + + // We try twice: the first time with a fixed-size stack allocated buffer, + // and the second time with a much larger dynamically allocated buffer. + char buffer[500]; + for (int iter = 0; iter < 2; iter++) { + char* base; + int bufsize; + if (iter == 0) { + bufsize = sizeof(buffer); + base = buffer; + } else { + bufsize = 30000; + base = new char[bufsize]; + } + char* p = base; + char* limit = base + bufsize; + + struct timeval now_tv; + gettimeofday(&now_tv, NULL); + const time_t seconds = now_tv.tv_sec; + struct tm t; + localtime_r(&seconds, &t); + p += snprintf(p, limit - p, + "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ", + t.tm_year + 1900, + t.tm_mon + 1, + t.tm_mday, + t.tm_hour, + t.tm_min, + t.tm_sec, + static_cast(now_tv.tv_usec), + static_cast(thread_id)); + + // Print the message + if (p < limit) { + va_list backup_ap; + va_copy(backup_ap, ap); + p += vsnprintf(p, limit - p, format, backup_ap); + va_end(backup_ap); + } + + // Truncate to available space if necessary + if (p >= limit) { + if (iter == 0) { + continue; // Try again with larger buffer + } else { + p = limit - 1; + } + } + + // Add newline if necessary + if (p == base || p[-1] != '\n') { + *p++ = '\n'; + } + + assert(p <= limit); + fwrite(base, 1, p - base, file_); + fflush(file_); + if (base != buffer) { + delete[] base; + } + break; + } + } +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ diff --git a/src/leveldb-lin/util/random.h b/src/leveldb-lin/util/random.h new file mode 100644 index 0000000..0753824 --- /dev/null +++ b/src/leveldb-lin/util/random.h @@ -0,0 +1,59 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_RANDOM_H_ +#define STORAGE_LEVELDB_UTIL_RANDOM_H_ + +#include + +namespace leveldb { + +// A very simple random number generator. Not especially good at +// generating truly random bits, but good enough for our needs in this +// package. +class Random { + private: + uint32_t seed_; + public: + explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) { } + uint32_t Next() { + static const uint32_t M = 2147483647L; // 2^31-1 + static const uint64_t A = 16807; // bits 14, 8, 7, 5, 2, 1, 0 + // We are computing + // seed_ = (seed_ * A) % M, where M = 2^31-1 + // + // seed_ must not be zero or M, or else all subsequent computed values + // will be zero or M respectively. For all other values, seed_ will end + // up cycling through every number in [1,M-1] + uint64_t product = seed_ * A; + + // Compute (product % M) using the fact that ((x << 31) % M) == x. + seed_ = static_cast((product >> 31) + (product & M)); + // The first reduction may overflow by 1 bit, so we may need to + // repeat. mod == M is not possible; using > allows the faster + // sign-bit-based test. + if (seed_ > M) { + seed_ -= M; + } + return seed_; + } + // Returns a uniformly distributed value in the range [0..n-1] + // REQUIRES: n > 0 + uint32_t Uniform(int n) { return Next() % n; } + + // Randomly returns true ~"1/n" of the time, and false otherwise. + // REQUIRES: n > 0 + bool OneIn(int n) { return (Next() % n) == 0; } + + // Skewed: pick "base" uniformly from range [0,max_log] and then + // return "base" random bits. The effect is to pick a number in the + // range [0,2^max_log-1] with exponential bias towards smaller numbers. + uint32_t Skewed(int max_log) { + return Uniform(1 << Uniform(max_log + 1)); + } +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_RANDOM_H_ diff --git a/src/leveldb-lin/util/status.cc b/src/leveldb-lin/util/status.cc new file mode 100644 index 0000000..a44f35b --- /dev/null +++ b/src/leveldb-lin/util/status.cc @@ -0,0 +1,75 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include "port/port.h" +#include "leveldb/status.h" + +namespace leveldb { + +const char* Status::CopyState(const char* state) { + uint32_t size; + memcpy(&size, state, sizeof(size)); + char* result = new char[size + 5]; + memcpy(result, state, size + 5); + return result; +} + +Status::Status(Code code, const Slice& msg, const Slice& msg2) { + assert(code != kOk); + const uint32_t len1 = msg.size(); + const uint32_t len2 = msg2.size(); + const uint32_t size = len1 + (len2 ? (2 + len2) : 0); + char* result = new char[size + 5]; + memcpy(result, &size, sizeof(size)); + result[4] = static_cast(code); + memcpy(result + 5, msg.data(), len1); + if (len2) { + result[5 + len1] = ':'; + result[6 + len1] = ' '; + memcpy(result + 7 + len1, msg2.data(), len2); + } + state_ = result; +} + +std::string Status::ToString() const { + if (state_ == NULL) { + return "OK"; + } else { + char tmp[30]; + const char* type; + switch (code()) { + case kOk: + type = "OK"; + break; + case kNotFound: + type = "NotFound: "; + break; + case kCorruption: + type = "Corruption: "; + break; + case kNotSupported: + type = "Not implemented: "; + break; + case kInvalidArgument: + type = "Invalid argument: "; + break; + case kIOError: + type = "IO error: "; + break; + default: + snprintf(tmp, sizeof(tmp), "Unknown code(%d): ", + static_cast(code())); + type = tmp; + break; + } + std::string result(type); + uint32_t length; + memcpy(&length, state_, sizeof(length)); + result.append(state_ + 5, length); + return result; + } +} + +} // namespace leveldb diff --git a/src/leveldb-lin/util/status.o b/src/leveldb-lin/util/status.o new file mode 100644 index 0000000..7d5901d Binary files /dev/null and b/src/leveldb-lin/util/status.o differ diff --git a/src/leveldb-lin/util/testharness.cc b/src/leveldb-lin/util/testharness.cc new file mode 100644 index 0000000..eb1bdd5 --- /dev/null +++ b/src/leveldb-lin/util/testharness.cc @@ -0,0 +1,77 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/testharness.h" + +#include +#include +#include +#include + +namespace leveldb { +namespace test { + +namespace { +struct Test { + const char* base; + const char* name; + void (*func)(); +}; +std::vector* tests; +} + +bool RegisterTest(const char* base, const char* name, void (*func)()) { + if (tests == NULL) { + tests = new std::vector; + } + Test t; + t.base = base; + t.name = name; + t.func = func; + tests->push_back(t); + return true; +} + +int RunAllTests() { + const char* matcher = getenv("LEVELDB_TESTS"); + + int num = 0; + if (tests != NULL) { + for (int i = 0; i < tests->size(); i++) { + const Test& t = (*tests)[i]; + if (matcher != NULL) { + std::string name = t.base; + name.push_back('.'); + name.append(t.name); + if (strstr(name.c_str(), matcher) == NULL) { + continue; + } + } + fprintf(stderr, "==== Test %s.%s\n", t.base, t.name); + (*t.func)(); + ++num; + } + } + fprintf(stderr, "==== PASSED %d tests\n", num); + return 0; +} + +std::string TmpDir() { + std::string dir; + Status s = Env::Default()->GetTestDirectory(&dir); + ASSERT_TRUE(s.ok()) << s.ToString(); + return dir; +} + +int RandomSeed() { + const char* env = getenv("TEST_RANDOM_SEED"); + int result = (env != NULL ? atoi(env) : 301); + if (result <= 0) { + result = 301; + } + return result; +} + +} // namespace test +} // namespace leveldb diff --git a/src/leveldb-lin/util/testharness.h b/src/leveldb-lin/util/testharness.h new file mode 100644 index 0000000..da4fe68 --- /dev/null +++ b/src/leveldb-lin/util/testharness.h @@ -0,0 +1,138 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_TESTHARNESS_H_ +#define STORAGE_LEVELDB_UTIL_TESTHARNESS_H_ + +#include +#include +#include +#include "leveldb/env.h" +#include "leveldb/slice.h" +#include "util/random.h" + +namespace leveldb { +namespace test { + +// Run some of the tests registered by the TEST() macro. If the +// environment variable "LEVELDB_TESTS" is not set, runs all tests. +// Otherwise, runs only the tests whose name contains the value of +// "LEVELDB_TESTS" as a substring. E.g., suppose the tests are: +// TEST(Foo, Hello) { ... } +// TEST(Foo, World) { ... } +// LEVELDB_TESTS=Hello will run the first test +// LEVELDB_TESTS=o will run both tests +// LEVELDB_TESTS=Junk will run no tests +// +// Returns 0 if all tests pass. +// Dies or returns a non-zero value if some test fails. +extern int RunAllTests(); + +// Return the directory to use for temporary storage. +extern std::string TmpDir(); + +// Return a randomization seed for this run. Typically returns the +// same number on repeated invocations of this binary, but automated +// runs may be able to vary the seed. +extern int RandomSeed(); + +// An instance of Tester is allocated to hold temporary state during +// the execution of an assertion. +class Tester { + private: + bool ok_; + const char* fname_; + int line_; + std::stringstream ss_; + + public: + Tester(const char* f, int l) + : ok_(true), fname_(f), line_(l) { + } + + ~Tester() { + if (!ok_) { + fprintf(stderr, "%s:%d:%s\n", fname_, line_, ss_.str().c_str()); + exit(1); + } + } + + Tester& Is(bool b, const char* msg) { + if (!b) { + ss_ << " Assertion failure " << msg; + ok_ = false; + } + return *this; + } + + Tester& IsOk(const Status& s) { + if (!s.ok()) { + ss_ << " " << s.ToString(); + ok_ = false; + } + return *this; + } + +#define BINARY_OP(name,op) \ + template \ + Tester& name(const X& x, const Y& y) { \ + if (! (x op y)) { \ + ss_ << " failed: " << x << (" " #op " ") << y; \ + ok_ = false; \ + } \ + return *this; \ + } + + BINARY_OP(IsEq, ==) + BINARY_OP(IsNe, !=) + BINARY_OP(IsGe, >=) + BINARY_OP(IsGt, >) + BINARY_OP(IsLe, <=) + BINARY_OP(IsLt, <) +#undef BINARY_OP + + // Attach the specified value to the error message if an error has occurred + template + Tester& operator<<(const V& value) { + if (!ok_) { + ss_ << " " << value; + } + return *this; + } +}; + +#define ASSERT_TRUE(c) ::leveldb::test::Tester(__FILE__, __LINE__).Is((c), #c) +#define ASSERT_OK(s) ::leveldb::test::Tester(__FILE__, __LINE__).IsOk((s)) +#define ASSERT_EQ(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsEq((a),(b)) +#define ASSERT_NE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsNe((a),(b)) +#define ASSERT_GE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsGe((a),(b)) +#define ASSERT_GT(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsGt((a),(b)) +#define ASSERT_LE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsLe((a),(b)) +#define ASSERT_LT(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsLt((a),(b)) + +#define TCONCAT(a,b) TCONCAT1(a,b) +#define TCONCAT1(a,b) a##b + +#define TEST(base,name) \ +class TCONCAT(_Test_,name) : public base { \ + public: \ + void _Run(); \ + static void _RunIt() { \ + TCONCAT(_Test_,name) t; \ + t._Run(); \ + } \ +}; \ +bool TCONCAT(_Test_ignored_,name) = \ + ::leveldb::test::RegisterTest(#base, #name, &TCONCAT(_Test_,name)::_RunIt); \ +void TCONCAT(_Test_,name)::_Run() + +// Register the specified test. Typically not used directly, but +// invoked via the macro expansion of TEST. +extern bool RegisterTest(const char* base, const char* name, void (*func)()); + + +} // namespace test +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_TESTHARNESS_H_ diff --git a/src/leveldb-lin/util/testutil.cc b/src/leveldb-lin/util/testutil.cc new file mode 100644 index 0000000..538d095 --- /dev/null +++ b/src/leveldb-lin/util/testutil.cc @@ -0,0 +1,51 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/testutil.h" + +#include "util/random.h" + +namespace leveldb { +namespace test { + +Slice RandomString(Random* rnd, int len, std::string* dst) { + dst->resize(len); + for (int i = 0; i < len; i++) { + (*dst)[i] = static_cast(' ' + rnd->Uniform(95)); // ' ' .. '~' + } + return Slice(*dst); +} + +std::string RandomKey(Random* rnd, int len) { + // Make sure to generate a wide variety of characters so we + // test the boundary conditions for short-key optimizations. + static const char kTestChars[] = { + '\0', '\1', 'a', 'b', 'c', 'd', 'e', '\xfd', '\xfe', '\xff' + }; + std::string result; + for (int i = 0; i < len; i++) { + result += kTestChars[rnd->Uniform(sizeof(kTestChars))]; + } + return result; +} + + +extern Slice CompressibleString(Random* rnd, double compressed_fraction, + int len, std::string* dst) { + int raw = static_cast(len * compressed_fraction); + if (raw < 1) raw = 1; + std::string raw_data; + RandomString(rnd, raw, &raw_data); + + // Duplicate the random data until we have filled "len" bytes + dst->clear(); + while (dst->size() < len) { + dst->append(raw_data); + } + dst->resize(len); + return Slice(*dst); +} + +} // namespace test +} // namespace leveldb diff --git a/src/leveldb-lin/util/testutil.h b/src/leveldb-lin/util/testutil.h new file mode 100644 index 0000000..824e655 --- /dev/null +++ b/src/leveldb-lin/util/testutil.h @@ -0,0 +1,53 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_TESTUTIL_H_ +#define STORAGE_LEVELDB_UTIL_TESTUTIL_H_ + +#include "leveldb/env.h" +#include "leveldb/slice.h" +#include "util/random.h" + +namespace leveldb { +namespace test { + +// Store in *dst a random string of length "len" and return a Slice that +// references the generated data. +extern Slice RandomString(Random* rnd, int len, std::string* dst); + +// Return a random key with the specified length that may contain interesting +// characters (e.g. \x00, \xff, etc.). +extern std::string RandomKey(Random* rnd, int len); + +// Store in *dst a string of length "len" that will compress to +// "N*compressed_fraction" bytes and return a Slice that references +// the generated data. +extern Slice CompressibleString(Random* rnd, double compressed_fraction, + int len, std::string* dst); + +// A wrapper that allows injection of errors. +class ErrorEnv : public EnvWrapper { + public: + bool writable_file_error_; + int num_writable_file_errors_; + + ErrorEnv() : EnvWrapper(Env::Default()), + writable_file_error_(false), + num_writable_file_errors_(0) { } + + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) { + if (writable_file_error_) { + ++num_writable_file_errors_; + *result = NULL; + return Status::IOError(fname, "fake error"); + } + return target()->NewWritableFile(fname, result); + } +}; + +} // namespace test +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_TESTUTIL_H_ diff --git a/src/leveldb.cpp b/src/leveldb.cpp new file mode 100644 index 0000000..e66f851 --- /dev/null +++ b/src/leveldb.cpp @@ -0,0 +1,81 @@ +// Copyright (c) 2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "leveldb.h" +#include "util.h" + +#include +#include +#include +#include + +#include + +void HandleError(const leveldb::Status &status) throw(leveldb_error) { + if (status.ok()) + return; + if (status.IsCorruption()) + throw leveldb_error("Database corrupted"); + if (status.IsIOError()) + throw leveldb_error("Database I/O error"); + if (status.IsNotFound()) + throw leveldb_error("Database entry missing"); + throw leveldb_error("Unknown database error"); +} + +static leveldb::Options GetOptions(size_t nCacheSize) { + leveldb::Options options; + options.block_cache = leveldb::NewLRUCache(nCacheSize / 2); + options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously + options.filter_policy = leveldb::NewBloomFilterPolicy(10); + options.compression = leveldb::kNoCompression; + options.max_open_files = 64; + return options; +} + +CLevelDB::CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory, bool fWipe) { + penv = NULL; + readoptions.verify_checksums = true; + iteroptions.verify_checksums = true; + iteroptions.fill_cache = false; + syncoptions.sync = true; + options = GetOptions(nCacheSize); + options.create_if_missing = true; + if (fMemory) { + penv = leveldb::NewMemEnv(leveldb::Env::Default()); + options.env = penv; + } else { + if (fWipe) { + printf("Wiping LevelDB in %s\n", path.string().c_str()); + leveldb::DestroyDB(path.string(), options); + } + boost::filesystem::create_directory(path); + printf("Opening LevelDB in %s\n", path.string().c_str()); + } + leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb); + if (!status.ok()) + throw std::runtime_error(strprintf("CLevelDB(): error opening database environment %s", status.ToString().c_str())); + printf("Opened LevelDB successfully\n"); +} + +CLevelDB::~CLevelDB() { + delete pdb; + pdb = NULL; + delete options.filter_policy; + options.filter_policy = NULL; + delete options.block_cache; + options.block_cache = NULL; + delete penv; + options.env = NULL; +} + +bool CLevelDB::WriteBatch(CLevelDBBatch &batch, bool fSync) throw(leveldb_error) { + leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch); + if (!status.ok()) { + printf("LevelDB write failure: %s\n", status.ToString().c_str()); + HandleError(status); + return false; + } + return true; +} diff --git a/src/leveldb.h b/src/leveldb.h new file mode 100644 index 0000000..79262ed --- /dev/null +++ b/src/leveldb.h @@ -0,0 +1,153 @@ +// Copyright (c) 2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_LEVELDB_H +#define BITCOIN_LEVELDB_H + +#include "serialize.h" + +#include +#include + +#include + +class leveldb_error : public std::runtime_error +{ +public: + leveldb_error(const std::string &msg) : std::runtime_error(msg) {} +}; + +void HandleError(const leveldb::Status &status) throw(leveldb_error); + +// Batch of changes queued to be written to a CLevelDB +class CLevelDBBatch +{ + friend class CLevelDB; + +private: + leveldb::WriteBatch batch; + +public: + template void Write(const K& key, const V& value) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(ssKey.GetSerializeSize(key)); + ssKey << key; + leveldb::Slice slKey(&ssKey[0], ssKey.size()); + + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + ssValue.reserve(ssValue.GetSerializeSize(value)); + ssValue << value; + leveldb::Slice slValue(&ssValue[0], ssValue.size()); + + batch.Put(slKey, slValue); + } + + template void Erase(const K& key) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(ssKey.GetSerializeSize(key)); + ssKey << key; + leveldb::Slice slKey(&ssKey[0], ssKey.size()); + + batch.Delete(slKey); + } +}; + +class CLevelDB +{ +private: + // custom environment this database is using (may be NULL in case of default environment) + leveldb::Env *penv; + + // database options used + leveldb::Options options; + + // options used when reading from the database + leveldb::ReadOptions readoptions; + + // options used when iterating over values of the database + leveldb::ReadOptions iteroptions; + + // options used when writing to the database + leveldb::WriteOptions writeoptions; + + // options used when sync writing to the database + leveldb::WriteOptions syncoptions; + + // the database itself + leveldb::DB *pdb; + +public: + CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory = false, bool fWipe = false); + ~CLevelDB(); + + template bool Read(const K& key, V& value) throw(leveldb_error) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(ssKey.GetSerializeSize(key)); + ssKey << key; + leveldb::Slice slKey(&ssKey[0], ssKey.size()); + + std::string strValue; + leveldb::Status status = pdb->Get(readoptions, slKey, &strValue); + if (!status.ok()) { + if (status.IsNotFound()) + return false; + printf("LevelDB read failure: %s\n", status.ToString().c_str()); + HandleError(status); + } + try { + CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION); + ssValue >> value; + } catch(std::exception &e) { + return false; + } + return true; + } + + template bool Write(const K& key, const V& value, bool fSync = false) throw(leveldb_error) { + CLevelDBBatch batch; + batch.Write(key, value); + return WriteBatch(batch, fSync); + } + + template bool Exists(const K& key) throw(leveldb_error) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(ssKey.GetSerializeSize(key)); + ssKey << key; + leveldb::Slice slKey(&ssKey[0], ssKey.size()); + + std::string strValue; + leveldb::Status status = pdb->Get(readoptions, slKey, &strValue); + if (!status.ok()) { + if (status.IsNotFound()) + return false; + printf("LevelDB read failure: %s\n", status.ToString().c_str()); + HandleError(status); + } + return true; + } + + template bool Erase(const K& key, bool fSync = false) throw(leveldb_error) { + CLevelDBBatch batch; + batch.Erase(key); + return WriteBatch(batch, fSync); + } + + bool WriteBatch(CLevelDBBatch &batch, bool fSync = false) throw(leveldb_error); + + // not available for LevelDB; provide for compatibility with BDB + bool Flush() { + return true; + } + + bool Sync() throw(leveldb_error) { + CLevelDBBatch batch; + return WriteBatch(batch, true); + } + + // not exactly clean encapsulation, but it's easiest for now + leveldb::Iterator *NewIterator() { + return pdb->NewIterator(iteroptions); + } +}; + +#endif // BITCOIN_LEVELDB_H diff --git a/src/leveldb/AUTHORS b/src/leveldb/AUTHORS new file mode 100644 index 0000000..27a9407 --- /dev/null +++ b/src/leveldb/AUTHORS @@ -0,0 +1,8 @@ +# Names should be added to this file like so: +# Name or Organization + +Google Inc. + +# Initial version authors: +Jeffrey Dean +Sanjay Ghemawat diff --git a/src/leveldb/LICENSE b/src/leveldb/LICENSE new file mode 100644 index 0000000..8e80208 --- /dev/null +++ b/src/leveldb/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2011 The LevelDB Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/leveldb/Makefile b/src/leveldb/Makefile new file mode 100644 index 0000000..42c4952 --- /dev/null +++ b/src/leveldb/Makefile @@ -0,0 +1,202 @@ +# Copyright (c) 2011 The LevelDB Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. See the AUTHORS file for names of contributors. + +#----------------------------------------------- +# Uncomment exactly one of the lines labelled (A), (B), and (C) below +# to switch between compilation modes. + +OPT ?= -O2 -DNDEBUG # (A) Production use (optimized mode) +# OPT ?= -g2 # (B) Debug mode, w/ full line-level debugging symbols +# OPT ?= -O2 -g2 -DNDEBUG # (C) Profiling mode: opt, but w/debugging symbols +#----------------------------------------------- + +# detect what platform we're building on +$(shell CC=$(CC) CXX=$(CXX) TARGET_OS=$(TARGET_OS) \ + ./build_detect_platform build_config.mk ./) +# this file is generated by the previous line to set build flags and sources +include build_config.mk + +CFLAGS += -I. -I./include $(PLATFORM_CCFLAGS) $(OPT) +CXXFLAGS += -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) + +LDFLAGS += $(PLATFORM_LDFLAGS) +LIBS += $(PLATFORM_LIBS) + +LIBOBJECTS = $(SOURCES:.cc=.o) +MEMENVOBJECTS = $(MEMENV_SOURCES:.cc=.o) + +TESTUTIL = ./util/testutil.o +TESTHARNESS = ./util/testharness.o $(TESTUTIL) + +TESTS = \ + arena_test \ + bloom_test \ + c_test \ + cache_test \ + coding_test \ + corruption_test \ + crc32c_test \ + db_test \ + dbformat_test \ + env_test \ + filename_test \ + filter_block_test \ + log_test \ + memenv_test \ + skiplist_test \ + table_test \ + version_edit_test \ + version_set_test \ + write_batch_test + +PROGRAMS = db_bench leveldbutil $(TESTS) +BENCHMARKS = db_bench_sqlite3 db_bench_tree_db + +LIBRARY = libleveldb.a +MEMENVLIBRARY = libmemenv.a + +default: all + +# Should we build shared libraries? +ifneq ($(PLATFORM_SHARED_EXT),) + +ifneq ($(PLATFORM_SHARED_VERSIONED),true) +SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT) +SHARED2 = $(SHARED1) +SHARED3 = $(SHARED1) +SHARED = $(SHARED1) +else +# Update db.h if you change these. +SHARED_MAJOR = 1 +SHARED_MINOR = 9 +SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT) +SHARED2 = $(SHARED1).$(SHARED_MAJOR) +SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR) +SHARED = $(SHARED1) $(SHARED2) $(SHARED3) +$(SHARED1): $(SHARED3) + ln -fs $(SHARED3) $(SHARED1) +$(SHARED2): $(SHARED3) + ln -fs $(SHARED3) $(SHARED2) +endif + +$(SHARED3): + $(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED2) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SOURCES) -o $(SHARED3) $(LIBS) + +endif # PLATFORM_SHARED_EXT + +all: $(SHARED) $(LIBRARY) + +check: all $(PROGRAMS) $(TESTS) + for t in $(TESTS); do echo "***** Running $$t"; ./$$t || exit 1; done + +clean: + -rm -f $(PROGRAMS) $(BENCHMARKS) $(LIBRARY) $(SHARED) $(MEMENVLIBRARY) */*.o */*/*.o ios-x86/*/*.o ios-arm/*/*.o build_config.mk + -rm -rf ios-x86/* ios-arm/* + +$(LIBRARY): $(LIBOBJECTS) + rm -f $@ + $(AR) -rs $@ $(LIBOBJECTS) + +db_bench: db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) + $(CXX) $(LDFLAGS) db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LIBS) + +db_bench_sqlite3: doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) + $(CXX) $(LDFLAGS) doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) -o $@ -lsqlite3 $(LIBS) + +db_bench_tree_db: doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) + $(CXX) $(LDFLAGS) doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) -o $@ -lkyotocabinet $(LIBS) + +leveldbutil: db/leveldb_main.o $(LIBOBJECTS) + $(CXX) $(LDFLAGS) db/leveldb_main.o $(LIBOBJECTS) -o $@ $(LIBS) + +arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +bloom_test: util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +c_test: db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +cache_test: util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +coding_test: util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +corruption_test: db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +crc32c_test: util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +db_test: db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +dbformat_test: db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +env_test: util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +filter_block_test: table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +table_test: table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +skiplist_test: db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +version_edit_test: db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +version_set_test: db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +write_batch_test: db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(MEMENVLIBRARY) : $(MEMENVOBJECTS) + rm -f $@ + $(AR) -rs $@ $(MEMENVOBJECTS) + +memenv_test : helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) + $(CXX) $(LDFLAGS) helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) -o $@ $(LIBS) + +ifeq ($(PLATFORM), IOS) +# For iOS, create universal object files to be used on both the simulator and +# a device. +PLATFORMSROOT=/Applications/Xcode.app/Contents/Developer/Platforms +SIMULATORROOT=$(PLATFORMSROOT)/iPhoneSimulator.platform/Developer +DEVICEROOT=$(PLATFORMSROOT)/iPhoneOS.platform/Developer +IOSVERSION=$(shell defaults read $(PLATFORMSROOT)/iPhoneOS.platform/version CFBundleShortVersionString) + +.cc.o: + mkdir -p ios-x86/$(dir $@) + $(CXX) $(CXXFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -c $< -o ios-x86/$@ + mkdir -p ios-arm/$(dir $@) + $(DEVICEROOT)/usr/bin/$(CXX) $(CXXFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk -arch armv6 -arch armv7 -c $< -o ios-arm/$@ + lipo ios-x86/$@ ios-arm/$@ -create -output $@ + +.c.o: + mkdir -p ios-x86/$(dir $@) + $(CC) $(CFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -c $< -o ios-x86/$@ + mkdir -p ios-arm/$(dir $@) + $(DEVICEROOT)/usr/bin/$(CC) $(CFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk -arch armv6 -arch armv7 -c $< -o ios-arm/$@ + lipo ios-x86/$@ ios-arm/$@ -create -output $@ + +else +.cc.o: + $(CXX) $(CXXFLAGS) -c $< -o $@ + +.c.o: + $(CC) $(CFLAGS) -c $< -o $@ +endif diff --git a/src/leveldb/NEWS b/src/leveldb/NEWS new file mode 100644 index 0000000..3fd9924 --- /dev/null +++ b/src/leveldb/NEWS @@ -0,0 +1,17 @@ +Release 1.2 2011-05-16 +---------------------- + +Fixes for larger databases (tested up to one billion 100-byte entries, +i.e., ~100GB). + +(1) Place hard limit on number of level-0 files. This fixes errors +of the form "too many open files". + +(2) Fixed memtable management. Before the fix, a heavy write burst +could cause unbounded memory usage. + +A fix for a logging bug where the reader would incorrectly complain +about corruption. + +Allow public access to WriteBatch contents so that users can easily +wrap a DB. diff --git a/src/leveldb/README b/src/leveldb/README new file mode 100644 index 0000000..3618ade --- /dev/null +++ b/src/leveldb/README @@ -0,0 +1,51 @@ +leveldb: A key-value store +Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com) + +The code under this directory implements a system for maintaining a +persistent key/value store. + +See doc/index.html for more explanation. +See doc/impl.html for a brief overview of the implementation. + +The public interface is in include/*.h. Callers should not include or +rely on the details of any other header files in this package. Those +internal APIs may be changed without warning. + +Guide to header files: + +include/db.h + Main interface to the DB: Start here + +include/options.h + Control over the behavior of an entire database, and also + control over the behavior of individual reads and writes. + +include/comparator.h + Abstraction for user-specified comparison function. If you want + just bytewise comparison of keys, you can use the default comparator, + but clients can write their own comparator implementations if they + want custom ordering (e.g. to handle different character + encodings, etc.) + +include/iterator.h + Interface for iterating over data. You can get an iterator + from a DB object. + +include/write_batch.h + Interface for atomically applying multiple updates to a database. + +include/slice.h + A simple module for maintaining a pointer and a length into some + other byte array. + +include/status.h + Status is returned from many of the public interfaces and is used + to report success and various kinds of errors. + +include/env.h + Abstraction of the OS environment. A posix implementation of + this interface is in util/env_posix.cc + +include/table.h +include/table_builder.h + Lower-level modules that most clients probably won't use directly diff --git a/src/leveldb/TODO b/src/leveldb/TODO new file mode 100644 index 0000000..e603c07 --- /dev/null +++ b/src/leveldb/TODO @@ -0,0 +1,14 @@ +ss +- Stats + +db +- Maybe implement DB::BulkDeleteForRange(start_key, end_key) + that would blow away files whose ranges are entirely contained + within [start_key..end_key]? For Chrome, deletion of obsolete + object stores, etc. can be done in the background anyway, so + probably not that important. +- There have been requests for MultiGet. + +After a range is completely deleted, what gets rid of the +corresponding files if we do no future changes to that range. Make +the conditions for triggering compactions fire in more situations? diff --git a/src/leveldb/WINDOWS.md b/src/leveldb/WINDOWS.md new file mode 100644 index 0000000..5b76c24 --- /dev/null +++ b/src/leveldb/WINDOWS.md @@ -0,0 +1,39 @@ +# Building LevelDB On Windows + +## Prereqs + +Install the [Windows Software Development Kit version 7.1](http://www.microsoft.com/downloads/dlx/en-us/listdetailsview.aspx?FamilyID=6b6c21d2-2006-4afa-9702-529fa782d63b). + +Download and extract the [Snappy source distribution](http://snappy.googlecode.com/files/snappy-1.0.5.tar.gz) + +1. Open the "Windows SDK 7.1 Command Prompt" : + Start Menu -> "Microsoft Windows SDK v7.1" > "Windows SDK 7.1 Command Prompt" +2. Change the directory to the leveldb project + +## Building the Static lib + +* 32 bit Version + + setenv /x86 + msbuild.exe /p:Configuration=Release /p:Platform=Win32 /p:Snappy=..\snappy-1.0.5 + +* 64 bit Version + + setenv /x64 + msbuild.exe /p:Configuration=Release /p:Platform=x64 /p:Snappy=..\snappy-1.0.5 + + +## Building and Running the Benchmark app + +* 32 bit Version + + setenv /x86 + msbuild.exe /p:Configuration=Benchmark /p:Platform=Win32 /p:Snappy=..\snappy-1.0.5 + Benchmark\leveldb.exe + +* 64 bit Version + + setenv /x64 + msbuild.exe /p:Configuration=Benchmark /p:Platform=x64 /p:Snappy=..\snappy-1.0.5 + x64\Benchmark\leveldb.exe + diff --git a/src/leveldb/build_detect_platform b/src/leveldb/build_detect_platform new file mode 100644 index 0000000..609cb51 --- /dev/null +++ b/src/leveldb/build_detect_platform @@ -0,0 +1,200 @@ +#!/bin/sh +# +# Detects OS we're compiling on and outputs a file specified by the first +# argument, which in turn gets read while processing Makefile. +# +# The output will set the following variables: +# CC C Compiler path +# CXX C++ Compiler path +# PLATFORM_LDFLAGS Linker flags +# PLATFORM_LIBS Libraries flags +# PLATFORM_SHARED_EXT Extension for shared libraries +# PLATFORM_SHARED_LDFLAGS Flags for building shared library +# This flag is embedded just before the name +# of the shared library without intervening spaces +# PLATFORM_SHARED_CFLAGS Flags for compiling objects for shared library +# PLATFORM_CCFLAGS C compiler flags +# PLATFORM_CXXFLAGS C++ compiler flags. Will contain: +# PLATFORM_SHARED_VERSIONED Set to 'true' if platform supports versioned +# shared libraries, empty otherwise. +# +# The PLATFORM_CCFLAGS and PLATFORM_CXXFLAGS might include the following: +# +# -DLEVELDB_CSTDATOMIC_PRESENT if is present +# -DLEVELDB_PLATFORM_POSIX for Posix-based platforms +# -DSNAPPY if the Snappy library is present +# + +OUTPUT=$1 +PREFIX=$2 +if test -z "$OUTPUT" || test -z "$PREFIX"; then + echo "usage: $0 " >&2 + exit 1 +fi + +# Delete existing output, if it exists +rm -f $OUTPUT +touch $OUTPUT + +if test -z "$CC"; then + CC=cc +fi + +if test -z "$CXX"; then + CXX=g++ +fi + +# Detect OS +if test -z "$TARGET_OS"; then + TARGET_OS=`uname -s` +fi + +COMMON_FLAGS= +CROSS_COMPILE= +PLATFORM_CCFLAGS= +PLATFORM_CXXFLAGS= +PLATFORM_LDFLAGS= +PLATFORM_LIBS= +PLATFORM_SHARED_EXT="so" +PLATFORM_SHARED_LDFLAGS="-shared -Wl,-soname -Wl," +PLATFORM_SHARED_CFLAGS="-fPIC" +PLATFORM_SHARED_VERSIONED=true + +MEMCMP_FLAG= +if [ "$CXX" = "g++" ]; then + # Use libc's memcmp instead of GCC's memcmp. This results in ~40% + # performance improvement on readrandom under gcc 4.4.3 on Linux/x86. + MEMCMP_FLAG="-fno-builtin-memcmp" +fi + +case "$TARGET_OS" in + Darwin) + PLATFORM=OS_MACOSX + COMMON_FLAGS="$MEMCMP_FLAG -DOS_MACOSX" + PLATFORM_SHARED_EXT=dylib + [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd` + PLATFORM_SHARED_LDFLAGS="-dynamiclib -install_name $INSTALL_PATH/" + PORT_FILE=port/port_posix.cc + ;; + Linux) + PLATFORM=OS_LINUX + COMMON_FLAGS="$MEMCMP_FLAG -pthread -DOS_LINUX" + PLATFORM_LDFLAGS="-pthread" + PORT_FILE=port/port_posix.cc + ;; + SunOS) + PLATFORM=OS_SOLARIS + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_SOLARIS" + PLATFORM_LIBS="-lpthread -lrt" + PORT_FILE=port/port_posix.cc + ;; + FreeBSD) + PLATFORM=OS_FREEBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_FREEBSD" + PLATFORM_LIBS="-lpthread" + PORT_FILE=port/port_posix.cc + ;; + NetBSD) + PLATFORM=OS_NETBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_NETBSD" + PLATFORM_LIBS="-lpthread -lgcc_s" + PORT_FILE=port/port_posix.cc + ;; + OpenBSD) + PLATFORM=OS_OPENBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_OPENBSD" + PLATFORM_LDFLAGS="-pthread" + PORT_FILE=port/port_posix.cc + ;; + DragonFly) + PLATFORM=OS_DRAGONFLYBSD + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_DRAGONFLYBSD" + PLATFORM_LIBS="-lpthread" + PORT_FILE=port/port_posix.cc + ;; + OS_ANDROID_CROSSCOMPILE) + PLATFORM=OS_ANDROID + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_ANDROID -DLEVELDB_PLATFORM_POSIX" + PLATFORM_LDFLAGS="" # All pthread features are in the Android C library + PORT_FILE=port/port_posix.cc + CROSS_COMPILE=true + ;; + HP-UX) + PLATFORM=OS_HPUX + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_HPUX" + PLATFORM_LDFLAGS="-pthread" + PORT_FILE=port/port_posix.cc + # man ld: +h internal_name + PLATFORM_SHARED_LDFLAGS="-shared -Wl,+h -Wl," + ;; + OS_WINDOWS_CROSSCOMPILE | NATIVE_WINDOWS) + PLATFORM=OS_WINDOWS + COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_WINDOWS -DLEVELDB_PLATFORM_WINDOWS -DWINVER=0x0500 -D__USE_MINGW_ANSI_STDIO=1" + PLATFORM_SOURCES="util/env_win.cc" + PLATFORM_LIBS="-lshlwapi" + PORT_FILE=port/port_win.cc + CROSS_COMPILE=true + ;; + *) + echo "Unknown platform!" >&2 + exit 1 +esac + +# We want to make a list of all cc files within util, db, table, and helpers +# except for the test and benchmark files. By default, find will output a list +# of all files matching either rule, so we need to append -print to make the +# prune take effect. +DIRS="$PREFIX/db $PREFIX/util $PREFIX/table" + +set -f # temporarily disable globbing so that our patterns aren't expanded +PRUNE_TEST="-name *test*.cc -prune" +PRUNE_BENCH="-name *_bench.cc -prune" +PRUNE_TOOL="-name leveldb_main.cc -prune" +PORTABLE_FILES=`find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o $PRUNE_TOOL -o -name '*.cc' -print | sort | sed "s,^$PREFIX/,," | tr "\n" " "` + +set +f # re-enable globbing + +# The sources consist of the portable files, plus the platform-specific port +# file. +echo "SOURCES=$PORTABLE_FILES $PORT_FILE" >> $OUTPUT +echo "MEMENV_SOURCES=helpers/memenv/memenv.cc" >> $OUTPUT + +if [ "$CROSS_COMPILE" = "true" ]; then + # Cross-compiling; do not try any compilation tests. + true +else + # If -std=c++0x works, use . Otherwise use port_posix.h. + $CXX $CXXFLAGS -std=c++0x -x c++ - -o /dev/null 2>/dev/null < + int main() {} +EOF + if [ "$?" = 0 ]; then + COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX -DLEVELDB_CSTDATOMIC_PRESENT" + PLATFORM_CXXFLAGS="-std=c++0x" + else + COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX" + fi + + # Test whether tcmalloc is available + $CXX $CXXFLAGS -x c++ - -o /dev/null -ltcmalloc 2>/dev/null <> $OUTPUT +echo "CXX=$CXX" >> $OUTPUT +echo "PLATFORM=$PLATFORM" >> $OUTPUT +echo "PLATFORM_LDFLAGS=$PLATFORM_LDFLAGS" >> $OUTPUT +echo "PLATFORM_LIBS=$PLATFORM_LIBS" >> $OUTPUT +echo "PLATFORM_CCFLAGS=$PLATFORM_CCFLAGS" >> $OUTPUT +echo "PLATFORM_CXXFLAGS=$PLATFORM_CXXFLAGS" >> $OUTPUT +echo "PLATFORM_SHARED_CFLAGS=$PLATFORM_SHARED_CFLAGS" >> $OUTPUT +echo "PLATFORM_SHARED_EXT=$PLATFORM_SHARED_EXT" >> $OUTPUT +echo "PLATFORM_SHARED_LDFLAGS=$PLATFORM_SHARED_LDFLAGS" >> $OUTPUT +echo "PLATFORM_SHARED_VERSIONED=$PLATFORM_SHARED_VERSIONED" >> $OUTPUT diff --git a/src/leveldb/db/builder.cc b/src/leveldb/db/builder.cc new file mode 100644 index 0000000..f419882 --- /dev/null +++ b/src/leveldb/db/builder.cc @@ -0,0 +1,88 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/builder.h" + +#include "db/filename.h" +#include "db/dbformat.h" +#include "db/table_cache.h" +#include "db/version_edit.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" + +namespace leveldb { + +Status BuildTable(const std::string& dbname, + Env* env, + const Options& options, + TableCache* table_cache, + Iterator* iter, + FileMetaData* meta) { + Status s; + meta->file_size = 0; + iter->SeekToFirst(); + + std::string fname = TableFileName(dbname, meta->number); + if (iter->Valid()) { + WritableFile* file; + s = env->NewWritableFile(fname, &file); + if (!s.ok()) { + return s; + } + + TableBuilder* builder = new TableBuilder(options, file); + meta->smallest.DecodeFrom(iter->key()); + for (; iter->Valid(); iter->Next()) { + Slice key = iter->key(); + meta->largest.DecodeFrom(key); + builder->Add(key, iter->value()); + } + + // Finish and check for builder errors + if (s.ok()) { + s = builder->Finish(); + if (s.ok()) { + meta->file_size = builder->FileSize(); + assert(meta->file_size > 0); + } + } else { + builder->Abandon(); + } + delete builder; + + // Finish and check for file errors + if (s.ok()) { + s = file->Sync(); + } + if (s.ok()) { + s = file->Close(); + } + delete file; + file = NULL; + + if (s.ok()) { + // Verify that the table is usable + Iterator* it = table_cache->NewIterator(ReadOptions(), + meta->number, + meta->file_size); + s = it->status(); + delete it; + } + } + + // Check for input iterator errors + if (!iter->status().ok()) { + s = iter->status(); + } + + if (s.ok() && meta->file_size > 0) { + // Keep it + } else { + env->DeleteFile(fname); + } + return s; +} + +} // namespace leveldb diff --git a/src/leveldb/db/builder.h b/src/leveldb/db/builder.h new file mode 100644 index 0000000..62431fc --- /dev/null +++ b/src/leveldb/db/builder.h @@ -0,0 +1,34 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_BUILDER_H_ +#define STORAGE_LEVELDB_DB_BUILDER_H_ + +#include "leveldb/status.h" + +namespace leveldb { + +struct Options; +struct FileMetaData; + +class Env; +class Iterator; +class TableCache; +class VersionEdit; + +// Build a Table file from the contents of *iter. The generated file +// will be named according to meta->number. On success, the rest of +// *meta will be filled with metadata about the generated table. +// If no data is present in *iter, meta->file_size will be set to +// zero, and no Table file will be produced. +extern Status BuildTable(const std::string& dbname, + Env* env, + const Options& options, + TableCache* table_cache, + Iterator* iter, + FileMetaData* meta); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_BUILDER_H_ diff --git a/src/leveldb/db/c.cc b/src/leveldb/db/c.cc new file mode 100644 index 0000000..08ff0ad --- /dev/null +++ b/src/leveldb/db/c.cc @@ -0,0 +1,595 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/c.h" + +#include +#include +#include "leveldb/cache.h" +#include "leveldb/comparator.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/filter_policy.h" +#include "leveldb/iterator.h" +#include "leveldb/options.h" +#include "leveldb/status.h" +#include "leveldb/write_batch.h" + +using leveldb::Cache; +using leveldb::Comparator; +using leveldb::CompressionType; +using leveldb::DB; +using leveldb::Env; +using leveldb::FileLock; +using leveldb::FilterPolicy; +using leveldb::Iterator; +using leveldb::kMajorVersion; +using leveldb::kMinorVersion; +using leveldb::Logger; +using leveldb::NewBloomFilterPolicy; +using leveldb::NewLRUCache; +using leveldb::Options; +using leveldb::RandomAccessFile; +using leveldb::Range; +using leveldb::ReadOptions; +using leveldb::SequentialFile; +using leveldb::Slice; +using leveldb::Snapshot; +using leveldb::Status; +using leveldb::WritableFile; +using leveldb::WriteBatch; +using leveldb::WriteOptions; + +extern "C" { + +struct leveldb_t { DB* rep; }; +struct leveldb_iterator_t { Iterator* rep; }; +struct leveldb_writebatch_t { WriteBatch rep; }; +struct leveldb_snapshot_t { const Snapshot* rep; }; +struct leveldb_readoptions_t { ReadOptions rep; }; +struct leveldb_writeoptions_t { WriteOptions rep; }; +struct leveldb_options_t { Options rep; }; +struct leveldb_cache_t { Cache* rep; }; +struct leveldb_seqfile_t { SequentialFile* rep; }; +struct leveldb_randomfile_t { RandomAccessFile* rep; }; +struct leveldb_writablefile_t { WritableFile* rep; }; +struct leveldb_logger_t { Logger* rep; }; +struct leveldb_filelock_t { FileLock* rep; }; + +struct leveldb_comparator_t : public Comparator { + void* state_; + void (*destructor_)(void*); + int (*compare_)( + void*, + const char* a, size_t alen, + const char* b, size_t blen); + const char* (*name_)(void*); + + virtual ~leveldb_comparator_t() { + (*destructor_)(state_); + } + + virtual int Compare(const Slice& a, const Slice& b) const { + return (*compare_)(state_, a.data(), a.size(), b.data(), b.size()); + } + + virtual const char* Name() const { + return (*name_)(state_); + } + + // No-ops since the C binding does not support key shortening methods. + virtual void FindShortestSeparator(std::string*, const Slice&) const { } + virtual void FindShortSuccessor(std::string* key) const { } +}; + +struct leveldb_filterpolicy_t : public FilterPolicy { + void* state_; + void (*destructor_)(void*); + const char* (*name_)(void*); + char* (*create_)( + void*, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length); + unsigned char (*key_match_)( + void*, + const char* key, size_t length, + const char* filter, size_t filter_length); + + virtual ~leveldb_filterpolicy_t() { + (*destructor_)(state_); + } + + virtual const char* Name() const { + return (*name_)(state_); + } + + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + std::vector key_pointers(n); + std::vector key_sizes(n); + for (int i = 0; i < n; i++) { + key_pointers[i] = keys[i].data(); + key_sizes[i] = keys[i].size(); + } + size_t len; + char* filter = (*create_)(state_, &key_pointers[0], &key_sizes[0], n, &len); + dst->append(filter, len); + free(filter); + } + + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const { + return (*key_match_)(state_, key.data(), key.size(), + filter.data(), filter.size()); + } +}; + +struct leveldb_env_t { + Env* rep; + bool is_default; +}; + +static bool SaveError(char** errptr, const Status& s) { + assert(errptr != NULL); + if (s.ok()) { + return false; + } else if (*errptr == NULL) { + *errptr = strdup(s.ToString().c_str()); + } else { + // TODO(sanjay): Merge with existing error? + free(*errptr); + *errptr = strdup(s.ToString().c_str()); + } + return true; +} + +static char* CopyString(const std::string& str) { + char* result = reinterpret_cast(malloc(sizeof(char) * str.size())); + memcpy(result, str.data(), sizeof(char) * str.size()); + return result; +} + +leveldb_t* leveldb_open( + const leveldb_options_t* options, + const char* name, + char** errptr) { + DB* db; + if (SaveError(errptr, DB::Open(options->rep, std::string(name), &db))) { + return NULL; + } + leveldb_t* result = new leveldb_t; + result->rep = db; + return result; +} + +void leveldb_close(leveldb_t* db) { + delete db->rep; + delete db; +} + +void leveldb_put( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + const char* val, size_t vallen, + char** errptr) { + SaveError(errptr, + db->rep->Put(options->rep, Slice(key, keylen), Slice(val, vallen))); +} + +void leveldb_delete( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + char** errptr) { + SaveError(errptr, db->rep->Delete(options->rep, Slice(key, keylen))); +} + + +void leveldb_write( + leveldb_t* db, + const leveldb_writeoptions_t* options, + leveldb_writebatch_t* batch, + char** errptr) { + SaveError(errptr, db->rep->Write(options->rep, &batch->rep)); +} + +char* leveldb_get( + leveldb_t* db, + const leveldb_readoptions_t* options, + const char* key, size_t keylen, + size_t* vallen, + char** errptr) { + char* result = NULL; + std::string tmp; + Status s = db->rep->Get(options->rep, Slice(key, keylen), &tmp); + if (s.ok()) { + *vallen = tmp.size(); + result = CopyString(tmp); + } else { + *vallen = 0; + if (!s.IsNotFound()) { + SaveError(errptr, s); + } + } + return result; +} + +leveldb_iterator_t* leveldb_create_iterator( + leveldb_t* db, + const leveldb_readoptions_t* options) { + leveldb_iterator_t* result = new leveldb_iterator_t; + result->rep = db->rep->NewIterator(options->rep); + return result; +} + +const leveldb_snapshot_t* leveldb_create_snapshot( + leveldb_t* db) { + leveldb_snapshot_t* result = new leveldb_snapshot_t; + result->rep = db->rep->GetSnapshot(); + return result; +} + +void leveldb_release_snapshot( + leveldb_t* db, + const leveldb_snapshot_t* snapshot) { + db->rep->ReleaseSnapshot(snapshot->rep); + delete snapshot; +} + +char* leveldb_property_value( + leveldb_t* db, + const char* propname) { + std::string tmp; + if (db->rep->GetProperty(Slice(propname), &tmp)) { + // We use strdup() since we expect human readable output. + return strdup(tmp.c_str()); + } else { + return NULL; + } +} + +void leveldb_approximate_sizes( + leveldb_t* db, + int num_ranges, + const char* const* range_start_key, const size_t* range_start_key_len, + const char* const* range_limit_key, const size_t* range_limit_key_len, + uint64_t* sizes) { + Range* ranges = new Range[num_ranges]; + for (int i = 0; i < num_ranges; i++) { + ranges[i].start = Slice(range_start_key[i], range_start_key_len[i]); + ranges[i].limit = Slice(range_limit_key[i], range_limit_key_len[i]); + } + db->rep->GetApproximateSizes(ranges, num_ranges, sizes); + delete[] ranges; +} + +void leveldb_compact_range( + leveldb_t* db, + const char* start_key, size_t start_key_len, + const char* limit_key, size_t limit_key_len) { + Slice a, b; + db->rep->CompactRange( + // Pass NULL Slice if corresponding "const char*" is NULL + (start_key ? (a = Slice(start_key, start_key_len), &a) : NULL), + (limit_key ? (b = Slice(limit_key, limit_key_len), &b) : NULL)); +} + +void leveldb_destroy_db( + const leveldb_options_t* options, + const char* name, + char** errptr) { + SaveError(errptr, DestroyDB(name, options->rep)); +} + +void leveldb_repair_db( + const leveldb_options_t* options, + const char* name, + char** errptr) { + SaveError(errptr, RepairDB(name, options->rep)); +} + +void leveldb_iter_destroy(leveldb_iterator_t* iter) { + delete iter->rep; + delete iter; +} + +unsigned char leveldb_iter_valid(const leveldb_iterator_t* iter) { + return iter->rep->Valid(); +} + +void leveldb_iter_seek_to_first(leveldb_iterator_t* iter) { + iter->rep->SeekToFirst(); +} + +void leveldb_iter_seek_to_last(leveldb_iterator_t* iter) { + iter->rep->SeekToLast(); +} + +void leveldb_iter_seek(leveldb_iterator_t* iter, const char* k, size_t klen) { + iter->rep->Seek(Slice(k, klen)); +} + +void leveldb_iter_next(leveldb_iterator_t* iter) { + iter->rep->Next(); +} + +void leveldb_iter_prev(leveldb_iterator_t* iter) { + iter->rep->Prev(); +} + +const char* leveldb_iter_key(const leveldb_iterator_t* iter, size_t* klen) { + Slice s = iter->rep->key(); + *klen = s.size(); + return s.data(); +} + +const char* leveldb_iter_value(const leveldb_iterator_t* iter, size_t* vlen) { + Slice s = iter->rep->value(); + *vlen = s.size(); + return s.data(); +} + +void leveldb_iter_get_error(const leveldb_iterator_t* iter, char** errptr) { + SaveError(errptr, iter->rep->status()); +} + +leveldb_writebatch_t* leveldb_writebatch_create() { + return new leveldb_writebatch_t; +} + +void leveldb_writebatch_destroy(leveldb_writebatch_t* b) { + delete b; +} + +void leveldb_writebatch_clear(leveldb_writebatch_t* b) { + b->rep.Clear(); +} + +void leveldb_writebatch_put( + leveldb_writebatch_t* b, + const char* key, size_t klen, + const char* val, size_t vlen) { + b->rep.Put(Slice(key, klen), Slice(val, vlen)); +} + +void leveldb_writebatch_delete( + leveldb_writebatch_t* b, + const char* key, size_t klen) { + b->rep.Delete(Slice(key, klen)); +} + +void leveldb_writebatch_iterate( + leveldb_writebatch_t* b, + void* state, + void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen), + void (*deleted)(void*, const char* k, size_t klen)) { + class H : public WriteBatch::Handler { + public: + void* state_; + void (*put_)(void*, const char* k, size_t klen, const char* v, size_t vlen); + void (*deleted_)(void*, const char* k, size_t klen); + virtual void Put(const Slice& key, const Slice& value) { + (*put_)(state_, key.data(), key.size(), value.data(), value.size()); + } + virtual void Delete(const Slice& key) { + (*deleted_)(state_, key.data(), key.size()); + } + }; + H handler; + handler.state_ = state; + handler.put_ = put; + handler.deleted_ = deleted; + b->rep.Iterate(&handler); +} + +leveldb_options_t* leveldb_options_create() { + return new leveldb_options_t; +} + +void leveldb_options_destroy(leveldb_options_t* options) { + delete options; +} + +void leveldb_options_set_comparator( + leveldb_options_t* opt, + leveldb_comparator_t* cmp) { + opt->rep.comparator = cmp; +} + +void leveldb_options_set_filter_policy( + leveldb_options_t* opt, + leveldb_filterpolicy_t* policy) { + opt->rep.filter_policy = policy; +} + +void leveldb_options_set_create_if_missing( + leveldb_options_t* opt, unsigned char v) { + opt->rep.create_if_missing = v; +} + +void leveldb_options_set_error_if_exists( + leveldb_options_t* opt, unsigned char v) { + opt->rep.error_if_exists = v; +} + +void leveldb_options_set_paranoid_checks( + leveldb_options_t* opt, unsigned char v) { + opt->rep.paranoid_checks = v; +} + +void leveldb_options_set_env(leveldb_options_t* opt, leveldb_env_t* env) { + opt->rep.env = (env ? env->rep : NULL); +} + +void leveldb_options_set_info_log(leveldb_options_t* opt, leveldb_logger_t* l) { + opt->rep.info_log = (l ? l->rep : NULL); +} + +void leveldb_options_set_write_buffer_size(leveldb_options_t* opt, size_t s) { + opt->rep.write_buffer_size = s; +} + +void leveldb_options_set_max_open_files(leveldb_options_t* opt, int n) { + opt->rep.max_open_files = n; +} + +void leveldb_options_set_cache(leveldb_options_t* opt, leveldb_cache_t* c) { + opt->rep.block_cache = c->rep; +} + +void leveldb_options_set_block_size(leveldb_options_t* opt, size_t s) { + opt->rep.block_size = s; +} + +void leveldb_options_set_block_restart_interval(leveldb_options_t* opt, int n) { + opt->rep.block_restart_interval = n; +} + +void leveldb_options_set_compression(leveldb_options_t* opt, int t) { + opt->rep.compression = static_cast(t); +} + +leveldb_comparator_t* leveldb_comparator_create( + void* state, + void (*destructor)(void*), + int (*compare)( + void*, + const char* a, size_t alen, + const char* b, size_t blen), + const char* (*name)(void*)) { + leveldb_comparator_t* result = new leveldb_comparator_t; + result->state_ = state; + result->destructor_ = destructor; + result->compare_ = compare; + result->name_ = name; + return result; +} + +void leveldb_comparator_destroy(leveldb_comparator_t* cmp) { + delete cmp; +} + +leveldb_filterpolicy_t* leveldb_filterpolicy_create( + void* state, + void (*destructor)(void*), + char* (*create_filter)( + void*, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length), + unsigned char (*key_may_match)( + void*, + const char* key, size_t length, + const char* filter, size_t filter_length), + const char* (*name)(void*)) { + leveldb_filterpolicy_t* result = new leveldb_filterpolicy_t; + result->state_ = state; + result->destructor_ = destructor; + result->create_ = create_filter; + result->key_match_ = key_may_match; + result->name_ = name; + return result; +} + +void leveldb_filterpolicy_destroy(leveldb_filterpolicy_t* filter) { + delete filter; +} + +leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom(int bits_per_key) { + // Make a leveldb_filterpolicy_t, but override all of its methods so + // they delegate to a NewBloomFilterPolicy() instead of user + // supplied C functions. + struct Wrapper : public leveldb_filterpolicy_t { + const FilterPolicy* rep_; + ~Wrapper() { delete rep_; } + const char* Name() const { return rep_->Name(); } + void CreateFilter(const Slice* keys, int n, std::string* dst) const { + return rep_->CreateFilter(keys, n, dst); + } + bool KeyMayMatch(const Slice& key, const Slice& filter) const { + return rep_->KeyMayMatch(key, filter); + } + static void DoNothing(void*) { } + }; + Wrapper* wrapper = new Wrapper; + wrapper->rep_ = NewBloomFilterPolicy(bits_per_key); + wrapper->state_ = NULL; + wrapper->destructor_ = &Wrapper::DoNothing; + return wrapper; +} + +leveldb_readoptions_t* leveldb_readoptions_create() { + return new leveldb_readoptions_t; +} + +void leveldb_readoptions_destroy(leveldb_readoptions_t* opt) { + delete opt; +} + +void leveldb_readoptions_set_verify_checksums( + leveldb_readoptions_t* opt, + unsigned char v) { + opt->rep.verify_checksums = v; +} + +void leveldb_readoptions_set_fill_cache( + leveldb_readoptions_t* opt, unsigned char v) { + opt->rep.fill_cache = v; +} + +void leveldb_readoptions_set_snapshot( + leveldb_readoptions_t* opt, + const leveldb_snapshot_t* snap) { + opt->rep.snapshot = (snap ? snap->rep : NULL); +} + +leveldb_writeoptions_t* leveldb_writeoptions_create() { + return new leveldb_writeoptions_t; +} + +void leveldb_writeoptions_destroy(leveldb_writeoptions_t* opt) { + delete opt; +} + +void leveldb_writeoptions_set_sync( + leveldb_writeoptions_t* opt, unsigned char v) { + opt->rep.sync = v; +} + +leveldb_cache_t* leveldb_cache_create_lru(size_t capacity) { + leveldb_cache_t* c = new leveldb_cache_t; + c->rep = NewLRUCache(capacity); + return c; +} + +void leveldb_cache_destroy(leveldb_cache_t* cache) { + delete cache->rep; + delete cache; +} + +leveldb_env_t* leveldb_create_default_env() { + leveldb_env_t* result = new leveldb_env_t; + result->rep = Env::Default(); + result->is_default = true; + return result; +} + +void leveldb_env_destroy(leveldb_env_t* env) { + if (!env->is_default) delete env->rep; + delete env; +} + +void leveldb_free(void* ptr) { + free(ptr); +} + +int leveldb_major_version() { + return kMajorVersion; +} + +int leveldb_minor_version() { + return kMinorVersion; +} + +} // end extern "C" diff --git a/src/leveldb/db/c_test.c b/src/leveldb/db/c_test.c new file mode 100644 index 0000000..7cd5ee0 --- /dev/null +++ b/src/leveldb/db/c_test.c @@ -0,0 +1,390 @@ +/* Copyright (c) 2011 The LevelDB Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. See the AUTHORS file for names of contributors. */ + +#include "leveldb/c.h" + +#include +#include +#include +#include +#include +#include + +const char* phase = ""; +static char dbname[200]; + +static void StartPhase(const char* name) { + fprintf(stderr, "=== Test %s\n", name); + phase = name; +} + +static const char* GetTempDir(void) { + const char* ret = getenv("TEST_TMPDIR"); + if (ret == NULL || ret[0] == '\0') + ret = "/tmp"; + return ret; +} + +#define CheckNoError(err) \ + if ((err) != NULL) { \ + fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, (err)); \ + abort(); \ + } + +#define CheckCondition(cond) \ + if (!(cond)) { \ + fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, #cond); \ + abort(); \ + } + +static void CheckEqual(const char* expected, const char* v, size_t n) { + if (expected == NULL && v == NULL) { + // ok + } else if (expected != NULL && v != NULL && n == strlen(expected) && + memcmp(expected, v, n) == 0) { + // ok + return; + } else { + fprintf(stderr, "%s: expected '%s', got '%s'\n", + phase, + (expected ? expected : "(null)"), + (v ? v : "(null")); + abort(); + } +} + +static void Free(char** ptr) { + if (*ptr) { + free(*ptr); + *ptr = NULL; + } +} + +static void CheckGet( + leveldb_t* db, + const leveldb_readoptions_t* options, + const char* key, + const char* expected) { + char* err = NULL; + size_t val_len; + char* val; + val = leveldb_get(db, options, key, strlen(key), &val_len, &err); + CheckNoError(err); + CheckEqual(expected, val, val_len); + Free(&val); +} + +static void CheckIter(leveldb_iterator_t* iter, + const char* key, const char* val) { + size_t len; + const char* str; + str = leveldb_iter_key(iter, &len); + CheckEqual(key, str, len); + str = leveldb_iter_value(iter, &len); + CheckEqual(val, str, len); +} + +// Callback from leveldb_writebatch_iterate() +static void CheckPut(void* ptr, + const char* k, size_t klen, + const char* v, size_t vlen) { + int* state = (int*) ptr; + CheckCondition(*state < 2); + switch (*state) { + case 0: + CheckEqual("bar", k, klen); + CheckEqual("b", v, vlen); + break; + case 1: + CheckEqual("box", k, klen); + CheckEqual("c", v, vlen); + break; + } + (*state)++; +} + +// Callback from leveldb_writebatch_iterate() +static void CheckDel(void* ptr, const char* k, size_t klen) { + int* state = (int*) ptr; + CheckCondition(*state == 2); + CheckEqual("bar", k, klen); + (*state)++; +} + +static void CmpDestroy(void* arg) { } + +static int CmpCompare(void* arg, const char* a, size_t alen, + const char* b, size_t blen) { + int n = (alen < blen) ? alen : blen; + int r = memcmp(a, b, n); + if (r == 0) { + if (alen < blen) r = -1; + else if (alen > blen) r = +1; + } + return r; +} + +static const char* CmpName(void* arg) { + return "foo"; +} + +// Custom filter policy +static unsigned char fake_filter_result = 1; +static void FilterDestroy(void* arg) { } +static const char* FilterName(void* arg) { + return "TestFilter"; +} +static char* FilterCreate( + void* arg, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length) { + *filter_length = 4; + char* result = malloc(4); + memcpy(result, "fake", 4); + return result; +} +unsigned char FilterKeyMatch( + void* arg, + const char* key, size_t length, + const char* filter, size_t filter_length) { + CheckCondition(filter_length == 4); + CheckCondition(memcmp(filter, "fake", 4) == 0); + return fake_filter_result; +} + +int main(int argc, char** argv) { + leveldb_t* db; + leveldb_comparator_t* cmp; + leveldb_cache_t* cache; + leveldb_env_t* env; + leveldb_options_t* options; + leveldb_readoptions_t* roptions; + leveldb_writeoptions_t* woptions; + char* err = NULL; + int run = -1; + + CheckCondition(leveldb_major_version() >= 1); + CheckCondition(leveldb_minor_version() >= 1); + + snprintf(dbname, sizeof(dbname), + "%s/leveldb_c_test-%d", + GetTempDir(), + ((int) geteuid())); + + StartPhase("create_objects"); + cmp = leveldb_comparator_create(NULL, CmpDestroy, CmpCompare, CmpName); + env = leveldb_create_default_env(); + cache = leveldb_cache_create_lru(100000); + + options = leveldb_options_create(); + leveldb_options_set_comparator(options, cmp); + leveldb_options_set_error_if_exists(options, 1); + leveldb_options_set_cache(options, cache); + leveldb_options_set_env(options, env); + leveldb_options_set_info_log(options, NULL); + leveldb_options_set_write_buffer_size(options, 100000); + leveldb_options_set_paranoid_checks(options, 1); + leveldb_options_set_max_open_files(options, 10); + leveldb_options_set_block_size(options, 1024); + leveldb_options_set_block_restart_interval(options, 8); + leveldb_options_set_compression(options, leveldb_no_compression); + + roptions = leveldb_readoptions_create(); + leveldb_readoptions_set_verify_checksums(roptions, 1); + leveldb_readoptions_set_fill_cache(roptions, 0); + + woptions = leveldb_writeoptions_create(); + leveldb_writeoptions_set_sync(woptions, 1); + + StartPhase("destroy"); + leveldb_destroy_db(options, dbname, &err); + Free(&err); + + StartPhase("open_error"); + db = leveldb_open(options, dbname, &err); + CheckCondition(err != NULL); + Free(&err); + + StartPhase("leveldb_free"); + db = leveldb_open(options, dbname, &err); + CheckCondition(err != NULL); + leveldb_free(err); + err = NULL; + + StartPhase("open"); + leveldb_options_set_create_if_missing(options, 1); + db = leveldb_open(options, dbname, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", NULL); + + StartPhase("put"); + leveldb_put(db, woptions, "foo", 3, "hello", 5, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", "hello"); + + StartPhase("compactall"); + leveldb_compact_range(db, NULL, 0, NULL, 0); + CheckGet(db, roptions, "foo", "hello"); + + StartPhase("compactrange"); + leveldb_compact_range(db, "a", 1, "z", 1); + CheckGet(db, roptions, "foo", "hello"); + + StartPhase("writebatch"); + { + leveldb_writebatch_t* wb = leveldb_writebatch_create(); + leveldb_writebatch_put(wb, "foo", 3, "a", 1); + leveldb_writebatch_clear(wb); + leveldb_writebatch_put(wb, "bar", 3, "b", 1); + leveldb_writebatch_put(wb, "box", 3, "c", 1); + leveldb_writebatch_delete(wb, "bar", 3); + leveldb_write(db, woptions, wb, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", "hello"); + CheckGet(db, roptions, "bar", NULL); + CheckGet(db, roptions, "box", "c"); + int pos = 0; + leveldb_writebatch_iterate(wb, &pos, CheckPut, CheckDel); + CheckCondition(pos == 3); + leveldb_writebatch_destroy(wb); + } + + StartPhase("iter"); + { + leveldb_iterator_t* iter = leveldb_create_iterator(db, roptions); + CheckCondition(!leveldb_iter_valid(iter)); + leveldb_iter_seek_to_first(iter); + CheckCondition(leveldb_iter_valid(iter)); + CheckIter(iter, "box", "c"); + leveldb_iter_next(iter); + CheckIter(iter, "foo", "hello"); + leveldb_iter_prev(iter); + CheckIter(iter, "box", "c"); + leveldb_iter_prev(iter); + CheckCondition(!leveldb_iter_valid(iter)); + leveldb_iter_seek_to_last(iter); + CheckIter(iter, "foo", "hello"); + leveldb_iter_seek(iter, "b", 1); + CheckIter(iter, "box", "c"); + leveldb_iter_get_error(iter, &err); + CheckNoError(err); + leveldb_iter_destroy(iter); + } + + StartPhase("approximate_sizes"); + { + int i; + int n = 20000; + char keybuf[100]; + char valbuf[100]; + uint64_t sizes[2]; + const char* start[2] = { "a", "k00000000000000010000" }; + size_t start_len[2] = { 1, 21 }; + const char* limit[2] = { "k00000000000000010000", "z" }; + size_t limit_len[2] = { 21, 1 }; + leveldb_writeoptions_set_sync(woptions, 0); + for (i = 0; i < n; i++) { + snprintf(keybuf, sizeof(keybuf), "k%020d", i); + snprintf(valbuf, sizeof(valbuf), "v%020d", i); + leveldb_put(db, woptions, keybuf, strlen(keybuf), valbuf, strlen(valbuf), + &err); + CheckNoError(err); + } + leveldb_approximate_sizes(db, 2, start, start_len, limit, limit_len, sizes); + CheckCondition(sizes[0] > 0); + CheckCondition(sizes[1] > 0); + } + + StartPhase("property"); + { + char* prop = leveldb_property_value(db, "nosuchprop"); + CheckCondition(prop == NULL); + prop = leveldb_property_value(db, "leveldb.stats"); + CheckCondition(prop != NULL); + Free(&prop); + } + + StartPhase("snapshot"); + { + const leveldb_snapshot_t* snap; + snap = leveldb_create_snapshot(db); + leveldb_delete(db, woptions, "foo", 3, &err); + CheckNoError(err); + leveldb_readoptions_set_snapshot(roptions, snap); + CheckGet(db, roptions, "foo", "hello"); + leveldb_readoptions_set_snapshot(roptions, NULL); + CheckGet(db, roptions, "foo", NULL); + leveldb_release_snapshot(db, snap); + } + + StartPhase("repair"); + { + leveldb_close(db); + leveldb_options_set_create_if_missing(options, 0); + leveldb_options_set_error_if_exists(options, 0); + leveldb_repair_db(options, dbname, &err); + CheckNoError(err); + db = leveldb_open(options, dbname, &err); + CheckNoError(err); + CheckGet(db, roptions, "foo", NULL); + CheckGet(db, roptions, "bar", NULL); + CheckGet(db, roptions, "box", "c"); + leveldb_options_set_create_if_missing(options, 1); + leveldb_options_set_error_if_exists(options, 1); + } + + StartPhase("filter"); + for (run = 0; run < 2; run++) { + // First run uses custom filter, second run uses bloom filter + CheckNoError(err); + leveldb_filterpolicy_t* policy; + if (run == 0) { + policy = leveldb_filterpolicy_create( + NULL, FilterDestroy, FilterCreate, FilterKeyMatch, FilterName); + } else { + policy = leveldb_filterpolicy_create_bloom(10); + } + + // Create new database + leveldb_close(db); + leveldb_destroy_db(options, dbname, &err); + leveldb_options_set_filter_policy(options, policy); + db = leveldb_open(options, dbname, &err); + CheckNoError(err); + leveldb_put(db, woptions, "foo", 3, "foovalue", 8, &err); + CheckNoError(err); + leveldb_put(db, woptions, "bar", 3, "barvalue", 8, &err); + CheckNoError(err); + leveldb_compact_range(db, NULL, 0, NULL, 0); + + fake_filter_result = 1; + CheckGet(db, roptions, "foo", "foovalue"); + CheckGet(db, roptions, "bar", "barvalue"); + if (phase == 0) { + // Must not find value when custom filter returns false + fake_filter_result = 0; + CheckGet(db, roptions, "foo", NULL); + CheckGet(db, roptions, "bar", NULL); + fake_filter_result = 1; + + CheckGet(db, roptions, "foo", "foovalue"); + CheckGet(db, roptions, "bar", "barvalue"); + } + leveldb_options_set_filter_policy(options, NULL); + leveldb_filterpolicy_destroy(policy); + } + + StartPhase("cleanup"); + leveldb_close(db); + leveldb_options_destroy(options); + leveldb_readoptions_destroy(roptions); + leveldb_writeoptions_destroy(woptions); + leveldb_cache_destroy(cache); + leveldb_comparator_destroy(cmp); + leveldb_env_destroy(env); + + fprintf(stderr, "PASS\n"); + return 0; +} diff --git a/src/leveldb/db/corruption_test.cc b/src/leveldb/db/corruption_test.cc new file mode 100644 index 0000000..31b2d5f --- /dev/null +++ b/src/leveldb/db/corruption_test.cc @@ -0,0 +1,359 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" + +#include +#include +#include +#include +#include "leveldb/cache.h" +#include "leveldb/env.h" +#include "leveldb/table.h" +#include "leveldb/write_batch.h" +#include "db/db_impl.h" +#include "db/filename.h" +#include "db/log_format.h" +#include "db/version_set.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +static const int kValueSize = 1000; + +class CorruptionTest { + public: + test::ErrorEnv env_; + std::string dbname_; + Cache* tiny_cache_; + Options options_; + DB* db_; + + CorruptionTest() { + tiny_cache_ = NewLRUCache(100); + options_.env = &env_; + dbname_ = test::TmpDir() + "/db_test"; + DestroyDB(dbname_, options_); + + db_ = NULL; + options_.create_if_missing = true; + Reopen(); + options_.create_if_missing = false; + } + + ~CorruptionTest() { + delete db_; + DestroyDB(dbname_, Options()); + delete tiny_cache_; + } + + Status TryReopen(Options* options = NULL) { + delete db_; + db_ = NULL; + Options opt = (options ? *options : options_); + opt.env = &env_; + opt.block_cache = tiny_cache_; + return DB::Open(opt, dbname_, &db_); + } + + void Reopen(Options* options = NULL) { + ASSERT_OK(TryReopen(options)); + } + + void RepairDB() { + delete db_; + db_ = NULL; + ASSERT_OK(::leveldb::RepairDB(dbname_, options_)); + } + + void Build(int n) { + std::string key_space, value_space; + WriteBatch batch; + for (int i = 0; i < n; i++) { + //if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n); + Slice key = Key(i, &key_space); + batch.Clear(); + batch.Put(key, Value(i, &value_space)); + ASSERT_OK(db_->Write(WriteOptions(), &batch)); + } + } + + void Check(int min_expected, int max_expected) { + int next_expected = 0; + int missed = 0; + int bad_keys = 0; + int bad_values = 0; + int correct = 0; + std::string value_space; + Iterator* iter = db_->NewIterator(ReadOptions()); + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + uint64_t key; + Slice in(iter->key()); + if (!ConsumeDecimalNumber(&in, &key) || + !in.empty() || + key < next_expected) { + bad_keys++; + continue; + } + missed += (key - next_expected); + next_expected = key + 1; + if (iter->value() != Value(key, &value_space)) { + bad_values++; + } else { + correct++; + } + } + delete iter; + + fprintf(stderr, + "expected=%d..%d; got=%d; bad_keys=%d; bad_values=%d; missed=%d\n", + min_expected, max_expected, correct, bad_keys, bad_values, missed); + ASSERT_LE(min_expected, correct); + ASSERT_GE(max_expected, correct); + } + + void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) { + // Pick file to corrupt + std::vector filenames; + ASSERT_OK(env_.GetChildren(dbname_, &filenames)); + uint64_t number; + FileType type; + std::string fname; + int picked_number = -1; + for (int i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) && + type == filetype && + int(number) > picked_number) { // Pick latest file + fname = dbname_ + "/" + filenames[i]; + picked_number = number; + } + } + ASSERT_TRUE(!fname.empty()) << filetype; + + struct stat sbuf; + if (stat(fname.c_str(), &sbuf) != 0) { + const char* msg = strerror(errno); + ASSERT_TRUE(false) << fname << ": " << msg; + } + + if (offset < 0) { + // Relative to end of file; make it absolute + if (-offset > sbuf.st_size) { + offset = 0; + } else { + offset = sbuf.st_size + offset; + } + } + if (offset > sbuf.st_size) { + offset = sbuf.st_size; + } + if (offset + bytes_to_corrupt > sbuf.st_size) { + bytes_to_corrupt = sbuf.st_size - offset; + } + + // Do it + std::string contents; + Status s = ReadFileToString(Env::Default(), fname, &contents); + ASSERT_TRUE(s.ok()) << s.ToString(); + for (int i = 0; i < bytes_to_corrupt; i++) { + contents[i + offset] ^= 0x80; + } + s = WriteStringToFile(Env::Default(), contents, fname); + ASSERT_TRUE(s.ok()) << s.ToString(); + } + + int Property(const std::string& name) { + std::string property; + int result; + if (db_->GetProperty(name, &property) && + sscanf(property.c_str(), "%d", &result) == 1) { + return result; + } else { + return -1; + } + } + + // Return the ith key + Slice Key(int i, std::string* storage) { + char buf[100]; + snprintf(buf, sizeof(buf), "%016d", i); + storage->assign(buf, strlen(buf)); + return Slice(*storage); + } + + // Return the value to associate with the specified key + Slice Value(int k, std::string* storage) { + Random r(k); + return test::RandomString(&r, kValueSize, storage); + } +}; + +TEST(CorruptionTest, Recovery) { + Build(100); + Check(100, 100); + Corrupt(kLogFile, 19, 1); // WriteBatch tag for first record + Corrupt(kLogFile, log::kBlockSize + 1000, 1); // Somewhere in second block + Reopen(); + + // The 64 records in the first two log blocks are completely lost. + Check(36, 36); +} + +TEST(CorruptionTest, RecoverWriteError) { + env_.writable_file_error_ = true; + Status s = TryReopen(); + ASSERT_TRUE(!s.ok()); +} + +TEST(CorruptionTest, NewFileErrorDuringWrite) { + // Do enough writing to force minor compaction + env_.writable_file_error_ = true; + const int num = 3 + (Options().write_buffer_size / kValueSize); + std::string value_storage; + Status s; + for (int i = 0; s.ok() && i < num; i++) { + WriteBatch batch; + batch.Put("a", Value(100, &value_storage)); + s = db_->Write(WriteOptions(), &batch); + } + ASSERT_TRUE(!s.ok()); + ASSERT_GE(env_.num_writable_file_errors_, 1); + env_.writable_file_error_ = false; + Reopen(); +} + +TEST(CorruptionTest, TableFile) { + Build(100); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + dbi->TEST_CompactRange(0, NULL, NULL); + dbi->TEST_CompactRange(1, NULL, NULL); + + Corrupt(kTableFile, 100, 1); + Check(99, 99); +} + +TEST(CorruptionTest, TableFileIndexData) { + Build(10000); // Enough to build multiple Tables + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + + Corrupt(kTableFile, -2000, 500); + Reopen(); + Check(5000, 9999); +} + +TEST(CorruptionTest, MissingDescriptor) { + Build(1000); + RepairDB(); + Reopen(); + Check(1000, 1000); +} + +TEST(CorruptionTest, SequenceNumberRecovery) { + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v3")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v4")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v5")); + RepairDB(); + Reopen(); + std::string v; + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("v5", v); + // Write something. If sequence number was not recovered properly, + // it will be hidden by an earlier write. + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v6")); + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("v6", v); + Reopen(); + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("v6", v); +} + +TEST(CorruptionTest, CorruptedDescriptor) { + ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello")); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + dbi->TEST_CompactRange(0, NULL, NULL); + + Corrupt(kDescriptorFile, 0, 1000); + Status s = TryReopen(); + ASSERT_TRUE(!s.ok()); + + RepairDB(); + Reopen(); + std::string v; + ASSERT_OK(db_->Get(ReadOptions(), "foo", &v)); + ASSERT_EQ("hello", v); +} + +TEST(CorruptionTest, CompactionInputError) { + Build(10); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(1, Property("leveldb.num-files-at-level" + NumberToString(last))); + + Corrupt(kTableFile, 100, 1); + Check(9, 9); + + // Force compactions by writing lots of values + Build(10000); + Check(10000, 10000); +} + +TEST(CorruptionTest, CompactionInputErrorParanoid) { + Options options; + options.paranoid_checks = true; + options.write_buffer_size = 1048576; + Reopen(&options); + DBImpl* dbi = reinterpret_cast(db_); + + // Fill levels >= 1 so memtable compaction outputs to level 1 + for (int level = 1; level < config::kNumLevels; level++) { + dbi->Put(WriteOptions(), "", "begin"); + dbi->Put(WriteOptions(), "~", "end"); + dbi->TEST_CompactMemTable(); + } + + Build(10); + dbi->TEST_CompactMemTable(); + ASSERT_EQ(1, Property("leveldb.num-files-at-level0")); + + Corrupt(kTableFile, 100, 1); + Check(9, 9); + + // Write must eventually fail because of corrupted table + Status s; + std::string tmp1, tmp2; + for (int i = 0; i < 10000 && s.ok(); i++) { + s = db_->Put(WriteOptions(), Key(i, &tmp1), Value(i, &tmp2)); + } + ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db"; +} + +TEST(CorruptionTest, UnrelatedKeys) { + Build(10); + DBImpl* dbi = reinterpret_cast(db_); + dbi->TEST_CompactMemTable(); + Corrupt(kTableFile, 100, 1); + + std::string tmp1, tmp2; + ASSERT_OK(db_->Put(WriteOptions(), Key(1000, &tmp1), Value(1000, &tmp2))); + std::string v; + ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v)); + ASSERT_EQ(Value(1000, &tmp2).ToString(), v); + dbi->TEST_CompactMemTable(); + ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v)); + ASSERT_EQ(Value(1000, &tmp2).ToString(), v); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/db_bench.cc b/src/leveldb/db/db_bench.cc new file mode 100644 index 0000000..7abdf87 --- /dev/null +++ b/src/leveldb/db/db_bench.cc @@ -0,0 +1,979 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include +#include "db/db_impl.h" +#include "db/version_set.h" +#include "leveldb/cache.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/write_batch.h" +#include "port/port.h" +#include "util/crc32c.h" +#include "util/histogram.h" +#include "util/mutexlock.h" +#include "util/random.h" +#include "util/testutil.h" + +// Comma-separated list of operations to run in the specified order +// Actual benchmarks: +// fillseq -- write N values in sequential key order in async mode +// fillrandom -- write N values in random key order in async mode +// overwrite -- overwrite N values in random key order in async mode +// fillsync -- write N/100 values in random key order in sync mode +// fill100K -- write N/1000 100K values in random order in async mode +// deleteseq -- delete N keys in sequential order +// deleterandom -- delete N keys in random order +// readseq -- read N times sequentially +// readreverse -- read N times in reverse order +// readrandom -- read N times in random order +// readmissing -- read N missing keys in random order +// readhot -- read N times in random order from 1% section of DB +// seekrandom -- N random seeks +// crc32c -- repeated crc32c of 4K of data +// acquireload -- load N*1000 times +// Meta operations: +// compact -- Compact the entire DB +// stats -- Print DB stats +// sstables -- Print sstable info +// heapprofile -- Dump a heap profile (if supported by this port) +static const char* FLAGS_benchmarks = + "fillseq," + "fillsync," + "fillrandom," + "overwrite," + "readrandom," + "readrandom," // Extra run to allow previous compactions to quiesce + "readseq," + "readreverse," + "compact," + "readrandom," + "readseq," + "readreverse," + "fill100K," + "crc32c," + "snappycomp," + "snappyuncomp," + "acquireload," + ; + +// Number of key/values to place in database +static int FLAGS_num = 1000000; + +// Number of read operations to do. If negative, do FLAGS_num reads. +static int FLAGS_reads = -1; + +// Number of concurrent threads to run. +static int FLAGS_threads = 1; + +// Size of each value +static int FLAGS_value_size = 100; + +// Arrange to generate values that shrink to this fraction of +// their original size after compression +static double FLAGS_compression_ratio = 0.5; + +// Print histogram of operation timings +static bool FLAGS_histogram = false; + +// Number of bytes to buffer in memtable before compacting +// (initialized to default value by "main") +static int FLAGS_write_buffer_size = 0; + +// Number of bytes to use as a cache of uncompressed data. +// Negative means use default settings. +static int FLAGS_cache_size = -1; + +// Maximum number of files to keep open at the same time (use default if == 0) +static int FLAGS_open_files = 0; + +// Bloom filter bits per key. +// Negative means use default settings. +static int FLAGS_bloom_bits = -1; + +// If true, do not destroy the existing database. If you set this +// flag and also specify a benchmark that wants a fresh database, that +// benchmark will fail. +static bool FLAGS_use_existing_db = false; + +// Use the db with the following name. +static const char* FLAGS_db = NULL; + +namespace leveldb { + +namespace { + +// Helper for quickly generating random data. +class RandomGenerator { + private: + std::string data_; + int pos_; + + public: + RandomGenerator() { + // We use a limited amount of data over and over again and ensure + // that it is larger than the compression window (32KB), and also + // large enough to serve all typical value sizes we want to write. + Random rnd(301); + std::string piece; + while (data_.size() < 1048576) { + // Add a short fragment that is as compressible as specified + // by FLAGS_compression_ratio. + test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece); + data_.append(piece); + } + pos_ = 0; + } + + Slice Generate(int len) { + if (pos_ + len > data_.size()) { + pos_ = 0; + assert(len < data_.size()); + } + pos_ += len; + return Slice(data_.data() + pos_ - len, len); + } +}; + +static Slice TrimSpace(Slice s) { + int start = 0; + while (start < s.size() && isspace(s[start])) { + start++; + } + int limit = s.size(); + while (limit > start && isspace(s[limit-1])) { + limit--; + } + return Slice(s.data() + start, limit - start); +} + +static void AppendWithSpace(std::string* str, Slice msg) { + if (msg.empty()) return; + if (!str->empty()) { + str->push_back(' '); + } + str->append(msg.data(), msg.size()); +} + +class Stats { + private: + double start_; + double finish_; + double seconds_; + int done_; + int next_report_; + int64_t bytes_; + double last_op_finish_; + Histogram hist_; + std::string message_; + + public: + Stats() { Start(); } + + void Start() { + next_report_ = 100; + last_op_finish_ = start_; + hist_.Clear(); + done_ = 0; + bytes_ = 0; + seconds_ = 0; + start_ = Env::Default()->NowMicros(); + finish_ = start_; + message_.clear(); + } + + void Merge(const Stats& other) { + hist_.Merge(other.hist_); + done_ += other.done_; + bytes_ += other.bytes_; + seconds_ += other.seconds_; + if (other.start_ < start_) start_ = other.start_; + if (other.finish_ > finish_) finish_ = other.finish_; + + // Just keep the messages from one thread + if (message_.empty()) message_ = other.message_; + } + + void Stop() { + finish_ = Env::Default()->NowMicros(); + seconds_ = (finish_ - start_) * 1e-6; + } + + void AddMessage(Slice msg) { + AppendWithSpace(&message_, msg); + } + + void FinishedSingleOp() { + if (FLAGS_histogram) { + double now = Env::Default()->NowMicros(); + double micros = now - last_op_finish_; + hist_.Add(micros); + if (micros > 20000) { + fprintf(stderr, "long op: %.1f micros%30s\r", micros, ""); + fflush(stderr); + } + last_op_finish_ = now; + } + + done_++; + if (done_ >= next_report_) { + if (next_report_ < 1000) next_report_ += 100; + else if (next_report_ < 5000) next_report_ += 500; + else if (next_report_ < 10000) next_report_ += 1000; + else if (next_report_ < 50000) next_report_ += 5000; + else if (next_report_ < 100000) next_report_ += 10000; + else if (next_report_ < 500000) next_report_ += 50000; + else next_report_ += 100000; + fprintf(stderr, "... finished %d ops%30s\r", done_, ""); + fflush(stderr); + } + } + + void AddBytes(int64_t n) { + bytes_ += n; + } + + void Report(const Slice& name) { + // Pretend at least one op was done in case we are running a benchmark + // that does not call FinishedSingleOp(). + if (done_ < 1) done_ = 1; + + std::string extra; + if (bytes_ > 0) { + // Rate is computed on actual elapsed time, not the sum of per-thread + // elapsed times. + double elapsed = (finish_ - start_) * 1e-6; + char rate[100]; + snprintf(rate, sizeof(rate), "%6.1f MB/s", + (bytes_ / 1048576.0) / elapsed); + extra = rate; + } + AppendWithSpace(&extra, message_); + + fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", + name.ToString().c_str(), + seconds_ * 1e6 / done_, + (extra.empty() ? "" : " "), + extra.c_str()); + if (FLAGS_histogram) { + fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str()); + } + fflush(stdout); + } +}; + +// State shared by all concurrent executions of the same benchmark. +struct SharedState { + port::Mutex mu; + port::CondVar cv; + int total; + + // Each thread goes through the following states: + // (1) initializing + // (2) waiting for others to be initialized + // (3) running + // (4) done + + int num_initialized; + int num_done; + bool start; + + SharedState() : cv(&mu) { } +}; + +// Per-thread state for concurrent executions of the same benchmark. +struct ThreadState { + int tid; // 0..n-1 when running in n threads + Random rand; // Has different seeds for different threads + Stats stats; + SharedState* shared; + + ThreadState(int index) + : tid(index), + rand(1000 + index) { + } +}; + +} // namespace + +class Benchmark { + private: + Cache* cache_; + const FilterPolicy* filter_policy_; + DB* db_; + int num_; + int value_size_; + int entries_per_batch_; + WriteOptions write_options_; + int reads_; + int heap_counter_; + + void PrintHeader() { + const int kKeySize = 16; + PrintEnvironment(); + fprintf(stdout, "Keys: %d bytes each\n", kKeySize); + fprintf(stdout, "Values: %d bytes each (%d bytes after compression)\n", + FLAGS_value_size, + static_cast(FLAGS_value_size * FLAGS_compression_ratio + 0.5)); + fprintf(stdout, "Entries: %d\n", num_); + fprintf(stdout, "RawSize: %.1f MB (estimated)\n", + ((static_cast(kKeySize + FLAGS_value_size) * num_) + / 1048576.0)); + fprintf(stdout, "FileSize: %.1f MB (estimated)\n", + (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) + / 1048576.0)); + PrintWarnings(); + fprintf(stdout, "------------------------------------------------\n"); + } + + void PrintWarnings() { +#if defined(__GNUC__) && !defined(__OPTIMIZE__) + fprintf(stdout, + "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n" + ); +#endif +#ifndef NDEBUG + fprintf(stdout, + "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n"); +#endif + + // See if snappy is working by attempting to compress a compressible string + const char text[] = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"; + std::string compressed; + if (!port::Snappy_Compress(text, sizeof(text), &compressed)) { + fprintf(stdout, "WARNING: Snappy compression is not enabled\n"); + } else if (compressed.size() >= sizeof(text)) { + fprintf(stdout, "WARNING: Snappy compression is not effective\n"); + } + } + + void PrintEnvironment() { + fprintf(stderr, "LevelDB: version %d.%d\n", + kMajorVersion, kMinorVersion); + +#if defined(__linux) + time_t now = time(NULL); + fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline + + FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); + if (cpuinfo != NULL) { + char line[1000]; + int num_cpus = 0; + std::string cpu_type; + std::string cache_size; + while (fgets(line, sizeof(line), cpuinfo) != NULL) { + const char* sep = strchr(line, ':'); + if (sep == NULL) { + continue; + } + Slice key = TrimSpace(Slice(line, sep - 1 - line)); + Slice val = TrimSpace(Slice(sep + 1)); + if (key == "model name") { + ++num_cpus; + cpu_type = val.ToString(); + } else if (key == "cache size") { + cache_size = val.ToString(); + } + } + fclose(cpuinfo); + fprintf(stderr, "CPU: %d * %s\n", num_cpus, cpu_type.c_str()); + fprintf(stderr, "CPUCache: %s\n", cache_size.c_str()); + } +#endif + } + + public: + Benchmark() + : cache_(FLAGS_cache_size >= 0 ? NewLRUCache(FLAGS_cache_size) : NULL), + filter_policy_(FLAGS_bloom_bits >= 0 + ? NewBloomFilterPolicy(FLAGS_bloom_bits) + : NULL), + db_(NULL), + num_(FLAGS_num), + value_size_(FLAGS_value_size), + entries_per_batch_(1), + reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), + heap_counter_(0) { + std::vector files; + Env::Default()->GetChildren(FLAGS_db, &files); + for (int i = 0; i < files.size(); i++) { + if (Slice(files[i]).starts_with("heap-")) { + Env::Default()->DeleteFile(std::string(FLAGS_db) + "/" + files[i]); + } + } + if (!FLAGS_use_existing_db) { + DestroyDB(FLAGS_db, Options()); + } + } + + ~Benchmark() { + delete db_; + delete cache_; + delete filter_policy_; + } + + void Run() { + PrintHeader(); + Open(); + + const char* benchmarks = FLAGS_benchmarks; + while (benchmarks != NULL) { + const char* sep = strchr(benchmarks, ','); + Slice name; + if (sep == NULL) { + name = benchmarks; + benchmarks = NULL; + } else { + name = Slice(benchmarks, sep - benchmarks); + benchmarks = sep + 1; + } + + // Reset parameters that may be overriddden bwlow + num_ = FLAGS_num; + reads_ = (FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads); + value_size_ = FLAGS_value_size; + entries_per_batch_ = 1; + write_options_ = WriteOptions(); + + void (Benchmark::*method)(ThreadState*) = NULL; + bool fresh_db = false; + int num_threads = FLAGS_threads; + + if (name == Slice("fillseq")) { + fresh_db = true; + method = &Benchmark::WriteSeq; + } else if (name == Slice("fillbatch")) { + fresh_db = true; + entries_per_batch_ = 1000; + method = &Benchmark::WriteSeq; + } else if (name == Slice("fillrandom")) { + fresh_db = true; + method = &Benchmark::WriteRandom; + } else if (name == Slice("overwrite")) { + fresh_db = false; + method = &Benchmark::WriteRandom; + } else if (name == Slice("fillsync")) { + fresh_db = true; + num_ /= 1000; + write_options_.sync = true; + method = &Benchmark::WriteRandom; + } else if (name == Slice("fill100K")) { + fresh_db = true; + num_ /= 1000; + value_size_ = 100 * 1000; + method = &Benchmark::WriteRandom; + } else if (name == Slice("readseq")) { + method = &Benchmark::ReadSequential; + } else if (name == Slice("readreverse")) { + method = &Benchmark::ReadReverse; + } else if (name == Slice("readrandom")) { + method = &Benchmark::ReadRandom; + } else if (name == Slice("readmissing")) { + method = &Benchmark::ReadMissing; + } else if (name == Slice("seekrandom")) { + method = &Benchmark::SeekRandom; + } else if (name == Slice("readhot")) { + method = &Benchmark::ReadHot; + } else if (name == Slice("readrandomsmall")) { + reads_ /= 1000; + method = &Benchmark::ReadRandom; + } else if (name == Slice("deleteseq")) { + method = &Benchmark::DeleteSeq; + } else if (name == Slice("deleterandom")) { + method = &Benchmark::DeleteRandom; + } else if (name == Slice("readwhilewriting")) { + num_threads++; // Add extra thread for writing + method = &Benchmark::ReadWhileWriting; + } else if (name == Slice("compact")) { + method = &Benchmark::Compact; + } else if (name == Slice("crc32c")) { + method = &Benchmark::Crc32c; + } else if (name == Slice("acquireload")) { + method = &Benchmark::AcquireLoad; + } else if (name == Slice("snappycomp")) { + method = &Benchmark::SnappyCompress; + } else if (name == Slice("snappyuncomp")) { + method = &Benchmark::SnappyUncompress; + } else if (name == Slice("heapprofile")) { + HeapProfile(); + } else if (name == Slice("stats")) { + PrintStats("leveldb.stats"); + } else if (name == Slice("sstables")) { + PrintStats("leveldb.sstables"); + } else { + if (name != Slice()) { // No error message for empty name + fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str()); + } + } + + if (fresh_db) { + if (FLAGS_use_existing_db) { + fprintf(stdout, "%-12s : skipped (--use_existing_db is true)\n", + name.ToString().c_str()); + method = NULL; + } else { + delete db_; + db_ = NULL; + DestroyDB(FLAGS_db, Options()); + Open(); + } + } + + if (method != NULL) { + RunBenchmark(num_threads, name, method); + } + } + } + + private: + struct ThreadArg { + Benchmark* bm; + SharedState* shared; + ThreadState* thread; + void (Benchmark::*method)(ThreadState*); + }; + + static void ThreadBody(void* v) { + ThreadArg* arg = reinterpret_cast(v); + SharedState* shared = arg->shared; + ThreadState* thread = arg->thread; + { + MutexLock l(&shared->mu); + shared->num_initialized++; + if (shared->num_initialized >= shared->total) { + shared->cv.SignalAll(); + } + while (!shared->start) { + shared->cv.Wait(); + } + } + + thread->stats.Start(); + (arg->bm->*(arg->method))(thread); + thread->stats.Stop(); + + { + MutexLock l(&shared->mu); + shared->num_done++; + if (shared->num_done >= shared->total) { + shared->cv.SignalAll(); + } + } + } + + void RunBenchmark(int n, Slice name, + void (Benchmark::*method)(ThreadState*)) { + SharedState shared; + shared.total = n; + shared.num_initialized = 0; + shared.num_done = 0; + shared.start = false; + + ThreadArg* arg = new ThreadArg[n]; + for (int i = 0; i < n; i++) { + arg[i].bm = this; + arg[i].method = method; + arg[i].shared = &shared; + arg[i].thread = new ThreadState(i); + arg[i].thread->shared = &shared; + Env::Default()->StartThread(ThreadBody, &arg[i]); + } + + shared.mu.Lock(); + while (shared.num_initialized < n) { + shared.cv.Wait(); + } + + shared.start = true; + shared.cv.SignalAll(); + while (shared.num_done < n) { + shared.cv.Wait(); + } + shared.mu.Unlock(); + + for (int i = 1; i < n; i++) { + arg[0].thread->stats.Merge(arg[i].thread->stats); + } + arg[0].thread->stats.Report(name); + + for (int i = 0; i < n; i++) { + delete arg[i].thread; + } + delete[] arg; + } + + void Crc32c(ThreadState* thread) { + // Checksum about 500MB of data total + const int size = 4096; + const char* label = "(4K per op)"; + std::string data(size, 'x'); + int64_t bytes = 0; + uint32_t crc = 0; + while (bytes < 500 * 1048576) { + crc = crc32c::Value(data.data(), size); + thread->stats.FinishedSingleOp(); + bytes += size; + } + // Print so result is not dead + fprintf(stderr, "... crc=0x%x\r", static_cast(crc)); + + thread->stats.AddBytes(bytes); + thread->stats.AddMessage(label); + } + + void AcquireLoad(ThreadState* thread) { + int dummy; + port::AtomicPointer ap(&dummy); + int count = 0; + void *ptr = NULL; + thread->stats.AddMessage("(each op is 1000 loads)"); + while (count < 100000) { + for (int i = 0; i < 1000; i++) { + ptr = ap.Acquire_Load(); + } + count++; + thread->stats.FinishedSingleOp(); + } + if (ptr == NULL) exit(1); // Disable unused variable warning. + } + + void SnappyCompress(ThreadState* thread) { + RandomGenerator gen; + Slice input = gen.Generate(Options().block_size); + int64_t bytes = 0; + int64_t produced = 0; + bool ok = true; + std::string compressed; + while (ok && bytes < 1024 * 1048576) { // Compress 1G + ok = port::Snappy_Compress(input.data(), input.size(), &compressed); + produced += compressed.size(); + bytes += input.size(); + thread->stats.FinishedSingleOp(); + } + + if (!ok) { + thread->stats.AddMessage("(snappy failure)"); + } else { + char buf[100]; + snprintf(buf, sizeof(buf), "(output: %.1f%%)", + (produced * 100.0) / bytes); + thread->stats.AddMessage(buf); + thread->stats.AddBytes(bytes); + } + } + + void SnappyUncompress(ThreadState* thread) { + RandomGenerator gen; + Slice input = gen.Generate(Options().block_size); + std::string compressed; + bool ok = port::Snappy_Compress(input.data(), input.size(), &compressed); + int64_t bytes = 0; + char* uncompressed = new char[input.size()]; + while (ok && bytes < 1024 * 1048576) { // Compress 1G + ok = port::Snappy_Uncompress(compressed.data(), compressed.size(), + uncompressed); + bytes += input.size(); + thread->stats.FinishedSingleOp(); + } + delete[] uncompressed; + + if (!ok) { + thread->stats.AddMessage("(snappy failure)"); + } else { + thread->stats.AddBytes(bytes); + } + } + + void Open() { + assert(db_ == NULL); + Options options; + options.create_if_missing = !FLAGS_use_existing_db; + options.block_cache = cache_; + options.write_buffer_size = FLAGS_write_buffer_size; + options.max_open_files = FLAGS_open_files; + options.filter_policy = filter_policy_; + Status s = DB::Open(options, FLAGS_db, &db_); + if (!s.ok()) { + fprintf(stderr, "open error: %s\n", s.ToString().c_str()); + exit(1); + } + } + + void WriteSeq(ThreadState* thread) { + DoWrite(thread, true); + } + + void WriteRandom(ThreadState* thread) { + DoWrite(thread, false); + } + + void DoWrite(ThreadState* thread, bool seq) { + if (num_ != FLAGS_num) { + char msg[100]; + snprintf(msg, sizeof(msg), "(%d ops)", num_); + thread->stats.AddMessage(msg); + } + + RandomGenerator gen; + WriteBatch batch; + Status s; + int64_t bytes = 0; + for (int i = 0; i < num_; i += entries_per_batch_) { + batch.Clear(); + for (int j = 0; j < entries_per_batch_; j++) { + const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num); + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + batch.Put(key, gen.Generate(value_size_)); + bytes += value_size_ + strlen(key); + thread->stats.FinishedSingleOp(); + } + s = db_->Write(write_options_, &batch); + if (!s.ok()) { + fprintf(stderr, "put error: %s\n", s.ToString().c_str()); + exit(1); + } + } + thread->stats.AddBytes(bytes); + } + + void ReadSequential(ThreadState* thread) { + Iterator* iter = db_->NewIterator(ReadOptions()); + int i = 0; + int64_t bytes = 0; + for (iter->SeekToFirst(); i < reads_ && iter->Valid(); iter->Next()) { + bytes += iter->key().size() + iter->value().size(); + thread->stats.FinishedSingleOp(); + ++i; + } + delete iter; + thread->stats.AddBytes(bytes); + } + + void ReadReverse(ThreadState* thread) { + Iterator* iter = db_->NewIterator(ReadOptions()); + int i = 0; + int64_t bytes = 0; + for (iter->SeekToLast(); i < reads_ && iter->Valid(); iter->Prev()) { + bytes += iter->key().size() + iter->value().size(); + thread->stats.FinishedSingleOp(); + ++i; + } + delete iter; + thread->stats.AddBytes(bytes); + } + + void ReadRandom(ThreadState* thread) { + ReadOptions options; + std::string value; + int found = 0; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = thread->rand.Next() % FLAGS_num; + snprintf(key, sizeof(key), "%016d", k); + if (db_->Get(options, key, &value).ok()) { + found++; + } + thread->stats.FinishedSingleOp(); + } + char msg[100]; + snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_); + thread->stats.AddMessage(msg); + } + + void ReadMissing(ThreadState* thread) { + ReadOptions options; + std::string value; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = thread->rand.Next() % FLAGS_num; + snprintf(key, sizeof(key), "%016d.", k); + db_->Get(options, key, &value); + thread->stats.FinishedSingleOp(); + } + } + + void ReadHot(ThreadState* thread) { + ReadOptions options; + std::string value; + const int range = (FLAGS_num + 99) / 100; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = thread->rand.Next() % range; + snprintf(key, sizeof(key), "%016d", k); + db_->Get(options, key, &value); + thread->stats.FinishedSingleOp(); + } + } + + void SeekRandom(ThreadState* thread) { + ReadOptions options; + std::string value; + int found = 0; + for (int i = 0; i < reads_; i++) { + Iterator* iter = db_->NewIterator(options); + char key[100]; + const int k = thread->rand.Next() % FLAGS_num; + snprintf(key, sizeof(key), "%016d", k); + iter->Seek(key); + if (iter->Valid() && iter->key() == key) found++; + delete iter; + thread->stats.FinishedSingleOp(); + } + char msg[100]; + snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_); + thread->stats.AddMessage(msg); + } + + void DoDelete(ThreadState* thread, bool seq) { + RandomGenerator gen; + WriteBatch batch; + Status s; + for (int i = 0; i < num_; i += entries_per_batch_) { + batch.Clear(); + for (int j = 0; j < entries_per_batch_; j++) { + const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num); + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + batch.Delete(key); + thread->stats.FinishedSingleOp(); + } + s = db_->Write(write_options_, &batch); + if (!s.ok()) { + fprintf(stderr, "del error: %s\n", s.ToString().c_str()); + exit(1); + } + } + } + + void DeleteSeq(ThreadState* thread) { + DoDelete(thread, true); + } + + void DeleteRandom(ThreadState* thread) { + DoDelete(thread, false); + } + + void ReadWhileWriting(ThreadState* thread) { + if (thread->tid > 0) { + ReadRandom(thread); + } else { + // Special thread that keeps writing until other threads are done. + RandomGenerator gen; + while (true) { + { + MutexLock l(&thread->shared->mu); + if (thread->shared->num_done + 1 >= thread->shared->num_initialized) { + // Other threads have finished + break; + } + } + + const int k = thread->rand.Next() % FLAGS_num; + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + Status s = db_->Put(write_options_, key, gen.Generate(value_size_)); + if (!s.ok()) { + fprintf(stderr, "put error: %s\n", s.ToString().c_str()); + exit(1); + } + } + + // Do not count any of the preceding work/delay in stats. + thread->stats.Start(); + } + } + + void Compact(ThreadState* thread) { + db_->CompactRange(NULL, NULL); + } + + void PrintStats(const char* key) { + std::string stats; + if (!db_->GetProperty(key, &stats)) { + stats = "(failed)"; + } + fprintf(stdout, "\n%s\n", stats.c_str()); + } + + static void WriteToFile(void* arg, const char* buf, int n) { + reinterpret_cast(arg)->Append(Slice(buf, n)); + } + + void HeapProfile() { + char fname[100]; + snprintf(fname, sizeof(fname), "%s/heap-%04d", FLAGS_db, ++heap_counter_); + WritableFile* file; + Status s = Env::Default()->NewWritableFile(fname, &file); + if (!s.ok()) { + fprintf(stderr, "%s\n", s.ToString().c_str()); + return; + } + bool ok = port::GetHeapProfile(WriteToFile, file); + delete file; + if (!ok) { + fprintf(stderr, "heap profiling not supported\n"); + Env::Default()->DeleteFile(fname); + } + } +}; + +} // namespace leveldb + +int main(int argc, char** argv) { + FLAGS_write_buffer_size = leveldb::Options().write_buffer_size; + FLAGS_open_files = leveldb::Options().max_open_files; + std::string default_db_path; + + for (int i = 1; i < argc; i++) { + double d; + int n; + char junk; + if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) { + FLAGS_benchmarks = argv[i] + strlen("--benchmarks="); + } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) { + FLAGS_compression_ratio = d; + } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_histogram = n; + } else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_use_existing_db = n; + } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) { + FLAGS_num = n; + } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) { + FLAGS_reads = n; + } else if (sscanf(argv[i], "--threads=%d%c", &n, &junk) == 1) { + FLAGS_threads = n; + } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) { + FLAGS_value_size = n; + } else if (sscanf(argv[i], "--write_buffer_size=%d%c", &n, &junk) == 1) { + FLAGS_write_buffer_size = n; + } else if (sscanf(argv[i], "--cache_size=%d%c", &n, &junk) == 1) { + FLAGS_cache_size = n; + } else if (sscanf(argv[i], "--bloom_bits=%d%c", &n, &junk) == 1) { + FLAGS_bloom_bits = n; + } else if (sscanf(argv[i], "--open_files=%d%c", &n, &junk) == 1) { + FLAGS_open_files = n; + } else if (strncmp(argv[i], "--db=", 5) == 0) { + FLAGS_db = argv[i] + 5; + } else { + fprintf(stderr, "Invalid flag '%s'\n", argv[i]); + exit(1); + } + } + + // Choose a location for the test database if none given with --db= + if (FLAGS_db == NULL) { + leveldb::Env::Default()->GetTestDirectory(&default_db_path); + default_db_path += "/dbbench"; + FLAGS_db = default_db_path.c_str(); + } + + leveldb::Benchmark benchmark; + benchmark.Run(); + return 0; +} diff --git a/src/leveldb/db/db_impl.cc b/src/leveldb/db/db_impl.cc new file mode 100644 index 0000000..c9de169 --- /dev/null +++ b/src/leveldb/db/db_impl.cc @@ -0,0 +1,1467 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/db_impl.h" + +#include +#include +#include +#include +#include +#include +#include "db/builder.h" +#include "db/db_iter.h" +#include "db/dbformat.h" +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "db/memtable.h" +#include "db/table_cache.h" +#include "db/version_set.h" +#include "db/write_batch_internal.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/status.h" +#include "leveldb/table.h" +#include "leveldb/table_builder.h" +#include "port/port.h" +#include "table/block.h" +#include "table/merger.h" +#include "table/two_level_iterator.h" +#include "util/coding.h" +#include "util/logging.h" +#include "util/mutexlock.h" + +namespace leveldb { + +// Information kept for every waiting writer +struct DBImpl::Writer { + Status status; + WriteBatch* batch; + bool sync; + bool done; + port::CondVar cv; + + explicit Writer(port::Mutex* mu) : cv(mu) { } +}; + +struct DBImpl::CompactionState { + Compaction* const compaction; + + // Sequence numbers < smallest_snapshot are not significant since we + // will never have to service a snapshot below smallest_snapshot. + // Therefore if we have seen a sequence number S <= smallest_snapshot, + // we can drop all entries for the same key with sequence numbers < S. + SequenceNumber smallest_snapshot; + + // Files produced by compaction + struct Output { + uint64_t number; + uint64_t file_size; + InternalKey smallest, largest; + }; + std::vector outputs; + + // State kept for output being generated + WritableFile* outfile; + TableBuilder* builder; + + uint64_t total_bytes; + + Output* current_output() { return &outputs[outputs.size()-1]; } + + explicit CompactionState(Compaction* c) + : compaction(c), + outfile(NULL), + builder(NULL), + total_bytes(0) { + } +}; + +// Fix user-supplied options to be reasonable +template +static void ClipToRange(T* ptr, V minvalue, V maxvalue) { + if (static_cast(*ptr) > maxvalue) *ptr = maxvalue; + if (static_cast(*ptr) < minvalue) *ptr = minvalue; +} +Options SanitizeOptions(const std::string& dbname, + const InternalKeyComparator* icmp, + const InternalFilterPolicy* ipolicy, + const Options& src) { + Options result = src; + result.comparator = icmp; + result.filter_policy = (src.filter_policy != NULL) ? ipolicy : NULL; + ClipToRange(&result.max_open_files, 20, 50000); + ClipToRange(&result.write_buffer_size, 64<<10, 1<<30); + ClipToRange(&result.block_size, 1<<10, 4<<20); + if (result.info_log == NULL) { + // Open a log file in the same directory as the db + src.env->CreateDir(dbname); // In case it does not exist + src.env->RenameFile(InfoLogFileName(dbname), OldInfoLogFileName(dbname)); + Status s = src.env->NewLogger(InfoLogFileName(dbname), &result.info_log); + if (!s.ok()) { + // No place suitable for logging + result.info_log = NULL; + } + } + if (result.block_cache == NULL) { + result.block_cache = NewLRUCache(8 << 20); + } + return result; +} + +DBImpl::DBImpl(const Options& options, const std::string& dbname) + : env_(options.env), + internal_comparator_(options.comparator), + internal_filter_policy_(options.filter_policy), + options_(SanitizeOptions( + dbname, &internal_comparator_, &internal_filter_policy_, options)), + owns_info_log_(options_.info_log != options.info_log), + owns_cache_(options_.block_cache != options.block_cache), + dbname_(dbname), + db_lock_(NULL), + shutting_down_(NULL), + bg_cv_(&mutex_), + mem_(new MemTable(internal_comparator_)), + imm_(NULL), + logfile_(NULL), + logfile_number_(0), + log_(NULL), + tmp_batch_(new WriteBatch), + bg_compaction_scheduled_(false), + manual_compaction_(NULL) { + mem_->Ref(); + has_imm_.Release_Store(NULL); + + // Reserve ten files or so for other uses and give the rest to TableCache. + const int table_cache_size = options.max_open_files - 10; + table_cache_ = new TableCache(dbname_, &options_, table_cache_size); + + versions_ = new VersionSet(dbname_, &options_, table_cache_, + &internal_comparator_); +} + +DBImpl::~DBImpl() { + // Wait for background work to finish + mutex_.Lock(); + shutting_down_.Release_Store(this); // Any non-NULL value is ok + while (bg_compaction_scheduled_) { + bg_cv_.Wait(); + } + mutex_.Unlock(); + + if (db_lock_ != NULL) { + env_->UnlockFile(db_lock_); + } + + delete versions_; + if (mem_ != NULL) mem_->Unref(); + if (imm_ != NULL) imm_->Unref(); + delete tmp_batch_; + delete log_; + delete logfile_; + delete table_cache_; + + if (owns_info_log_) { + delete options_.info_log; + } + if (owns_cache_) { + delete options_.block_cache; + } +} + +Status DBImpl::NewDB() { + VersionEdit new_db; + new_db.SetComparatorName(user_comparator()->Name()); + new_db.SetLogNumber(0); + new_db.SetNextFile(2); + new_db.SetLastSequence(0); + + const std::string manifest = DescriptorFileName(dbname_, 1); + WritableFile* file; + Status s = env_->NewWritableFile(manifest, &file); + if (!s.ok()) { + return s; + } + { + log::Writer log(file); + std::string record; + new_db.EncodeTo(&record); + s = log.AddRecord(record); + if (s.ok()) { + s = file->Close(); + } + } + delete file; + if (s.ok()) { + // Make "CURRENT" file that points to the new manifest file. + s = SetCurrentFile(env_, dbname_, 1); + } else { + env_->DeleteFile(manifest); + } + return s; +} + +void DBImpl::MaybeIgnoreError(Status* s) const { + if (s->ok() || options_.paranoid_checks) { + // No change needed + } else { + Log(options_.info_log, "Ignoring error %s", s->ToString().c_str()); + *s = Status::OK(); + } +} + +void DBImpl::DeleteObsoleteFiles() { + // Make a set of all of the live files + std::set live = pending_outputs_; + versions_->AddLiveFiles(&live); + + std::vector filenames; + env_->GetChildren(dbname_, &filenames); // Ignoring errors on purpose + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type)) { + bool keep = true; + switch (type) { + case kLogFile: + keep = ((number >= versions_->LogNumber()) || + (number == versions_->PrevLogNumber())); + break; + case kDescriptorFile: + // Keep my manifest file, and any newer incarnations' + // (in case there is a race that allows other incarnations) + keep = (number >= versions_->ManifestFileNumber()); + break; + case kTableFile: + keep = (live.find(number) != live.end()); + break; + case kTempFile: + // Any temp files that are currently being written to must + // be recorded in pending_outputs_, which is inserted into "live" + keep = (live.find(number) != live.end()); + break; + case kCurrentFile: + case kDBLockFile: + case kInfoLogFile: + keep = true; + break; + } + + if (!keep) { + if (type == kTableFile) { + table_cache_->Evict(number); + } + Log(options_.info_log, "Delete type=%d #%lld\n", + int(type), + static_cast(number)); + env_->DeleteFile(dbname_ + "/" + filenames[i]); + } + } + } +} + +Status DBImpl::Recover(VersionEdit* edit) { + mutex_.AssertHeld(); + + // Ignore error from CreateDir since the creation of the DB is + // committed only when the descriptor is created, and this directory + // may already exist from a previous failed creation attempt. + env_->CreateDir(dbname_); + assert(db_lock_ == NULL); + Status s = env_->LockFile(LockFileName(dbname_), &db_lock_); + if (!s.ok()) { + return s; + } + + if (!env_->FileExists(CurrentFileName(dbname_))) { + if (options_.create_if_missing) { + s = NewDB(); + if (!s.ok()) { + return s; + } + } else { + return Status::InvalidArgument( + dbname_, "does not exist (create_if_missing is false)"); + } + } else { + if (options_.error_if_exists) { + return Status::InvalidArgument( + dbname_, "exists (error_if_exists is true)"); + } + } + + s = versions_->Recover(); + if (s.ok()) { + SequenceNumber max_sequence(0); + + // Recover from all newer log files than the ones named in the + // descriptor (new log files may have been added by the previous + // incarnation without registering them in the descriptor). + // + // Note that PrevLogNumber() is no longer used, but we pay + // attention to it in case we are recovering a database + // produced by an older version of leveldb. + const uint64_t min_log = versions_->LogNumber(); + const uint64_t prev_log = versions_->PrevLogNumber(); + std::vector filenames; + s = env_->GetChildren(dbname_, &filenames); + if (!s.ok()) { + return s; + } + uint64_t number; + FileType type; + std::vector logs; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) + && type == kLogFile + && ((number >= min_log) || (number == prev_log))) { + logs.push_back(number); + } + } + + // Recover in the order in which the logs were generated + std::sort(logs.begin(), logs.end()); + for (size_t i = 0; i < logs.size(); i++) { + s = RecoverLogFile(logs[i], edit, &max_sequence); + + // The previous incarnation may not have written any MANIFEST + // records after allocating this log number. So we manually + // update the file number allocation counter in VersionSet. + versions_->MarkFileNumberUsed(logs[i]); + } + + if (s.ok()) { + if (versions_->LastSequence() < max_sequence) { + versions_->SetLastSequence(max_sequence); + } + } + } + + return s; +} + +Status DBImpl::RecoverLogFile(uint64_t log_number, + VersionEdit* edit, + SequenceNumber* max_sequence) { + struct LogReporter : public log::Reader::Reporter { + Env* env; + Logger* info_log; + const char* fname; + Status* status; // NULL if options_.paranoid_checks==false + virtual void Corruption(size_t bytes, const Status& s) { + Log(info_log, "%s%s: dropping %d bytes; %s", + (this->status == NULL ? "(ignoring error) " : ""), + fname, static_cast(bytes), s.ToString().c_str()); + if (this->status != NULL && this->status->ok()) *this->status = s; + } + }; + + mutex_.AssertHeld(); + + // Open the log file + std::string fname = LogFileName(dbname_, log_number); + SequentialFile* file; + Status status = env_->NewSequentialFile(fname, &file); + if (!status.ok()) { + MaybeIgnoreError(&status); + return status; + } + + // Create the log reader. + LogReporter reporter; + reporter.env = env_; + reporter.info_log = options_.info_log; + reporter.fname = fname.c_str(); + reporter.status = (options_.paranoid_checks ? &status : NULL); + // We intentially make log::Reader do checksumming even if + // paranoid_checks==false so that corruptions cause entire commits + // to be skipped instead of propagating bad information (like overly + // large sequence numbers). + log::Reader reader(file, &reporter, true/*checksum*/, + 0/*initial_offset*/); + Log(options_.info_log, "Recovering log #%llu", + (unsigned long long) log_number); + + // Read all the records and add to a memtable + std::string scratch; + Slice record; + WriteBatch batch; + MemTable* mem = NULL; + while (reader.ReadRecord(&record, &scratch) && + status.ok()) { + if (record.size() < 12) { + reporter.Corruption( + record.size(), Status::Corruption("log record too small")); + continue; + } + WriteBatchInternal::SetContents(&batch, record); + + if (mem == NULL) { + mem = new MemTable(internal_comparator_); + mem->Ref(); + } + status = WriteBatchInternal::InsertInto(&batch, mem); + MaybeIgnoreError(&status); + if (!status.ok()) { + break; + } + const SequenceNumber last_seq = + WriteBatchInternal::Sequence(&batch) + + WriteBatchInternal::Count(&batch) - 1; + if (last_seq > *max_sequence) { + *max_sequence = last_seq; + } + + if (mem->ApproximateMemoryUsage() > options_.write_buffer_size) { + status = WriteLevel0Table(mem, edit, NULL); + if (!status.ok()) { + // Reflect errors immediately so that conditions like full + // file-systems cause the DB::Open() to fail. + break; + } + mem->Unref(); + mem = NULL; + } + } + + if (status.ok() && mem != NULL) { + status = WriteLevel0Table(mem, edit, NULL); + // Reflect errors immediately so that conditions like full + // file-systems cause the DB::Open() to fail. + } + + if (mem != NULL) mem->Unref(); + delete file; + return status; +} + +Status DBImpl::WriteLevel0Table(MemTable* mem, VersionEdit* edit, + Version* base) { + mutex_.AssertHeld(); + const uint64_t start_micros = env_->NowMicros(); + FileMetaData meta; + meta.number = versions_->NewFileNumber(); + pending_outputs_.insert(meta.number); + Iterator* iter = mem->NewIterator(); + Log(options_.info_log, "Level-0 table #%llu: started", + (unsigned long long) meta.number); + + Status s; + { + mutex_.Unlock(); + s = BuildTable(dbname_, env_, options_, table_cache_, iter, &meta); + mutex_.Lock(); + } + + Log(options_.info_log, "Level-0 table #%llu: %lld bytes %s", + (unsigned long long) meta.number, + (unsigned long long) meta.file_size, + s.ToString().c_str()); + delete iter; + pending_outputs_.erase(meta.number); + + + // Note that if file_size is zero, the file has been deleted and + // should not be added to the manifest. + int level = 0; + if (s.ok() && meta.file_size > 0) { + const Slice min_user_key = meta.smallest.user_key(); + const Slice max_user_key = meta.largest.user_key(); + if (base != NULL) { + level = base->PickLevelForMemTableOutput(min_user_key, max_user_key); + } + edit->AddFile(level, meta.number, meta.file_size, + meta.smallest, meta.largest); + } + + CompactionStats stats; + stats.micros = env_->NowMicros() - start_micros; + stats.bytes_written = meta.file_size; + stats_[level].Add(stats); + return s; +} + +Status DBImpl::CompactMemTable() { + mutex_.AssertHeld(); + assert(imm_ != NULL); + + // Save the contents of the memtable as a new Table + VersionEdit edit; + Version* base = versions_->current(); + base->Ref(); + Status s = WriteLevel0Table(imm_, &edit, base); + base->Unref(); + + if (s.ok() && shutting_down_.Acquire_Load()) { + s = Status::IOError("Deleting DB during memtable compaction"); + } + + // Replace immutable memtable with the generated Table + if (s.ok()) { + edit.SetPrevLogNumber(0); + edit.SetLogNumber(logfile_number_); // Earlier logs no longer needed + s = versions_->LogAndApply(&edit, &mutex_); + } + + if (s.ok()) { + // Commit to the new state + imm_->Unref(); + imm_ = NULL; + has_imm_.Release_Store(NULL); + DeleteObsoleteFiles(); + } + + return s; +} + +void DBImpl::CompactRange(const Slice* begin, const Slice* end) { + int max_level_with_files = 1; + { + MutexLock l(&mutex_); + Version* base = versions_->current(); + for (int level = 1; level < config::kNumLevels; level++) { + if (base->OverlapInLevel(level, begin, end)) { + max_level_with_files = level; + } + } + } + TEST_CompactMemTable(); // TODO(sanjay): Skip if memtable does not overlap + for (int level = 0; level < max_level_with_files; level++) { + TEST_CompactRange(level, begin, end); + } +} + +void DBImpl::TEST_CompactRange(int level, const Slice* begin,const Slice* end) { + assert(level >= 0); + assert(level + 1 < config::kNumLevels); + + InternalKey begin_storage, end_storage; + + ManualCompaction manual; + manual.level = level; + manual.done = false; + if (begin == NULL) { + manual.begin = NULL; + } else { + begin_storage = InternalKey(*begin, kMaxSequenceNumber, kValueTypeForSeek); + manual.begin = &begin_storage; + } + if (end == NULL) { + manual.end = NULL; + } else { + end_storage = InternalKey(*end, 0, static_cast(0)); + manual.end = &end_storage; + } + + MutexLock l(&mutex_); + while (!manual.done) { + while (manual_compaction_ != NULL) { + bg_cv_.Wait(); + } + manual_compaction_ = &manual; + MaybeScheduleCompaction(); + while (manual_compaction_ == &manual) { + bg_cv_.Wait(); + } + } +} + +Status DBImpl::TEST_CompactMemTable() { + // NULL batch means just wait for earlier writes to be done + Status s = Write(WriteOptions(), NULL); + if (s.ok()) { + // Wait until the compaction completes + MutexLock l(&mutex_); + while (imm_ != NULL && bg_error_.ok()) { + bg_cv_.Wait(); + } + if (imm_ != NULL) { + s = bg_error_; + } + } + return s; +} + +void DBImpl::MaybeScheduleCompaction() { + mutex_.AssertHeld(); + if (bg_compaction_scheduled_) { + // Already scheduled + } else if (shutting_down_.Acquire_Load()) { + // DB is being deleted; no more background compactions + } else if (imm_ == NULL && + manual_compaction_ == NULL && + !versions_->NeedsCompaction()) { + // No work to be done + } else { + bg_compaction_scheduled_ = true; + env_->Schedule(&DBImpl::BGWork, this); + } +} + +void DBImpl::BGWork(void* db) { + reinterpret_cast(db)->BackgroundCall(); +} + +void DBImpl::BackgroundCall() { + MutexLock l(&mutex_); + assert(bg_compaction_scheduled_); + if (!shutting_down_.Acquire_Load()) { + Status s = BackgroundCompaction(); + if (s.ok()) { + // Success + } else if (shutting_down_.Acquire_Load()) { + // Error most likely due to shutdown; do not wait + } else { + // Wait a little bit before retrying background compaction in + // case this is an environmental problem and we do not want to + // chew up resources for failed compactions for the duration of + // the problem. + bg_cv_.SignalAll(); // In case a waiter can proceed despite the error + Log(options_.info_log, "Waiting after background compaction error: %s", + s.ToString().c_str()); + mutex_.Unlock(); + env_->SleepForMicroseconds(1000000); + mutex_.Lock(); + } + } + + bg_compaction_scheduled_ = false; + + // Previous compaction may have produced too many files in a level, + // so reschedule another compaction if needed. + MaybeScheduleCompaction(); + bg_cv_.SignalAll(); +} + +Status DBImpl::BackgroundCompaction() { + mutex_.AssertHeld(); + + if (imm_ != NULL) { + return CompactMemTable(); + } + + Compaction* c; + bool is_manual = (manual_compaction_ != NULL); + InternalKey manual_end; + if (is_manual) { + ManualCompaction* m = manual_compaction_; + c = versions_->CompactRange(m->level, m->begin, m->end); + m->done = (c == NULL); + if (c != NULL) { + manual_end = c->input(0, c->num_input_files(0) - 1)->largest; + } + Log(options_.info_log, + "Manual compaction at level-%d from %s .. %s; will stop at %s\n", + m->level, + (m->begin ? m->begin->DebugString().c_str() : "(begin)"), + (m->end ? m->end->DebugString().c_str() : "(end)"), + (m->done ? "(end)" : manual_end.DebugString().c_str())); + } else { + c = versions_->PickCompaction(); + } + + Status status; + if (c == NULL) { + // Nothing to do + } else if (!is_manual && c->IsTrivialMove()) { + // Move file to next level + assert(c->num_input_files(0) == 1); + FileMetaData* f = c->input(0, 0); + c->edit()->DeleteFile(c->level(), f->number); + c->edit()->AddFile(c->level() + 1, f->number, f->file_size, + f->smallest, f->largest); + status = versions_->LogAndApply(c->edit(), &mutex_); + VersionSet::LevelSummaryStorage tmp; + Log(options_.info_log, "Moved #%lld to level-%d %lld bytes %s: %s\n", + static_cast(f->number), + c->level() + 1, + static_cast(f->file_size), + status.ToString().c_str(), + versions_->LevelSummary(&tmp)); + } else { + CompactionState* compact = new CompactionState(c); + status = DoCompactionWork(compact); + CleanupCompaction(compact); + c->ReleaseInputs(); + DeleteObsoleteFiles(); + } + delete c; + + if (status.ok()) { + // Done + } else if (shutting_down_.Acquire_Load()) { + // Ignore compaction errors found during shutting down + } else { + Log(options_.info_log, + "Compaction error: %s", status.ToString().c_str()); + if (options_.paranoid_checks && bg_error_.ok()) { + bg_error_ = status; + } + } + + if (is_manual) { + ManualCompaction* m = manual_compaction_; + if (!status.ok()) { + m->done = true; + } + if (!m->done) { + // We only compacted part of the requested range. Update *m + // to the range that is left to be compacted. + m->tmp_storage = manual_end; + m->begin = &m->tmp_storage; + } + manual_compaction_ = NULL; + } + return status; +} + +void DBImpl::CleanupCompaction(CompactionState* compact) { + mutex_.AssertHeld(); + if (compact->builder != NULL) { + // May happen if we get a shutdown call in the middle of compaction + compact->builder->Abandon(); + delete compact->builder; + } else { + assert(compact->outfile == NULL); + } + delete compact->outfile; + for (size_t i = 0; i < compact->outputs.size(); i++) { + const CompactionState::Output& out = compact->outputs[i]; + pending_outputs_.erase(out.number); + } + delete compact; +} + +Status DBImpl::OpenCompactionOutputFile(CompactionState* compact) { + assert(compact != NULL); + assert(compact->builder == NULL); + uint64_t file_number; + { + mutex_.Lock(); + file_number = versions_->NewFileNumber(); + pending_outputs_.insert(file_number); + CompactionState::Output out; + out.number = file_number; + out.smallest.Clear(); + out.largest.Clear(); + compact->outputs.push_back(out); + mutex_.Unlock(); + } + + // Make the output file + std::string fname = TableFileName(dbname_, file_number); + Status s = env_->NewWritableFile(fname, &compact->outfile); + if (s.ok()) { + compact->builder = new TableBuilder(options_, compact->outfile); + } + return s; +} + +Status DBImpl::FinishCompactionOutputFile(CompactionState* compact, + Iterator* input) { + assert(compact != NULL); + assert(compact->outfile != NULL); + assert(compact->builder != NULL); + + const uint64_t output_number = compact->current_output()->number; + assert(output_number != 0); + + // Check for iterator errors + Status s = input->status(); + const uint64_t current_entries = compact->builder->NumEntries(); + if (s.ok()) { + s = compact->builder->Finish(); + } else { + compact->builder->Abandon(); + } + const uint64_t current_bytes = compact->builder->FileSize(); + compact->current_output()->file_size = current_bytes; + compact->total_bytes += current_bytes; + delete compact->builder; + compact->builder = NULL; + + // Finish and check for file errors + if (s.ok()) { + s = compact->outfile->Sync(); + } + if (s.ok()) { + s = compact->outfile->Close(); + } + delete compact->outfile; + compact->outfile = NULL; + + if (s.ok() && current_entries > 0) { + // Verify that the table is usable + Iterator* iter = table_cache_->NewIterator(ReadOptions(), + output_number, + current_bytes); + s = iter->status(); + delete iter; + if (s.ok()) { + Log(options_.info_log, + "Generated table #%llu: %lld keys, %lld bytes", + (unsigned long long) output_number, + (unsigned long long) current_entries, + (unsigned long long) current_bytes); + } + } + return s; +} + + +Status DBImpl::InstallCompactionResults(CompactionState* compact) { + mutex_.AssertHeld(); + Log(options_.info_log, "Compacted %d@%d + %d@%d files => %lld bytes", + compact->compaction->num_input_files(0), + compact->compaction->level(), + compact->compaction->num_input_files(1), + compact->compaction->level() + 1, + static_cast(compact->total_bytes)); + + // Add compaction outputs + compact->compaction->AddInputDeletions(compact->compaction->edit()); + const int level = compact->compaction->level(); + for (size_t i = 0; i < compact->outputs.size(); i++) { + const CompactionState::Output& out = compact->outputs[i]; + compact->compaction->edit()->AddFile( + level + 1, + out.number, out.file_size, out.smallest, out.largest); + } + return versions_->LogAndApply(compact->compaction->edit(), &mutex_); +} + +Status DBImpl::DoCompactionWork(CompactionState* compact) { + const uint64_t start_micros = env_->NowMicros(); + int64_t imm_micros = 0; // Micros spent doing imm_ compactions + + Log(options_.info_log, "Compacting %d@%d + %d@%d files", + compact->compaction->num_input_files(0), + compact->compaction->level(), + compact->compaction->num_input_files(1), + compact->compaction->level() + 1); + + assert(versions_->NumLevelFiles(compact->compaction->level()) > 0); + assert(compact->builder == NULL); + assert(compact->outfile == NULL); + if (snapshots_.empty()) { + compact->smallest_snapshot = versions_->LastSequence(); + } else { + compact->smallest_snapshot = snapshots_.oldest()->number_; + } + + // Release mutex while we're actually doing the compaction work + mutex_.Unlock(); + + Iterator* input = versions_->MakeInputIterator(compact->compaction); + input->SeekToFirst(); + Status status; + ParsedInternalKey ikey; + std::string current_user_key; + bool has_current_user_key = false; + SequenceNumber last_sequence_for_key = kMaxSequenceNumber; + for (; input->Valid() && !shutting_down_.Acquire_Load(); ) { + // Prioritize immutable compaction work + if (has_imm_.NoBarrier_Load() != NULL) { + const uint64_t imm_start = env_->NowMicros(); + mutex_.Lock(); + if (imm_ != NULL) { + CompactMemTable(); + bg_cv_.SignalAll(); // Wakeup MakeRoomForWrite() if necessary + } + mutex_.Unlock(); + imm_micros += (env_->NowMicros() - imm_start); + } + + Slice key = input->key(); + if (compact->compaction->ShouldStopBefore(key) && + compact->builder != NULL) { + status = FinishCompactionOutputFile(compact, input); + if (!status.ok()) { + break; + } + } + + // Handle key/value, add to state, etc. + bool drop = false; + if (!ParseInternalKey(key, &ikey)) { + // Do not hide error keys + current_user_key.clear(); + has_current_user_key = false; + last_sequence_for_key = kMaxSequenceNumber; + } else { + if (!has_current_user_key || + user_comparator()->Compare(ikey.user_key, + Slice(current_user_key)) != 0) { + // First occurrence of this user key + current_user_key.assign(ikey.user_key.data(), ikey.user_key.size()); + has_current_user_key = true; + last_sequence_for_key = kMaxSequenceNumber; + } + + if (last_sequence_for_key <= compact->smallest_snapshot) { + // Hidden by an newer entry for same user key + drop = true; // (A) + } else if (ikey.type == kTypeDeletion && + ikey.sequence <= compact->smallest_snapshot && + compact->compaction->IsBaseLevelForKey(ikey.user_key)) { + // For this user key: + // (1) there is no data in higher levels + // (2) data in lower levels will have larger sequence numbers + // (3) data in layers that are being compacted here and have + // smaller sequence numbers will be dropped in the next + // few iterations of this loop (by rule (A) above). + // Therefore this deletion marker is obsolete and can be dropped. + drop = true; + } + + last_sequence_for_key = ikey.sequence; + } +#if 0 + Log(options_.info_log, + " Compact: %s, seq %d, type: %d %d, drop: %d, is_base: %d, " + "%d smallest_snapshot: %d", + ikey.user_key.ToString().c_str(), + (int)ikey.sequence, ikey.type, kTypeValue, drop, + compact->compaction->IsBaseLevelForKey(ikey.user_key), + (int)last_sequence_for_key, (int)compact->smallest_snapshot); +#endif + + if (!drop) { + // Open output file if necessary + if (compact->builder == NULL) { + status = OpenCompactionOutputFile(compact); + if (!status.ok()) { + break; + } + } + if (compact->builder->NumEntries() == 0) { + compact->current_output()->smallest.DecodeFrom(key); + } + compact->current_output()->largest.DecodeFrom(key); + compact->builder->Add(key, input->value()); + + // Close output file if it is big enough + if (compact->builder->FileSize() >= + compact->compaction->MaxOutputFileSize()) { + status = FinishCompactionOutputFile(compact, input); + if (!status.ok()) { + break; + } + } + } + + input->Next(); + } + + if (status.ok() && shutting_down_.Acquire_Load()) { + status = Status::IOError("Deleting DB during compaction"); + } + if (status.ok() && compact->builder != NULL) { + status = FinishCompactionOutputFile(compact, input); + } + if (status.ok()) { + status = input->status(); + } + delete input; + input = NULL; + + CompactionStats stats; + stats.micros = env_->NowMicros() - start_micros - imm_micros; + for (int which = 0; which < 2; which++) { + for (int i = 0; i < compact->compaction->num_input_files(which); i++) { + stats.bytes_read += compact->compaction->input(which, i)->file_size; + } + } + for (size_t i = 0; i < compact->outputs.size(); i++) { + stats.bytes_written += compact->outputs[i].file_size; + } + + mutex_.Lock(); + stats_[compact->compaction->level() + 1].Add(stats); + + if (status.ok()) { + status = InstallCompactionResults(compact); + } + VersionSet::LevelSummaryStorage tmp; + Log(options_.info_log, + "compacted to: %s", versions_->LevelSummary(&tmp)); + return status; +} + +namespace { +struct IterState { + port::Mutex* mu; + Version* version; + MemTable* mem; + MemTable* imm; +}; + +static void CleanupIteratorState(void* arg1, void* arg2) { + IterState* state = reinterpret_cast(arg1); + state->mu->Lock(); + state->mem->Unref(); + if (state->imm != NULL) state->imm->Unref(); + state->version->Unref(); + state->mu->Unlock(); + delete state; +} +} // namespace + +Iterator* DBImpl::NewInternalIterator(const ReadOptions& options, + SequenceNumber* latest_snapshot) { + IterState* cleanup = new IterState; + mutex_.Lock(); + *latest_snapshot = versions_->LastSequence(); + + // Collect together all needed child iterators + std::vector list; + list.push_back(mem_->NewIterator()); + mem_->Ref(); + if (imm_ != NULL) { + list.push_back(imm_->NewIterator()); + imm_->Ref(); + } + versions_->current()->AddIterators(options, &list); + Iterator* internal_iter = + NewMergingIterator(&internal_comparator_, &list[0], list.size()); + versions_->current()->Ref(); + + cleanup->mu = &mutex_; + cleanup->mem = mem_; + cleanup->imm = imm_; + cleanup->version = versions_->current(); + internal_iter->RegisterCleanup(CleanupIteratorState, cleanup, NULL); + + mutex_.Unlock(); + return internal_iter; +} + +Iterator* DBImpl::TEST_NewInternalIterator() { + SequenceNumber ignored; + return NewInternalIterator(ReadOptions(), &ignored); +} + +int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes() { + MutexLock l(&mutex_); + return versions_->MaxNextLevelOverlappingBytes(); +} + +Status DBImpl::Get(const ReadOptions& options, + const Slice& key, + std::string* value) { + Status s; + MutexLock l(&mutex_); + SequenceNumber snapshot; + if (options.snapshot != NULL) { + snapshot = reinterpret_cast(options.snapshot)->number_; + } else { + snapshot = versions_->LastSequence(); + } + + MemTable* mem = mem_; + MemTable* imm = imm_; + Version* current = versions_->current(); + mem->Ref(); + if (imm != NULL) imm->Ref(); + current->Ref(); + + bool have_stat_update = false; + Version::GetStats stats; + + // Unlock while reading from files and memtables + { + mutex_.Unlock(); + // First look in the memtable, then in the immutable memtable (if any). + LookupKey lkey(key, snapshot); + if (mem->Get(lkey, value, &s)) { + // Done + } else if (imm != NULL && imm->Get(lkey, value, &s)) { + // Done + } else { + s = current->Get(options, lkey, value, &stats); + have_stat_update = true; + } + mutex_.Lock(); + } + + if (have_stat_update && current->UpdateStats(stats)) { + MaybeScheduleCompaction(); + } + mem->Unref(); + if (imm != NULL) imm->Unref(); + current->Unref(); + return s; +} + +Iterator* DBImpl::NewIterator(const ReadOptions& options) { + SequenceNumber latest_snapshot; + Iterator* internal_iter = NewInternalIterator(options, &latest_snapshot); + return NewDBIterator( + &dbname_, env_, user_comparator(), internal_iter, + (options.snapshot != NULL + ? reinterpret_cast(options.snapshot)->number_ + : latest_snapshot)); +} + +const Snapshot* DBImpl::GetSnapshot() { + MutexLock l(&mutex_); + return snapshots_.New(versions_->LastSequence()); +} + +void DBImpl::ReleaseSnapshot(const Snapshot* s) { + MutexLock l(&mutex_); + snapshots_.Delete(reinterpret_cast(s)); +} + +// Convenience methods +Status DBImpl::Put(const WriteOptions& o, const Slice& key, const Slice& val) { + return DB::Put(o, key, val); +} + +Status DBImpl::Delete(const WriteOptions& options, const Slice& key) { + return DB::Delete(options, key); +} + +Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) { + Writer w(&mutex_); + w.batch = my_batch; + w.sync = options.sync; + w.done = false; + + MutexLock l(&mutex_); + writers_.push_back(&w); + while (!w.done && &w != writers_.front()) { + w.cv.Wait(); + } + if (w.done) { + return w.status; + } + + // May temporarily unlock and wait. + Status status = MakeRoomForWrite(my_batch == NULL); + uint64_t last_sequence = versions_->LastSequence(); + Writer* last_writer = &w; + if (status.ok() && my_batch != NULL) { // NULL batch is for compactions + WriteBatch* updates = BuildBatchGroup(&last_writer); + WriteBatchInternal::SetSequence(updates, last_sequence + 1); + last_sequence += WriteBatchInternal::Count(updates); + + // Add to log and apply to memtable. We can release the lock + // during this phase since &w is currently responsible for logging + // and protects against concurrent loggers and concurrent writes + // into mem_. + { + mutex_.Unlock(); + status = log_->AddRecord(WriteBatchInternal::Contents(updates)); + if (status.ok() && options.sync) { + status = logfile_->Sync(); + } + if (status.ok()) { + status = WriteBatchInternal::InsertInto(updates, mem_); + } + mutex_.Lock(); + } + if (updates == tmp_batch_) tmp_batch_->Clear(); + + versions_->SetLastSequence(last_sequence); + } + + while (true) { + Writer* ready = writers_.front(); + writers_.pop_front(); + if (ready != &w) { + ready->status = status; + ready->done = true; + ready->cv.Signal(); + } + if (ready == last_writer) break; + } + + // Notify new head of write queue + if (!writers_.empty()) { + writers_.front()->cv.Signal(); + } + + return status; +} + +// REQUIRES: Writer list must be non-empty +// REQUIRES: First writer must have a non-NULL batch +WriteBatch* DBImpl::BuildBatchGroup(Writer** last_writer) { + assert(!writers_.empty()); + Writer* first = writers_.front(); + WriteBatch* result = first->batch; + assert(result != NULL); + + size_t size = WriteBatchInternal::ByteSize(first->batch); + + // Allow the group to grow up to a maximum size, but if the + // original write is small, limit the growth so we do not slow + // down the small write too much. + size_t max_size = 1 << 20; + if (size <= (128<<10)) { + max_size = size + (128<<10); + } + + *last_writer = first; + std::deque::iterator iter = writers_.begin(); + ++iter; // Advance past "first" + for (; iter != writers_.end(); ++iter) { + Writer* w = *iter; + if (w->sync && !first->sync) { + // Do not include a sync write into a batch handled by a non-sync write. + break; + } + + if (w->batch != NULL) { + size += WriteBatchInternal::ByteSize(w->batch); + if (size > max_size) { + // Do not make batch too big + break; + } + + // Append to *reuslt + if (result == first->batch) { + // Switch to temporary batch instead of disturbing caller's batch + result = tmp_batch_; + assert(WriteBatchInternal::Count(result) == 0); + WriteBatchInternal::Append(result, first->batch); + } + WriteBatchInternal::Append(result, w->batch); + } + *last_writer = w; + } + return result; +} + +// REQUIRES: mutex_ is held +// REQUIRES: this thread is currently at the front of the writer queue +Status DBImpl::MakeRoomForWrite(bool force) { + mutex_.AssertHeld(); + assert(!writers_.empty()); + bool allow_delay = !force; + Status s; + while (true) { + if (!bg_error_.ok()) { + // Yield previous error + s = bg_error_; + break; + } else if ( + allow_delay && + versions_->NumLevelFiles(0) >= config::kL0_SlowdownWritesTrigger) { + // We are getting close to hitting a hard limit on the number of + // L0 files. Rather than delaying a single write by several + // seconds when we hit the hard limit, start delaying each + // individual write by 1ms to reduce latency variance. Also, + // this delay hands over some CPU to the compaction thread in + // case it is sharing the same core as the writer. + mutex_.Unlock(); + env_->SleepForMicroseconds(1000); + allow_delay = false; // Do not delay a single write more than once + mutex_.Lock(); + } else if (!force && + (mem_->ApproximateMemoryUsage() <= options_.write_buffer_size)) { + // There is room in current memtable + break; + } else if (imm_ != NULL) { + // We have filled up the current memtable, but the previous + // one is still being compacted, so we wait. + bg_cv_.Wait(); + } else if (versions_->NumLevelFiles(0) >= config::kL0_StopWritesTrigger) { + // There are too many level-0 files. + Log(options_.info_log, "waiting...\n"); + bg_cv_.Wait(); + } else { + // Attempt to switch to a new memtable and trigger compaction of old + assert(versions_->PrevLogNumber() == 0); + uint64_t new_log_number = versions_->NewFileNumber(); + WritableFile* lfile = NULL; + s = env_->NewWritableFile(LogFileName(dbname_, new_log_number), &lfile); + if (!s.ok()) { + // Avoid chewing through file number space in a tight loop. + versions_->ReuseFileNumber(new_log_number); + break; + } + delete log_; + delete logfile_; + logfile_ = lfile; + logfile_number_ = new_log_number; + log_ = new log::Writer(lfile); + imm_ = mem_; + has_imm_.Release_Store(imm_); + mem_ = new MemTable(internal_comparator_); + mem_->Ref(); + force = false; // Do not force another compaction if have room + MaybeScheduleCompaction(); + } + } + return s; +} + +bool DBImpl::GetProperty(const Slice& property, std::string* value) { + value->clear(); + + MutexLock l(&mutex_); + Slice in = property; + Slice prefix("leveldb."); + if (!in.starts_with(prefix)) return false; + in.remove_prefix(prefix.size()); + + if (in.starts_with("num-files-at-level")) { + in.remove_prefix(strlen("num-files-at-level")); + uint64_t level; + bool ok = ConsumeDecimalNumber(&in, &level) && in.empty(); + if (!ok || level >= config::kNumLevels) { + return false; + } else { + char buf[100]; + snprintf(buf, sizeof(buf), "%d", + versions_->NumLevelFiles(static_cast(level))); + *value = buf; + return true; + } + } else if (in == "stats") { + char buf[200]; + snprintf(buf, sizeof(buf), + " Compactions\n" + "Level Files Size(MB) Time(sec) Read(MB) Write(MB)\n" + "--------------------------------------------------\n" + ); + value->append(buf); + for (int level = 0; level < config::kNumLevels; level++) { + int files = versions_->NumLevelFiles(level); + if (stats_[level].micros > 0 || files > 0) { + snprintf( + buf, sizeof(buf), + "%3d %8d %8.0f %9.0f %8.0f %9.0f\n", + level, + files, + versions_->NumLevelBytes(level) / 1048576.0, + stats_[level].micros / 1e6, + stats_[level].bytes_read / 1048576.0, + stats_[level].bytes_written / 1048576.0); + value->append(buf); + } + } + return true; + } else if (in == "sstables") { + *value = versions_->current()->DebugString(); + return true; + } + + return false; +} + +void DBImpl::GetApproximateSizes( + const Range* range, int n, + uint64_t* sizes) { + // TODO(opt): better implementation + Version* v; + { + MutexLock l(&mutex_); + versions_->current()->Ref(); + v = versions_->current(); + } + + for (int i = 0; i < n; i++) { + // Convert user_key into a corresponding internal key. + InternalKey k1(range[i].start, kMaxSequenceNumber, kValueTypeForSeek); + InternalKey k2(range[i].limit, kMaxSequenceNumber, kValueTypeForSeek); + uint64_t start = versions_->ApproximateOffsetOf(v, k1); + uint64_t limit = versions_->ApproximateOffsetOf(v, k2); + sizes[i] = (limit >= start ? limit - start : 0); + } + + { + MutexLock l(&mutex_); + v->Unref(); + } +} + +// Default implementations of convenience methods that subclasses of DB +// can call if they wish +Status DB::Put(const WriteOptions& opt, const Slice& key, const Slice& value) { + WriteBatch batch; + batch.Put(key, value); + return Write(opt, &batch); +} + +Status DB::Delete(const WriteOptions& opt, const Slice& key) { + WriteBatch batch; + batch.Delete(key); + return Write(opt, &batch); +} + +DB::~DB() { } + +Status DB::Open(const Options& options, const std::string& dbname, + DB** dbptr) { + *dbptr = NULL; + + DBImpl* impl = new DBImpl(options, dbname); + impl->mutex_.Lock(); + VersionEdit edit; + Status s = impl->Recover(&edit); // Handles create_if_missing, error_if_exists + if (s.ok()) { + uint64_t new_log_number = impl->versions_->NewFileNumber(); + WritableFile* lfile; + s = options.env->NewWritableFile(LogFileName(dbname, new_log_number), + &lfile); + if (s.ok()) { + edit.SetLogNumber(new_log_number); + impl->logfile_ = lfile; + impl->logfile_number_ = new_log_number; + impl->log_ = new log::Writer(lfile); + s = impl->versions_->LogAndApply(&edit, &impl->mutex_); + } + if (s.ok()) { + impl->DeleteObsoleteFiles(); + impl->MaybeScheduleCompaction(); + } + } + impl->mutex_.Unlock(); + if (s.ok()) { + *dbptr = impl; + } else { + delete impl; + } + return s; +} + +Snapshot::~Snapshot() { +} + +Status DestroyDB(const std::string& dbname, const Options& options) { + Env* env = options.env; + std::vector filenames; + // Ignore error in case directory does not exist + env->GetChildren(dbname, &filenames); + if (filenames.empty()) { + return Status::OK(); + } + + FileLock* lock; + const std::string lockname = LockFileName(dbname); + Status result = env->LockFile(lockname, &lock); + if (result.ok()) { + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type) && + type != kDBLockFile) { // Lock file will be deleted at end + Status del = env->DeleteFile(dbname + "/" + filenames[i]); + if (result.ok() && !del.ok()) { + result = del; + } + } + } + env->UnlockFile(lock); // Ignore error since state is already gone + env->DeleteFile(lockname); + env->DeleteDir(dbname); // Ignore error in case dir contains other files + } + return result; +} + +} // namespace leveldb diff --git a/src/leveldb/db/db_impl.h b/src/leveldb/db/db_impl.h new file mode 100644 index 0000000..bd29dd8 --- /dev/null +++ b/src/leveldb/db/db_impl.h @@ -0,0 +1,202 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_DB_IMPL_H_ +#define STORAGE_LEVELDB_DB_DB_IMPL_H_ + +#include +#include +#include "db/dbformat.h" +#include "db/log_writer.h" +#include "db/snapshot.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "port/port.h" +#include "port/thread_annotations.h" + +namespace leveldb { + +class MemTable; +class TableCache; +class Version; +class VersionEdit; +class VersionSet; + +class DBImpl : public DB { + public: + DBImpl(const Options& options, const std::string& dbname); + virtual ~DBImpl(); + + // Implementations of the DB interface + virtual Status Put(const WriteOptions&, const Slice& key, const Slice& value); + virtual Status Delete(const WriteOptions&, const Slice& key); + virtual Status Write(const WriteOptions& options, WriteBatch* updates); + virtual Status Get(const ReadOptions& options, + const Slice& key, + std::string* value); + virtual Iterator* NewIterator(const ReadOptions&); + virtual const Snapshot* GetSnapshot(); + virtual void ReleaseSnapshot(const Snapshot* snapshot); + virtual bool GetProperty(const Slice& property, std::string* value); + virtual void GetApproximateSizes(const Range* range, int n, uint64_t* sizes); + virtual void CompactRange(const Slice* begin, const Slice* end); + + // Extra methods (for testing) that are not in the public DB interface + + // Compact any files in the named level that overlap [*begin,*end] + void TEST_CompactRange(int level, const Slice* begin, const Slice* end); + + // Force current memtable contents to be compacted. + Status TEST_CompactMemTable(); + + // Return an internal iterator over the current state of the database. + // The keys of this iterator are internal keys (see format.h). + // The returned iterator should be deleted when no longer needed. + Iterator* TEST_NewInternalIterator(); + + // Return the maximum overlapping data (in bytes) at next level for any + // file at a level >= 1. + int64_t TEST_MaxNextLevelOverlappingBytes(); + + private: + friend class DB; + struct CompactionState; + struct Writer; + + Iterator* NewInternalIterator(const ReadOptions&, + SequenceNumber* latest_snapshot); + + Status NewDB(); + + // Recover the descriptor from persistent storage. May do a significant + // amount of work to recover recently logged updates. Any changes to + // be made to the descriptor are added to *edit. + Status Recover(VersionEdit* edit) EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + void MaybeIgnoreError(Status* s) const; + + // Delete any unneeded files and stale in-memory entries. + void DeleteObsoleteFiles(); + + // Compact the in-memory write buffer to disk. Switches to a new + // log-file/memtable and writes a new descriptor iff successful. + Status CompactMemTable() + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Status RecoverLogFile(uint64_t log_number, + VersionEdit* edit, + SequenceNumber* max_sequence) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Status WriteLevel0Table(MemTable* mem, VersionEdit* edit, Version* base) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Status MakeRoomForWrite(bool force /* compact even if there is room? */) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + WriteBatch* BuildBatchGroup(Writer** last_writer); + + void MaybeScheduleCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_); + static void BGWork(void* db); + void BackgroundCall(); + Status BackgroundCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_); + void CleanupCompaction(CompactionState* compact) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + Status DoCompactionWork(CompactionState* compact) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Status OpenCompactionOutputFile(CompactionState* compact); + Status FinishCompactionOutputFile(CompactionState* compact, Iterator* input); + Status InstallCompactionResults(CompactionState* compact) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Constant after construction + Env* const env_; + const InternalKeyComparator internal_comparator_; + const InternalFilterPolicy internal_filter_policy_; + const Options options_; // options_.comparator == &internal_comparator_ + bool owns_info_log_; + bool owns_cache_; + const std::string dbname_; + + // table_cache_ provides its own synchronization + TableCache* table_cache_; + + // Lock over the persistent DB state. Non-NULL iff successfully acquired. + FileLock* db_lock_; + + // State below is protected by mutex_ + port::Mutex mutex_; + port::AtomicPointer shutting_down_; + port::CondVar bg_cv_; // Signalled when background work finishes + MemTable* mem_; + MemTable* imm_; // Memtable being compacted + port::AtomicPointer has_imm_; // So bg thread can detect non-NULL imm_ + WritableFile* logfile_; + uint64_t logfile_number_; + log::Writer* log_; + + // Queue of writers. + std::deque writers_; + WriteBatch* tmp_batch_; + + SnapshotList snapshots_; + + // Set of table files to protect from deletion because they are + // part of ongoing compactions. + std::set pending_outputs_; + + // Has a background compaction been scheduled or is running? + bool bg_compaction_scheduled_; + + // Information for a manual compaction + struct ManualCompaction { + int level; + bool done; + const InternalKey* begin; // NULL means beginning of key range + const InternalKey* end; // NULL means end of key range + InternalKey tmp_storage; // Used to keep track of compaction progress + }; + ManualCompaction* manual_compaction_; + + VersionSet* versions_; + + // Have we encountered a background error in paranoid mode? + Status bg_error_; + + // Per level compaction stats. stats_[level] stores the stats for + // compactions that produced data for the specified "level". + struct CompactionStats { + int64_t micros; + int64_t bytes_read; + int64_t bytes_written; + + CompactionStats() : micros(0), bytes_read(0), bytes_written(0) { } + + void Add(const CompactionStats& c) { + this->micros += c.micros; + this->bytes_read += c.bytes_read; + this->bytes_written += c.bytes_written; + } + }; + CompactionStats stats_[config::kNumLevels]; + + // No copying allowed + DBImpl(const DBImpl&); + void operator=(const DBImpl&); + + const Comparator* user_comparator() const { + return internal_comparator_.user_comparator(); + } +}; + +// Sanitize db options. The caller should delete result.info_log if +// it is not equal to src.info_log. +extern Options SanitizeOptions(const std::string& db, + const InternalKeyComparator* icmp, + const InternalFilterPolicy* ipolicy, + const Options& src); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_DB_IMPL_H_ diff --git a/src/leveldb/db/db_iter.cc b/src/leveldb/db/db_iter.cc new file mode 100644 index 0000000..87dca2d --- /dev/null +++ b/src/leveldb/db/db_iter.cc @@ -0,0 +1,299 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/db_iter.h" + +#include "db/filename.h" +#include "db/dbformat.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "port/port.h" +#include "util/logging.h" +#include "util/mutexlock.h" + +namespace leveldb { + +#if 0 +static void DumpInternalIter(Iterator* iter) { + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + ParsedInternalKey k; + if (!ParseInternalKey(iter->key(), &k)) { + fprintf(stderr, "Corrupt '%s'\n", EscapeString(iter->key()).c_str()); + } else { + fprintf(stderr, "@ '%s'\n", k.DebugString().c_str()); + } + } +} +#endif + +namespace { + +// Memtables and sstables that make the DB representation contain +// (userkey,seq,type) => uservalue entries. DBIter +// combines multiple entries for the same userkey found in the DB +// representation into a single entry while accounting for sequence +// numbers, deletion markers, overwrites, etc. +class DBIter: public Iterator { + public: + // Which direction is the iterator currently moving? + // (1) When moving forward, the internal iterator is positioned at + // the exact entry that yields this->key(), this->value() + // (2) When moving backwards, the internal iterator is positioned + // just before all entries whose user key == this->key(). + enum Direction { + kForward, + kReverse + }; + + DBIter(const std::string* dbname, Env* env, + const Comparator* cmp, Iterator* iter, SequenceNumber s) + : dbname_(dbname), + env_(env), + user_comparator_(cmp), + iter_(iter), + sequence_(s), + direction_(kForward), + valid_(false) { + } + virtual ~DBIter() { + delete iter_; + } + virtual bool Valid() const { return valid_; } + virtual Slice key() const { + assert(valid_); + return (direction_ == kForward) ? ExtractUserKey(iter_->key()) : saved_key_; + } + virtual Slice value() const { + assert(valid_); + return (direction_ == kForward) ? iter_->value() : saved_value_; + } + virtual Status status() const { + if (status_.ok()) { + return iter_->status(); + } else { + return status_; + } + } + + virtual void Next(); + virtual void Prev(); + virtual void Seek(const Slice& target); + virtual void SeekToFirst(); + virtual void SeekToLast(); + + private: + void FindNextUserEntry(bool skipping, std::string* skip); + void FindPrevUserEntry(); + bool ParseKey(ParsedInternalKey* key); + + inline void SaveKey(const Slice& k, std::string* dst) { + dst->assign(k.data(), k.size()); + } + + inline void ClearSavedValue() { + if (saved_value_.capacity() > 1048576) { + std::string empty; + swap(empty, saved_value_); + } else { + saved_value_.clear(); + } + } + + const std::string* const dbname_; + Env* const env_; + const Comparator* const user_comparator_; + Iterator* const iter_; + SequenceNumber const sequence_; + + Status status_; + std::string saved_key_; // == current key when direction_==kReverse + std::string saved_value_; // == current raw value when direction_==kReverse + Direction direction_; + bool valid_; + + // No copying allowed + DBIter(const DBIter&); + void operator=(const DBIter&); +}; + +inline bool DBIter::ParseKey(ParsedInternalKey* ikey) { + if (!ParseInternalKey(iter_->key(), ikey)) { + status_ = Status::Corruption("corrupted internal key in DBIter"); + return false; + } else { + return true; + } +} + +void DBIter::Next() { + assert(valid_); + + if (direction_ == kReverse) { // Switch directions? + direction_ = kForward; + // iter_ is pointing just before the entries for this->key(), + // so advance into the range of entries for this->key() and then + // use the normal skipping code below. + if (!iter_->Valid()) { + iter_->SeekToFirst(); + } else { + iter_->Next(); + } + if (!iter_->Valid()) { + valid_ = false; + saved_key_.clear(); + return; + } + } + + // Temporarily use saved_key_ as storage for key to skip. + std::string* skip = &saved_key_; + SaveKey(ExtractUserKey(iter_->key()), skip); + FindNextUserEntry(true, skip); +} + +void DBIter::FindNextUserEntry(bool skipping, std::string* skip) { + // Loop until we hit an acceptable entry to yield + assert(iter_->Valid()); + assert(direction_ == kForward); + do { + ParsedInternalKey ikey; + if (ParseKey(&ikey) && ikey.sequence <= sequence_) { + switch (ikey.type) { + case kTypeDeletion: + // Arrange to skip all upcoming entries for this key since + // they are hidden by this deletion. + SaveKey(ikey.user_key, skip); + skipping = true; + break; + case kTypeValue: + if (skipping && + user_comparator_->Compare(ikey.user_key, *skip) <= 0) { + // Entry hidden + } else { + valid_ = true; + saved_key_.clear(); + return; + } + break; + } + } + iter_->Next(); + } while (iter_->Valid()); + saved_key_.clear(); + valid_ = false; +} + +void DBIter::Prev() { + assert(valid_); + + if (direction_ == kForward) { // Switch directions? + // iter_ is pointing at the current entry. Scan backwards until + // the key changes so we can use the normal reverse scanning code. + assert(iter_->Valid()); // Otherwise valid_ would have been false + SaveKey(ExtractUserKey(iter_->key()), &saved_key_); + while (true) { + iter_->Prev(); + if (!iter_->Valid()) { + valid_ = false; + saved_key_.clear(); + ClearSavedValue(); + return; + } + if (user_comparator_->Compare(ExtractUserKey(iter_->key()), + saved_key_) < 0) { + break; + } + } + direction_ = kReverse; + } + + FindPrevUserEntry(); +} + +void DBIter::FindPrevUserEntry() { + assert(direction_ == kReverse); + + ValueType value_type = kTypeDeletion; + if (iter_->Valid()) { + do { + ParsedInternalKey ikey; + if (ParseKey(&ikey) && ikey.sequence <= sequence_) { + if ((value_type != kTypeDeletion) && + user_comparator_->Compare(ikey.user_key, saved_key_) < 0) { + // We encountered a non-deleted value in entries for previous keys, + break; + } + value_type = ikey.type; + if (value_type == kTypeDeletion) { + saved_key_.clear(); + ClearSavedValue(); + } else { + Slice raw_value = iter_->value(); + if (saved_value_.capacity() > raw_value.size() + 1048576) { + std::string empty; + swap(empty, saved_value_); + } + SaveKey(ExtractUserKey(iter_->key()), &saved_key_); + saved_value_.assign(raw_value.data(), raw_value.size()); + } + } + iter_->Prev(); + } while (iter_->Valid()); + } + + if (value_type == kTypeDeletion) { + // End + valid_ = false; + saved_key_.clear(); + ClearSavedValue(); + direction_ = kForward; + } else { + valid_ = true; + } +} + +void DBIter::Seek(const Slice& target) { + direction_ = kForward; + ClearSavedValue(); + saved_key_.clear(); + AppendInternalKey( + &saved_key_, ParsedInternalKey(target, sequence_, kValueTypeForSeek)); + iter_->Seek(saved_key_); + if (iter_->Valid()) { + FindNextUserEntry(false, &saved_key_ /* temporary storage */); + } else { + valid_ = false; + } +} + +void DBIter::SeekToFirst() { + direction_ = kForward; + ClearSavedValue(); + iter_->SeekToFirst(); + if (iter_->Valid()) { + FindNextUserEntry(false, &saved_key_ /* temporary storage */); + } else { + valid_ = false; + } +} + +void DBIter::SeekToLast() { + direction_ = kReverse; + ClearSavedValue(); + iter_->SeekToLast(); + FindPrevUserEntry(); +} + +} // anonymous namespace + +Iterator* NewDBIterator( + const std::string* dbname, + Env* env, + const Comparator* user_key_comparator, + Iterator* internal_iter, + const SequenceNumber& sequence) { + return new DBIter(dbname, env, user_key_comparator, internal_iter, sequence); +} + +} // namespace leveldb diff --git a/src/leveldb/db/db_iter.h b/src/leveldb/db/db_iter.h new file mode 100644 index 0000000..d9e1b17 --- /dev/null +++ b/src/leveldb/db/db_iter.h @@ -0,0 +1,26 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_DB_ITER_H_ +#define STORAGE_LEVELDB_DB_DB_ITER_H_ + +#include +#include "leveldb/db.h" +#include "db/dbformat.h" + +namespace leveldb { + +// Return a new iterator that converts internal keys (yielded by +// "*internal_iter") that were live at the specified "sequence" number +// into appropriate user keys. +extern Iterator* NewDBIterator( + const std::string* dbname, + Env* env, + const Comparator* user_key_comparator, + Iterator* internal_iter, + const SequenceNumber& sequence); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_DB_ITER_H_ diff --git a/src/leveldb/db/db_test.cc b/src/leveldb/db/db_test.cc new file mode 100644 index 0000000..684ea3b --- /dev/null +++ b/src/leveldb/db/db_test.cc @@ -0,0 +1,2027 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" +#include "leveldb/filter_policy.h" +#include "db/db_impl.h" +#include "db/filename.h" +#include "db/version_set.h" +#include "db/write_batch_internal.h" +#include "leveldb/cache.h" +#include "leveldb/env.h" +#include "leveldb/table.h" +#include "util/hash.h" +#include "util/logging.h" +#include "util/mutexlock.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +static std::string RandomString(Random* rnd, int len) { + std::string r; + test::RandomString(rnd, len, &r); + return r; +} + +namespace { +class AtomicCounter { + private: + port::Mutex mu_; + int count_; + public: + AtomicCounter() : count_(0) { } + void Increment() { + MutexLock l(&mu_); + count_++; + } + int Read() { + MutexLock l(&mu_); + return count_; + } + void Reset() { + MutexLock l(&mu_); + count_ = 0; + } +}; +} + +// Special Env used to delay background operations +class SpecialEnv : public EnvWrapper { + public: + // sstable Sync() calls are blocked while this pointer is non-NULL. + port::AtomicPointer delay_sstable_sync_; + + // Simulate no-space errors while this pointer is non-NULL. + port::AtomicPointer no_space_; + + // Simulate non-writable file system while this pointer is non-NULL + port::AtomicPointer non_writable_; + + // Force sync of manifest files to fail while this pointer is non-NULL + port::AtomicPointer manifest_sync_error_; + + // Force write to manifest files to fail while this pointer is non-NULL + port::AtomicPointer manifest_write_error_; + + bool count_random_reads_; + AtomicCounter random_read_counter_; + + AtomicCounter sleep_counter_; + + explicit SpecialEnv(Env* base) : EnvWrapper(base) { + delay_sstable_sync_.Release_Store(NULL); + no_space_.Release_Store(NULL); + non_writable_.Release_Store(NULL); + count_random_reads_ = false; + manifest_sync_error_.Release_Store(NULL); + manifest_write_error_.Release_Store(NULL); + } + + Status NewWritableFile(const std::string& f, WritableFile** r) { + class SSTableFile : public WritableFile { + private: + SpecialEnv* env_; + WritableFile* base_; + + public: + SSTableFile(SpecialEnv* env, WritableFile* base) + : env_(env), + base_(base) { + } + ~SSTableFile() { delete base_; } + Status Append(const Slice& data) { + if (env_->no_space_.Acquire_Load() != NULL) { + // Drop writes on the floor + return Status::OK(); + } else { + return base_->Append(data); + } + } + Status Close() { return base_->Close(); } + Status Flush() { return base_->Flush(); } + Status Sync() { + while (env_->delay_sstable_sync_.Acquire_Load() != NULL) { + env_->SleepForMicroseconds(100000); + } + return base_->Sync(); + } + }; + class ManifestFile : public WritableFile { + private: + SpecialEnv* env_; + WritableFile* base_; + public: + ManifestFile(SpecialEnv* env, WritableFile* b) : env_(env), base_(b) { } + ~ManifestFile() { delete base_; } + Status Append(const Slice& data) { + if (env_->manifest_write_error_.Acquire_Load() != NULL) { + return Status::IOError("simulated writer error"); + } else { + return base_->Append(data); + } + } + Status Close() { return base_->Close(); } + Status Flush() { return base_->Flush(); } + Status Sync() { + if (env_->manifest_sync_error_.Acquire_Load() != NULL) { + return Status::IOError("simulated sync error"); + } else { + return base_->Sync(); + } + } + }; + + if (non_writable_.Acquire_Load() != NULL) { + return Status::IOError("simulated write error"); + } + + Status s = target()->NewWritableFile(f, r); + if (s.ok()) { + if (strstr(f.c_str(), ".sst") != NULL) { + *r = new SSTableFile(this, *r); + } else if (strstr(f.c_str(), "MANIFEST") != NULL) { + *r = new ManifestFile(this, *r); + } + } + return s; + } + + Status NewRandomAccessFile(const std::string& f, RandomAccessFile** r) { + class CountingFile : public RandomAccessFile { + private: + RandomAccessFile* target_; + AtomicCounter* counter_; + public: + CountingFile(RandomAccessFile* target, AtomicCounter* counter) + : target_(target), counter_(counter) { + } + virtual ~CountingFile() { delete target_; } + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + counter_->Increment(); + return target_->Read(offset, n, result, scratch); + } + }; + + Status s = target()->NewRandomAccessFile(f, r); + if (s.ok() && count_random_reads_) { + *r = new CountingFile(*r, &random_read_counter_); + } + return s; + } + + virtual void SleepForMicroseconds(int micros) { + sleep_counter_.Increment(); + target()->SleepForMicroseconds(micros); + } +}; + +class DBTest { + private: + const FilterPolicy* filter_policy_; + + // Sequence of option configurations to try + enum OptionConfig { + kDefault, + kFilter, + kUncompressed, + kEnd + }; + int option_config_; + + public: + std::string dbname_; + SpecialEnv* env_; + DB* db_; + + Options last_options_; + + DBTest() : option_config_(kDefault), + env_(new SpecialEnv(Env::Default())) { + filter_policy_ = NewBloomFilterPolicy(10); + dbname_ = test::TmpDir() + "/db_test"; + DestroyDB(dbname_, Options()); + db_ = NULL; + Reopen(); + } + + ~DBTest() { + delete db_; + DestroyDB(dbname_, Options()); + delete env_; + delete filter_policy_; + } + + // Switch to a fresh database with the next option configuration to + // test. Return false if there are no more configurations to test. + bool ChangeOptions() { + option_config_++; + if (option_config_ >= kEnd) { + return false; + } else { + DestroyAndReopen(); + return true; + } + } + + // Return the current option configuration. + Options CurrentOptions() { + Options options; + switch (option_config_) { + case kFilter: + options.filter_policy = filter_policy_; + break; + case kUncompressed: + options.compression = kNoCompression; + break; + default: + break; + } + return options; + } + + DBImpl* dbfull() { + return reinterpret_cast(db_); + } + + void Reopen(Options* options = NULL) { + ASSERT_OK(TryReopen(options)); + } + + void Close() { + delete db_; + db_ = NULL; + } + + void DestroyAndReopen(Options* options = NULL) { + delete db_; + db_ = NULL; + DestroyDB(dbname_, Options()); + ASSERT_OK(TryReopen(options)); + } + + Status TryReopen(Options* options) { + delete db_; + db_ = NULL; + Options opts; + if (options != NULL) { + opts = *options; + } else { + opts = CurrentOptions(); + opts.create_if_missing = true; + } + last_options_ = opts; + + return DB::Open(opts, dbname_, &db_); + } + + Status Put(const std::string& k, const std::string& v) { + return db_->Put(WriteOptions(), k, v); + } + + Status Delete(const std::string& k) { + return db_->Delete(WriteOptions(), k); + } + + std::string Get(const std::string& k, const Snapshot* snapshot = NULL) { + ReadOptions options; + options.snapshot = snapshot; + std::string result; + Status s = db_->Get(options, k, &result); + if (s.IsNotFound()) { + result = "NOT_FOUND"; + } else if (!s.ok()) { + result = s.ToString(); + } + return result; + } + + // Return a string that contains all key,value pairs in order, + // formatted like "(k1->v1)(k2->v2)". + std::string Contents() { + std::vector forward; + std::string result; + Iterator* iter = db_->NewIterator(ReadOptions()); + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + std::string s = IterStatus(iter); + result.push_back('('); + result.append(s); + result.push_back(')'); + forward.push_back(s); + } + + // Check reverse iteration results are the reverse of forward results + int matched = 0; + for (iter->SeekToLast(); iter->Valid(); iter->Prev()) { + ASSERT_LT(matched, forward.size()); + ASSERT_EQ(IterStatus(iter), forward[forward.size() - matched - 1]); + matched++; + } + ASSERT_EQ(matched, forward.size()); + + delete iter; + return result; + } + + std::string AllEntriesFor(const Slice& user_key) { + Iterator* iter = dbfull()->TEST_NewInternalIterator(); + InternalKey target(user_key, kMaxSequenceNumber, kTypeValue); + iter->Seek(target.Encode()); + std::string result; + if (!iter->status().ok()) { + result = iter->status().ToString(); + } else { + result = "[ "; + bool first = true; + while (iter->Valid()) { + ParsedInternalKey ikey; + if (!ParseInternalKey(iter->key(), &ikey)) { + result += "CORRUPTED"; + } else { + if (last_options_.comparator->Compare(ikey.user_key, user_key) != 0) { + break; + } + if (!first) { + result += ", "; + } + first = false; + switch (ikey.type) { + case kTypeValue: + result += iter->value().ToString(); + break; + case kTypeDeletion: + result += "DEL"; + break; + } + } + iter->Next(); + } + if (!first) { + result += " "; + } + result += "]"; + } + delete iter; + return result; + } + + int NumTableFilesAtLevel(int level) { + std::string property; + ASSERT_TRUE( + db_->GetProperty("leveldb.num-files-at-level" + NumberToString(level), + &property)); + return atoi(property.c_str()); + } + + int TotalTableFiles() { + int result = 0; + for (int level = 0; level < config::kNumLevels; level++) { + result += NumTableFilesAtLevel(level); + } + return result; + } + + // Return spread of files per level + std::string FilesPerLevel() { + std::string result; + int last_non_zero_offset = 0; + for (int level = 0; level < config::kNumLevels; level++) { + int f = NumTableFilesAtLevel(level); + char buf[100]; + snprintf(buf, sizeof(buf), "%s%d", (level ? "," : ""), f); + result += buf; + if (f > 0) { + last_non_zero_offset = result.size(); + } + } + result.resize(last_non_zero_offset); + return result; + } + + int CountFiles() { + std::vector files; + env_->GetChildren(dbname_, &files); + return static_cast(files.size()); + } + + uint64_t Size(const Slice& start, const Slice& limit) { + Range r(start, limit); + uint64_t size; + db_->GetApproximateSizes(&r, 1, &size); + return size; + } + + void Compact(const Slice& start, const Slice& limit) { + db_->CompactRange(&start, &limit); + } + + // Do n memtable compactions, each of which produces an sstable + // covering the range [small,large]. + void MakeTables(int n, const std::string& small, const std::string& large) { + for (int i = 0; i < n; i++) { + Put(small, "begin"); + Put(large, "end"); + dbfull()->TEST_CompactMemTable(); + } + } + + // Prevent pushing of new sstables into deeper levels by adding + // tables that cover a specified range to all levels. + void FillLevels(const std::string& smallest, const std::string& largest) { + MakeTables(config::kNumLevels, smallest, largest); + } + + void DumpFileCounts(const char* label) { + fprintf(stderr, "---\n%s:\n", label); + fprintf(stderr, "maxoverlap: %lld\n", + static_cast( + dbfull()->TEST_MaxNextLevelOverlappingBytes())); + for (int level = 0; level < config::kNumLevels; level++) { + int num = NumTableFilesAtLevel(level); + if (num > 0) { + fprintf(stderr, " level %3d : %d files\n", level, num); + } + } + } + + std::string DumpSSTableList() { + std::string property; + db_->GetProperty("leveldb.sstables", &property); + return property; + } + + std::string IterStatus(Iterator* iter) { + std::string result; + if (iter->Valid()) { + result = iter->key().ToString() + "->" + iter->value().ToString(); + } else { + result = "(invalid)"; + } + return result; + } +}; + +TEST(DBTest, Empty) { + do { + ASSERT_TRUE(db_ != NULL); + ASSERT_EQ("NOT_FOUND", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, ReadWrite) { + do { + ASSERT_OK(Put("foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + ASSERT_OK(Put("bar", "v2")); + ASSERT_OK(Put("foo", "v3")); + ASSERT_EQ("v3", Get("foo")); + ASSERT_EQ("v2", Get("bar")); + } while (ChangeOptions()); +} + +TEST(DBTest, PutDeleteGet) { + do { + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2")); + ASSERT_EQ("v2", Get("foo")); + ASSERT_OK(db_->Delete(WriteOptions(), "foo")); + ASSERT_EQ("NOT_FOUND", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetFromImmutableLayer) { + do { + Options options = CurrentOptions(); + options.env = env_; + options.write_buffer_size = 100000; // Small write buffer + Reopen(&options); + + ASSERT_OK(Put("foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + + env_->delay_sstable_sync_.Release_Store(env_); // Block sync calls + Put("k1", std::string(100000, 'x')); // Fill memtable + Put("k2", std::string(100000, 'y')); // Trigger compaction + ASSERT_EQ("v1", Get("foo")); + env_->delay_sstable_sync_.Release_Store(NULL); // Release sync calls + } while (ChangeOptions()); +} + +TEST(DBTest, GetFromVersions) { + do { + ASSERT_OK(Put("foo", "v1")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v1", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetSnapshot) { + do { + // Try with both a short key and a long key + for (int i = 0; i < 2; i++) { + std::string key = (i == 0) ? std::string("foo") : std::string(200, 'x'); + ASSERT_OK(Put(key, "v1")); + const Snapshot* s1 = db_->GetSnapshot(); + ASSERT_OK(Put(key, "v2")); + ASSERT_EQ("v2", Get(key)); + ASSERT_EQ("v1", Get(key, s1)); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v2", Get(key)); + ASSERT_EQ("v1", Get(key, s1)); + db_->ReleaseSnapshot(s1); + } + } while (ChangeOptions()); +} + +TEST(DBTest, GetLevel0Ordering) { + do { + // Check that we process level-0 files in correct order. The code + // below generates two level-0 files where the earlier one comes + // before the later one in the level-0 file list since the earlier + // one has a smaller "smallest" key. + ASSERT_OK(Put("bar", "b")); + ASSERT_OK(Put("foo", "v1")); + dbfull()->TEST_CompactMemTable(); + ASSERT_OK(Put("foo", "v2")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v2", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetOrderedByLevels) { + do { + ASSERT_OK(Put("foo", "v1")); + Compact("a", "z"); + ASSERT_EQ("v1", Get("foo")); + ASSERT_OK(Put("foo", "v2")); + ASSERT_EQ("v2", Get("foo")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v2", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetPicksCorrectFile) { + do { + // Arrange to have multiple files in a non-level-0 level. + ASSERT_OK(Put("a", "va")); + Compact("a", "b"); + ASSERT_OK(Put("x", "vx")); + Compact("x", "y"); + ASSERT_OK(Put("f", "vf")); + Compact("f", "g"); + ASSERT_EQ("va", Get("a")); + ASSERT_EQ("vf", Get("f")); + ASSERT_EQ("vx", Get("x")); + } while (ChangeOptions()); +} + +TEST(DBTest, GetEncountersEmptyLevel) { + do { + // Arrange for the following to happen: + // * sstable A in level 0 + // * nothing in level 1 + // * sstable B in level 2 + // Then do enough Get() calls to arrange for an automatic compaction + // of sstable A. A bug would cause the compaction to be marked as + // occuring at level 1 (instead of the correct level 0). + + // Step 1: First place sstables in levels 0 and 2 + int compaction_count = 0; + while (NumTableFilesAtLevel(0) == 0 || + NumTableFilesAtLevel(2) == 0) { + ASSERT_LE(compaction_count, 100) << "could not fill levels 0 and 2"; + compaction_count++; + Put("a", "begin"); + Put("z", "end"); + dbfull()->TEST_CompactMemTable(); + } + + // Step 2: clear level 1 if necessary. + dbfull()->TEST_CompactRange(1, NULL, NULL); + ASSERT_EQ(NumTableFilesAtLevel(0), 1); + ASSERT_EQ(NumTableFilesAtLevel(1), 0); + ASSERT_EQ(NumTableFilesAtLevel(2), 1); + + // Step 3: read a bunch of times + for (int i = 0; i < 1000; i++) { + ASSERT_EQ("NOT_FOUND", Get("missing")); + } + + // Step 4: Wait for compaction to finish + env_->SleepForMicroseconds(1000000); + + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + } while (ChangeOptions()); +} + +TEST(DBTest, IterEmpty) { + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek("foo"); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterSingle) { + ASSERT_OK(Put("a", "va")); + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek(""); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek("a"); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek("b"); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterMulti) { + ASSERT_OK(Put("a", "va")); + ASSERT_OK(Put("b", "vb")); + ASSERT_OK(Put("c", "vc")); + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek(""); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Seek("a"); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Seek("ax"); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Seek("b"); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Seek("z"); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + // Switch from reverse to forward + iter->SeekToLast(); + iter->Prev(); + iter->Prev(); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + + // Switch from forward to reverse + iter->SeekToFirst(); + iter->Next(); + iter->Next(); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + + // Make sure iter stays at snapshot + ASSERT_OK(Put("a", "va2")); + ASSERT_OK(Put("a2", "va3")); + ASSERT_OK(Put("b", "vb2")); + ASSERT_OK(Put("c", "vc2")); + ASSERT_OK(Delete("b")); + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterSmallAndLargeMix) { + ASSERT_OK(Put("a", "va")); + ASSERT_OK(Put("b", std::string(100000, 'b'))); + ASSERT_OK(Put("c", "vc")); + ASSERT_OK(Put("d", std::string(100000, 'd'))); + ASSERT_OK(Put("e", std::string(100000, 'e'))); + + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b')); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd')); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e')); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e')); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd')); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b')); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterMultiWithDelete) { + do { + ASSERT_OK(Put("a", "va")); + ASSERT_OK(Put("b", "vb")); + ASSERT_OK(Put("c", "vc")); + ASSERT_OK(Delete("b")); + ASSERT_EQ("NOT_FOUND", Get("b")); + + Iterator* iter = db_->NewIterator(ReadOptions()); + iter->Seek("c"); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + delete iter; + } while (ChangeOptions()); +} + +TEST(DBTest, Recover) { + do { + ASSERT_OK(Put("foo", "v1")); + ASSERT_OK(Put("baz", "v5")); + + Reopen(); + ASSERT_EQ("v1", Get("foo")); + + ASSERT_EQ("v1", Get("foo")); + ASSERT_EQ("v5", Get("baz")); + ASSERT_OK(Put("bar", "v2")); + ASSERT_OK(Put("foo", "v3")); + + Reopen(); + ASSERT_EQ("v3", Get("foo")); + ASSERT_OK(Put("foo", "v4")); + ASSERT_EQ("v4", Get("foo")); + ASSERT_EQ("v2", Get("bar")); + ASSERT_EQ("v5", Get("baz")); + } while (ChangeOptions()); +} + +TEST(DBTest, RecoveryWithEmptyLog) { + do { + ASSERT_OK(Put("foo", "v1")); + ASSERT_OK(Put("foo", "v2")); + Reopen(); + Reopen(); + ASSERT_OK(Put("foo", "v3")); + Reopen(); + ASSERT_EQ("v3", Get("foo")); + } while (ChangeOptions()); +} + +// Check that writes done during a memtable compaction are recovered +// if the database is shutdown during the memtable compaction. +TEST(DBTest, RecoverDuringMemtableCompaction) { + do { + Options options = CurrentOptions(); + options.env = env_; + options.write_buffer_size = 1000000; + Reopen(&options); + + // Trigger a long memtable compaction and reopen the database during it + ASSERT_OK(Put("foo", "v1")); // Goes to 1st log file + ASSERT_OK(Put("big1", std::string(10000000, 'x'))); // Fills memtable + ASSERT_OK(Put("big2", std::string(1000, 'y'))); // Triggers compaction + ASSERT_OK(Put("bar", "v2")); // Goes to new log file + + Reopen(&options); + ASSERT_EQ("v1", Get("foo")); + ASSERT_EQ("v2", Get("bar")); + ASSERT_EQ(std::string(10000000, 'x'), Get("big1")); + ASSERT_EQ(std::string(1000, 'y'), Get("big2")); + } while (ChangeOptions()); +} + +static std::string Key(int i) { + char buf[100]; + snprintf(buf, sizeof(buf), "key%06d", i); + return std::string(buf); +} + +TEST(DBTest, MinorCompactionsHappen) { + Options options = CurrentOptions(); + options.write_buffer_size = 10000; + Reopen(&options); + + const int N = 500; + + int starting_num_tables = TotalTableFiles(); + for (int i = 0; i < N; i++) { + ASSERT_OK(Put(Key(i), Key(i) + std::string(1000, 'v'))); + } + int ending_num_tables = TotalTableFiles(); + ASSERT_GT(ending_num_tables, starting_num_tables); + + for (int i = 0; i < N; i++) { + ASSERT_EQ(Key(i) + std::string(1000, 'v'), Get(Key(i))); + } + + Reopen(); + + for (int i = 0; i < N; i++) { + ASSERT_EQ(Key(i) + std::string(1000, 'v'), Get(Key(i))); + } +} + +TEST(DBTest, RecoverWithLargeLog) { + { + Options options = CurrentOptions(); + Reopen(&options); + ASSERT_OK(Put("big1", std::string(200000, '1'))); + ASSERT_OK(Put("big2", std::string(200000, '2'))); + ASSERT_OK(Put("small3", std::string(10, '3'))); + ASSERT_OK(Put("small4", std::string(10, '4'))); + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + } + + // Make sure that if we re-open with a small write buffer size that + // we flush table files in the middle of a large log file. + Options options = CurrentOptions(); + options.write_buffer_size = 100000; + Reopen(&options); + ASSERT_EQ(NumTableFilesAtLevel(0), 3); + ASSERT_EQ(std::string(200000, '1'), Get("big1")); + ASSERT_EQ(std::string(200000, '2'), Get("big2")); + ASSERT_EQ(std::string(10, '3'), Get("small3")); + ASSERT_EQ(std::string(10, '4'), Get("small4")); + ASSERT_GT(NumTableFilesAtLevel(0), 1); +} + +TEST(DBTest, CompactionsGenerateMultipleFiles) { + Options options = CurrentOptions(); + options.write_buffer_size = 100000000; // Large write buffer + Reopen(&options); + + Random rnd(301); + + // Write 8MB (80 values, each 100K) + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + std::vector values; + for (int i = 0; i < 80; i++) { + values.push_back(RandomString(&rnd, 100000)); + ASSERT_OK(Put(Key(i), values[i])); + } + + // Reopening moves updates to level-0 + Reopen(&options); + dbfull()->TEST_CompactRange(0, NULL, NULL); + + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + ASSERT_GT(NumTableFilesAtLevel(1), 1); + for (int i = 0; i < 80; i++) { + ASSERT_EQ(Get(Key(i)), values[i]); + } +} + +TEST(DBTest, RepeatedWritesToSameKey) { + Options options = CurrentOptions(); + options.env = env_; + options.write_buffer_size = 100000; // Small write buffer + Reopen(&options); + + // We must have at most one file per level except for level-0, + // which may have up to kL0_StopWritesTrigger files. + const int kMaxFiles = config::kNumLevels + config::kL0_StopWritesTrigger; + + Random rnd(301); + std::string value = RandomString(&rnd, 2 * options.write_buffer_size); + for (int i = 0; i < 5 * kMaxFiles; i++) { + Put("key", value); + ASSERT_LE(TotalTableFiles(), kMaxFiles); + fprintf(stderr, "after %d: %d files\n", int(i+1), TotalTableFiles()); + } +} + +TEST(DBTest, SparseMerge) { + Options options = CurrentOptions(); + options.compression = kNoCompression; + Reopen(&options); + + FillLevels("A", "Z"); + + // Suppose there is: + // small amount of data with prefix A + // large amount of data with prefix B + // small amount of data with prefix C + // and that recent updates have made small changes to all three prefixes. + // Check that we do not do a compaction that merges all of B in one shot. + const std::string value(1000, 'x'); + Put("A", "va"); + // Write approximately 100MB of "B" values + for (int i = 0; i < 100000; i++) { + char key[100]; + snprintf(key, sizeof(key), "B%010d", i); + Put(key, value); + } + Put("C", "vc"); + dbfull()->TEST_CompactMemTable(); + dbfull()->TEST_CompactRange(0, NULL, NULL); + + // Make sparse update + Put("A", "va2"); + Put("B100", "bvalue2"); + Put("C", "vc2"); + dbfull()->TEST_CompactMemTable(); + + // Compactions should not cause us to create a situation where + // a file overlaps too much data at the next level. + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); + dbfull()->TEST_CompactRange(0, NULL, NULL); + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); + dbfull()->TEST_CompactRange(1, NULL, NULL); + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); +} + +static bool Between(uint64_t val, uint64_t low, uint64_t high) { + bool result = (val >= low) && (val <= high); + if (!result) { + fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n", + (unsigned long long)(val), + (unsigned long long)(low), + (unsigned long long)(high)); + } + return result; +} + +TEST(DBTest, ApproximateSizes) { + do { + Options options = CurrentOptions(); + options.write_buffer_size = 100000000; // Large write buffer + options.compression = kNoCompression; + DestroyAndReopen(); + + ASSERT_TRUE(Between(Size("", "xyz"), 0, 0)); + Reopen(&options); + ASSERT_TRUE(Between(Size("", "xyz"), 0, 0)); + + // Write 8MB (80 values, each 100K) + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + const int N = 80; + static const int S1 = 100000; + static const int S2 = 105000; // Allow some expansion from metadata + Random rnd(301); + for (int i = 0; i < N; i++) { + ASSERT_OK(Put(Key(i), RandomString(&rnd, S1))); + } + + // 0 because GetApproximateSizes() does not account for memtable space + ASSERT_TRUE(Between(Size("", Key(50)), 0, 0)); + + // Check sizes across recovery by reopening a few times + for (int run = 0; run < 3; run++) { + Reopen(&options); + + for (int compact_start = 0; compact_start < N; compact_start += 10) { + for (int i = 0; i < N; i += 10) { + ASSERT_TRUE(Between(Size("", Key(i)), S1*i, S2*i)); + ASSERT_TRUE(Between(Size("", Key(i)+".suffix"), S1*(i+1), S2*(i+1))); + ASSERT_TRUE(Between(Size(Key(i), Key(i+10)), S1*10, S2*10)); + } + ASSERT_TRUE(Between(Size("", Key(50)), S1*50, S2*50)); + ASSERT_TRUE(Between(Size("", Key(50)+".suffix"), S1*50, S2*50)); + + std::string cstart_str = Key(compact_start); + std::string cend_str = Key(compact_start + 9); + Slice cstart = cstart_str; + Slice cend = cend_str; + dbfull()->TEST_CompactRange(0, &cstart, &cend); + } + + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + ASSERT_GT(NumTableFilesAtLevel(1), 0); + } + } while (ChangeOptions()); +} + +TEST(DBTest, ApproximateSizes_MixOfSmallAndLarge) { + do { + Options options = CurrentOptions(); + options.compression = kNoCompression; + Reopen(); + + Random rnd(301); + std::string big1 = RandomString(&rnd, 100000); + ASSERT_OK(Put(Key(0), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(1), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(2), big1)); + ASSERT_OK(Put(Key(3), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(4), big1)); + ASSERT_OK(Put(Key(5), RandomString(&rnd, 10000))); + ASSERT_OK(Put(Key(6), RandomString(&rnd, 300000))); + ASSERT_OK(Put(Key(7), RandomString(&rnd, 10000))); + + // Check sizes across recovery by reopening a few times + for (int run = 0; run < 3; run++) { + Reopen(&options); + + ASSERT_TRUE(Between(Size("", Key(0)), 0, 0)); + ASSERT_TRUE(Between(Size("", Key(1)), 10000, 11000)); + ASSERT_TRUE(Between(Size("", Key(2)), 20000, 21000)); + ASSERT_TRUE(Between(Size("", Key(3)), 120000, 121000)); + ASSERT_TRUE(Between(Size("", Key(4)), 130000, 131000)); + ASSERT_TRUE(Between(Size("", Key(5)), 230000, 231000)); + ASSERT_TRUE(Between(Size("", Key(6)), 240000, 241000)); + ASSERT_TRUE(Between(Size("", Key(7)), 540000, 541000)); + ASSERT_TRUE(Between(Size("", Key(8)), 550000, 560000)); + + ASSERT_TRUE(Between(Size(Key(3), Key(5)), 110000, 111000)); + + dbfull()->TEST_CompactRange(0, NULL, NULL); + } + } while (ChangeOptions()); +} + +TEST(DBTest, IteratorPinsRef) { + Put("foo", "hello"); + + // Get iterator that will yield the current contents of the DB. + Iterator* iter = db_->NewIterator(ReadOptions()); + + // Write to force compactions + Put("foo", "newvalue1"); + for (int i = 0; i < 100; i++) { + ASSERT_OK(Put(Key(i), Key(i) + std::string(100000, 'v'))); // 100K values + } + Put("foo", "newvalue2"); + + iter->SeekToFirst(); + ASSERT_TRUE(iter->Valid()); + ASSERT_EQ("foo", iter->key().ToString()); + ASSERT_EQ("hello", iter->value().ToString()); + iter->Next(); + ASSERT_TRUE(!iter->Valid()); + delete iter; +} + +TEST(DBTest, Snapshot) { + do { + Put("foo", "v1"); + const Snapshot* s1 = db_->GetSnapshot(); + Put("foo", "v2"); + const Snapshot* s2 = db_->GetSnapshot(); + Put("foo", "v3"); + const Snapshot* s3 = db_->GetSnapshot(); + + Put("foo", "v4"); + ASSERT_EQ("v1", Get("foo", s1)); + ASSERT_EQ("v2", Get("foo", s2)); + ASSERT_EQ("v3", Get("foo", s3)); + ASSERT_EQ("v4", Get("foo")); + + db_->ReleaseSnapshot(s3); + ASSERT_EQ("v1", Get("foo", s1)); + ASSERT_EQ("v2", Get("foo", s2)); + ASSERT_EQ("v4", Get("foo")); + + db_->ReleaseSnapshot(s1); + ASSERT_EQ("v2", Get("foo", s2)); + ASSERT_EQ("v4", Get("foo")); + + db_->ReleaseSnapshot(s2); + ASSERT_EQ("v4", Get("foo")); + } while (ChangeOptions()); +} + +TEST(DBTest, HiddenValuesAreRemoved) { + do { + Random rnd(301); + FillLevels("a", "z"); + + std::string big = RandomString(&rnd, 50000); + Put("foo", big); + Put("pastfoo", "v"); + const Snapshot* snapshot = db_->GetSnapshot(); + Put("foo", "tiny"); + Put("pastfoo2", "v2"); // Advance sequence number one more + + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + ASSERT_GT(NumTableFilesAtLevel(0), 0); + + ASSERT_EQ(big, Get("foo", snapshot)); + ASSERT_TRUE(Between(Size("", "pastfoo"), 50000, 60000)); + db_->ReleaseSnapshot(snapshot); + ASSERT_EQ(AllEntriesFor("foo"), "[ tiny, " + big + " ]"); + Slice x("x"); + dbfull()->TEST_CompactRange(0, NULL, &x); + ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]"); + ASSERT_EQ(NumTableFilesAtLevel(0), 0); + ASSERT_GE(NumTableFilesAtLevel(1), 1); + dbfull()->TEST_CompactRange(1, NULL, &x); + ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]"); + + ASSERT_TRUE(Between(Size("", "pastfoo"), 0, 1000)); + } while (ChangeOptions()); +} + +TEST(DBTest, DeletionMarkers1) { + Put("foo", "v1"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level + + // Place a table at level last-1 to prevent merging with preceding mutation + Put("a", "begin"); + Put("z", "end"); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ(NumTableFilesAtLevel(last), 1); + ASSERT_EQ(NumTableFilesAtLevel(last-1), 1); + + Delete("foo"); + Put("foo", "v2"); + ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2 + ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]"); + Slice z("z"); + dbfull()->TEST_CompactRange(last-2, NULL, &z); + // DEL eliminated, but v1 remains because we aren't compacting that level + // (DEL can be eliminated because v2 hides v1). + ASSERT_EQ(AllEntriesFor("foo"), "[ v2, v1 ]"); + dbfull()->TEST_CompactRange(last-1, NULL, NULL); + // Merging last-1 w/ last, so we are the base level for "foo", so + // DEL is removed. (as is v1). + ASSERT_EQ(AllEntriesFor("foo"), "[ v2 ]"); +} + +TEST(DBTest, DeletionMarkers2) { + Put("foo", "v1"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level + + // Place a table at level last-1 to prevent merging with preceding mutation + Put("a", "begin"); + Put("z", "end"); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ(NumTableFilesAtLevel(last), 1); + ASSERT_EQ(NumTableFilesAtLevel(last-1), 1); + + Delete("foo"); + ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); + ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2 + ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); + dbfull()->TEST_CompactRange(last-2, NULL, NULL); + // DEL kept: "last" file overlaps + ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); + dbfull()->TEST_CompactRange(last-1, NULL, NULL); + // Merging last-1 w/ last, so we are the base level for "foo", so + // DEL is removed. (as is v1). + ASSERT_EQ(AllEntriesFor("foo"), "[ ]"); +} + +TEST(DBTest, OverlapInLevel0) { + do { + ASSERT_EQ(config::kMaxMemCompactLevel, 2) << "Fix test to match config"; + + // Fill levels 1 and 2 to disable the pushing of new memtables to levels > 0. + ASSERT_OK(Put("100", "v100")); + ASSERT_OK(Put("999", "v999")); + dbfull()->TEST_CompactMemTable(); + ASSERT_OK(Delete("100")); + ASSERT_OK(Delete("999")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("0,1,1", FilesPerLevel()); + + // Make files spanning the following ranges in level-0: + // files[0] 200 .. 900 + // files[1] 300 .. 500 + // Note that files are sorted by smallest key. + ASSERT_OK(Put("300", "v300")); + ASSERT_OK(Put("500", "v500")); + dbfull()->TEST_CompactMemTable(); + ASSERT_OK(Put("200", "v200")); + ASSERT_OK(Put("600", "v600")); + ASSERT_OK(Put("900", "v900")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("2,1,1", FilesPerLevel()); + + // Compact away the placeholder files we created initially + dbfull()->TEST_CompactRange(1, NULL, NULL); + dbfull()->TEST_CompactRange(2, NULL, NULL); + ASSERT_EQ("2", FilesPerLevel()); + + // Do a memtable compaction. Before bug-fix, the compaction would + // not detect the overlap with level-0 files and would incorrectly place + // the deletion in a deeper level. + ASSERT_OK(Delete("600")); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("3", FilesPerLevel()); + ASSERT_EQ("NOT_FOUND", Get("600")); + } while (ChangeOptions()); +} + +TEST(DBTest, L0_CompactionBug_Issue44_a) { + Reopen(); + ASSERT_OK(Put("b", "v")); + Reopen(); + ASSERT_OK(Delete("b")); + ASSERT_OK(Delete("a")); + Reopen(); + ASSERT_OK(Delete("a")); + Reopen(); + ASSERT_OK(Put("a", "v")); + Reopen(); + Reopen(); + ASSERT_EQ("(a->v)", Contents()); + env_->SleepForMicroseconds(1000000); // Wait for compaction to finish + ASSERT_EQ("(a->v)", Contents()); +} + +TEST(DBTest, L0_CompactionBug_Issue44_b) { + Reopen(); + Put("",""); + Reopen(); + Delete("e"); + Put("",""); + Reopen(); + Put("c", "cv"); + Reopen(); + Put("",""); + Reopen(); + Put("",""); + env_->SleepForMicroseconds(1000000); // Wait for compaction to finish + Reopen(); + Put("d","dv"); + Reopen(); + Put("",""); + Reopen(); + Delete("d"); + Delete("b"); + Reopen(); + ASSERT_EQ("(->)(c->cv)", Contents()); + env_->SleepForMicroseconds(1000000); // Wait for compaction to finish + ASSERT_EQ("(->)(c->cv)", Contents()); +} + +TEST(DBTest, ComparatorCheck) { + class NewComparator : public Comparator { + public: + virtual const char* Name() const { return "leveldb.NewComparator"; } + virtual int Compare(const Slice& a, const Slice& b) const { + return BytewiseComparator()->Compare(a, b); + } + virtual void FindShortestSeparator(std::string* s, const Slice& l) const { + BytewiseComparator()->FindShortestSeparator(s, l); + } + virtual void FindShortSuccessor(std::string* key) const { + BytewiseComparator()->FindShortSuccessor(key); + } + }; + NewComparator cmp; + Options new_options = CurrentOptions(); + new_options.comparator = &cmp; + Status s = TryReopen(&new_options); + ASSERT_TRUE(!s.ok()); + ASSERT_TRUE(s.ToString().find("comparator") != std::string::npos) + << s.ToString(); +} + +TEST(DBTest, CustomComparator) { + class NumberComparator : public Comparator { + public: + virtual const char* Name() const { return "test.NumberComparator"; } + virtual int Compare(const Slice& a, const Slice& b) const { + return ToNumber(a) - ToNumber(b); + } + virtual void FindShortestSeparator(std::string* s, const Slice& l) const { + ToNumber(*s); // Check format + ToNumber(l); // Check format + } + virtual void FindShortSuccessor(std::string* key) const { + ToNumber(*key); // Check format + } + private: + static int ToNumber(const Slice& x) { + // Check that there are no extra characters. + ASSERT_TRUE(x.size() >= 2 && x[0] == '[' && x[x.size()-1] == ']') + << EscapeString(x); + int val; + char ignored; + ASSERT_TRUE(sscanf(x.ToString().c_str(), "[%i]%c", &val, &ignored) == 1) + << EscapeString(x); + return val; + } + }; + NumberComparator cmp; + Options new_options = CurrentOptions(); + new_options.create_if_missing = true; + new_options.comparator = &cmp; + new_options.filter_policy = NULL; // Cannot use bloom filters + new_options.write_buffer_size = 1000; // Compact more often + DestroyAndReopen(&new_options); + ASSERT_OK(Put("[10]", "ten")); + ASSERT_OK(Put("[0x14]", "twenty")); + for (int i = 0; i < 2; i++) { + ASSERT_EQ("ten", Get("[10]")); + ASSERT_EQ("ten", Get("[0xa]")); + ASSERT_EQ("twenty", Get("[20]")); + ASSERT_EQ("twenty", Get("[0x14]")); + ASSERT_EQ("NOT_FOUND", Get("[15]")); + ASSERT_EQ("NOT_FOUND", Get("[0xf]")); + Compact("[0]", "[9999]"); + } + + for (int run = 0; run < 2; run++) { + for (int i = 0; i < 1000; i++) { + char buf[100]; + snprintf(buf, sizeof(buf), "[%d]", i*10); + ASSERT_OK(Put(buf, buf)); + } + Compact("[0]", "[1000000]"); + } +} + +TEST(DBTest, ManualCompaction) { + ASSERT_EQ(config::kMaxMemCompactLevel, 2) + << "Need to update this test to match kMaxMemCompactLevel"; + + MakeTables(3, "p", "q"); + ASSERT_EQ("1,1,1", FilesPerLevel()); + + // Compaction range falls before files + Compact("", "c"); + ASSERT_EQ("1,1,1", FilesPerLevel()); + + // Compaction range falls after files + Compact("r", "z"); + ASSERT_EQ("1,1,1", FilesPerLevel()); + + // Compaction range overlaps files + Compact("p1", "p9"); + ASSERT_EQ("0,0,1", FilesPerLevel()); + + // Populate a different range + MakeTables(3, "c", "e"); + ASSERT_EQ("1,1,2", FilesPerLevel()); + + // Compact just the new range + Compact("b", "f"); + ASSERT_EQ("0,0,2", FilesPerLevel()); + + // Compact all + MakeTables(1, "a", "z"); + ASSERT_EQ("0,1,2", FilesPerLevel()); + db_->CompactRange(NULL, NULL); + ASSERT_EQ("0,0,1", FilesPerLevel()); +} + +TEST(DBTest, DBOpen_Options) { + std::string dbname = test::TmpDir() + "/db_options_test"; + DestroyDB(dbname, Options()); + + // Does not exist, and create_if_missing == false: error + DB* db = NULL; + Options opts; + opts.create_if_missing = false; + Status s = DB::Open(opts, dbname, &db); + ASSERT_TRUE(strstr(s.ToString().c_str(), "does not exist") != NULL); + ASSERT_TRUE(db == NULL); + + // Does not exist, and create_if_missing == true: OK + opts.create_if_missing = true; + s = DB::Open(opts, dbname, &db); + ASSERT_OK(s); + ASSERT_TRUE(db != NULL); + + delete db; + db = NULL; + + // Does exist, and error_if_exists == true: error + opts.create_if_missing = false; + opts.error_if_exists = true; + s = DB::Open(opts, dbname, &db); + ASSERT_TRUE(strstr(s.ToString().c_str(), "exists") != NULL); + ASSERT_TRUE(db == NULL); + + // Does exist, and error_if_exists == false: OK + opts.create_if_missing = true; + opts.error_if_exists = false; + s = DB::Open(opts, dbname, &db); + ASSERT_OK(s); + ASSERT_TRUE(db != NULL); + + delete db; + db = NULL; +} + +TEST(DBTest, Locking) { + DB* db2 = NULL; + Status s = DB::Open(CurrentOptions(), dbname_, &db2); + ASSERT_TRUE(!s.ok()) << "Locking did not prevent re-opening db"; +} + +// Check that number of files does not grow when we are out of space +TEST(DBTest, NoSpace) { + Options options = CurrentOptions(); + options.env = env_; + Reopen(&options); + + ASSERT_OK(Put("foo", "v1")); + ASSERT_EQ("v1", Get("foo")); + Compact("a", "z"); + const int num_files = CountFiles(); + env_->no_space_.Release_Store(env_); // Force out-of-space errors + env_->sleep_counter_.Reset(); + for (int i = 0; i < 5; i++) { + for (int level = 0; level < config::kNumLevels-1; level++) { + dbfull()->TEST_CompactRange(level, NULL, NULL); + } + } + env_->no_space_.Release_Store(NULL); + ASSERT_LT(CountFiles(), num_files + 3); + + // Check that compaction attempts slept after errors + ASSERT_GE(env_->sleep_counter_.Read(), 5); +} + +TEST(DBTest, NonWritableFileSystem) { + Options options = CurrentOptions(); + options.write_buffer_size = 1000; + options.env = env_; + Reopen(&options); + ASSERT_OK(Put("foo", "v1")); + env_->non_writable_.Release_Store(env_); // Force errors for new files + std::string big(100000, 'x'); + int errors = 0; + for (int i = 0; i < 20; i++) { + fprintf(stderr, "iter %d; errors %d\n", i, errors); + if (!Put("foo", big).ok()) { + errors++; + env_->SleepForMicroseconds(100000); + } + } + ASSERT_GT(errors, 0); + env_->non_writable_.Release_Store(NULL); +} + +TEST(DBTest, ManifestWriteError) { + // Test for the following problem: + // (a) Compaction produces file F + // (b) Log record containing F is written to MANIFEST file, but Sync() fails + // (c) GC deletes F + // (d) After reopening DB, reads fail since deleted F is named in log record + + // We iterate twice. In the second iteration, everything is the + // same except the log record never makes it to the MANIFEST file. + for (int iter = 0; iter < 2; iter++) { + port::AtomicPointer* error_type = (iter == 0) + ? &env_->manifest_sync_error_ + : &env_->manifest_write_error_; + + // Insert foo=>bar mapping + Options options = CurrentOptions(); + options.env = env_; + options.create_if_missing = true; + options.error_if_exists = false; + DestroyAndReopen(&options); + ASSERT_OK(Put("foo", "bar")); + ASSERT_EQ("bar", Get("foo")); + + // Memtable compaction (will succeed) + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("bar", Get("foo")); + const int last = config::kMaxMemCompactLevel; + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo=>bar is now in last level + + // Merging compaction (will fail) + error_type->Release_Store(env_); + dbfull()->TEST_CompactRange(last, NULL, NULL); // Should fail + ASSERT_EQ("bar", Get("foo")); + + // Recovery: should not lose data + error_type->Release_Store(NULL); + Reopen(&options); + ASSERT_EQ("bar", Get("foo")); + } +} + +TEST(DBTest, FilesDeletedAfterCompaction) { + ASSERT_OK(Put("foo", "v2")); + Compact("a", "z"); + const int num_files = CountFiles(); + for (int i = 0; i < 10; i++) { + ASSERT_OK(Put("foo", "v2")); + Compact("a", "z"); + } + ASSERT_EQ(CountFiles(), num_files); +} + +TEST(DBTest, BloomFilter) { + env_->count_random_reads_ = true; + Options options = CurrentOptions(); + options.env = env_; + options.block_cache = NewLRUCache(0); // Prevent cache hits + options.filter_policy = NewBloomFilterPolicy(10); + Reopen(&options); + + // Populate multiple layers + const int N = 10000; + for (int i = 0; i < N; i++) { + ASSERT_OK(Put(Key(i), Key(i))); + } + Compact("a", "z"); + for (int i = 0; i < N; i += 100) { + ASSERT_OK(Put(Key(i), Key(i))); + } + dbfull()->TEST_CompactMemTable(); + + // Prevent auto compactions triggered by seeks + env_->delay_sstable_sync_.Release_Store(env_); + + // Lookup present keys. Should rarely read from small sstable. + env_->random_read_counter_.Reset(); + for (int i = 0; i < N; i++) { + ASSERT_EQ(Key(i), Get(Key(i))); + } + int reads = env_->random_read_counter_.Read(); + fprintf(stderr, "%d present => %d reads\n", N, reads); + ASSERT_GE(reads, N); + ASSERT_LE(reads, N + 2*N/100); + + // Lookup present keys. Should rarely read from either sstable. + env_->random_read_counter_.Reset(); + for (int i = 0; i < N; i++) { + ASSERT_EQ("NOT_FOUND", Get(Key(i) + ".missing")); + } + reads = env_->random_read_counter_.Read(); + fprintf(stderr, "%d missing => %d reads\n", N, reads); + ASSERT_LE(reads, 3*N/100); + + env_->delay_sstable_sync_.Release_Store(NULL); + Close(); + delete options.block_cache; + delete options.filter_policy; +} + +// Multi-threaded test: +namespace { + +static const int kNumThreads = 4; +static const int kTestSeconds = 10; +static const int kNumKeys = 1000; + +struct MTState { + DBTest* test; + port::AtomicPointer stop; + port::AtomicPointer counter[kNumThreads]; + port::AtomicPointer thread_done[kNumThreads]; +}; + +struct MTThread { + MTState* state; + int id; +}; + +static void MTThreadBody(void* arg) { + MTThread* t = reinterpret_cast(arg); + int id = t->id; + DB* db = t->state->test->db_; + uintptr_t counter = 0; + fprintf(stderr, "... starting thread %d\n", id); + Random rnd(1000 + id); + std::string value; + char valbuf[1500]; + while (t->state->stop.Acquire_Load() == NULL) { + t->state->counter[id].Release_Store(reinterpret_cast(counter)); + + int key = rnd.Uniform(kNumKeys); + char keybuf[20]; + snprintf(keybuf, sizeof(keybuf), "%016d", key); + + if (rnd.OneIn(2)) { + // Write values of the form . + // We add some padding for force compactions. + snprintf(valbuf, sizeof(valbuf), "%d.%d.%-1000d", + key, id, static_cast(counter)); + ASSERT_OK(db->Put(WriteOptions(), Slice(keybuf), Slice(valbuf))); + } else { + // Read a value and verify that it matches the pattern written above. + Status s = db->Get(ReadOptions(), Slice(keybuf), &value); + if (s.IsNotFound()) { + // Key has not yet been written + } else { + // Check that the writer thread counter is >= the counter in the value + ASSERT_OK(s); + int k, w, c; + ASSERT_EQ(3, sscanf(value.c_str(), "%d.%d.%d", &k, &w, &c)) << value; + ASSERT_EQ(k, key); + ASSERT_GE(w, 0); + ASSERT_LT(w, kNumThreads); + ASSERT_LE(c, reinterpret_cast( + t->state->counter[w].Acquire_Load())); + } + } + counter++; + } + t->state->thread_done[id].Release_Store(t); + fprintf(stderr, "... stopping thread %d after %d ops\n", id, int(counter)); +} + +} // namespace + +TEST(DBTest, MultiThreaded) { + do { + // Initialize state + MTState mt; + mt.test = this; + mt.stop.Release_Store(0); + for (int id = 0; id < kNumThreads; id++) { + mt.counter[id].Release_Store(0); + mt.thread_done[id].Release_Store(0); + } + + // Start threads + MTThread thread[kNumThreads]; + for (int id = 0; id < kNumThreads; id++) { + thread[id].state = &mt; + thread[id].id = id; + env_->StartThread(MTThreadBody, &thread[id]); + } + + // Let them run for a while + env_->SleepForMicroseconds(kTestSeconds * 1000000); + + // Stop the threads and wait for them to finish + mt.stop.Release_Store(&mt); + for (int id = 0; id < kNumThreads; id++) { + while (mt.thread_done[id].Acquire_Load() == NULL) { + env_->SleepForMicroseconds(100000); + } + } + } while (ChangeOptions()); +} + +namespace { +typedef std::map KVMap; +} + +class ModelDB: public DB { + public: + class ModelSnapshot : public Snapshot { + public: + KVMap map_; + }; + + explicit ModelDB(const Options& options): options_(options) { } + ~ModelDB() { } + virtual Status Put(const WriteOptions& o, const Slice& k, const Slice& v) { + return DB::Put(o, k, v); + } + virtual Status Delete(const WriteOptions& o, const Slice& key) { + return DB::Delete(o, key); + } + virtual Status Get(const ReadOptions& options, + const Slice& key, std::string* value) { + assert(false); // Not implemented + return Status::NotFound(key); + } + virtual Iterator* NewIterator(const ReadOptions& options) { + if (options.snapshot == NULL) { + KVMap* saved = new KVMap; + *saved = map_; + return new ModelIter(saved, true); + } else { + const KVMap* snapshot_state = + &(reinterpret_cast(options.snapshot)->map_); + return new ModelIter(snapshot_state, false); + } + } + virtual const Snapshot* GetSnapshot() { + ModelSnapshot* snapshot = new ModelSnapshot; + snapshot->map_ = map_; + return snapshot; + } + + virtual void ReleaseSnapshot(const Snapshot* snapshot) { + delete reinterpret_cast(snapshot); + } + virtual Status Write(const WriteOptions& options, WriteBatch* batch) { + class Handler : public WriteBatch::Handler { + public: + KVMap* map_; + virtual void Put(const Slice& key, const Slice& value) { + (*map_)[key.ToString()] = value.ToString(); + } + virtual void Delete(const Slice& key) { + map_->erase(key.ToString()); + } + }; + Handler handler; + handler.map_ = &map_; + return batch->Iterate(&handler); + } + + virtual bool GetProperty(const Slice& property, std::string* value) { + return false; + } + virtual void GetApproximateSizes(const Range* r, int n, uint64_t* sizes) { + for (int i = 0; i < n; i++) { + sizes[i] = 0; + } + } + virtual void CompactRange(const Slice* start, const Slice* end) { + } + + private: + class ModelIter: public Iterator { + public: + ModelIter(const KVMap* map, bool owned) + : map_(map), owned_(owned), iter_(map_->end()) { + } + ~ModelIter() { + if (owned_) delete map_; + } + virtual bool Valid() const { return iter_ != map_->end(); } + virtual void SeekToFirst() { iter_ = map_->begin(); } + virtual void SeekToLast() { + if (map_->empty()) { + iter_ = map_->end(); + } else { + iter_ = map_->find(map_->rbegin()->first); + } + } + virtual void Seek(const Slice& k) { + iter_ = map_->lower_bound(k.ToString()); + } + virtual void Next() { ++iter_; } + virtual void Prev() { --iter_; } + virtual Slice key() const { return iter_->first; } + virtual Slice value() const { return iter_->second; } + virtual Status status() const { return Status::OK(); } + private: + const KVMap* const map_; + const bool owned_; // Do we own map_ + KVMap::const_iterator iter_; + }; + const Options options_; + KVMap map_; +}; + +static std::string RandomKey(Random* rnd) { + int len = (rnd->OneIn(3) + ? 1 // Short sometimes to encourage collisions + : (rnd->OneIn(100) ? rnd->Skewed(10) : rnd->Uniform(10))); + return test::RandomKey(rnd, len); +} + +static bool CompareIterators(int step, + DB* model, + DB* db, + const Snapshot* model_snap, + const Snapshot* db_snap) { + ReadOptions options; + options.snapshot = model_snap; + Iterator* miter = model->NewIterator(options); + options.snapshot = db_snap; + Iterator* dbiter = db->NewIterator(options); + bool ok = true; + int count = 0; + for (miter->SeekToFirst(), dbiter->SeekToFirst(); + ok && miter->Valid() && dbiter->Valid(); + miter->Next(), dbiter->Next()) { + count++; + if (miter->key().compare(dbiter->key()) != 0) { + fprintf(stderr, "step %d: Key mismatch: '%s' vs. '%s'\n", + step, + EscapeString(miter->key()).c_str(), + EscapeString(dbiter->key()).c_str()); + ok = false; + break; + } + + if (miter->value().compare(dbiter->value()) != 0) { + fprintf(stderr, "step %d: Value mismatch for key '%s': '%s' vs. '%s'\n", + step, + EscapeString(miter->key()).c_str(), + EscapeString(miter->value()).c_str(), + EscapeString(miter->value()).c_str()); + ok = false; + } + } + + if (ok) { + if (miter->Valid() != dbiter->Valid()) { + fprintf(stderr, "step %d: Mismatch at end of iterators: %d vs. %d\n", + step, miter->Valid(), dbiter->Valid()); + ok = false; + } + } + fprintf(stderr, "%d entries compared: ok=%d\n", count, ok); + delete miter; + delete dbiter; + return ok; +} + +TEST(DBTest, Randomized) { + Random rnd(test::RandomSeed()); + do { + ModelDB model(CurrentOptions()); + const int N = 10000; + const Snapshot* model_snap = NULL; + const Snapshot* db_snap = NULL; + std::string k, v; + for (int step = 0; step < N; step++) { + if (step % 100 == 0) { + fprintf(stderr, "Step %d of %d\n", step, N); + } + // TODO(sanjay): Test Get() works + int p = rnd.Uniform(100); + if (p < 45) { // Put + k = RandomKey(&rnd); + v = RandomString(&rnd, + rnd.OneIn(20) + ? 100 + rnd.Uniform(100) + : rnd.Uniform(8)); + ASSERT_OK(model.Put(WriteOptions(), k, v)); + ASSERT_OK(db_->Put(WriteOptions(), k, v)); + + } else if (p < 90) { // Delete + k = RandomKey(&rnd); + ASSERT_OK(model.Delete(WriteOptions(), k)); + ASSERT_OK(db_->Delete(WriteOptions(), k)); + + + } else { // Multi-element batch + WriteBatch b; + const int num = rnd.Uniform(8); + for (int i = 0; i < num; i++) { + if (i == 0 || !rnd.OneIn(10)) { + k = RandomKey(&rnd); + } else { + // Periodically re-use the same key from the previous iter, so + // we have multiple entries in the write batch for the same key + } + if (rnd.OneIn(2)) { + v = RandomString(&rnd, rnd.Uniform(10)); + b.Put(k, v); + } else { + b.Delete(k); + } + } + ASSERT_OK(model.Write(WriteOptions(), &b)); + ASSERT_OK(db_->Write(WriteOptions(), &b)); + } + + if ((step % 100) == 0) { + ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL)); + ASSERT_TRUE(CompareIterators(step, &model, db_, model_snap, db_snap)); + // Save a snapshot from each DB this time that we'll use next + // time we compare things, to make sure the current state is + // preserved with the snapshot + if (model_snap != NULL) model.ReleaseSnapshot(model_snap); + if (db_snap != NULL) db_->ReleaseSnapshot(db_snap); + + Reopen(); + ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL)); + + model_snap = model.GetSnapshot(); + db_snap = db_->GetSnapshot(); + } + } + if (model_snap != NULL) model.ReleaseSnapshot(model_snap); + if (db_snap != NULL) db_->ReleaseSnapshot(db_snap); + } while (ChangeOptions()); +} + +std::string MakeKey(unsigned int num) { + char buf[30]; + snprintf(buf, sizeof(buf), "%016u", num); + return std::string(buf); +} + +void BM_LogAndApply(int iters, int num_base_files) { + std::string dbname = test::TmpDir() + "/leveldb_test_benchmark"; + DestroyDB(dbname, Options()); + + DB* db = NULL; + Options opts; + opts.create_if_missing = true; + Status s = DB::Open(opts, dbname, &db); + ASSERT_OK(s); + ASSERT_TRUE(db != NULL); + + delete db; + db = NULL; + + Env* env = Env::Default(); + + port::Mutex mu; + MutexLock l(&mu); + + InternalKeyComparator cmp(BytewiseComparator()); + Options options; + VersionSet vset(dbname, &options, NULL, &cmp); + ASSERT_OK(vset.Recover()); + VersionEdit vbase; + uint64_t fnum = 1; + for (int i = 0; i < num_base_files; i++) { + InternalKey start(MakeKey(2*fnum), 1, kTypeValue); + InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion); + vbase.AddFile(2, fnum++, 1 /* file size */, start, limit); + } + ASSERT_OK(vset.LogAndApply(&vbase, &mu)); + + uint64_t start_micros = env->NowMicros(); + + for (int i = 0; i < iters; i++) { + VersionEdit vedit; + vedit.DeleteFile(2, fnum); + InternalKey start(MakeKey(2*fnum), 1, kTypeValue); + InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion); + vedit.AddFile(2, fnum++, 1 /* file size */, start, limit); + vset.LogAndApply(&vedit, &mu); + } + uint64_t stop_micros = env->NowMicros(); + unsigned int us = stop_micros - start_micros; + char buf[16]; + snprintf(buf, sizeof(buf), "%d", num_base_files); + fprintf(stderr, + "BM_LogAndApply/%-6s %8d iters : %9u us (%7.0f us / iter)\n", + buf, iters, us, ((float)us) / iters); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + if (argc > 1 && std::string(argv[1]) == "--benchmark") { + leveldb::BM_LogAndApply(1000, 1); + leveldb::BM_LogAndApply(1000, 100); + leveldb::BM_LogAndApply(1000, 10000); + leveldb::BM_LogAndApply(100, 100000); + return 0; + } + + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/dbformat.cc b/src/leveldb/db/dbformat.cc new file mode 100644 index 0000000..28e11b3 --- /dev/null +++ b/src/leveldb/db/dbformat.cc @@ -0,0 +1,140 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include "db/dbformat.h" +#include "port/port.h" +#include "util/coding.h" + +namespace leveldb { + +static uint64_t PackSequenceAndType(uint64_t seq, ValueType t) { + assert(seq <= kMaxSequenceNumber); + assert(t <= kValueTypeForSeek); + return (seq << 8) | t; +} + +void AppendInternalKey(std::string* result, const ParsedInternalKey& key) { + result->append(key.user_key.data(), key.user_key.size()); + PutFixed64(result, PackSequenceAndType(key.sequence, key.type)); +} + +std::string ParsedInternalKey::DebugString() const { + char buf[50]; + snprintf(buf, sizeof(buf), "' @ %llu : %d", + (unsigned long long) sequence, + int(type)); + std::string result = "'"; + result += user_key.ToString(); + result += buf; + return result; +} + +std::string InternalKey::DebugString() const { + std::string result; + ParsedInternalKey parsed; + if (ParseInternalKey(rep_, &parsed)) { + result = parsed.DebugString(); + } else { + result = "(bad)"; + result.append(EscapeString(rep_)); + } + return result; +} + +const char* InternalKeyComparator::Name() const { + return "leveldb.InternalKeyComparator"; +} + +int InternalKeyComparator::Compare(const Slice& akey, const Slice& bkey) const { + // Order by: + // increasing user key (according to user-supplied comparator) + // decreasing sequence number + // decreasing type (though sequence# should be enough to disambiguate) + int r = user_comparator_->Compare(ExtractUserKey(akey), ExtractUserKey(bkey)); + if (r == 0) { + const uint64_t anum = DecodeFixed64(akey.data() + akey.size() - 8); + const uint64_t bnum = DecodeFixed64(bkey.data() + bkey.size() - 8); + if (anum > bnum) { + r = -1; + } else if (anum < bnum) { + r = +1; + } + } + return r; +} + +void InternalKeyComparator::FindShortestSeparator( + std::string* start, + const Slice& limit) const { + // Attempt to shorten the user portion of the key + Slice user_start = ExtractUserKey(*start); + Slice user_limit = ExtractUserKey(limit); + std::string tmp(user_start.data(), user_start.size()); + user_comparator_->FindShortestSeparator(&tmp, user_limit); + if (tmp.size() < user_start.size() && + user_comparator_->Compare(user_start, tmp) < 0) { + // User key has become shorter physically, but larger logically. + // Tack on the earliest possible number to the shortened user key. + PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek)); + assert(this->Compare(*start, tmp) < 0); + assert(this->Compare(tmp, limit) < 0); + start->swap(tmp); + } +} + +void InternalKeyComparator::FindShortSuccessor(std::string* key) const { + Slice user_key = ExtractUserKey(*key); + std::string tmp(user_key.data(), user_key.size()); + user_comparator_->FindShortSuccessor(&tmp); + if (tmp.size() < user_key.size() && + user_comparator_->Compare(user_key, tmp) < 0) { + // User key has become shorter physically, but larger logically. + // Tack on the earliest possible number to the shortened user key. + PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek)); + assert(this->Compare(*key, tmp) < 0); + key->swap(tmp); + } +} + +const char* InternalFilterPolicy::Name() const { + return user_policy_->Name(); +} + +void InternalFilterPolicy::CreateFilter(const Slice* keys, int n, + std::string* dst) const { + // We rely on the fact that the code in table.cc does not mind us + // adjusting keys[]. + Slice* mkey = const_cast(keys); + for (int i = 0; i < n; i++) { + mkey[i] = ExtractUserKey(keys[i]); + // TODO(sanjay): Suppress dups? + } + user_policy_->CreateFilter(keys, n, dst); +} + +bool InternalFilterPolicy::KeyMayMatch(const Slice& key, const Slice& f) const { + return user_policy_->KeyMayMatch(ExtractUserKey(key), f); +} + +LookupKey::LookupKey(const Slice& user_key, SequenceNumber s) { + size_t usize = user_key.size(); + size_t needed = usize + 13; // A conservative estimate + char* dst; + if (needed <= sizeof(space_)) { + dst = space_; + } else { + dst = new char[needed]; + } + start_ = dst; + dst = EncodeVarint32(dst, usize + 8); + kstart_ = dst; + memcpy(dst, user_key.data(), usize); + dst += usize; + EncodeFixed64(dst, PackSequenceAndType(s, kValueTypeForSeek)); + dst += 8; + end_ = dst; +} + +} // namespace leveldb diff --git a/src/leveldb/db/dbformat.h b/src/leveldb/db/dbformat.h new file mode 100644 index 0000000..f7f64da --- /dev/null +++ b/src/leveldb/db/dbformat.h @@ -0,0 +1,227 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_FORMAT_H_ +#define STORAGE_LEVELDB_DB_FORMAT_H_ + +#include +#include "leveldb/comparator.h" +#include "leveldb/db.h" +#include "leveldb/filter_policy.h" +#include "leveldb/slice.h" +#include "leveldb/table_builder.h" +#include "util/coding.h" +#include "util/logging.h" + +namespace leveldb { + +// Grouping of constants. We may want to make some of these +// parameters set via options. +namespace config { +static const int kNumLevels = 7; + +// Level-0 compaction is started when we hit this many files. +static const int kL0_CompactionTrigger = 4; + +// Soft limit on number of level-0 files. We slow down writes at this point. +static const int kL0_SlowdownWritesTrigger = 8; + +// Maximum number of level-0 files. We stop writes at this point. +static const int kL0_StopWritesTrigger = 12; + +// Maximum level to which a new compacted memtable is pushed if it +// does not create overlap. We try to push to level 2 to avoid the +// relatively expensive level 0=>1 compactions and to avoid some +// expensive manifest file operations. We do not push all the way to +// the largest level since that can generate a lot of wasted disk +// space if the same key space is being repeatedly overwritten. +static const int kMaxMemCompactLevel = 2; + +} // namespace config + +class InternalKey; + +// Value types encoded as the last component of internal keys. +// DO NOT CHANGE THESE ENUM VALUES: they are embedded in the on-disk +// data structures. +enum ValueType { + kTypeDeletion = 0x0, + kTypeValue = 0x1 +}; +// kValueTypeForSeek defines the ValueType that should be passed when +// constructing a ParsedInternalKey object for seeking to a particular +// sequence number (since we sort sequence numbers in decreasing order +// and the value type is embedded as the low 8 bits in the sequence +// number in internal keys, we need to use the highest-numbered +// ValueType, not the lowest). +static const ValueType kValueTypeForSeek = kTypeValue; + +typedef uint64_t SequenceNumber; + +// We leave eight bits empty at the bottom so a type and sequence# +// can be packed together into 64-bits. +static const SequenceNumber kMaxSequenceNumber = + ((0x1ull << 56) - 1); + +struct ParsedInternalKey { + Slice user_key; + SequenceNumber sequence; + ValueType type; + + ParsedInternalKey() { } // Intentionally left uninitialized (for speed) + ParsedInternalKey(const Slice& u, const SequenceNumber& seq, ValueType t) + : user_key(u), sequence(seq), type(t) { } + std::string DebugString() const; +}; + +// Return the length of the encoding of "key". +inline size_t InternalKeyEncodingLength(const ParsedInternalKey& key) { + return key.user_key.size() + 8; +} + +// Append the serialization of "key" to *result. +extern void AppendInternalKey(std::string* result, + const ParsedInternalKey& key); + +// Attempt to parse an internal key from "internal_key". On success, +// stores the parsed data in "*result", and returns true. +// +// On error, returns false, leaves "*result" in an undefined state. +extern bool ParseInternalKey(const Slice& internal_key, + ParsedInternalKey* result); + +// Returns the user key portion of an internal key. +inline Slice ExtractUserKey(const Slice& internal_key) { + assert(internal_key.size() >= 8); + return Slice(internal_key.data(), internal_key.size() - 8); +} + +inline ValueType ExtractValueType(const Slice& internal_key) { + assert(internal_key.size() >= 8); + const size_t n = internal_key.size(); + uint64_t num = DecodeFixed64(internal_key.data() + n - 8); + unsigned char c = num & 0xff; + return static_cast(c); +} + +// A comparator for internal keys that uses a specified comparator for +// the user key portion and breaks ties by decreasing sequence number. +class InternalKeyComparator : public Comparator { + private: + const Comparator* user_comparator_; + public: + explicit InternalKeyComparator(const Comparator* c) : user_comparator_(c) { } + virtual const char* Name() const; + virtual int Compare(const Slice& a, const Slice& b) const; + virtual void FindShortestSeparator( + std::string* start, + const Slice& limit) const; + virtual void FindShortSuccessor(std::string* key) const; + + const Comparator* user_comparator() const { return user_comparator_; } + + int Compare(const InternalKey& a, const InternalKey& b) const; +}; + +// Filter policy wrapper that converts from internal keys to user keys +class InternalFilterPolicy : public FilterPolicy { + private: + const FilterPolicy* const user_policy_; + public: + explicit InternalFilterPolicy(const FilterPolicy* p) : user_policy_(p) { } + virtual const char* Name() const; + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const; + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const; +}; + +// Modules in this directory should keep internal keys wrapped inside +// the following class instead of plain strings so that we do not +// incorrectly use string comparisons instead of an InternalKeyComparator. +class InternalKey { + private: + std::string rep_; + public: + InternalKey() { } // Leave rep_ as empty to indicate it is invalid + InternalKey(const Slice& user_key, SequenceNumber s, ValueType t) { + AppendInternalKey(&rep_, ParsedInternalKey(user_key, s, t)); + } + + void DecodeFrom(const Slice& s) { rep_.assign(s.data(), s.size()); } + Slice Encode() const { + assert(!rep_.empty()); + return rep_; + } + + Slice user_key() const { return ExtractUserKey(rep_); } + + void SetFrom(const ParsedInternalKey& p) { + rep_.clear(); + AppendInternalKey(&rep_, p); + } + + void Clear() { rep_.clear(); } + + std::string DebugString() const; +}; + +inline int InternalKeyComparator::Compare( + const InternalKey& a, const InternalKey& b) const { + return Compare(a.Encode(), b.Encode()); +} + +inline bool ParseInternalKey(const Slice& internal_key, + ParsedInternalKey* result) { + const size_t n = internal_key.size(); + if (n < 8) return false; + uint64_t num = DecodeFixed64(internal_key.data() + n - 8); + unsigned char c = num & 0xff; + result->sequence = num >> 8; + result->type = static_cast(c); + result->user_key = Slice(internal_key.data(), n - 8); + return (c <= static_cast(kTypeValue)); +} + +// A helper class useful for DBImpl::Get() +class LookupKey { + public: + // Initialize *this for looking up user_key at a snapshot with + // the specified sequence number. + LookupKey(const Slice& user_key, SequenceNumber sequence); + + ~LookupKey(); + + // Return a key suitable for lookup in a MemTable. + Slice memtable_key() const { return Slice(start_, end_ - start_); } + + // Return an internal key (suitable for passing to an internal iterator) + Slice internal_key() const { return Slice(kstart_, end_ - kstart_); } + + // Return the user key + Slice user_key() const { return Slice(kstart_, end_ - kstart_ - 8); } + + private: + // We construct a char array of the form: + // klength varint32 <-- start_ + // userkey char[klength] <-- kstart_ + // tag uint64 + // <-- end_ + // The array is a suitable MemTable key. + // The suffix starting with "userkey" can be used as an InternalKey. + const char* start_; + const char* kstart_; + const char* end_; + char space_[200]; // Avoid allocation for short keys + + // No copying allowed + LookupKey(const LookupKey&); + void operator=(const LookupKey&); +}; + +inline LookupKey::~LookupKey() { + if (start_ != space_) delete[] start_; +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_FORMAT_H_ diff --git a/src/leveldb/db/dbformat_test.cc b/src/leveldb/db/dbformat_test.cc new file mode 100644 index 0000000..5d82f5d --- /dev/null +++ b/src/leveldb/db/dbformat_test.cc @@ -0,0 +1,112 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/dbformat.h" +#include "util/logging.h" +#include "util/testharness.h" + +namespace leveldb { + +static std::string IKey(const std::string& user_key, + uint64_t seq, + ValueType vt) { + std::string encoded; + AppendInternalKey(&encoded, ParsedInternalKey(user_key, seq, vt)); + return encoded; +} + +static std::string Shorten(const std::string& s, const std::string& l) { + std::string result = s; + InternalKeyComparator(BytewiseComparator()).FindShortestSeparator(&result, l); + return result; +} + +static std::string ShortSuccessor(const std::string& s) { + std::string result = s; + InternalKeyComparator(BytewiseComparator()).FindShortSuccessor(&result); + return result; +} + +static void TestKey(const std::string& key, + uint64_t seq, + ValueType vt) { + std::string encoded = IKey(key, seq, vt); + + Slice in(encoded); + ParsedInternalKey decoded("", 0, kTypeValue); + + ASSERT_TRUE(ParseInternalKey(in, &decoded)); + ASSERT_EQ(key, decoded.user_key.ToString()); + ASSERT_EQ(seq, decoded.sequence); + ASSERT_EQ(vt, decoded.type); + + ASSERT_TRUE(!ParseInternalKey(Slice("bar"), &decoded)); +} + +class FormatTest { }; + +TEST(FormatTest, InternalKey_EncodeDecode) { + const char* keys[] = { "", "k", "hello", "longggggggggggggggggggggg" }; + const uint64_t seq[] = { + 1, 2, 3, + (1ull << 8) - 1, 1ull << 8, (1ull << 8) + 1, + (1ull << 16) - 1, 1ull << 16, (1ull << 16) + 1, + (1ull << 32) - 1, 1ull << 32, (1ull << 32) + 1 + }; + for (int k = 0; k < sizeof(keys) / sizeof(keys[0]); k++) { + for (int s = 0; s < sizeof(seq) / sizeof(seq[0]); s++) { + TestKey(keys[k], seq[s], kTypeValue); + TestKey("hello", 1, kTypeDeletion); + } + } +} + +TEST(FormatTest, InternalKeyShortSeparator) { + // When user keys are same + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 99, kTypeValue))); + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 101, kTypeValue))); + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 100, kTypeValue))); + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foo", 100, kTypeDeletion))); + + // When user keys are misordered + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("bar", 99, kTypeValue))); + + // When user keys are different, but correctly ordered + ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek), + Shorten(IKey("foo", 100, kTypeValue), + IKey("hello", 200, kTypeValue))); + + // When start user key is prefix of limit user key + ASSERT_EQ(IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), + IKey("foobar", 200, kTypeValue))); + + // When limit user key is prefix of start user key + ASSERT_EQ(IKey("foobar", 100, kTypeValue), + Shorten(IKey("foobar", 100, kTypeValue), + IKey("foo", 200, kTypeValue))); +} + +TEST(FormatTest, InternalKeyShortestSuccessor) { + ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek), + ShortSuccessor(IKey("foo", 100, kTypeValue))); + ASSERT_EQ(IKey("\xff\xff", 100, kTypeValue), + ShortSuccessor(IKey("\xff\xff", 100, kTypeValue))); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/filename.cc b/src/leveldb/db/filename.cc new file mode 100644 index 0000000..3c4d49f --- /dev/null +++ b/src/leveldb/db/filename.cc @@ -0,0 +1,139 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include "db/filename.h" +#include "db/dbformat.h" +#include "leveldb/env.h" +#include "util/logging.h" + +namespace leveldb { + +// A utility routine: write "data" to the named file and Sync() it. +extern Status WriteStringToFileSync(Env* env, const Slice& data, + const std::string& fname); + +static std::string MakeFileName(const std::string& name, uint64_t number, + const char* suffix) { + char buf[100]; + snprintf(buf, sizeof(buf), "/%06llu.%s", + static_cast(number), + suffix); + return name + buf; +} + +std::string LogFileName(const std::string& name, uint64_t number) { + assert(number > 0); + return MakeFileName(name, number, "log"); +} + +std::string TableFileName(const std::string& name, uint64_t number) { + assert(number > 0); + return MakeFileName(name, number, "sst"); +} + +std::string DescriptorFileName(const std::string& dbname, uint64_t number) { + assert(number > 0); + char buf[100]; + snprintf(buf, sizeof(buf), "/MANIFEST-%06llu", + static_cast(number)); + return dbname + buf; +} + +std::string CurrentFileName(const std::string& dbname) { + return dbname + "/CURRENT"; +} + +std::string LockFileName(const std::string& dbname) { + return dbname + "/LOCK"; +} + +std::string TempFileName(const std::string& dbname, uint64_t number) { + assert(number > 0); + return MakeFileName(dbname, number, "dbtmp"); +} + +std::string InfoLogFileName(const std::string& dbname) { + return dbname + "/LOG"; +} + +// Return the name of the old info log file for "dbname". +std::string OldInfoLogFileName(const std::string& dbname) { + return dbname + "/LOG.old"; +} + + +// Owned filenames have the form: +// dbname/CURRENT +// dbname/LOCK +// dbname/LOG +// dbname/LOG.old +// dbname/MANIFEST-[0-9]+ +// dbname/[0-9]+.(log|sst) +bool ParseFileName(const std::string& fname, + uint64_t* number, + FileType* type) { + Slice rest(fname); + if (rest == "CURRENT") { + *number = 0; + *type = kCurrentFile; + } else if (rest == "LOCK") { + *number = 0; + *type = kDBLockFile; + } else if (rest == "LOG" || rest == "LOG.old") { + *number = 0; + *type = kInfoLogFile; + } else if (rest.starts_with("MANIFEST-")) { + rest.remove_prefix(strlen("MANIFEST-")); + uint64_t num; + if (!ConsumeDecimalNumber(&rest, &num)) { + return false; + } + if (!rest.empty()) { + return false; + } + *type = kDescriptorFile; + *number = num; + } else { + // Avoid strtoull() to keep filename format independent of the + // current locale + uint64_t num; + if (!ConsumeDecimalNumber(&rest, &num)) { + return false; + } + Slice suffix = rest; + if (suffix == Slice(".log")) { + *type = kLogFile; + } else if (suffix == Slice(".sst")) { + *type = kTableFile; + } else if (suffix == Slice(".dbtmp")) { + *type = kTempFile; + } else { + return false; + } + *number = num; + } + return true; +} + +Status SetCurrentFile(Env* env, const std::string& dbname, + uint64_t descriptor_number) { + // Remove leading "dbname/" and add newline to manifest file name + std::string manifest = DescriptorFileName(dbname, descriptor_number); + Slice contents = manifest; + assert(contents.starts_with(dbname + "/")); + contents.remove_prefix(dbname.size() + 1); + std::string tmp = TempFileName(dbname, descriptor_number); + Status s = WriteStringToFileSync(env, contents.ToString() + "\n", tmp); + if (s.ok()) { + s = env->RenameFile(tmp, CurrentFileName(dbname)); + } + if (!s.ok()) { + env->DeleteFile(tmp); + } + return s; +} + +} // namespace leveldb diff --git a/src/leveldb/db/filename.h b/src/leveldb/db/filename.h new file mode 100644 index 0000000..d5d09b1 --- /dev/null +++ b/src/leveldb/db/filename.h @@ -0,0 +1,80 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// File names used by DB code + +#ifndef STORAGE_LEVELDB_DB_FILENAME_H_ +#define STORAGE_LEVELDB_DB_FILENAME_H_ + +#include +#include +#include "leveldb/slice.h" +#include "leveldb/status.h" +#include "port/port.h" + +namespace leveldb { + +class Env; + +enum FileType { + kLogFile, + kDBLockFile, + kTableFile, + kDescriptorFile, + kCurrentFile, + kTempFile, + kInfoLogFile // Either the current one, or an old one +}; + +// Return the name of the log file with the specified number +// in the db named by "dbname". The result will be prefixed with +// "dbname". +extern std::string LogFileName(const std::string& dbname, uint64_t number); + +// Return the name of the sstable with the specified number +// in the db named by "dbname". The result will be prefixed with +// "dbname". +extern std::string TableFileName(const std::string& dbname, uint64_t number); + +// Return the name of the descriptor file for the db named by +// "dbname" and the specified incarnation number. The result will be +// prefixed with "dbname". +extern std::string DescriptorFileName(const std::string& dbname, + uint64_t number); + +// Return the name of the current file. This file contains the name +// of the current manifest file. The result will be prefixed with +// "dbname". +extern std::string CurrentFileName(const std::string& dbname); + +// Return the name of the lock file for the db named by +// "dbname". The result will be prefixed with "dbname". +extern std::string LockFileName(const std::string& dbname); + +// Return the name of a temporary file owned by the db named "dbname". +// The result will be prefixed with "dbname". +extern std::string TempFileName(const std::string& dbname, uint64_t number); + +// Return the name of the info log file for "dbname". +extern std::string InfoLogFileName(const std::string& dbname); + +// Return the name of the old info log file for "dbname". +extern std::string OldInfoLogFileName(const std::string& dbname); + +// If filename is a leveldb file, store the type of the file in *type. +// The number encoded in the filename is stored in *number. If the +// filename was successfully parsed, returns true. Else return false. +extern bool ParseFileName(const std::string& filename, + uint64_t* number, + FileType* type); + +// Make the CURRENT file point to the descriptor file with the +// specified number. +extern Status SetCurrentFile(Env* env, const std::string& dbname, + uint64_t descriptor_number); + + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_FILENAME_H_ diff --git a/src/leveldb/db/filename_test.cc b/src/leveldb/db/filename_test.cc new file mode 100644 index 0000000..47353d6 --- /dev/null +++ b/src/leveldb/db/filename_test.cc @@ -0,0 +1,122 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/filename.h" + +#include "db/dbformat.h" +#include "port/port.h" +#include "util/logging.h" +#include "util/testharness.h" + +namespace leveldb { + +class FileNameTest { }; + +TEST(FileNameTest, Parse) { + Slice db; + FileType type; + uint64_t number; + + // Successful parses + static struct { + const char* fname; + uint64_t number; + FileType type; + } cases[] = { + { "100.log", 100, kLogFile }, + { "0.log", 0, kLogFile }, + { "0.sst", 0, kTableFile }, + { "CURRENT", 0, kCurrentFile }, + { "LOCK", 0, kDBLockFile }, + { "MANIFEST-2", 2, kDescriptorFile }, + { "MANIFEST-7", 7, kDescriptorFile }, + { "LOG", 0, kInfoLogFile }, + { "LOG.old", 0, kInfoLogFile }, + { "18446744073709551615.log", 18446744073709551615ull, kLogFile }, + }; + for (int i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + std::string f = cases[i].fname; + ASSERT_TRUE(ParseFileName(f, &number, &type)) << f; + ASSERT_EQ(cases[i].type, type) << f; + ASSERT_EQ(cases[i].number, number) << f; + } + + // Errors + static const char* errors[] = { + "", + "foo", + "foo-dx-100.log", + ".log", + "", + "manifest", + "CURREN", + "CURRENTX", + "MANIFES", + "MANIFEST", + "MANIFEST-", + "XMANIFEST-3", + "MANIFEST-3x", + "LOC", + "LOCKx", + "LO", + "LOGx", + "18446744073709551616.log", + "184467440737095516150.log", + "100", + "100.", + "100.lop" + }; + for (int i = 0; i < sizeof(errors) / sizeof(errors[0]); i++) { + std::string f = errors[i]; + ASSERT_TRUE(!ParseFileName(f, &number, &type)) << f; + }; +} + +TEST(FileNameTest, Construction) { + uint64_t number; + FileType type; + std::string fname; + + fname = CurrentFileName("foo"); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(0, number); + ASSERT_EQ(kCurrentFile, type); + + fname = LockFileName("foo"); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(0, number); + ASSERT_EQ(kDBLockFile, type); + + fname = LogFileName("foo", 192); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(192, number); + ASSERT_EQ(kLogFile, type); + + fname = TableFileName("bar", 200); + ASSERT_EQ("bar/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(200, number); + ASSERT_EQ(kTableFile, type); + + fname = DescriptorFileName("bar", 100); + ASSERT_EQ("bar/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(100, number); + ASSERT_EQ(kDescriptorFile, type); + + fname = TempFileName("tmp", 999); + ASSERT_EQ("tmp/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(999, number); + ASSERT_EQ(kTempFile, type); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/leveldb_main.cc b/src/leveldb/db/leveldb_main.cc new file mode 100644 index 0000000..995d761 --- /dev/null +++ b/src/leveldb/db/leveldb_main.cc @@ -0,0 +1,238 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include "db/dbformat.h" +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/version_edit.h" +#include "db/write_batch_internal.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "leveldb/options.h" +#include "leveldb/status.h" +#include "leveldb/table.h" +#include "leveldb/write_batch.h" +#include "util/logging.h" + +namespace leveldb { + +namespace { + +bool GuessType(const std::string& fname, FileType* type) { + size_t pos = fname.rfind('/'); + std::string basename; + if (pos == std::string::npos) { + basename = fname; + } else { + basename = std::string(fname.data() + pos + 1, fname.size() - pos - 1); + } + uint64_t ignored; + return ParseFileName(basename, &ignored, type); +} + +// Notified when log reader encounters corruption. +class CorruptionReporter : public log::Reader::Reporter { + public: + virtual void Corruption(size_t bytes, const Status& status) { + printf("corruption: %d bytes; %s\n", + static_cast(bytes), + status.ToString().c_str()); + } +}; + +// Print contents of a log file. (*func)() is called on every record. +bool PrintLogContents(Env* env, const std::string& fname, + void (*func)(Slice)) { + SequentialFile* file; + Status s = env->NewSequentialFile(fname, &file); + if (!s.ok()) { + fprintf(stderr, "%s\n", s.ToString().c_str()); + return false; + } + CorruptionReporter reporter; + log::Reader reader(file, &reporter, true, 0); + Slice record; + std::string scratch; + while (reader.ReadRecord(&record, &scratch)) { + printf("--- offset %llu; ", + static_cast(reader.LastRecordOffset())); + (*func)(record); + } + delete file; + return true; +} + +// Called on every item found in a WriteBatch. +class WriteBatchItemPrinter : public WriteBatch::Handler { + public: + uint64_t offset_; + uint64_t sequence_; + + virtual void Put(const Slice& key, const Slice& value) { + printf(" put '%s' '%s'\n", + EscapeString(key).c_str(), + EscapeString(value).c_str()); + } + virtual void Delete(const Slice& key) { + printf(" del '%s'\n", + EscapeString(key).c_str()); + } +}; + + +// Called on every log record (each one of which is a WriteBatch) +// found in a kLogFile. +static void WriteBatchPrinter(Slice record) { + if (record.size() < 12) { + printf("log record length %d is too small\n", + static_cast(record.size())); + return; + } + WriteBatch batch; + WriteBatchInternal::SetContents(&batch, record); + printf("sequence %llu\n", + static_cast(WriteBatchInternal::Sequence(&batch))); + WriteBatchItemPrinter batch_item_printer; + Status s = batch.Iterate(&batch_item_printer); + if (!s.ok()) { + printf(" error: %s\n", s.ToString().c_str()); + } +} + +bool DumpLog(Env* env, const std::string& fname) { + return PrintLogContents(env, fname, WriteBatchPrinter); +} + +// Called on every log record (each one of which is a WriteBatch) +// found in a kDescriptorFile. +static void VersionEditPrinter(Slice record) { + VersionEdit edit; + Status s = edit.DecodeFrom(record); + if (!s.ok()) { + printf("%s\n", s.ToString().c_str()); + return; + } + printf("%s", edit.DebugString().c_str()); +} + +bool DumpDescriptor(Env* env, const std::string& fname) { + return PrintLogContents(env, fname, VersionEditPrinter); +} + +bool DumpTable(Env* env, const std::string& fname) { + uint64_t file_size; + RandomAccessFile* file = NULL; + Table* table = NULL; + Status s = env->GetFileSize(fname, &file_size); + if (s.ok()) { + s = env->NewRandomAccessFile(fname, &file); + } + if (s.ok()) { + // We use the default comparator, which may or may not match the + // comparator used in this database. However this should not cause + // problems since we only use Table operations that do not require + // any comparisons. In particular, we do not call Seek or Prev. + s = Table::Open(Options(), file, file_size, &table); + } + if (!s.ok()) { + fprintf(stderr, "%s\n", s.ToString().c_str()); + delete table; + delete file; + return false; + } + + ReadOptions ro; + ro.fill_cache = false; + Iterator* iter = table->NewIterator(ro); + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + ParsedInternalKey key; + if (!ParseInternalKey(iter->key(), &key)) { + printf("badkey '%s' => '%s'\n", + EscapeString(iter->key()).c_str(), + EscapeString(iter->value()).c_str()); + } else { + char kbuf[20]; + const char* type; + if (key.type == kTypeDeletion) { + type = "del"; + } else if (key.type == kTypeValue) { + type = "val"; + } else { + snprintf(kbuf, sizeof(kbuf), "%d", static_cast(key.type)); + type = kbuf; + } + printf("'%s' @ %8llu : %s => '%s'\n", + EscapeString(key.user_key).c_str(), + static_cast(key.sequence), + type, + EscapeString(iter->value()).c_str()); + } + } + s = iter->status(); + if (!s.ok()) { + printf("iterator error: %s\n", s.ToString().c_str()); + } + + delete iter; + delete table; + delete file; + return true; +} + +bool DumpFile(Env* env, const std::string& fname) { + FileType ftype; + if (!GuessType(fname, &ftype)) { + fprintf(stderr, "%s: unknown file type\n", fname.c_str()); + return false; + } + switch (ftype) { + case kLogFile: return DumpLog(env, fname); + case kDescriptorFile: return DumpDescriptor(env, fname); + case kTableFile: return DumpTable(env, fname); + + default: { + fprintf(stderr, "%s: not a dump-able file type\n", fname.c_str()); + break; + } + } + return false; +} + +bool HandleDumpCommand(Env* env, char** files, int num) { + bool ok = true; + for (int i = 0; i < num; i++) { + ok &= DumpFile(env, files[i]); + } + return ok; +} + +} +} // namespace leveldb + +static void Usage() { + fprintf( + stderr, + "Usage: leveldbutil command...\n" + " dump files... -- dump contents of specified files\n" + ); +} + +int main(int argc, char** argv) { + leveldb::Env* env = leveldb::Env::Default(); + bool ok = true; + if (argc < 2) { + Usage(); + ok = false; + } else { + std::string command = argv[1]; + if (command == "dump") { + ok = leveldb::HandleDumpCommand(env, argv+2, argc-2); + } else { + Usage(); + ok = false; + } + } + return (ok ? 0 : 1); +} diff --git a/src/leveldb/db/log_format.h b/src/leveldb/db/log_format.h new file mode 100644 index 0000000..2690cb9 --- /dev/null +++ b/src/leveldb/db/log_format.h @@ -0,0 +1,35 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Log format information shared by reader and writer. +// See ../doc/log_format.txt for more detail. + +#ifndef STORAGE_LEVELDB_DB_LOG_FORMAT_H_ +#define STORAGE_LEVELDB_DB_LOG_FORMAT_H_ + +namespace leveldb { +namespace log { + +enum RecordType { + // Zero is reserved for preallocated files + kZeroType = 0, + + kFullType = 1, + + // For fragments + kFirstType = 2, + kMiddleType = 3, + kLastType = 4 +}; +static const int kMaxRecordType = kLastType; + +static const int kBlockSize = 32768; + +// Header is checksum (4 bytes), type (1 byte), length (2 bytes). +static const int kHeaderSize = 4 + 1 + 2; + +} // namespace log +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_LOG_FORMAT_H_ diff --git a/src/leveldb/db/log_reader.cc b/src/leveldb/db/log_reader.cc new file mode 100644 index 0000000..b35f115 --- /dev/null +++ b/src/leveldb/db/log_reader.cc @@ -0,0 +1,259 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/log_reader.h" + +#include +#include "leveldb/env.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { +namespace log { + +Reader::Reporter::~Reporter() { +} + +Reader::Reader(SequentialFile* file, Reporter* reporter, bool checksum, + uint64_t initial_offset) + : file_(file), + reporter_(reporter), + checksum_(checksum), + backing_store_(new char[kBlockSize]), + buffer_(), + eof_(false), + last_record_offset_(0), + end_of_buffer_offset_(0), + initial_offset_(initial_offset) { +} + +Reader::~Reader() { + delete[] backing_store_; +} + +bool Reader::SkipToInitialBlock() { + size_t offset_in_block = initial_offset_ % kBlockSize; + uint64_t block_start_location = initial_offset_ - offset_in_block; + + // Don't search a block if we'd be in the trailer + if (offset_in_block > kBlockSize - 6) { + offset_in_block = 0; + block_start_location += kBlockSize; + } + + end_of_buffer_offset_ = block_start_location; + + // Skip to start of first block that can contain the initial record + if (block_start_location > 0) { + Status skip_status = file_->Skip(block_start_location); + if (!skip_status.ok()) { + ReportDrop(block_start_location, skip_status); + return false; + } + } + + return true; +} + +bool Reader::ReadRecord(Slice* record, std::string* scratch) { + if (last_record_offset_ < initial_offset_) { + if (!SkipToInitialBlock()) { + return false; + } + } + + scratch->clear(); + record->clear(); + bool in_fragmented_record = false; + // Record offset of the logical record that we're reading + // 0 is a dummy value to make compilers happy + uint64_t prospective_record_offset = 0; + + Slice fragment; + while (true) { + uint64_t physical_record_offset = end_of_buffer_offset_ - buffer_.size(); + const unsigned int record_type = ReadPhysicalRecord(&fragment); + switch (record_type) { + case kFullType: + if (in_fragmented_record) { + // Handle bug in earlier versions of log::Writer where + // it could emit an empty kFirstType record at the tail end + // of a block followed by a kFullType or kFirstType record + // at the beginning of the next block. + if (scratch->empty()) { + in_fragmented_record = false; + } else { + ReportCorruption(scratch->size(), "partial record without end(1)"); + } + } + prospective_record_offset = physical_record_offset; + scratch->clear(); + *record = fragment; + last_record_offset_ = prospective_record_offset; + return true; + + case kFirstType: + if (in_fragmented_record) { + // Handle bug in earlier versions of log::Writer where + // it could emit an empty kFirstType record at the tail end + // of a block followed by a kFullType or kFirstType record + // at the beginning of the next block. + if (scratch->empty()) { + in_fragmented_record = false; + } else { + ReportCorruption(scratch->size(), "partial record without end(2)"); + } + } + prospective_record_offset = physical_record_offset; + scratch->assign(fragment.data(), fragment.size()); + in_fragmented_record = true; + break; + + case kMiddleType: + if (!in_fragmented_record) { + ReportCorruption(fragment.size(), + "missing start of fragmented record(1)"); + } else { + scratch->append(fragment.data(), fragment.size()); + } + break; + + case kLastType: + if (!in_fragmented_record) { + ReportCorruption(fragment.size(), + "missing start of fragmented record(2)"); + } else { + scratch->append(fragment.data(), fragment.size()); + *record = Slice(*scratch); + last_record_offset_ = prospective_record_offset; + return true; + } + break; + + case kEof: + if (in_fragmented_record) { + ReportCorruption(scratch->size(), "partial record without end(3)"); + scratch->clear(); + } + return false; + + case kBadRecord: + if (in_fragmented_record) { + ReportCorruption(scratch->size(), "error in middle of record"); + in_fragmented_record = false; + scratch->clear(); + } + break; + + default: { + char buf[40]; + snprintf(buf, sizeof(buf), "unknown record type %u", record_type); + ReportCorruption( + (fragment.size() + (in_fragmented_record ? scratch->size() : 0)), + buf); + in_fragmented_record = false; + scratch->clear(); + break; + } + } + } + return false; +} + +uint64_t Reader::LastRecordOffset() { + return last_record_offset_; +} + +void Reader::ReportCorruption(size_t bytes, const char* reason) { + ReportDrop(bytes, Status::Corruption(reason)); +} + +void Reader::ReportDrop(size_t bytes, const Status& reason) { + if (reporter_ != NULL && + end_of_buffer_offset_ - buffer_.size() - bytes >= initial_offset_) { + reporter_->Corruption(bytes, reason); + } +} + +unsigned int Reader::ReadPhysicalRecord(Slice* result) { + while (true) { + if (buffer_.size() < kHeaderSize) { + if (!eof_) { + // Last read was a full read, so this is a trailer to skip + buffer_.clear(); + Status status = file_->Read(kBlockSize, &buffer_, backing_store_); + end_of_buffer_offset_ += buffer_.size(); + if (!status.ok()) { + buffer_.clear(); + ReportDrop(kBlockSize, status); + eof_ = true; + return kEof; + } else if (buffer_.size() < kBlockSize) { + eof_ = true; + } + continue; + } else if (buffer_.size() == 0) { + // End of file + return kEof; + } else { + size_t drop_size = buffer_.size(); + buffer_.clear(); + ReportCorruption(drop_size, "truncated record at end of file"); + return kEof; + } + } + + // Parse the header + const char* header = buffer_.data(); + const uint32_t a = static_cast(header[4]) & 0xff; + const uint32_t b = static_cast(header[5]) & 0xff; + const unsigned int type = header[6]; + const uint32_t length = a | (b << 8); + if (kHeaderSize + length > buffer_.size()) { + size_t drop_size = buffer_.size(); + buffer_.clear(); + ReportCorruption(drop_size, "bad record length"); + return kBadRecord; + } + + if (type == kZeroType && length == 0) { + // Skip zero length record without reporting any drops since + // such records are produced by the mmap based writing code in + // env_posix.cc that preallocates file regions. + buffer_.clear(); + return kBadRecord; + } + + // Check crc + if (checksum_) { + uint32_t expected_crc = crc32c::Unmask(DecodeFixed32(header)); + uint32_t actual_crc = crc32c::Value(header + 6, 1 + length); + if (actual_crc != expected_crc) { + // Drop the rest of the buffer since "length" itself may have + // been corrupted and if we trust it, we could find some + // fragment of a real log record that just happens to look + // like a valid log record. + size_t drop_size = buffer_.size(); + buffer_.clear(); + ReportCorruption(drop_size, "checksum mismatch"); + return kBadRecord; + } + } + + buffer_.remove_prefix(kHeaderSize + length); + + // Skip physical record that started before initial_offset_ + if (end_of_buffer_offset_ - buffer_.size() - kHeaderSize - length < + initial_offset_) { + result->clear(); + return kBadRecord; + } + + *result = Slice(header + kHeaderSize, length); + return type; + } +} + +} // namespace log +} // namespace leveldb diff --git a/src/leveldb/db/log_reader.h b/src/leveldb/db/log_reader.h new file mode 100644 index 0000000..82d4bee --- /dev/null +++ b/src/leveldb/db/log_reader.h @@ -0,0 +1,108 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_LOG_READER_H_ +#define STORAGE_LEVELDB_DB_LOG_READER_H_ + +#include + +#include "db/log_format.h" +#include "leveldb/slice.h" +#include "leveldb/status.h" + +namespace leveldb { + +class SequentialFile; + +namespace log { + +class Reader { + public: + // Interface for reporting errors. + class Reporter { + public: + virtual ~Reporter(); + + // Some corruption was detected. "size" is the approximate number + // of bytes dropped due to the corruption. + virtual void Corruption(size_t bytes, const Status& status) = 0; + }; + + // Create a reader that will return log records from "*file". + // "*file" must remain live while this Reader is in use. + // + // If "reporter" is non-NULL, it is notified whenever some data is + // dropped due to a detected corruption. "*reporter" must remain + // live while this Reader is in use. + // + // If "checksum" is true, verify checksums if available. + // + // The Reader will start reading at the first record located at physical + // position >= initial_offset within the file. + Reader(SequentialFile* file, Reporter* reporter, bool checksum, + uint64_t initial_offset); + + ~Reader(); + + // Read the next record into *record. Returns true if read + // successfully, false if we hit end of the input. May use + // "*scratch" as temporary storage. The contents filled in *record + // will only be valid until the next mutating operation on this + // reader or the next mutation to *scratch. + bool ReadRecord(Slice* record, std::string* scratch); + + // Returns the physical offset of the last record returned by ReadRecord. + // + // Undefined before the first call to ReadRecord. + uint64_t LastRecordOffset(); + + private: + SequentialFile* const file_; + Reporter* const reporter_; + bool const checksum_; + char* const backing_store_; + Slice buffer_; + bool eof_; // Last Read() indicated EOF by returning < kBlockSize + + // Offset of the last record returned by ReadRecord. + uint64_t last_record_offset_; + // Offset of the first location past the end of buffer_. + uint64_t end_of_buffer_offset_; + + // Offset at which to start looking for the first record to return + uint64_t const initial_offset_; + + // Extend record types with the following special values + enum { + kEof = kMaxRecordType + 1, + // Returned whenever we find an invalid physical record. + // Currently there are three situations in which this happens: + // * The record has an invalid CRC (ReadPhysicalRecord reports a drop) + // * The record is a 0-length record (No drop is reported) + // * The record is below constructor's initial_offset (No drop is reported) + kBadRecord = kMaxRecordType + 2 + }; + + // Skips all blocks that are completely before "initial_offset_". + // + // Returns true on success. Handles reporting. + bool SkipToInitialBlock(); + + // Return type, or one of the preceding special values + unsigned int ReadPhysicalRecord(Slice* result); + + // Reports dropped bytes to the reporter. + // buffer_ must be updated to remove the dropped bytes prior to invocation. + void ReportCorruption(size_t bytes, const char* reason); + void ReportDrop(size_t bytes, const Status& reason); + + // No copying allowed + Reader(const Reader&); + void operator=(const Reader&); +}; + +} // namespace log +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_LOG_READER_H_ diff --git a/src/leveldb/db/log_test.cc b/src/leveldb/db/log_test.cc new file mode 100644 index 0000000..4c5cf87 --- /dev/null +++ b/src/leveldb/db/log_test.cc @@ -0,0 +1,500 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "leveldb/env.h" +#include "util/coding.h" +#include "util/crc32c.h" +#include "util/random.h" +#include "util/testharness.h" + +namespace leveldb { +namespace log { + +// Construct a string of the specified length made out of the supplied +// partial string. +static std::string BigString(const std::string& partial_string, size_t n) { + std::string result; + while (result.size() < n) { + result.append(partial_string); + } + result.resize(n); + return result; +} + +// Construct a string from a number +static std::string NumberString(int n) { + char buf[50]; + snprintf(buf, sizeof(buf), "%d.", n); + return std::string(buf); +} + +// Return a skewed potentially long string +static std::string RandomSkewedString(int i, Random* rnd) { + return BigString(NumberString(i), rnd->Skewed(17)); +} + +class LogTest { + private: + class StringDest : public WritableFile { + public: + std::string contents_; + + virtual Status Close() { return Status::OK(); } + virtual Status Flush() { return Status::OK(); } + virtual Status Sync() { return Status::OK(); } + virtual Status Append(const Slice& slice) { + contents_.append(slice.data(), slice.size()); + return Status::OK(); + } + }; + + class StringSource : public SequentialFile { + public: + Slice contents_; + bool force_error_; + bool returned_partial_; + StringSource() : force_error_(false), returned_partial_(false) { } + + virtual Status Read(size_t n, Slice* result, char* scratch) { + ASSERT_TRUE(!returned_partial_) << "must not Read() after eof/error"; + + if (force_error_) { + force_error_ = false; + returned_partial_ = true; + return Status::Corruption("read error"); + } + + if (contents_.size() < n) { + n = contents_.size(); + returned_partial_ = true; + } + *result = Slice(contents_.data(), n); + contents_.remove_prefix(n); + return Status::OK(); + } + + virtual Status Skip(uint64_t n) { + if (n > contents_.size()) { + contents_.clear(); + return Status::NotFound("in-memory file skipepd past end"); + } + + contents_.remove_prefix(n); + + return Status::OK(); + } + }; + + class ReportCollector : public Reader::Reporter { + public: + size_t dropped_bytes_; + std::string message_; + + ReportCollector() : dropped_bytes_(0) { } + virtual void Corruption(size_t bytes, const Status& status) { + dropped_bytes_ += bytes; + message_.append(status.ToString()); + } + }; + + StringDest dest_; + StringSource source_; + ReportCollector report_; + bool reading_; + Writer writer_; + Reader reader_; + + // Record metadata for testing initial offset functionality + static size_t initial_offset_record_sizes_[]; + static uint64_t initial_offset_last_record_offsets_[]; + + public: + LogTest() : reading_(false), + writer_(&dest_), + reader_(&source_, &report_, true/*checksum*/, + 0/*initial_offset*/) { + } + + void Write(const std::string& msg) { + ASSERT_TRUE(!reading_) << "Write() after starting to read"; + writer_.AddRecord(Slice(msg)); + } + + size_t WrittenBytes() const { + return dest_.contents_.size(); + } + + std::string Read() { + if (!reading_) { + reading_ = true; + source_.contents_ = Slice(dest_.contents_); + } + std::string scratch; + Slice record; + if (reader_.ReadRecord(&record, &scratch)) { + return record.ToString(); + } else { + return "EOF"; + } + } + + void IncrementByte(int offset, int delta) { + dest_.contents_[offset] += delta; + } + + void SetByte(int offset, char new_byte) { + dest_.contents_[offset] = new_byte; + } + + void ShrinkSize(int bytes) { + dest_.contents_.resize(dest_.contents_.size() - bytes); + } + + void FixChecksum(int header_offset, int len) { + // Compute crc of type/len/data + uint32_t crc = crc32c::Value(&dest_.contents_[header_offset+6], 1 + len); + crc = crc32c::Mask(crc); + EncodeFixed32(&dest_.contents_[header_offset], crc); + } + + void ForceError() { + source_.force_error_ = true; + } + + size_t DroppedBytes() const { + return report_.dropped_bytes_; + } + + std::string ReportMessage() const { + return report_.message_; + } + + // Returns OK iff recorded error message contains "msg" + std::string MatchError(const std::string& msg) const { + if (report_.message_.find(msg) == std::string::npos) { + return report_.message_; + } else { + return "OK"; + } + } + + void WriteInitialOffsetLog() { + for (int i = 0; i < 4; i++) { + std::string record(initial_offset_record_sizes_[i], + static_cast('a' + i)); + Write(record); + } + } + + void CheckOffsetPastEndReturnsNoRecords(uint64_t offset_past_end) { + WriteInitialOffsetLog(); + reading_ = true; + source_.contents_ = Slice(dest_.contents_); + Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/, + WrittenBytes() + offset_past_end); + Slice record; + std::string scratch; + ASSERT_TRUE(!offset_reader->ReadRecord(&record, &scratch)); + delete offset_reader; + } + + void CheckInitialOffsetRecord(uint64_t initial_offset, + int expected_record_offset) { + WriteInitialOffsetLog(); + reading_ = true; + source_.contents_ = Slice(dest_.contents_); + Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/, + initial_offset); + Slice record; + std::string scratch; + ASSERT_TRUE(offset_reader->ReadRecord(&record, &scratch)); + ASSERT_EQ(initial_offset_record_sizes_[expected_record_offset], + record.size()); + ASSERT_EQ(initial_offset_last_record_offsets_[expected_record_offset], + offset_reader->LastRecordOffset()); + ASSERT_EQ((char)('a' + expected_record_offset), record.data()[0]); + delete offset_reader; + } + +}; + +size_t LogTest::initial_offset_record_sizes_[] = + {10000, // Two sizable records in first block + 10000, + 2 * log::kBlockSize - 1000, // Span three blocks + 1}; + +uint64_t LogTest::initial_offset_last_record_offsets_[] = + {0, + kHeaderSize + 10000, + 2 * (kHeaderSize + 10000), + 2 * (kHeaderSize + 10000) + + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize}; + + +TEST(LogTest, Empty) { + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, ReadWrite) { + Write("foo"); + Write("bar"); + Write(""); + Write("xxxx"); + ASSERT_EQ("foo", Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("", Read()); + ASSERT_EQ("xxxx", Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ("EOF", Read()); // Make sure reads at eof work +} + +TEST(LogTest, ManyBlocks) { + for (int i = 0; i < 100000; i++) { + Write(NumberString(i)); + } + for (int i = 0; i < 100000; i++) { + ASSERT_EQ(NumberString(i), Read()); + } + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, Fragmentation) { + Write("small"); + Write(BigString("medium", 50000)); + Write(BigString("large", 100000)); + ASSERT_EQ("small", Read()); + ASSERT_EQ(BigString("medium", 50000), Read()); + ASSERT_EQ(BigString("large", 100000), Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, MarginalTrailer) { + // Make a trailer that is exactly the same length as an empty record. + const int n = kBlockSize - 2*kHeaderSize; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes()); + Write(""); + Write("bar"); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("", Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, MarginalTrailer2) { + // Make a trailer that is exactly the same length as an empty record. + const int n = kBlockSize - 2*kHeaderSize; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes()); + Write("bar"); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(0, DroppedBytes()); + ASSERT_EQ("", ReportMessage()); +} + +TEST(LogTest, ShortTrailer) { + const int n = kBlockSize - 2*kHeaderSize + 4; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes()); + Write(""); + Write("bar"); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("", Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, AlignedEof) { + const int n = kBlockSize - 2*kHeaderSize + 4; + Write(BigString("foo", n)); + ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes()); + ASSERT_EQ(BigString("foo", n), Read()); + ASSERT_EQ("EOF", Read()); +} + +TEST(LogTest, RandomRead) { + const int N = 500; + Random write_rnd(301); + for (int i = 0; i < N; i++) { + Write(RandomSkewedString(i, &write_rnd)); + } + Random read_rnd(301); + for (int i = 0; i < N; i++) { + ASSERT_EQ(RandomSkewedString(i, &read_rnd), Read()); + } + ASSERT_EQ("EOF", Read()); +} + +// Tests of all the error paths in log_reader.cc follow: + +TEST(LogTest, ReadError) { + Write("foo"); + ForceError(); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(kBlockSize, DroppedBytes()); + ASSERT_EQ("OK", MatchError("read error")); +} + +TEST(LogTest, BadRecordType) { + Write("foo"); + // Type is stored in header[6] + IncrementByte(6, 100); + FixChecksum(0, 3); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("unknown record type")); +} + +TEST(LogTest, TruncatedTrailingRecord) { + Write("foo"); + ShrinkSize(4); // Drop all payload as well as a header byte + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(kHeaderSize - 1, DroppedBytes()); + ASSERT_EQ("OK", MatchError("truncated record at end of file")); +} + +TEST(LogTest, BadLength) { + Write("foo"); + ShrinkSize(1); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(kHeaderSize + 2, DroppedBytes()); + ASSERT_EQ("OK", MatchError("bad record length")); +} + +TEST(LogTest, ChecksumMismatch) { + Write("foo"); + IncrementByte(0, 10); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(10, DroppedBytes()); + ASSERT_EQ("OK", MatchError("checksum mismatch")); +} + +TEST(LogTest, UnexpectedMiddleType) { + Write("foo"); + SetByte(6, kMiddleType); + FixChecksum(0, 3); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("missing start")); +} + +TEST(LogTest, UnexpectedLastType) { + Write("foo"); + SetByte(6, kLastType); + FixChecksum(0, 3); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("missing start")); +} + +TEST(LogTest, UnexpectedFullType) { + Write("foo"); + Write("bar"); + SetByte(6, kFirstType); + FixChecksum(0, 3); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("partial record without end")); +} + +TEST(LogTest, UnexpectedFirstType) { + Write("foo"); + Write(BigString("bar", 100000)); + SetByte(6, kFirstType); + FixChecksum(0, 3); + ASSERT_EQ(BigString("bar", 100000), Read()); + ASSERT_EQ("EOF", Read()); + ASSERT_EQ(3, DroppedBytes()); + ASSERT_EQ("OK", MatchError("partial record without end")); +} + +TEST(LogTest, ErrorJoinsRecords) { + // Consider two fragmented records: + // first(R1) last(R1) first(R2) last(R2) + // where the middle two fragments disappear. We do not want + // first(R1),last(R2) to get joined and returned as a valid record. + + // Write records that span two blocks + Write(BigString("foo", kBlockSize)); + Write(BigString("bar", kBlockSize)); + Write("correct"); + + // Wipe the middle block + for (int offset = kBlockSize; offset < 2*kBlockSize; offset++) { + SetByte(offset, 'x'); + } + + ASSERT_EQ("correct", Read()); + ASSERT_EQ("EOF", Read()); + const int dropped = DroppedBytes(); + ASSERT_LE(dropped, 2*kBlockSize + 100); + ASSERT_GE(dropped, 2*kBlockSize); +} + +TEST(LogTest, ReadStart) { + CheckInitialOffsetRecord(0, 0); +} + +TEST(LogTest, ReadSecondOneOff) { + CheckInitialOffsetRecord(1, 1); +} + +TEST(LogTest, ReadSecondTenThousand) { + CheckInitialOffsetRecord(10000, 1); +} + +TEST(LogTest, ReadSecondStart) { + CheckInitialOffsetRecord(10007, 1); +} + +TEST(LogTest, ReadThirdOneOff) { + CheckInitialOffsetRecord(10008, 2); +} + +TEST(LogTest, ReadThirdStart) { + CheckInitialOffsetRecord(20014, 2); +} + +TEST(LogTest, ReadFourthOneOff) { + CheckInitialOffsetRecord(20015, 3); +} + +TEST(LogTest, ReadFourthFirstBlockTrailer) { + CheckInitialOffsetRecord(log::kBlockSize - 4, 3); +} + +TEST(LogTest, ReadFourthMiddleBlock) { + CheckInitialOffsetRecord(log::kBlockSize + 1, 3); +} + +TEST(LogTest, ReadFourthLastBlock) { + CheckInitialOffsetRecord(2 * log::kBlockSize + 1, 3); +} + +TEST(LogTest, ReadFourthStart) { + CheckInitialOffsetRecord( + 2 * (kHeaderSize + 1000) + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize, + 3); +} + +TEST(LogTest, ReadEnd) { + CheckOffsetPastEndReturnsNoRecords(0); +} + +TEST(LogTest, ReadPastEnd) { + CheckOffsetPastEndReturnsNoRecords(5); +} + +} // namespace log +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/log_writer.cc b/src/leveldb/db/log_writer.cc new file mode 100644 index 0000000..2da99ac --- /dev/null +++ b/src/leveldb/db/log_writer.cc @@ -0,0 +1,103 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/log_writer.h" + +#include +#include "leveldb/env.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { +namespace log { + +Writer::Writer(WritableFile* dest) + : dest_(dest), + block_offset_(0) { + for (int i = 0; i <= kMaxRecordType; i++) { + char t = static_cast(i); + type_crc_[i] = crc32c::Value(&t, 1); + } +} + +Writer::~Writer() { +} + +Status Writer::AddRecord(const Slice& slice) { + const char* ptr = slice.data(); + size_t left = slice.size(); + + // Fragment the record if necessary and emit it. Note that if slice + // is empty, we still want to iterate once to emit a single + // zero-length record + Status s; + bool begin = true; + do { + const int leftover = kBlockSize - block_offset_; + assert(leftover >= 0); + if (leftover < kHeaderSize) { + // Switch to a new block + if (leftover > 0) { + // Fill the trailer (literal below relies on kHeaderSize being 7) + assert(kHeaderSize == 7); + dest_->Append(Slice("\x00\x00\x00\x00\x00\x00", leftover)); + } + block_offset_ = 0; + } + + // Invariant: we never leave < kHeaderSize bytes in a block. + assert(kBlockSize - block_offset_ - kHeaderSize >= 0); + + const size_t avail = kBlockSize - block_offset_ - kHeaderSize; + const size_t fragment_length = (left < avail) ? left : avail; + + RecordType type; + const bool end = (left == fragment_length); + if (begin && end) { + type = kFullType; + } else if (begin) { + type = kFirstType; + } else if (end) { + type = kLastType; + } else { + type = kMiddleType; + } + + s = EmitPhysicalRecord(type, ptr, fragment_length); + ptr += fragment_length; + left -= fragment_length; + begin = false; + } while (s.ok() && left > 0); + return s; +} + +Status Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) { + assert(n <= 0xffff); // Must fit in two bytes + assert(block_offset_ + kHeaderSize + n <= kBlockSize); + + // Format the header + char buf[kHeaderSize]; + buf[4] = static_cast(n & 0xff); + buf[5] = static_cast(n >> 8); + buf[6] = static_cast(t); + + // Compute the crc of the record type and the payload. + uint32_t crc = crc32c::Extend(type_crc_[t], ptr, n); + crc = crc32c::Mask(crc); // Adjust for storage + EncodeFixed32(buf, crc); + + // Write the header and the payload + Status s = dest_->Append(Slice(buf, kHeaderSize)); + if (s.ok()) { + s = dest_->Append(Slice(ptr, n)); + if (s.ok()) { + s = dest_->Flush(); + } + } + block_offset_ += kHeaderSize + n; + return s; +} + +} // namespace log +} // namespace leveldb diff --git a/src/leveldb/db/log_writer.h b/src/leveldb/db/log_writer.h new file mode 100644 index 0000000..a3a954d --- /dev/null +++ b/src/leveldb/db/log_writer.h @@ -0,0 +1,48 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_LOG_WRITER_H_ +#define STORAGE_LEVELDB_DB_LOG_WRITER_H_ + +#include +#include "db/log_format.h" +#include "leveldb/slice.h" +#include "leveldb/status.h" + +namespace leveldb { + +class WritableFile; + +namespace log { + +class Writer { + public: + // Create a writer that will append data to "*dest". + // "*dest" must be initially empty. + // "*dest" must remain live while this Writer is in use. + explicit Writer(WritableFile* dest); + ~Writer(); + + Status AddRecord(const Slice& slice); + + private: + WritableFile* dest_; + int block_offset_; // Current offset in block + + // crc32c values for all supported record types. These are + // pre-computed to reduce the overhead of computing the crc of the + // record type stored in the header. + uint32_t type_crc_[kMaxRecordType + 1]; + + Status EmitPhysicalRecord(RecordType type, const char* ptr, size_t length); + + // No copying allowed + Writer(const Writer&); + void operator=(const Writer&); +}; + +} // namespace log +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_LOG_WRITER_H_ diff --git a/src/leveldb/db/memtable.cc b/src/leveldb/db/memtable.cc new file mode 100644 index 0000000..bfec0a7 --- /dev/null +++ b/src/leveldb/db/memtable.cc @@ -0,0 +1,145 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/memtable.h" +#include "db/dbformat.h" +#include "leveldb/comparator.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "util/coding.h" + +namespace leveldb { + +static Slice GetLengthPrefixedSlice(const char* data) { + uint32_t len; + const char* p = data; + p = GetVarint32Ptr(p, p + 5, &len); // +5: we assume "p" is not corrupted + return Slice(p, len); +} + +MemTable::MemTable(const InternalKeyComparator& cmp) + : comparator_(cmp), + refs_(0), + table_(comparator_, &arena_) { +} + +MemTable::~MemTable() { + assert(refs_ == 0); +} + +size_t MemTable::ApproximateMemoryUsage() { return arena_.MemoryUsage(); } + +int MemTable::KeyComparator::operator()(const char* aptr, const char* bptr) + const { + // Internal keys are encoded as length-prefixed strings. + Slice a = GetLengthPrefixedSlice(aptr); + Slice b = GetLengthPrefixedSlice(bptr); + return comparator.Compare(a, b); +} + +// Encode a suitable internal key target for "target" and return it. +// Uses *scratch as scratch space, and the returned pointer will point +// into this scratch space. +static const char* EncodeKey(std::string* scratch, const Slice& target) { + scratch->clear(); + PutVarint32(scratch, target.size()); + scratch->append(target.data(), target.size()); + return scratch->data(); +} + +class MemTableIterator: public Iterator { + public: + explicit MemTableIterator(MemTable::Table* table) : iter_(table) { } + + virtual bool Valid() const { return iter_.Valid(); } + virtual void Seek(const Slice& k) { iter_.Seek(EncodeKey(&tmp_, k)); } + virtual void SeekToFirst() { iter_.SeekToFirst(); } + virtual void SeekToLast() { iter_.SeekToLast(); } + virtual void Next() { iter_.Next(); } + virtual void Prev() { iter_.Prev(); } + virtual Slice key() const { return GetLengthPrefixedSlice(iter_.key()); } + virtual Slice value() const { + Slice key_slice = GetLengthPrefixedSlice(iter_.key()); + return GetLengthPrefixedSlice(key_slice.data() + key_slice.size()); + } + + virtual Status status() const { return Status::OK(); } + + private: + MemTable::Table::Iterator iter_; + std::string tmp_; // For passing to EncodeKey + + // No copying allowed + MemTableIterator(const MemTableIterator&); + void operator=(const MemTableIterator&); +}; + +Iterator* MemTable::NewIterator() { + return new MemTableIterator(&table_); +} + +void MemTable::Add(SequenceNumber s, ValueType type, + const Slice& key, + const Slice& value) { + // Format of an entry is concatenation of: + // key_size : varint32 of internal_key.size() + // key bytes : char[internal_key.size()] + // value_size : varint32 of value.size() + // value bytes : char[value.size()] + size_t key_size = key.size(); + size_t val_size = value.size(); + size_t internal_key_size = key_size + 8; + const size_t encoded_len = + VarintLength(internal_key_size) + internal_key_size + + VarintLength(val_size) + val_size; + char* buf = arena_.Allocate(encoded_len); + char* p = EncodeVarint32(buf, internal_key_size); + memcpy(p, key.data(), key_size); + p += key_size; + EncodeFixed64(p, (s << 8) | type); + p += 8; + p = EncodeVarint32(p, val_size); + memcpy(p, value.data(), val_size); + assert((p + val_size) - buf == encoded_len); + table_.Insert(buf); +} + +bool MemTable::Get(const LookupKey& key, std::string* value, Status* s) { + Slice memkey = key.memtable_key(); + Table::Iterator iter(&table_); + iter.Seek(memkey.data()); + if (iter.Valid()) { + // entry format is: + // klength varint32 + // userkey char[klength] + // tag uint64 + // vlength varint32 + // value char[vlength] + // Check that it belongs to same user key. We do not check the + // sequence number since the Seek() call above should have skipped + // all entries with overly large sequence numbers. + const char* entry = iter.key(); + uint32_t key_length; + const char* key_ptr = GetVarint32Ptr(entry, entry+5, &key_length); + if (comparator_.comparator.user_comparator()->Compare( + Slice(key_ptr, key_length - 8), + key.user_key()) == 0) { + // Correct user key + const uint64_t tag = DecodeFixed64(key_ptr + key_length - 8); + switch (static_cast(tag & 0xff)) { + case kTypeValue: { + Slice v = GetLengthPrefixedSlice(key_ptr + key_length); + value->assign(v.data(), v.size()); + return true; + } + case kTypeDeletion: + *s = Status::NotFound(Slice()); + return true; + } + } + } + return false; +} + +} // namespace leveldb diff --git a/src/leveldb/db/memtable.h b/src/leveldb/db/memtable.h new file mode 100644 index 0000000..92e90bb --- /dev/null +++ b/src/leveldb/db/memtable.h @@ -0,0 +1,91 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_MEMTABLE_H_ +#define STORAGE_LEVELDB_DB_MEMTABLE_H_ + +#include +#include "leveldb/db.h" +#include "db/dbformat.h" +#include "db/skiplist.h" +#include "util/arena.h" + +namespace leveldb { + +class InternalKeyComparator; +class Mutex; +class MemTableIterator; + +class MemTable { + public: + // MemTables are reference counted. The initial reference count + // is zero and the caller must call Ref() at least once. + explicit MemTable(const InternalKeyComparator& comparator); + + // Increase reference count. + void Ref() { ++refs_; } + + // Drop reference count. Delete if no more references exist. + void Unref() { + --refs_; + assert(refs_ >= 0); + if (refs_ <= 0) { + delete this; + } + } + + // Returns an estimate of the number of bytes of data in use by this + // data structure. + // + // REQUIRES: external synchronization to prevent simultaneous + // operations on the same MemTable. + size_t ApproximateMemoryUsage(); + + // Return an iterator that yields the contents of the memtable. + // + // The caller must ensure that the underlying MemTable remains live + // while the returned iterator is live. The keys returned by this + // iterator are internal keys encoded by AppendInternalKey in the + // db/format.{h,cc} module. + Iterator* NewIterator(); + + // Add an entry into memtable that maps key to value at the + // specified sequence number and with the specified type. + // Typically value will be empty if type==kTypeDeletion. + void Add(SequenceNumber seq, ValueType type, + const Slice& key, + const Slice& value); + + // If memtable contains a value for key, store it in *value and return true. + // If memtable contains a deletion for key, store a NotFound() error + // in *status and return true. + // Else, return false. + bool Get(const LookupKey& key, std::string* value, Status* s); + + private: + ~MemTable(); // Private since only Unref() should be used to delete it + + struct KeyComparator { + const InternalKeyComparator comparator; + explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) { } + int operator()(const char* a, const char* b) const; + }; + friend class MemTableIterator; + friend class MemTableBackwardIterator; + + typedef SkipList Table; + + KeyComparator comparator_; + int refs_; + Arena arena_; + Table table_; + + // No copying allowed + MemTable(const MemTable&); + void operator=(const MemTable&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_MEMTABLE_H_ diff --git a/src/leveldb/db/repair.cc b/src/leveldb/db/repair.cc new file mode 100644 index 0000000..022d52f --- /dev/null +++ b/src/leveldb/db/repair.cc @@ -0,0 +1,389 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// We recover the contents of the descriptor from the other files we find. +// (1) Any log files are first converted to tables +// (2) We scan every table to compute +// (a) smallest/largest for the table +// (b) largest sequence number in the table +// (3) We generate descriptor contents: +// - log number is set to zero +// - next-file-number is set to 1 + largest file number we found +// - last-sequence-number is set to largest sequence# found across +// all tables (see 2c) +// - compaction pointers are cleared +// - every table file is added at level 0 +// +// Possible optimization 1: +// (a) Compute total size and use to pick appropriate max-level M +// (b) Sort tables by largest sequence# in the table +// (c) For each table: if it overlaps earlier table, place in level-0, +// else place in level-M. +// Possible optimization 2: +// Store per-table metadata (smallest, largest, largest-seq#, ...) +// in the table's meta section to speed up ScanTable. + +#include "db/builder.h" +#include "db/db_impl.h" +#include "db/dbformat.h" +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "db/memtable.h" +#include "db/table_cache.h" +#include "db/version_edit.h" +#include "db/write_batch_internal.h" +#include "leveldb/comparator.h" +#include "leveldb/db.h" +#include "leveldb/env.h" + +namespace leveldb { + +namespace { + +class Repairer { + public: + Repairer(const std::string& dbname, const Options& options) + : dbname_(dbname), + env_(options.env), + icmp_(options.comparator), + ipolicy_(options.filter_policy), + options_(SanitizeOptions(dbname, &icmp_, &ipolicy_, options)), + owns_info_log_(options_.info_log != options.info_log), + owns_cache_(options_.block_cache != options.block_cache), + next_file_number_(1) { + // TableCache can be small since we expect each table to be opened once. + table_cache_ = new TableCache(dbname_, &options_, 10); + } + + ~Repairer() { + delete table_cache_; + if (owns_info_log_) { + delete options_.info_log; + } + if (owns_cache_) { + delete options_.block_cache; + } + } + + Status Run() { + Status status = FindFiles(); + if (status.ok()) { + ConvertLogFilesToTables(); + ExtractMetaData(); + status = WriteDescriptor(); + } + if (status.ok()) { + unsigned long long bytes = 0; + for (size_t i = 0; i < tables_.size(); i++) { + bytes += tables_[i].meta.file_size; + } + Log(options_.info_log, + "**** Repaired leveldb %s; " + "recovered %d files; %llu bytes. " + "Some data may have been lost. " + "****", + dbname_.c_str(), + static_cast(tables_.size()), + bytes); + } + return status; + } + + private: + struct TableInfo { + FileMetaData meta; + SequenceNumber max_sequence; + }; + + std::string const dbname_; + Env* const env_; + InternalKeyComparator const icmp_; + InternalFilterPolicy const ipolicy_; + Options const options_; + bool owns_info_log_; + bool owns_cache_; + TableCache* table_cache_; + VersionEdit edit_; + + std::vector manifests_; + std::vector table_numbers_; + std::vector logs_; + std::vector tables_; + uint64_t next_file_number_; + + Status FindFiles() { + std::vector filenames; + Status status = env_->GetChildren(dbname_, &filenames); + if (!status.ok()) { + return status; + } + if (filenames.empty()) { + return Status::IOError(dbname_, "repair found no files"); + } + + uint64_t number; + FileType type; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type)) { + if (type == kDescriptorFile) { + manifests_.push_back(filenames[i]); + } else { + if (number + 1 > next_file_number_) { + next_file_number_ = number + 1; + } + if (type == kLogFile) { + logs_.push_back(number); + } else if (type == kTableFile) { + table_numbers_.push_back(number); + } else { + // Ignore other files + } + } + } + } + return status; + } + + void ConvertLogFilesToTables() { + for (size_t i = 0; i < logs_.size(); i++) { + std::string logname = LogFileName(dbname_, logs_[i]); + Status status = ConvertLogToTable(logs_[i]); + if (!status.ok()) { + Log(options_.info_log, "Log #%llu: ignoring conversion error: %s", + (unsigned long long) logs_[i], + status.ToString().c_str()); + } + ArchiveFile(logname); + } + } + + Status ConvertLogToTable(uint64_t log) { + struct LogReporter : public log::Reader::Reporter { + Env* env; + Logger* info_log; + uint64_t lognum; + virtual void Corruption(size_t bytes, const Status& s) { + // We print error messages for corruption, but continue repairing. + Log(info_log, "Log #%llu: dropping %d bytes; %s", + (unsigned long long) lognum, + static_cast(bytes), + s.ToString().c_str()); + } + }; + + // Open the log file + std::string logname = LogFileName(dbname_, log); + SequentialFile* lfile; + Status status = env_->NewSequentialFile(logname, &lfile); + if (!status.ok()) { + return status; + } + + // Create the log reader. + LogReporter reporter; + reporter.env = env_; + reporter.info_log = options_.info_log; + reporter.lognum = log; + // We intentially make log::Reader do checksumming so that + // corruptions cause entire commits to be skipped instead of + // propagating bad information (like overly large sequence + // numbers). + log::Reader reader(lfile, &reporter, false/*do not checksum*/, + 0/*initial_offset*/); + + // Read all the records and add to a memtable + std::string scratch; + Slice record; + WriteBatch batch; + MemTable* mem = new MemTable(icmp_); + mem->Ref(); + int counter = 0; + while (reader.ReadRecord(&record, &scratch)) { + if (record.size() < 12) { + reporter.Corruption( + record.size(), Status::Corruption("log record too small")); + continue; + } + WriteBatchInternal::SetContents(&batch, record); + status = WriteBatchInternal::InsertInto(&batch, mem); + if (status.ok()) { + counter += WriteBatchInternal::Count(&batch); + } else { + Log(options_.info_log, "Log #%llu: ignoring %s", + (unsigned long long) log, + status.ToString().c_str()); + status = Status::OK(); // Keep going with rest of file + } + } + delete lfile; + + // Do not record a version edit for this conversion to a Table + // since ExtractMetaData() will also generate edits. + FileMetaData meta; + meta.number = next_file_number_++; + Iterator* iter = mem->NewIterator(); + status = BuildTable(dbname_, env_, options_, table_cache_, iter, &meta); + delete iter; + mem->Unref(); + mem = NULL; + if (status.ok()) { + if (meta.file_size > 0) { + table_numbers_.push_back(meta.number); + } + } + Log(options_.info_log, "Log #%llu: %d ops saved to Table #%llu %s", + (unsigned long long) log, + counter, + (unsigned long long) meta.number, + status.ToString().c_str()); + return status; + } + + void ExtractMetaData() { + std::vector kept; + for (size_t i = 0; i < table_numbers_.size(); i++) { + TableInfo t; + t.meta.number = table_numbers_[i]; + Status status = ScanTable(&t); + if (!status.ok()) { + std::string fname = TableFileName(dbname_, table_numbers_[i]); + Log(options_.info_log, "Table #%llu: ignoring %s", + (unsigned long long) table_numbers_[i], + status.ToString().c_str()); + ArchiveFile(fname); + } else { + tables_.push_back(t); + } + } + } + + Status ScanTable(TableInfo* t) { + std::string fname = TableFileName(dbname_, t->meta.number); + int counter = 0; + Status status = env_->GetFileSize(fname, &t->meta.file_size); + if (status.ok()) { + Iterator* iter = table_cache_->NewIterator( + ReadOptions(), t->meta.number, t->meta.file_size); + bool empty = true; + ParsedInternalKey parsed; + t->max_sequence = 0; + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + Slice key = iter->key(); + if (!ParseInternalKey(key, &parsed)) { + Log(options_.info_log, "Table #%llu: unparsable key %s", + (unsigned long long) t->meta.number, + EscapeString(key).c_str()); + continue; + } + + counter++; + if (empty) { + empty = false; + t->meta.smallest.DecodeFrom(key); + } + t->meta.largest.DecodeFrom(key); + if (parsed.sequence > t->max_sequence) { + t->max_sequence = parsed.sequence; + } + } + if (!iter->status().ok()) { + status = iter->status(); + } + delete iter; + } + Log(options_.info_log, "Table #%llu: %d entries %s", + (unsigned long long) t->meta.number, + counter, + status.ToString().c_str()); + return status; + } + + Status WriteDescriptor() { + std::string tmp = TempFileName(dbname_, 1); + WritableFile* file; + Status status = env_->NewWritableFile(tmp, &file); + if (!status.ok()) { + return status; + } + + SequenceNumber max_sequence = 0; + for (size_t i = 0; i < tables_.size(); i++) { + if (max_sequence < tables_[i].max_sequence) { + max_sequence = tables_[i].max_sequence; + } + } + + edit_.SetComparatorName(icmp_.user_comparator()->Name()); + edit_.SetLogNumber(0); + edit_.SetNextFile(next_file_number_); + edit_.SetLastSequence(max_sequence); + + for (size_t i = 0; i < tables_.size(); i++) { + // TODO(opt): separate out into multiple levels + const TableInfo& t = tables_[i]; + edit_.AddFile(0, t.meta.number, t.meta.file_size, + t.meta.smallest, t.meta.largest); + } + + //fprintf(stderr, "NewDescriptor:\n%s\n", edit_.DebugString().c_str()); + { + log::Writer log(file); + std::string record; + edit_.EncodeTo(&record); + status = log.AddRecord(record); + } + if (status.ok()) { + status = file->Close(); + } + delete file; + file = NULL; + + if (!status.ok()) { + env_->DeleteFile(tmp); + } else { + // Discard older manifests + for (size_t i = 0; i < manifests_.size(); i++) { + ArchiveFile(dbname_ + "/" + manifests_[i]); + } + + // Install new manifest + status = env_->RenameFile(tmp, DescriptorFileName(dbname_, 1)); + if (status.ok()) { + status = SetCurrentFile(env_, dbname_, 1); + } else { + env_->DeleteFile(tmp); + } + } + return status; + } + + void ArchiveFile(const std::string& fname) { + // Move into another directory. E.g., for + // dir/foo + // rename to + // dir/lost/foo + const char* slash = strrchr(fname.c_str(), '/'); + std::string new_dir; + if (slash != NULL) { + new_dir.assign(fname.data(), slash - fname.data()); + } + new_dir.append("/lost"); + env_->CreateDir(new_dir); // Ignore error + std::string new_file = new_dir; + new_file.append("/"); + new_file.append((slash == NULL) ? fname.c_str() : slash + 1); + Status s = env_->RenameFile(fname, new_file); + Log(options_.info_log, "Archiving %s: %s\n", + fname.c_str(), s.ToString().c_str()); + } +}; +} // namespace + +Status RepairDB(const std::string& dbname, const Options& options) { + Repairer repairer(dbname, options); + return repairer.Run(); +} + +} // namespace leveldb diff --git a/src/leveldb/db/skiplist.h b/src/leveldb/db/skiplist.h new file mode 100644 index 0000000..af85be6 --- /dev/null +++ b/src/leveldb/db/skiplist.h @@ -0,0 +1,379 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Thread safety +// ------------- +// +// Writes require external synchronization, most likely a mutex. +// Reads require a guarantee that the SkipList will not be destroyed +// while the read is in progress. Apart from that, reads progress +// without any internal locking or synchronization. +// +// Invariants: +// +// (1) Allocated nodes are never deleted until the SkipList is +// destroyed. This is trivially guaranteed by the code since we +// never delete any skip list nodes. +// +// (2) The contents of a Node except for the next/prev pointers are +// immutable after the Node has been linked into the SkipList. +// Only Insert() modifies the list, and it is careful to initialize +// a node and use release-stores to publish the nodes in one or +// more lists. +// +// ... prev vs. next pointer ordering ... + +#include +#include +#include "port/port.h" +#include "util/arena.h" +#include "util/random.h" + +namespace leveldb { + +class Arena; + +template +class SkipList { + private: + struct Node; + + public: + // Create a new SkipList object that will use "cmp" for comparing keys, + // and will allocate memory using "*arena". Objects allocated in the arena + // must remain allocated for the lifetime of the skiplist object. + explicit SkipList(Comparator cmp, Arena* arena); + + // Insert key into the list. + // REQUIRES: nothing that compares equal to key is currently in the list. + void Insert(const Key& key); + + // Returns true iff an entry that compares equal to key is in the list. + bool Contains(const Key& key) const; + + // Iteration over the contents of a skip list + class Iterator { + public: + // Initialize an iterator over the specified list. + // The returned iterator is not valid. + explicit Iterator(const SkipList* list); + + // Returns true iff the iterator is positioned at a valid node. + bool Valid() const; + + // Returns the key at the current position. + // REQUIRES: Valid() + const Key& key() const; + + // Advances to the next position. + // REQUIRES: Valid() + void Next(); + + // Advances to the previous position. + // REQUIRES: Valid() + void Prev(); + + // Advance to the first entry with a key >= target + void Seek(const Key& target); + + // Position at the first entry in list. + // Final state of iterator is Valid() iff list is not empty. + void SeekToFirst(); + + // Position at the last entry in list. + // Final state of iterator is Valid() iff list is not empty. + void SeekToLast(); + + private: + const SkipList* list_; + Node* node_; + // Intentionally copyable + }; + + private: + enum { kMaxHeight = 12 }; + + // Immutable after construction + Comparator const compare_; + Arena* const arena_; // Arena used for allocations of nodes + + Node* const head_; + + // Modified only by Insert(). Read racily by readers, but stale + // values are ok. + port::AtomicPointer max_height_; // Height of the entire list + + inline int GetMaxHeight() const { + return static_cast( + reinterpret_cast(max_height_.NoBarrier_Load())); + } + + // Read/written only by Insert(). + Random rnd_; + + Node* NewNode(const Key& key, int height); + int RandomHeight(); + bool Equal(const Key& a, const Key& b) const { return (compare_(a, b) == 0); } + + // Return true if key is greater than the data stored in "n" + bool KeyIsAfterNode(const Key& key, Node* n) const; + + // Return the earliest node that comes at or after key. + // Return NULL if there is no such node. + // + // If prev is non-NULL, fills prev[level] with pointer to previous + // node at "level" for every level in [0..max_height_-1]. + Node* FindGreaterOrEqual(const Key& key, Node** prev) const; + + // Return the latest node with a key < key. + // Return head_ if there is no such node. + Node* FindLessThan(const Key& key) const; + + // Return the last node in the list. + // Return head_ if list is empty. + Node* FindLast() const; + + // No copying allowed + SkipList(const SkipList&); + void operator=(const SkipList&); +}; + +// Implementation details follow +template +struct SkipList::Node { + explicit Node(const Key& k) : key(k) { } + + Key const key; + + // Accessors/mutators for links. Wrapped in methods so we can + // add the appropriate barriers as necessary. + Node* Next(int n) { + assert(n >= 0); + // Use an 'acquire load' so that we observe a fully initialized + // version of the returned Node. + return reinterpret_cast(next_[n].Acquire_Load()); + } + void SetNext(int n, Node* x) { + assert(n >= 0); + // Use a 'release store' so that anybody who reads through this + // pointer observes a fully initialized version of the inserted node. + next_[n].Release_Store(x); + } + + // No-barrier variants that can be safely used in a few locations. + Node* NoBarrier_Next(int n) { + assert(n >= 0); + return reinterpret_cast(next_[n].NoBarrier_Load()); + } + void NoBarrier_SetNext(int n, Node* x) { + assert(n >= 0); + next_[n].NoBarrier_Store(x); + } + + private: + // Array of length equal to the node height. next_[0] is lowest level link. + port::AtomicPointer next_[1]; +}; + +template +typename SkipList::Node* +SkipList::NewNode(const Key& key, int height) { + char* mem = arena_->AllocateAligned( + sizeof(Node) + sizeof(port::AtomicPointer) * (height - 1)); + return new (mem) Node(key); +} + +template +inline SkipList::Iterator::Iterator(const SkipList* list) { + list_ = list; + node_ = NULL; +} + +template +inline bool SkipList::Iterator::Valid() const { + return node_ != NULL; +} + +template +inline const Key& SkipList::Iterator::key() const { + assert(Valid()); + return node_->key; +} + +template +inline void SkipList::Iterator::Next() { + assert(Valid()); + node_ = node_->Next(0); +} + +template +inline void SkipList::Iterator::Prev() { + // Instead of using explicit "prev" links, we just search for the + // last node that falls before key. + assert(Valid()); + node_ = list_->FindLessThan(node_->key); + if (node_ == list_->head_) { + node_ = NULL; + } +} + +template +inline void SkipList::Iterator::Seek(const Key& target) { + node_ = list_->FindGreaterOrEqual(target, NULL); +} + +template +inline void SkipList::Iterator::SeekToFirst() { + node_ = list_->head_->Next(0); +} + +template +inline void SkipList::Iterator::SeekToLast() { + node_ = list_->FindLast(); + if (node_ == list_->head_) { + node_ = NULL; + } +} + +template +int SkipList::RandomHeight() { + // Increase height with probability 1 in kBranching + static const unsigned int kBranching = 4; + int height = 1; + while (height < kMaxHeight && ((rnd_.Next() % kBranching) == 0)) { + height++; + } + assert(height > 0); + assert(height <= kMaxHeight); + return height; +} + +template +bool SkipList::KeyIsAfterNode(const Key& key, Node* n) const { + // NULL n is considered infinite + return (n != NULL) && (compare_(n->key, key) < 0); +} + +template +typename SkipList::Node* SkipList::FindGreaterOrEqual(const Key& key, Node** prev) + const { + Node* x = head_; + int level = GetMaxHeight() - 1; + while (true) { + Node* next = x->Next(level); + if (KeyIsAfterNode(key, next)) { + // Keep searching in this list + x = next; + } else { + if (prev != NULL) prev[level] = x; + if (level == 0) { + return next; + } else { + // Switch to next list + level--; + } + } + } +} + +template +typename SkipList::Node* +SkipList::FindLessThan(const Key& key) const { + Node* x = head_; + int level = GetMaxHeight() - 1; + while (true) { + assert(x == head_ || compare_(x->key, key) < 0); + Node* next = x->Next(level); + if (next == NULL || compare_(next->key, key) >= 0) { + if (level == 0) { + return x; + } else { + // Switch to next list + level--; + } + } else { + x = next; + } + } +} + +template +typename SkipList::Node* SkipList::FindLast() + const { + Node* x = head_; + int level = GetMaxHeight() - 1; + while (true) { + Node* next = x->Next(level); + if (next == NULL) { + if (level == 0) { + return x; + } else { + // Switch to next list + level--; + } + } else { + x = next; + } + } +} + +template +SkipList::SkipList(Comparator cmp, Arena* arena) + : compare_(cmp), + arena_(arena), + head_(NewNode(0 /* any key will do */, kMaxHeight)), + max_height_(reinterpret_cast(1)), + rnd_(0xdeadbeef) { + for (int i = 0; i < kMaxHeight; i++) { + head_->SetNext(i, NULL); + } +} + +template +void SkipList::Insert(const Key& key) { + // TODO(opt): We can use a barrier-free variant of FindGreaterOrEqual() + // here since Insert() is externally synchronized. + Node* prev[kMaxHeight]; + Node* x = FindGreaterOrEqual(key, prev); + + // Our data structure does not allow duplicate insertion + assert(x == NULL || !Equal(key, x->key)); + + int height = RandomHeight(); + if (height > GetMaxHeight()) { + for (int i = GetMaxHeight(); i < height; i++) { + prev[i] = head_; + } + //fprintf(stderr, "Change height from %d to %d\n", max_height_, height); + + // It is ok to mutate max_height_ without any synchronization + // with concurrent readers. A concurrent reader that observes + // the new value of max_height_ will see either the old value of + // new level pointers from head_ (NULL), or a new value set in + // the loop below. In the former case the reader will + // immediately drop to the next level since NULL sorts after all + // keys. In the latter case the reader will use the new node. + max_height_.NoBarrier_Store(reinterpret_cast(height)); + } + + x = NewNode(key, height); + for (int i = 0; i < height; i++) { + // NoBarrier_SetNext() suffices since we will add a barrier when + // we publish a pointer to "x" in prev[i]. + x->NoBarrier_SetNext(i, prev[i]->NoBarrier_Next(i)); + prev[i]->SetNext(i, x); + } +} + +template +bool SkipList::Contains(const Key& key) const { + Node* x = FindGreaterOrEqual(key, NULL); + if (x != NULL && Equal(key, x->key)) { + return true; + } else { + return false; + } +} + +} // namespace leveldb diff --git a/src/leveldb/db/skiplist_test.cc b/src/leveldb/db/skiplist_test.cc new file mode 100644 index 0000000..c78f4b4 --- /dev/null +++ b/src/leveldb/db/skiplist_test.cc @@ -0,0 +1,378 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/skiplist.h" +#include +#include "leveldb/env.h" +#include "util/arena.h" +#include "util/hash.h" +#include "util/random.h" +#include "util/testharness.h" + +namespace leveldb { + +typedef uint64_t Key; + +struct Comparator { + int operator()(const Key& a, const Key& b) const { + if (a < b) { + return -1; + } else if (a > b) { + return +1; + } else { + return 0; + } + } +}; + +class SkipTest { }; + +TEST(SkipTest, Empty) { + Arena arena; + Comparator cmp; + SkipList list(cmp, &arena); + ASSERT_TRUE(!list.Contains(10)); + + SkipList::Iterator iter(&list); + ASSERT_TRUE(!iter.Valid()); + iter.SeekToFirst(); + ASSERT_TRUE(!iter.Valid()); + iter.Seek(100); + ASSERT_TRUE(!iter.Valid()); + iter.SeekToLast(); + ASSERT_TRUE(!iter.Valid()); +} + +TEST(SkipTest, InsertAndLookup) { + const int N = 2000; + const int R = 5000; + Random rnd(1000); + std::set keys; + Arena arena; + Comparator cmp; + SkipList list(cmp, &arena); + for (int i = 0; i < N; i++) { + Key key = rnd.Next() % R; + if (keys.insert(key).second) { + list.Insert(key); + } + } + + for (int i = 0; i < R; i++) { + if (list.Contains(i)) { + ASSERT_EQ(keys.count(i), 1); + } else { + ASSERT_EQ(keys.count(i), 0); + } + } + + // Simple iterator tests + { + SkipList::Iterator iter(&list); + ASSERT_TRUE(!iter.Valid()); + + iter.Seek(0); + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*(keys.begin()), iter.key()); + + iter.SeekToFirst(); + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*(keys.begin()), iter.key()); + + iter.SeekToLast(); + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*(keys.rbegin()), iter.key()); + } + + // Forward iteration test + for (int i = 0; i < R; i++) { + SkipList::Iterator iter(&list); + iter.Seek(i); + + // Compare against model iterator + std::set::iterator model_iter = keys.lower_bound(i); + for (int j = 0; j < 3; j++) { + if (model_iter == keys.end()) { + ASSERT_TRUE(!iter.Valid()); + break; + } else { + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*model_iter, iter.key()); + ++model_iter; + iter.Next(); + } + } + } + + // Backward iteration test + { + SkipList::Iterator iter(&list); + iter.SeekToLast(); + + // Compare against model iterator + for (std::set::reverse_iterator model_iter = keys.rbegin(); + model_iter != keys.rend(); + ++model_iter) { + ASSERT_TRUE(iter.Valid()); + ASSERT_EQ(*model_iter, iter.key()); + iter.Prev(); + } + ASSERT_TRUE(!iter.Valid()); + } +} + +// We want to make sure that with a single writer and multiple +// concurrent readers (with no synchronization other than when a +// reader's iterator is created), the reader always observes all the +// data that was present in the skip list when the iterator was +// constructor. Because insertions are happening concurrently, we may +// also observe new values that were inserted since the iterator was +// constructed, but we should never miss any values that were present +// at iterator construction time. +// +// We generate multi-part keys: +// +// where: +// key is in range [0..K-1] +// gen is a generation number for key +// hash is hash(key,gen) +// +// The insertion code picks a random key, sets gen to be 1 + the last +// generation number inserted for that key, and sets hash to Hash(key,gen). +// +// At the beginning of a read, we snapshot the last inserted +// generation number for each key. We then iterate, including random +// calls to Next() and Seek(). For every key we encounter, we +// check that it is either expected given the initial snapshot or has +// been concurrently added since the iterator started. +class ConcurrentTest { + private: + static const uint32_t K = 4; + + static uint64_t key(Key key) { return (key >> 40); } + static uint64_t gen(Key key) { return (key >> 8) & 0xffffffffu; } + static uint64_t hash(Key key) { return key & 0xff; } + + static uint64_t HashNumbers(uint64_t k, uint64_t g) { + uint64_t data[2] = { k, g }; + return Hash(reinterpret_cast(data), sizeof(data), 0); + } + + static Key MakeKey(uint64_t k, uint64_t g) { + assert(sizeof(Key) == sizeof(uint64_t)); + assert(k <= K); // We sometimes pass K to seek to the end of the skiplist + assert(g <= 0xffffffffu); + return ((k << 40) | (g << 8) | (HashNumbers(k, g) & 0xff)); + } + + static bool IsValidKey(Key k) { + return hash(k) == (HashNumbers(key(k), gen(k)) & 0xff); + } + + static Key RandomTarget(Random* rnd) { + switch (rnd->Next() % 10) { + case 0: + // Seek to beginning + return MakeKey(0, 0); + case 1: + // Seek to end + return MakeKey(K, 0); + default: + // Seek to middle + return MakeKey(rnd->Next() % K, 0); + } + } + + // Per-key generation + struct State { + port::AtomicPointer generation[K]; + void Set(int k, intptr_t v) { + generation[k].Release_Store(reinterpret_cast(v)); + } + intptr_t Get(int k) { + return reinterpret_cast(generation[k].Acquire_Load()); + } + + State() { + for (int k = 0; k < K; k++) { + Set(k, 0); + } + } + }; + + // Current state of the test + State current_; + + Arena arena_; + + // SkipList is not protected by mu_. We just use a single writer + // thread to modify it. + SkipList list_; + + public: + ConcurrentTest() : list_(Comparator(), &arena_) { } + + // REQUIRES: External synchronization + void WriteStep(Random* rnd) { + const uint32_t k = rnd->Next() % K; + const intptr_t g = current_.Get(k) + 1; + const Key key = MakeKey(k, g); + list_.Insert(key); + current_.Set(k, g); + } + + void ReadStep(Random* rnd) { + // Remember the initial committed state of the skiplist. + State initial_state; + for (int k = 0; k < K; k++) { + initial_state.Set(k, current_.Get(k)); + } + + Key pos = RandomTarget(rnd); + SkipList::Iterator iter(&list_); + iter.Seek(pos); + while (true) { + Key current; + if (!iter.Valid()) { + current = MakeKey(K, 0); + } else { + current = iter.key(); + ASSERT_TRUE(IsValidKey(current)) << current; + } + ASSERT_LE(pos, current) << "should not go backwards"; + + // Verify that everything in [pos,current) was not present in + // initial_state. + while (pos < current) { + ASSERT_LT(key(pos), K) << pos; + + // Note that generation 0 is never inserted, so it is ok if + // <*,0,*> is missing. + ASSERT_TRUE((gen(pos) == 0) || + (gen(pos) > initial_state.Get(key(pos))) + ) << "key: " << key(pos) + << "; gen: " << gen(pos) + << "; initgen: " + << initial_state.Get(key(pos)); + + // Advance to next key in the valid key space + if (key(pos) < key(current)) { + pos = MakeKey(key(pos) + 1, 0); + } else { + pos = MakeKey(key(pos), gen(pos) + 1); + } + } + + if (!iter.Valid()) { + break; + } + + if (rnd->Next() % 2) { + iter.Next(); + pos = MakeKey(key(pos), gen(pos) + 1); + } else { + Key new_target = RandomTarget(rnd); + if (new_target > pos) { + pos = new_target; + iter.Seek(new_target); + } + } + } + } +}; +const uint32_t ConcurrentTest::K; + +// Simple test that does single-threaded testing of the ConcurrentTest +// scaffolding. +TEST(SkipTest, ConcurrentWithoutThreads) { + ConcurrentTest test; + Random rnd(test::RandomSeed()); + for (int i = 0; i < 10000; i++) { + test.ReadStep(&rnd); + test.WriteStep(&rnd); + } +} + +class TestState { + public: + ConcurrentTest t_; + int seed_; + port::AtomicPointer quit_flag_; + + enum ReaderState { + STARTING, + RUNNING, + DONE + }; + + explicit TestState(int s) + : seed_(s), + quit_flag_(NULL), + state_(STARTING), + state_cv_(&mu_) {} + + void Wait(ReaderState s) { + mu_.Lock(); + while (state_ != s) { + state_cv_.Wait(); + } + mu_.Unlock(); + } + + void Change(ReaderState s) { + mu_.Lock(); + state_ = s; + state_cv_.Signal(); + mu_.Unlock(); + } + + private: + port::Mutex mu_; + ReaderState state_; + port::CondVar state_cv_; +}; + +static void ConcurrentReader(void* arg) { + TestState* state = reinterpret_cast(arg); + Random rnd(state->seed_); + int64_t reads = 0; + state->Change(TestState::RUNNING); + while (!state->quit_flag_.Acquire_Load()) { + state->t_.ReadStep(&rnd); + ++reads; + } + state->Change(TestState::DONE); +} + +static void RunConcurrent(int run) { + const int seed = test::RandomSeed() + (run * 100); + Random rnd(seed); + const int N = 1000; + const int kSize = 1000; + for (int i = 0; i < N; i++) { + if ((i % 100) == 0) { + fprintf(stderr, "Run %d of %d\n", i, N); + } + TestState state(seed + 1); + Env::Default()->Schedule(ConcurrentReader, &state); + state.Wait(TestState::RUNNING); + for (int i = 0; i < kSize; i++) { + state.t_.WriteStep(&rnd); + } + state.quit_flag_.Release_Store(&state); // Any non-NULL arg will do + state.Wait(TestState::DONE); + } +} + +TEST(SkipTest, Concurrent1) { RunConcurrent(1); } +TEST(SkipTest, Concurrent2) { RunConcurrent(2); } +TEST(SkipTest, Concurrent3) { RunConcurrent(3); } +TEST(SkipTest, Concurrent4) { RunConcurrent(4); } +TEST(SkipTest, Concurrent5) { RunConcurrent(5); } + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/snapshot.h b/src/leveldb/db/snapshot.h new file mode 100644 index 0000000..e7f8fd2 --- /dev/null +++ b/src/leveldb/db/snapshot.h @@ -0,0 +1,66 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_SNAPSHOT_H_ +#define STORAGE_LEVELDB_DB_SNAPSHOT_H_ + +#include "leveldb/db.h" + +namespace leveldb { + +class SnapshotList; + +// Snapshots are kept in a doubly-linked list in the DB. +// Each SnapshotImpl corresponds to a particular sequence number. +class SnapshotImpl : public Snapshot { + public: + SequenceNumber number_; // const after creation + + private: + friend class SnapshotList; + + // SnapshotImpl is kept in a doubly-linked circular list + SnapshotImpl* prev_; + SnapshotImpl* next_; + + SnapshotList* list_; // just for sanity checks +}; + +class SnapshotList { + public: + SnapshotList() { + list_.prev_ = &list_; + list_.next_ = &list_; + } + + bool empty() const { return list_.next_ == &list_; } + SnapshotImpl* oldest() const { assert(!empty()); return list_.next_; } + SnapshotImpl* newest() const { assert(!empty()); return list_.prev_; } + + const SnapshotImpl* New(SequenceNumber seq) { + SnapshotImpl* s = new SnapshotImpl; + s->number_ = seq; + s->list_ = this; + s->next_ = &list_; + s->prev_ = list_.prev_; + s->prev_->next_ = s; + s->next_->prev_ = s; + return s; + } + + void Delete(const SnapshotImpl* s) { + assert(s->list_ == this); + s->prev_->next_ = s->next_; + s->next_->prev_ = s->prev_; + delete s; + } + + private: + // Dummy head of doubly-linked list of snapshots + SnapshotImpl list_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_SNAPSHOT_H_ diff --git a/src/leveldb/db/table_cache.cc b/src/leveldb/db/table_cache.cc new file mode 100644 index 0000000..497db27 --- /dev/null +++ b/src/leveldb/db/table_cache.cc @@ -0,0 +1,121 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/table_cache.h" + +#include "db/filename.h" +#include "leveldb/env.h" +#include "leveldb/table.h" +#include "util/coding.h" + +namespace leveldb { + +struct TableAndFile { + RandomAccessFile* file; + Table* table; +}; + +static void DeleteEntry(const Slice& key, void* value) { + TableAndFile* tf = reinterpret_cast(value); + delete tf->table; + delete tf->file; + delete tf; +} + +static void UnrefEntry(void* arg1, void* arg2) { + Cache* cache = reinterpret_cast(arg1); + Cache::Handle* h = reinterpret_cast(arg2); + cache->Release(h); +} + +TableCache::TableCache(const std::string& dbname, + const Options* options, + int entries) + : env_(options->env), + dbname_(dbname), + options_(options), + cache_(NewLRUCache(entries)) { +} + +TableCache::~TableCache() { + delete cache_; +} + +Status TableCache::FindTable(uint64_t file_number, uint64_t file_size, + Cache::Handle** handle) { + Status s; + char buf[sizeof(file_number)]; + EncodeFixed64(buf, file_number); + Slice key(buf, sizeof(buf)); + *handle = cache_->Lookup(key); + if (*handle == NULL) { + std::string fname = TableFileName(dbname_, file_number); + RandomAccessFile* file = NULL; + Table* table = NULL; + s = env_->NewRandomAccessFile(fname, &file); + if (s.ok()) { + s = Table::Open(*options_, file, file_size, &table); + } + + if (!s.ok()) { + assert(table == NULL); + delete file; + // We do not cache error results so that if the error is transient, + // or somebody repairs the file, we recover automatically. + } else { + TableAndFile* tf = new TableAndFile; + tf->file = file; + tf->table = table; + *handle = cache_->Insert(key, tf, 1, &DeleteEntry); + } + } + return s; +} + +Iterator* TableCache::NewIterator(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + Table** tableptr) { + if (tableptr != NULL) { + *tableptr = NULL; + } + + Cache::Handle* handle = NULL; + Status s = FindTable(file_number, file_size, &handle); + if (!s.ok()) { + return NewErrorIterator(s); + } + + Table* table = reinterpret_cast(cache_->Value(handle))->table; + Iterator* result = table->NewIterator(options); + result->RegisterCleanup(&UnrefEntry, cache_, handle); + if (tableptr != NULL) { + *tableptr = table; + } + return result; +} + +Status TableCache::Get(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + const Slice& k, + void* arg, + void (*saver)(void*, const Slice&, const Slice&)) { + Cache::Handle* handle = NULL; + Status s = FindTable(file_number, file_size, &handle); + if (s.ok()) { + Table* t = reinterpret_cast(cache_->Value(handle))->table; + s = t->InternalGet(options, k, arg, saver); + cache_->Release(handle); + } + return s; +} + +void TableCache::Evict(uint64_t file_number) { + char buf[sizeof(file_number)]; + EncodeFixed64(buf, file_number); + cache_->Erase(Slice(buf, sizeof(buf))); +} + +} // namespace leveldb diff --git a/src/leveldb/db/table_cache.h b/src/leveldb/db/table_cache.h new file mode 100644 index 0000000..8cf4aaf --- /dev/null +++ b/src/leveldb/db/table_cache.h @@ -0,0 +1,61 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Thread-safe (provides internal synchronization) + +#ifndef STORAGE_LEVELDB_DB_TABLE_CACHE_H_ +#define STORAGE_LEVELDB_DB_TABLE_CACHE_H_ + +#include +#include +#include "db/dbformat.h" +#include "leveldb/cache.h" +#include "leveldb/table.h" +#include "port/port.h" + +namespace leveldb { + +class Env; + +class TableCache { + public: + TableCache(const std::string& dbname, const Options* options, int entries); + ~TableCache(); + + // Return an iterator for the specified file number (the corresponding + // file length must be exactly "file_size" bytes). If "tableptr" is + // non-NULL, also sets "*tableptr" to point to the Table object + // underlying the returned iterator, or NULL if no Table object underlies + // the returned iterator. The returned "*tableptr" object is owned by + // the cache and should not be deleted, and is valid for as long as the + // returned iterator is live. + Iterator* NewIterator(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + Table** tableptr = NULL); + + // If a seek to internal key "k" in specified file finds an entry, + // call (*handle_result)(arg, found_key, found_value). + Status Get(const ReadOptions& options, + uint64_t file_number, + uint64_t file_size, + const Slice& k, + void* arg, + void (*handle_result)(void*, const Slice&, const Slice&)); + + // Evict any entry for the specified file number + void Evict(uint64_t file_number); + + private: + Env* const env_; + const std::string dbname_; + const Options* options_; + Cache* cache_; + + Status FindTable(uint64_t file_number, uint64_t file_size, Cache::Handle**); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_TABLE_CACHE_H_ diff --git a/src/leveldb/db/version_edit.cc b/src/leveldb/db/version_edit.cc new file mode 100644 index 0000000..f10a2d5 --- /dev/null +++ b/src/leveldb/db/version_edit.cc @@ -0,0 +1,266 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/version_edit.h" + +#include "db/version_set.h" +#include "util/coding.h" + +namespace leveldb { + +// Tag numbers for serialized VersionEdit. These numbers are written to +// disk and should not be changed. +enum Tag { + kComparator = 1, + kLogNumber = 2, + kNextFileNumber = 3, + kLastSequence = 4, + kCompactPointer = 5, + kDeletedFile = 6, + kNewFile = 7, + // 8 was used for large value refs + kPrevLogNumber = 9 +}; + +void VersionEdit::Clear() { + comparator_.clear(); + log_number_ = 0; + prev_log_number_ = 0; + last_sequence_ = 0; + next_file_number_ = 0; + has_comparator_ = false; + has_log_number_ = false; + has_prev_log_number_ = false; + has_next_file_number_ = false; + has_last_sequence_ = false; + deleted_files_.clear(); + new_files_.clear(); +} + +void VersionEdit::EncodeTo(std::string* dst) const { + if (has_comparator_) { + PutVarint32(dst, kComparator); + PutLengthPrefixedSlice(dst, comparator_); + } + if (has_log_number_) { + PutVarint32(dst, kLogNumber); + PutVarint64(dst, log_number_); + } + if (has_prev_log_number_) { + PutVarint32(dst, kPrevLogNumber); + PutVarint64(dst, prev_log_number_); + } + if (has_next_file_number_) { + PutVarint32(dst, kNextFileNumber); + PutVarint64(dst, next_file_number_); + } + if (has_last_sequence_) { + PutVarint32(dst, kLastSequence); + PutVarint64(dst, last_sequence_); + } + + for (size_t i = 0; i < compact_pointers_.size(); i++) { + PutVarint32(dst, kCompactPointer); + PutVarint32(dst, compact_pointers_[i].first); // level + PutLengthPrefixedSlice(dst, compact_pointers_[i].second.Encode()); + } + + for (DeletedFileSet::const_iterator iter = deleted_files_.begin(); + iter != deleted_files_.end(); + ++iter) { + PutVarint32(dst, kDeletedFile); + PutVarint32(dst, iter->first); // level + PutVarint64(dst, iter->second); // file number + } + + for (size_t i = 0; i < new_files_.size(); i++) { + const FileMetaData& f = new_files_[i].second; + PutVarint32(dst, kNewFile); + PutVarint32(dst, new_files_[i].first); // level + PutVarint64(dst, f.number); + PutVarint64(dst, f.file_size); + PutLengthPrefixedSlice(dst, f.smallest.Encode()); + PutLengthPrefixedSlice(dst, f.largest.Encode()); + } +} + +static bool GetInternalKey(Slice* input, InternalKey* dst) { + Slice str; + if (GetLengthPrefixedSlice(input, &str)) { + dst->DecodeFrom(str); + return true; + } else { + return false; + } +} + +static bool GetLevel(Slice* input, int* level) { + uint32_t v; + if (GetVarint32(input, &v) && + v < config::kNumLevels) { + *level = v; + return true; + } else { + return false; + } +} + +Status VersionEdit::DecodeFrom(const Slice& src) { + Clear(); + Slice input = src; + const char* msg = NULL; + uint32_t tag; + + // Temporary storage for parsing + int level; + uint64_t number; + FileMetaData f; + Slice str; + InternalKey key; + + while (msg == NULL && GetVarint32(&input, &tag)) { + switch (tag) { + case kComparator: + if (GetLengthPrefixedSlice(&input, &str)) { + comparator_ = str.ToString(); + has_comparator_ = true; + } else { + msg = "comparator name"; + } + break; + + case kLogNumber: + if (GetVarint64(&input, &log_number_)) { + has_log_number_ = true; + } else { + msg = "log number"; + } + break; + + case kPrevLogNumber: + if (GetVarint64(&input, &prev_log_number_)) { + has_prev_log_number_ = true; + } else { + msg = "previous log number"; + } + break; + + case kNextFileNumber: + if (GetVarint64(&input, &next_file_number_)) { + has_next_file_number_ = true; + } else { + msg = "next file number"; + } + break; + + case kLastSequence: + if (GetVarint64(&input, &last_sequence_)) { + has_last_sequence_ = true; + } else { + msg = "last sequence number"; + } + break; + + case kCompactPointer: + if (GetLevel(&input, &level) && + GetInternalKey(&input, &key)) { + compact_pointers_.push_back(std::make_pair(level, key)); + } else { + msg = "compaction pointer"; + } + break; + + case kDeletedFile: + if (GetLevel(&input, &level) && + GetVarint64(&input, &number)) { + deleted_files_.insert(std::make_pair(level, number)); + } else { + msg = "deleted file"; + } + break; + + case kNewFile: + if (GetLevel(&input, &level) && + GetVarint64(&input, &f.number) && + GetVarint64(&input, &f.file_size) && + GetInternalKey(&input, &f.smallest) && + GetInternalKey(&input, &f.largest)) { + new_files_.push_back(std::make_pair(level, f)); + } else { + msg = "new-file entry"; + } + break; + + default: + msg = "unknown tag"; + break; + } + } + + if (msg == NULL && !input.empty()) { + msg = "invalid tag"; + } + + Status result; + if (msg != NULL) { + result = Status::Corruption("VersionEdit", msg); + } + return result; +} + +std::string VersionEdit::DebugString() const { + std::string r; + r.append("VersionEdit {"); + if (has_comparator_) { + r.append("\n Comparator: "); + r.append(comparator_); + } + if (has_log_number_) { + r.append("\n LogNumber: "); + AppendNumberTo(&r, log_number_); + } + if (has_prev_log_number_) { + r.append("\n PrevLogNumber: "); + AppendNumberTo(&r, prev_log_number_); + } + if (has_next_file_number_) { + r.append("\n NextFile: "); + AppendNumberTo(&r, next_file_number_); + } + if (has_last_sequence_) { + r.append("\n LastSeq: "); + AppendNumberTo(&r, last_sequence_); + } + for (size_t i = 0; i < compact_pointers_.size(); i++) { + r.append("\n CompactPointer: "); + AppendNumberTo(&r, compact_pointers_[i].first); + r.append(" "); + r.append(compact_pointers_[i].second.DebugString()); + } + for (DeletedFileSet::const_iterator iter = deleted_files_.begin(); + iter != deleted_files_.end(); + ++iter) { + r.append("\n DeleteFile: "); + AppendNumberTo(&r, iter->first); + r.append(" "); + AppendNumberTo(&r, iter->second); + } + for (size_t i = 0; i < new_files_.size(); i++) { + const FileMetaData& f = new_files_[i].second; + r.append("\n AddFile: "); + AppendNumberTo(&r, new_files_[i].first); + r.append(" "); + AppendNumberTo(&r, f.number); + r.append(" "); + AppendNumberTo(&r, f.file_size); + r.append(" "); + r.append(f.smallest.DebugString()); + r.append(" .. "); + r.append(f.largest.DebugString()); + } + r.append("\n}\n"); + return r; +} + +} // namespace leveldb diff --git a/src/leveldb/db/version_edit.h b/src/leveldb/db/version_edit.h new file mode 100644 index 0000000..eaef77b --- /dev/null +++ b/src/leveldb/db/version_edit.h @@ -0,0 +1,107 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_VERSION_EDIT_H_ +#define STORAGE_LEVELDB_DB_VERSION_EDIT_H_ + +#include +#include +#include +#include "db/dbformat.h" + +namespace leveldb { + +class VersionSet; + +struct FileMetaData { + int refs; + int allowed_seeks; // Seeks allowed until compaction + uint64_t number; + uint64_t file_size; // File size in bytes + InternalKey smallest; // Smallest internal key served by table + InternalKey largest; // Largest internal key served by table + + FileMetaData() : refs(0), allowed_seeks(1 << 30), file_size(0) { } +}; + +class VersionEdit { + public: + VersionEdit() { Clear(); } + ~VersionEdit() { } + + void Clear(); + + void SetComparatorName(const Slice& name) { + has_comparator_ = true; + comparator_ = name.ToString(); + } + void SetLogNumber(uint64_t num) { + has_log_number_ = true; + log_number_ = num; + } + void SetPrevLogNumber(uint64_t num) { + has_prev_log_number_ = true; + prev_log_number_ = num; + } + void SetNextFile(uint64_t num) { + has_next_file_number_ = true; + next_file_number_ = num; + } + void SetLastSequence(SequenceNumber seq) { + has_last_sequence_ = true; + last_sequence_ = seq; + } + void SetCompactPointer(int level, const InternalKey& key) { + compact_pointers_.push_back(std::make_pair(level, key)); + } + + // Add the specified file at the specified number. + // REQUIRES: This version has not been saved (see VersionSet::SaveTo) + // REQUIRES: "smallest" and "largest" are smallest and largest keys in file + void AddFile(int level, uint64_t file, + uint64_t file_size, + const InternalKey& smallest, + const InternalKey& largest) { + FileMetaData f; + f.number = file; + f.file_size = file_size; + f.smallest = smallest; + f.largest = largest; + new_files_.push_back(std::make_pair(level, f)); + } + + // Delete the specified "file" from the specified "level". + void DeleteFile(int level, uint64_t file) { + deleted_files_.insert(std::make_pair(level, file)); + } + + void EncodeTo(std::string* dst) const; + Status DecodeFrom(const Slice& src); + + std::string DebugString() const; + + private: + friend class VersionSet; + + typedef std::set< std::pair > DeletedFileSet; + + std::string comparator_; + uint64_t log_number_; + uint64_t prev_log_number_; + uint64_t next_file_number_; + SequenceNumber last_sequence_; + bool has_comparator_; + bool has_log_number_; + bool has_prev_log_number_; + bool has_next_file_number_; + bool has_last_sequence_; + + std::vector< std::pair > compact_pointers_; + DeletedFileSet deleted_files_; + std::vector< std::pair > new_files_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_VERSION_EDIT_H_ diff --git a/src/leveldb/db/version_edit_test.cc b/src/leveldb/db/version_edit_test.cc new file mode 100644 index 0000000..280310b --- /dev/null +++ b/src/leveldb/db/version_edit_test.cc @@ -0,0 +1,46 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/version_edit.h" +#include "util/testharness.h" + +namespace leveldb { + +static void TestEncodeDecode(const VersionEdit& edit) { + std::string encoded, encoded2; + edit.EncodeTo(&encoded); + VersionEdit parsed; + Status s = parsed.DecodeFrom(encoded); + ASSERT_TRUE(s.ok()) << s.ToString(); + parsed.EncodeTo(&encoded2); + ASSERT_EQ(encoded, encoded2); +} + +class VersionEditTest { }; + +TEST(VersionEditTest, EncodeDecode) { + static const uint64_t kBig = 1ull << 50; + + VersionEdit edit; + for (int i = 0; i < 4; i++) { + TestEncodeDecode(edit); + edit.AddFile(3, kBig + 300 + i, kBig + 400 + i, + InternalKey("foo", kBig + 500 + i, kTypeValue), + InternalKey("zoo", kBig + 600 + i, kTypeDeletion)); + edit.DeleteFile(4, kBig + 700 + i); + edit.SetCompactPointer(i, InternalKey("x", kBig + 900 + i, kTypeValue)); + } + + edit.SetComparatorName("foo"); + edit.SetLogNumber(kBig + 100); + edit.SetNextFile(kBig + 200); + edit.SetLastSequence(kBig + 1000); + TestEncodeDecode(edit); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/version_set.cc b/src/leveldb/db/version_set.cc new file mode 100644 index 0000000..7d0a5de --- /dev/null +++ b/src/leveldb/db/version_set.cc @@ -0,0 +1,1438 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/version_set.h" + +#include +#include +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "db/memtable.h" +#include "db/table_cache.h" +#include "leveldb/env.h" +#include "leveldb/table_builder.h" +#include "table/merger.h" +#include "table/two_level_iterator.h" +#include "util/coding.h" +#include "util/logging.h" + +namespace leveldb { + +static const int kTargetFileSize = 2 * 1048576; + +// Maximum bytes of overlaps in grandparent (i.e., level+2) before we +// stop building a single file in a level->level+1 compaction. +static const int64_t kMaxGrandParentOverlapBytes = 10 * kTargetFileSize; + +// Maximum number of bytes in all compacted files. We avoid expanding +// the lower level file set of a compaction if it would make the +// total compaction cover more than this many bytes. +static const int64_t kExpandedCompactionByteSizeLimit = 25 * kTargetFileSize; + +static double MaxBytesForLevel(int level) { + // Note: the result for level zero is not really used since we set + // the level-0 compaction threshold based on number of files. + double result = 10 * 1048576.0; // Result for both level-0 and level-1 + while (level > 1) { + result *= 10; + level--; + } + return result; +} + +static uint64_t MaxFileSizeForLevel(int level) { + return kTargetFileSize; // We could vary per level to reduce number of files? +} + +static int64_t TotalFileSize(const std::vector& files) { + int64_t sum = 0; + for (size_t i = 0; i < files.size(); i++) { + sum += files[i]->file_size; + } + return sum; +} + +namespace { +std::string IntSetToString(const std::set& s) { + std::string result = "{"; + for (std::set::const_iterator it = s.begin(); + it != s.end(); + ++it) { + result += (result.size() > 1) ? "," : ""; + result += NumberToString(*it); + } + result += "}"; + return result; +} +} // namespace + +Version::~Version() { + assert(refs_ == 0); + + // Remove from linked list + prev_->next_ = next_; + next_->prev_ = prev_; + + // Drop references to files + for (int level = 0; level < config::kNumLevels; level++) { + for (size_t i = 0; i < files_[level].size(); i++) { + FileMetaData* f = files_[level][i]; + assert(f->refs > 0); + f->refs--; + if (f->refs <= 0) { + delete f; + } + } + } +} + +int FindFile(const InternalKeyComparator& icmp, + const std::vector& files, + const Slice& key) { + uint32_t left = 0; + uint32_t right = files.size(); + while (left < right) { + uint32_t mid = (left + right) / 2; + const FileMetaData* f = files[mid]; + if (icmp.InternalKeyComparator::Compare(f->largest.Encode(), key) < 0) { + // Key at "mid.largest" is < "target". Therefore all + // files at or before "mid" are uninteresting. + left = mid + 1; + } else { + // Key at "mid.largest" is >= "target". Therefore all files + // after "mid" are uninteresting. + right = mid; + } + } + return right; +} + +static bool AfterFile(const Comparator* ucmp, + const Slice* user_key, const FileMetaData* f) { + // NULL user_key occurs before all keys and is therefore never after *f + return (user_key != NULL && + ucmp->Compare(*user_key, f->largest.user_key()) > 0); +} + +static bool BeforeFile(const Comparator* ucmp, + const Slice* user_key, const FileMetaData* f) { + // NULL user_key occurs after all keys and is therefore never before *f + return (user_key != NULL && + ucmp->Compare(*user_key, f->smallest.user_key()) < 0); +} + +bool SomeFileOverlapsRange( + const InternalKeyComparator& icmp, + bool disjoint_sorted_files, + const std::vector& files, + const Slice* smallest_user_key, + const Slice* largest_user_key) { + const Comparator* ucmp = icmp.user_comparator(); + if (!disjoint_sorted_files) { + // Need to check against all files + for (size_t i = 0; i < files.size(); i++) { + const FileMetaData* f = files[i]; + if (AfterFile(ucmp, smallest_user_key, f) || + BeforeFile(ucmp, largest_user_key, f)) { + // No overlap + } else { + return true; // Overlap + } + } + return false; + } + + // Binary search over file list + uint32_t index = 0; + if (smallest_user_key != NULL) { + // Find the earliest possible internal key for smallest_user_key + InternalKey small(*smallest_user_key, kMaxSequenceNumber,kValueTypeForSeek); + index = FindFile(icmp, files, small.Encode()); + } + + if (index >= files.size()) { + // beginning of range is after all files, so no overlap. + return false; + } + + return !BeforeFile(ucmp, largest_user_key, files[index]); +} + +// An internal iterator. For a given version/level pair, yields +// information about the files in the level. For a given entry, key() +// is the largest key that occurs in the file, and value() is an +// 16-byte value containing the file number and file size, both +// encoded using EncodeFixed64. +class Version::LevelFileNumIterator : public Iterator { + public: + LevelFileNumIterator(const InternalKeyComparator& icmp, + const std::vector* flist) + : icmp_(icmp), + flist_(flist), + index_(flist->size()) { // Marks as invalid + } + virtual bool Valid() const { + return index_ < flist_->size(); + } + virtual void Seek(const Slice& target) { + index_ = FindFile(icmp_, *flist_, target); + } + virtual void SeekToFirst() { index_ = 0; } + virtual void SeekToLast() { + index_ = flist_->empty() ? 0 : flist_->size() - 1; + } + virtual void Next() { + assert(Valid()); + index_++; + } + virtual void Prev() { + assert(Valid()); + if (index_ == 0) { + index_ = flist_->size(); // Marks as invalid + } else { + index_--; + } + } + Slice key() const { + assert(Valid()); + return (*flist_)[index_]->largest.Encode(); + } + Slice value() const { + assert(Valid()); + EncodeFixed64(value_buf_, (*flist_)[index_]->number); + EncodeFixed64(value_buf_+8, (*flist_)[index_]->file_size); + return Slice(value_buf_, sizeof(value_buf_)); + } + virtual Status status() const { return Status::OK(); } + private: + const InternalKeyComparator icmp_; + const std::vector* const flist_; + uint32_t index_; + + // Backing store for value(). Holds the file number and size. + mutable char value_buf_[16]; +}; + +static Iterator* GetFileIterator(void* arg, + const ReadOptions& options, + const Slice& file_value) { + TableCache* cache = reinterpret_cast(arg); + if (file_value.size() != 16) { + return NewErrorIterator( + Status::Corruption("FileReader invoked with unexpected value")); + } else { + return cache->NewIterator(options, + DecodeFixed64(file_value.data()), + DecodeFixed64(file_value.data() + 8)); + } +} + +Iterator* Version::NewConcatenatingIterator(const ReadOptions& options, + int level) const { + return NewTwoLevelIterator( + new LevelFileNumIterator(vset_->icmp_, &files_[level]), + &GetFileIterator, vset_->table_cache_, options); +} + +void Version::AddIterators(const ReadOptions& options, + std::vector* iters) { + // Merge all level zero files together since they may overlap + for (size_t i = 0; i < files_[0].size(); i++) { + iters->push_back( + vset_->table_cache_->NewIterator( + options, files_[0][i]->number, files_[0][i]->file_size)); + } + + // For levels > 0, we can use a concatenating iterator that sequentially + // walks through the non-overlapping files in the level, opening them + // lazily. + for (int level = 1; level < config::kNumLevels; level++) { + if (!files_[level].empty()) { + iters->push_back(NewConcatenatingIterator(options, level)); + } + } +} + +// Callback from TableCache::Get() +namespace { +enum SaverState { + kNotFound, + kFound, + kDeleted, + kCorrupt, +}; +struct Saver { + SaverState state; + const Comparator* ucmp; + Slice user_key; + std::string* value; +}; +} +static void SaveValue(void* arg, const Slice& ikey, const Slice& v) { + Saver* s = reinterpret_cast(arg); + ParsedInternalKey parsed_key; + if (!ParseInternalKey(ikey, &parsed_key)) { + s->state = kCorrupt; + } else { + if (s->ucmp->Compare(parsed_key.user_key, s->user_key) == 0) { + s->state = (parsed_key.type == kTypeValue) ? kFound : kDeleted; + if (s->state == kFound) { + s->value->assign(v.data(), v.size()); + } + } + } +} + +static bool NewestFirst(FileMetaData* a, FileMetaData* b) { + return a->number > b->number; +} + +Status Version::Get(const ReadOptions& options, + const LookupKey& k, + std::string* value, + GetStats* stats) { + Slice ikey = k.internal_key(); + Slice user_key = k.user_key(); + const Comparator* ucmp = vset_->icmp_.user_comparator(); + Status s; + + stats->seek_file = NULL; + stats->seek_file_level = -1; + FileMetaData* last_file_read = NULL; + int last_file_read_level = -1; + + // We can search level-by-level since entries never hop across + // levels. Therefore we are guaranteed that if we find data + // in an smaller level, later levels are irrelevant. + std::vector tmp; + FileMetaData* tmp2; + for (int level = 0; level < config::kNumLevels; level++) { + size_t num_files = files_[level].size(); + if (num_files == 0) continue; + + // Get the list of files to search in this level + FileMetaData* const* files = &files_[level][0]; + if (level == 0) { + // Level-0 files may overlap each other. Find all files that + // overlap user_key and process them in order from newest to oldest. + tmp.reserve(num_files); + for (uint32_t i = 0; i < num_files; i++) { + FileMetaData* f = files[i]; + if (ucmp->Compare(user_key, f->smallest.user_key()) >= 0 && + ucmp->Compare(user_key, f->largest.user_key()) <= 0) { + tmp.push_back(f); + } + } + if (tmp.empty()) continue; + + std::sort(tmp.begin(), tmp.end(), NewestFirst); + files = &tmp[0]; + num_files = tmp.size(); + } else { + // Binary search to find earliest index whose largest key >= ikey. + uint32_t index = FindFile(vset_->icmp_, files_[level], ikey); + if (index >= num_files) { + files = NULL; + num_files = 0; + } else { + tmp2 = files[index]; + if (ucmp->Compare(user_key, tmp2->smallest.user_key()) < 0) { + // All of "tmp2" is past any data for user_key + files = NULL; + num_files = 0; + } else { + files = &tmp2; + num_files = 1; + } + } + } + + for (uint32_t i = 0; i < num_files; ++i) { + if (last_file_read != NULL && stats->seek_file == NULL) { + // We have had more than one seek for this read. Charge the 1st file. + stats->seek_file = last_file_read; + stats->seek_file_level = last_file_read_level; + } + + FileMetaData* f = files[i]; + last_file_read = f; + last_file_read_level = level; + + Saver saver; + saver.state = kNotFound; + saver.ucmp = ucmp; + saver.user_key = user_key; + saver.value = value; + s = vset_->table_cache_->Get(options, f->number, f->file_size, + ikey, &saver, SaveValue); + if (!s.ok()) { + return s; + } + switch (saver.state) { + case kNotFound: + break; // Keep searching in other files + case kFound: + return s; + case kDeleted: + s = Status::NotFound(Slice()); // Use empty error message for speed + return s; + case kCorrupt: + s = Status::Corruption("corrupted key for ", user_key); + return s; + } + } + } + + return Status::NotFound(Slice()); // Use an empty error message for speed +} + +bool Version::UpdateStats(const GetStats& stats) { + FileMetaData* f = stats.seek_file; + if (f != NULL) { + f->allowed_seeks--; + if (f->allowed_seeks <= 0 && file_to_compact_ == NULL) { + file_to_compact_ = f; + file_to_compact_level_ = stats.seek_file_level; + return true; + } + } + return false; +} + +void Version::Ref() { + ++refs_; +} + +void Version::Unref() { + assert(this != &vset_->dummy_versions_); + assert(refs_ >= 1); + --refs_; + if (refs_ == 0) { + delete this; + } +} + +bool Version::OverlapInLevel(int level, + const Slice* smallest_user_key, + const Slice* largest_user_key) { + return SomeFileOverlapsRange(vset_->icmp_, (level > 0), files_[level], + smallest_user_key, largest_user_key); +} + +int Version::PickLevelForMemTableOutput( + const Slice& smallest_user_key, + const Slice& largest_user_key) { + int level = 0; + if (!OverlapInLevel(0, &smallest_user_key, &largest_user_key)) { + // Push to next level if there is no overlap in next level, + // and the #bytes overlapping in the level after that are limited. + InternalKey start(smallest_user_key, kMaxSequenceNumber, kValueTypeForSeek); + InternalKey limit(largest_user_key, 0, static_cast(0)); + std::vector overlaps; + while (level < config::kMaxMemCompactLevel) { + if (OverlapInLevel(level + 1, &smallest_user_key, &largest_user_key)) { + break; + } + GetOverlappingInputs(level + 2, &start, &limit, &overlaps); + const int64_t sum = TotalFileSize(overlaps); + if (sum > kMaxGrandParentOverlapBytes) { + break; + } + level++; + } + } + return level; +} + +// Store in "*inputs" all files in "level" that overlap [begin,end] +void Version::GetOverlappingInputs( + int level, + const InternalKey* begin, + const InternalKey* end, + std::vector* inputs) { + inputs->clear(); + Slice user_begin, user_end; + if (begin != NULL) { + user_begin = begin->user_key(); + } + if (end != NULL) { + user_end = end->user_key(); + } + const Comparator* user_cmp = vset_->icmp_.user_comparator(); + for (size_t i = 0; i < files_[level].size(); ) { + FileMetaData* f = files_[level][i++]; + const Slice file_start = f->smallest.user_key(); + const Slice file_limit = f->largest.user_key(); + if (begin != NULL && user_cmp->Compare(file_limit, user_begin) < 0) { + // "f" is completely before specified range; skip it + } else if (end != NULL && user_cmp->Compare(file_start, user_end) > 0) { + // "f" is completely after specified range; skip it + } else { + inputs->push_back(f); + if (level == 0) { + // Level-0 files may overlap each other. So check if the newly + // added file has expanded the range. If so, restart search. + if (begin != NULL && user_cmp->Compare(file_start, user_begin) < 0) { + user_begin = file_start; + inputs->clear(); + i = 0; + } else if (end != NULL && user_cmp->Compare(file_limit, user_end) > 0) { + user_end = file_limit; + inputs->clear(); + i = 0; + } + } + } + } +} + +std::string Version::DebugString() const { + std::string r; + for (int level = 0; level < config::kNumLevels; level++) { + // E.g., + // --- level 1 --- + // 17:123['a' .. 'd'] + // 20:43['e' .. 'g'] + r.append("--- level "); + AppendNumberTo(&r, level); + r.append(" ---\n"); + const std::vector& files = files_[level]; + for (size_t i = 0; i < files.size(); i++) { + r.push_back(' '); + AppendNumberTo(&r, files[i]->number); + r.push_back(':'); + AppendNumberTo(&r, files[i]->file_size); + r.append("["); + r.append(files[i]->smallest.DebugString()); + r.append(" .. "); + r.append(files[i]->largest.DebugString()); + r.append("]\n"); + } + } + return r; +} + +// A helper class so we can efficiently apply a whole sequence +// of edits to a particular state without creating intermediate +// Versions that contain full copies of the intermediate state. +class VersionSet::Builder { + private: + // Helper to sort by v->files_[file_number].smallest + struct BySmallestKey { + const InternalKeyComparator* internal_comparator; + + bool operator()(FileMetaData* f1, FileMetaData* f2) const { + int r = internal_comparator->Compare(f1->smallest, f2->smallest); + if (r != 0) { + return (r < 0); + } else { + // Break ties by file number + return (f1->number < f2->number); + } + } + }; + + typedef std::set FileSet; + struct LevelState { + std::set deleted_files; + FileSet* added_files; + }; + + VersionSet* vset_; + Version* base_; + LevelState levels_[config::kNumLevels]; + + public: + // Initialize a builder with the files from *base and other info from *vset + Builder(VersionSet* vset, Version* base) + : vset_(vset), + base_(base) { + base_->Ref(); + BySmallestKey cmp; + cmp.internal_comparator = &vset_->icmp_; + for (int level = 0; level < config::kNumLevels; level++) { + levels_[level].added_files = new FileSet(cmp); + } + } + + ~Builder() { + for (int level = 0; level < config::kNumLevels; level++) { + const FileSet* added = levels_[level].added_files; + std::vector to_unref; + to_unref.reserve(added->size()); + for (FileSet::const_iterator it = added->begin(); + it != added->end(); ++it) { + to_unref.push_back(*it); + } + delete added; + for (uint32_t i = 0; i < to_unref.size(); i++) { + FileMetaData* f = to_unref[i]; + f->refs--; + if (f->refs <= 0) { + delete f; + } + } + } + base_->Unref(); + } + + // Apply all of the edits in *edit to the current state. + void Apply(VersionEdit* edit) { + // Update compaction pointers + for (size_t i = 0; i < edit->compact_pointers_.size(); i++) { + const int level = edit->compact_pointers_[i].first; + vset_->compact_pointer_[level] = + edit->compact_pointers_[i].second.Encode().ToString(); + } + + // Delete files + const VersionEdit::DeletedFileSet& del = edit->deleted_files_; + for (VersionEdit::DeletedFileSet::const_iterator iter = del.begin(); + iter != del.end(); + ++iter) { + const int level = iter->first; + const uint64_t number = iter->second; + levels_[level].deleted_files.insert(number); + } + + // Add new files + for (size_t i = 0; i < edit->new_files_.size(); i++) { + const int level = edit->new_files_[i].first; + FileMetaData* f = new FileMetaData(edit->new_files_[i].second); + f->refs = 1; + + // We arrange to automatically compact this file after + // a certain number of seeks. Let's assume: + // (1) One seek costs 10ms + // (2) Writing or reading 1MB costs 10ms (100MB/s) + // (3) A compaction of 1MB does 25MB of IO: + // 1MB read from this level + // 10-12MB read from next level (boundaries may be misaligned) + // 10-12MB written to next level + // This implies that 25 seeks cost the same as the compaction + // of 1MB of data. I.e., one seek costs approximately the + // same as the compaction of 40KB of data. We are a little + // conservative and allow approximately one seek for every 16KB + // of data before triggering a compaction. + f->allowed_seeks = (f->file_size / 16384); + if (f->allowed_seeks < 100) f->allowed_seeks = 100; + + levels_[level].deleted_files.erase(f->number); + levels_[level].added_files->insert(f); + } + } + + // Save the current state in *v. + void SaveTo(Version* v) { + BySmallestKey cmp; + cmp.internal_comparator = &vset_->icmp_; + for (int level = 0; level < config::kNumLevels; level++) { + // Merge the set of added files with the set of pre-existing files. + // Drop any deleted files. Store the result in *v. + const std::vector& base_files = base_->files_[level]; + std::vector::const_iterator base_iter = base_files.begin(); + std::vector::const_iterator base_end = base_files.end(); + const FileSet* added = levels_[level].added_files; + v->files_[level].reserve(base_files.size() + added->size()); + for (FileSet::const_iterator added_iter = added->begin(); + added_iter != added->end(); + ++added_iter) { + // Add all smaller files listed in base_ + for (std::vector::const_iterator bpos + = std::upper_bound(base_iter, base_end, *added_iter, cmp); + base_iter != bpos; + ++base_iter) { + MaybeAddFile(v, level, *base_iter); + } + + MaybeAddFile(v, level, *added_iter); + } + + // Add remaining base files + for (; base_iter != base_end; ++base_iter) { + MaybeAddFile(v, level, *base_iter); + } + +#ifndef NDEBUG + // Make sure there is no overlap in levels > 0 + if (level > 0) { + for (uint32_t i = 1; i < v->files_[level].size(); i++) { + const InternalKey& prev_end = v->files_[level][i-1]->largest; + const InternalKey& this_begin = v->files_[level][i]->smallest; + if (vset_->icmp_.Compare(prev_end, this_begin) >= 0) { + fprintf(stderr, "overlapping ranges in same level %s vs. %s\n", + prev_end.DebugString().c_str(), + this_begin.DebugString().c_str()); + abort(); + } + } + } +#endif + } + } + + void MaybeAddFile(Version* v, int level, FileMetaData* f) { + if (levels_[level].deleted_files.count(f->number) > 0) { + // File is deleted: do nothing + } else { + std::vector* files = &v->files_[level]; + if (level > 0 && !files->empty()) { + // Must not overlap + assert(vset_->icmp_.Compare((*files)[files->size()-1]->largest, + f->smallest) < 0); + } + f->refs++; + files->push_back(f); + } + } +}; + +VersionSet::VersionSet(const std::string& dbname, + const Options* options, + TableCache* table_cache, + const InternalKeyComparator* cmp) + : env_(options->env), + dbname_(dbname), + options_(options), + table_cache_(table_cache), + icmp_(*cmp), + next_file_number_(2), + manifest_file_number_(0), // Filled by Recover() + last_sequence_(0), + log_number_(0), + prev_log_number_(0), + descriptor_file_(NULL), + descriptor_log_(NULL), + dummy_versions_(this), + current_(NULL) { + AppendVersion(new Version(this)); +} + +VersionSet::~VersionSet() { + current_->Unref(); + assert(dummy_versions_.next_ == &dummy_versions_); // List must be empty + delete descriptor_log_; + delete descriptor_file_; +} + +void VersionSet::AppendVersion(Version* v) { + // Make "v" current + assert(v->refs_ == 0); + assert(v != current_); + if (current_ != NULL) { + current_->Unref(); + } + current_ = v; + v->Ref(); + + // Append to linked list + v->prev_ = dummy_versions_.prev_; + v->next_ = &dummy_versions_; + v->prev_->next_ = v; + v->next_->prev_ = v; +} + +Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) { + if (edit->has_log_number_) { + assert(edit->log_number_ >= log_number_); + assert(edit->log_number_ < next_file_number_); + } else { + edit->SetLogNumber(log_number_); + } + + if (!edit->has_prev_log_number_) { + edit->SetPrevLogNumber(prev_log_number_); + } + + edit->SetNextFile(next_file_number_); + edit->SetLastSequence(last_sequence_); + + Version* v = new Version(this); + { + Builder builder(this, current_); + builder.Apply(edit); + builder.SaveTo(v); + } + Finalize(v); + + // Initialize new descriptor log file if necessary by creating + // a temporary file that contains a snapshot of the current version. + std::string new_manifest_file; + Status s; + if (descriptor_log_ == NULL) { + // No reason to unlock *mu here since we only hit this path in the + // first call to LogAndApply (when opening the database). + assert(descriptor_file_ == NULL); + new_manifest_file = DescriptorFileName(dbname_, manifest_file_number_); + edit->SetNextFile(next_file_number_); + s = env_->NewWritableFile(new_manifest_file, &descriptor_file_); + if (s.ok()) { + descriptor_log_ = new log::Writer(descriptor_file_); + s = WriteSnapshot(descriptor_log_); + } + } + + // Unlock during expensive MANIFEST log write + { + mu->Unlock(); + + // Write new record to MANIFEST log + if (s.ok()) { + std::string record; + edit->EncodeTo(&record); + s = descriptor_log_->AddRecord(record); + if (s.ok()) { + s = descriptor_file_->Sync(); + } + if (!s.ok()) { + Log(options_->info_log, "MANIFEST write: %s\n", s.ToString().c_str()); + if (ManifestContains(record)) { + Log(options_->info_log, + "MANIFEST contains log record despite error; advancing to new " + "version to prevent mismatch between in-memory and logged state"); + s = Status::OK(); + } + } + } + + // If we just created a new descriptor file, install it by writing a + // new CURRENT file that points to it. + if (s.ok() && !new_manifest_file.empty()) { + s = SetCurrentFile(env_, dbname_, manifest_file_number_); + // No need to double-check MANIFEST in case of error since it + // will be discarded below. + } + + mu->Lock(); + } + + // Install the new version + if (s.ok()) { + AppendVersion(v); + log_number_ = edit->log_number_; + prev_log_number_ = edit->prev_log_number_; + } else { + delete v; + if (!new_manifest_file.empty()) { + delete descriptor_log_; + delete descriptor_file_; + descriptor_log_ = NULL; + descriptor_file_ = NULL; + env_->DeleteFile(new_manifest_file); + } + } + + return s; +} + +Status VersionSet::Recover() { + struct LogReporter : public log::Reader::Reporter { + Status* status; + virtual void Corruption(size_t bytes, const Status& s) { + if (this->status->ok()) *this->status = s; + } + }; + + // Read "CURRENT" file, which contains a pointer to the current manifest file + std::string current; + Status s = ReadFileToString(env_, CurrentFileName(dbname_), ¤t); + if (!s.ok()) { + return s; + } + if (current.empty() || current[current.size()-1] != '\n') { + return Status::Corruption("CURRENT file does not end with newline"); + } + current.resize(current.size() - 1); + + std::string dscname = dbname_ + "/" + current; + SequentialFile* file; + s = env_->NewSequentialFile(dscname, &file); + if (!s.ok()) { + return s; + } + + bool have_log_number = false; + bool have_prev_log_number = false; + bool have_next_file = false; + bool have_last_sequence = false; + uint64_t next_file = 0; + uint64_t last_sequence = 0; + uint64_t log_number = 0; + uint64_t prev_log_number = 0; + Builder builder(this, current_); + + { + LogReporter reporter; + reporter.status = &s; + log::Reader reader(file, &reporter, true/*checksum*/, 0/*initial_offset*/); + Slice record; + std::string scratch; + while (reader.ReadRecord(&record, &scratch) && s.ok()) { + VersionEdit edit; + s = edit.DecodeFrom(record); + if (s.ok()) { + if (edit.has_comparator_ && + edit.comparator_ != icmp_.user_comparator()->Name()) { + s = Status::InvalidArgument( + edit.comparator_ + " does not match existing comparator ", + icmp_.user_comparator()->Name()); + } + } + + if (s.ok()) { + builder.Apply(&edit); + } + + if (edit.has_log_number_) { + log_number = edit.log_number_; + have_log_number = true; + } + + if (edit.has_prev_log_number_) { + prev_log_number = edit.prev_log_number_; + have_prev_log_number = true; + } + + if (edit.has_next_file_number_) { + next_file = edit.next_file_number_; + have_next_file = true; + } + + if (edit.has_last_sequence_) { + last_sequence = edit.last_sequence_; + have_last_sequence = true; + } + } + } + delete file; + file = NULL; + + if (s.ok()) { + if (!have_next_file) { + s = Status::Corruption("no meta-nextfile entry in descriptor"); + } else if (!have_log_number) { + s = Status::Corruption("no meta-lognumber entry in descriptor"); + } else if (!have_last_sequence) { + s = Status::Corruption("no last-sequence-number entry in descriptor"); + } + + if (!have_prev_log_number) { + prev_log_number = 0; + } + + MarkFileNumberUsed(prev_log_number); + MarkFileNumberUsed(log_number); + } + + if (s.ok()) { + Version* v = new Version(this); + builder.SaveTo(v); + // Install recovered version + Finalize(v); + AppendVersion(v); + manifest_file_number_ = next_file; + next_file_number_ = next_file + 1; + last_sequence_ = last_sequence; + log_number_ = log_number; + prev_log_number_ = prev_log_number; + } + + return s; +} + +void VersionSet::MarkFileNumberUsed(uint64_t number) { + if (next_file_number_ <= number) { + next_file_number_ = number + 1; + } +} + +void VersionSet::Finalize(Version* v) { + // Precomputed best level for next compaction + int best_level = -1; + double best_score = -1; + + for (int level = 0; level < config::kNumLevels-1; level++) { + double score; + if (level == 0) { + // We treat level-0 specially by bounding the number of files + // instead of number of bytes for two reasons: + // + // (1) With larger write-buffer sizes, it is nice not to do too + // many level-0 compactions. + // + // (2) The files in level-0 are merged on every read and + // therefore we wish to avoid too many files when the individual + // file size is small (perhaps because of a small write-buffer + // setting, or very high compression ratios, or lots of + // overwrites/deletions). + score = v->files_[level].size() / + static_cast(config::kL0_CompactionTrigger); + } else { + // Compute the ratio of current size to size limit. + const uint64_t level_bytes = TotalFileSize(v->files_[level]); + score = static_cast(level_bytes) / MaxBytesForLevel(level); + } + + if (score > best_score) { + best_level = level; + best_score = score; + } + } + + v->compaction_level_ = best_level; + v->compaction_score_ = best_score; +} + +Status VersionSet::WriteSnapshot(log::Writer* log) { + // TODO: Break up into multiple records to reduce memory usage on recovery? + + // Save metadata + VersionEdit edit; + edit.SetComparatorName(icmp_.user_comparator()->Name()); + + // Save compaction pointers + for (int level = 0; level < config::kNumLevels; level++) { + if (!compact_pointer_[level].empty()) { + InternalKey key; + key.DecodeFrom(compact_pointer_[level]); + edit.SetCompactPointer(level, key); + } + } + + // Save files + for (int level = 0; level < config::kNumLevels; level++) { + const std::vector& files = current_->files_[level]; + for (size_t i = 0; i < files.size(); i++) { + const FileMetaData* f = files[i]; + edit.AddFile(level, f->number, f->file_size, f->smallest, f->largest); + } + } + + std::string record; + edit.EncodeTo(&record); + return log->AddRecord(record); +} + +int VersionSet::NumLevelFiles(int level) const { + assert(level >= 0); + assert(level < config::kNumLevels); + return current_->files_[level].size(); +} + +const char* VersionSet::LevelSummary(LevelSummaryStorage* scratch) const { + // Update code if kNumLevels changes + assert(config::kNumLevels == 7); + snprintf(scratch->buffer, sizeof(scratch->buffer), + "files[ %d %d %d %d %d %d %d ]", + int(current_->files_[0].size()), + int(current_->files_[1].size()), + int(current_->files_[2].size()), + int(current_->files_[3].size()), + int(current_->files_[4].size()), + int(current_->files_[5].size()), + int(current_->files_[6].size())); + return scratch->buffer; +} + +// Return true iff the manifest contains the specified record. +bool VersionSet::ManifestContains(const std::string& record) const { + std::string fname = DescriptorFileName(dbname_, manifest_file_number_); + Log(options_->info_log, "ManifestContains: checking %s\n", fname.c_str()); + SequentialFile* file = NULL; + Status s = env_->NewSequentialFile(fname, &file); + if (!s.ok()) { + Log(options_->info_log, "ManifestContains: %s\n", s.ToString().c_str()); + return false; + } + log::Reader reader(file, NULL, true/*checksum*/, 0); + Slice r; + std::string scratch; + bool result = false; + while (reader.ReadRecord(&r, &scratch)) { + if (r == Slice(record)) { + result = true; + break; + } + } + delete file; + Log(options_->info_log, "ManifestContains: result = %d\n", result ? 1 : 0); + return result; +} + +uint64_t VersionSet::ApproximateOffsetOf(Version* v, const InternalKey& ikey) { + uint64_t result = 0; + for (int level = 0; level < config::kNumLevels; level++) { + const std::vector& files = v->files_[level]; + for (size_t i = 0; i < files.size(); i++) { + if (icmp_.Compare(files[i]->largest, ikey) <= 0) { + // Entire file is before "ikey", so just add the file size + result += files[i]->file_size; + } else if (icmp_.Compare(files[i]->smallest, ikey) > 0) { + // Entire file is after "ikey", so ignore + if (level > 0) { + // Files other than level 0 are sorted by meta->smallest, so + // no further files in this level will contain data for + // "ikey". + break; + } + } else { + // "ikey" falls in the range for this table. Add the + // approximate offset of "ikey" within the table. + Table* tableptr; + Iterator* iter = table_cache_->NewIterator( + ReadOptions(), files[i]->number, files[i]->file_size, &tableptr); + if (tableptr != NULL) { + result += tableptr->ApproximateOffsetOf(ikey.Encode()); + } + delete iter; + } + } + } + return result; +} + +void VersionSet::AddLiveFiles(std::set* live) { + for (Version* v = dummy_versions_.next_; + v != &dummy_versions_; + v = v->next_) { + for (int level = 0; level < config::kNumLevels; level++) { + const std::vector& files = v->files_[level]; + for (size_t i = 0; i < files.size(); i++) { + live->insert(files[i]->number); + } + } + } +} + +int64_t VersionSet::NumLevelBytes(int level) const { + assert(level >= 0); + assert(level < config::kNumLevels); + return TotalFileSize(current_->files_[level]); +} + +int64_t VersionSet::MaxNextLevelOverlappingBytes() { + int64_t result = 0; + std::vector overlaps; + for (int level = 1; level < config::kNumLevels - 1; level++) { + for (size_t i = 0; i < current_->files_[level].size(); i++) { + const FileMetaData* f = current_->files_[level][i]; + current_->GetOverlappingInputs(level+1, &f->smallest, &f->largest, + &overlaps); + const int64_t sum = TotalFileSize(overlaps); + if (sum > result) { + result = sum; + } + } + } + return result; +} + +// Stores the minimal range that covers all entries in inputs in +// *smallest, *largest. +// REQUIRES: inputs is not empty +void VersionSet::GetRange(const std::vector& inputs, + InternalKey* smallest, + InternalKey* largest) { + assert(!inputs.empty()); + smallest->Clear(); + largest->Clear(); + for (size_t i = 0; i < inputs.size(); i++) { + FileMetaData* f = inputs[i]; + if (i == 0) { + *smallest = f->smallest; + *largest = f->largest; + } else { + if (icmp_.Compare(f->smallest, *smallest) < 0) { + *smallest = f->smallest; + } + if (icmp_.Compare(f->largest, *largest) > 0) { + *largest = f->largest; + } + } + } +} + +// Stores the minimal range that covers all entries in inputs1 and inputs2 +// in *smallest, *largest. +// REQUIRES: inputs is not empty +void VersionSet::GetRange2(const std::vector& inputs1, + const std::vector& inputs2, + InternalKey* smallest, + InternalKey* largest) { + std::vector all = inputs1; + all.insert(all.end(), inputs2.begin(), inputs2.end()); + GetRange(all, smallest, largest); +} + +Iterator* VersionSet::MakeInputIterator(Compaction* c) { + ReadOptions options; + options.verify_checksums = options_->paranoid_checks; + options.fill_cache = false; + + // Level-0 files have to be merged together. For other levels, + // we will make a concatenating iterator per level. + // TODO(opt): use concatenating iterator for level-0 if there is no overlap + const int space = (c->level() == 0 ? c->inputs_[0].size() + 1 : 2); + Iterator** list = new Iterator*[space]; + int num = 0; + for (int which = 0; which < 2; which++) { + if (!c->inputs_[which].empty()) { + if (c->level() + which == 0) { + const std::vector& files = c->inputs_[which]; + for (size_t i = 0; i < files.size(); i++) { + list[num++] = table_cache_->NewIterator( + options, files[i]->number, files[i]->file_size); + } + } else { + // Create concatenating iterator for the files from this level + list[num++] = NewTwoLevelIterator( + new Version::LevelFileNumIterator(icmp_, &c->inputs_[which]), + &GetFileIterator, table_cache_, options); + } + } + } + assert(num <= space); + Iterator* result = NewMergingIterator(&icmp_, list, num); + delete[] list; + return result; +} + +Compaction* VersionSet::PickCompaction() { + Compaction* c; + int level; + + // We prefer compactions triggered by too much data in a level over + // the compactions triggered by seeks. + const bool size_compaction = (current_->compaction_score_ >= 1); + const bool seek_compaction = (current_->file_to_compact_ != NULL); + if (size_compaction) { + level = current_->compaction_level_; + assert(level >= 0); + assert(level+1 < config::kNumLevels); + c = new Compaction(level); + + // Pick the first file that comes after compact_pointer_[level] + for (size_t i = 0; i < current_->files_[level].size(); i++) { + FileMetaData* f = current_->files_[level][i]; + if (compact_pointer_[level].empty() || + icmp_.Compare(f->largest.Encode(), compact_pointer_[level]) > 0) { + c->inputs_[0].push_back(f); + break; + } + } + if (c->inputs_[0].empty()) { + // Wrap-around to the beginning of the key space + c->inputs_[0].push_back(current_->files_[level][0]); + } + } else if (seek_compaction) { + level = current_->file_to_compact_level_; + c = new Compaction(level); + c->inputs_[0].push_back(current_->file_to_compact_); + } else { + return NULL; + } + + c->input_version_ = current_; + c->input_version_->Ref(); + + // Files in level 0 may overlap each other, so pick up all overlapping ones + if (level == 0) { + InternalKey smallest, largest; + GetRange(c->inputs_[0], &smallest, &largest); + // Note that the next call will discard the file we placed in + // c->inputs_[0] earlier and replace it with an overlapping set + // which will include the picked file. + current_->GetOverlappingInputs(0, &smallest, &largest, &c->inputs_[0]); + assert(!c->inputs_[0].empty()); + } + + SetupOtherInputs(c); + + return c; +} + +void VersionSet::SetupOtherInputs(Compaction* c) { + const int level = c->level(); + InternalKey smallest, largest; + GetRange(c->inputs_[0], &smallest, &largest); + + current_->GetOverlappingInputs(level+1, &smallest, &largest, &c->inputs_[1]); + + // Get entire range covered by compaction + InternalKey all_start, all_limit; + GetRange2(c->inputs_[0], c->inputs_[1], &all_start, &all_limit); + + // See if we can grow the number of inputs in "level" without + // changing the number of "level+1" files we pick up. + if (!c->inputs_[1].empty()) { + std::vector expanded0; + current_->GetOverlappingInputs(level, &all_start, &all_limit, &expanded0); + const int64_t inputs0_size = TotalFileSize(c->inputs_[0]); + const int64_t inputs1_size = TotalFileSize(c->inputs_[1]); + const int64_t expanded0_size = TotalFileSize(expanded0); + if (expanded0.size() > c->inputs_[0].size() && + inputs1_size + expanded0_size < kExpandedCompactionByteSizeLimit) { + InternalKey new_start, new_limit; + GetRange(expanded0, &new_start, &new_limit); + std::vector expanded1; + current_->GetOverlappingInputs(level+1, &new_start, &new_limit, + &expanded1); + if (expanded1.size() == c->inputs_[1].size()) { + Log(options_->info_log, + "Expanding@%d %d+%d (%ld+%ld bytes) to %d+%d (%ld+%ld bytes)\n", + level, + int(c->inputs_[0].size()), + int(c->inputs_[1].size()), + long(inputs0_size), long(inputs1_size), + int(expanded0.size()), + int(expanded1.size()), + long(expanded0_size), long(inputs1_size)); + smallest = new_start; + largest = new_limit; + c->inputs_[0] = expanded0; + c->inputs_[1] = expanded1; + GetRange2(c->inputs_[0], c->inputs_[1], &all_start, &all_limit); + } + } + } + + // Compute the set of grandparent files that overlap this compaction + // (parent == level+1; grandparent == level+2) + if (level + 2 < config::kNumLevels) { + current_->GetOverlappingInputs(level + 2, &all_start, &all_limit, + &c->grandparents_); + } + + if (false) { + Log(options_->info_log, "Compacting %d '%s' .. '%s'", + level, + smallest.DebugString().c_str(), + largest.DebugString().c_str()); + } + + // Update the place where we will do the next compaction for this level. + // We update this immediately instead of waiting for the VersionEdit + // to be applied so that if the compaction fails, we will try a different + // key range next time. + compact_pointer_[level] = largest.Encode().ToString(); + c->edit_.SetCompactPointer(level, largest); +} + +Compaction* VersionSet::CompactRange( + int level, + const InternalKey* begin, + const InternalKey* end) { + std::vector inputs; + current_->GetOverlappingInputs(level, begin, end, &inputs); + if (inputs.empty()) { + return NULL; + } + + // Avoid compacting too much in one shot in case the range is large. + const uint64_t limit = MaxFileSizeForLevel(level); + uint64_t total = 0; + for (size_t i = 0; i < inputs.size(); i++) { + uint64_t s = inputs[i]->file_size; + total += s; + if (total >= limit) { + inputs.resize(i + 1); + break; + } + } + + Compaction* c = new Compaction(level); + c->input_version_ = current_; + c->input_version_->Ref(); + c->inputs_[0] = inputs; + SetupOtherInputs(c); + return c; +} + +Compaction::Compaction(int level) + : level_(level), + max_output_file_size_(MaxFileSizeForLevel(level)), + input_version_(NULL), + grandparent_index_(0), + seen_key_(false), + overlapped_bytes_(0) { + for (int i = 0; i < config::kNumLevels; i++) { + level_ptrs_[i] = 0; + } +} + +Compaction::~Compaction() { + if (input_version_ != NULL) { + input_version_->Unref(); + } +} + +bool Compaction::IsTrivialMove() const { + // Avoid a move if there is lots of overlapping grandparent data. + // Otherwise, the move could create a parent file that will require + // a very expensive merge later on. + return (num_input_files(0) == 1 && + num_input_files(1) == 0 && + TotalFileSize(grandparents_) <= kMaxGrandParentOverlapBytes); +} + +void Compaction::AddInputDeletions(VersionEdit* edit) { + for (int which = 0; which < 2; which++) { + for (size_t i = 0; i < inputs_[which].size(); i++) { + edit->DeleteFile(level_ + which, inputs_[which][i]->number); + } + } +} + +bool Compaction::IsBaseLevelForKey(const Slice& user_key) { + // Maybe use binary search to find right entry instead of linear search? + const Comparator* user_cmp = input_version_->vset_->icmp_.user_comparator(); + for (int lvl = level_ + 2; lvl < config::kNumLevels; lvl++) { + const std::vector& files = input_version_->files_[lvl]; + for (; level_ptrs_[lvl] < files.size(); ) { + FileMetaData* f = files[level_ptrs_[lvl]]; + if (user_cmp->Compare(user_key, f->largest.user_key()) <= 0) { + // We've advanced far enough + if (user_cmp->Compare(user_key, f->smallest.user_key()) >= 0) { + // Key falls in this file's range, so definitely not base level + return false; + } + break; + } + level_ptrs_[lvl]++; + } + } + return true; +} + +bool Compaction::ShouldStopBefore(const Slice& internal_key) { + // Scan to find earliest grandparent file that contains key. + const InternalKeyComparator* icmp = &input_version_->vset_->icmp_; + while (grandparent_index_ < grandparents_.size() && + icmp->Compare(internal_key, + grandparents_[grandparent_index_]->largest.Encode()) > 0) { + if (seen_key_) { + overlapped_bytes_ += grandparents_[grandparent_index_]->file_size; + } + grandparent_index_++; + } + seen_key_ = true; + + if (overlapped_bytes_ > kMaxGrandParentOverlapBytes) { + // Too much overlap for current output; start new output + overlapped_bytes_ = 0; + return true; + } else { + return false; + } +} + +void Compaction::ReleaseInputs() { + if (input_version_ != NULL) { + input_version_->Unref(); + input_version_ = NULL; + } +} + +} // namespace leveldb diff --git a/src/leveldb/db/version_set.h b/src/leveldb/db/version_set.h new file mode 100644 index 0000000..9d084fd --- /dev/null +++ b/src/leveldb/db/version_set.h @@ -0,0 +1,383 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// The representation of a DBImpl consists of a set of Versions. The +// newest version is called "current". Older versions may be kept +// around to provide a consistent view to live iterators. +// +// Each Version keeps track of a set of Table files per level. The +// entire set of versions is maintained in a VersionSet. +// +// Version,VersionSet are thread-compatible, but require external +// synchronization on all accesses. + +#ifndef STORAGE_LEVELDB_DB_VERSION_SET_H_ +#define STORAGE_LEVELDB_DB_VERSION_SET_H_ + +#include +#include +#include +#include "db/dbformat.h" +#include "db/version_edit.h" +#include "port/port.h" +#include "port/thread_annotations.h" + +namespace leveldb { + +namespace log { class Writer; } + +class Compaction; +class Iterator; +class MemTable; +class TableBuilder; +class TableCache; +class Version; +class VersionSet; +class WritableFile; + +// Return the smallest index i such that files[i]->largest >= key. +// Return files.size() if there is no such file. +// REQUIRES: "files" contains a sorted list of non-overlapping files. +extern int FindFile(const InternalKeyComparator& icmp, + const std::vector& files, + const Slice& key); + +// Returns true iff some file in "files" overlaps the user key range +// [*smallest,*largest]. +// smallest==NULL represents a key smaller than all keys in the DB. +// largest==NULL represents a key largest than all keys in the DB. +// REQUIRES: If disjoint_sorted_files, files[] contains disjoint ranges +// in sorted order. +extern bool SomeFileOverlapsRange( + const InternalKeyComparator& icmp, + bool disjoint_sorted_files, + const std::vector& files, + const Slice* smallest_user_key, + const Slice* largest_user_key); + +class Version { + public: + // Append to *iters a sequence of iterators that will + // yield the contents of this Version when merged together. + // REQUIRES: This version has been saved (see VersionSet::SaveTo) + void AddIterators(const ReadOptions&, std::vector* iters); + + // Lookup the value for key. If found, store it in *val and + // return OK. Else return a non-OK status. Fills *stats. + // REQUIRES: lock is not held + struct GetStats { + FileMetaData* seek_file; + int seek_file_level; + }; + Status Get(const ReadOptions&, const LookupKey& key, std::string* val, + GetStats* stats); + + // Adds "stats" into the current state. Returns true if a new + // compaction may need to be triggered, false otherwise. + // REQUIRES: lock is held + bool UpdateStats(const GetStats& stats); + + // Reference count management (so Versions do not disappear out from + // under live iterators) + void Ref(); + void Unref(); + + void GetOverlappingInputs( + int level, + const InternalKey* begin, // NULL means before all keys + const InternalKey* end, // NULL means after all keys + std::vector* inputs); + + // Returns true iff some file in the specified level overlaps + // some part of [*smallest_user_key,*largest_user_key]. + // smallest_user_key==NULL represents a key smaller than all keys in the DB. + // largest_user_key==NULL represents a key largest than all keys in the DB. + bool OverlapInLevel(int level, + const Slice* smallest_user_key, + const Slice* largest_user_key); + + // Return the level at which we should place a new memtable compaction + // result that covers the range [smallest_user_key,largest_user_key]. + int PickLevelForMemTableOutput(const Slice& smallest_user_key, + const Slice& largest_user_key); + + int NumFiles(int level) const { return files_[level].size(); } + + // Return a human readable string that describes this version's contents. + std::string DebugString() const; + + private: + friend class Compaction; + friend class VersionSet; + + class LevelFileNumIterator; + Iterator* NewConcatenatingIterator(const ReadOptions&, int level) const; + + VersionSet* vset_; // VersionSet to which this Version belongs + Version* next_; // Next version in linked list + Version* prev_; // Previous version in linked list + int refs_; // Number of live refs to this version + + // List of files per level + std::vector files_[config::kNumLevels]; + + // Next file to compact based on seek stats. + FileMetaData* file_to_compact_; + int file_to_compact_level_; + + // Level that should be compacted next and its compaction score. + // Score < 1 means compaction is not strictly needed. These fields + // are initialized by Finalize(). + double compaction_score_; + int compaction_level_; + + explicit Version(VersionSet* vset) + : vset_(vset), next_(this), prev_(this), refs_(0), + file_to_compact_(NULL), + file_to_compact_level_(-1), + compaction_score_(-1), + compaction_level_(-1) { + } + + ~Version(); + + // No copying allowed + Version(const Version&); + void operator=(const Version&); +}; + +class VersionSet { + public: + VersionSet(const std::string& dbname, + const Options* options, + TableCache* table_cache, + const InternalKeyComparator*); + ~VersionSet(); + + // Apply *edit to the current version to form a new descriptor that + // is both saved to persistent state and installed as the new + // current version. Will release *mu while actually writing to the file. + // REQUIRES: *mu is held on entry. + // REQUIRES: no other thread concurrently calls LogAndApply() + Status LogAndApply(VersionEdit* edit, port::Mutex* mu) + EXCLUSIVE_LOCKS_REQUIRED(mu); + + // Recover the last saved descriptor from persistent storage. + Status Recover(); + + // Return the current version. + Version* current() const { return current_; } + + // Return the current manifest file number + uint64_t ManifestFileNumber() const { return manifest_file_number_; } + + // Allocate and return a new file number + uint64_t NewFileNumber() { return next_file_number_++; } + + // Arrange to reuse "file_number" unless a newer file number has + // already been allocated. + // REQUIRES: "file_number" was returned by a call to NewFileNumber(). + void ReuseFileNumber(uint64_t file_number) { + if (next_file_number_ == file_number + 1) { + next_file_number_ = file_number; + } + } + + // Return the number of Table files at the specified level. + int NumLevelFiles(int level) const; + + // Return the combined file size of all files at the specified level. + int64_t NumLevelBytes(int level) const; + + // Return the last sequence number. + uint64_t LastSequence() const { return last_sequence_; } + + // Set the last sequence number to s. + void SetLastSequence(uint64_t s) { + assert(s >= last_sequence_); + last_sequence_ = s; + } + + // Mark the specified file number as used. + void MarkFileNumberUsed(uint64_t number); + + // Return the current log file number. + uint64_t LogNumber() const { return log_number_; } + + // Return the log file number for the log file that is currently + // being compacted, or zero if there is no such log file. + uint64_t PrevLogNumber() const { return prev_log_number_; } + + // Pick level and inputs for a new compaction. + // Returns NULL if there is no compaction to be done. + // Otherwise returns a pointer to a heap-allocated object that + // describes the compaction. Caller should delete the result. + Compaction* PickCompaction(); + + // Return a compaction object for compacting the range [begin,end] in + // the specified level. Returns NULL if there is nothing in that + // level that overlaps the specified range. Caller should delete + // the result. + Compaction* CompactRange( + int level, + const InternalKey* begin, + const InternalKey* end); + + // Return the maximum overlapping data (in bytes) at next level for any + // file at a level >= 1. + int64_t MaxNextLevelOverlappingBytes(); + + // Create an iterator that reads over the compaction inputs for "*c". + // The caller should delete the iterator when no longer needed. + Iterator* MakeInputIterator(Compaction* c); + + // Returns true iff some level needs a compaction. + bool NeedsCompaction() const { + Version* v = current_; + return (v->compaction_score_ >= 1) || (v->file_to_compact_ != NULL); + } + + // Add all files listed in any live version to *live. + // May also mutate some internal state. + void AddLiveFiles(std::set* live); + + // Return the approximate offset in the database of the data for + // "key" as of version "v". + uint64_t ApproximateOffsetOf(Version* v, const InternalKey& key); + + // Return a human-readable short (single-line) summary of the number + // of files per level. Uses *scratch as backing store. + struct LevelSummaryStorage { + char buffer[100]; + }; + const char* LevelSummary(LevelSummaryStorage* scratch) const; + + private: + class Builder; + + friend class Compaction; + friend class Version; + + void Finalize(Version* v); + + void GetRange(const std::vector& inputs, + InternalKey* smallest, + InternalKey* largest); + + void GetRange2(const std::vector& inputs1, + const std::vector& inputs2, + InternalKey* smallest, + InternalKey* largest); + + void SetupOtherInputs(Compaction* c); + + // Save current contents to *log + Status WriteSnapshot(log::Writer* log); + + void AppendVersion(Version* v); + + bool ManifestContains(const std::string& record) const; + + Env* const env_; + const std::string dbname_; + const Options* const options_; + TableCache* const table_cache_; + const InternalKeyComparator icmp_; + uint64_t next_file_number_; + uint64_t manifest_file_number_; + uint64_t last_sequence_; + uint64_t log_number_; + uint64_t prev_log_number_; // 0 or backing store for memtable being compacted + + // Opened lazily + WritableFile* descriptor_file_; + log::Writer* descriptor_log_; + Version dummy_versions_; // Head of circular doubly-linked list of versions. + Version* current_; // == dummy_versions_.prev_ + + // Per-level key at which the next compaction at that level should start. + // Either an empty string, or a valid InternalKey. + std::string compact_pointer_[config::kNumLevels]; + + // No copying allowed + VersionSet(const VersionSet&); + void operator=(const VersionSet&); +}; + +// A Compaction encapsulates information about a compaction. +class Compaction { + public: + ~Compaction(); + + // Return the level that is being compacted. Inputs from "level" + // and "level+1" will be merged to produce a set of "level+1" files. + int level() const { return level_; } + + // Return the object that holds the edits to the descriptor done + // by this compaction. + VersionEdit* edit() { return &edit_; } + + // "which" must be either 0 or 1 + int num_input_files(int which) const { return inputs_[which].size(); } + + // Return the ith input file at "level()+which" ("which" must be 0 or 1). + FileMetaData* input(int which, int i) const { return inputs_[which][i]; } + + // Maximum size of files to build during this compaction. + uint64_t MaxOutputFileSize() const { return max_output_file_size_; } + + // Is this a trivial compaction that can be implemented by just + // moving a single input file to the next level (no merging or splitting) + bool IsTrivialMove() const; + + // Add all inputs to this compaction as delete operations to *edit. + void AddInputDeletions(VersionEdit* edit); + + // Returns true if the information we have available guarantees that + // the compaction is producing data in "level+1" for which no data exists + // in levels greater than "level+1". + bool IsBaseLevelForKey(const Slice& user_key); + + // Returns true iff we should stop building the current output + // before processing "internal_key". + bool ShouldStopBefore(const Slice& internal_key); + + // Release the input version for the compaction, once the compaction + // is successful. + void ReleaseInputs(); + + private: + friend class Version; + friend class VersionSet; + + explicit Compaction(int level); + + int level_; + uint64_t max_output_file_size_; + Version* input_version_; + VersionEdit edit_; + + // Each compaction reads inputs from "level_" and "level_+1" + std::vector inputs_[2]; // The two sets of inputs + + // State used to check for number of of overlapping grandparent files + // (parent == level_ + 1, grandparent == level_ + 2) + std::vector grandparents_; + size_t grandparent_index_; // Index in grandparent_starts_ + bool seen_key_; // Some output key has been seen + int64_t overlapped_bytes_; // Bytes of overlap between current output + // and grandparent files + + // State for implementing IsBaseLevelForKey + + // level_ptrs_ holds indices into input_version_->levels_: our state + // is that we are positioned at one of the file ranges for each + // higher level than the ones involved in this compaction (i.e. for + // all L >= level_ + 2). + size_t level_ptrs_[config::kNumLevels]; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_DB_VERSION_SET_H_ diff --git a/src/leveldb/db/version_set_test.cc b/src/leveldb/db/version_set_test.cc new file mode 100644 index 0000000..501e34d --- /dev/null +++ b/src/leveldb/db/version_set_test.cc @@ -0,0 +1,179 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/version_set.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +class FindFileTest { + public: + std::vector files_; + bool disjoint_sorted_files_; + + FindFileTest() : disjoint_sorted_files_(true) { } + + ~FindFileTest() { + for (int i = 0; i < files_.size(); i++) { + delete files_[i]; + } + } + + void Add(const char* smallest, const char* largest, + SequenceNumber smallest_seq = 100, + SequenceNumber largest_seq = 100) { + FileMetaData* f = new FileMetaData; + f->number = files_.size() + 1; + f->smallest = InternalKey(smallest, smallest_seq, kTypeValue); + f->largest = InternalKey(largest, largest_seq, kTypeValue); + files_.push_back(f); + } + + int Find(const char* key) { + InternalKey target(key, 100, kTypeValue); + InternalKeyComparator cmp(BytewiseComparator()); + return FindFile(cmp, files_, target.Encode()); + } + + bool Overlaps(const char* smallest, const char* largest) { + InternalKeyComparator cmp(BytewiseComparator()); + Slice s(smallest != NULL ? smallest : ""); + Slice l(largest != NULL ? largest : ""); + return SomeFileOverlapsRange(cmp, disjoint_sorted_files_, files_, + (smallest != NULL ? &s : NULL), + (largest != NULL ? &l : NULL)); + } +}; + +TEST(FindFileTest, Empty) { + ASSERT_EQ(0, Find("foo")); + ASSERT_TRUE(! Overlaps("a", "z")); + ASSERT_TRUE(! Overlaps(NULL, "z")); + ASSERT_TRUE(! Overlaps("a", NULL)); + ASSERT_TRUE(! Overlaps(NULL, NULL)); +} + +TEST(FindFileTest, Single) { + Add("p", "q"); + ASSERT_EQ(0, Find("a")); + ASSERT_EQ(0, Find("p")); + ASSERT_EQ(0, Find("p1")); + ASSERT_EQ(0, Find("q")); + ASSERT_EQ(1, Find("q1")); + ASSERT_EQ(1, Find("z")); + + ASSERT_TRUE(! Overlaps("a", "b")); + ASSERT_TRUE(! Overlaps("z1", "z2")); + ASSERT_TRUE(Overlaps("a", "p")); + ASSERT_TRUE(Overlaps("a", "q")); + ASSERT_TRUE(Overlaps("a", "z")); + ASSERT_TRUE(Overlaps("p", "p1")); + ASSERT_TRUE(Overlaps("p", "q")); + ASSERT_TRUE(Overlaps("p", "z")); + ASSERT_TRUE(Overlaps("p1", "p2")); + ASSERT_TRUE(Overlaps("p1", "z")); + ASSERT_TRUE(Overlaps("q", "q")); + ASSERT_TRUE(Overlaps("q", "q1")); + + ASSERT_TRUE(! Overlaps(NULL, "j")); + ASSERT_TRUE(! Overlaps("r", NULL)); + ASSERT_TRUE(Overlaps(NULL, "p")); + ASSERT_TRUE(Overlaps(NULL, "p1")); + ASSERT_TRUE(Overlaps("q", NULL)); + ASSERT_TRUE(Overlaps(NULL, NULL)); +} + + +TEST(FindFileTest, Multiple) { + Add("150", "200"); + Add("200", "250"); + Add("300", "350"); + Add("400", "450"); + ASSERT_EQ(0, Find("100")); + ASSERT_EQ(0, Find("150")); + ASSERT_EQ(0, Find("151")); + ASSERT_EQ(0, Find("199")); + ASSERT_EQ(0, Find("200")); + ASSERT_EQ(1, Find("201")); + ASSERT_EQ(1, Find("249")); + ASSERT_EQ(1, Find("250")); + ASSERT_EQ(2, Find("251")); + ASSERT_EQ(2, Find("299")); + ASSERT_EQ(2, Find("300")); + ASSERT_EQ(2, Find("349")); + ASSERT_EQ(2, Find("350")); + ASSERT_EQ(3, Find("351")); + ASSERT_EQ(3, Find("400")); + ASSERT_EQ(3, Find("450")); + ASSERT_EQ(4, Find("451")); + + ASSERT_TRUE(! Overlaps("100", "149")); + ASSERT_TRUE(! Overlaps("251", "299")); + ASSERT_TRUE(! Overlaps("451", "500")); + ASSERT_TRUE(! Overlaps("351", "399")); + + ASSERT_TRUE(Overlaps("100", "150")); + ASSERT_TRUE(Overlaps("100", "200")); + ASSERT_TRUE(Overlaps("100", "300")); + ASSERT_TRUE(Overlaps("100", "400")); + ASSERT_TRUE(Overlaps("100", "500")); + ASSERT_TRUE(Overlaps("375", "400")); + ASSERT_TRUE(Overlaps("450", "450")); + ASSERT_TRUE(Overlaps("450", "500")); +} + +TEST(FindFileTest, MultipleNullBoundaries) { + Add("150", "200"); + Add("200", "250"); + Add("300", "350"); + Add("400", "450"); + ASSERT_TRUE(! Overlaps(NULL, "149")); + ASSERT_TRUE(! Overlaps("451", NULL)); + ASSERT_TRUE(Overlaps(NULL, NULL)); + ASSERT_TRUE(Overlaps(NULL, "150")); + ASSERT_TRUE(Overlaps(NULL, "199")); + ASSERT_TRUE(Overlaps(NULL, "200")); + ASSERT_TRUE(Overlaps(NULL, "201")); + ASSERT_TRUE(Overlaps(NULL, "400")); + ASSERT_TRUE(Overlaps(NULL, "800")); + ASSERT_TRUE(Overlaps("100", NULL)); + ASSERT_TRUE(Overlaps("200", NULL)); + ASSERT_TRUE(Overlaps("449", NULL)); + ASSERT_TRUE(Overlaps("450", NULL)); +} + +TEST(FindFileTest, OverlapSequenceChecks) { + Add("200", "200", 5000, 3000); + ASSERT_TRUE(! Overlaps("199", "199")); + ASSERT_TRUE(! Overlaps("201", "300")); + ASSERT_TRUE(Overlaps("200", "200")); + ASSERT_TRUE(Overlaps("190", "200")); + ASSERT_TRUE(Overlaps("200", "210")); +} + +TEST(FindFileTest, OverlappingFiles) { + Add("150", "600"); + Add("400", "500"); + disjoint_sorted_files_ = false; + ASSERT_TRUE(! Overlaps("100", "149")); + ASSERT_TRUE(! Overlaps("601", "700")); + ASSERT_TRUE(Overlaps("100", "150")); + ASSERT_TRUE(Overlaps("100", "200")); + ASSERT_TRUE(Overlaps("100", "300")); + ASSERT_TRUE(Overlaps("100", "400")); + ASSERT_TRUE(Overlaps("100", "500")); + ASSERT_TRUE(Overlaps("375", "400")); + ASSERT_TRUE(Overlaps("450", "450")); + ASSERT_TRUE(Overlaps("450", "500")); + ASSERT_TRUE(Overlaps("450", "700")); + ASSERT_TRUE(Overlaps("600", "700")); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/write_batch.cc b/src/leveldb/db/write_batch.cc new file mode 100644 index 0000000..33f4a42 --- /dev/null +++ b/src/leveldb/db/write_batch.cc @@ -0,0 +1,147 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// WriteBatch::rep_ := +// sequence: fixed64 +// count: fixed32 +// data: record[count] +// record := +// kTypeValue varstring varstring | +// kTypeDeletion varstring +// varstring := +// len: varint32 +// data: uint8[len] + +#include "leveldb/write_batch.h" + +#include "leveldb/db.h" +#include "db/dbformat.h" +#include "db/memtable.h" +#include "db/write_batch_internal.h" +#include "util/coding.h" + +namespace leveldb { + +// WriteBatch header has an 8-byte sequence number followed by a 4-byte count. +static const size_t kHeader = 12; + +WriteBatch::WriteBatch() { + Clear(); +} + +WriteBatch::~WriteBatch() { } + +WriteBatch::Handler::~Handler() { } + +void WriteBatch::Clear() { + rep_.clear(); + rep_.resize(kHeader); +} + +Status WriteBatch::Iterate(Handler* handler) const { + Slice input(rep_); + if (input.size() < kHeader) { + return Status::Corruption("malformed WriteBatch (too small)"); + } + + input.remove_prefix(kHeader); + Slice key, value; + int found = 0; + while (!input.empty()) { + found++; + char tag = input[0]; + input.remove_prefix(1); + switch (tag) { + case kTypeValue: + if (GetLengthPrefixedSlice(&input, &key) && + GetLengthPrefixedSlice(&input, &value)) { + handler->Put(key, value); + } else { + return Status::Corruption("bad WriteBatch Put"); + } + break; + case kTypeDeletion: + if (GetLengthPrefixedSlice(&input, &key)) { + handler->Delete(key); + } else { + return Status::Corruption("bad WriteBatch Delete"); + } + break; + default: + return Status::Corruption("unknown WriteBatch tag"); + } + } + if (found != WriteBatchInternal::Count(this)) { + return Status::Corruption("WriteBatch has wrong count"); + } else { + return Status::OK(); + } +} + +int WriteBatchInternal::Count(const WriteBatch* b) { + return DecodeFixed32(b->rep_.data() + 8); +} + +void WriteBatchInternal::SetCount(WriteBatch* b, int n) { + EncodeFixed32(&b->rep_[8], n); +} + +SequenceNumber WriteBatchInternal::Sequence(const WriteBatch* b) { + return SequenceNumber(DecodeFixed64(b->rep_.data())); +} + +void WriteBatchInternal::SetSequence(WriteBatch* b, SequenceNumber seq) { + EncodeFixed64(&b->rep_[0], seq); +} + +void WriteBatch::Put(const Slice& key, const Slice& value) { + WriteBatchInternal::SetCount(this, WriteBatchInternal::Count(this) + 1); + rep_.push_back(static_cast(kTypeValue)); + PutLengthPrefixedSlice(&rep_, key); + PutLengthPrefixedSlice(&rep_, value); +} + +void WriteBatch::Delete(const Slice& key) { + WriteBatchInternal::SetCount(this, WriteBatchInternal::Count(this) + 1); + rep_.push_back(static_cast(kTypeDeletion)); + PutLengthPrefixedSlice(&rep_, key); +} + +namespace { +class MemTableInserter : public WriteBatch::Handler { + public: + SequenceNumber sequence_; + MemTable* mem_; + + virtual void Put(const Slice& key, const Slice& value) { + mem_->Add(sequence_, kTypeValue, key, value); + sequence_++; + } + virtual void Delete(const Slice& key) { + mem_->Add(sequence_, kTypeDeletion, key, Slice()); + sequence_++; + } +}; +} // namespace + +Status WriteBatchInternal::InsertInto(const WriteBatch* b, + MemTable* memtable) { + MemTableInserter inserter; + inserter.sequence_ = WriteBatchInternal::Sequence(b); + inserter.mem_ = memtable; + return b->Iterate(&inserter); +} + +void WriteBatchInternal::SetContents(WriteBatch* b, const Slice& contents) { + assert(contents.size() >= kHeader); + b->rep_.assign(contents.data(), contents.size()); +} + +void WriteBatchInternal::Append(WriteBatch* dst, const WriteBatch* src) { + SetCount(dst, Count(dst) + Count(src)); + assert(src->rep_.size() >= kHeader); + dst->rep_.append(src->rep_.data() + kHeader, src->rep_.size() - kHeader); +} + +} // namespace leveldb diff --git a/src/leveldb/db/write_batch_internal.h b/src/leveldb/db/write_batch_internal.h new file mode 100644 index 0000000..4423a7f --- /dev/null +++ b/src/leveldb/db/write_batch_internal.h @@ -0,0 +1,49 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_ +#define STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_ + +#include "leveldb/write_batch.h" + +namespace leveldb { + +class MemTable; + +// WriteBatchInternal provides static methods for manipulating a +// WriteBatch that we don't want in the public WriteBatch interface. +class WriteBatchInternal { + public: + // Return the number of entries in the batch. + static int Count(const WriteBatch* batch); + + // Set the count for the number of entries in the batch. + static void SetCount(WriteBatch* batch, int n); + + // Return the seqeunce number for the start of this batch. + static SequenceNumber Sequence(const WriteBatch* batch); + + // Store the specified number as the seqeunce number for the start of + // this batch. + static void SetSequence(WriteBatch* batch, SequenceNumber seq); + + static Slice Contents(const WriteBatch* batch) { + return Slice(batch->rep_); + } + + static size_t ByteSize(const WriteBatch* batch) { + return batch->rep_.size(); + } + + static void SetContents(WriteBatch* batch, const Slice& contents); + + static Status InsertInto(const WriteBatch* batch, MemTable* memtable); + + static void Append(WriteBatch* dst, const WriteBatch* src); +}; + +} // namespace leveldb + + +#endif // STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_ diff --git a/src/leveldb/db/write_batch_test.cc b/src/leveldb/db/write_batch_test.cc new file mode 100644 index 0000000..9064e3d --- /dev/null +++ b/src/leveldb/db/write_batch_test.cc @@ -0,0 +1,120 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" + +#include "db/memtable.h" +#include "db/write_batch_internal.h" +#include "leveldb/env.h" +#include "util/logging.h" +#include "util/testharness.h" + +namespace leveldb { + +static std::string PrintContents(WriteBatch* b) { + InternalKeyComparator cmp(BytewiseComparator()); + MemTable* mem = new MemTable(cmp); + mem->Ref(); + std::string state; + Status s = WriteBatchInternal::InsertInto(b, mem); + int count = 0; + Iterator* iter = mem->NewIterator(); + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + ParsedInternalKey ikey; + ASSERT_TRUE(ParseInternalKey(iter->key(), &ikey)); + switch (ikey.type) { + case kTypeValue: + state.append("Put("); + state.append(ikey.user_key.ToString()); + state.append(", "); + state.append(iter->value().ToString()); + state.append(")"); + count++; + break; + case kTypeDeletion: + state.append("Delete("); + state.append(ikey.user_key.ToString()); + state.append(")"); + count++; + break; + } + state.append("@"); + state.append(NumberToString(ikey.sequence)); + } + delete iter; + if (!s.ok()) { + state.append("ParseError()"); + } else if (count != WriteBatchInternal::Count(b)) { + state.append("CountMismatch()"); + } + mem->Unref(); + return state; +} + +class WriteBatchTest { }; + +TEST(WriteBatchTest, Empty) { + WriteBatch batch; + ASSERT_EQ("", PrintContents(&batch)); + ASSERT_EQ(0, WriteBatchInternal::Count(&batch)); +} + +TEST(WriteBatchTest, Multiple) { + WriteBatch batch; + batch.Put(Slice("foo"), Slice("bar")); + batch.Delete(Slice("box")); + batch.Put(Slice("baz"), Slice("boo")); + WriteBatchInternal::SetSequence(&batch, 100); + ASSERT_EQ(100, WriteBatchInternal::Sequence(&batch)); + ASSERT_EQ(3, WriteBatchInternal::Count(&batch)); + ASSERT_EQ("Put(baz, boo)@102" + "Delete(box)@101" + "Put(foo, bar)@100", + PrintContents(&batch)); +} + +TEST(WriteBatchTest, Corruption) { + WriteBatch batch; + batch.Put(Slice("foo"), Slice("bar")); + batch.Delete(Slice("box")); + WriteBatchInternal::SetSequence(&batch, 200); + Slice contents = WriteBatchInternal::Contents(&batch); + WriteBatchInternal::SetContents(&batch, + Slice(contents.data(),contents.size()-1)); + ASSERT_EQ("Put(foo, bar)@200" + "ParseError()", + PrintContents(&batch)); +} + +TEST(WriteBatchTest, Append) { + WriteBatch b1, b2; + WriteBatchInternal::SetSequence(&b1, 200); + WriteBatchInternal::SetSequence(&b2, 300); + WriteBatchInternal::Append(&b1, &b2); + ASSERT_EQ("", + PrintContents(&b1)); + b2.Put("a", "va"); + WriteBatchInternal::Append(&b1, &b2); + ASSERT_EQ("Put(a, va)@200", + PrintContents(&b1)); + b2.Clear(); + b2.Put("b", "vb"); + WriteBatchInternal::Append(&b1, &b2); + ASSERT_EQ("Put(a, va)@200" + "Put(b, vb)@201", + PrintContents(&b1)); + b2.Delete("foo"); + WriteBatchInternal::Append(&b1, &b2); + ASSERT_EQ("Put(a, va)@200" + "Put(b, vb)@202" + "Put(b, vb)@201" + "Delete(foo)@203", + PrintContents(&b1)); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/doc/bench/db_bench_sqlite3.cc b/src/leveldb/doc/bench/db_bench_sqlite3.cc new file mode 100644 index 0000000..e63aaa8 --- /dev/null +++ b/src/leveldb/doc/bench/db_bench_sqlite3.cc @@ -0,0 +1,718 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include +#include "util/histogram.h" +#include "util/random.h" +#include "util/testutil.h" + +// Comma-separated list of operations to run in the specified order +// Actual benchmarks: +// +// fillseq -- write N values in sequential key order in async mode +// fillseqsync -- write N/100 values in sequential key order in sync mode +// fillseqbatch -- batch write N values in sequential key order in async mode +// fillrandom -- write N values in random key order in async mode +// fillrandsync -- write N/100 values in random key order in sync mode +// fillrandbatch -- batch write N values in sequential key order in async mode +// overwrite -- overwrite N values in random key order in async mode +// fillrand100K -- write N/1000 100K values in random order in async mode +// fillseq100K -- write N/1000 100K values in sequential order in async mode +// readseq -- read N times sequentially +// readrandom -- read N times in random order +// readrand100K -- read N/1000 100K values in sequential order in async mode +static const char* FLAGS_benchmarks = + "fillseq," + "fillseqsync," + "fillseqbatch," + "fillrandom," + "fillrandsync," + "fillrandbatch," + "overwrite," + "overwritebatch," + "readrandom," + "readseq," + "fillrand100K," + "fillseq100K," + "readseq," + "readrand100K," + ; + +// Number of key/values to place in database +static int FLAGS_num = 1000000; + +// Number of read operations to do. If negative, do FLAGS_num reads. +static int FLAGS_reads = -1; + +// Size of each value +static int FLAGS_value_size = 100; + +// Print histogram of operation timings +static bool FLAGS_histogram = false; + +// Arrange to generate values that shrink to this fraction of +// their original size after compression +static double FLAGS_compression_ratio = 0.5; + +// Page size. Default 1 KB. +static int FLAGS_page_size = 1024; + +// Number of pages. +// Default cache size = FLAGS_page_size * FLAGS_num_pages = 4 MB. +static int FLAGS_num_pages = 4096; + +// If true, do not destroy the existing database. If you set this +// flag and also specify a benchmark that wants a fresh database, that +// benchmark will fail. +static bool FLAGS_use_existing_db = false; + +// If true, we allow batch writes to occur +static bool FLAGS_transaction = true; + +// If true, we enable Write-Ahead Logging +static bool FLAGS_WAL_enabled = true; + +// Use the db with the following name. +static const char* FLAGS_db = NULL; + +inline +static void ExecErrorCheck(int status, char *err_msg) { + if (status != SQLITE_OK) { + fprintf(stderr, "SQL error: %s\n", err_msg); + sqlite3_free(err_msg); + exit(1); + } +} + +inline +static void StepErrorCheck(int status) { + if (status != SQLITE_DONE) { + fprintf(stderr, "SQL step error: status = %d\n", status); + exit(1); + } +} + +inline +static void ErrorCheck(int status) { + if (status != SQLITE_OK) { + fprintf(stderr, "sqlite3 error: status = %d\n", status); + exit(1); + } +} + +inline +static void WalCheckpoint(sqlite3* db_) { + // Flush all writes to disk + if (FLAGS_WAL_enabled) { + sqlite3_wal_checkpoint_v2(db_, NULL, SQLITE_CHECKPOINT_FULL, NULL, NULL); + } +} + +namespace leveldb { + +// Helper for quickly generating random data. +namespace { +class RandomGenerator { + private: + std::string data_; + int pos_; + + public: + RandomGenerator() { + // We use a limited amount of data over and over again and ensure + // that it is larger than the compression window (32KB), and also + // large enough to serve all typical value sizes we want to write. + Random rnd(301); + std::string piece; + while (data_.size() < 1048576) { + // Add a short fragment that is as compressible as specified + // by FLAGS_compression_ratio. + test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece); + data_.append(piece); + } + pos_ = 0; + } + + Slice Generate(int len) { + if (pos_ + len > data_.size()) { + pos_ = 0; + assert(len < data_.size()); + } + pos_ += len; + return Slice(data_.data() + pos_ - len, len); + } +}; + +static Slice TrimSpace(Slice s) { + int start = 0; + while (start < s.size() && isspace(s[start])) { + start++; + } + int limit = s.size(); + while (limit > start && isspace(s[limit-1])) { + limit--; + } + return Slice(s.data() + start, limit - start); +} + +} // namespace + +class Benchmark { + private: + sqlite3* db_; + int db_num_; + int num_; + int reads_; + double start_; + double last_op_finish_; + int64_t bytes_; + std::string message_; + Histogram hist_; + RandomGenerator gen_; + Random rand_; + + // State kept for progress messages + int done_; + int next_report_; // When to report next + + void PrintHeader() { + const int kKeySize = 16; + PrintEnvironment(); + fprintf(stdout, "Keys: %d bytes each\n", kKeySize); + fprintf(stdout, "Values: %d bytes each\n", FLAGS_value_size); + fprintf(stdout, "Entries: %d\n", num_); + fprintf(stdout, "RawSize: %.1f MB (estimated)\n", + ((static_cast(kKeySize + FLAGS_value_size) * num_) + / 1048576.0)); + PrintWarnings(); + fprintf(stdout, "------------------------------------------------\n"); + } + + void PrintWarnings() { +#if defined(__GNUC__) && !defined(__OPTIMIZE__) + fprintf(stdout, + "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n" + ); +#endif +#ifndef NDEBUG + fprintf(stdout, + "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n"); +#endif + } + + void PrintEnvironment() { + fprintf(stderr, "SQLite: version %s\n", SQLITE_VERSION); + +#if defined(__linux) + time_t now = time(NULL); + fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline + + FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); + if (cpuinfo != NULL) { + char line[1000]; + int num_cpus = 0; + std::string cpu_type; + std::string cache_size; + while (fgets(line, sizeof(line), cpuinfo) != NULL) { + const char* sep = strchr(line, ':'); + if (sep == NULL) { + continue; + } + Slice key = TrimSpace(Slice(line, sep - 1 - line)); + Slice val = TrimSpace(Slice(sep + 1)); + if (key == "model name") { + ++num_cpus; + cpu_type = val.ToString(); + } else if (key == "cache size") { + cache_size = val.ToString(); + } + } + fclose(cpuinfo); + fprintf(stderr, "CPU: %d * %s\n", num_cpus, cpu_type.c_str()); + fprintf(stderr, "CPUCache: %s\n", cache_size.c_str()); + } +#endif + } + + void Start() { + start_ = Env::Default()->NowMicros() * 1e-6; + bytes_ = 0; + message_.clear(); + last_op_finish_ = start_; + hist_.Clear(); + done_ = 0; + next_report_ = 100; + } + + void FinishedSingleOp() { + if (FLAGS_histogram) { + double now = Env::Default()->NowMicros() * 1e-6; + double micros = (now - last_op_finish_) * 1e6; + hist_.Add(micros); + if (micros > 20000) { + fprintf(stderr, "long op: %.1f micros%30s\r", micros, ""); + fflush(stderr); + } + last_op_finish_ = now; + } + + done_++; + if (done_ >= next_report_) { + if (next_report_ < 1000) next_report_ += 100; + else if (next_report_ < 5000) next_report_ += 500; + else if (next_report_ < 10000) next_report_ += 1000; + else if (next_report_ < 50000) next_report_ += 5000; + else if (next_report_ < 100000) next_report_ += 10000; + else if (next_report_ < 500000) next_report_ += 50000; + else next_report_ += 100000; + fprintf(stderr, "... finished %d ops%30s\r", done_, ""); + fflush(stderr); + } + } + + void Stop(const Slice& name) { + double finish = Env::Default()->NowMicros() * 1e-6; + + // Pretend at least one op was done in case we are running a benchmark + // that does not call FinishedSingleOp(). + if (done_ < 1) done_ = 1; + + if (bytes_ > 0) { + char rate[100]; + snprintf(rate, sizeof(rate), "%6.1f MB/s", + (bytes_ / 1048576.0) / (finish - start_)); + if (!message_.empty()) { + message_ = std::string(rate) + " " + message_; + } else { + message_ = rate; + } + } + + fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", + name.ToString().c_str(), + (finish - start_) * 1e6 / done_, + (message_.empty() ? "" : " "), + message_.c_str()); + if (FLAGS_histogram) { + fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str()); + } + fflush(stdout); + } + + public: + enum Order { + SEQUENTIAL, + RANDOM + }; + enum DBState { + FRESH, + EXISTING + }; + + Benchmark() + : db_(NULL), + db_num_(0), + num_(FLAGS_num), + reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), + bytes_(0), + rand_(301) { + std::vector files; + std::string test_dir; + Env::Default()->GetTestDirectory(&test_dir); + Env::Default()->GetChildren(test_dir, &files); + if (!FLAGS_use_existing_db) { + for (int i = 0; i < files.size(); i++) { + if (Slice(files[i]).starts_with("dbbench_sqlite3")) { + std::string file_name(test_dir); + file_name += "/"; + file_name += files[i]; + Env::Default()->DeleteFile(file_name.c_str()); + } + } + } + } + + ~Benchmark() { + int status = sqlite3_close(db_); + ErrorCheck(status); + } + + void Run() { + PrintHeader(); + Open(); + + const char* benchmarks = FLAGS_benchmarks; + while (benchmarks != NULL) { + const char* sep = strchr(benchmarks, ','); + Slice name; + if (sep == NULL) { + name = benchmarks; + benchmarks = NULL; + } else { + name = Slice(benchmarks, sep - benchmarks); + benchmarks = sep + 1; + } + + bytes_ = 0; + Start(); + + bool known = true; + bool write_sync = false; + if (name == Slice("fillseq")) { + Write(write_sync, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1); + WalCheckpoint(db_); + } else if (name == Slice("fillseqbatch")) { + Write(write_sync, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1000); + WalCheckpoint(db_); + } else if (name == Slice("fillrandom")) { + Write(write_sync, RANDOM, FRESH, num_, FLAGS_value_size, 1); + WalCheckpoint(db_); + } else if (name == Slice("fillrandbatch")) { + Write(write_sync, RANDOM, FRESH, num_, FLAGS_value_size, 1000); + WalCheckpoint(db_); + } else if (name == Slice("overwrite")) { + Write(write_sync, RANDOM, EXISTING, num_, FLAGS_value_size, 1); + WalCheckpoint(db_); + } else if (name == Slice("overwritebatch")) { + Write(write_sync, RANDOM, EXISTING, num_, FLAGS_value_size, 1000); + WalCheckpoint(db_); + } else if (name == Slice("fillrandsync")) { + write_sync = true; + Write(write_sync, RANDOM, FRESH, num_ / 100, FLAGS_value_size, 1); + WalCheckpoint(db_); + } else if (name == Slice("fillseqsync")) { + write_sync = true; + Write(write_sync, SEQUENTIAL, FRESH, num_ / 100, FLAGS_value_size, 1); + WalCheckpoint(db_); + } else if (name == Slice("fillrand100K")) { + Write(write_sync, RANDOM, FRESH, num_ / 1000, 100 * 1000, 1); + WalCheckpoint(db_); + } else if (name == Slice("fillseq100K")) { + Write(write_sync, SEQUENTIAL, FRESH, num_ / 1000, 100 * 1000, 1); + WalCheckpoint(db_); + } else if (name == Slice("readseq")) { + ReadSequential(); + } else if (name == Slice("readrandom")) { + Read(RANDOM, 1); + } else if (name == Slice("readrand100K")) { + int n = reads_; + reads_ /= 1000; + Read(RANDOM, 1); + reads_ = n; + } else { + known = false; + if (name != Slice()) { // No error message for empty name + fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str()); + } + } + if (known) { + Stop(name); + } + } + } + + void Open() { + assert(db_ == NULL); + + int status; + char file_name[100]; + char* err_msg = NULL; + db_num_++; + + // Open database + std::string tmp_dir; + Env::Default()->GetTestDirectory(&tmp_dir); + snprintf(file_name, sizeof(file_name), + "%s/dbbench_sqlite3-%d.db", + tmp_dir.c_str(), + db_num_); + status = sqlite3_open(file_name, &db_); + if (status) { + fprintf(stderr, "open error: %s\n", sqlite3_errmsg(db_)); + exit(1); + } + + // Change SQLite cache size + char cache_size[100]; + snprintf(cache_size, sizeof(cache_size), "PRAGMA cache_size = %d", + FLAGS_num_pages); + status = sqlite3_exec(db_, cache_size, NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + + // FLAGS_page_size is defaulted to 1024 + if (FLAGS_page_size != 1024) { + char page_size[100]; + snprintf(page_size, sizeof(page_size), "PRAGMA page_size = %d", + FLAGS_page_size); + status = sqlite3_exec(db_, page_size, NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + } + + // Change journal mode to WAL if WAL enabled flag is on + if (FLAGS_WAL_enabled) { + std::string WAL_stmt = "PRAGMA journal_mode = WAL"; + + // LevelDB's default cache size is a combined 4 MB + std::string WAL_checkpoint = "PRAGMA wal_autocheckpoint = 4096"; + status = sqlite3_exec(db_, WAL_stmt.c_str(), NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + status = sqlite3_exec(db_, WAL_checkpoint.c_str(), NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + } + + // Change locking mode to exclusive and create tables/index for database + std::string locking_stmt = "PRAGMA locking_mode = EXCLUSIVE"; + std::string create_stmt = + "CREATE TABLE test (key blob, value blob, PRIMARY KEY(key))"; + std::string stmt_array[] = { locking_stmt, create_stmt }; + int stmt_array_length = sizeof(stmt_array) / sizeof(std::string); + for (int i = 0; i < stmt_array_length; i++) { + status = sqlite3_exec(db_, stmt_array[i].c_str(), NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + } + } + + void Write(bool write_sync, Order order, DBState state, + int num_entries, int value_size, int entries_per_batch) { + // Create new database if state == FRESH + if (state == FRESH) { + if (FLAGS_use_existing_db) { + message_ = "skipping (--use_existing_db is true)"; + return; + } + sqlite3_close(db_); + db_ = NULL; + Open(); + Start(); + } + + if (num_entries != num_) { + char msg[100]; + snprintf(msg, sizeof(msg), "(%d ops)", num_entries); + message_ = msg; + } + + char* err_msg = NULL; + int status; + + sqlite3_stmt *replace_stmt, *begin_trans_stmt, *end_trans_stmt; + std::string replace_str = "REPLACE INTO test (key, value) VALUES (?, ?)"; + std::string begin_trans_str = "BEGIN TRANSACTION;"; + std::string end_trans_str = "END TRANSACTION;"; + + // Check for synchronous flag in options + std::string sync_stmt = (write_sync) ? "PRAGMA synchronous = FULL" : + "PRAGMA synchronous = OFF"; + status = sqlite3_exec(db_, sync_stmt.c_str(), NULL, NULL, &err_msg); + ExecErrorCheck(status, err_msg); + + // Preparing sqlite3 statements + status = sqlite3_prepare_v2(db_, replace_str.c_str(), -1, + &replace_stmt, NULL); + ErrorCheck(status); + status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1, + &begin_trans_stmt, NULL); + ErrorCheck(status); + status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, + &end_trans_stmt, NULL); + ErrorCheck(status); + + bool transaction = (entries_per_batch > 1); + for (int i = 0; i < num_entries; i += entries_per_batch) { + // Begin write transaction + if (FLAGS_transaction && transaction) { + status = sqlite3_step(begin_trans_stmt); + StepErrorCheck(status); + status = sqlite3_reset(begin_trans_stmt); + ErrorCheck(status); + } + + // Create and execute SQL statements + for (int j = 0; j < entries_per_batch; j++) { + const char* value = gen_.Generate(value_size).data(); + + // Create values for key-value pair + const int k = (order == SEQUENTIAL) ? i + j : + (rand_.Next() % num_entries); + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + + // Bind KV values into replace_stmt + status = sqlite3_bind_blob(replace_stmt, 1, key, 16, SQLITE_STATIC); + ErrorCheck(status); + status = sqlite3_bind_blob(replace_stmt, 2, value, + value_size, SQLITE_STATIC); + ErrorCheck(status); + + // Execute replace_stmt + bytes_ += value_size + strlen(key); + status = sqlite3_step(replace_stmt); + StepErrorCheck(status); + + // Reset SQLite statement for another use + status = sqlite3_clear_bindings(replace_stmt); + ErrorCheck(status); + status = sqlite3_reset(replace_stmt); + ErrorCheck(status); + + FinishedSingleOp(); + } + + // End write transaction + if (FLAGS_transaction && transaction) { + status = sqlite3_step(end_trans_stmt); + StepErrorCheck(status); + status = sqlite3_reset(end_trans_stmt); + ErrorCheck(status); + } + } + + status = sqlite3_finalize(replace_stmt); + ErrorCheck(status); + status = sqlite3_finalize(begin_trans_stmt); + ErrorCheck(status); + status = sqlite3_finalize(end_trans_stmt); + ErrorCheck(status); + } + + void Read(Order order, int entries_per_batch) { + int status; + sqlite3_stmt *read_stmt, *begin_trans_stmt, *end_trans_stmt; + + std::string read_str = "SELECT * FROM test WHERE key = ?"; + std::string begin_trans_str = "BEGIN TRANSACTION;"; + std::string end_trans_str = "END TRANSACTION;"; + + // Preparing sqlite3 statements + status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1, + &begin_trans_stmt, NULL); + ErrorCheck(status); + status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, + &end_trans_stmt, NULL); + ErrorCheck(status); + status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &read_stmt, NULL); + ErrorCheck(status); + + bool transaction = (entries_per_batch > 1); + for (int i = 0; i < reads_; i += entries_per_batch) { + // Begin read transaction + if (FLAGS_transaction && transaction) { + status = sqlite3_step(begin_trans_stmt); + StepErrorCheck(status); + status = sqlite3_reset(begin_trans_stmt); + ErrorCheck(status); + } + + // Create and execute SQL statements + for (int j = 0; j < entries_per_batch; j++) { + // Create key value + char key[100]; + int k = (order == SEQUENTIAL) ? i + j : (rand_.Next() % reads_); + snprintf(key, sizeof(key), "%016d", k); + + // Bind key value into read_stmt + status = sqlite3_bind_blob(read_stmt, 1, key, 16, SQLITE_STATIC); + ErrorCheck(status); + + // Execute read statement + while ((status = sqlite3_step(read_stmt)) == SQLITE_ROW) {} + StepErrorCheck(status); + + // Reset SQLite statement for another use + status = sqlite3_clear_bindings(read_stmt); + ErrorCheck(status); + status = sqlite3_reset(read_stmt); + ErrorCheck(status); + FinishedSingleOp(); + } + + // End read transaction + if (FLAGS_transaction && transaction) { + status = sqlite3_step(end_trans_stmt); + StepErrorCheck(status); + status = sqlite3_reset(end_trans_stmt); + ErrorCheck(status); + } + } + + status = sqlite3_finalize(read_stmt); + ErrorCheck(status); + status = sqlite3_finalize(begin_trans_stmt); + ErrorCheck(status); + status = sqlite3_finalize(end_trans_stmt); + ErrorCheck(status); + } + + void ReadSequential() { + int status; + sqlite3_stmt *pStmt; + std::string read_str = "SELECT * FROM test ORDER BY key"; + + status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &pStmt, NULL); + ErrorCheck(status); + for (int i = 0; i < reads_ && SQLITE_ROW == sqlite3_step(pStmt); i++) { + bytes_ += sqlite3_column_bytes(pStmt, 1) + sqlite3_column_bytes(pStmt, 2); + FinishedSingleOp(); + } + + status = sqlite3_finalize(pStmt); + ErrorCheck(status); + } + +}; + +} // namespace leveldb + +int main(int argc, char** argv) { + std::string default_db_path; + for (int i = 1; i < argc; i++) { + double d; + int n; + char junk; + if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) { + FLAGS_benchmarks = argv[i] + strlen("--benchmarks="); + } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_histogram = n; + } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) { + FLAGS_compression_ratio = d; + } else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_use_existing_db = n; + } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) { + FLAGS_num = n; + } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) { + FLAGS_reads = n; + } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) { + FLAGS_value_size = n; + } else if (leveldb::Slice(argv[i]) == leveldb::Slice("--no_transaction")) { + FLAGS_transaction = false; + } else if (sscanf(argv[i], "--page_size=%d%c", &n, &junk) == 1) { + FLAGS_page_size = n; + } else if (sscanf(argv[i], "--num_pages=%d%c", &n, &junk) == 1) { + FLAGS_num_pages = n; + } else if (sscanf(argv[i], "--WAL_enabled=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_WAL_enabled = n; + } else if (strncmp(argv[i], "--db=", 5) == 0) { + FLAGS_db = argv[i] + 5; + } else { + fprintf(stderr, "Invalid flag '%s'\n", argv[i]); + exit(1); + } + } + + // Choose a location for the test database if none given with --db= + if (FLAGS_db == NULL) { + leveldb::Env::Default()->GetTestDirectory(&default_db_path); + default_db_path += "/dbbench"; + FLAGS_db = default_db_path.c_str(); + } + + leveldb::Benchmark benchmark; + benchmark.Run(); + return 0; +} diff --git a/src/leveldb/doc/bench/db_bench_tree_db.cc b/src/leveldb/doc/bench/db_bench_tree_db.cc new file mode 100644 index 0000000..ed86f03 --- /dev/null +++ b/src/leveldb/doc/bench/db_bench_tree_db.cc @@ -0,0 +1,528 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include +#include "util/histogram.h" +#include "util/random.h" +#include "util/testutil.h" + +// Comma-separated list of operations to run in the specified order +// Actual benchmarks: +// +// fillseq -- write N values in sequential key order in async mode +// fillrandom -- write N values in random key order in async mode +// overwrite -- overwrite N values in random key order in async mode +// fillseqsync -- write N/100 values in sequential key order in sync mode +// fillrandsync -- write N/100 values in random key order in sync mode +// fillrand100K -- write N/1000 100K values in random order in async mode +// fillseq100K -- write N/1000 100K values in seq order in async mode +// readseq -- read N times sequentially +// readseq100K -- read N/1000 100K values in sequential order in async mode +// readrand100K -- read N/1000 100K values in sequential order in async mode +// readrandom -- read N times in random order +static const char* FLAGS_benchmarks = + "fillseq," + "fillseqsync," + "fillrandsync," + "fillrandom," + "overwrite," + "readrandom," + "readseq," + "fillrand100K," + "fillseq100K," + "readseq100K," + "readrand100K," + ; + +// Number of key/values to place in database +static int FLAGS_num = 1000000; + +// Number of read operations to do. If negative, do FLAGS_num reads. +static int FLAGS_reads = -1; + +// Size of each value +static int FLAGS_value_size = 100; + +// Arrange to generate values that shrink to this fraction of +// their original size after compression +static double FLAGS_compression_ratio = 0.5; + +// Print histogram of operation timings +static bool FLAGS_histogram = false; + +// Cache size. Default 4 MB +static int FLAGS_cache_size = 4194304; + +// Page size. Default 1 KB +static int FLAGS_page_size = 1024; + +// If true, do not destroy the existing database. If you set this +// flag and also specify a benchmark that wants a fresh database, that +// benchmark will fail. +static bool FLAGS_use_existing_db = false; + +// Compression flag. If true, compression is on. If false, compression +// is off. +static bool FLAGS_compression = true; + +// Use the db with the following name. +static const char* FLAGS_db = NULL; + +inline +static void DBSynchronize(kyotocabinet::TreeDB* db_) +{ + // Synchronize will flush writes to disk + if (!db_->synchronize()) { + fprintf(stderr, "synchronize error: %s\n", db_->error().name()); + } +} + +namespace leveldb { + +// Helper for quickly generating random data. +namespace { +class RandomGenerator { + private: + std::string data_; + int pos_; + + public: + RandomGenerator() { + // We use a limited amount of data over and over again and ensure + // that it is larger than the compression window (32KB), and also + // large enough to serve all typical value sizes we want to write. + Random rnd(301); + std::string piece; + while (data_.size() < 1048576) { + // Add a short fragment that is as compressible as specified + // by FLAGS_compression_ratio. + test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece); + data_.append(piece); + } + pos_ = 0; + } + + Slice Generate(int len) { + if (pos_ + len > data_.size()) { + pos_ = 0; + assert(len < data_.size()); + } + pos_ += len; + return Slice(data_.data() + pos_ - len, len); + } +}; + +static Slice TrimSpace(Slice s) { + int start = 0; + while (start < s.size() && isspace(s[start])) { + start++; + } + int limit = s.size(); + while (limit > start && isspace(s[limit-1])) { + limit--; + } + return Slice(s.data() + start, limit - start); +} + +} // namespace + +class Benchmark { + private: + kyotocabinet::TreeDB* db_; + int db_num_; + int num_; + int reads_; + double start_; + double last_op_finish_; + int64_t bytes_; + std::string message_; + Histogram hist_; + RandomGenerator gen_; + Random rand_; + kyotocabinet::LZOCompressor comp_; + + // State kept for progress messages + int done_; + int next_report_; // When to report next + + void PrintHeader() { + const int kKeySize = 16; + PrintEnvironment(); + fprintf(stdout, "Keys: %d bytes each\n", kKeySize); + fprintf(stdout, "Values: %d bytes each (%d bytes after compression)\n", + FLAGS_value_size, + static_cast(FLAGS_value_size * FLAGS_compression_ratio + 0.5)); + fprintf(stdout, "Entries: %d\n", num_); + fprintf(stdout, "RawSize: %.1f MB (estimated)\n", + ((static_cast(kKeySize + FLAGS_value_size) * num_) + / 1048576.0)); + fprintf(stdout, "FileSize: %.1f MB (estimated)\n", + (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) + / 1048576.0)); + PrintWarnings(); + fprintf(stdout, "------------------------------------------------\n"); + } + + void PrintWarnings() { +#if defined(__GNUC__) && !defined(__OPTIMIZE__) + fprintf(stdout, + "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n" + ); +#endif +#ifndef NDEBUG + fprintf(stdout, + "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n"); +#endif + } + + void PrintEnvironment() { + fprintf(stderr, "Kyoto Cabinet: version %s, lib ver %d, lib rev %d\n", + kyotocabinet::VERSION, kyotocabinet::LIBVER, kyotocabinet::LIBREV); + +#if defined(__linux) + time_t now = time(NULL); + fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline + + FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); + if (cpuinfo != NULL) { + char line[1000]; + int num_cpus = 0; + std::string cpu_type; + std::string cache_size; + while (fgets(line, sizeof(line), cpuinfo) != NULL) { + const char* sep = strchr(line, ':'); + if (sep == NULL) { + continue; + } + Slice key = TrimSpace(Slice(line, sep - 1 - line)); + Slice val = TrimSpace(Slice(sep + 1)); + if (key == "model name") { + ++num_cpus; + cpu_type = val.ToString(); + } else if (key == "cache size") { + cache_size = val.ToString(); + } + } + fclose(cpuinfo); + fprintf(stderr, "CPU: %d * %s\n", num_cpus, cpu_type.c_str()); + fprintf(stderr, "CPUCache: %s\n", cache_size.c_str()); + } +#endif + } + + void Start() { + start_ = Env::Default()->NowMicros() * 1e-6; + bytes_ = 0; + message_.clear(); + last_op_finish_ = start_; + hist_.Clear(); + done_ = 0; + next_report_ = 100; + } + + void FinishedSingleOp() { + if (FLAGS_histogram) { + double now = Env::Default()->NowMicros() * 1e-6; + double micros = (now - last_op_finish_) * 1e6; + hist_.Add(micros); + if (micros > 20000) { + fprintf(stderr, "long op: %.1f micros%30s\r", micros, ""); + fflush(stderr); + } + last_op_finish_ = now; + } + + done_++; + if (done_ >= next_report_) { + if (next_report_ < 1000) next_report_ += 100; + else if (next_report_ < 5000) next_report_ += 500; + else if (next_report_ < 10000) next_report_ += 1000; + else if (next_report_ < 50000) next_report_ += 5000; + else if (next_report_ < 100000) next_report_ += 10000; + else if (next_report_ < 500000) next_report_ += 50000; + else next_report_ += 100000; + fprintf(stderr, "... finished %d ops%30s\r", done_, ""); + fflush(stderr); + } + } + + void Stop(const Slice& name) { + double finish = Env::Default()->NowMicros() * 1e-6; + + // Pretend at least one op was done in case we are running a benchmark + // that does not call FinishedSingleOp(). + if (done_ < 1) done_ = 1; + + if (bytes_ > 0) { + char rate[100]; + snprintf(rate, sizeof(rate), "%6.1f MB/s", + (bytes_ / 1048576.0) / (finish - start_)); + if (!message_.empty()) { + message_ = std::string(rate) + " " + message_; + } else { + message_ = rate; + } + } + + fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", + name.ToString().c_str(), + (finish - start_) * 1e6 / done_, + (message_.empty() ? "" : " "), + message_.c_str()); + if (FLAGS_histogram) { + fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str()); + } + fflush(stdout); + } + + public: + enum Order { + SEQUENTIAL, + RANDOM + }; + enum DBState { + FRESH, + EXISTING + }; + + Benchmark() + : db_(NULL), + num_(FLAGS_num), + reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), + bytes_(0), + rand_(301) { + std::vector files; + std::string test_dir; + Env::Default()->GetTestDirectory(&test_dir); + Env::Default()->GetChildren(test_dir.c_str(), &files); + if (!FLAGS_use_existing_db) { + for (int i = 0; i < files.size(); i++) { + if (Slice(files[i]).starts_with("dbbench_polyDB")) { + std::string file_name(test_dir); + file_name += "/"; + file_name += files[i]; + Env::Default()->DeleteFile(file_name.c_str()); + } + } + } + } + + ~Benchmark() { + if (!db_->close()) { + fprintf(stderr, "close error: %s\n", db_->error().name()); + } + } + + void Run() { + PrintHeader(); + Open(false); + + const char* benchmarks = FLAGS_benchmarks; + while (benchmarks != NULL) { + const char* sep = strchr(benchmarks, ','); + Slice name; + if (sep == NULL) { + name = benchmarks; + benchmarks = NULL; + } else { + name = Slice(benchmarks, sep - benchmarks); + benchmarks = sep + 1; + } + + Start(); + + bool known = true; + bool write_sync = false; + if (name == Slice("fillseq")) { + Write(write_sync, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1); + + } else if (name == Slice("fillrandom")) { + Write(write_sync, RANDOM, FRESH, num_, FLAGS_value_size, 1); + DBSynchronize(db_); + } else if (name == Slice("overwrite")) { + Write(write_sync, RANDOM, EXISTING, num_, FLAGS_value_size, 1); + DBSynchronize(db_); + } else if (name == Slice("fillrandsync")) { + write_sync = true; + Write(write_sync, RANDOM, FRESH, num_ / 100, FLAGS_value_size, 1); + DBSynchronize(db_); + } else if (name == Slice("fillseqsync")) { + write_sync = true; + Write(write_sync, SEQUENTIAL, FRESH, num_ / 100, FLAGS_value_size, 1); + DBSynchronize(db_); + } else if (name == Slice("fillrand100K")) { + Write(write_sync, RANDOM, FRESH, num_ / 1000, 100 * 1000, 1); + DBSynchronize(db_); + } else if (name == Slice("fillseq100K")) { + Write(write_sync, SEQUENTIAL, FRESH, num_ / 1000, 100 * 1000, 1); + DBSynchronize(db_); + } else if (name == Slice("readseq")) { + ReadSequential(); + } else if (name == Slice("readrandom")) { + ReadRandom(); + } else if (name == Slice("readrand100K")) { + int n = reads_; + reads_ /= 1000; + ReadRandom(); + reads_ = n; + } else if (name == Slice("readseq100K")) { + int n = reads_; + reads_ /= 1000; + ReadSequential(); + reads_ = n; + } else { + known = false; + if (name != Slice()) { // No error message for empty name + fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str()); + } + } + if (known) { + Stop(name); + } + } + } + + private: + void Open(bool sync) { + assert(db_ == NULL); + + // Initialize db_ + db_ = new kyotocabinet::TreeDB(); + char file_name[100]; + db_num_++; + std::string test_dir; + Env::Default()->GetTestDirectory(&test_dir); + snprintf(file_name, sizeof(file_name), + "%s/dbbench_polyDB-%d.kct", + test_dir.c_str(), + db_num_); + + // Create tuning options and open the database + int open_options = kyotocabinet::PolyDB::OWRITER | + kyotocabinet::PolyDB::OCREATE; + int tune_options = kyotocabinet::TreeDB::TSMALL | + kyotocabinet::TreeDB::TLINEAR; + if (FLAGS_compression) { + tune_options |= kyotocabinet::TreeDB::TCOMPRESS; + db_->tune_compressor(&comp_); + } + db_->tune_options(tune_options); + db_->tune_page_cache(FLAGS_cache_size); + db_->tune_page(FLAGS_page_size); + db_->tune_map(256LL<<20); + if (sync) { + open_options |= kyotocabinet::PolyDB::OAUTOSYNC; + } + if (!db_->open(file_name, open_options)) { + fprintf(stderr, "open error: %s\n", db_->error().name()); + } + } + + void Write(bool sync, Order order, DBState state, + int num_entries, int value_size, int entries_per_batch) { + // Create new database if state == FRESH + if (state == FRESH) { + if (FLAGS_use_existing_db) { + message_ = "skipping (--use_existing_db is true)"; + return; + } + delete db_; + db_ = NULL; + Open(sync); + Start(); // Do not count time taken to destroy/open + } + + if (num_entries != num_) { + char msg[100]; + snprintf(msg, sizeof(msg), "(%d ops)", num_entries); + message_ = msg; + } + + // Write to database + for (int i = 0; i < num_entries; i++) + { + const int k = (order == SEQUENTIAL) ? i : (rand_.Next() % num_entries); + char key[100]; + snprintf(key, sizeof(key), "%016d", k); + bytes_ += value_size + strlen(key); + std::string cpp_key = key; + if (!db_->set(cpp_key, gen_.Generate(value_size).ToString())) { + fprintf(stderr, "set error: %s\n", db_->error().name()); + } + FinishedSingleOp(); + } + } + + void ReadSequential() { + kyotocabinet::DB::Cursor* cur = db_->cursor(); + cur->jump(); + std::string ckey, cvalue; + while (cur->get(&ckey, &cvalue, true)) { + bytes_ += ckey.size() + cvalue.size(); + FinishedSingleOp(); + } + delete cur; + } + + void ReadRandom() { + std::string value; + for (int i = 0; i < reads_; i++) { + char key[100]; + const int k = rand_.Next() % reads_; + snprintf(key, sizeof(key), "%016d", k); + db_->get(key, &value); + FinishedSingleOp(); + } + } +}; + +} // namespace leveldb + +int main(int argc, char** argv) { + std::string default_db_path; + for (int i = 1; i < argc; i++) { + double d; + int n; + char junk; + if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) { + FLAGS_benchmarks = argv[i] + strlen("--benchmarks="); + } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) { + FLAGS_compression_ratio = d; + } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_histogram = n; + } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) { + FLAGS_num = n; + } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) { + FLAGS_reads = n; + } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) { + FLAGS_value_size = n; + } else if (sscanf(argv[i], "--cache_size=%d%c", &n, &junk) == 1) { + FLAGS_cache_size = n; + } else if (sscanf(argv[i], "--page_size=%d%c", &n, &junk) == 1) { + FLAGS_page_size = n; + } else if (sscanf(argv[i], "--compression=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_compression = (n == 1) ? true : false; + } else if (strncmp(argv[i], "--db=", 5) == 0) { + FLAGS_db = argv[i] + 5; + } else { + fprintf(stderr, "Invalid flag '%s'\n", argv[i]); + exit(1); + } + } + + // Choose a location for the test database if none given with --db= + if (FLAGS_db == NULL) { + leveldb::Env::Default()->GetTestDirectory(&default_db_path); + default_db_path += "/dbbench"; + FLAGS_db = default_db_path.c_str(); + } + + leveldb::Benchmark benchmark; + benchmark.Run(); + return 0; +} diff --git a/src/leveldb/doc/benchmark.html b/src/leveldb/doc/benchmark.html new file mode 100644 index 0000000..c463977 --- /dev/null +++ b/src/leveldb/doc/benchmark.html @@ -0,0 +1,459 @@ + + + +LevelDB Benchmarks + + + + +

LevelDB Benchmarks

+

Google, July 2011

+
+ +

In order to test LevelDB's performance, we benchmark it against other well-established database implementations. We compare LevelDB (revision 39) against SQLite3 (version 3.7.6.3) and Kyoto Cabinet's (version 1.2.67) TreeDB (a B+Tree based key-value store). We would like to acknowledge Scott Hess and Mikio Hirabayashi for their suggestions and contributions to the SQLite3 and Kyoto Cabinet benchmarks, respectively.

+ +

Benchmarks were all performed on a six-core Intel(R) Xeon(R) CPU X5650 @ 2.67GHz, with 12288 KB of total L3 cache and 12 GB of DDR3 RAM at 1333 MHz. (Note that LevelDB uses at most two CPUs since the benchmarks are single threaded: one to run the benchmark, and one for background compactions.) We ran the benchmarks on two machines (with identical processors), one with an Ext3 file system and one with an Ext4 file system. The machine with the Ext3 file system has a SATA Hitachi HDS721050CLA362 hard drive. The machine with the Ext4 file system has a SATA Samsung HD502HJ hard drive. Both hard drives spin at 7200 RPM and have hard drive write-caching enabled (using `hdparm -W 1 [device]`). The numbers reported below are the median of three measurements.

+ +

Benchmark Source Code

+

We wrote benchmark tools for SQLite and Kyoto TreeDB based on LevelDB's db_bench. The code for each of the benchmarks resides here:

+ + +

Custom Build Specifications

+
    +
  • LevelDB: LevelDB was compiled with the tcmalloc library and the Snappy compression library (revision 33). Assertions were disabled.
  • +
  • TreeDB: TreeDB was compiled using the LZO compression library (version 2.03). Furthermore, we enabled the TSMALL and TLINEAR options when opening the database in order to reduce the footprint of each record.
  • +
  • SQLite: We tuned SQLite's performance, by setting its locking mode to exclusive. We also enabled SQLite's write-ahead logging.
  • +
+ +

1. Baseline Performance

+

This section gives the baseline performance of all the +databases. Following sections show how performance changes as various +parameters are varied. For the baseline:

+
    +
  • Each database is allowed 4 MB of cache memory.
  • +
  • Databases are opened in asynchronous write mode. + (LevelDB's sync option, TreeDB's OAUTOSYNC option, and + SQLite3's synchronous options are all turned off). I.e., + every write is pushed to the operating system, but the + benchmark does not wait for the write to reach the disk.
  • +
  • Keys are 16 bytes each.
  • +
  • Value are 100 bytes each (with enough redundancy so that + a simple compressor shrinks them to 50% of their original + size).
  • +
  • Sequential reads/writes traverse the key space in increasing order.
  • +
  • Random reads/writes traverse the key space in random order.
  • +
+ +

A. Sequential Reads

+ + + + + + + + + + +
LevelDB4,030,000 ops/sec
 
Kyoto TreeDB1,010,000 ops/sec
 
SQLite3383,000 ops/sec
 
+

B. Random Reads

+ + + + + + + + + + +
LevelDB129,000 ops/sec
 
Kyoto TreeDB151,000 ops/sec
 
SQLite3134,000 ops/sec
 
+

C. Sequential Writes

+ + + + + + + + + + +
LevelDB779,000 ops/sec
 
Kyoto TreeDB342,000 ops/sec
 
SQLite348,600 ops/sec
 
+

D. Random Writes

+ + + + + + + + + + +
LevelDB164,000 ops/sec
 
Kyoto TreeDB88,500 ops/sec
 
SQLite39,860 ops/sec
 
+ +

LevelDB outperforms both SQLite3 and TreeDB in sequential and random write operations and sequential read operations. Kyoto Cabinet has the fastest random read operations.

+ +

2. Write Performance under Different Configurations

+

A. Large Values

+

For this benchmark, we start with an empty database, and write 100,000 byte values (~50% compressible). To keep the benchmark running time reasonable, we stop after writing 1000 values.

+

Sequential Writes

+ + + + + + + + + + +
LevelDB1,100 ops/sec
 
Kyoto TreeDB1,000 ops/sec
 
SQLite31,600 ops/sec
 
+

Random Writes

+ + + + + + + + + + +
LevelDB480 ops/sec
 
Kyoto TreeDB1,100 ops/sec
 
SQLite31,600 ops/sec
 
+

LevelDB doesn't perform as well with large values of 100,000 bytes each. This is because LevelDB writes keys and values at least twice: first time to the transaction log, and second time (during a compaction) to a sorted file. +With larger values, LevelDB's per-operation efficiency is swamped by the +cost of extra copies of large values.

+

B. Batch Writes

+

A batch write is a set of writes that are applied atomically to the underlying database. A single batch of N writes may be significantly faster than N individual writes. The following benchmark writes one thousand batches where each batch contains one thousand 100-byte values. TreeDB does not support batch writes and is omitted from this benchmark.

+

Sequential Writes

+ + + + + + + + + +
LevelDB840,000 entries/sec
 
(1.08x baseline)
SQLite3124,000 entries/sec
 
(2.55x baseline)
+

Random Writes

+ + + + + + + + + +
LevelDB221,000 entries/sec
 
(1.35x baseline)
SQLite322,000 entries/sec
 
(2.23x baseline)
+ +

Because of the way LevelDB persistent storage is organized, batches of +random writes are not much slower (only a factor of 4x) than batches +of sequential writes.

+ +

C. Synchronous Writes

+

In the following benchmark, we enable the synchronous writing modes +of all of the databases. Since this change significantly slows down the +benchmark, we stop after 10,000 writes. For synchronous write tests, we've +disabled hard drive write-caching (using `hdparm -W 0 [device]`).

+
    +
  • For LevelDB, we set WriteOptions.sync = true.
  • +
  • In TreeDB, we enabled TreeDB's OAUTOSYNC option.
  • +
  • For SQLite3, we set "PRAGMA synchronous = FULL".
  • +
+

Sequential Writes

+ + + + + + + + + + + + + +
LevelDB100 ops/sec
 
(0.003x baseline)
Kyoto TreeDB7 ops/sec
 
(0.0004x baseline)
SQLite388 ops/sec
 
(0.002x baseline)
+

Random Writes

+ + + + + + + + + + + + + +
LevelDB100 ops/sec
 
(0.015x baseline)
Kyoto TreeDB8 ops/sec
 
(0.001x baseline)
SQLite388 ops/sec
 
(0.009x baseline)
+ +

Also see the ext4 performance numbers below +since synchronous writes behave significantly differently +on ext3 and ext4.

+ +

D. Turning Compression Off

+ +

In the baseline measurements, LevelDB and TreeDB were using +light-weight compression +(Snappy for LevelDB, +and LZO for +TreeDB). SQLite3, by default does not use compression. The +experiments below show what happens when compression is disabled in +all of the databases (the SQLite3 numbers are just a copy of +its baseline measurements):

+ +

Sequential Writes

+ + + + + + + + + + + + + +
LevelDB594,000 ops/sec
 
(0.76x baseline)
Kyoto TreeDB485,000 ops/sec
 
(1.42x baseline)
SQLite348,600 ops/sec
 
(1.00x baseline)
+

Random Writes

+ + + + + + + + + + + + + +
LevelDB135,000 ops/sec
 
(0.82x baseline)
Kyoto TreeDB159,000 ops/sec
 
(1.80x baseline)
SQLite39,860 ops/sec
 
(1.00x baseline)
+ +

LevelDB's write performance is better with compression than without +since compression decreases the amount of data that has to be written +to disk. Therefore LevelDB users can leave compression enabled in +most scenarios without having worry about a tradeoff between space +usage and performance. TreeDB's performance on the other hand is +better without compression than with compression. Presumably this is +because TreeDB's compression library (LZO) is more expensive than +LevelDB's compression library (Snappy).

+ +

E. Using More Memory

+

We increased the overall cache size for each database to 128 MB. For LevelDB, we partitioned 128 MB into a 120 MB write buffer and 8 MB of cache (up from 2 MB of write buffer and 2 MB of cache). For SQLite3, we kept the page size at 1024 bytes, but increased the number of pages to 131,072 (up from 4096). For TreeDB, we also kept the page size at 1024 bytes, but increased the cache size to 128 MB (up from 4 MB).

+

Sequential Writes

+ + + + + + + + + + + + + +
LevelDB812,000 ops/sec
 
(1.04x baseline)
Kyoto TreeDB321,000 ops/sec
 
(0.94x baseline)
SQLite348,500 ops/sec
 
(1.00x baseline)
+

Random Writes

+ + + + + + + + + + + + + +
LevelDB355,000 ops/sec
 
(2.16x baseline)
Kyoto TreeDB284,000 ops/sec
 
(3.21x baseline)
SQLite39,670 ops/sec
 
(0.98x baseline)
+ +

SQLite's performance does not change substantially when compared to +the baseline, but the random write performance for both LevelDB and +TreeDB increases significantly. LevelDB's performance improves +because a larger write buffer reduces the need to merge sorted files +(since it creates a smaller number of larger sorted files). TreeDB's +performance goes up because the entire database is available in memory +for fast in-place updates.

+ +

3. Read Performance under Different Configurations

+

A. Larger Caches

+

We increased the overall memory usage to 128 MB for each database. +For LevelDB, we allocated 8 MB to LevelDB's write buffer and 120 MB +to LevelDB's cache. The other databases don't differentiate between a +write buffer and a cache, so we simply set their cache size to 128 +MB.

+

Sequential Reads

+ + + + + + + + + + + + + +
LevelDB5,210,000 ops/sec
 
(1.29x baseline)
Kyoto TreeDB1,070,000 ops/sec
 
(1.06x baseline)
SQLite3609,000 ops/sec
 
(1.59x baseline)
+ +

Random Reads

+ + + + + + + + + + + + + +
LevelDB190,000 ops/sec
 
(1.47x baseline)
Kyoto TreeDB463,000 ops/sec
 
(3.07x baseline)
SQLite3186,000 ops/sec
 
(1.39x baseline)
+ +

As expected, the read performance of all of the databases increases +when the caches are enlarged. In particular, TreeDB seems to make +very effective use of a cache that is large enough to hold the entire +database.

+ +

B. No Compression Reads

+

For this benchmark, we populated a database with 1 million entries consisting of 16 byte keys and 100 byte values. We compiled LevelDB and Kyoto Cabinet without compression support, so results that are read out from the database are already uncompressed. We've listed the SQLite3 baseline read performance as a point of comparison.

+

Sequential Reads

+ + + + + + + + + + + + + +
LevelDB4,880,000 ops/sec
 
(1.21x baseline)
Kyoto TreeDB1,230,000 ops/sec
 
(3.60x baseline)
SQLite3383,000 ops/sec
 
(1.00x baseline)
+

Random Reads

+ + + + + + + + + + + + + +
LevelDB149,000 ops/sec
 
(1.16x baseline)
Kyoto TreeDB175,000 ops/sec
 
(1.16x baseline)
SQLite3134,000 ops/sec
 
(1.00x baseline)
+ +

Performance of both LevelDB and TreeDB improves a small amount when +compression is disabled. Note however that under different workloads, +performance may very well be better with compression if it allows more +of the working set to fit in memory.

+ +

Note about Ext4 Filesystems

+

The preceding numbers are for an ext3 file system. Synchronous writes are much slower under ext4 (LevelDB drops to ~31 writes / second and TreeDB drops to ~5 writes / second; SQLite3's synchronous writes do not noticeably drop) due to ext4's different handling of fsync / msync calls. Even LevelDB's asynchronous write performance drops somewhat since it spreads its storage across multiple files and issues fsync calls when switching to a new file.

+ +

Acknowledgements

+

Jeff Dean and Sanjay Ghemawat wrote LevelDB. Kevin Tseng wrote and compiled these benchmarks. Mikio Hirabayashi, Scott Hess, and Gabor Cselle provided help and advice.

+ + diff --git a/src/leveldb/doc/doc.css b/src/leveldb/doc/doc.css new file mode 100644 index 0000000..700c564 --- /dev/null +++ b/src/leveldb/doc/doc.css @@ -0,0 +1,89 @@ +body { + margin-left: 0.5in; + margin-right: 0.5in; + background: white; + color: black; +} + +h1 { + margin-left: -0.2in; + font-size: 14pt; +} +h2 { + margin-left: -0in; + font-size: 12pt; +} +h3 { + margin-left: -0in; +} +h4 { + margin-left: -0in; +} +hr { + margin-left: -0in; +} + +/* Definition lists: definition term bold */ +dt { + font-weight: bold; +} + +address { + text-align: center; +} +code,samp,var { + color: blue; +} +kbd { + color: #600000; +} +div.note p { + float: right; + width: 3in; + margin-right: 0%; + padding: 1px; + border: 2px solid #6060a0; + background-color: #fffff0; +} + +ul { + margin-top: -0em; + margin-bottom: -0em; +} + +ol { + margin-top: -0em; + margin-bottom: -0em; +} + +UL.nobullets { + list-style-type: none; + list-style-image: none; + margin-left: -1em; +} + +p { + margin: 1em 0 1em 0; + padding: 0 0 0 0; +} + +pre { + line-height: 1.3em; + padding: 0.4em 0 0.8em 0; + margin: 0 0 0 0; + border: 0 0 0 0; + color: blue; +} + +.datatable { + margin-left: auto; + margin-right: auto; + margin-top: 2em; + margin-bottom: 2em; + border: 1px solid; +} + +.datatable td,th { + padding: 0 0.5em 0 0.5em; + text-align: right; +} diff --git a/src/leveldb/doc/impl.html b/src/leveldb/doc/impl.html new file mode 100644 index 0000000..e870795 --- /dev/null +++ b/src/leveldb/doc/impl.html @@ -0,0 +1,213 @@ + + + + +Leveldb file layout and compactions + + + + +

Files

+ +The implementation of leveldb is similar in spirit to the +representation of a single + +Bigtable tablet (section 5.3). +However the organization of the files that make up the representation +is somewhat different and is explained below. + +

+Each database is represented by a set of files stored in a directory. +There are several different types of files as documented below: +

+

Log files

+

+A log file (*.log) stores a sequence of recent updates. Each update +is appended to the current log file. When the log file reaches a +pre-determined size (approximately 4MB by default), it is converted +to a sorted table (see below) and a new log file is created for future +updates. +

+A copy of the current log file is kept in an in-memory structure (the +memtable). This copy is consulted on every read so that read +operations reflect all logged updates. +

+

Sorted tables

+

+A sorted table (*.sst) stores a sequence of entries sorted by key. +Each entry is either a value for the key, or a deletion marker for the +key. (Deletion markers are kept around to hide obsolete values +present in older sorted tables). +

+The set of sorted tables are organized into a sequence of levels. The +sorted table generated from a log file is placed in a special young +level (also called level-0). When the number of young files exceeds a +certain threshold (currently four), all of the young files are merged +together with all of the overlapping level-1 files to produce a +sequence of new level-1 files (we create a new level-1 file for every +2MB of data.) +

+Files in the young level may contain overlapping keys. However files +in other levels have distinct non-overlapping key ranges. Consider +level number L where L >= 1. When the combined size of files in +level-L exceeds (10^L) MB (i.e., 10MB for level-1, 100MB for level-2, +...), one file in level-L, and all of the overlapping files in +level-(L+1) are merged to form a set of new files for level-(L+1). +These merges have the effect of gradually migrating new updates from +the young level to the largest level using only bulk reads and writes +(i.e., minimizing expensive seeks). + +

Manifest

+

+A MANIFEST file lists the set of sorted tables that make up each +level, the corresponding key ranges, and other important metadata. +A new MANIFEST file (with a new number embedded in the file name) +is created whenever the database is reopened. The MANIFEST file is +formatted as a log, and changes made to the serving state (as files +are added or removed) are appended to this log. +

+

Current

+

+CURRENT is a simple text file that contains the name of the latest +MANIFEST file. +

+

Info logs

+

+Informational messages are printed to files named LOG and LOG.old. +

+

Others

+

+Other files used for miscellaneous purposes may also be present +(LOCK, *.dbtmp). + +

Level 0

+When the log file grows above a certain size (1MB by default): +
    +
  • Create a brand new memtable and log file and direct future updates here +
  • In the background: +
      +
    • Write the contents of the previous memtable to an sstable +
    • Discard the memtable +
    • Delete the old log file and the old memtable +
    • Add the new sstable to the young (level-0) level. +
    +
+ +

Compactions

+ +

+When the size of level L exceeds its limit, we compact it in a +background thread. The compaction picks a file from level L and all +overlapping files from the next level L+1. Note that if a level-L +file overlaps only part of a level-(L+1) file, the entire file at +level-(L+1) is used as an input to the compaction and will be +discarded after the compaction. Aside: because level-0 is special +(files in it may overlap each other), we treat compactions from +level-0 to level-1 specially: a level-0 compaction may pick more than +one level-0 file in case some of these files overlap each other. + +

+A compaction merges the contents of the picked files to produce a +sequence of level-(L+1) files. We switch to producing a new +level-(L+1) file after the current output file has reached the target +file size (2MB). We also switch to a new output file when the key +range of the current output file has grown enough to overlap more then +ten level-(L+2) files. This last rule ensures that a later compaction +of a level-(L+1) file will not pick up too much data from level-(L+2). + +

+The old files are discarded and the new files are added to the serving +state. + +

+Compactions for a particular level rotate through the key space. In +more detail, for each level L, we remember the ending key of the last +compaction at level L. The next compaction for level L will pick the +first file that starts after this key (wrapping around to the +beginning of the key space if there is no such file). + +

+Compactions drop overwritten values. They also drop deletion markers +if there are no higher numbered levels that contain a file whose range +overlaps the current key. + +

Timing

+ +Level-0 compactions will read up to four 1MB files from level-0, and +at worst all the level-1 files (10MB). I.e., we will read 14MB and +write 14MB. + +

+Other than the special level-0 compactions, we will pick one 2MB file +from level L. In the worst case, this will overlap ~ 12 files from +level L+1 (10 because level-(L+1) is ten times the size of level-L, +and another two at the boundaries since the file ranges at level-L +will usually not be aligned with the file ranges at level-L+1). The +compaction will therefore read 26MB and write 26MB. Assuming a disk +IO rate of 100MB/s (ballpark range for modern drives), the worst +compaction cost will be approximately 0.5 second. + +

+If we throttle the background writing to something small, say 10% of +the full 100MB/s speed, a compaction may take up to 5 seconds. If the +user is writing at 10MB/s, we might build up lots of level-0 files +(~50 to hold the 5*10MB). This may signficantly increase the cost of +reads due to the overhead of merging more files together on every +read. + +

+Solution 1: To reduce this problem, we might want to increase the log +switching threshold when the number of level-0 files is large. Though +the downside is that the larger this threshold, the more memory we will +need to hold the corresponding memtable. + +

+Solution 2: We might want to decrease write rate artificially when the +number of level-0 files goes up. + +

+Solution 3: We work on reducing the cost of very wide merges. +Perhaps most of the level-0 files will have their blocks sitting +uncompressed in the cache and we will only need to worry about the +O(N) complexity in the merging iterator. + +

Number of files

+ +Instead of always making 2MB files, we could make larger files for +larger levels to reduce the total file count, though at the expense of +more bursty compactions. Alternatively, we could shard the set of +files into multiple directories. + +

+An experiment on an ext3 filesystem on Feb 04, 2011 shows +the following timings to do 100K file opens in directories with +varying number of files: + + + + + +
Files in directoryMicroseconds to open a file
10009
1000010
10000016
+So maybe even the sharding is not necessary on modern filesystems? + +

Recovery

+ +
    +
  • Read CURRENT to find name of the latest committed MANIFEST +
  • Read the named MANIFEST file +
  • Clean up stale files +
  • We could open all sstables here, but it is probably better to be lazy... +
  • Convert log chunk to a new level-0 sstable +
  • Start directing new writes to a new log file with recovered sequence# +
+ +

Garbage collection of files

+ +DeleteObsoleteFiles() is called at the end of every +compaction and at the end of recovery. It finds the names of all +files in the database. It deletes all log files that are not the +current log file. It deletes all table files that are not referenced +from some level and are not the output of an active compaction. + + + diff --git a/src/leveldb/doc/index.html b/src/leveldb/doc/index.html new file mode 100644 index 0000000..3ed0ed9 --- /dev/null +++ b/src/leveldb/doc/index.html @@ -0,0 +1,549 @@ + + + + +Leveldb + + + +

Leveldb

+
Jeff Dean, Sanjay Ghemawat
+

+The leveldb library provides a persistent key value store. Keys and +values are arbitrary byte arrays. The keys are ordered within the key +value store according to a user-specified comparator function. + +

+

Opening A Database

+

+A leveldb database has a name which corresponds to a file system +directory. All of the contents of database are stored in this +directory. The following example shows how to open a database, +creating it if necessary: +

+

+  #include <assert>
+  #include "leveldb/db.h"
+
+  leveldb::DB* db;
+  leveldb::Options options;
+  options.create_if_missing = true;
+  leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
+  assert(status.ok());
+  ...
+
+If you want to raise an error if the database already exists, add +the following line before the leveldb::DB::Open call: +
+  options.error_if_exists = true;
+
+

Status

+

+You may have noticed the leveldb::Status type above. Values of this +type are returned by most functions in leveldb that may encounter an +error. You can check if such a result is ok, and also print an +associated error message: +

+

+   leveldb::Status s = ...;
+   if (!s.ok()) cerr << s.ToString() << endl;
+
+

Closing A Database

+

+When you are done with a database, just delete the database object. +Example: +

+

+  ... open the db as described above ...
+  ... do something with db ...
+  delete db;
+
+

Reads And Writes

+

+The database provides Put, Delete, and Get methods to +modify/query the database. For example, the following code +moves the value stored under key1 to key2. +

+  std::string value;
+  leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
+  if (s.ok()) s = db->Put(leveldb::WriteOptions(), key2, value);
+  if (s.ok()) s = db->Delete(leveldb::WriteOptions(), key1);
+
+ +

Atomic Updates

+

+Note that if the process dies after the Put of key2 but before the +delete of key1, the same value may be left stored under multiple keys. +Such problems can be avoided by using the WriteBatch class to +atomically apply a set of updates: +

+

+  #include "leveldb/write_batch.h"
+  ...
+  std::string value;
+  leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
+  if (s.ok()) {
+    leveldb::WriteBatch batch;
+    batch.Delete(key1);
+    batch.Put(key2, value);
+    s = db->Write(leveldb::WriteOptions(), &batch);
+  }
+
+The WriteBatch holds a sequence of edits to be made to the database, +and these edits within the batch are applied in order. Note that we +called Delete before Put so that if key1 is identical to key2, +we do not end up erroneously dropping the value entirely. +

+Apart from its atomicity benefits, WriteBatch may also be used to +speed up bulk updates by placing lots of individual mutations into the +same batch. + +

Synchronous Writes

+By default, each write to leveldb is asynchronous: it +returns after pushing the write from the process into the operating +system. The transfer from operating system memory to the underlying +persistent storage happens asynchronously. The sync flag +can be turned on for a particular write to make the write operation +not return until the data being written has been pushed all the way to +persistent storage. (On Posix systems, this is implemented by calling +either fsync(...) or fdatasync(...) or +msync(..., MS_SYNC) before the write operation returns.) +
+  leveldb::WriteOptions write_options;
+  write_options.sync = true;
+  db->Put(write_options, ...);
+
+Asynchronous writes are often more than a thousand times as fast as +synchronous writes. The downside of asynchronous writes is that a +crash of the machine may cause the last few updates to be lost. Note +that a crash of just the writing process (i.e., not a reboot) will not +cause any loss since even when sync is false, an update +is pushed from the process memory into the operating system before it +is considered done. + +

+Asynchronous writes can often be used safely. For example, when +loading a large amount of data into the database you can handle lost +updates by restarting the bulk load after a crash. A hybrid scheme is +also possible where every Nth write is synchronous, and in the event +of a crash, the bulk load is restarted just after the last synchronous +write finished by the previous run. (The synchronous write can update +a marker that describes where to restart on a crash.) + +

+WriteBatch provides an alternative to asynchronous writes. +Multiple updates may be placed in the same WriteBatch and +applied together using a synchronous write (i.e., +write_options.sync is set to true). The extra cost of +the synchronous write will be amortized across all of the writes in +the batch. + +

+

Concurrency

+

+A database may only be opened by one process at a time. +The leveldb implementation acquires a lock from the +operating system to prevent misuse. Within a single process, the +same leveldb::DB object may be safely shared by multiple +concurrent threads. I.e., different threads may write into or fetch +iterators or call Get on the same database without any +external synchronization (the leveldb implementation will +automatically do the required synchronization). However other objects +(like Iterator and WriteBatch) may require external synchronization. +If two threads share such an object, they must protect access to it +using their own locking protocol. More details are available in +the public header files. +

+

Iteration

+

+The following example demonstrates how to print all key,value pairs +in a database. +

+

+  leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
+  for (it->SeekToFirst(); it->Valid(); it->Next()) {
+    cout << it->key().ToString() << ": "  << it->value().ToString() << endl;
+  }
+  assert(it->status().ok());  // Check for any errors found during the scan
+  delete it;
+
+The following variation shows how to process just the keys in the +range [start,limit): +

+

+  for (it->Seek(start);
+       it->Valid() && it->key().ToString() < limit;
+       it->Next()) {
+    ...
+  }
+
+You can also process entries in reverse order. (Caveat: reverse +iteration may be somewhat slower than forward iteration.) +

+

+  for (it->SeekToLast(); it->Valid(); it->Prev()) {
+    ...
+  }
+
+

Snapshots

+

+Snapshots provide consistent read-only views over the entire state of +the key-value store. ReadOptions::snapshot may be non-NULL to indicate +that a read should operate on a particular version of the DB state. +If ReadOptions::snapshot is NULL, the read will operate on an +implicit snapshot of the current state. +

+Snapshots are created by the DB::GetSnapshot() method: +

+

+  leveldb::ReadOptions options;
+  options.snapshot = db->GetSnapshot();
+  ... apply some updates to db ...
+  leveldb::Iterator* iter = db->NewIterator(options);
+  ... read using iter to view the state when the snapshot was created ...
+  delete iter;
+  db->ReleaseSnapshot(options.snapshot);
+
+Note that when a snapshot is no longer needed, it should be released +using the DB::ReleaseSnapshot interface. This allows the +implementation to get rid of state that was being maintained just to +support reading as of that snapshot. +

Slice

+

+The return value of the it->key() and it->value() calls above +are instances of the leveldb::Slice type. Slice is a simple +structure that contains a length and a pointer to an external byte +array. Returning a Slice is a cheaper alternative to returning a +std::string since we do not need to copy potentially large keys and +values. In addition, leveldb methods do not return null-terminated +C-style strings since leveldb keys and values are allowed to +contain '\0' bytes. +

+C++ strings and null-terminated C-style strings can be easily converted +to a Slice: +

+

+   leveldb::Slice s1 = "hello";
+
+   std::string str("world");
+   leveldb::Slice s2 = str;
+
+A Slice can be easily converted back to a C++ string: +
+   std::string str = s1.ToString();
+   assert(str == std::string("hello"));
+
+Be careful when using Slices since it is up to the caller to ensure that +the external byte array into which the Slice points remains live while +the Slice is in use. For example, the following is buggy: +

+

+   leveldb::Slice slice;
+   if (...) {
+     std::string str = ...;
+     slice = str;
+   }
+   Use(slice);
+
+When the if statement goes out of scope, str will be destroyed and the +backing storage for slice will disappear. +

+

Comparators

+

+The preceding examples used the default ordering function for key, +which orders bytes lexicographically. You can however supply a custom +comparator when opening a database. For example, suppose each +database key consists of two numbers and we should sort by the first +number, breaking ties by the second number. First, define a proper +subclass of leveldb::Comparator that expresses these rules: +

+

+  class TwoPartComparator : public leveldb::Comparator {
+   public:
+    // Three-way comparison function:
+    //   if a < b: negative result
+    //   if a > b: positive result
+    //   else: zero result
+    int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const {
+      int a1, a2, b1, b2;
+      ParseKey(a, &a1, &a2);
+      ParseKey(b, &b1, &b2);
+      if (a1 < b1) return -1;
+      if (a1 > b1) return +1;
+      if (a2 < b2) return -1;
+      if (a2 > b2) return +1;
+      return 0;
+    }
+
+    // Ignore the following methods for now:
+    const char* Name() const { return "TwoPartComparator"; }
+    void FindShortestSeparator(std::string*, const leveldb::Slice&) const { }
+    void FindShortSuccessor(std::string*) const { }
+  };
+
+Now create a database using this custom comparator: +

+

+  TwoPartComparator cmp;
+  leveldb::DB* db;
+  leveldb::Options options;
+  options.create_if_missing = true;
+  options.comparator = &cmp;
+  leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
+  ...
+
+

Backwards compatibility

+

+The result of the comparator's Name method is attached to the +database when it is created, and is checked on every subsequent +database open. If the name changes, the leveldb::DB::Open call will +fail. Therefore, change the name if and only if the new key format +and comparison function are incompatible with existing databases, and +it is ok to discard the contents of all existing databases. +

+You can however still gradually evolve your key format over time with +a little bit of pre-planning. For example, you could store a version +number at the end of each key (one byte should suffice for most uses). +When you wish to switch to a new key format (e.g., adding an optional +third part to the keys processed by TwoPartComparator), +(a) keep the same comparator name (b) increment the version number +for new keys (c) change the comparator function so it uses the +version numbers found in the keys to decide how to interpret them. +

+

Performance

+

+Performance can be tuned by changing the default values of the +types defined in include/leveldb/options.h. + +

+

Block size

+

+leveldb groups adjacent keys together into the same block and such a +block is the unit of transfer to and from persistent storage. The +default block size is approximately 4096 uncompressed bytes. +Applications that mostly do bulk scans over the contents of the +database may wish to increase this size. Applications that do a lot +of point reads of small values may wish to switch to a smaller block +size if performance measurements indicate an improvement. There isn't +much benefit in using blocks smaller than one kilobyte, or larger than +a few megabytes. Also note that compression will be more effective +with larger block sizes. +

+

Compression

+

+Each block is individually compressed before being written to +persistent storage. Compression is on by default since the default +compression method is very fast, and is automatically disabled for +uncompressible data. In rare cases, applications may want to disable +compression entirely, but should only do so if benchmarks show a +performance improvement: +

+

+  leveldb::Options options;
+  options.compression = leveldb::kNoCompression;
+  ... leveldb::DB::Open(options, name, ...) ....
+
+

Cache

+

+The contents of the database are stored in a set of files in the +filesystem and each file stores a sequence of compressed blocks. If +options.cache is non-NULL, it is used to cache frequently used +uncompressed block contents. +

+

+  #include "leveldb/cache.h"
+
+  leveldb::Options options;
+  options.cache = leveldb::NewLRUCache(100 * 1048576);  // 100MB cache
+  leveldb::DB* db;
+  leveldb::DB::Open(options, name, &db);
+  ... use the db ...
+  delete db
+  delete options.cache;
+
+Note that the cache holds uncompressed data, and therefore it should +be sized according to application level data sizes, without any +reduction from compression. (Caching of compressed blocks is left to +the operating system buffer cache, or any custom Env +implementation provided by the client.) +

+When performing a bulk read, the application may wish to disable +caching so that the data processed by the bulk read does not end up +displacing most of the cached contents. A per-iterator option can be +used to achieve this: +

+

+  leveldb::ReadOptions options;
+  options.fill_cache = false;
+  leveldb::Iterator* it = db->NewIterator(options);
+  for (it->SeekToFirst(); it->Valid(); it->Next()) {
+    ...
+  }
+
+

Key Layout

+

+Note that the unit of disk transfer and caching is a block. Adjacent +keys (according to the database sort order) will usually be placed in +the same block. Therefore the application can improve its performance +by placing keys that are accessed together near each other and placing +infrequently used keys in a separate region of the key space. +

+For example, suppose we are implementing a simple file system on top +of leveldb. The types of entries we might wish to store are: +

+

+   filename -> permission-bits, length, list of file_block_ids
+   file_block_id -> data
+
+We might want to prefix filename keys with one letter (say '/') and the +file_block_id keys with a different letter (say '0') so that scans +over just the metadata do not force us to fetch and cache bulky file +contents. +

+

Filters

+

+Because of the way leveldb data is organized on disk, +a single Get() call may involve multiple reads from disk. +The optional FilterPolicy mechanism can be used to reduce +the number of disk reads substantially. +

+   leveldb::Options options;
+   options.filter_policy = NewBloomFilterPolicy(10);
+   leveldb::DB* db;
+   leveldb::DB::Open(options, "/tmp/testdb", &db);
+   ... use the database ...
+   delete db;
+   delete options.filter_policy;
+
+The preceding code associates a +Bloom filter +based filtering policy with the database. Bloom filter based +filtering relies on keeping some number of bits of data in memory per +key (in this case 10 bits per key since that is the argument we passed +to NewBloomFilterPolicy). This filter will reduce the number of unnecessary +disk reads needed for Get() calls by a factor of +approximately a 100. Increasing the bits per key will lead to a +larger reduction at the cost of more memory usage. We recommend that +applications whose working set does not fit in memory and that do a +lot of random reads set a filter policy. +

+If you are using a custom comparator, you should ensure that the filter +policy you are using is compatible with your comparator. For example, +consider a comparator that ignores trailing spaces when comparing keys. +NewBloomFilterPolicy must not be used with such a comparator. +Instead, the application should provide a custom filter policy that +also ignores trailing spaces. For example: +

+  class CustomFilterPolicy : public leveldb::FilterPolicy {
+   private:
+    FilterPolicy* builtin_policy_;
+   public:
+    CustomFilterPolicy() : builtin_policy_(NewBloomFilterPolicy(10)) { }
+    ~CustomFilterPolicy() { delete builtin_policy_; }
+
+    const char* Name() const { return "IgnoreTrailingSpacesFilter"; }
+
+    void CreateFilter(const Slice* keys, int n, std::string* dst) const {
+      // Use builtin bloom filter code after removing trailing spaces
+      std::vector<Slice> trimmed(n);
+      for (int i = 0; i < n; i++) {
+        trimmed[i] = RemoveTrailingSpaces(keys[i]);
+      }
+      return builtin_policy_->CreateFilter(&trimmed[i], n, dst);
+    }
+
+    bool KeyMayMatch(const Slice& key, const Slice& filter) const {
+      // Use builtin bloom filter code after removing trailing spaces
+      return builtin_policy_->KeyMayMatch(RemoveTrailingSpaces(key), filter);
+    }
+  };
+
+

+Advanced applications may provide a filter policy that does not use +a bloom filter but uses some other mechanism for summarizing a set +of keys. See leveldb/filter_policy.h for detail. +

+

Checksums

+

+leveldb associates checksums with all data it stores in the file system. +There are two separate controls provided over how aggressively these +checksums are verified: +

+

    +
  • ReadOptions::verify_checksums may be set to true to force + checksum verification of all data that is read from the file system on + behalf of a particular read. By default, no such verification is + done. +

    +

  • Options::paranoid_checks may be set to true before opening a + database to make the database implementation raise an error as soon as + it detects an internal corruption. Depending on which portion of the + database has been corrupted, the error may be raised when the database + is opened, or later by another database operation. By default, + paranoid checking is off so that the database can be used even if + parts of its persistent storage have been corrupted. +

    + If a database is corrupted (perhaps it cannot be opened when + paranoid checking is turned on), the leveldb::RepairDB function + may be used to recover as much of the data as possible +

    +

+

Approximate Sizes

+

+The GetApproximateSizes method can used to get the approximate +number of bytes of file system space used by one or more key ranges. +

+

+   leveldb::Range ranges[2];
+   ranges[0] = leveldb::Range("a", "c");
+   ranges[1] = leveldb::Range("x", "z");
+   uint64_t sizes[2];
+   leveldb::Status s = db->GetApproximateSizes(ranges, 2, sizes);
+
+The preceding call will set sizes[0] to the approximate number of +bytes of file system space used by the key range [a..c) and +sizes[1] to the approximate number of bytes used by the key range +[x..z). +

+

Environment

+

+All file operations (and other operating system calls) issued by the +leveldb implementation are routed through a leveldb::Env object. +Sophisticated clients may wish to provide their own Env +implementation to get better control. For example, an application may +introduce artificial delays in the file IO paths to limit the impact +of leveldb on other activities in the system. +

+

+  class SlowEnv : public leveldb::Env {
+    .. implementation of the Env interface ...
+  };
+
+  SlowEnv env;
+  leveldb::Options options;
+  options.env = &env;
+  Status s = leveldb::DB::Open(options, ...);
+
+

Porting

+

+leveldb may be ported to a new platform by providing platform +specific implementations of the types/methods/functions exported by +leveldb/port/port.h. See leveldb/port/port_example.h for more +details. +

+In addition, the new platform may need a new default leveldb::Env +implementation. See leveldb/util/env_posix.h for an example. + +

Other Information

+ +

+Details about the leveldb implementation may be found in +the following documents: +

+ + + diff --git a/src/leveldb/doc/log_format.txt b/src/leveldb/doc/log_format.txt new file mode 100644 index 0000000..5228f62 --- /dev/null +++ b/src/leveldb/doc/log_format.txt @@ -0,0 +1,75 @@ +The log file contents are a sequence of 32KB blocks. The only +exception is that the tail of the file may contain a partial block. + +Each block consists of a sequence of records: + block := record* trailer? + record := + checksum: uint32 // crc32c of type and data[] ; little-endian + length: uint16 // little-endian + type: uint8 // One of FULL, FIRST, MIDDLE, LAST + data: uint8[length] + +A record never starts within the last six bytes of a block (since it +won't fit). Any leftover bytes here form the trailer, which must +consist entirely of zero bytes and must be skipped by readers. + +Aside: if exactly seven bytes are left in the current block, and a new +non-zero length record is added, the writer must emit a FIRST record +(which contains zero bytes of user data) to fill up the trailing seven +bytes of the block and then emit all of the user data in subsequent +blocks. + +More types may be added in the future. Some Readers may skip record +types they do not understand, others may report that some data was +skipped. + +FULL == 1 +FIRST == 2 +MIDDLE == 3 +LAST == 4 + +The FULL record contains the contents of an entire user record. + +FIRST, MIDDLE, LAST are types used for user records that have been +split into multiple fragments (typically because of block boundaries). +FIRST is the type of the first fragment of a user record, LAST is the +type of the last fragment of a user record, and MID is the type of all +interior fragments of a user record. + +Example: consider a sequence of user records: + A: length 1000 + B: length 97270 + C: length 8000 +A will be stored as a FULL record in the first block. + +B will be split into three fragments: first fragment occupies the rest +of the first block, second fragment occupies the entirety of the +second block, and the third fragment occupies a prefix of the third +block. This will leave six bytes free in the third block, which will +be left empty as the trailer. + +C will be stored as a FULL record in the fourth block. + +=================== + +Some benefits over the recordio format: + +(1) We do not need any heuristics for resyncing - just go to next +block boundary and scan. If there is a corruption, skip to the next +block. As a side-benefit, we do not get confused when part of the +contents of one log file are embedded as a record inside another log +file. + +(2) Splitting at approximate boundaries (e.g., for mapreduce) is +simple: find the next block boundary and skip records until we +hit a FULL or FIRST record. + +(3) We do not need extra buffering for large records. + +Some downsides compared to recordio format: + +(1) No packing of tiny records. This could be fixed by adding a new +record type, so it is a shortcoming of the current implementation, +not necessarily the format. + +(2) No compression. Again, this could be fixed by adding new record types. diff --git a/src/leveldb/doc/table_format.txt b/src/leveldb/doc/table_format.txt new file mode 100644 index 0000000..ca8f9b4 --- /dev/null +++ b/src/leveldb/doc/table_format.txt @@ -0,0 +1,104 @@ +File format +=========== + + + [data block 1] + [data block 2] + ... + [data block N] + [meta block 1] + ... + [meta block K] + [metaindex block] + [index block] + [Footer] (fixed size; starts at file_size - sizeof(Footer)) + + +The file contains internal pointers. Each such pointer is called +a BlockHandle and contains the following information: + offset: varint64 + size: varint64 +See https://developers.google.com/protocol-buffers/docs/encoding#varints +for an explanation of varint64 format. + +(1) The sequence of key/value pairs in the file are stored in sorted +order and partitioned into a sequence of data blocks. These blocks +come one after another at the beginning of the file. Each data block +is formatted according to the code in block_builder.cc, and then +optionally compressed. + +(2) After the data blocks we store a bunch of meta blocks. The +supported meta block types are described below. More meta block types +may be added in the future. Each meta block is again formatted using +block_builder.cc and then optionally compressed. + +(3) A "metaindex" block. It contains one entry for every other meta +block where the key is the name of the meta block and the value is a +BlockHandle pointing to that meta block. + +(4) An "index" block. This block contains one entry per data block, +where the key is a string >= last key in that data block and before +the first key in the successive data block. The value is the +BlockHandle for the data block. + +(6) At the very end of the file is a fixed length footer that contains +the BlockHandle of the metaindex and index blocks as well as a magic number. + metaindex_handle: char[p]; // Block handle for metaindex + index_handle: char[q]; // Block handle for index + padding: char[40-p-q]; // zeroed bytes to make fixed length + // (40==2*BlockHandle::kMaxEncodedLength) + magic: fixed64; // == 0xdb4775248b80fb57 (little-endian) + +"filter" Meta Block +------------------- + +If a "FilterPolicy" was specified when the database was opened, a +filter block is stored in each table. The "metaindex" block contains +an entry that maps from "filter." to the BlockHandle for the filter +block where "" is the string returned by the filter policy's +"Name()" method. + +The filter block stores a sequence of filters, where filter i contains +the output of FilterPolicy::CreateFilter() on all keys that are stored +in a block whose file offset falls within the range + + [ i*base ... (i+1)*base-1 ] + +Currently, "base" is 2KB. So for example, if blocks X and Y start in +the range [ 0KB .. 2KB-1 ], all of the keys in X and Y will be +converted to a filter by calling FilterPolicy::CreateFilter(), and the +resulting filter will be stored as the first filter in the filter +block. + +The filter block is formatted as follows: + + [filter 0] + [filter 1] + [filter 2] + ... + [filter N-1] + + [offset of filter 0] : 4 bytes + [offset of filter 1] : 4 bytes + [offset of filter 2] : 4 bytes + ... + [offset of filter N-1] : 4 bytes + + [offset of beginning of offset array] : 4 bytes + lg(base) : 1 byte + +The offset array at the end of the filter block allows efficient +mapping from a data block offset to the corresponding filter. + +"stats" Meta Block +------------------ + +This meta block contains a bunch of stats. The key is the name +of the statistic. The value contains the statistic. +TODO(postrelease): record following stats. + data size + index size + key size (uncompressed) + value size (uncompressed) + number of entries + number of data blocks diff --git a/src/leveldb/helpers/memenv/memenv.cc b/src/leveldb/helpers/memenv/memenv.cc new file mode 100644 index 0000000..5879de1 --- /dev/null +++ b/src/leveldb/helpers/memenv/memenv.cc @@ -0,0 +1,384 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "helpers/memenv/memenv.h" + +#include "leveldb/env.h" +#include "leveldb/status.h" +#include "port/port.h" +#include "util/mutexlock.h" +#include +#include +#include +#include + +namespace leveldb { + +namespace { + +class FileState { + public: + // FileStates are reference counted. The initial reference count is zero + // and the caller must call Ref() at least once. + FileState() : refs_(0), size_(0) {} + + // Increase the reference count. + void Ref() { + MutexLock lock(&refs_mutex_); + ++refs_; + } + + // Decrease the reference count. Delete if this is the last reference. + void Unref() { + bool do_delete = false; + + { + MutexLock lock(&refs_mutex_); + --refs_; + assert(refs_ >= 0); + if (refs_ <= 0) { + do_delete = true; + } + } + + if (do_delete) { + delete this; + } + } + + uint64_t Size() const { return size_; } + + Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const { + if (offset > size_) { + return Status::IOError("Offset greater than file size."); + } + const uint64_t available = size_ - offset; + if (n > available) { + n = available; + } + if (n == 0) { + *result = Slice(); + return Status::OK(); + } + + size_t block = offset / kBlockSize; + size_t block_offset = offset % kBlockSize; + + if (n <= kBlockSize - block_offset) { + // The requested bytes are all in the first block. + *result = Slice(blocks_[block] + block_offset, n); + return Status::OK(); + } + + size_t bytes_to_copy = n; + char* dst = scratch; + + while (bytes_to_copy > 0) { + size_t avail = kBlockSize - block_offset; + if (avail > bytes_to_copy) { + avail = bytes_to_copy; + } + memcpy(dst, blocks_[block] + block_offset, avail); + + bytes_to_copy -= avail; + dst += avail; + block++; + block_offset = 0; + } + + *result = Slice(scratch, n); + return Status::OK(); + } + + Status Append(const Slice& data) { + const char* src = data.data(); + size_t src_len = data.size(); + + while (src_len > 0) { + size_t avail; + size_t offset = size_ % kBlockSize; + + if (offset != 0) { + // There is some room in the last block. + avail = kBlockSize - offset; + } else { + // No room in the last block; push new one. + blocks_.push_back(new char[kBlockSize]); + avail = kBlockSize; + } + + if (avail > src_len) { + avail = src_len; + } + memcpy(blocks_.back() + offset, src, avail); + src_len -= avail; + src += avail; + size_ += avail; + } + + return Status::OK(); + } + + private: + // Private since only Unref() should be used to delete it. + ~FileState() { + for (std::vector::iterator i = blocks_.begin(); i != blocks_.end(); + ++i) { + delete [] *i; + } + } + + // No copying allowed. + FileState(const FileState&); + void operator=(const FileState&); + + port::Mutex refs_mutex_; + int refs_; // Protected by refs_mutex_; + + // The following fields are not protected by any mutex. They are only mutable + // while the file is being written, and concurrent access is not allowed + // to writable files. + std::vector blocks_; + uint64_t size_; + + enum { kBlockSize = 8 * 1024 }; +}; + +class SequentialFileImpl : public SequentialFile { + public: + explicit SequentialFileImpl(FileState* file) : file_(file), pos_(0) { + file_->Ref(); + } + + ~SequentialFileImpl() { + file_->Unref(); + } + + virtual Status Read(size_t n, Slice* result, char* scratch) { + Status s = file_->Read(pos_, n, result, scratch); + if (s.ok()) { + pos_ += result->size(); + } + return s; + } + + virtual Status Skip(uint64_t n) { + if (pos_ > file_->Size()) { + return Status::IOError("pos_ > file_->Size()"); + } + const size_t available = file_->Size() - pos_; + if (n > available) { + n = available; + } + pos_ += n; + return Status::OK(); + } + + private: + FileState* file_; + size_t pos_; +}; + +class RandomAccessFileImpl : public RandomAccessFile { + public: + explicit RandomAccessFileImpl(FileState* file) : file_(file) { + file_->Ref(); + } + + ~RandomAccessFileImpl() { + file_->Unref(); + } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + return file_->Read(offset, n, result, scratch); + } + + private: + FileState* file_; +}; + +class WritableFileImpl : public WritableFile { + public: + WritableFileImpl(FileState* file) : file_(file) { + file_->Ref(); + } + + ~WritableFileImpl() { + file_->Unref(); + } + + virtual Status Append(const Slice& data) { + return file_->Append(data); + } + + virtual Status Close() { return Status::OK(); } + virtual Status Flush() { return Status::OK(); } + virtual Status Sync() { return Status::OK(); } + + private: + FileState* file_; +}; + +class NoOpLogger : public Logger { + public: + virtual void Logv(const char* format, va_list ap) { } +}; + +class InMemoryEnv : public EnvWrapper { + public: + explicit InMemoryEnv(Env* base_env) : EnvWrapper(base_env) { } + + virtual ~InMemoryEnv() { + for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){ + i->second->Unref(); + } + } + + // Partial implementation of the Env interface. + virtual Status NewSequentialFile(const std::string& fname, + SequentialFile** result) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) == file_map_.end()) { + *result = NULL; + return Status::IOError(fname, "File not found"); + } + + *result = new SequentialFileImpl(file_map_[fname]); + return Status::OK(); + } + + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) == file_map_.end()) { + *result = NULL; + return Status::IOError(fname, "File not found"); + } + + *result = new RandomAccessFileImpl(file_map_[fname]); + return Status::OK(); + } + + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) != file_map_.end()) { + DeleteFileInternal(fname); + } + + FileState* file = new FileState(); + file->Ref(); + file_map_[fname] = file; + + *result = new WritableFileImpl(file); + return Status::OK(); + } + + virtual bool FileExists(const std::string& fname) { + MutexLock lock(&mutex_); + return file_map_.find(fname) != file_map_.end(); + } + + virtual Status GetChildren(const std::string& dir, + std::vector* result) { + MutexLock lock(&mutex_); + result->clear(); + + for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){ + const std::string& filename = i->first; + + if (filename.size() >= dir.size() + 1 && filename[dir.size()] == '/' && + Slice(filename).starts_with(Slice(dir))) { + result->push_back(filename.substr(dir.size() + 1)); + } + } + + return Status::OK(); + } + + void DeleteFileInternal(const std::string& fname) { + if (file_map_.find(fname) == file_map_.end()) { + return; + } + + file_map_[fname]->Unref(); + file_map_.erase(fname); + } + + virtual Status DeleteFile(const std::string& fname) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) == file_map_.end()) { + return Status::IOError(fname, "File not found"); + } + + DeleteFileInternal(fname); + return Status::OK(); + } + + virtual Status CreateDir(const std::string& dirname) { + return Status::OK(); + } + + virtual Status DeleteDir(const std::string& dirname) { + return Status::OK(); + } + + virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) { + MutexLock lock(&mutex_); + if (file_map_.find(fname) == file_map_.end()) { + return Status::IOError(fname, "File not found"); + } + + *file_size = file_map_[fname]->Size(); + return Status::OK(); + } + + virtual Status RenameFile(const std::string& src, + const std::string& target) { + MutexLock lock(&mutex_); + if (file_map_.find(src) == file_map_.end()) { + return Status::IOError(src, "File not found"); + } + + DeleteFileInternal(target); + file_map_[target] = file_map_[src]; + file_map_.erase(src); + return Status::OK(); + } + + virtual Status LockFile(const std::string& fname, FileLock** lock) { + *lock = new FileLock; + return Status::OK(); + } + + virtual Status UnlockFile(FileLock* lock) { + delete lock; + return Status::OK(); + } + + virtual Status GetTestDirectory(std::string* path) { + *path = "/test"; + return Status::OK(); + } + + virtual Status NewLogger(const std::string& fname, Logger** result) { + *result = new NoOpLogger; + return Status::OK(); + } + + private: + // Map from filenames to FileState objects, representing a simple file system. + typedef std::map FileSystem; + port::Mutex mutex_; + FileSystem file_map_; // Protected by mutex_. +}; + +} // namespace + +Env* NewMemEnv(Env* base_env) { + return new InMemoryEnv(base_env); +} + +} // namespace leveldb diff --git a/src/leveldb/helpers/memenv/memenv.h b/src/leveldb/helpers/memenv/memenv.h new file mode 100644 index 0000000..03b88de --- /dev/null +++ b/src/leveldb/helpers/memenv/memenv.h @@ -0,0 +1,20 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_ +#define STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_ + +namespace leveldb { + +class Env; + +// Returns a new environment that stores its data in memory and delegates +// all non-file-storage tasks to base_env. The caller must delete the result +// when it is no longer needed. +// *base_env must remain live while the result is in use. +Env* NewMemEnv(Env* base_env); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_ diff --git a/src/leveldb/helpers/memenv/memenv_test.cc b/src/leveldb/helpers/memenv/memenv_test.cc new file mode 100644 index 0000000..a44310f --- /dev/null +++ b/src/leveldb/helpers/memenv/memenv_test.cc @@ -0,0 +1,232 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "helpers/memenv/memenv.h" + +#include "db/db_impl.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "util/testharness.h" +#include +#include + +namespace leveldb { + +class MemEnvTest { + public: + Env* env_; + + MemEnvTest() + : env_(NewMemEnv(Env::Default())) { + } + ~MemEnvTest() { + delete env_; + } +}; + +TEST(MemEnvTest, Basics) { + uint64_t file_size; + WritableFile* writable_file; + std::vector children; + + ASSERT_OK(env_->CreateDir("/dir")); + + // Check that the directory is empty. + ASSERT_TRUE(!env_->FileExists("/dir/non_existent")); + ASSERT_TRUE(!env_->GetFileSize("/dir/non_existent", &file_size).ok()); + ASSERT_OK(env_->GetChildren("/dir", &children)); + ASSERT_EQ(0, children.size()); + + // Create a file. + ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + delete writable_file; + + // Check that the file exists. + ASSERT_TRUE(env_->FileExists("/dir/f")); + ASSERT_OK(env_->GetFileSize("/dir/f", &file_size)); + ASSERT_EQ(0, file_size); + ASSERT_OK(env_->GetChildren("/dir", &children)); + ASSERT_EQ(1, children.size()); + ASSERT_EQ("f", children[0]); + + // Write to the file. + ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + ASSERT_OK(writable_file->Append("abc")); + delete writable_file; + + // Check for expected size. + ASSERT_OK(env_->GetFileSize("/dir/f", &file_size)); + ASSERT_EQ(3, file_size); + + // Check that renaming works. + ASSERT_TRUE(!env_->RenameFile("/dir/non_existent", "/dir/g").ok()); + ASSERT_OK(env_->RenameFile("/dir/f", "/dir/g")); + ASSERT_TRUE(!env_->FileExists("/dir/f")); + ASSERT_TRUE(env_->FileExists("/dir/g")); + ASSERT_OK(env_->GetFileSize("/dir/g", &file_size)); + ASSERT_EQ(3, file_size); + + // Check that opening non-existent file fails. + SequentialFile* seq_file; + RandomAccessFile* rand_file; + ASSERT_TRUE(!env_->NewSequentialFile("/dir/non_existent", &seq_file).ok()); + ASSERT_TRUE(!seq_file); + ASSERT_TRUE(!env_->NewRandomAccessFile("/dir/non_existent", &rand_file).ok()); + ASSERT_TRUE(!rand_file); + + // Check that deleting works. + ASSERT_TRUE(!env_->DeleteFile("/dir/non_existent").ok()); + ASSERT_OK(env_->DeleteFile("/dir/g")); + ASSERT_TRUE(!env_->FileExists("/dir/g")); + ASSERT_OK(env_->GetChildren("/dir", &children)); + ASSERT_EQ(0, children.size()); + ASSERT_OK(env_->DeleteDir("/dir")); +} + +TEST(MemEnvTest, ReadWrite) { + WritableFile* writable_file; + SequentialFile* seq_file; + RandomAccessFile* rand_file; + Slice result; + char scratch[100]; + + ASSERT_OK(env_->CreateDir("/dir")); + + ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + ASSERT_OK(writable_file->Append("hello ")); + ASSERT_OK(writable_file->Append("world")); + delete writable_file; + + // Read sequentially. + ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file)); + ASSERT_OK(seq_file->Read(5, &result, scratch)); // Read "hello". + ASSERT_EQ(0, result.compare("hello")); + ASSERT_OK(seq_file->Skip(1)); + ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Read "world". + ASSERT_EQ(0, result.compare("world")); + ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Try reading past EOF. + ASSERT_EQ(0, result.size()); + ASSERT_OK(seq_file->Skip(100)); // Try to skip past end of file. + ASSERT_OK(seq_file->Read(1000, &result, scratch)); + ASSERT_EQ(0, result.size()); + delete seq_file; + + // Random reads. + ASSERT_OK(env_->NewRandomAccessFile("/dir/f", &rand_file)); + ASSERT_OK(rand_file->Read(6, 5, &result, scratch)); // Read "world". + ASSERT_EQ(0, result.compare("world")); + ASSERT_OK(rand_file->Read(0, 5, &result, scratch)); // Read "hello". + ASSERT_EQ(0, result.compare("hello")); + ASSERT_OK(rand_file->Read(10, 100, &result, scratch)); // Read "d". + ASSERT_EQ(0, result.compare("d")); + + // Too high offset. + ASSERT_TRUE(!rand_file->Read(1000, 5, &result, scratch).ok()); + delete rand_file; +} + +TEST(MemEnvTest, Locks) { + FileLock* lock; + + // These are no-ops, but we test they return success. + ASSERT_OK(env_->LockFile("some file", &lock)); + ASSERT_OK(env_->UnlockFile(lock)); +} + +TEST(MemEnvTest, Misc) { + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + ASSERT_TRUE(!test_dir.empty()); + + WritableFile* writable_file; + ASSERT_OK(env_->NewWritableFile("/a/b", &writable_file)); + + // These are no-ops, but we test they return success. + ASSERT_OK(writable_file->Sync()); + ASSERT_OK(writable_file->Flush()); + ASSERT_OK(writable_file->Close()); + delete writable_file; +} + +TEST(MemEnvTest, LargeWrite) { + const size_t kWriteSize = 300 * 1024; + char* scratch = new char[kWriteSize * 2]; + + std::string write_data; + for (size_t i = 0; i < kWriteSize; ++i) { + write_data.append(1, static_cast(i)); + } + + WritableFile* writable_file; + ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + ASSERT_OK(writable_file->Append("foo")); + ASSERT_OK(writable_file->Append(write_data)); + delete writable_file; + + SequentialFile* seq_file; + Slice result; + ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file)); + ASSERT_OK(seq_file->Read(3, &result, scratch)); // Read "foo". + ASSERT_EQ(0, result.compare("foo")); + + size_t read = 0; + std::string read_data; + while (read < kWriteSize) { + ASSERT_OK(seq_file->Read(kWriteSize - read, &result, scratch)); + read_data.append(result.data(), result.size()); + read += result.size(); + } + ASSERT_TRUE(write_data == read_data); + delete seq_file; + delete [] scratch; +} + +TEST(MemEnvTest, DBTest) { + Options options; + options.create_if_missing = true; + options.env = env_; + DB* db; + + const Slice keys[] = {Slice("aaa"), Slice("bbb"), Slice("ccc")}; + const Slice vals[] = {Slice("foo"), Slice("bar"), Slice("baz")}; + + ASSERT_OK(DB::Open(options, "/dir/db", &db)); + for (size_t i = 0; i < 3; ++i) { + ASSERT_OK(db->Put(WriteOptions(), keys[i], vals[i])); + } + + for (size_t i = 0; i < 3; ++i) { + std::string res; + ASSERT_OK(db->Get(ReadOptions(), keys[i], &res)); + ASSERT_TRUE(res == vals[i]); + } + + Iterator* iterator = db->NewIterator(ReadOptions()); + iterator->SeekToFirst(); + for (size_t i = 0; i < 3; ++i) { + ASSERT_TRUE(iterator->Valid()); + ASSERT_TRUE(keys[i] == iterator->key()); + ASSERT_TRUE(vals[i] == iterator->value()); + iterator->Next(); + } + ASSERT_TRUE(!iterator->Valid()); + delete iterator; + + DBImpl* dbi = reinterpret_cast(db); + ASSERT_OK(dbi->TEST_CompactMemTable()); + + for (size_t i = 0; i < 3; ++i) { + std::string res; + ASSERT_OK(db->Get(ReadOptions(), keys[i], &res)); + ASSERT_TRUE(res == vals[i]); + } + + delete db; +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/include/leveldb/c.h b/src/leveldb/include/leveldb/c.h new file mode 100644 index 0000000..1fa5886 --- /dev/null +++ b/src/leveldb/include/leveldb/c.h @@ -0,0 +1,291 @@ +/* Copyright (c) 2011 The LevelDB Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. See the AUTHORS file for names of contributors. + + C bindings for leveldb. May be useful as a stable ABI that can be + used by programs that keep leveldb in a shared library, or for + a JNI api. + + Does not support: + . getters for the option types + . custom comparators that implement key shortening + . capturing post-write-snapshot + . custom iter, db, env, cache implementations using just the C bindings + + Some conventions: + + (1) We expose just opaque struct pointers and functions to clients. + This allows us to change internal representations without having to + recompile clients. + + (2) For simplicity, there is no equivalent to the Slice type. Instead, + the caller has to pass the pointer and length as separate + arguments. + + (3) Errors are represented by a null-terminated c string. NULL + means no error. All operations that can raise an error are passed + a "char** errptr" as the last argument. One of the following must + be true on entry: + *errptr == NULL + *errptr points to a malloc()ed null-terminated error message + (On Windows, *errptr must have been malloc()-ed by this library.) + On success, a leveldb routine leaves *errptr unchanged. + On failure, leveldb frees the old value of *errptr and + set *errptr to a malloc()ed error message. + + (4) Bools have the type unsigned char (0 == false; rest == true) + + (5) All of the pointer arguments must be non-NULL. +*/ + +#ifndef STORAGE_LEVELDB_INCLUDE_C_H_ +#define STORAGE_LEVELDB_INCLUDE_C_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* Exported types */ + +typedef struct leveldb_t leveldb_t; +typedef struct leveldb_cache_t leveldb_cache_t; +typedef struct leveldb_comparator_t leveldb_comparator_t; +typedef struct leveldb_env_t leveldb_env_t; +typedef struct leveldb_filelock_t leveldb_filelock_t; +typedef struct leveldb_filterpolicy_t leveldb_filterpolicy_t; +typedef struct leveldb_iterator_t leveldb_iterator_t; +typedef struct leveldb_logger_t leveldb_logger_t; +typedef struct leveldb_options_t leveldb_options_t; +typedef struct leveldb_randomfile_t leveldb_randomfile_t; +typedef struct leveldb_readoptions_t leveldb_readoptions_t; +typedef struct leveldb_seqfile_t leveldb_seqfile_t; +typedef struct leveldb_snapshot_t leveldb_snapshot_t; +typedef struct leveldb_writablefile_t leveldb_writablefile_t; +typedef struct leveldb_writebatch_t leveldb_writebatch_t; +typedef struct leveldb_writeoptions_t leveldb_writeoptions_t; + +/* DB operations */ + +extern leveldb_t* leveldb_open( + const leveldb_options_t* options, + const char* name, + char** errptr); + +extern void leveldb_close(leveldb_t* db); + +extern void leveldb_put( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + const char* val, size_t vallen, + char** errptr); + +extern void leveldb_delete( + leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + char** errptr); + +extern void leveldb_write( + leveldb_t* db, + const leveldb_writeoptions_t* options, + leveldb_writebatch_t* batch, + char** errptr); + +/* Returns NULL if not found. A malloc()ed array otherwise. + Stores the length of the array in *vallen. */ +extern char* leveldb_get( + leveldb_t* db, + const leveldb_readoptions_t* options, + const char* key, size_t keylen, + size_t* vallen, + char** errptr); + +extern leveldb_iterator_t* leveldb_create_iterator( + leveldb_t* db, + const leveldb_readoptions_t* options); + +extern const leveldb_snapshot_t* leveldb_create_snapshot( + leveldb_t* db); + +extern void leveldb_release_snapshot( + leveldb_t* db, + const leveldb_snapshot_t* snapshot); + +/* Returns NULL if property name is unknown. + Else returns a pointer to a malloc()-ed null-terminated value. */ +extern char* leveldb_property_value( + leveldb_t* db, + const char* propname); + +extern void leveldb_approximate_sizes( + leveldb_t* db, + int num_ranges, + const char* const* range_start_key, const size_t* range_start_key_len, + const char* const* range_limit_key, const size_t* range_limit_key_len, + uint64_t* sizes); + +extern void leveldb_compact_range( + leveldb_t* db, + const char* start_key, size_t start_key_len, + const char* limit_key, size_t limit_key_len); + +/* Management operations */ + +extern void leveldb_destroy_db( + const leveldb_options_t* options, + const char* name, + char** errptr); + +extern void leveldb_repair_db( + const leveldb_options_t* options, + const char* name, + char** errptr); + +/* Iterator */ + +extern void leveldb_iter_destroy(leveldb_iterator_t*); +extern unsigned char leveldb_iter_valid(const leveldb_iterator_t*); +extern void leveldb_iter_seek_to_first(leveldb_iterator_t*); +extern void leveldb_iter_seek_to_last(leveldb_iterator_t*); +extern void leveldb_iter_seek(leveldb_iterator_t*, const char* k, size_t klen); +extern void leveldb_iter_next(leveldb_iterator_t*); +extern void leveldb_iter_prev(leveldb_iterator_t*); +extern const char* leveldb_iter_key(const leveldb_iterator_t*, size_t* klen); +extern const char* leveldb_iter_value(const leveldb_iterator_t*, size_t* vlen); +extern void leveldb_iter_get_error(const leveldb_iterator_t*, char** errptr); + +/* Write batch */ + +extern leveldb_writebatch_t* leveldb_writebatch_create(); +extern void leveldb_writebatch_destroy(leveldb_writebatch_t*); +extern void leveldb_writebatch_clear(leveldb_writebatch_t*); +extern void leveldb_writebatch_put( + leveldb_writebatch_t*, + const char* key, size_t klen, + const char* val, size_t vlen); +extern void leveldb_writebatch_delete( + leveldb_writebatch_t*, + const char* key, size_t klen); +extern void leveldb_writebatch_iterate( + leveldb_writebatch_t*, + void* state, + void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen), + void (*deleted)(void*, const char* k, size_t klen)); + +/* Options */ + +extern leveldb_options_t* leveldb_options_create(); +extern void leveldb_options_destroy(leveldb_options_t*); +extern void leveldb_options_set_comparator( + leveldb_options_t*, + leveldb_comparator_t*); +extern void leveldb_options_set_filter_policy( + leveldb_options_t*, + leveldb_filterpolicy_t*); +extern void leveldb_options_set_create_if_missing( + leveldb_options_t*, unsigned char); +extern void leveldb_options_set_error_if_exists( + leveldb_options_t*, unsigned char); +extern void leveldb_options_set_paranoid_checks( + leveldb_options_t*, unsigned char); +extern void leveldb_options_set_env(leveldb_options_t*, leveldb_env_t*); +extern void leveldb_options_set_info_log(leveldb_options_t*, leveldb_logger_t*); +extern void leveldb_options_set_write_buffer_size(leveldb_options_t*, size_t); +extern void leveldb_options_set_max_open_files(leveldb_options_t*, int); +extern void leveldb_options_set_cache(leveldb_options_t*, leveldb_cache_t*); +extern void leveldb_options_set_block_size(leveldb_options_t*, size_t); +extern void leveldb_options_set_block_restart_interval(leveldb_options_t*, int); + +enum { + leveldb_no_compression = 0, + leveldb_snappy_compression = 1 +}; +extern void leveldb_options_set_compression(leveldb_options_t*, int); + +/* Comparator */ + +extern leveldb_comparator_t* leveldb_comparator_create( + void* state, + void (*destructor)(void*), + int (*compare)( + void*, + const char* a, size_t alen, + const char* b, size_t blen), + const char* (*name)(void*)); +extern void leveldb_comparator_destroy(leveldb_comparator_t*); + +/* Filter policy */ + +extern leveldb_filterpolicy_t* leveldb_filterpolicy_create( + void* state, + void (*destructor)(void*), + char* (*create_filter)( + void*, + const char* const* key_array, const size_t* key_length_array, + int num_keys, + size_t* filter_length), + unsigned char (*key_may_match)( + void*, + const char* key, size_t length, + const char* filter, size_t filter_length), + const char* (*name)(void*)); +extern void leveldb_filterpolicy_destroy(leveldb_filterpolicy_t*); + +extern leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom( + int bits_per_key); + +/* Read options */ + +extern leveldb_readoptions_t* leveldb_readoptions_create(); +extern void leveldb_readoptions_destroy(leveldb_readoptions_t*); +extern void leveldb_readoptions_set_verify_checksums( + leveldb_readoptions_t*, + unsigned char); +extern void leveldb_readoptions_set_fill_cache( + leveldb_readoptions_t*, unsigned char); +extern void leveldb_readoptions_set_snapshot( + leveldb_readoptions_t*, + const leveldb_snapshot_t*); + +/* Write options */ + +extern leveldb_writeoptions_t* leveldb_writeoptions_create(); +extern void leveldb_writeoptions_destroy(leveldb_writeoptions_t*); +extern void leveldb_writeoptions_set_sync( + leveldb_writeoptions_t*, unsigned char); + +/* Cache */ + +extern leveldb_cache_t* leveldb_cache_create_lru(size_t capacity); +extern void leveldb_cache_destroy(leveldb_cache_t* cache); + +/* Env */ + +extern leveldb_env_t* leveldb_create_default_env(); +extern void leveldb_env_destroy(leveldb_env_t*); + +/* Utility */ + +/* Calls free(ptr). + REQUIRES: ptr was malloc()-ed and returned by one of the routines + in this file. Note that in certain cases (typically on Windows), you + may need to call this routine instead of free(ptr) to dispose of + malloc()-ed memory returned by this library. */ +extern void leveldb_free(void* ptr); + +/* Return the major version number for this release. */ +extern int leveldb_major_version(); + +/* Return the minor version number for this release. */ +extern int leveldb_minor_version(); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* STORAGE_LEVELDB_INCLUDE_C_H_ */ diff --git a/src/leveldb/include/leveldb/cache.h b/src/leveldb/include/leveldb/cache.h new file mode 100644 index 0000000..5e3b476 --- /dev/null +++ b/src/leveldb/include/leveldb/cache.h @@ -0,0 +1,99 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A Cache is an interface that maps keys to values. It has internal +// synchronization and may be safely accessed concurrently from +// multiple threads. It may automatically evict entries to make room +// for new entries. Values have a specified charge against the cache +// capacity. For example, a cache where the values are variable +// length strings, may use the length of the string as the charge for +// the string. +// +// A builtin cache implementation with a least-recently-used eviction +// policy is provided. Clients may use their own implementations if +// they want something more sophisticated (like scan-resistance, a +// custom eviction policy, variable cache sizing, etc.) + +#ifndef STORAGE_LEVELDB_INCLUDE_CACHE_H_ +#define STORAGE_LEVELDB_INCLUDE_CACHE_H_ + +#include +#include "leveldb/slice.h" + +namespace leveldb { + +class Cache; + +// Create a new cache with a fixed size capacity. This implementation +// of Cache uses a least-recently-used eviction policy. +extern Cache* NewLRUCache(size_t capacity); + +class Cache { + public: + Cache() { } + + // Destroys all existing entries by calling the "deleter" + // function that was passed to the constructor. + virtual ~Cache(); + + // Opaque handle to an entry stored in the cache. + struct Handle { }; + + // Insert a mapping from key->value into the cache and assign it + // the specified charge against the total cache capacity. + // + // Returns a handle that corresponds to the mapping. The caller + // must call this->Release(handle) when the returned mapping is no + // longer needed. + // + // When the inserted entry is no longer needed, the key and + // value will be passed to "deleter". + virtual Handle* Insert(const Slice& key, void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)) = 0; + + // If the cache has no mapping for "key", returns NULL. + // + // Else return a handle that corresponds to the mapping. The caller + // must call this->Release(handle) when the returned mapping is no + // longer needed. + virtual Handle* Lookup(const Slice& key) = 0; + + // Release a mapping returned by a previous Lookup(). + // REQUIRES: handle must not have been released yet. + // REQUIRES: handle must have been returned by a method on *this. + virtual void Release(Handle* handle) = 0; + + // Return the value encapsulated in a handle returned by a + // successful Lookup(). + // REQUIRES: handle must not have been released yet. + // REQUIRES: handle must have been returned by a method on *this. + virtual void* Value(Handle* handle) = 0; + + // If the cache contains entry for key, erase it. Note that the + // underlying entry will be kept around until all existing handles + // to it have been released. + virtual void Erase(const Slice& key) = 0; + + // Return a new numeric id. May be used by multiple clients who are + // sharing the same cache to partition the key space. Typically the + // client will allocate a new id at startup and prepend the id to + // its cache keys. + virtual uint64_t NewId() = 0; + + private: + void LRU_Remove(Handle* e); + void LRU_Append(Handle* e); + void Unref(Handle* e); + + struct Rep; + Rep* rep_; + + // No copying allowed + Cache(const Cache&); + void operator=(const Cache&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_CACHE_H_ diff --git a/src/leveldb/include/leveldb/comparator.h b/src/leveldb/include/leveldb/comparator.h new file mode 100644 index 0000000..556b984 --- /dev/null +++ b/src/leveldb/include/leveldb/comparator.h @@ -0,0 +1,63 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_ +#define STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_ + +#include + +namespace leveldb { + +class Slice; + +// A Comparator object provides a total order across slices that are +// used as keys in an sstable or a database. A Comparator implementation +// must be thread-safe since leveldb may invoke its methods concurrently +// from multiple threads. +class Comparator { + public: + virtual ~Comparator(); + + // Three-way comparison. Returns value: + // < 0 iff "a" < "b", + // == 0 iff "a" == "b", + // > 0 iff "a" > "b" + virtual int Compare(const Slice& a, const Slice& b) const = 0; + + // The name of the comparator. Used to check for comparator + // mismatches (i.e., a DB created with one comparator is + // accessed using a different comparator. + // + // The client of this package should switch to a new name whenever + // the comparator implementation changes in a way that will cause + // the relative ordering of any two keys to change. + // + // Names starting with "leveldb." are reserved and should not be used + // by any clients of this package. + virtual const char* Name() const = 0; + + // Advanced functions: these are used to reduce the space requirements + // for internal data structures like index blocks. + + // If *start < limit, changes *start to a short string in [start,limit). + // Simple comparator implementations may return with *start unchanged, + // i.e., an implementation of this method that does nothing is correct. + virtual void FindShortestSeparator( + std::string* start, + const Slice& limit) const = 0; + + // Changes *key to a short string >= *key. + // Simple comparator implementations may return with *key unchanged, + // i.e., an implementation of this method that does nothing is correct. + virtual void FindShortSuccessor(std::string* key) const = 0; +}; + +// Return a builtin comparator that uses lexicographic byte-wise +// ordering. The result remains the property of this module and +// must not be deleted. +extern const Comparator* BytewiseComparator(); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_ diff --git a/src/leveldb/include/leveldb/db.h b/src/leveldb/include/leveldb/db.h new file mode 100644 index 0000000..29d3674 --- /dev/null +++ b/src/leveldb/include/leveldb/db.h @@ -0,0 +1,161 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_DB_H_ +#define STORAGE_LEVELDB_INCLUDE_DB_H_ + +#include +#include +#include "leveldb/iterator.h" +#include "leveldb/options.h" + +namespace leveldb { + +// Update Makefile if you change these +static const int kMajorVersion = 1; +static const int kMinorVersion = 9; + +struct Options; +struct ReadOptions; +struct WriteOptions; +class WriteBatch; + +// Abstract handle to particular state of a DB. +// A Snapshot is an immutable object and can therefore be safely +// accessed from multiple threads without any external synchronization. +class Snapshot { + protected: + virtual ~Snapshot(); +}; + +// A range of keys +struct Range { + Slice start; // Included in the range + Slice limit; // Not included in the range + + Range() { } + Range(const Slice& s, const Slice& l) : start(s), limit(l) { } +}; + +// A DB is a persistent ordered map from keys to values. +// A DB is safe for concurrent access from multiple threads without +// any external synchronization. +class DB { + public: + // Open the database with the specified "name". + // Stores a pointer to a heap-allocated database in *dbptr and returns + // OK on success. + // Stores NULL in *dbptr and returns a non-OK status on error. + // Caller should delete *dbptr when it is no longer needed. + static Status Open(const Options& options, + const std::string& name, + DB** dbptr); + + DB() { } + virtual ~DB(); + + // Set the database entry for "key" to "value". Returns OK on success, + // and a non-OK status on error. + // Note: consider setting options.sync = true. + virtual Status Put(const WriteOptions& options, + const Slice& key, + const Slice& value) = 0; + + // Remove the database entry (if any) for "key". Returns OK on + // success, and a non-OK status on error. It is not an error if "key" + // did not exist in the database. + // Note: consider setting options.sync = true. + virtual Status Delete(const WriteOptions& options, const Slice& key) = 0; + + // Apply the specified updates to the database. + // Returns OK on success, non-OK on failure. + // Note: consider setting options.sync = true. + virtual Status Write(const WriteOptions& options, WriteBatch* updates) = 0; + + // If the database contains an entry for "key" store the + // corresponding value in *value and return OK. + // + // If there is no entry for "key" leave *value unchanged and return + // a status for which Status::IsNotFound() returns true. + // + // May return some other Status on an error. + virtual Status Get(const ReadOptions& options, + const Slice& key, std::string* value) = 0; + + // Return a heap-allocated iterator over the contents of the database. + // The result of NewIterator() is initially invalid (caller must + // call one of the Seek methods on the iterator before using it). + // + // Caller should delete the iterator when it is no longer needed. + // The returned iterator should be deleted before this db is deleted. + virtual Iterator* NewIterator(const ReadOptions& options) = 0; + + // Return a handle to the current DB state. Iterators created with + // this handle will all observe a stable snapshot of the current DB + // state. The caller must call ReleaseSnapshot(result) when the + // snapshot is no longer needed. + virtual const Snapshot* GetSnapshot() = 0; + + // Release a previously acquired snapshot. The caller must not + // use "snapshot" after this call. + virtual void ReleaseSnapshot(const Snapshot* snapshot) = 0; + + // DB implementations can export properties about their state + // via this method. If "property" is a valid property understood by this + // DB implementation, fills "*value" with its current value and returns + // true. Otherwise returns false. + // + // + // Valid property names include: + // + // "leveldb.num-files-at-level" - return the number of files at level , + // where is an ASCII representation of a level number (e.g. "0"). + // "leveldb.stats" - returns a multi-line string that describes statistics + // about the internal operation of the DB. + // "leveldb.sstables" - returns a multi-line string that describes all + // of the sstables that make up the db contents. + virtual bool GetProperty(const Slice& property, std::string* value) = 0; + + // For each i in [0,n-1], store in "sizes[i]", the approximate + // file system space used by keys in "[range[i].start .. range[i].limit)". + // + // Note that the returned sizes measure file system space usage, so + // if the user data compresses by a factor of ten, the returned + // sizes will be one-tenth the size of the corresponding user data size. + // + // The results may not include the sizes of recently written data. + virtual void GetApproximateSizes(const Range* range, int n, + uint64_t* sizes) = 0; + + // Compact the underlying storage for the key range [*begin,*end]. + // In particular, deleted and overwritten versions are discarded, + // and the data is rearranged to reduce the cost of operations + // needed to access the data. This operation should typically only + // be invoked by users who understand the underlying implementation. + // + // begin==NULL is treated as a key before all keys in the database. + // end==NULL is treated as a key after all keys in the database. + // Therefore the following call will compact the entire database: + // db->CompactRange(NULL, NULL); + virtual void CompactRange(const Slice* begin, const Slice* end) = 0; + + private: + // No copying allowed + DB(const DB&); + void operator=(const DB&); +}; + +// Destroy the contents of the specified database. +// Be very careful using this method. +Status DestroyDB(const std::string& name, const Options& options); + +// If a DB cannot be opened, you may attempt to call this method to +// resurrect as much of the contents of the database as possible. +// Some data may be lost, so be careful when calling this function +// on a database that contains important information. +Status RepairDB(const std::string& dbname, const Options& options); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_DB_H_ diff --git a/src/leveldb/include/leveldb/env.h b/src/leveldb/include/leveldb/env.h new file mode 100644 index 0000000..fa32289 --- /dev/null +++ b/src/leveldb/include/leveldb/env.h @@ -0,0 +1,333 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// An Env is an interface used by the leveldb implementation to access +// operating system functionality like the filesystem etc. Callers +// may wish to provide a custom Env object when opening a database to +// get fine gain control; e.g., to rate limit file system operations. +// +// All Env implementations are safe for concurrent access from +// multiple threads without any external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_ENV_H_ +#define STORAGE_LEVELDB_INCLUDE_ENV_H_ + +#include +#include +#include +#include +#include "leveldb/status.h" + +namespace leveldb { + +class FileLock; +class Logger; +class RandomAccessFile; +class SequentialFile; +class Slice; +class WritableFile; + +class Env { + public: + Env() { } + virtual ~Env(); + + // Return a default environment suitable for the current operating + // system. Sophisticated users may wish to provide their own Env + // implementation instead of relying on this default environment. + // + // The result of Default() belongs to leveldb and must never be deleted. + static Env* Default(); + + // Create a brand new sequentially-readable file with the specified name. + // On success, stores a pointer to the new file in *result and returns OK. + // On failure stores NULL in *result and returns non-OK. If the file does + // not exist, returns a non-OK status. + // + // The returned file will only be accessed by one thread at a time. + virtual Status NewSequentialFile(const std::string& fname, + SequentialFile** result) = 0; + + // Create a brand new random access read-only file with the + // specified name. On success, stores a pointer to the new file in + // *result and returns OK. On failure stores NULL in *result and + // returns non-OK. If the file does not exist, returns a non-OK + // status. + // + // The returned file may be concurrently accessed by multiple threads. + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) = 0; + + // Create an object that writes to a new file with the specified + // name. Deletes any existing file with the same name and creates a + // new file. On success, stores a pointer to the new file in + // *result and returns OK. On failure stores NULL in *result and + // returns non-OK. + // + // The returned file will only be accessed by one thread at a time. + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) = 0; + + // Returns true iff the named file exists. + virtual bool FileExists(const std::string& fname) = 0; + + // Store in *result the names of the children of the specified directory. + // The names are relative to "dir". + // Original contents of *results are dropped. + virtual Status GetChildren(const std::string& dir, + std::vector* result) = 0; + + // Delete the named file. + virtual Status DeleteFile(const std::string& fname) = 0; + + // Create the specified directory. + virtual Status CreateDir(const std::string& dirname) = 0; + + // Delete the specified directory. + virtual Status DeleteDir(const std::string& dirname) = 0; + + // Store the size of fname in *file_size. + virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) = 0; + + // Rename file src to target. + virtual Status RenameFile(const std::string& src, + const std::string& target) = 0; + + // Lock the specified file. Used to prevent concurrent access to + // the same db by multiple processes. On failure, stores NULL in + // *lock and returns non-OK. + // + // On success, stores a pointer to the object that represents the + // acquired lock in *lock and returns OK. The caller should call + // UnlockFile(*lock) to release the lock. If the process exits, + // the lock will be automatically released. + // + // If somebody else already holds the lock, finishes immediately + // with a failure. I.e., this call does not wait for existing locks + // to go away. + // + // May create the named file if it does not already exist. + virtual Status LockFile(const std::string& fname, FileLock** lock) = 0; + + // Release the lock acquired by a previous successful call to LockFile. + // REQUIRES: lock was returned by a successful LockFile() call + // REQUIRES: lock has not already been unlocked. + virtual Status UnlockFile(FileLock* lock) = 0; + + // Arrange to run "(*function)(arg)" once in a background thread. + // + // "function" may run in an unspecified thread. Multiple functions + // added to the same Env may run concurrently in different threads. + // I.e., the caller may not assume that background work items are + // serialized. + virtual void Schedule( + void (*function)(void* arg), + void* arg) = 0; + + // Start a new thread, invoking "function(arg)" within the new thread. + // When "function(arg)" returns, the thread will be destroyed. + virtual void StartThread(void (*function)(void* arg), void* arg) = 0; + + // *path is set to a temporary directory that can be used for testing. It may + // or many not have just been created. The directory may or may not differ + // between runs of the same process, but subsequent calls will return the + // same directory. + virtual Status GetTestDirectory(std::string* path) = 0; + + // Create and return a log file for storing informational messages. + virtual Status NewLogger(const std::string& fname, Logger** result) = 0; + + // Returns the number of micro-seconds since some fixed point in time. Only + // useful for computing deltas of time. + virtual uint64_t NowMicros() = 0; + + // Sleep/delay the thread for the perscribed number of micro-seconds. + virtual void SleepForMicroseconds(int micros) = 0; + + private: + // No copying allowed + Env(const Env&); + void operator=(const Env&); +}; + +// A file abstraction for reading sequentially through a file +class SequentialFile { + public: + SequentialFile() { } + virtual ~SequentialFile(); + + // Read up to "n" bytes from the file. "scratch[0..n-1]" may be + // written by this routine. Sets "*result" to the data that was + // read (including if fewer than "n" bytes were successfully read). + // May set "*result" to point at data in "scratch[0..n-1]", so + // "scratch[0..n-1]" must be live when "*result" is used. + // If an error was encountered, returns a non-OK status. + // + // REQUIRES: External synchronization + virtual Status Read(size_t n, Slice* result, char* scratch) = 0; + + // Skip "n" bytes from the file. This is guaranteed to be no + // slower that reading the same data, but may be faster. + // + // If end of file is reached, skipping will stop at the end of the + // file, and Skip will return OK. + // + // REQUIRES: External synchronization + virtual Status Skip(uint64_t n) = 0; + + private: + // No copying allowed + SequentialFile(const SequentialFile&); + void operator=(const SequentialFile&); +}; + +// A file abstraction for randomly reading the contents of a file. +class RandomAccessFile { + public: + RandomAccessFile() { } + virtual ~RandomAccessFile(); + + // Read up to "n" bytes from the file starting at "offset". + // "scratch[0..n-1]" may be written by this routine. Sets "*result" + // to the data that was read (including if fewer than "n" bytes were + // successfully read). May set "*result" to point at data in + // "scratch[0..n-1]", so "scratch[0..n-1]" must be live when + // "*result" is used. If an error was encountered, returns a non-OK + // status. + // + // Safe for concurrent use by multiple threads. + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const = 0; + + private: + // No copying allowed + RandomAccessFile(const RandomAccessFile&); + void operator=(const RandomAccessFile&); +}; + +// A file abstraction for sequential writing. The implementation +// must provide buffering since callers may append small fragments +// at a time to the file. +class WritableFile { + public: + WritableFile() { } + virtual ~WritableFile(); + + virtual Status Append(const Slice& data) = 0; + virtual Status Close() = 0; + virtual Status Flush() = 0; + virtual Status Sync() = 0; + + private: + // No copying allowed + WritableFile(const WritableFile&); + void operator=(const WritableFile&); +}; + +// An interface for writing log messages. +class Logger { + public: + Logger() { } + virtual ~Logger(); + + // Write an entry to the log file with the specified format. + virtual void Logv(const char* format, va_list ap) = 0; + + private: + // No copying allowed + Logger(const Logger&); + void operator=(const Logger&); +}; + + +// Identifies a locked file. +class FileLock { + public: + FileLock() { } + virtual ~FileLock(); + private: + // No copying allowed + FileLock(const FileLock&); + void operator=(const FileLock&); +}; + +// Log the specified data to *info_log if info_log is non-NULL. +extern void Log(Logger* info_log, const char* format, ...) +# if defined(__GNUC__) || defined(__clang__) + __attribute__((__format__ (__printf__, 2, 3))) +# endif + ; + +// A utility routine: write "data" to the named file. +extern Status WriteStringToFile(Env* env, const Slice& data, + const std::string& fname); + +// A utility routine: read contents of named file into *data +extern Status ReadFileToString(Env* env, const std::string& fname, + std::string* data); + +// An implementation of Env that forwards all calls to another Env. +// May be useful to clients who wish to override just part of the +// functionality of another Env. +class EnvWrapper : public Env { + public: + // Initialize an EnvWrapper that delegates all calls to *t + explicit EnvWrapper(Env* t) : target_(t) { } + virtual ~EnvWrapper(); + + // Return the target to which this Env forwards all calls + Env* target() const { return target_; } + + // The following text is boilerplate that forwards all methods to target() + Status NewSequentialFile(const std::string& f, SequentialFile** r) { + return target_->NewSequentialFile(f, r); + } + Status NewRandomAccessFile(const std::string& f, RandomAccessFile** r) { + return target_->NewRandomAccessFile(f, r); + } + Status NewWritableFile(const std::string& f, WritableFile** r) { + return target_->NewWritableFile(f, r); + } + bool FileExists(const std::string& f) { return target_->FileExists(f); } + Status GetChildren(const std::string& dir, std::vector* r) { + return target_->GetChildren(dir, r); + } + Status DeleteFile(const std::string& f) { return target_->DeleteFile(f); } + Status CreateDir(const std::string& d) { return target_->CreateDir(d); } + Status DeleteDir(const std::string& d) { return target_->DeleteDir(d); } + Status GetFileSize(const std::string& f, uint64_t* s) { + return target_->GetFileSize(f, s); + } + Status RenameFile(const std::string& s, const std::string& t) { + return target_->RenameFile(s, t); + } + Status LockFile(const std::string& f, FileLock** l) { + return target_->LockFile(f, l); + } + Status UnlockFile(FileLock* l) { return target_->UnlockFile(l); } + void Schedule(void (*f)(void*), void* a) { + return target_->Schedule(f, a); + } + void StartThread(void (*f)(void*), void* a) { + return target_->StartThread(f, a); + } + virtual Status GetTestDirectory(std::string* path) { + return target_->GetTestDirectory(path); + } + virtual Status NewLogger(const std::string& fname, Logger** result) { + return target_->NewLogger(fname, result); + } + uint64_t NowMicros() { + return target_->NowMicros(); + } + void SleepForMicroseconds(int micros) { + target_->SleepForMicroseconds(micros); + } + private: + Env* target_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_ENV_H_ diff --git a/src/leveldb/include/leveldb/filter_policy.h b/src/leveldb/include/leveldb/filter_policy.h new file mode 100644 index 0000000..1fba080 --- /dev/null +++ b/src/leveldb/include/leveldb/filter_policy.h @@ -0,0 +1,70 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A database can be configured with a custom FilterPolicy object. +// This object is responsible for creating a small filter from a set +// of keys. These filters are stored in leveldb and are consulted +// automatically by leveldb to decide whether or not to read some +// information from disk. In many cases, a filter can cut down the +// number of disk seeks form a handful to a single disk seek per +// DB::Get() call. +// +// Most people will want to use the builtin bloom filter support (see +// NewBloomFilterPolicy() below). + +#ifndef STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ +#define STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ + +#include + +namespace leveldb { + +class Slice; + +class FilterPolicy { + public: + virtual ~FilterPolicy(); + + // Return the name of this policy. Note that if the filter encoding + // changes in an incompatible way, the name returned by this method + // must be changed. Otherwise, old incompatible filters may be + // passed to methods of this type. + virtual const char* Name() const = 0; + + // keys[0,n-1] contains a list of keys (potentially with duplicates) + // that are ordered according to the user supplied comparator. + // Append a filter that summarizes keys[0,n-1] to *dst. + // + // Warning: do not change the initial contents of *dst. Instead, + // append the newly constructed filter to *dst. + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) + const = 0; + + // "filter" contains the data appended by a preceding call to + // CreateFilter() on this class. This method must return true if + // the key was in the list of keys passed to CreateFilter(). + // This method may return true or false if the key was not on the + // list, but it should aim to return false with a high probability. + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const = 0; +}; + +// Return a new filter policy that uses a bloom filter with approximately +// the specified number of bits per key. A good value for bits_per_key +// is 10, which yields a filter with ~ 1% false positive rate. +// +// Callers must delete the result after any database that is using the +// result has been closed. +// +// Note: if you are using a custom comparator that ignores some parts +// of the keys being compared, you must not use NewBloomFilterPolicy() +// and must provide your own FilterPolicy that also ignores the +// corresponding parts of the keys. For example, if the comparator +// ignores trailing spaces, it would be incorrect to use a +// FilterPolicy (like NewBloomFilterPolicy) that does not ignore +// trailing spaces in keys. +extern const FilterPolicy* NewBloomFilterPolicy(int bits_per_key); + +} + +#endif // STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ diff --git a/src/leveldb/include/leveldb/iterator.h b/src/leveldb/include/leveldb/iterator.h new file mode 100644 index 0000000..ad543eb --- /dev/null +++ b/src/leveldb/include/leveldb/iterator.h @@ -0,0 +1,100 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// An iterator yields a sequence of key/value pairs from a source. +// The following class defines the interface. Multiple implementations +// are provided by this library. In particular, iterators are provided +// to access the contents of a Table or a DB. +// +// Multiple threads can invoke const methods on an Iterator without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same Iterator must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ +#define STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ + +#include "leveldb/slice.h" +#include "leveldb/status.h" + +namespace leveldb { + +class Iterator { + public: + Iterator(); + virtual ~Iterator(); + + // An iterator is either positioned at a key/value pair, or + // not valid. This method returns true iff the iterator is valid. + virtual bool Valid() const = 0; + + // Position at the first key in the source. The iterator is Valid() + // after this call iff the source is not empty. + virtual void SeekToFirst() = 0; + + // Position at the last key in the source. The iterator is + // Valid() after this call iff the source is not empty. + virtual void SeekToLast() = 0; + + // Position at the first key in the source that at or past target + // The iterator is Valid() after this call iff the source contains + // an entry that comes at or past target. + virtual void Seek(const Slice& target) = 0; + + // Moves to the next entry in the source. After this call, Valid() is + // true iff the iterator was not positioned at the last entry in the source. + // REQUIRES: Valid() + virtual void Next() = 0; + + // Moves to the previous entry in the source. After this call, Valid() is + // true iff the iterator was not positioned at the first entry in source. + // REQUIRES: Valid() + virtual void Prev() = 0; + + // Return the key for the current entry. The underlying storage for + // the returned slice is valid only until the next modification of + // the iterator. + // REQUIRES: Valid() + virtual Slice key() const = 0; + + // Return the value for the current entry. The underlying storage for + // the returned slice is valid only until the next modification of + // the iterator. + // REQUIRES: !AtEnd() && !AtStart() + virtual Slice value() const = 0; + + // If an error has occurred, return it. Else return an ok status. + virtual Status status() const = 0; + + // Clients are allowed to register function/arg1/arg2 triples that + // will be invoked when this iterator is destroyed. + // + // Note that unlike all of the preceding methods, this method is + // not abstract and therefore clients should not override it. + typedef void (*CleanupFunction)(void* arg1, void* arg2); + void RegisterCleanup(CleanupFunction function, void* arg1, void* arg2); + + private: + struct Cleanup { + CleanupFunction function; + void* arg1; + void* arg2; + Cleanup* next; + }; + Cleanup cleanup_; + + // No copying allowed + Iterator(const Iterator&); + void operator=(const Iterator&); +}; + +// Return an empty iterator (yields nothing). +extern Iterator* NewEmptyIterator(); + +// Return an empty iterator with the specified status. +extern Iterator* NewErrorIterator(const Status& status); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ diff --git a/src/leveldb/include/leveldb/options.h b/src/leveldb/include/leveldb/options.h new file mode 100644 index 0000000..fdda718 --- /dev/null +++ b/src/leveldb/include/leveldb/options.h @@ -0,0 +1,195 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_OPTIONS_H_ +#define STORAGE_LEVELDB_INCLUDE_OPTIONS_H_ + +#include + +namespace leveldb { + +class Cache; +class Comparator; +class Env; +class FilterPolicy; +class Logger; +class Snapshot; + +// DB contents are stored in a set of blocks, each of which holds a +// sequence of key,value pairs. Each block may be compressed before +// being stored in a file. The following enum describes which +// compression method (if any) is used to compress a block. +enum CompressionType { + // NOTE: do not change the values of existing entries, as these are + // part of the persistent format on disk. + kNoCompression = 0x0, + kSnappyCompression = 0x1 +}; + +// Options to control the behavior of a database (passed to DB::Open) +struct Options { + // ------------------- + // Parameters that affect behavior + + // Comparator used to define the order of keys in the table. + // Default: a comparator that uses lexicographic byte-wise ordering + // + // REQUIRES: The client must ensure that the comparator supplied + // here has the same name and orders keys *exactly* the same as the + // comparator provided to previous open calls on the same DB. + const Comparator* comparator; + + // If true, the database will be created if it is missing. + // Default: false + bool create_if_missing; + + // If true, an error is raised if the database already exists. + // Default: false + bool error_if_exists; + + // If true, the implementation will do aggressive checking of the + // data it is processing and will stop early if it detects any + // errors. This may have unforeseen ramifications: for example, a + // corruption of one DB entry may cause a large number of entries to + // become unreadable or for the entire DB to become unopenable. + // Default: false + bool paranoid_checks; + + // Use the specified object to interact with the environment, + // e.g. to read/write files, schedule background work, etc. + // Default: Env::Default() + Env* env; + + // Any internal progress/error information generated by the db will + // be written to info_log if it is non-NULL, or to a file stored + // in the same directory as the DB contents if info_log is NULL. + // Default: NULL + Logger* info_log; + + // ------------------- + // Parameters that affect performance + + // Amount of data to build up in memory (backed by an unsorted log + // on disk) before converting to a sorted on-disk file. + // + // Larger values increase performance, especially during bulk loads. + // Up to two write buffers may be held in memory at the same time, + // so you may wish to adjust this parameter to control memory usage. + // Also, a larger write buffer will result in a longer recovery time + // the next time the database is opened. + // + // Default: 4MB + size_t write_buffer_size; + + // Number of open files that can be used by the DB. You may need to + // increase this if your database has a large working set (budget + // one open file per 2MB of working set). + // + // Default: 1000 + int max_open_files; + + // Control over blocks (user data is stored in a set of blocks, and + // a block is the unit of reading from disk). + + // If non-NULL, use the specified cache for blocks. + // If NULL, leveldb will automatically create and use an 8MB internal cache. + // Default: NULL + Cache* block_cache; + + // Approximate size of user data packed per block. Note that the + // block size specified here corresponds to uncompressed data. The + // actual size of the unit read from disk may be smaller if + // compression is enabled. This parameter can be changed dynamically. + // + // Default: 4K + size_t block_size; + + // Number of keys between restart points for delta encoding of keys. + // This parameter can be changed dynamically. Most clients should + // leave this parameter alone. + // + // Default: 16 + int block_restart_interval; + + // Compress blocks using the specified compression algorithm. This + // parameter can be changed dynamically. + // + // Default: kSnappyCompression, which gives lightweight but fast + // compression. + // + // Typical speeds of kSnappyCompression on an Intel(R) Core(TM)2 2.4GHz: + // ~200-500MB/s compression + // ~400-800MB/s decompression + // Note that these speeds are significantly faster than most + // persistent storage speeds, and therefore it is typically never + // worth switching to kNoCompression. Even if the input data is + // incompressible, the kSnappyCompression implementation will + // efficiently detect that and will switch to uncompressed mode. + CompressionType compression; + + // If non-NULL, use the specified filter policy to reduce disk reads. + // Many applications will benefit from passing the result of + // NewBloomFilterPolicy() here. + // + // Default: NULL + const FilterPolicy* filter_policy; + + // Create an Options object with default values for all fields. + Options(); +}; + +// Options that control read operations +struct ReadOptions { + // If true, all data read from underlying storage will be + // verified against corresponding checksums. + // Default: false + bool verify_checksums; + + // Should the data read for this iteration be cached in memory? + // Callers may wish to set this field to false for bulk scans. + // Default: true + bool fill_cache; + + // If "snapshot" is non-NULL, read as of the supplied snapshot + // (which must belong to the DB that is being read and which must + // not have been released). If "snapshot" is NULL, use an impliicit + // snapshot of the state at the beginning of this read operation. + // Default: NULL + const Snapshot* snapshot; + + ReadOptions() + : verify_checksums(false), + fill_cache(true), + snapshot(NULL) { + } +}; + +// Options that control write operations +struct WriteOptions { + // If true, the write will be flushed from the operating system + // buffer cache (by calling WritableFile::Sync()) before the write + // is considered complete. If this flag is true, writes will be + // slower. + // + // If this flag is false, and the machine crashes, some recent + // writes may be lost. Note that if it is just the process that + // crashes (i.e., the machine does not reboot), no writes will be + // lost even if sync==false. + // + // In other words, a DB write with sync==false has similar + // crash semantics as the "write()" system call. A DB write + // with sync==true has similar crash semantics to a "write()" + // system call followed by "fsync()". + // + // Default: false + bool sync; + + WriteOptions() + : sync(false) { + } +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_OPTIONS_H_ diff --git a/src/leveldb/include/leveldb/slice.h b/src/leveldb/include/leveldb/slice.h new file mode 100644 index 0000000..74ea8fa --- /dev/null +++ b/src/leveldb/include/leveldb/slice.h @@ -0,0 +1,109 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Slice is a simple structure containing a pointer into some external +// storage and a size. The user of a Slice must ensure that the slice +// is not used after the corresponding external storage has been +// deallocated. +// +// Multiple threads can invoke const methods on a Slice without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same Slice must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_SLICE_H_ +#define STORAGE_LEVELDB_INCLUDE_SLICE_H_ + +#include +#include +#include +#include + +namespace leveldb { + +class Slice { + public: + // Create an empty slice. + Slice() : data_(""), size_(0) { } + + // Create a slice that refers to d[0,n-1]. + Slice(const char* d, size_t n) : data_(d), size_(n) { } + + // Create a slice that refers to the contents of "s" + Slice(const std::string& s) : data_(s.data()), size_(s.size()) { } + + // Create a slice that refers to s[0,strlen(s)-1] + Slice(const char* s) : data_(s), size_(strlen(s)) { } + + // Return a pointer to the beginning of the referenced data + const char* data() const { return data_; } + + // Return the length (in bytes) of the referenced data + size_t size() const { return size_; } + + // Return true iff the length of the referenced data is zero + bool empty() const { return size_ == 0; } + + // Return the ith byte in the referenced data. + // REQUIRES: n < size() + char operator[](size_t n) const { + assert(n < size()); + return data_[n]; + } + + // Change this slice to refer to an empty array + void clear() { data_ = ""; size_ = 0; } + + // Drop the first "n" bytes from this slice. + void remove_prefix(size_t n) { + assert(n <= size()); + data_ += n; + size_ -= n; + } + + // Return a string that contains the copy of the referenced data. + std::string ToString() const { return std::string(data_, size_); } + + // Three-way comparison. Returns value: + // < 0 iff "*this" < "b", + // == 0 iff "*this" == "b", + // > 0 iff "*this" > "b" + int compare(const Slice& b) const; + + // Return true iff "x" is a prefix of "*this" + bool starts_with(const Slice& x) const { + return ((size_ >= x.size_) && + (memcmp(data_, x.data_, x.size_) == 0)); + } + + private: + const char* data_; + size_t size_; + + // Intentionally copyable +}; + +inline bool operator==(const Slice& x, const Slice& y) { + return ((x.size() == y.size()) && + (memcmp(x.data(), y.data(), x.size()) == 0)); +} + +inline bool operator!=(const Slice& x, const Slice& y) { + return !(x == y); +} + +inline int Slice::compare(const Slice& b) const { + const int min_len = (size_ < b.size_) ? size_ : b.size_; + int r = memcmp(data_, b.data_, min_len); + if (r == 0) { + if (size_ < b.size_) r = -1; + else if (size_ > b.size_) r = +1; + } + return r; +} + +} // namespace leveldb + + +#endif // STORAGE_LEVELDB_INCLUDE_SLICE_H_ diff --git a/src/leveldb/include/leveldb/status.h b/src/leveldb/include/leveldb/status.h new file mode 100644 index 0000000..11dbd4b --- /dev/null +++ b/src/leveldb/include/leveldb/status.h @@ -0,0 +1,106 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A Status encapsulates the result of an operation. It may indicate success, +// or it may indicate an error with an associated error message. +// +// Multiple threads can invoke const methods on a Status without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same Status must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_STATUS_H_ +#define STORAGE_LEVELDB_INCLUDE_STATUS_H_ + +#include +#include "leveldb/slice.h" + +namespace leveldb { + +class Status { + public: + // Create a success status. + Status() : state_(NULL) { } + ~Status() { delete[] state_; } + + // Copy the specified status. + Status(const Status& s); + void operator=(const Status& s); + + // Return a success status. + static Status OK() { return Status(); } + + // Return error status of an appropriate type. + static Status NotFound(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kNotFound, msg, msg2); + } + static Status Corruption(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kCorruption, msg, msg2); + } + static Status NotSupported(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kNotSupported, msg, msg2); + } + static Status InvalidArgument(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kInvalidArgument, msg, msg2); + } + static Status IOError(const Slice& msg, const Slice& msg2 = Slice()) { + return Status(kIOError, msg, msg2); + } + + // Returns true iff the status indicates success. + bool ok() const { return (state_ == NULL); } + + // Returns true iff the status indicates a NotFound error. + bool IsNotFound() const { return code() == kNotFound; } + + // Returns true iff the status indicates a Corruption error. + bool IsCorruption() const { return code() == kCorruption; } + + // Returns true iff the status indicates an IOError. + bool IsIOError() const { return code() == kIOError; } + + // Return a string representation of this status suitable for printing. + // Returns the string "OK" for success. + std::string ToString() const; + + private: + // OK status has a NULL state_. Otherwise, state_ is a new[] array + // of the following form: + // state_[0..3] == length of message + // state_[4] == code + // state_[5..] == message + const char* state_; + + enum Code { + kOk = 0, + kNotFound = 1, + kCorruption = 2, + kNotSupported = 3, + kInvalidArgument = 4, + kIOError = 5 + }; + + Code code() const { + return (state_ == NULL) ? kOk : static_cast(state_[4]); + } + + Status(Code code, const Slice& msg, const Slice& msg2); + static const char* CopyState(const char* s); +}; + +inline Status::Status(const Status& s) { + state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_); +} +inline void Status::operator=(const Status& s) { + // The following condition catches both aliasing (when this == &s), + // and the common case where both s and *this are ok. + if (state_ != s.state_) { + delete[] state_; + state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_); + } +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_STATUS_H_ diff --git a/src/leveldb/include/leveldb/table.h b/src/leveldb/include/leveldb/table.h new file mode 100644 index 0000000..a9746c3 --- /dev/null +++ b/src/leveldb/include/leveldb/table.h @@ -0,0 +1,85 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_TABLE_H_ +#define STORAGE_LEVELDB_INCLUDE_TABLE_H_ + +#include +#include "leveldb/iterator.h" + +namespace leveldb { + +class Block; +class BlockHandle; +class Footer; +struct Options; +class RandomAccessFile; +struct ReadOptions; +class TableCache; + +// A Table is a sorted map from strings to strings. Tables are +// immutable and persistent. A Table may be safely accessed from +// multiple threads without external synchronization. +class Table { + public: + // Attempt to open the table that is stored in bytes [0..file_size) + // of "file", and read the metadata entries necessary to allow + // retrieving data from the table. + // + // If successful, returns ok and sets "*table" to the newly opened + // table. The client should delete "*table" when no longer needed. + // If there was an error while initializing the table, sets "*table" + // to NULL and returns a non-ok status. Does not take ownership of + // "*source", but the client must ensure that "source" remains live + // for the duration of the returned table's lifetime. + // + // *file must remain live while this Table is in use. + static Status Open(const Options& options, + RandomAccessFile* file, + uint64_t file_size, + Table** table); + + ~Table(); + + // Returns a new iterator over the table contents. + // The result of NewIterator() is initially invalid (caller must + // call one of the Seek methods on the iterator before using it). + Iterator* NewIterator(const ReadOptions&) const; + + // Given a key, return an approximate byte offset in the file where + // the data for that key begins (or would begin if the key were + // present in the file). The returned value is in terms of file + // bytes, and so includes effects like compression of the underlying data. + // E.g., the approximate offset of the last key in the table will + // be close to the file length. + uint64_t ApproximateOffsetOf(const Slice& key) const; + + private: + struct Rep; + Rep* rep_; + + explicit Table(Rep* rep) { rep_ = rep; } + static Iterator* BlockReader(void*, const ReadOptions&, const Slice&); + + // Calls (*handle_result)(arg, ...) with the entry found after a call + // to Seek(key). May not make such a call if filter policy says + // that key is not present. + friend class TableCache; + Status InternalGet( + const ReadOptions&, const Slice& key, + void* arg, + void (*handle_result)(void* arg, const Slice& k, const Slice& v)); + + + void ReadMeta(const Footer& footer); + void ReadFilter(const Slice& filter_handle_value); + + // No copying allowed + Table(const Table&); + void operator=(const Table&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_TABLE_H_ diff --git a/src/leveldb/include/leveldb/table_builder.h b/src/leveldb/include/leveldb/table_builder.h new file mode 100644 index 0000000..5fd1dc7 --- /dev/null +++ b/src/leveldb/include/leveldb/table_builder.h @@ -0,0 +1,92 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// TableBuilder provides the interface used to build a Table +// (an immutable and sorted map from keys to values). +// +// Multiple threads can invoke const methods on a TableBuilder without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same TableBuilder must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_ +#define STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_ + +#include +#include "leveldb/options.h" +#include "leveldb/status.h" + +namespace leveldb { + +class BlockBuilder; +class BlockHandle; +class WritableFile; + +class TableBuilder { + public: + // Create a builder that will store the contents of the table it is + // building in *file. Does not close the file. It is up to the + // caller to close the file after calling Finish(). + TableBuilder(const Options& options, WritableFile* file); + + // REQUIRES: Either Finish() or Abandon() has been called. + ~TableBuilder(); + + // Change the options used by this builder. Note: only some of the + // option fields can be changed after construction. If a field is + // not allowed to change dynamically and its value in the structure + // passed to the constructor is different from its value in the + // structure passed to this method, this method will return an error + // without changing any fields. + Status ChangeOptions(const Options& options); + + // Add key,value to the table being constructed. + // REQUIRES: key is after any previously added key according to comparator. + // REQUIRES: Finish(), Abandon() have not been called + void Add(const Slice& key, const Slice& value); + + // Advanced operation: flush any buffered key/value pairs to file. + // Can be used to ensure that two adjacent entries never live in + // the same data block. Most clients should not need to use this method. + // REQUIRES: Finish(), Abandon() have not been called + void Flush(); + + // Return non-ok iff some error has been detected. + Status status() const; + + // Finish building the table. Stops using the file passed to the + // constructor after this function returns. + // REQUIRES: Finish(), Abandon() have not been called + Status Finish(); + + // Indicate that the contents of this builder should be abandoned. Stops + // using the file passed to the constructor after this function returns. + // If the caller is not going to call Finish(), it must call Abandon() + // before destroying this builder. + // REQUIRES: Finish(), Abandon() have not been called + void Abandon(); + + // Number of calls to Add() so far. + uint64_t NumEntries() const; + + // Size of the file generated so far. If invoked after a successful + // Finish() call, returns the size of the final generated file. + uint64_t FileSize() const; + + private: + bool ok() const { return status().ok(); } + void WriteBlock(BlockBuilder* block, BlockHandle* handle); + void WriteRawBlock(const Slice& data, CompressionType, BlockHandle* handle); + + struct Rep; + Rep* rep_; + + // No copying allowed + TableBuilder(const TableBuilder&); + void operator=(const TableBuilder&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_ diff --git a/src/leveldb/include/leveldb/write_batch.h b/src/leveldb/include/leveldb/write_batch.h new file mode 100644 index 0000000..ee9aab6 --- /dev/null +++ b/src/leveldb/include/leveldb/write_batch.h @@ -0,0 +1,64 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// WriteBatch holds a collection of updates to apply atomically to a DB. +// +// The updates are applied in the order in which they are added +// to the WriteBatch. For example, the value of "key" will be "v3" +// after the following batch is written: +// +// batch.Put("key", "v1"); +// batch.Delete("key"); +// batch.Put("key", "v2"); +// batch.Put("key", "v3"); +// +// Multiple threads can invoke const methods on a WriteBatch without +// external synchronization, but if any of the threads may call a +// non-const method, all threads accessing the same WriteBatch must use +// external synchronization. + +#ifndef STORAGE_LEVELDB_INCLUDE_WRITE_BATCH_H_ +#define STORAGE_LEVELDB_INCLUDE_WRITE_BATCH_H_ + +#include +#include "leveldb/status.h" + +namespace leveldb { + +class Slice; + +class WriteBatch { + public: + WriteBatch(); + ~WriteBatch(); + + // Store the mapping "key->value" in the database. + void Put(const Slice& key, const Slice& value); + + // If the database contains a mapping for "key", erase it. Else do nothing. + void Delete(const Slice& key); + + // Clear all updates buffered in this batch. + void Clear(); + + // Support for iterating over the contents of a batch. + class Handler { + public: + virtual ~Handler(); + virtual void Put(const Slice& key, const Slice& value) = 0; + virtual void Delete(const Slice& key) = 0; + }; + Status Iterate(Handler* handler) const; + + private: + friend class WriteBatchInternal; + + std::string rep_; // See comment in write_batch.cc for the format of rep_ + + // Intentionally copyable +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_WRITE_BATCH_H_ diff --git a/src/leveldb/port/README b/src/leveldb/port/README new file mode 100644 index 0000000..422563e --- /dev/null +++ b/src/leveldb/port/README @@ -0,0 +1,10 @@ +This directory contains interfaces and implementations that isolate the +rest of the package from platform details. + +Code in the rest of the package includes "port.h" from this directory. +"port.h" in turn includes a platform specific "port_.h" file +that provides the platform specific implementation. + +See port_posix.h for an example of what must be provided in a platform +specific header file. + diff --git a/src/leveldb/port/atomic_pointer.h b/src/leveldb/port/atomic_pointer.h new file mode 100644 index 0000000..e17bf43 --- /dev/null +++ b/src/leveldb/port/atomic_pointer.h @@ -0,0 +1,224 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// AtomicPointer provides storage for a lock-free pointer. +// Platform-dependent implementation of AtomicPointer: +// - If the platform provides a cheap barrier, we use it with raw pointers +// - If cstdatomic is present (on newer versions of gcc, it is), we use +// a cstdatomic-based AtomicPointer. However we prefer the memory +// barrier based version, because at least on a gcc 4.4 32-bit build +// on linux, we have encountered a buggy +// implementation. Also, some implementations are much +// slower than a memory-barrier based implementation (~16ns for +// based acquire-load vs. ~1ns for a barrier based +// acquire-load). +// This code is based on atomicops-internals-* in Google's perftools: +// http://code.google.com/p/google-perftools/source/browse/#svn%2Ftrunk%2Fsrc%2Fbase + +#ifndef PORT_ATOMIC_POINTER_H_ +#define PORT_ATOMIC_POINTER_H_ + +#include +#ifdef LEVELDB_CSTDATOMIC_PRESENT +#include +#endif +#ifdef OS_WIN +#include +#endif +#ifdef OS_MACOSX +#include +#endif + +#if defined(_M_X64) || defined(__x86_64__) +#define ARCH_CPU_X86_FAMILY 1 +#elif defined(_M_IX86) || defined(__i386__) || defined(__i386) +#define ARCH_CPU_X86_FAMILY 1 +#elif defined(__ARMEL__) +#define ARCH_CPU_ARM_FAMILY 1 +#elif defined(__ppc__) || defined(__powerpc__) || defined(__powerpc64__) +#define ARCH_CPU_PPC_FAMILY 1 +#endif + +namespace leveldb { +namespace port { + +// Define MemoryBarrier() if available +// Windows on x86 +#if defined(OS_WIN) && defined(COMPILER_MSVC) && defined(ARCH_CPU_X86_FAMILY) +// windows.h already provides a MemoryBarrier(void) macro +// http://msdn.microsoft.com/en-us/library/ms684208(v=vs.85).aspx +#define LEVELDB_HAVE_MEMORY_BARRIER + +// Gcc on x86 +#elif defined(ARCH_CPU_X86_FAMILY) && defined(__GNUC__) +inline void MemoryBarrier() { + // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on + // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering. + __asm__ __volatile__("" : : : "memory"); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +// Sun Studio +#elif defined(ARCH_CPU_X86_FAMILY) && defined(__SUNPRO_CC) +inline void MemoryBarrier() { + // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on + // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering. + asm volatile("" : : : "memory"); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +// Mac OS +#elif defined(OS_MACOSX) +inline void MemoryBarrier() { + OSMemoryBarrier(); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +// ARM Linux +#elif defined(ARCH_CPU_ARM_FAMILY) && defined(__linux__) +typedef void (*LinuxKernelMemoryBarrierFunc)(void); +// The Linux ARM kernel provides a highly optimized device-specific memory +// barrier function at a fixed memory address that is mapped in every +// user-level process. +// +// This beats using CPU-specific instructions which are, on single-core +// devices, un-necessary and very costly (e.g. ARMv7-A "dmb" takes more +// than 180ns on a Cortex-A8 like the one on a Nexus One). Benchmarking +// shows that the extra function call cost is completely negligible on +// multi-core devices. +// +inline void MemoryBarrier() { + (*(LinuxKernelMemoryBarrierFunc)0xffff0fa0)(); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +// PPC +#elif defined(ARCH_CPU_PPC_FAMILY) && defined(__GNUC__) +inline void MemoryBarrier() { + // TODO for some powerpc expert: is there a cheaper suitable variant? + // Perhaps by having separate barriers for acquire and release ops. + asm volatile("sync" : : : "memory"); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + +#endif + +// AtomicPointer built using platform-specific MemoryBarrier() +#if defined(LEVELDB_HAVE_MEMORY_BARRIER) +class AtomicPointer { + private: + void* rep_; + public: + AtomicPointer() { } + explicit AtomicPointer(void* p) : rep_(p) {} + inline void* NoBarrier_Load() const { return rep_; } + inline void NoBarrier_Store(void* v) { rep_ = v; } + inline void* Acquire_Load() const { + void* result = rep_; + MemoryBarrier(); + return result; + } + inline void Release_Store(void* v) { + MemoryBarrier(); + rep_ = v; + } +}; + +// AtomicPointer based on +#elif defined(LEVELDB_CSTDATOMIC_PRESENT) +class AtomicPointer { + private: + std::atomic rep_; + public: + AtomicPointer() { } + explicit AtomicPointer(void* v) : rep_(v) { } + inline void* Acquire_Load() const { + return rep_.load(std::memory_order_acquire); + } + inline void Release_Store(void* v) { + rep_.store(v, std::memory_order_release); + } + inline void* NoBarrier_Load() const { + return rep_.load(std::memory_order_relaxed); + } + inline void NoBarrier_Store(void* v) { + rep_.store(v, std::memory_order_relaxed); + } +}; + +// Atomic pointer based on sparc memory barriers +#elif defined(__sparcv9) && defined(__GNUC__) +class AtomicPointer { + private: + void* rep_; + public: + AtomicPointer() { } + explicit AtomicPointer(void* v) : rep_(v) { } + inline void* Acquire_Load() const { + void* val; + __asm__ __volatile__ ( + "ldx [%[rep_]], %[val] \n\t" + "membar #LoadLoad|#LoadStore \n\t" + : [val] "=r" (val) + : [rep_] "r" (&rep_) + : "memory"); + return val; + } + inline void Release_Store(void* v) { + __asm__ __volatile__ ( + "membar #LoadStore|#StoreStore \n\t" + "stx %[v], [%[rep_]] \n\t" + : + : [rep_] "r" (&rep_), [v] "r" (v) + : "memory"); + } + inline void* NoBarrier_Load() const { return rep_; } + inline void NoBarrier_Store(void* v) { rep_ = v; } +}; + +// Atomic pointer based on ia64 acq/rel +#elif defined(__ia64) && defined(__GNUC__) +class AtomicPointer { + private: + void* rep_; + public: + AtomicPointer() { } + explicit AtomicPointer(void* v) : rep_(v) { } + inline void* Acquire_Load() const { + void* val ; + __asm__ __volatile__ ( + "ld8.acq %[val] = [%[rep_]] \n\t" + : [val] "=r" (val) + : [rep_] "r" (&rep_) + : "memory" + ); + return val; + } + inline void Release_Store(void* v) { + __asm__ __volatile__ ( + "st8.rel [%[rep_]] = %[v] \n\t" + : + : [rep_] "r" (&rep_), [v] "r" (v) + : "memory" + ); + } + inline void* NoBarrier_Load() const { return rep_; } + inline void NoBarrier_Store(void* v) { rep_ = v; } +}; + +// We have neither MemoryBarrier(), nor +#else +#error Please implement AtomicPointer for this platform. + +#endif + +#undef LEVELDB_HAVE_MEMORY_BARRIER +#undef ARCH_CPU_X86_FAMILY +#undef ARCH_CPU_ARM_FAMILY +#undef ARCH_CPU_PPC_FAMILY + +} // namespace port +} // namespace leveldb + +#endif // PORT_ATOMIC_POINTER_H_ diff --git a/src/leveldb/port/port.h b/src/leveldb/port/port.h new file mode 100644 index 0000000..4baafa8 --- /dev/null +++ b/src/leveldb/port/port.h @@ -0,0 +1,21 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_PORT_PORT_H_ +#define STORAGE_LEVELDB_PORT_PORT_H_ + +#include + +// Include the appropriate platform specific file below. If you are +// porting to a new platform, see "port_example.h" for documentation +// of what the new port_.h file must provide. +#if defined(LEVELDB_PLATFORM_POSIX) +# include "port/port_posix.h" +#elif defined(LEVELDB_PLATFORM_CHROMIUM) +# include "port/port_chromium.h" +#elif defined(LEVELDB_PLATFORM_WINDOWS) +# include "port/port_win.h" +#endif + +#endif // STORAGE_LEVELDB_PORT_PORT_H_ diff --git a/src/leveldb/port/port_example.h b/src/leveldb/port/port_example.h new file mode 100644 index 0000000..ab9e489 --- /dev/null +++ b/src/leveldb/port/port_example.h @@ -0,0 +1,135 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// This file contains the specification, but not the implementations, +// of the types/operations/etc. that should be defined by a platform +// specific port_.h file. Use this file as a reference for +// how to port this package to a new platform. + +#ifndef STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_ +#define STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_ + +namespace leveldb { +namespace port { + +// TODO(jorlow): Many of these belong more in the environment class rather than +// here. We should try moving them and see if it affects perf. + +// The following boolean constant must be true on a little-endian machine +// and false otherwise. +static const bool kLittleEndian = true /* or some other expression */; + +// ------------------ Threading ------------------- + +// A Mutex represents an exclusive lock. +class Mutex { + public: + Mutex(); + ~Mutex(); + + // Lock the mutex. Waits until other lockers have exited. + // Will deadlock if the mutex is already locked by this thread. + void Lock(); + + // Unlock the mutex. + // REQUIRES: This mutex was locked by this thread. + void Unlock(); + + // Optionally crash if this thread does not hold this mutex. + // The implementation must be fast, especially if NDEBUG is + // defined. The implementation is allowed to skip all checks. + void AssertHeld(); +}; + +class CondVar { + public: + explicit CondVar(Mutex* mu); + ~CondVar(); + + // Atomically release *mu and block on this condition variable until + // either a call to SignalAll(), or a call to Signal() that picks + // this thread to wakeup. + // REQUIRES: this thread holds *mu + void Wait(); + + // If there are some threads waiting, wake up at least one of them. + void Signal(); + + // Wake up all waiting threads. + void SignallAll(); +}; + +// Thread-safe initialization. +// Used as follows: +// static port::OnceType init_control = LEVELDB_ONCE_INIT; +// static void Initializer() { ... do something ...; } +// ... +// port::InitOnce(&init_control, &Initializer); +typedef intptr_t OnceType; +#define LEVELDB_ONCE_INIT 0 +extern void InitOnce(port::OnceType*, void (*initializer)()); + +// A type that holds a pointer that can be read or written atomically +// (i.e., without word-tearing.) +class AtomicPointer { + private: + intptr_t rep_; + public: + // Initialize to arbitrary value + AtomicPointer(); + + // Initialize to hold v + explicit AtomicPointer(void* v) : rep_(v) { } + + // Read and return the stored pointer with the guarantee that no + // later memory access (read or write) by this thread can be + // reordered ahead of this read. + void* Acquire_Load() const; + + // Set v as the stored pointer with the guarantee that no earlier + // memory access (read or write) by this thread can be reordered + // after this store. + void Release_Store(void* v); + + // Read the stored pointer with no ordering guarantees. + void* NoBarrier_Load() const; + + // Set va as the stored pointer with no ordering guarantees. + void NoBarrier_Store(void* v); +}; + +// ------------------ Compression ------------------- + +// Store the snappy compression of "input[0,input_length-1]" in *output. +// Returns false if snappy is not supported by this port. +extern bool Snappy_Compress(const char* input, size_t input_length, + std::string* output); + +// If input[0,input_length-1] looks like a valid snappy compressed +// buffer, store the size of the uncompressed data in *result and +// return true. Else return false. +extern bool Snappy_GetUncompressedLength(const char* input, size_t length, + size_t* result); + +// Attempt to snappy uncompress input[0,input_length-1] into *output. +// Returns true if successful, false if the input is invalid lightweight +// compressed data. +// +// REQUIRES: at least the first "n" bytes of output[] must be writable +// where "n" is the result of a successful call to +// Snappy_GetUncompressedLength. +extern bool Snappy_Uncompress(const char* input_data, size_t input_length, + char* output); + +// ------------------ Miscellaneous ------------------- + +// If heap profiling is not supported, returns false. +// Else repeatedly calls (*func)(arg, data, n) and then returns true. +// The concatenation of all "data[0,n-1]" fragments is the heap profile. +extern bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg); + +} // namespace port +} // namespace leveldb + +#endif // STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_ diff --git a/src/leveldb/port/port_posix.cc b/src/leveldb/port/port_posix.cc new file mode 100644 index 0000000..5ba127a --- /dev/null +++ b/src/leveldb/port/port_posix.cc @@ -0,0 +1,54 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "port/port_posix.h" + +#include +#include +#include +#include "util/logging.h" + +namespace leveldb { +namespace port { + +static void PthreadCall(const char* label, int result) { + if (result != 0) { + fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); + abort(); + } +} + +Mutex::Mutex() { PthreadCall("init mutex", pthread_mutex_init(&mu_, NULL)); } + +Mutex::~Mutex() { PthreadCall("destroy mutex", pthread_mutex_destroy(&mu_)); } + +void Mutex::Lock() { PthreadCall("lock", pthread_mutex_lock(&mu_)); } + +void Mutex::Unlock() { PthreadCall("unlock", pthread_mutex_unlock(&mu_)); } + +CondVar::CondVar(Mutex* mu) + : mu_(mu) { + PthreadCall("init cv", pthread_cond_init(&cv_, NULL)); +} + +CondVar::~CondVar() { PthreadCall("destroy cv", pthread_cond_destroy(&cv_)); } + +void CondVar::Wait() { + PthreadCall("wait", pthread_cond_wait(&cv_, &mu_->mu_)); +} + +void CondVar::Signal() { + PthreadCall("signal", pthread_cond_signal(&cv_)); +} + +void CondVar::SignalAll() { + PthreadCall("broadcast", pthread_cond_broadcast(&cv_)); +} + +void InitOnce(OnceType* once, void (*initializer)()) { + PthreadCall("once", pthread_once(once, initializer)); +} + +} // namespace port +} // namespace leveldb diff --git a/src/leveldb/port/port_posix.h b/src/leveldb/port/port_posix.h new file mode 100644 index 0000000..f2b89bf --- /dev/null +++ b/src/leveldb/port/port_posix.h @@ -0,0 +1,157 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// See port_example.h for documentation for the following types/functions. + +#ifndef STORAGE_LEVELDB_PORT_PORT_POSIX_H_ +#define STORAGE_LEVELDB_PORT_PORT_POSIX_H_ + +#undef PLATFORM_IS_LITTLE_ENDIAN +#if defined(OS_MACOSX) + #include + #if defined(__DARWIN_LITTLE_ENDIAN) && defined(__DARWIN_BYTE_ORDER) + #define PLATFORM_IS_LITTLE_ENDIAN \ + (__DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN) + #endif +#elif defined(OS_SOLARIS) + #include + #ifdef _LITTLE_ENDIAN + #define PLATFORM_IS_LITTLE_ENDIAN true + #else + #define PLATFORM_IS_LITTLE_ENDIAN false + #endif +#elif defined(OS_FREEBSD) + #include + #include + #define PLATFORM_IS_LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN) +#elif defined(OS_OPENBSD) || defined(OS_NETBSD) ||\ + defined(OS_DRAGONFLYBSD) + #include + #include +#elif defined(OS_HPUX) + #define PLATFORM_IS_LITTLE_ENDIAN false +#elif defined(OS_ANDROID) + // Due to a bug in the NDK x86 definition, + // _BYTE_ORDER must be used instead of __BYTE_ORDER on Android. + // See http://code.google.com/p/android/issues/detail?id=39824 + #include + #define PLATFORM_IS_LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN) +#else + #include +#endif + +#include +#ifdef SNAPPY +#include +#endif +#include +#include +#include "port/atomic_pointer.h" + +#ifndef PLATFORM_IS_LITTLE_ENDIAN +#define PLATFORM_IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN) +#endif + +#if defined(OS_MACOSX) || defined(OS_SOLARIS) || defined(OS_FREEBSD) ||\ + defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) ||\ + defined(OS_ANDROID) || defined(OS_HPUX) +// Use fread/fwrite/fflush on platforms without _unlocked variants +#define fread_unlocked fread +#define fwrite_unlocked fwrite +#define fflush_unlocked fflush +#endif + +#if defined(OS_MACOSX) || defined(OS_FREEBSD) ||\ + defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) +// Use fsync() on platforms without fdatasync() +#define fdatasync fsync +#endif + +#if defined(OS_ANDROID) && __ANDROID_API__ < 9 +// fdatasync() was only introduced in API level 9 on Android. Use fsync() +// when targetting older platforms. +#define fdatasync fsync +#endif + +namespace leveldb { +namespace port { + +static const bool kLittleEndian = PLATFORM_IS_LITTLE_ENDIAN; +#undef PLATFORM_IS_LITTLE_ENDIAN + +class CondVar; + +class Mutex { + public: + Mutex(); + ~Mutex(); + + void Lock(); + void Unlock(); + void AssertHeld() { } + + private: + friend class CondVar; + pthread_mutex_t mu_; + + // No copying + Mutex(const Mutex&); + void operator=(const Mutex&); +}; + +class CondVar { + public: + explicit CondVar(Mutex* mu); + ~CondVar(); + void Wait(); + void Signal(); + void SignalAll(); + private: + pthread_cond_t cv_; + Mutex* mu_; +}; + +typedef pthread_once_t OnceType; +#define LEVELDB_ONCE_INIT PTHREAD_ONCE_INIT +extern void InitOnce(OnceType* once, void (*initializer)()); + +inline bool Snappy_Compress(const char* input, size_t length, + ::std::string* output) { +#ifdef SNAPPY + output->resize(snappy::MaxCompressedLength(length)); + size_t outlen; + snappy::RawCompress(input, length, &(*output)[0], &outlen); + output->resize(outlen); + return true; +#endif + + return false; +} + +inline bool Snappy_GetUncompressedLength(const char* input, size_t length, + size_t* result) { +#ifdef SNAPPY + return snappy::GetUncompressedLength(input, length, result); +#else + return false; +#endif +} + +inline bool Snappy_Uncompress(const char* input, size_t length, + char* output) { +#ifdef SNAPPY + return snappy::RawUncompress(input, length, output); +#else + return false; +#endif +} + +inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { + return false; +} + +} // namespace port +} // namespace leveldb + +#endif // STORAGE_LEVELDB_PORT_PORT_POSIX_H_ diff --git a/src/leveldb/port/port_win.cc b/src/leveldb/port/port_win.cc new file mode 100644 index 0000000..99c1d8e --- /dev/null +++ b/src/leveldb/port/port_win.cc @@ -0,0 +1,149 @@ +// LevelDB Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// See port_example.h for documentation for the following types/functions. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the University of California, Berkeley nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "port/port_win.h" + +#include +#include + +namespace leveldb { +namespace port { + +Mutex::Mutex() : + cs_(NULL) { + assert(!cs_); + cs_ = static_cast(new CRITICAL_SECTION()); + ::InitializeCriticalSection(static_cast(cs_)); + assert(cs_); +} + +Mutex::~Mutex() { + assert(cs_); + ::DeleteCriticalSection(static_cast(cs_)); + delete static_cast(cs_); + cs_ = NULL; + assert(!cs_); +} + +void Mutex::Lock() { + assert(cs_); + ::EnterCriticalSection(static_cast(cs_)); +} + +void Mutex::Unlock() { + assert(cs_); + ::LeaveCriticalSection(static_cast(cs_)); +} + +void Mutex::AssertHeld() { + assert(cs_); + assert(1); +} + +CondVar::CondVar(Mutex* mu) : + waiting_(0), + mu_(mu), + sem1_(::CreateSemaphore(NULL, 0, 10000, NULL)), + sem2_(::CreateSemaphore(NULL, 0, 10000, NULL)) { + assert(mu_); +} + +CondVar::~CondVar() { + ::CloseHandle(sem1_); + ::CloseHandle(sem2_); +} + +void CondVar::Wait() { + mu_->AssertHeld(); + + wait_mtx_.Lock(); + ++waiting_; + wait_mtx_.Unlock(); + + mu_->Unlock(); + + // initiate handshake + ::WaitForSingleObject(sem1_, INFINITE); + ::ReleaseSemaphore(sem2_, 1, NULL); + mu_->Lock(); +} + +void CondVar::Signal() { + wait_mtx_.Lock(); + if (waiting_ > 0) { + --waiting_; + + // finalize handshake + ::ReleaseSemaphore(sem1_, 1, NULL); + ::WaitForSingleObject(sem2_, INFINITE); + } + wait_mtx_.Unlock(); +} + +void CondVar::SignalAll() { + wait_mtx_.Lock(); + for(long i = 0; i < waiting_; ++i) { + ::ReleaseSemaphore(sem1_, 1, NULL); + while(waiting_ > 0) { + --waiting_; + ::WaitForSingleObject(sem2_, INFINITE); + } + } + wait_mtx_.Unlock(); +} + +AtomicPointer::AtomicPointer(void* v) { + Release_Store(v); +} + +void InitOnce(OnceType* once, void (*initializer)()) { + once->InitOnce(initializer); +} + +void* AtomicPointer::Acquire_Load() const { + void * p = NULL; + InterlockedExchangePointer(&p, rep_); + return p; +} + +void AtomicPointer::Release_Store(void* v) { + InterlockedExchangePointer(&rep_, v); +} + +void* AtomicPointer::NoBarrier_Load() const { + return rep_; +} + +void AtomicPointer::NoBarrier_Store(void* v) { + rep_ = v; +} + +} +} diff --git a/src/leveldb/port/port_win.h b/src/leveldb/port/port_win.h new file mode 100644 index 0000000..45bf2f0 --- /dev/null +++ b/src/leveldb/port/port_win.h @@ -0,0 +1,174 @@ +// LevelDB Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// See port_example.h for documentation for the following types/functions. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the University of California, Berkeley nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef STORAGE_LEVELDB_PORT_PORT_WIN_H_ +#define STORAGE_LEVELDB_PORT_PORT_WIN_H_ + +#ifdef _MSC_VER +#define snprintf _snprintf +#define close _close +#define fread_unlocked _fread_nolock +#endif + +#include +#include +#ifdef SNAPPY +#include +#endif + +namespace leveldb { +namespace port { + +// Windows is little endian (for now :p) +static const bool kLittleEndian = true; + +class CondVar; + +class Mutex { + public: + Mutex(); + ~Mutex(); + + void Lock(); + void Unlock(); + void AssertHeld(); + + private: + friend class CondVar; + // critical sections are more efficient than mutexes + // but they are not recursive and can only be used to synchronize threads within the same process + // we use opaque void * to avoid including windows.h in port_win.h + void * cs_; + + // No copying + Mutex(const Mutex&); + void operator=(const Mutex&); +}; + +// the Win32 API offers a dependable condition variable mechanism, but only starting with +// Windows 2008 and Vista +// no matter what we will implement our own condition variable with a semaphore +// implementation as described in a paper written by Andrew D. Birrell in 2003 +class CondVar { + public: + explicit CondVar(Mutex* mu); + ~CondVar(); + void Wait(); + void Signal(); + void SignalAll(); + private: + Mutex* mu_; + + Mutex wait_mtx_; + long waiting_; + + void * sem1_; + void * sem2_; + + +}; + +class OnceType { +public: +// OnceType() : init_(false) {} + OnceType(const OnceType &once) : init_(once.init_) {} + OnceType(bool f) : init_(f) {} + void InitOnce(void (*initializer)()) { + mutex_.Lock(); + if (!init_) { + init_ = true; + initializer(); + } + mutex_.Unlock(); + } + +private: + bool init_; + Mutex mutex_; +}; + +#define LEVELDB_ONCE_INIT false +extern void InitOnce(port::OnceType*, void (*initializer)()); + +// Storage for a lock-free pointer +class AtomicPointer { + private: + void * rep_; + public: + AtomicPointer() : rep_(NULL) { } + explicit AtomicPointer(void* v); + void* Acquire_Load() const; + + void Release_Store(void* v); + + void* NoBarrier_Load() const; + + void NoBarrier_Store(void* v); +}; + +inline bool Snappy_Compress(const char* input, size_t length, + ::std::string* output) { +#ifdef SNAPPY + output->resize(snappy::MaxCompressedLength(length)); + size_t outlen; + snappy::RawCompress(input, length, &(*output)[0], &outlen); + output->resize(outlen); + return true; +#endif + + return false; +} + +inline bool Snappy_GetUncompressedLength(const char* input, size_t length, + size_t* result) { +#ifdef SNAPPY + return snappy::GetUncompressedLength(input, length, result); +#else + return false; +#endif +} + +inline bool Snappy_Uncompress(const char* input, size_t length, + char* output) { +#ifdef SNAPPY + return snappy::RawUncompress(input, length, output); +#else + return false; +#endif +} + +inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { + return false; +} + +} +} + +#endif // STORAGE_LEVELDB_PORT_PORT_WIN_H_ diff --git a/src/leveldb/port/thread_annotations.h b/src/leveldb/port/thread_annotations.h new file mode 100644 index 0000000..6f9b6a7 --- /dev/null +++ b/src/leveldb/port/thread_annotations.h @@ -0,0 +1,59 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H + +// Some environments provide custom macros to aid in static thread-safety +// analysis. Provide empty definitions of such macros unless they are already +// defined. + +#ifndef EXCLUSIVE_LOCKS_REQUIRED +#define EXCLUSIVE_LOCKS_REQUIRED(...) +#endif + +#ifndef SHARED_LOCKS_REQUIRED +#define SHARED_LOCKS_REQUIRED(...) +#endif + +#ifndef LOCKS_EXCLUDED +#define LOCKS_EXCLUDED(...) +#endif + +#ifndef LOCK_RETURNED +#define LOCK_RETURNED(x) +#endif + +#ifndef LOCKABLE +#define LOCKABLE +#endif + +#ifndef SCOPED_LOCKABLE +#define SCOPED_LOCKABLE +#endif + +#ifndef EXCLUSIVE_LOCK_FUNCTION +#define EXCLUSIVE_LOCK_FUNCTION(...) +#endif + +#ifndef SHARED_LOCK_FUNCTION +#define SHARED_LOCK_FUNCTION(...) +#endif + +#ifndef EXCLUSIVE_TRYLOCK_FUNCTION +#define EXCLUSIVE_TRYLOCK_FUNCTION(...) +#endif + +#ifndef SHARED_TRYLOCK_FUNCTION +#define SHARED_TRYLOCK_FUNCTION(...) +#endif + +#ifndef UNLOCK_FUNCTION +#define UNLOCK_FUNCTION(...) +#endif + +#ifndef NO_THREAD_SAFETY_ANALYSIS +#define NO_THREAD_SAFETY_ANALYSIS +#endif + +#endif // STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H diff --git a/src/leveldb/port/win/stdint.h b/src/leveldb/port/win/stdint.h new file mode 100644 index 0000000..39edd0d --- /dev/null +++ b/src/leveldb/port/win/stdint.h @@ -0,0 +1,24 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// MSVC didn't ship with this file until the 2010 version. + +#ifndef STORAGE_LEVELDB_PORT_WIN_STDINT_H_ +#define STORAGE_LEVELDB_PORT_WIN_STDINT_H_ + +#if !defined(_MSC_VER) +#error This file should only be included when compiling with MSVC. +#endif + +// Define C99 equivalent types. +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long long int64_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +#endif // STORAGE_LEVELDB_PORT_WIN_STDINT_H_ diff --git a/src/leveldb/table/block.cc b/src/leveldb/table/block.cc new file mode 100644 index 0000000..ab83c11 --- /dev/null +++ b/src/leveldb/table/block.cc @@ -0,0 +1,267 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Decodes the blocks generated by block_builder.cc. + +#include "table/block.h" + +#include +#include +#include "leveldb/comparator.h" +#include "table/format.h" +#include "util/coding.h" +#include "util/logging.h" + +namespace leveldb { + +inline uint32_t Block::NumRestarts() const { + assert(size_ >= 2*sizeof(uint32_t)); + return DecodeFixed32(data_ + size_ - sizeof(uint32_t)); +} + +Block::Block(const BlockContents& contents) + : data_(contents.data.data()), + size_(contents.data.size()), + owned_(contents.heap_allocated) { + if (size_ < sizeof(uint32_t)) { + size_ = 0; // Error marker + } else { + restart_offset_ = size_ - (1 + NumRestarts()) * sizeof(uint32_t); + if (restart_offset_ > size_ - sizeof(uint32_t)) { + // The size is too small for NumRestarts() and therefore + // restart_offset_ wrapped around. + size_ = 0; + } + } +} + +Block::~Block() { + if (owned_) { + delete[] data_; + } +} + +// Helper routine: decode the next block entry starting at "p", +// storing the number of shared key bytes, non_shared key bytes, +// and the length of the value in "*shared", "*non_shared", and +// "*value_length", respectively. Will not derefence past "limit". +// +// If any errors are detected, returns NULL. Otherwise, returns a +// pointer to the key delta (just past the three decoded values). +static inline const char* DecodeEntry(const char* p, const char* limit, + uint32_t* shared, + uint32_t* non_shared, + uint32_t* value_length) { + if (limit - p < 3) return NULL; + *shared = reinterpret_cast(p)[0]; + *non_shared = reinterpret_cast(p)[1]; + *value_length = reinterpret_cast(p)[2]; + if ((*shared | *non_shared | *value_length) < 128) { + // Fast path: all three values are encoded in one byte each + p += 3; + } else { + if ((p = GetVarint32Ptr(p, limit, shared)) == NULL) return NULL; + if ((p = GetVarint32Ptr(p, limit, non_shared)) == NULL) return NULL; + if ((p = GetVarint32Ptr(p, limit, value_length)) == NULL) return NULL; + } + + if (static_cast(limit - p) < (*non_shared + *value_length)) { + return NULL; + } + return p; +} + +class Block::Iter : public Iterator { + private: + const Comparator* const comparator_; + const char* const data_; // underlying block contents + uint32_t const restarts_; // Offset of restart array (list of fixed32) + uint32_t const num_restarts_; // Number of uint32_t entries in restart array + + // current_ is offset in data_ of current entry. >= restarts_ if !Valid + uint32_t current_; + uint32_t restart_index_; // Index of restart block in which current_ falls + std::string key_; + Slice value_; + Status status_; + + inline int Compare(const Slice& a, const Slice& b) const { + return comparator_->Compare(a, b); + } + + // Return the offset in data_ just past the end of the current entry. + inline uint32_t NextEntryOffset() const { + return (value_.data() + value_.size()) - data_; + } + + uint32_t GetRestartPoint(uint32_t index) { + assert(index < num_restarts_); + return DecodeFixed32(data_ + restarts_ + index * sizeof(uint32_t)); + } + + void SeekToRestartPoint(uint32_t index) { + key_.clear(); + restart_index_ = index; + // current_ will be fixed by ParseNextKey(); + + // ParseNextKey() starts at the end of value_, so set value_ accordingly + uint32_t offset = GetRestartPoint(index); + value_ = Slice(data_ + offset, 0); + } + + public: + Iter(const Comparator* comparator, + const char* data, + uint32_t restarts, + uint32_t num_restarts) + : comparator_(comparator), + data_(data), + restarts_(restarts), + num_restarts_(num_restarts), + current_(restarts_), + restart_index_(num_restarts_) { + assert(num_restarts_ > 0); + } + + virtual bool Valid() const { return current_ < restarts_; } + virtual Status status() const { return status_; } + virtual Slice key() const { + assert(Valid()); + return key_; + } + virtual Slice value() const { + assert(Valid()); + return value_; + } + + virtual void Next() { + assert(Valid()); + ParseNextKey(); + } + + virtual void Prev() { + assert(Valid()); + + // Scan backwards to a restart point before current_ + const uint32_t original = current_; + while (GetRestartPoint(restart_index_) >= original) { + if (restart_index_ == 0) { + // No more entries + current_ = restarts_; + restart_index_ = num_restarts_; + return; + } + restart_index_--; + } + + SeekToRestartPoint(restart_index_); + do { + // Loop until end of current entry hits the start of original entry + } while (ParseNextKey() && NextEntryOffset() < original); + } + + virtual void Seek(const Slice& target) { + // Binary search in restart array to find the last restart point + // with a key < target + uint32_t left = 0; + uint32_t right = num_restarts_ - 1; + while (left < right) { + uint32_t mid = (left + right + 1) / 2; + uint32_t region_offset = GetRestartPoint(mid); + uint32_t shared, non_shared, value_length; + const char* key_ptr = DecodeEntry(data_ + region_offset, + data_ + restarts_, + &shared, &non_shared, &value_length); + if (key_ptr == NULL || (shared != 0)) { + CorruptionError(); + return; + } + Slice mid_key(key_ptr, non_shared); + if (Compare(mid_key, target) < 0) { + // Key at "mid" is smaller than "target". Therefore all + // blocks before "mid" are uninteresting. + left = mid; + } else { + // Key at "mid" is >= "target". Therefore all blocks at or + // after "mid" are uninteresting. + right = mid - 1; + } + } + + // Linear search (within restart block) for first key >= target + SeekToRestartPoint(left); + while (true) { + if (!ParseNextKey()) { + return; + } + if (Compare(key_, target) >= 0) { + return; + } + } + } + + virtual void SeekToFirst() { + SeekToRestartPoint(0); + ParseNextKey(); + } + + virtual void SeekToLast() { + SeekToRestartPoint(num_restarts_ - 1); + while (ParseNextKey() && NextEntryOffset() < restarts_) { + // Keep skipping + } + } + + private: + void CorruptionError() { + current_ = restarts_; + restart_index_ = num_restarts_; + status_ = Status::Corruption("bad entry in block"); + key_.clear(); + value_.clear(); + } + + bool ParseNextKey() { + current_ = NextEntryOffset(); + const char* p = data_ + current_; + const char* limit = data_ + restarts_; // Restarts come right after data + if (p >= limit) { + // No more entries to return. Mark as invalid. + current_ = restarts_; + restart_index_ = num_restarts_; + return false; + } + + // Decode next entry + uint32_t shared, non_shared, value_length; + p = DecodeEntry(p, limit, &shared, &non_shared, &value_length); + if (p == NULL || key_.size() < shared) { + CorruptionError(); + return false; + } else { + key_.resize(shared); + key_.append(p, non_shared); + value_ = Slice(p + non_shared, value_length); + while (restart_index_ + 1 < num_restarts_ && + GetRestartPoint(restart_index_ + 1) < current_) { + ++restart_index_; + } + return true; + } + } +}; + +Iterator* Block::NewIterator(const Comparator* cmp) { + if (size_ < 2*sizeof(uint32_t)) { + return NewErrorIterator(Status::Corruption("bad block contents")); + } + const uint32_t num_restarts = NumRestarts(); + if (num_restarts == 0) { + return NewEmptyIterator(); + } else { + return new Iter(cmp, data_, restart_offset_, num_restarts); + } +} + +} // namespace leveldb diff --git a/src/leveldb/table/block.h b/src/leveldb/table/block.h new file mode 100644 index 0000000..2493eb9 --- /dev/null +++ b/src/leveldb/table/block.h @@ -0,0 +1,44 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_BLOCK_H_ +#define STORAGE_LEVELDB_TABLE_BLOCK_H_ + +#include +#include +#include "leveldb/iterator.h" + +namespace leveldb { + +struct BlockContents; +class Comparator; + +class Block { + public: + // Initialize the block with the specified contents. + explicit Block(const BlockContents& contents); + + ~Block(); + + size_t size() const { return size_; } + Iterator* NewIterator(const Comparator* comparator); + + private: + uint32_t NumRestarts() const; + + const char* data_; + size_t size_; + uint32_t restart_offset_; // Offset in data_ of restart array + bool owned_; // Block owns data_[] + + // No copying allowed + Block(const Block&); + void operator=(const Block&); + + class Iter; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_BLOCK_H_ diff --git a/src/leveldb/table/block_builder.cc b/src/leveldb/table/block_builder.cc new file mode 100644 index 0000000..db660cd --- /dev/null +++ b/src/leveldb/table/block_builder.cc @@ -0,0 +1,109 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// BlockBuilder generates blocks where keys are prefix-compressed: +// +// When we store a key, we drop the prefix shared with the previous +// string. This helps reduce the space requirement significantly. +// Furthermore, once every K keys, we do not apply the prefix +// compression and store the entire key. We call this a "restart +// point". The tail end of the block stores the offsets of all of the +// restart points, and can be used to do a binary search when looking +// for a particular key. Values are stored as-is (without compression) +// immediately following the corresponding key. +// +// An entry for a particular key-value pair has the form: +// shared_bytes: varint32 +// unshared_bytes: varint32 +// value_length: varint32 +// key_delta: char[unshared_bytes] +// value: char[value_length] +// shared_bytes == 0 for restart points. +// +// The trailer of the block has the form: +// restarts: uint32[num_restarts] +// num_restarts: uint32 +// restarts[i] contains the offset within the block of the ith restart point. + +#include "table/block_builder.h" + +#include +#include +#include "leveldb/comparator.h" +#include "leveldb/table_builder.h" +#include "util/coding.h" + +namespace leveldb { + +BlockBuilder::BlockBuilder(const Options* options) + : options_(options), + restarts_(), + counter_(0), + finished_(false) { + assert(options->block_restart_interval >= 1); + restarts_.push_back(0); // First restart point is at offset 0 +} + +void BlockBuilder::Reset() { + buffer_.clear(); + restarts_.clear(); + restarts_.push_back(0); // First restart point is at offset 0 + counter_ = 0; + finished_ = false; + last_key_.clear(); +} + +size_t BlockBuilder::CurrentSizeEstimate() const { + return (buffer_.size() + // Raw data buffer + restarts_.size() * sizeof(uint32_t) + // Restart array + sizeof(uint32_t)); // Restart array length +} + +Slice BlockBuilder::Finish() { + // Append restart array + for (size_t i = 0; i < restarts_.size(); i++) { + PutFixed32(&buffer_, restarts_[i]); + } + PutFixed32(&buffer_, restarts_.size()); + finished_ = true; + return Slice(buffer_); +} + +void BlockBuilder::Add(const Slice& key, const Slice& value) { + Slice last_key_piece(last_key_); + assert(!finished_); + assert(counter_ <= options_->block_restart_interval); + assert(buffer_.empty() // No values yet? + || options_->comparator->Compare(key, last_key_piece) > 0); + size_t shared = 0; + if (counter_ < options_->block_restart_interval) { + // See how much sharing to do with previous string + const size_t min_length = std::min(last_key_piece.size(), key.size()); + while ((shared < min_length) && (last_key_piece[shared] == key[shared])) { + shared++; + } + } else { + // Restart compression + restarts_.push_back(buffer_.size()); + counter_ = 0; + } + const size_t non_shared = key.size() - shared; + + // Add "" to buffer_ + PutVarint32(&buffer_, shared); + PutVarint32(&buffer_, non_shared); + PutVarint32(&buffer_, value.size()); + + // Add string delta to buffer_ followed by value + buffer_.append(key.data() + shared, non_shared); + buffer_.append(value.data(), value.size()); + + // Update state + last_key_.resize(shared); + last_key_.append(key.data() + shared, non_shared); + assert(Slice(last_key_) == key); + counter_++; +} + +} // namespace leveldb diff --git a/src/leveldb/table/block_builder.h b/src/leveldb/table/block_builder.h new file mode 100644 index 0000000..5b545bd --- /dev/null +++ b/src/leveldb/table/block_builder.h @@ -0,0 +1,57 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_ +#define STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_ + +#include + +#include +#include "leveldb/slice.h" + +namespace leveldb { + +struct Options; + +class BlockBuilder { + public: + explicit BlockBuilder(const Options* options); + + // Reset the contents as if the BlockBuilder was just constructed. + void Reset(); + + // REQUIRES: Finish() has not been callled since the last call to Reset(). + // REQUIRES: key is larger than any previously added key + void Add(const Slice& key, const Slice& value); + + // Finish building the block and return a slice that refers to the + // block contents. The returned slice will remain valid for the + // lifetime of this builder or until Reset() is called. + Slice Finish(); + + // Returns an estimate of the current (uncompressed) size of the block + // we are building. + size_t CurrentSizeEstimate() const; + + // Return true iff no entries have been added since the last Reset() + bool empty() const { + return buffer_.empty(); + } + + private: + const Options* options_; + std::string buffer_; // Destination buffer + std::vector restarts_; // Restart points + int counter_; // Number of entries emitted since restart + bool finished_; // Has Finish() been called? + std::string last_key_; + + // No copying allowed + BlockBuilder(const BlockBuilder&); + void operator=(const BlockBuilder&); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_ diff --git a/src/leveldb/table/filter_block.cc b/src/leveldb/table/filter_block.cc new file mode 100644 index 0000000..203e15c --- /dev/null +++ b/src/leveldb/table/filter_block.cc @@ -0,0 +1,111 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/filter_block.h" + +#include "leveldb/filter_policy.h" +#include "util/coding.h" + +namespace leveldb { + +// See doc/table_format.txt for an explanation of the filter block format. + +// Generate new filter every 2KB of data +static const size_t kFilterBaseLg = 11; +static const size_t kFilterBase = 1 << kFilterBaseLg; + +FilterBlockBuilder::FilterBlockBuilder(const FilterPolicy* policy) + : policy_(policy) { +} + +void FilterBlockBuilder::StartBlock(uint64_t block_offset) { + uint64_t filter_index = (block_offset / kFilterBase); + assert(filter_index >= filter_offsets_.size()); + while (filter_index > filter_offsets_.size()) { + GenerateFilter(); + } +} + +void FilterBlockBuilder::AddKey(const Slice& key) { + Slice k = key; + start_.push_back(keys_.size()); + keys_.append(k.data(), k.size()); +} + +Slice FilterBlockBuilder::Finish() { + if (!start_.empty()) { + GenerateFilter(); + } + + // Append array of per-filter offsets + const uint32_t array_offset = result_.size(); + for (size_t i = 0; i < filter_offsets_.size(); i++) { + PutFixed32(&result_, filter_offsets_[i]); + } + + PutFixed32(&result_, array_offset); + result_.push_back(kFilterBaseLg); // Save encoding parameter in result + return Slice(result_); +} + +void FilterBlockBuilder::GenerateFilter() { + const size_t num_keys = start_.size(); + if (num_keys == 0) { + // Fast path if there are no keys for this filter + filter_offsets_.push_back(result_.size()); + return; + } + + // Make list of keys from flattened key structure + start_.push_back(keys_.size()); // Simplify length computation + tmp_keys_.resize(num_keys); + for (size_t i = 0; i < num_keys; i++) { + const char* base = keys_.data() + start_[i]; + size_t length = start_[i+1] - start_[i]; + tmp_keys_[i] = Slice(base, length); + } + + // Generate filter for current set of keys and append to result_. + filter_offsets_.push_back(result_.size()); + policy_->CreateFilter(&tmp_keys_[0], num_keys, &result_); + + tmp_keys_.clear(); + keys_.clear(); + start_.clear(); +} + +FilterBlockReader::FilterBlockReader(const FilterPolicy* policy, + const Slice& contents) + : policy_(policy), + data_(NULL), + offset_(NULL), + num_(0), + base_lg_(0) { + size_t n = contents.size(); + if (n < 5) return; // 1 byte for base_lg_ and 4 for start of offset array + base_lg_ = contents[n-1]; + uint32_t last_word = DecodeFixed32(contents.data() + n - 5); + if (last_word > n - 5) return; + data_ = contents.data(); + offset_ = data_ + last_word; + num_ = (n - 5 - last_word) / 4; +} + +bool FilterBlockReader::KeyMayMatch(uint64_t block_offset, const Slice& key) { + uint64_t index = block_offset >> base_lg_; + if (index < num_) { + uint32_t start = DecodeFixed32(offset_ + index*4); + uint32_t limit = DecodeFixed32(offset_ + index*4 + 4); + if (start <= limit && limit <= (offset_ - data_)) { + Slice filter = Slice(data_ + start, limit - start); + return policy_->KeyMayMatch(key, filter); + } else if (start == limit) { + // Empty filters do not match any keys + return false; + } + } + return true; // Errors are treated as potential matches +} + +} diff --git a/src/leveldb/table/filter_block.h b/src/leveldb/table/filter_block.h new file mode 100644 index 0000000..c67d010 --- /dev/null +++ b/src/leveldb/table/filter_block.h @@ -0,0 +1,68 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A filter block is stored near the end of a Table file. It contains +// filters (e.g., bloom filters) for all data blocks in the table combined +// into a single filter block. + +#ifndef STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_ +#define STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_ + +#include +#include +#include +#include +#include "leveldb/slice.h" +#include "util/hash.h" + +namespace leveldb { + +class FilterPolicy; + +// A FilterBlockBuilder is used to construct all of the filters for a +// particular Table. It generates a single string which is stored as +// a special block in the Table. +// +// The sequence of calls to FilterBlockBuilder must match the regexp: +// (StartBlock AddKey*)* Finish +class FilterBlockBuilder { + public: + explicit FilterBlockBuilder(const FilterPolicy*); + + void StartBlock(uint64_t block_offset); + void AddKey(const Slice& key); + Slice Finish(); + + private: + void GenerateFilter(); + + const FilterPolicy* policy_; + std::string keys_; // Flattened key contents + std::vector start_; // Starting index in keys_ of each key + std::string result_; // Filter data computed so far + std::vector tmp_keys_; // policy_->CreateFilter() argument + std::vector filter_offsets_; + + // No copying allowed + FilterBlockBuilder(const FilterBlockBuilder&); + void operator=(const FilterBlockBuilder&); +}; + +class FilterBlockReader { + public: + // REQUIRES: "contents" and *policy must stay live while *this is live. + FilterBlockReader(const FilterPolicy* policy, const Slice& contents); + bool KeyMayMatch(uint64_t block_offset, const Slice& key); + + private: + const FilterPolicy* policy_; + const char* data_; // Pointer to filter data (at block-start) + const char* offset_; // Pointer to beginning of offset array (at block-end) + size_t num_; // Number of entries in offset array + size_t base_lg_; // Encoding parameter (see kFilterBaseLg in .cc file) +}; + +} + +#endif // STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_ diff --git a/src/leveldb/table/filter_block_test.cc b/src/leveldb/table/filter_block_test.cc new file mode 100644 index 0000000..3a2a07c --- /dev/null +++ b/src/leveldb/table/filter_block_test.cc @@ -0,0 +1,128 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/filter_block.h" + +#include "leveldb/filter_policy.h" +#include "util/coding.h" +#include "util/hash.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +// For testing: emit an array with one hash value per key +class TestHashFilter : public FilterPolicy { + public: + virtual const char* Name() const { + return "TestHashFilter"; + } + + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + for (int i = 0; i < n; i++) { + uint32_t h = Hash(keys[i].data(), keys[i].size(), 1); + PutFixed32(dst, h); + } + } + + virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const { + uint32_t h = Hash(key.data(), key.size(), 1); + for (int i = 0; i + 4 <= filter.size(); i += 4) { + if (h == DecodeFixed32(filter.data() + i)) { + return true; + } + } + return false; + } +}; + +class FilterBlockTest { + public: + TestHashFilter policy_; +}; + +TEST(FilterBlockTest, EmptyBuilder) { + FilterBlockBuilder builder(&policy_); + Slice block = builder.Finish(); + ASSERT_EQ("\\x00\\x00\\x00\\x00\\x0b", EscapeString(block)); + FilterBlockReader reader(&policy_, block); + ASSERT_TRUE(reader.KeyMayMatch(0, "foo")); + ASSERT_TRUE(reader.KeyMayMatch(100000, "foo")); +} + +TEST(FilterBlockTest, SingleChunk) { + FilterBlockBuilder builder(&policy_); + builder.StartBlock(100); + builder.AddKey("foo"); + builder.AddKey("bar"); + builder.AddKey("box"); + builder.StartBlock(200); + builder.AddKey("box"); + builder.StartBlock(300); + builder.AddKey("hello"); + Slice block = builder.Finish(); + FilterBlockReader reader(&policy_, block); + ASSERT_TRUE(reader.KeyMayMatch(100, "foo")); + ASSERT_TRUE(reader.KeyMayMatch(100, "bar")); + ASSERT_TRUE(reader.KeyMayMatch(100, "box")); + ASSERT_TRUE(reader.KeyMayMatch(100, "hello")); + ASSERT_TRUE(reader.KeyMayMatch(100, "foo")); + ASSERT_TRUE(! reader.KeyMayMatch(100, "missing")); + ASSERT_TRUE(! reader.KeyMayMatch(100, "other")); +} + +TEST(FilterBlockTest, MultiChunk) { + FilterBlockBuilder builder(&policy_); + + // First filter + builder.StartBlock(0); + builder.AddKey("foo"); + builder.StartBlock(2000); + builder.AddKey("bar"); + + // Second filter + builder.StartBlock(3100); + builder.AddKey("box"); + + // Third filter is empty + + // Last filter + builder.StartBlock(9000); + builder.AddKey("box"); + builder.AddKey("hello"); + + Slice block = builder.Finish(); + FilterBlockReader reader(&policy_, block); + + // Check first filter + ASSERT_TRUE(reader.KeyMayMatch(0, "foo")); + ASSERT_TRUE(reader.KeyMayMatch(2000, "bar")); + ASSERT_TRUE(! reader.KeyMayMatch(0, "box")); + ASSERT_TRUE(! reader.KeyMayMatch(0, "hello")); + + // Check second filter + ASSERT_TRUE(reader.KeyMayMatch(3100, "box")); + ASSERT_TRUE(! reader.KeyMayMatch(3100, "foo")); + ASSERT_TRUE(! reader.KeyMayMatch(3100, "bar")); + ASSERT_TRUE(! reader.KeyMayMatch(3100, "hello")); + + // Check third filter (empty) + ASSERT_TRUE(! reader.KeyMayMatch(4100, "foo")); + ASSERT_TRUE(! reader.KeyMayMatch(4100, "bar")); + ASSERT_TRUE(! reader.KeyMayMatch(4100, "box")); + ASSERT_TRUE(! reader.KeyMayMatch(4100, "hello")); + + // Check last filter + ASSERT_TRUE(reader.KeyMayMatch(9000, "box")); + ASSERT_TRUE(reader.KeyMayMatch(9000, "hello")); + ASSERT_TRUE(! reader.KeyMayMatch(9000, "foo")); + ASSERT_TRUE(! reader.KeyMayMatch(9000, "bar")); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/table/format.cc b/src/leveldb/table/format.cc new file mode 100644 index 0000000..cda1dec --- /dev/null +++ b/src/leveldb/table/format.cc @@ -0,0 +1,145 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/format.h" + +#include "leveldb/env.h" +#include "port/port.h" +#include "table/block.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { + +void BlockHandle::EncodeTo(std::string* dst) const { + // Sanity check that all fields have been set + assert(offset_ != ~static_cast(0)); + assert(size_ != ~static_cast(0)); + PutVarint64(dst, offset_); + PutVarint64(dst, size_); +} + +Status BlockHandle::DecodeFrom(Slice* input) { + if (GetVarint64(input, &offset_) && + GetVarint64(input, &size_)) { + return Status::OK(); + } else { + return Status::Corruption("bad block handle"); + } +} + +void Footer::EncodeTo(std::string* dst) const { +#ifndef NDEBUG + const size_t original_size = dst->size(); +#endif + metaindex_handle_.EncodeTo(dst); + index_handle_.EncodeTo(dst); + dst->resize(2 * BlockHandle::kMaxEncodedLength); // Padding + PutFixed32(dst, static_cast(kTableMagicNumber & 0xffffffffu)); + PutFixed32(dst, static_cast(kTableMagicNumber >> 32)); + assert(dst->size() == original_size + kEncodedLength); +} + +Status Footer::DecodeFrom(Slice* input) { + const char* magic_ptr = input->data() + kEncodedLength - 8; + const uint32_t magic_lo = DecodeFixed32(magic_ptr); + const uint32_t magic_hi = DecodeFixed32(magic_ptr + 4); + const uint64_t magic = ((static_cast(magic_hi) << 32) | + (static_cast(magic_lo))); + if (magic != kTableMagicNumber) { + return Status::InvalidArgument("not an sstable (bad magic number)"); + } + + Status result = metaindex_handle_.DecodeFrom(input); + if (result.ok()) { + result = index_handle_.DecodeFrom(input); + } + if (result.ok()) { + // We skip over any leftover data (just padding for now) in "input" + const char* end = magic_ptr + 8; + *input = Slice(end, input->data() + input->size() - end); + } + return result; +} + +Status ReadBlock(RandomAccessFile* file, + const ReadOptions& options, + const BlockHandle& handle, + BlockContents* result) { + result->data = Slice(); + result->cachable = false; + result->heap_allocated = false; + + // Read the block contents as well as the type/crc footer. + // See table_builder.cc for the code that built this structure. + size_t n = static_cast(handle.size()); + char* buf = new char[n + kBlockTrailerSize]; + Slice contents; + Status s = file->Read(handle.offset(), n + kBlockTrailerSize, &contents, buf); + if (!s.ok()) { + delete[] buf; + return s; + } + if (contents.size() != n + kBlockTrailerSize) { + delete[] buf; + return Status::Corruption("truncated block read"); + } + + // Check the crc of the type and the block contents + const char* data = contents.data(); // Pointer to where Read put the data + if (options.verify_checksums) { + const uint32_t crc = crc32c::Unmask(DecodeFixed32(data + n + 1)); + const uint32_t actual = crc32c::Value(data, n + 1); + if (actual != crc) { + delete[] buf; + s = Status::Corruption("block checksum mismatch"); + return s; + } + } + + switch (data[n]) { + case kNoCompression: + if (data != buf) { + // File implementation gave us pointer to some other data. + // Use it directly under the assumption that it will be live + // while the file is open. + delete[] buf; + result->data = Slice(data, n); + result->heap_allocated = false; + result->cachable = false; // Do not double-cache + } else { + result->data = Slice(buf, n); + result->heap_allocated = true; + result->cachable = true; + } + + // Ok + break; + case kSnappyCompression: { + size_t ulength = 0; + if (!port::Snappy_GetUncompressedLength(data, n, &ulength)) { + delete[] buf; + return Status::Corruption("corrupted compressed block contents"); + } + char* ubuf = new char[ulength]; + if (!port::Snappy_Uncompress(data, n, ubuf)) { + delete[] buf; + delete[] ubuf; + return Status::Corruption("corrupted compressed block contents"); + } + delete[] buf; + result->data = Slice(ubuf, ulength); + result->heap_allocated = true; + result->cachable = true; + break; + } + default: + delete[] buf; + return Status::Corruption("bad block type"); + } + + return Status::OK(); +} + +} // namespace leveldb diff --git a/src/leveldb/table/format.h b/src/leveldb/table/format.h new file mode 100644 index 0000000..6c0b80c --- /dev/null +++ b/src/leveldb/table/format.h @@ -0,0 +1,108 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_FORMAT_H_ +#define STORAGE_LEVELDB_TABLE_FORMAT_H_ + +#include +#include +#include "leveldb/slice.h" +#include "leveldb/status.h" +#include "leveldb/table_builder.h" + +namespace leveldb { + +class Block; +class RandomAccessFile; +struct ReadOptions; + +// BlockHandle is a pointer to the extent of a file that stores a data +// block or a meta block. +class BlockHandle { + public: + BlockHandle(); + + // The offset of the block in the file. + uint64_t offset() const { return offset_; } + void set_offset(uint64_t offset) { offset_ = offset; } + + // The size of the stored block + uint64_t size() const { return size_; } + void set_size(uint64_t size) { size_ = size; } + + void EncodeTo(std::string* dst) const; + Status DecodeFrom(Slice* input); + + // Maximum encoding length of a BlockHandle + enum { kMaxEncodedLength = 10 + 10 }; + + private: + uint64_t offset_; + uint64_t size_; +}; + +// Footer encapsulates the fixed information stored at the tail +// end of every table file. +class Footer { + public: + Footer() { } + + // The block handle for the metaindex block of the table + const BlockHandle& metaindex_handle() const { return metaindex_handle_; } + void set_metaindex_handle(const BlockHandle& h) { metaindex_handle_ = h; } + + // The block handle for the index block of the table + const BlockHandle& index_handle() const { + return index_handle_; + } + void set_index_handle(const BlockHandle& h) { + index_handle_ = h; + } + + void EncodeTo(std::string* dst) const; + Status DecodeFrom(Slice* input); + + // Encoded length of a Footer. Note that the serialization of a + // Footer will always occupy exactly this many bytes. It consists + // of two block handles and a magic number. + enum { + kEncodedLength = 2*BlockHandle::kMaxEncodedLength + 8 + }; + + private: + BlockHandle metaindex_handle_; + BlockHandle index_handle_; +}; + +// kTableMagicNumber was picked by running +// echo http://code.google.com/p/leveldb/ | sha1sum +// and taking the leading 64 bits. +static const uint64_t kTableMagicNumber = 0xdb4775248b80fb57ull; + +// 1-byte type + 32-bit crc +static const size_t kBlockTrailerSize = 5; + +struct BlockContents { + Slice data; // Actual contents of data + bool cachable; // True iff data can be cached + bool heap_allocated; // True iff caller should delete[] data.data() +}; + +// Read the block identified by "handle" from "file". On failure +// return non-OK. On success fill *result and return OK. +extern Status ReadBlock(RandomAccessFile* file, + const ReadOptions& options, + const BlockHandle& handle, + BlockContents* result); + +// Implementation details follow. Clients should ignore, + +inline BlockHandle::BlockHandle() + : offset_(~static_cast(0)), + size_(~static_cast(0)) { +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_FORMAT_H_ diff --git a/src/leveldb/table/iterator.cc b/src/leveldb/table/iterator.cc new file mode 100644 index 0000000..3d1c87f --- /dev/null +++ b/src/leveldb/table/iterator.cc @@ -0,0 +1,67 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/iterator.h" + +namespace leveldb { + +Iterator::Iterator() { + cleanup_.function = NULL; + cleanup_.next = NULL; +} + +Iterator::~Iterator() { + if (cleanup_.function != NULL) { + (*cleanup_.function)(cleanup_.arg1, cleanup_.arg2); + for (Cleanup* c = cleanup_.next; c != NULL; ) { + (*c->function)(c->arg1, c->arg2); + Cleanup* next = c->next; + delete c; + c = next; + } + } +} + +void Iterator::RegisterCleanup(CleanupFunction func, void* arg1, void* arg2) { + assert(func != NULL); + Cleanup* c; + if (cleanup_.function == NULL) { + c = &cleanup_; + } else { + c = new Cleanup; + c->next = cleanup_.next; + cleanup_.next = c; + } + c->function = func; + c->arg1 = arg1; + c->arg2 = arg2; +} + +namespace { +class EmptyIterator : public Iterator { + public: + EmptyIterator(const Status& s) : status_(s) { } + virtual bool Valid() const { return false; } + virtual void Seek(const Slice& target) { } + virtual void SeekToFirst() { } + virtual void SeekToLast() { } + virtual void Next() { assert(false); } + virtual void Prev() { assert(false); } + Slice key() const { assert(false); return Slice(); } + Slice value() const { assert(false); return Slice(); } + virtual Status status() const { return status_; } + private: + Status status_; +}; +} // namespace + +Iterator* NewEmptyIterator() { + return new EmptyIterator(Status::OK()); +} + +Iterator* NewErrorIterator(const Status& status) { + return new EmptyIterator(status); +} + +} // namespace leveldb diff --git a/src/leveldb/table/iterator_wrapper.h b/src/leveldb/table/iterator_wrapper.h new file mode 100644 index 0000000..9e16b3d --- /dev/null +++ b/src/leveldb/table/iterator_wrapper.h @@ -0,0 +1,63 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_ +#define STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_ + +namespace leveldb { + +// A internal wrapper class with an interface similar to Iterator that +// caches the valid() and key() results for an underlying iterator. +// This can help avoid virtual function calls and also gives better +// cache locality. +class IteratorWrapper { + public: + IteratorWrapper(): iter_(NULL), valid_(false) { } + explicit IteratorWrapper(Iterator* iter): iter_(NULL) { + Set(iter); + } + ~IteratorWrapper() { delete iter_; } + Iterator* iter() const { return iter_; } + + // Takes ownership of "iter" and will delete it when destroyed, or + // when Set() is invoked again. + void Set(Iterator* iter) { + delete iter_; + iter_ = iter; + if (iter_ == NULL) { + valid_ = false; + } else { + Update(); + } + } + + + // Iterator interface methods + bool Valid() const { return valid_; } + Slice key() const { assert(Valid()); return key_; } + Slice value() const { assert(Valid()); return iter_->value(); } + // Methods below require iter() != NULL + Status status() const { assert(iter_); return iter_->status(); } + void Next() { assert(iter_); iter_->Next(); Update(); } + void Prev() { assert(iter_); iter_->Prev(); Update(); } + void Seek(const Slice& k) { assert(iter_); iter_->Seek(k); Update(); } + void SeekToFirst() { assert(iter_); iter_->SeekToFirst(); Update(); } + void SeekToLast() { assert(iter_); iter_->SeekToLast(); Update(); } + + private: + void Update() { + valid_ = iter_->Valid(); + if (valid_) { + key_ = iter_->key(); + } + } + + Iterator* iter_; + bool valid_; + Slice key_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_ diff --git a/src/leveldb/table/merger.cc b/src/leveldb/table/merger.cc new file mode 100644 index 0000000..2dde4dc --- /dev/null +++ b/src/leveldb/table/merger.cc @@ -0,0 +1,197 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/merger.h" + +#include "leveldb/comparator.h" +#include "leveldb/iterator.h" +#include "table/iterator_wrapper.h" + +namespace leveldb { + +namespace { +class MergingIterator : public Iterator { + public: + MergingIterator(const Comparator* comparator, Iterator** children, int n) + : comparator_(comparator), + children_(new IteratorWrapper[n]), + n_(n), + current_(NULL), + direction_(kForward) { + for (int i = 0; i < n; i++) { + children_[i].Set(children[i]); + } + } + + virtual ~MergingIterator() { + delete[] children_; + } + + virtual bool Valid() const { + return (current_ != NULL); + } + + virtual void SeekToFirst() { + for (int i = 0; i < n_; i++) { + children_[i].SeekToFirst(); + } + FindSmallest(); + direction_ = kForward; + } + + virtual void SeekToLast() { + for (int i = 0; i < n_; i++) { + children_[i].SeekToLast(); + } + FindLargest(); + direction_ = kReverse; + } + + virtual void Seek(const Slice& target) { + for (int i = 0; i < n_; i++) { + children_[i].Seek(target); + } + FindSmallest(); + direction_ = kForward; + } + + virtual void Next() { + assert(Valid()); + + // Ensure that all children are positioned after key(). + // If we are moving in the forward direction, it is already + // true for all of the non-current_ children since current_ is + // the smallest child and key() == current_->key(). Otherwise, + // we explicitly position the non-current_ children. + if (direction_ != kForward) { + for (int i = 0; i < n_; i++) { + IteratorWrapper* child = &children_[i]; + if (child != current_) { + child->Seek(key()); + if (child->Valid() && + comparator_->Compare(key(), child->key()) == 0) { + child->Next(); + } + } + } + direction_ = kForward; + } + + current_->Next(); + FindSmallest(); + } + + virtual void Prev() { + assert(Valid()); + + // Ensure that all children are positioned before key(). + // If we are moving in the reverse direction, it is already + // true for all of the non-current_ children since current_ is + // the largest child and key() == current_->key(). Otherwise, + // we explicitly position the non-current_ children. + if (direction_ != kReverse) { + for (int i = 0; i < n_; i++) { + IteratorWrapper* child = &children_[i]; + if (child != current_) { + child->Seek(key()); + if (child->Valid()) { + // Child is at first entry >= key(). Step back one to be < key() + child->Prev(); + } else { + // Child has no entries >= key(). Position at last entry. + child->SeekToLast(); + } + } + } + direction_ = kReverse; + } + + current_->Prev(); + FindLargest(); + } + + virtual Slice key() const { + assert(Valid()); + return current_->key(); + } + + virtual Slice value() const { + assert(Valid()); + return current_->value(); + } + + virtual Status status() const { + Status status; + for (int i = 0; i < n_; i++) { + status = children_[i].status(); + if (!status.ok()) { + break; + } + } + return status; + } + + private: + void FindSmallest(); + void FindLargest(); + + // We might want to use a heap in case there are lots of children. + // For now we use a simple array since we expect a very small number + // of children in leveldb. + const Comparator* comparator_; + IteratorWrapper* children_; + int n_; + IteratorWrapper* current_; + + // Which direction is the iterator moving? + enum Direction { + kForward, + kReverse + }; + Direction direction_; +}; + +void MergingIterator::FindSmallest() { + IteratorWrapper* smallest = NULL; + for (int i = 0; i < n_; i++) { + IteratorWrapper* child = &children_[i]; + if (child->Valid()) { + if (smallest == NULL) { + smallest = child; + } else if (comparator_->Compare(child->key(), smallest->key()) < 0) { + smallest = child; + } + } + } + current_ = smallest; +} + +void MergingIterator::FindLargest() { + IteratorWrapper* largest = NULL; + for (int i = n_-1; i >= 0; i--) { + IteratorWrapper* child = &children_[i]; + if (child->Valid()) { + if (largest == NULL) { + largest = child; + } else if (comparator_->Compare(child->key(), largest->key()) > 0) { + largest = child; + } + } + } + current_ = largest; +} +} // namespace + +Iterator* NewMergingIterator(const Comparator* cmp, Iterator** list, int n) { + assert(n >= 0); + if (n == 0) { + return NewEmptyIterator(); + } else if (n == 1) { + return list[0]; + } else { + return new MergingIterator(cmp, list, n); + } +} + +} // namespace leveldb diff --git a/src/leveldb/table/merger.h b/src/leveldb/table/merger.h new file mode 100644 index 0000000..91ddd80 --- /dev/null +++ b/src/leveldb/table/merger.h @@ -0,0 +1,26 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_MERGER_H_ +#define STORAGE_LEVELDB_TABLE_MERGER_H_ + +namespace leveldb { + +class Comparator; +class Iterator; + +// Return an iterator that provided the union of the data in +// children[0,n-1]. Takes ownership of the child iterators and +// will delete them when the result iterator is deleted. +// +// The result does no duplicate suppression. I.e., if a particular +// key is present in K child iterators, it will be yielded K times. +// +// REQUIRES: n >= 0 +extern Iterator* NewMergingIterator( + const Comparator* comparator, Iterator** children, int n); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_MERGER_H_ diff --git a/src/leveldb/table/table.cc b/src/leveldb/table/table.cc new file mode 100644 index 0000000..dbd6d3a --- /dev/null +++ b/src/leveldb/table/table.cc @@ -0,0 +1,276 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/table.h" + +#include "leveldb/cache.h" +#include "leveldb/comparator.h" +#include "leveldb/env.h" +#include "leveldb/filter_policy.h" +#include "leveldb/options.h" +#include "table/block.h" +#include "table/filter_block.h" +#include "table/format.h" +#include "table/two_level_iterator.h" +#include "util/coding.h" + +namespace leveldb { + +struct Table::Rep { + ~Rep() { + delete filter; + delete [] filter_data; + delete index_block; + } + + Options options; + Status status; + RandomAccessFile* file; + uint64_t cache_id; + FilterBlockReader* filter; + const char* filter_data; + + BlockHandle metaindex_handle; // Handle to metaindex_block: saved from footer + Block* index_block; +}; + +Status Table::Open(const Options& options, + RandomAccessFile* file, + uint64_t size, + Table** table) { + *table = NULL; + if (size < Footer::kEncodedLength) { + return Status::InvalidArgument("file is too short to be an sstable"); + } + + char footer_space[Footer::kEncodedLength]; + Slice footer_input; + Status s = file->Read(size - Footer::kEncodedLength, Footer::kEncodedLength, + &footer_input, footer_space); + if (!s.ok()) return s; + + Footer footer; + s = footer.DecodeFrom(&footer_input); + if (!s.ok()) return s; + + // Read the index block + BlockContents contents; + Block* index_block = NULL; + if (s.ok()) { + s = ReadBlock(file, ReadOptions(), footer.index_handle(), &contents); + if (s.ok()) { + index_block = new Block(contents); + } + } + + if (s.ok()) { + // We've successfully read the footer and the index block: we're + // ready to serve requests. + Rep* rep = new Table::Rep; + rep->options = options; + rep->file = file; + rep->metaindex_handle = footer.metaindex_handle(); + rep->index_block = index_block; + rep->cache_id = (options.block_cache ? options.block_cache->NewId() : 0); + rep->filter_data = NULL; + rep->filter = NULL; + *table = new Table(rep); + (*table)->ReadMeta(footer); + } else { + if (index_block) delete index_block; + } + + return s; +} + +void Table::ReadMeta(const Footer& footer) { + if (rep_->options.filter_policy == NULL) { + return; // Do not need any metadata + } + + // TODO(sanjay): Skip this if footer.metaindex_handle() size indicates + // it is an empty block. + ReadOptions opt; + BlockContents contents; + if (!ReadBlock(rep_->file, opt, footer.metaindex_handle(), &contents).ok()) { + // Do not propagate errors since meta info is not needed for operation + return; + } + Block* meta = new Block(contents); + + Iterator* iter = meta->NewIterator(BytewiseComparator()); + std::string key = "filter."; + key.append(rep_->options.filter_policy->Name()); + iter->Seek(key); + if (iter->Valid() && iter->key() == Slice(key)) { + ReadFilter(iter->value()); + } + delete iter; + delete meta; +} + +void Table::ReadFilter(const Slice& filter_handle_value) { + Slice v = filter_handle_value; + BlockHandle filter_handle; + if (!filter_handle.DecodeFrom(&v).ok()) { + return; + } + + // We might want to unify with ReadBlock() if we start + // requiring checksum verification in Table::Open. + ReadOptions opt; + BlockContents block; + if (!ReadBlock(rep_->file, opt, filter_handle, &block).ok()) { + return; + } + if (block.heap_allocated) { + rep_->filter_data = block.data.data(); // Will need to delete later + } + rep_->filter = new FilterBlockReader(rep_->options.filter_policy, block.data); +} + +Table::~Table() { + delete rep_; +} + +static void DeleteBlock(void* arg, void* ignored) { + delete reinterpret_cast(arg); +} + +static void DeleteCachedBlock(const Slice& key, void* value) { + Block* block = reinterpret_cast(value); + delete block; +} + +static void ReleaseBlock(void* arg, void* h) { + Cache* cache = reinterpret_cast(arg); + Cache::Handle* handle = reinterpret_cast(h); + cache->Release(handle); +} + +// Convert an index iterator value (i.e., an encoded BlockHandle) +// into an iterator over the contents of the corresponding block. +Iterator* Table::BlockReader(void* arg, + const ReadOptions& options, + const Slice& index_value) { + Table* table = reinterpret_cast(arg); + Cache* block_cache = table->rep_->options.block_cache; + Block* block = NULL; + Cache::Handle* cache_handle = NULL; + + BlockHandle handle; + Slice input = index_value; + Status s = handle.DecodeFrom(&input); + // We intentionally allow extra stuff in index_value so that we + // can add more features in the future. + + if (s.ok()) { + BlockContents contents; + if (block_cache != NULL) { + char cache_key_buffer[16]; + EncodeFixed64(cache_key_buffer, table->rep_->cache_id); + EncodeFixed64(cache_key_buffer+8, handle.offset()); + Slice key(cache_key_buffer, sizeof(cache_key_buffer)); + cache_handle = block_cache->Lookup(key); + if (cache_handle != NULL) { + block = reinterpret_cast(block_cache->Value(cache_handle)); + } else { + s = ReadBlock(table->rep_->file, options, handle, &contents); + if (s.ok()) { + block = new Block(contents); + if (contents.cachable && options.fill_cache) { + cache_handle = block_cache->Insert( + key, block, block->size(), &DeleteCachedBlock); + } + } + } + } else { + s = ReadBlock(table->rep_->file, options, handle, &contents); + if (s.ok()) { + block = new Block(contents); + } + } + } + + Iterator* iter; + if (block != NULL) { + iter = block->NewIterator(table->rep_->options.comparator); + if (cache_handle == NULL) { + iter->RegisterCleanup(&DeleteBlock, block, NULL); + } else { + iter->RegisterCleanup(&ReleaseBlock, block_cache, cache_handle); + } + } else { + iter = NewErrorIterator(s); + } + return iter; +} + +Iterator* Table::NewIterator(const ReadOptions& options) const { + return NewTwoLevelIterator( + rep_->index_block->NewIterator(rep_->options.comparator), + &Table::BlockReader, const_cast(this), options); +} + +Status Table::InternalGet(const ReadOptions& options, const Slice& k, + void* arg, + void (*saver)(void*, const Slice&, const Slice&)) { + Status s; + Iterator* iiter = rep_->index_block->NewIterator(rep_->options.comparator); + iiter->Seek(k); + if (iiter->Valid()) { + Slice handle_value = iiter->value(); + FilterBlockReader* filter = rep_->filter; + BlockHandle handle; + if (filter != NULL && + handle.DecodeFrom(&handle_value).ok() && + !filter->KeyMayMatch(handle.offset(), k)) { + // Not found + } else { + Slice handle = iiter->value(); + Iterator* block_iter = BlockReader(this, options, iiter->value()); + block_iter->Seek(k); + if (block_iter->Valid()) { + (*saver)(arg, block_iter->key(), block_iter->value()); + } + s = block_iter->status(); + delete block_iter; + } + } + if (s.ok()) { + s = iiter->status(); + } + delete iiter; + return s; +} + + +uint64_t Table::ApproximateOffsetOf(const Slice& key) const { + Iterator* index_iter = + rep_->index_block->NewIterator(rep_->options.comparator); + index_iter->Seek(key); + uint64_t result; + if (index_iter->Valid()) { + BlockHandle handle; + Slice input = index_iter->value(); + Status s = handle.DecodeFrom(&input); + if (s.ok()) { + result = handle.offset(); + } else { + // Strange: we can't decode the block handle in the index block. + // We'll just return the offset of the metaindex block, which is + // close to the whole file size for this case. + result = rep_->metaindex_handle.offset(); + } + } else { + // key is past the last key in the file. Approximate the offset + // by returning the offset of the metaindex block (which is + // right near the end of the file). + result = rep_->metaindex_handle.offset(); + } + delete index_iter; + return result; +} + +} // namespace leveldb diff --git a/src/leveldb/table/table_builder.cc b/src/leveldb/table/table_builder.cc new file mode 100644 index 0000000..62002c8 --- /dev/null +++ b/src/leveldb/table/table_builder.cc @@ -0,0 +1,270 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/table_builder.h" + +#include +#include "leveldb/comparator.h" +#include "leveldb/env.h" +#include "leveldb/filter_policy.h" +#include "leveldb/options.h" +#include "table/block_builder.h" +#include "table/filter_block.h" +#include "table/format.h" +#include "util/coding.h" +#include "util/crc32c.h" + +namespace leveldb { + +struct TableBuilder::Rep { + Options options; + Options index_block_options; + WritableFile* file; + uint64_t offset; + Status status; + BlockBuilder data_block; + BlockBuilder index_block; + std::string last_key; + int64_t num_entries; + bool closed; // Either Finish() or Abandon() has been called. + FilterBlockBuilder* filter_block; + + // We do not emit the index entry for a block until we have seen the + // first key for the next data block. This allows us to use shorter + // keys in the index block. For example, consider a block boundary + // between the keys "the quick brown fox" and "the who". We can use + // "the r" as the key for the index block entry since it is >= all + // entries in the first block and < all entries in subsequent + // blocks. + // + // Invariant: r->pending_index_entry is true only if data_block is empty. + bool pending_index_entry; + BlockHandle pending_handle; // Handle to add to index block + + std::string compressed_output; + + Rep(const Options& opt, WritableFile* f) + : options(opt), + index_block_options(opt), + file(f), + offset(0), + data_block(&options), + index_block(&index_block_options), + num_entries(0), + closed(false), + filter_block(opt.filter_policy == NULL ? NULL + : new FilterBlockBuilder(opt.filter_policy)), + pending_index_entry(false) { + index_block_options.block_restart_interval = 1; + } +}; + +TableBuilder::TableBuilder(const Options& options, WritableFile* file) + : rep_(new Rep(options, file)) { + if (rep_->filter_block != NULL) { + rep_->filter_block->StartBlock(0); + } +} + +TableBuilder::~TableBuilder() { + assert(rep_->closed); // Catch errors where caller forgot to call Finish() + delete rep_->filter_block; + delete rep_; +} + +Status TableBuilder::ChangeOptions(const Options& options) { + // Note: if more fields are added to Options, update + // this function to catch changes that should not be allowed to + // change in the middle of building a Table. + if (options.comparator != rep_->options.comparator) { + return Status::InvalidArgument("changing comparator while building table"); + } + + // Note that any live BlockBuilders point to rep_->options and therefore + // will automatically pick up the updated options. + rep_->options = options; + rep_->index_block_options = options; + rep_->index_block_options.block_restart_interval = 1; + return Status::OK(); +} + +void TableBuilder::Add(const Slice& key, const Slice& value) { + Rep* r = rep_; + assert(!r->closed); + if (!ok()) return; + if (r->num_entries > 0) { + assert(r->options.comparator->Compare(key, Slice(r->last_key)) > 0); + } + + if (r->pending_index_entry) { + assert(r->data_block.empty()); + r->options.comparator->FindShortestSeparator(&r->last_key, key); + std::string handle_encoding; + r->pending_handle.EncodeTo(&handle_encoding); + r->index_block.Add(r->last_key, Slice(handle_encoding)); + r->pending_index_entry = false; + } + + if (r->filter_block != NULL) { + r->filter_block->AddKey(key); + } + + r->last_key.assign(key.data(), key.size()); + r->num_entries++; + r->data_block.Add(key, value); + + const size_t estimated_block_size = r->data_block.CurrentSizeEstimate(); + if (estimated_block_size >= r->options.block_size) { + Flush(); + } +} + +void TableBuilder::Flush() { + Rep* r = rep_; + assert(!r->closed); + if (!ok()) return; + if (r->data_block.empty()) return; + assert(!r->pending_index_entry); + WriteBlock(&r->data_block, &r->pending_handle); + if (ok()) { + r->pending_index_entry = true; + r->status = r->file->Flush(); + } + if (r->filter_block != NULL) { + r->filter_block->StartBlock(r->offset); + } +} + +void TableBuilder::WriteBlock(BlockBuilder* block, BlockHandle* handle) { + // File format contains a sequence of blocks where each block has: + // block_data: uint8[n] + // type: uint8 + // crc: uint32 + assert(ok()); + Rep* r = rep_; + Slice raw = block->Finish(); + + Slice block_contents; + CompressionType type = r->options.compression; + // TODO(postrelease): Support more compression options: zlib? + switch (type) { + case kNoCompression: + block_contents = raw; + break; + + case kSnappyCompression: { + std::string* compressed = &r->compressed_output; + if (port::Snappy_Compress(raw.data(), raw.size(), compressed) && + compressed->size() < raw.size() - (raw.size() / 8u)) { + block_contents = *compressed; + } else { + // Snappy not supported, or compressed less than 12.5%, so just + // store uncompressed form + block_contents = raw; + type = kNoCompression; + } + break; + } + } + WriteRawBlock(block_contents, type, handle); + r->compressed_output.clear(); + block->Reset(); +} + +void TableBuilder::WriteRawBlock(const Slice& block_contents, + CompressionType type, + BlockHandle* handle) { + Rep* r = rep_; + handle->set_offset(r->offset); + handle->set_size(block_contents.size()); + r->status = r->file->Append(block_contents); + if (r->status.ok()) { + char trailer[kBlockTrailerSize]; + trailer[0] = type; + uint32_t crc = crc32c::Value(block_contents.data(), block_contents.size()); + crc = crc32c::Extend(crc, trailer, 1); // Extend crc to cover block type + EncodeFixed32(trailer+1, crc32c::Mask(crc)); + r->status = r->file->Append(Slice(trailer, kBlockTrailerSize)); + if (r->status.ok()) { + r->offset += block_contents.size() + kBlockTrailerSize; + } + } +} + +Status TableBuilder::status() const { + return rep_->status; +} + +Status TableBuilder::Finish() { + Rep* r = rep_; + Flush(); + assert(!r->closed); + r->closed = true; + + BlockHandle filter_block_handle, metaindex_block_handle, index_block_handle; + + // Write filter block + if (ok() && r->filter_block != NULL) { + WriteRawBlock(r->filter_block->Finish(), kNoCompression, + &filter_block_handle); + } + + // Write metaindex block + if (ok()) { + BlockBuilder meta_index_block(&r->options); + if (r->filter_block != NULL) { + // Add mapping from "filter.Name" to location of filter data + std::string key = "filter."; + key.append(r->options.filter_policy->Name()); + std::string handle_encoding; + filter_block_handle.EncodeTo(&handle_encoding); + meta_index_block.Add(key, handle_encoding); + } + + // TODO(postrelease): Add stats and other meta blocks + WriteBlock(&meta_index_block, &metaindex_block_handle); + } + + // Write index block + if (ok()) { + if (r->pending_index_entry) { + r->options.comparator->FindShortSuccessor(&r->last_key); + std::string handle_encoding; + r->pending_handle.EncodeTo(&handle_encoding); + r->index_block.Add(r->last_key, Slice(handle_encoding)); + r->pending_index_entry = false; + } + WriteBlock(&r->index_block, &index_block_handle); + } + + // Write footer + if (ok()) { + Footer footer; + footer.set_metaindex_handle(metaindex_block_handle); + footer.set_index_handle(index_block_handle); + std::string footer_encoding; + footer.EncodeTo(&footer_encoding); + r->status = r->file->Append(footer_encoding); + if (r->status.ok()) { + r->offset += footer_encoding.size(); + } + } + return r->status; +} + +void TableBuilder::Abandon() { + Rep* r = rep_; + assert(!r->closed); + r->closed = true; +} + +uint64_t TableBuilder::NumEntries() const { + return rep_->num_entries; +} + +uint64_t TableBuilder::FileSize() const { + return rep_->offset; +} + +} // namespace leveldb diff --git a/src/leveldb/table/table_test.cc b/src/leveldb/table/table_test.cc new file mode 100644 index 0000000..57cea25 --- /dev/null +++ b/src/leveldb/table/table_test.cc @@ -0,0 +1,838 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/table.h" + +#include +#include +#include "db/dbformat.h" +#include "db/memtable.h" +#include "db/write_batch_internal.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/iterator.h" +#include "leveldb/table_builder.h" +#include "table/block.h" +#include "table/block_builder.h" +#include "table/format.h" +#include "util/random.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +// Return reverse of "key". +// Used to test non-lexicographic comparators. +static std::string Reverse(const Slice& key) { + std::string str(key.ToString()); + std::string rev(""); + for (std::string::reverse_iterator rit = str.rbegin(); + rit != str.rend(); ++rit) { + rev.push_back(*rit); + } + return rev; +} + +namespace { +class ReverseKeyComparator : public Comparator { + public: + virtual const char* Name() const { + return "leveldb.ReverseBytewiseComparator"; + } + + virtual int Compare(const Slice& a, const Slice& b) const { + return BytewiseComparator()->Compare(Reverse(a), Reverse(b)); + } + + virtual void FindShortestSeparator( + std::string* start, + const Slice& limit) const { + std::string s = Reverse(*start); + std::string l = Reverse(limit); + BytewiseComparator()->FindShortestSeparator(&s, l); + *start = Reverse(s); + } + + virtual void FindShortSuccessor(std::string* key) const { + std::string s = Reverse(*key); + BytewiseComparator()->FindShortSuccessor(&s); + *key = Reverse(s); + } +}; +} // namespace +static ReverseKeyComparator reverse_key_comparator; + +static void Increment(const Comparator* cmp, std::string* key) { + if (cmp == BytewiseComparator()) { + key->push_back('\0'); + } else { + assert(cmp == &reverse_key_comparator); + std::string rev = Reverse(*key); + rev.push_back('\0'); + *key = Reverse(rev); + } +} + +// An STL comparator that uses a Comparator +namespace { +struct STLLessThan { + const Comparator* cmp; + + STLLessThan() : cmp(BytewiseComparator()) { } + STLLessThan(const Comparator* c) : cmp(c) { } + bool operator()(const std::string& a, const std::string& b) const { + return cmp->Compare(Slice(a), Slice(b)) < 0; + } +}; +} // namespace + +class StringSink: public WritableFile { + public: + ~StringSink() { } + + const std::string& contents() const { return contents_; } + + virtual Status Close() { return Status::OK(); } + virtual Status Flush() { return Status::OK(); } + virtual Status Sync() { return Status::OK(); } + + virtual Status Append(const Slice& data) { + contents_.append(data.data(), data.size()); + return Status::OK(); + } + + private: + std::string contents_; +}; + + +class StringSource: public RandomAccessFile { + public: + StringSource(const Slice& contents) + : contents_(contents.data(), contents.size()) { + } + + virtual ~StringSource() { } + + uint64_t Size() const { return contents_.size(); } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + if (offset > contents_.size()) { + return Status::InvalidArgument("invalid Read offset"); + } + if (offset + n > contents_.size()) { + n = contents_.size() - offset; + } + memcpy(scratch, &contents_[offset], n); + *result = Slice(scratch, n); + return Status::OK(); + } + + private: + std::string contents_; +}; + +typedef std::map KVMap; + +// Helper class for tests to unify the interface between +// BlockBuilder/TableBuilder and Block/Table. +class Constructor { + public: + explicit Constructor(const Comparator* cmp) : data_(STLLessThan(cmp)) { } + virtual ~Constructor() { } + + void Add(const std::string& key, const Slice& value) { + data_[key] = value.ToString(); + } + + // Finish constructing the data structure with all the keys that have + // been added so far. Returns the keys in sorted order in "*keys" + // and stores the key/value pairs in "*kvmap" + void Finish(const Options& options, + std::vector* keys, + KVMap* kvmap) { + *kvmap = data_; + keys->clear(); + for (KVMap::const_iterator it = data_.begin(); + it != data_.end(); + ++it) { + keys->push_back(it->first); + } + data_.clear(); + Status s = FinishImpl(options, *kvmap); + ASSERT_TRUE(s.ok()) << s.ToString(); + } + + // Construct the data structure from the data in "data" + virtual Status FinishImpl(const Options& options, const KVMap& data) = 0; + + virtual Iterator* NewIterator() const = 0; + + virtual const KVMap& data() { return data_; } + + virtual DB* db() const { return NULL; } // Overridden in DBConstructor + + private: + KVMap data_; +}; + +class BlockConstructor: public Constructor { + public: + explicit BlockConstructor(const Comparator* cmp) + : Constructor(cmp), + comparator_(cmp), + block_(NULL) { } + ~BlockConstructor() { + delete block_; + } + virtual Status FinishImpl(const Options& options, const KVMap& data) { + delete block_; + block_ = NULL; + BlockBuilder builder(&options); + + for (KVMap::const_iterator it = data.begin(); + it != data.end(); + ++it) { + builder.Add(it->first, it->second); + } + // Open the block + data_ = builder.Finish().ToString(); + BlockContents contents; + contents.data = data_; + contents.cachable = false; + contents.heap_allocated = false; + block_ = new Block(contents); + return Status::OK(); + } + virtual Iterator* NewIterator() const { + return block_->NewIterator(comparator_); + } + + private: + const Comparator* comparator_; + std::string data_; + Block* block_; + + BlockConstructor(); +}; + +class TableConstructor: public Constructor { + public: + TableConstructor(const Comparator* cmp) + : Constructor(cmp), + source_(NULL), table_(NULL) { + } + ~TableConstructor() { + Reset(); + } + virtual Status FinishImpl(const Options& options, const KVMap& data) { + Reset(); + StringSink sink; + TableBuilder builder(options, &sink); + + for (KVMap::const_iterator it = data.begin(); + it != data.end(); + ++it) { + builder.Add(it->first, it->second); + ASSERT_TRUE(builder.status().ok()); + } + Status s = builder.Finish(); + ASSERT_TRUE(s.ok()) << s.ToString(); + + ASSERT_EQ(sink.contents().size(), builder.FileSize()); + + // Open the table + source_ = new StringSource(sink.contents()); + Options table_options; + table_options.comparator = options.comparator; + return Table::Open(table_options, source_, sink.contents().size(), &table_); + } + + virtual Iterator* NewIterator() const { + return table_->NewIterator(ReadOptions()); + } + + uint64_t ApproximateOffsetOf(const Slice& key) const { + return table_->ApproximateOffsetOf(key); + } + + private: + void Reset() { + delete table_; + delete source_; + table_ = NULL; + source_ = NULL; + } + + StringSource* source_; + Table* table_; + + TableConstructor(); +}; + +// A helper class that converts internal format keys into user keys +class KeyConvertingIterator: public Iterator { + public: + explicit KeyConvertingIterator(Iterator* iter) : iter_(iter) { } + virtual ~KeyConvertingIterator() { delete iter_; } + virtual bool Valid() const { return iter_->Valid(); } + virtual void Seek(const Slice& target) { + ParsedInternalKey ikey(target, kMaxSequenceNumber, kTypeValue); + std::string encoded; + AppendInternalKey(&encoded, ikey); + iter_->Seek(encoded); + } + virtual void SeekToFirst() { iter_->SeekToFirst(); } + virtual void SeekToLast() { iter_->SeekToLast(); } + virtual void Next() { iter_->Next(); } + virtual void Prev() { iter_->Prev(); } + + virtual Slice key() const { + assert(Valid()); + ParsedInternalKey key; + if (!ParseInternalKey(iter_->key(), &key)) { + status_ = Status::Corruption("malformed internal key"); + return Slice("corrupted key"); + } + return key.user_key; + } + + virtual Slice value() const { return iter_->value(); } + virtual Status status() const { + return status_.ok() ? iter_->status() : status_; + } + + private: + mutable Status status_; + Iterator* iter_; + + // No copying allowed + KeyConvertingIterator(const KeyConvertingIterator&); + void operator=(const KeyConvertingIterator&); +}; + +class MemTableConstructor: public Constructor { + public: + explicit MemTableConstructor(const Comparator* cmp) + : Constructor(cmp), + internal_comparator_(cmp) { + memtable_ = new MemTable(internal_comparator_); + memtable_->Ref(); + } + ~MemTableConstructor() { + memtable_->Unref(); + } + virtual Status FinishImpl(const Options& options, const KVMap& data) { + memtable_->Unref(); + memtable_ = new MemTable(internal_comparator_); + memtable_->Ref(); + int seq = 1; + for (KVMap::const_iterator it = data.begin(); + it != data.end(); + ++it) { + memtable_->Add(seq, kTypeValue, it->first, it->second); + seq++; + } + return Status::OK(); + } + virtual Iterator* NewIterator() const { + return new KeyConvertingIterator(memtable_->NewIterator()); + } + + private: + InternalKeyComparator internal_comparator_; + MemTable* memtable_; +}; + +class DBConstructor: public Constructor { + public: + explicit DBConstructor(const Comparator* cmp) + : Constructor(cmp), + comparator_(cmp) { + db_ = NULL; + NewDB(); + } + ~DBConstructor() { + delete db_; + } + virtual Status FinishImpl(const Options& options, const KVMap& data) { + delete db_; + db_ = NULL; + NewDB(); + for (KVMap::const_iterator it = data.begin(); + it != data.end(); + ++it) { + WriteBatch batch; + batch.Put(it->first, it->second); + ASSERT_TRUE(db_->Write(WriteOptions(), &batch).ok()); + } + return Status::OK(); + } + virtual Iterator* NewIterator() const { + return db_->NewIterator(ReadOptions()); + } + + virtual DB* db() const { return db_; } + + private: + void NewDB() { + std::string name = test::TmpDir() + "/table_testdb"; + + Options options; + options.comparator = comparator_; + Status status = DestroyDB(name, options); + ASSERT_TRUE(status.ok()) << status.ToString(); + + options.create_if_missing = true; + options.error_if_exists = true; + options.write_buffer_size = 10000; // Something small to force merging + status = DB::Open(options, name, &db_); + ASSERT_TRUE(status.ok()) << status.ToString(); + } + + const Comparator* comparator_; + DB* db_; +}; + +enum TestType { + TABLE_TEST, + BLOCK_TEST, + MEMTABLE_TEST, + DB_TEST +}; + +struct TestArgs { + TestType type; + bool reverse_compare; + int restart_interval; +}; + +static const TestArgs kTestArgList[] = { + { TABLE_TEST, false, 16 }, + { TABLE_TEST, false, 1 }, + { TABLE_TEST, false, 1024 }, + { TABLE_TEST, true, 16 }, + { TABLE_TEST, true, 1 }, + { TABLE_TEST, true, 1024 }, + + { BLOCK_TEST, false, 16 }, + { BLOCK_TEST, false, 1 }, + { BLOCK_TEST, false, 1024 }, + { BLOCK_TEST, true, 16 }, + { BLOCK_TEST, true, 1 }, + { BLOCK_TEST, true, 1024 }, + + // Restart interval does not matter for memtables + { MEMTABLE_TEST, false, 16 }, + { MEMTABLE_TEST, true, 16 }, + + // Do not bother with restart interval variations for DB + { DB_TEST, false, 16 }, + { DB_TEST, true, 16 }, +}; +static const int kNumTestArgs = sizeof(kTestArgList) / sizeof(kTestArgList[0]); + +class Harness { + public: + Harness() : constructor_(NULL) { } + + void Init(const TestArgs& args) { + delete constructor_; + constructor_ = NULL; + options_ = Options(); + + options_.block_restart_interval = args.restart_interval; + // Use shorter block size for tests to exercise block boundary + // conditions more. + options_.block_size = 256; + if (args.reverse_compare) { + options_.comparator = &reverse_key_comparator; + } + switch (args.type) { + case TABLE_TEST: + constructor_ = new TableConstructor(options_.comparator); + break; + case BLOCK_TEST: + constructor_ = new BlockConstructor(options_.comparator); + break; + case MEMTABLE_TEST: + constructor_ = new MemTableConstructor(options_.comparator); + break; + case DB_TEST: + constructor_ = new DBConstructor(options_.comparator); + break; + } + } + + ~Harness() { + delete constructor_; + } + + void Add(const std::string& key, const std::string& value) { + constructor_->Add(key, value); + } + + void Test(Random* rnd) { + std::vector keys; + KVMap data; + constructor_->Finish(options_, &keys, &data); + + TestForwardScan(keys, data); + TestBackwardScan(keys, data); + TestRandomAccess(rnd, keys, data); + } + + void TestForwardScan(const std::vector& keys, + const KVMap& data) { + Iterator* iter = constructor_->NewIterator(); + ASSERT_TRUE(!iter->Valid()); + iter->SeekToFirst(); + for (KVMap::const_iterator model_iter = data.begin(); + model_iter != data.end(); + ++model_iter) { + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + iter->Next(); + } + ASSERT_TRUE(!iter->Valid()); + delete iter; + } + + void TestBackwardScan(const std::vector& keys, + const KVMap& data) { + Iterator* iter = constructor_->NewIterator(); + ASSERT_TRUE(!iter->Valid()); + iter->SeekToLast(); + for (KVMap::const_reverse_iterator model_iter = data.rbegin(); + model_iter != data.rend(); + ++model_iter) { + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + iter->Prev(); + } + ASSERT_TRUE(!iter->Valid()); + delete iter; + } + + void TestRandomAccess(Random* rnd, + const std::vector& keys, + const KVMap& data) { + static const bool kVerbose = false; + Iterator* iter = constructor_->NewIterator(); + ASSERT_TRUE(!iter->Valid()); + KVMap::const_iterator model_iter = data.begin(); + if (kVerbose) fprintf(stderr, "---\n"); + for (int i = 0; i < 200; i++) { + const int toss = rnd->Uniform(5); + switch (toss) { + case 0: { + if (iter->Valid()) { + if (kVerbose) fprintf(stderr, "Next\n"); + iter->Next(); + ++model_iter; + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + } + break; + } + + case 1: { + if (kVerbose) fprintf(stderr, "SeekToFirst\n"); + iter->SeekToFirst(); + model_iter = data.begin(); + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + break; + } + + case 2: { + std::string key = PickRandomKey(rnd, keys); + model_iter = data.lower_bound(key); + if (kVerbose) fprintf(stderr, "Seek '%s'\n", + EscapeString(key).c_str()); + iter->Seek(Slice(key)); + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + break; + } + + case 3: { + if (iter->Valid()) { + if (kVerbose) fprintf(stderr, "Prev\n"); + iter->Prev(); + if (model_iter == data.begin()) { + model_iter = data.end(); // Wrap around to invalid value + } else { + --model_iter; + } + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + } + break; + } + + case 4: { + if (kVerbose) fprintf(stderr, "SeekToLast\n"); + iter->SeekToLast(); + if (keys.empty()) { + model_iter = data.end(); + } else { + std::string last = data.rbegin()->first; + model_iter = data.lower_bound(last); + } + ASSERT_EQ(ToString(data, model_iter), ToString(iter)); + break; + } + } + } + delete iter; + } + + std::string ToString(const KVMap& data, const KVMap::const_iterator& it) { + if (it == data.end()) { + return "END"; + } else { + return "'" + it->first + "->" + it->second + "'"; + } + } + + std::string ToString(const KVMap& data, + const KVMap::const_reverse_iterator& it) { + if (it == data.rend()) { + return "END"; + } else { + return "'" + it->first + "->" + it->second + "'"; + } + } + + std::string ToString(const Iterator* it) { + if (!it->Valid()) { + return "END"; + } else { + return "'" + it->key().ToString() + "->" + it->value().ToString() + "'"; + } + } + + std::string PickRandomKey(Random* rnd, const std::vector& keys) { + if (keys.empty()) { + return "foo"; + } else { + const int index = rnd->Uniform(keys.size()); + std::string result = keys[index]; + switch (rnd->Uniform(3)) { + case 0: + // Return an existing key + break; + case 1: { + // Attempt to return something smaller than an existing key + if (result.size() > 0 && result[result.size()-1] > '\0') { + result[result.size()-1]--; + } + break; + } + case 2: { + // Return something larger than an existing key + Increment(options_.comparator, &result); + break; + } + } + return result; + } + } + + // Returns NULL if not running against a DB + DB* db() const { return constructor_->db(); } + + private: + Options options_; + Constructor* constructor_; +}; + +// Test the empty key +TEST(Harness, SimpleEmptyKey) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 1); + Add("", "v"); + Test(&rnd); + } +} + +TEST(Harness, SimpleSingle) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 2); + Add("abc", "v"); + Test(&rnd); + } +} + +TEST(Harness, SimpleMulti) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 3); + Add("abc", "v"); + Add("abcd", "v"); + Add("ac", "v2"); + Test(&rnd); + } +} + +TEST(Harness, SimpleSpecialKey) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 4); + Add("\xff\xff", "v3"); + Test(&rnd); + } +} + +TEST(Harness, Randomized) { + for (int i = 0; i < kNumTestArgs; i++) { + Init(kTestArgList[i]); + Random rnd(test::RandomSeed() + 5); + for (int num_entries = 0; num_entries < 2000; + num_entries += (num_entries < 50 ? 1 : 200)) { + if ((num_entries % 10) == 0) { + fprintf(stderr, "case %d of %d: num_entries = %d\n", + (i + 1), int(kNumTestArgs), num_entries); + } + for (int e = 0; e < num_entries; e++) { + std::string v; + Add(test::RandomKey(&rnd, rnd.Skewed(4)), + test::RandomString(&rnd, rnd.Skewed(5), &v).ToString()); + } + Test(&rnd); + } + } +} + +TEST(Harness, RandomizedLongDB) { + Random rnd(test::RandomSeed()); + TestArgs args = { DB_TEST, false, 16 }; + Init(args); + int num_entries = 100000; + for (int e = 0; e < num_entries; e++) { + std::string v; + Add(test::RandomKey(&rnd, rnd.Skewed(4)), + test::RandomString(&rnd, rnd.Skewed(5), &v).ToString()); + } + Test(&rnd); + + // We must have created enough data to force merging + int files = 0; + for (int level = 0; level < config::kNumLevels; level++) { + std::string value; + char name[100]; + snprintf(name, sizeof(name), "leveldb.num-files-at-level%d", level); + ASSERT_TRUE(db()->GetProperty(name, &value)); + files += atoi(value.c_str()); + } + ASSERT_GT(files, 0); +} + +class MemTableTest { }; + +TEST(MemTableTest, Simple) { + InternalKeyComparator cmp(BytewiseComparator()); + MemTable* memtable = new MemTable(cmp); + memtable->Ref(); + WriteBatch batch; + WriteBatchInternal::SetSequence(&batch, 100); + batch.Put(std::string("k1"), std::string("v1")); + batch.Put(std::string("k2"), std::string("v2")); + batch.Put(std::string("k3"), std::string("v3")); + batch.Put(std::string("largekey"), std::string("vlarge")); + ASSERT_TRUE(WriteBatchInternal::InsertInto(&batch, memtable).ok()); + + Iterator* iter = memtable->NewIterator(); + iter->SeekToFirst(); + while (iter->Valid()) { + fprintf(stderr, "key: '%s' -> '%s'\n", + iter->key().ToString().c_str(), + iter->value().ToString().c_str()); + iter->Next(); + } + + delete iter; + memtable->Unref(); +} + +static bool Between(uint64_t val, uint64_t low, uint64_t high) { + bool result = (val >= low) && (val <= high); + if (!result) { + fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n", + (unsigned long long)(val), + (unsigned long long)(low), + (unsigned long long)(high)); + } + return result; +} + +class TableTest { }; + +TEST(TableTest, ApproximateOffsetOfPlain) { + TableConstructor c(BytewiseComparator()); + c.Add("k01", "hello"); + c.Add("k02", "hello2"); + c.Add("k03", std::string(10000, 'x')); + c.Add("k04", std::string(200000, 'x')); + c.Add("k05", std::string(300000, 'x')); + c.Add("k06", "hello3"); + c.Add("k07", std::string(100000, 'x')); + std::vector keys; + KVMap kvmap; + Options options; + options.block_size = 1024; + options.compression = kNoCompression; + c.Finish(options, &keys, &kvmap); + + ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01a"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 10000, 11000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04a"), 210000, 211000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k05"), 210000, 211000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k06"), 510000, 511000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k07"), 510000, 511000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 610000, 612000)); + +} + +static bool SnappyCompressionSupported() { + std::string out; + Slice in = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + return port::Snappy_Compress(in.data(), in.size(), &out); +} + +TEST(TableTest, ApproximateOffsetOfCompressed) { + if (!SnappyCompressionSupported()) { + fprintf(stderr, "skipping compression tests\n"); + return; + } + + Random rnd(301); + TableConstructor c(BytewiseComparator()); + std::string tmp; + c.Add("k01", "hello"); + c.Add("k02", test::CompressibleString(&rnd, 0.25, 10000, &tmp)); + c.Add("k03", "hello3"); + c.Add("k04", test::CompressibleString(&rnd, 0.25, 10000, &tmp)); + std::vector keys; + KVMap kvmap; + Options options; + options.block_size = 1024; + options.compression = kSnappyCompression; + c.Finish(options, &keys, &kvmap); + + ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 2000, 3000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 2000, 3000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 4000, 6000)); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/table/two_level_iterator.cc b/src/leveldb/table/two_level_iterator.cc new file mode 100644 index 0000000..7822eba --- /dev/null +++ b/src/leveldb/table/two_level_iterator.cc @@ -0,0 +1,182 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "table/two_level_iterator.h" + +#include "leveldb/table.h" +#include "table/block.h" +#include "table/format.h" +#include "table/iterator_wrapper.h" + +namespace leveldb { + +namespace { + +typedef Iterator* (*BlockFunction)(void*, const ReadOptions&, const Slice&); + +class TwoLevelIterator: public Iterator { + public: + TwoLevelIterator( + Iterator* index_iter, + BlockFunction block_function, + void* arg, + const ReadOptions& options); + + virtual ~TwoLevelIterator(); + + virtual void Seek(const Slice& target); + virtual void SeekToFirst(); + virtual void SeekToLast(); + virtual void Next(); + virtual void Prev(); + + virtual bool Valid() const { + return data_iter_.Valid(); + } + virtual Slice key() const { + assert(Valid()); + return data_iter_.key(); + } + virtual Slice value() const { + assert(Valid()); + return data_iter_.value(); + } + virtual Status status() const { + // It'd be nice if status() returned a const Status& instead of a Status + if (!index_iter_.status().ok()) { + return index_iter_.status(); + } else if (data_iter_.iter() != NULL && !data_iter_.status().ok()) { + return data_iter_.status(); + } else { + return status_; + } + } + + private: + void SaveError(const Status& s) { + if (status_.ok() && !s.ok()) status_ = s; + } + void SkipEmptyDataBlocksForward(); + void SkipEmptyDataBlocksBackward(); + void SetDataIterator(Iterator* data_iter); + void InitDataBlock(); + + BlockFunction block_function_; + void* arg_; + const ReadOptions options_; + Status status_; + IteratorWrapper index_iter_; + IteratorWrapper data_iter_; // May be NULL + // If data_iter_ is non-NULL, then "data_block_handle_" holds the + // "index_value" passed to block_function_ to create the data_iter_. + std::string data_block_handle_; +}; + +TwoLevelIterator::TwoLevelIterator( + Iterator* index_iter, + BlockFunction block_function, + void* arg, + const ReadOptions& options) + : block_function_(block_function), + arg_(arg), + options_(options), + index_iter_(index_iter), + data_iter_(NULL) { +} + +TwoLevelIterator::~TwoLevelIterator() { +} + +void TwoLevelIterator::Seek(const Slice& target) { + index_iter_.Seek(target); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.Seek(target); + SkipEmptyDataBlocksForward(); +} + +void TwoLevelIterator::SeekToFirst() { + index_iter_.SeekToFirst(); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.SeekToFirst(); + SkipEmptyDataBlocksForward(); +} + +void TwoLevelIterator::SeekToLast() { + index_iter_.SeekToLast(); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.SeekToLast(); + SkipEmptyDataBlocksBackward(); +} + +void TwoLevelIterator::Next() { + assert(Valid()); + data_iter_.Next(); + SkipEmptyDataBlocksForward(); +} + +void TwoLevelIterator::Prev() { + assert(Valid()); + data_iter_.Prev(); + SkipEmptyDataBlocksBackward(); +} + + +void TwoLevelIterator::SkipEmptyDataBlocksForward() { + while (data_iter_.iter() == NULL || !data_iter_.Valid()) { + // Move to next block + if (!index_iter_.Valid()) { + SetDataIterator(NULL); + return; + } + index_iter_.Next(); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.SeekToFirst(); + } +} + +void TwoLevelIterator::SkipEmptyDataBlocksBackward() { + while (data_iter_.iter() == NULL || !data_iter_.Valid()) { + // Move to next block + if (!index_iter_.Valid()) { + SetDataIterator(NULL); + return; + } + index_iter_.Prev(); + InitDataBlock(); + if (data_iter_.iter() != NULL) data_iter_.SeekToLast(); + } +} + +void TwoLevelIterator::SetDataIterator(Iterator* data_iter) { + if (data_iter_.iter() != NULL) SaveError(data_iter_.status()); + data_iter_.Set(data_iter); +} + +void TwoLevelIterator::InitDataBlock() { + if (!index_iter_.Valid()) { + SetDataIterator(NULL); + } else { + Slice handle = index_iter_.value(); + if (data_iter_.iter() != NULL && handle.compare(data_block_handle_) == 0) { + // data_iter_ is already constructed with this iterator, so + // no need to change anything + } else { + Iterator* iter = (*block_function_)(arg_, options_, handle); + data_block_handle_.assign(handle.data(), handle.size()); + SetDataIterator(iter); + } + } +} + +} // namespace + +Iterator* NewTwoLevelIterator( + Iterator* index_iter, + BlockFunction block_function, + void* arg, + const ReadOptions& options) { + return new TwoLevelIterator(index_iter, block_function, arg, options); +} + +} // namespace leveldb diff --git a/src/leveldb/table/two_level_iterator.h b/src/leveldb/table/two_level_iterator.h new file mode 100644 index 0000000..629ca34 --- /dev/null +++ b/src/leveldb/table/two_level_iterator.h @@ -0,0 +1,34 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_TABLE_TWO_LEVEL_ITERATOR_H_ +#define STORAGE_LEVELDB_TABLE_TWO_LEVEL_ITERATOR_H_ + +#include "leveldb/iterator.h" + +namespace leveldb { + +struct ReadOptions; + +// Return a new two level iterator. A two-level iterator contains an +// index iterator whose values point to a sequence of blocks where +// each block is itself a sequence of key,value pairs. The returned +// two-level iterator yields the concatenation of all key/value pairs +// in the sequence of blocks. Takes ownership of "index_iter" and +// will delete it when no longer needed. +// +// Uses a supplied function to convert an index_iter value into +// an iterator over the contents of the corresponding block. +extern Iterator* NewTwoLevelIterator( + Iterator* index_iter, + Iterator* (*block_function)( + void* arg, + const ReadOptions& options, + const Slice& index_value), + void* arg, + const ReadOptions& options); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_TABLE_TWO_LEVEL_ITERATOR_H_ diff --git a/src/leveldb/util/arena.cc b/src/leveldb/util/arena.cc new file mode 100644 index 0000000..9551d6a --- /dev/null +++ b/src/leveldb/util/arena.cc @@ -0,0 +1,68 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/arena.h" +#include + +namespace leveldb { + +static const int kBlockSize = 4096; + +Arena::Arena() { + blocks_memory_ = 0; + alloc_ptr_ = NULL; // First allocation will allocate a block + alloc_bytes_remaining_ = 0; +} + +Arena::~Arena() { + for (size_t i = 0; i < blocks_.size(); i++) { + delete[] blocks_[i]; + } +} + +char* Arena::AllocateFallback(size_t bytes) { + if (bytes > kBlockSize / 4) { + // Object is more than a quarter of our block size. Allocate it separately + // to avoid wasting too much space in leftover bytes. + char* result = AllocateNewBlock(bytes); + return result; + } + + // We waste the remaining space in the current block. + alloc_ptr_ = AllocateNewBlock(kBlockSize); + alloc_bytes_remaining_ = kBlockSize; + + char* result = alloc_ptr_; + alloc_ptr_ += bytes; + alloc_bytes_remaining_ -= bytes; + return result; +} + +char* Arena::AllocateAligned(size_t bytes) { + const int align = sizeof(void*); // We'll align to pointer size + assert((align & (align-1)) == 0); // Pointer size should be a power of 2 + size_t current_mod = reinterpret_cast(alloc_ptr_) & (align-1); + size_t slop = (current_mod == 0 ? 0 : align - current_mod); + size_t needed = bytes + slop; + char* result; + if (needed <= alloc_bytes_remaining_) { + result = alloc_ptr_ + slop; + alloc_ptr_ += needed; + alloc_bytes_remaining_ -= needed; + } else { + // AllocateFallback always returned aligned memory + result = AllocateFallback(bytes); + } + assert((reinterpret_cast(result) & (align-1)) == 0); + return result; +} + +char* Arena::AllocateNewBlock(size_t block_bytes) { + char* result = new char[block_bytes]; + blocks_memory_ += block_bytes; + blocks_.push_back(result); + return result; +} + +} // namespace leveldb diff --git a/src/leveldb/util/arena.h b/src/leveldb/util/arena.h new file mode 100644 index 0000000..8f7dde2 --- /dev/null +++ b/src/leveldb/util/arena.h @@ -0,0 +1,68 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_ARENA_H_ +#define STORAGE_LEVELDB_UTIL_ARENA_H_ + +#include +#include +#include +#include + +namespace leveldb { + +class Arena { + public: + Arena(); + ~Arena(); + + // Return a pointer to a newly allocated memory block of "bytes" bytes. + char* Allocate(size_t bytes); + + // Allocate memory with the normal alignment guarantees provided by malloc + char* AllocateAligned(size_t bytes); + + // Returns an estimate of the total memory usage of data allocated + // by the arena (including space allocated but not yet used for user + // allocations). + size_t MemoryUsage() const { + return blocks_memory_ + blocks_.capacity() * sizeof(char*); + } + + private: + char* AllocateFallback(size_t bytes); + char* AllocateNewBlock(size_t block_bytes); + + // Allocation state + char* alloc_ptr_; + size_t alloc_bytes_remaining_; + + // Array of new[] allocated memory blocks + std::vector blocks_; + + // Bytes of memory in blocks allocated so far + size_t blocks_memory_; + + // No copying allowed + Arena(const Arena&); + void operator=(const Arena&); +}; + +inline char* Arena::Allocate(size_t bytes) { + // The semantics of what to return are a bit messy if we allow + // 0-byte allocations, so we disallow them here (we don't need + // them for our internal use). + assert(bytes > 0); + if (bytes <= alloc_bytes_remaining_) { + char* result = alloc_ptr_; + alloc_ptr_ += bytes; + alloc_bytes_remaining_ -= bytes; + return result; + } + return AllocateFallback(bytes); +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_ARENA_H_ diff --git a/src/leveldb/util/arena_test.cc b/src/leveldb/util/arena_test.cc new file mode 100644 index 0000000..63d1778 --- /dev/null +++ b/src/leveldb/util/arena_test.cc @@ -0,0 +1,68 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/arena.h" + +#include "util/random.h" +#include "util/testharness.h" + +namespace leveldb { + +class ArenaTest { }; + +TEST(ArenaTest, Empty) { + Arena arena; +} + +TEST(ArenaTest, Simple) { + std::vector > allocated; + Arena arena; + const int N = 100000; + size_t bytes = 0; + Random rnd(301); + for (int i = 0; i < N; i++) { + size_t s; + if (i % (N / 10) == 0) { + s = i; + } else { + s = rnd.OneIn(4000) ? rnd.Uniform(6000) : + (rnd.OneIn(10) ? rnd.Uniform(100) : rnd.Uniform(20)); + } + if (s == 0) { + // Our arena disallows size 0 allocations. + s = 1; + } + char* r; + if (rnd.OneIn(10)) { + r = arena.AllocateAligned(s); + } else { + r = arena.Allocate(s); + } + + for (int b = 0; b < s; b++) { + // Fill the "i"th allocation with a known bit pattern + r[b] = i % 256; + } + bytes += s; + allocated.push_back(std::make_pair(s, r)); + ASSERT_GE(arena.MemoryUsage(), bytes); + if (i > N/10) { + ASSERT_LE(arena.MemoryUsage(), bytes * 1.10); + } + } + for (int i = 0; i < allocated.size(); i++) { + size_t num_bytes = allocated[i].first; + const char* p = allocated[i].second; + for (int b = 0; b < num_bytes; b++) { + // Check the "i"th allocation for the known bit pattern + ASSERT_EQ(int(p[b]) & 0xff, i % 256); + } + } +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/bloom.cc b/src/leveldb/util/bloom.cc new file mode 100644 index 0000000..d7941cd --- /dev/null +++ b/src/leveldb/util/bloom.cc @@ -0,0 +1,95 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/filter_policy.h" + +#include "leveldb/slice.h" +#include "util/hash.h" + +namespace leveldb { + +namespace { +static uint32_t BloomHash(const Slice& key) { + return Hash(key.data(), key.size(), 0xbc9f1d34); +} + +class BloomFilterPolicy : public FilterPolicy { + private: + size_t bits_per_key_; + size_t k_; + + public: + explicit BloomFilterPolicy(int bits_per_key) + : bits_per_key_(bits_per_key) { + // We intentionally round down to reduce probing cost a little bit + k_ = static_cast(bits_per_key * 0.69); // 0.69 =~ ln(2) + if (k_ < 1) k_ = 1; + if (k_ > 30) k_ = 30; + } + + virtual const char* Name() const { + return "leveldb.BuiltinBloomFilter"; + } + + virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + // Compute bloom filter size (in both bits and bytes) + size_t bits = n * bits_per_key_; + + // For small n, we can see a very high false positive rate. Fix it + // by enforcing a minimum bloom filter length. + if (bits < 64) bits = 64; + + size_t bytes = (bits + 7) / 8; + bits = bytes * 8; + + const size_t init_size = dst->size(); + dst->resize(init_size + bytes, 0); + dst->push_back(static_cast(k_)); // Remember # of probes in filter + char* array = &(*dst)[init_size]; + for (size_t i = 0; i < n; i++) { + // Use double-hashing to generate a sequence of hash values. + // See analysis in [Kirsch,Mitzenmacher 2006]. + uint32_t h = BloomHash(keys[i]); + const uint32_t delta = (h >> 17) | (h << 15); // Rotate right 17 bits + for (size_t j = 0; j < k_; j++) { + const uint32_t bitpos = h % bits; + array[bitpos/8] |= (1 << (bitpos % 8)); + h += delta; + } + } + } + + virtual bool KeyMayMatch(const Slice& key, const Slice& bloom_filter) const { + const size_t len = bloom_filter.size(); + if (len < 2) return false; + + const char* array = bloom_filter.data(); + const size_t bits = (len - 1) * 8; + + // Use the encoded k so that we can read filters generated by + // bloom filters created using different parameters. + const size_t k = array[len-1]; + if (k > 30) { + // Reserved for potentially new encodings for short bloom filters. + // Consider it a match. + return true; + } + + uint32_t h = BloomHash(key); + const uint32_t delta = (h >> 17) | (h << 15); // Rotate right 17 bits + for (size_t j = 0; j < k; j++) { + const uint32_t bitpos = h % bits; + if ((array[bitpos/8] & (1 << (bitpos % 8))) == 0) return false; + h += delta; + } + return true; + } +}; +} + +const FilterPolicy* NewBloomFilterPolicy(int bits_per_key) { + return new BloomFilterPolicy(bits_per_key); +} + +} // namespace leveldb diff --git a/src/leveldb/util/bloom_test.cc b/src/leveldb/util/bloom_test.cc new file mode 100644 index 0000000..0bf8e8d --- /dev/null +++ b/src/leveldb/util/bloom_test.cc @@ -0,0 +1,160 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/filter_policy.h" + +#include "util/coding.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +static const int kVerbose = 1; + +static Slice Key(int i, char* buffer) { + EncodeFixed32(buffer, i); + return Slice(buffer, sizeof(uint32_t)); +} + +class BloomTest { + private: + const FilterPolicy* policy_; + std::string filter_; + std::vector keys_; + + public: + BloomTest() : policy_(NewBloomFilterPolicy(10)) { } + + ~BloomTest() { + delete policy_; + } + + void Reset() { + keys_.clear(); + filter_.clear(); + } + + void Add(const Slice& s) { + keys_.push_back(s.ToString()); + } + + void Build() { + std::vector key_slices; + for (size_t i = 0; i < keys_.size(); i++) { + key_slices.push_back(Slice(keys_[i])); + } + filter_.clear(); + policy_->CreateFilter(&key_slices[0], key_slices.size(), &filter_); + keys_.clear(); + if (kVerbose >= 2) DumpFilter(); + } + + size_t FilterSize() const { + return filter_.size(); + } + + void DumpFilter() { + fprintf(stderr, "F("); + for (size_t i = 0; i+1 < filter_.size(); i++) { + const unsigned int c = static_cast(filter_[i]); + for (int j = 0; j < 8; j++) { + fprintf(stderr, "%c", (c & (1 <KeyMayMatch(s, filter_); + } + + double FalsePositiveRate() { + char buffer[sizeof(int)]; + int result = 0; + for (int i = 0; i < 10000; i++) { + if (Matches(Key(i + 1000000000, buffer))) { + result++; + } + } + return result / 10000.0; + } +}; + +TEST(BloomTest, EmptyFilter) { + ASSERT_TRUE(! Matches("hello")); + ASSERT_TRUE(! Matches("world")); +} + +TEST(BloomTest, Small) { + Add("hello"); + Add("world"); + ASSERT_TRUE(Matches("hello")); + ASSERT_TRUE(Matches("world")); + ASSERT_TRUE(! Matches("x")); + ASSERT_TRUE(! Matches("foo")); +} + +static int NextLength(int length) { + if (length < 10) { + length += 1; + } else if (length < 100) { + length += 10; + } else if (length < 1000) { + length += 100; + } else { + length += 1000; + } + return length; +} + +TEST(BloomTest, VaryingLengths) { + char buffer[sizeof(int)]; + + // Count number of filters that significantly exceed the false positive rate + int mediocre_filters = 0; + int good_filters = 0; + + for (int length = 1; length <= 10000; length = NextLength(length)) { + Reset(); + for (int i = 0; i < length; i++) { + Add(Key(i, buffer)); + } + Build(); + + ASSERT_LE(FilterSize(), (length * 10 / 8) + 40) << length; + + // All added keys must match + for (int i = 0; i < length; i++) { + ASSERT_TRUE(Matches(Key(i, buffer))) + << "Length " << length << "; key " << i; + } + + // Check false positive rate + double rate = FalsePositiveRate(); + if (kVerbose >= 1) { + fprintf(stderr, "False positives: %5.2f%% @ length = %6d ; bytes = %6d\n", + rate*100.0, length, static_cast(FilterSize())); + } + ASSERT_LE(rate, 0.02); // Must not be over 2% + if (rate > 0.0125) mediocre_filters++; // Allowed, but not too often + else good_filters++; + } + if (kVerbose >= 1) { + fprintf(stderr, "Filters: %d good, %d mediocre\n", + good_filters, mediocre_filters); + } + ASSERT_LE(mediocre_filters, good_filters/5); +} + +// Different bits-per-byte + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/cache.cc b/src/leveldb/util/cache.cc new file mode 100644 index 0000000..24f1f63 --- /dev/null +++ b/src/leveldb/util/cache.cc @@ -0,0 +1,328 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include + +#include "leveldb/cache.h" +#include "port/port.h" +#include "util/hash.h" +#include "util/mutexlock.h" + +namespace leveldb { + +Cache::~Cache() { +} + +namespace { + +// LRU cache implementation + +// An entry is a variable length heap-allocated structure. Entries +// are kept in a circular doubly linked list ordered by access time. +struct LRUHandle { + void* value; + void (*deleter)(const Slice&, void* value); + LRUHandle* next_hash; + LRUHandle* next; + LRUHandle* prev; + size_t charge; // TODO(opt): Only allow uint32_t? + size_t key_length; + uint32_t refs; + uint32_t hash; // Hash of key(); used for fast sharding and comparisons + char key_data[1]; // Beginning of key + + Slice key() const { + // For cheaper lookups, we allow a temporary Handle object + // to store a pointer to a key in "value". + if (next == this) { + return *(reinterpret_cast(value)); + } else { + return Slice(key_data, key_length); + } + } +}; + +// We provide our own simple hash table since it removes a whole bunch +// of porting hacks and is also faster than some of the built-in hash +// table implementations in some of the compiler/runtime combinations +// we have tested. E.g., readrandom speeds up by ~5% over the g++ +// 4.4.3's builtin hashtable. +class HandleTable { + public: + HandleTable() : length_(0), elems_(0), list_(NULL) { Resize(); } + ~HandleTable() { delete[] list_; } + + LRUHandle* Lookup(const Slice& key, uint32_t hash) { + return *FindPointer(key, hash); + } + + LRUHandle* Insert(LRUHandle* h) { + LRUHandle** ptr = FindPointer(h->key(), h->hash); + LRUHandle* old = *ptr; + h->next_hash = (old == NULL ? NULL : old->next_hash); + *ptr = h; + if (old == NULL) { + ++elems_; + if (elems_ > length_) { + // Since each cache entry is fairly large, we aim for a small + // average linked list length (<= 1). + Resize(); + } + } + return old; + } + + LRUHandle* Remove(const Slice& key, uint32_t hash) { + LRUHandle** ptr = FindPointer(key, hash); + LRUHandle* result = *ptr; + if (result != NULL) { + *ptr = result->next_hash; + --elems_; + } + return result; + } + + private: + // The table consists of an array of buckets where each bucket is + // a linked list of cache entries that hash into the bucket. + uint32_t length_; + uint32_t elems_; + LRUHandle** list_; + + // Return a pointer to slot that points to a cache entry that + // matches key/hash. If there is no such cache entry, return a + // pointer to the trailing slot in the corresponding linked list. + LRUHandle** FindPointer(const Slice& key, uint32_t hash) { + LRUHandle** ptr = &list_[hash & (length_ - 1)]; + while (*ptr != NULL && + ((*ptr)->hash != hash || key != (*ptr)->key())) { + ptr = &(*ptr)->next_hash; + } + return ptr; + } + + void Resize() { + uint32_t new_length = 4; + while (new_length < elems_) { + new_length *= 2; + } + LRUHandle** new_list = new LRUHandle*[new_length]; + memset(new_list, 0, sizeof(new_list[0]) * new_length); + uint32_t count = 0; + for (uint32_t i = 0; i < length_; i++) { + LRUHandle* h = list_[i]; + while (h != NULL) { + LRUHandle* next = h->next_hash; + Slice key = h->key(); + uint32_t hash = h->hash; + LRUHandle** ptr = &new_list[hash & (new_length - 1)]; + h->next_hash = *ptr; + *ptr = h; + h = next; + count++; + } + } + assert(elems_ == count); + delete[] list_; + list_ = new_list; + length_ = new_length; + } +}; + +// A single shard of sharded cache. +class LRUCache { + public: + LRUCache(); + ~LRUCache(); + + // Separate from constructor so caller can easily make an array of LRUCache + void SetCapacity(size_t capacity) { capacity_ = capacity; } + + // Like Cache methods, but with an extra "hash" parameter. + Cache::Handle* Insert(const Slice& key, uint32_t hash, + void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)); + Cache::Handle* Lookup(const Slice& key, uint32_t hash); + void Release(Cache::Handle* handle); + void Erase(const Slice& key, uint32_t hash); + + private: + void LRU_Remove(LRUHandle* e); + void LRU_Append(LRUHandle* e); + void Unref(LRUHandle* e); + + // Initialized before use. + size_t capacity_; + + // mutex_ protects the following state. + port::Mutex mutex_; + size_t usage_; + uint64_t last_id_; + + // Dummy head of LRU list. + // lru.prev is newest entry, lru.next is oldest entry. + LRUHandle lru_; + + HandleTable table_; +}; + +LRUCache::LRUCache() + : usage_(0), + last_id_(0) { + // Make empty circular linked list + lru_.next = &lru_; + lru_.prev = &lru_; +} + +LRUCache::~LRUCache() { + for (LRUHandle* e = lru_.next; e != &lru_; ) { + LRUHandle* next = e->next; + assert(e->refs == 1); // Error if caller has an unreleased handle + Unref(e); + e = next; + } +} + +void LRUCache::Unref(LRUHandle* e) { + assert(e->refs > 0); + e->refs--; + if (e->refs <= 0) { + usage_ -= e->charge; + (*e->deleter)(e->key(), e->value); + free(e); + } +} + +void LRUCache::LRU_Remove(LRUHandle* e) { + e->next->prev = e->prev; + e->prev->next = e->next; +} + +void LRUCache::LRU_Append(LRUHandle* e) { + // Make "e" newest entry by inserting just before lru_ + e->next = &lru_; + e->prev = lru_.prev; + e->prev->next = e; + e->next->prev = e; +} + +Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) { + MutexLock l(&mutex_); + LRUHandle* e = table_.Lookup(key, hash); + if (e != NULL) { + e->refs++; + LRU_Remove(e); + LRU_Append(e); + } + return reinterpret_cast(e); +} + +void LRUCache::Release(Cache::Handle* handle) { + MutexLock l(&mutex_); + Unref(reinterpret_cast(handle)); +} + +Cache::Handle* LRUCache::Insert( + const Slice& key, uint32_t hash, void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)) { + MutexLock l(&mutex_); + + LRUHandle* e = reinterpret_cast( + malloc(sizeof(LRUHandle)-1 + key.size())); + e->value = value; + e->deleter = deleter; + e->charge = charge; + e->key_length = key.size(); + e->hash = hash; + e->refs = 2; // One from LRUCache, one for the returned handle + memcpy(e->key_data, key.data(), key.size()); + LRU_Append(e); + usage_ += charge; + + LRUHandle* old = table_.Insert(e); + if (old != NULL) { + LRU_Remove(old); + Unref(old); + } + + while (usage_ > capacity_ && lru_.next != &lru_) { + LRUHandle* old = lru_.next; + LRU_Remove(old); + table_.Remove(old->key(), old->hash); + Unref(old); + } + + return reinterpret_cast(e); +} + +void LRUCache::Erase(const Slice& key, uint32_t hash) { + MutexLock l(&mutex_); + LRUHandle* e = table_.Remove(key, hash); + if (e != NULL) { + LRU_Remove(e); + Unref(e); + } +} + +static const int kNumShardBits = 4; +static const int kNumShards = 1 << kNumShardBits; + +class ShardedLRUCache : public Cache { + private: + LRUCache shard_[kNumShards]; + port::Mutex id_mutex_; + uint64_t last_id_; + + static inline uint32_t HashSlice(const Slice& s) { + return Hash(s.data(), s.size(), 0); + } + + static uint32_t Shard(uint32_t hash) { + return hash >> (32 - kNumShardBits); + } + + public: + explicit ShardedLRUCache(size_t capacity) + : last_id_(0) { + const size_t per_shard = (capacity + (kNumShards - 1)) / kNumShards; + for (int s = 0; s < kNumShards; s++) { + shard_[s].SetCapacity(per_shard); + } + } + virtual ~ShardedLRUCache() { } + virtual Handle* Insert(const Slice& key, void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)) { + const uint32_t hash = HashSlice(key); + return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter); + } + virtual Handle* Lookup(const Slice& key) { + const uint32_t hash = HashSlice(key); + return shard_[Shard(hash)].Lookup(key, hash); + } + virtual void Release(Handle* handle) { + LRUHandle* h = reinterpret_cast(handle); + shard_[Shard(h->hash)].Release(handle); + } + virtual void Erase(const Slice& key) { + const uint32_t hash = HashSlice(key); + shard_[Shard(hash)].Erase(key, hash); + } + virtual void* Value(Handle* handle) { + return reinterpret_cast(handle)->value; + } + virtual uint64_t NewId() { + MutexLock l(&id_mutex_); + return ++(last_id_); + } +}; + +} // end anonymous namespace + +Cache* NewLRUCache(size_t capacity) { + return new ShardedLRUCache(capacity); +} + +} // namespace leveldb diff --git a/src/leveldb/util/cache_test.cc b/src/leveldb/util/cache_test.cc new file mode 100644 index 0000000..4371671 --- /dev/null +++ b/src/leveldb/util/cache_test.cc @@ -0,0 +1,186 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/cache.h" + +#include +#include "util/coding.h" +#include "util/testharness.h" + +namespace leveldb { + +// Conversions between numeric keys/values and the types expected by Cache. +static std::string EncodeKey(int k) { + std::string result; + PutFixed32(&result, k); + return result; +} +static int DecodeKey(const Slice& k) { + assert(k.size() == 4); + return DecodeFixed32(k.data()); +} +static void* EncodeValue(uintptr_t v) { return reinterpret_cast(v); } +static int DecodeValue(void* v) { return reinterpret_cast(v); } + +class CacheTest { + public: + static CacheTest* current_; + + static void Deleter(const Slice& key, void* v) { + current_->deleted_keys_.push_back(DecodeKey(key)); + current_->deleted_values_.push_back(DecodeValue(v)); + } + + static const int kCacheSize = 1000; + std::vector deleted_keys_; + std::vector deleted_values_; + Cache* cache_; + + CacheTest() : cache_(NewLRUCache(kCacheSize)) { + current_ = this; + } + + ~CacheTest() { + delete cache_; + } + + int Lookup(int key) { + Cache::Handle* handle = cache_->Lookup(EncodeKey(key)); + const int r = (handle == NULL) ? -1 : DecodeValue(cache_->Value(handle)); + if (handle != NULL) { + cache_->Release(handle); + } + return r; + } + + void Insert(int key, int value, int charge = 1) { + cache_->Release(cache_->Insert(EncodeKey(key), EncodeValue(value), charge, + &CacheTest::Deleter)); + } + + void Erase(int key) { + cache_->Erase(EncodeKey(key)); + } +}; +CacheTest* CacheTest::current_; + +TEST(CacheTest, HitAndMiss) { + ASSERT_EQ(-1, Lookup(100)); + + Insert(100, 101); + ASSERT_EQ(101, Lookup(100)); + ASSERT_EQ(-1, Lookup(200)); + ASSERT_EQ(-1, Lookup(300)); + + Insert(200, 201); + ASSERT_EQ(101, Lookup(100)); + ASSERT_EQ(201, Lookup(200)); + ASSERT_EQ(-1, Lookup(300)); + + Insert(100, 102); + ASSERT_EQ(102, Lookup(100)); + ASSERT_EQ(201, Lookup(200)); + ASSERT_EQ(-1, Lookup(300)); + + ASSERT_EQ(1, deleted_keys_.size()); + ASSERT_EQ(100, deleted_keys_[0]); + ASSERT_EQ(101, deleted_values_[0]); +} + +TEST(CacheTest, Erase) { + Erase(200); + ASSERT_EQ(0, deleted_keys_.size()); + + Insert(100, 101); + Insert(200, 201); + Erase(100); + ASSERT_EQ(-1, Lookup(100)); + ASSERT_EQ(201, Lookup(200)); + ASSERT_EQ(1, deleted_keys_.size()); + ASSERT_EQ(100, deleted_keys_[0]); + ASSERT_EQ(101, deleted_values_[0]); + + Erase(100); + ASSERT_EQ(-1, Lookup(100)); + ASSERT_EQ(201, Lookup(200)); + ASSERT_EQ(1, deleted_keys_.size()); +} + +TEST(CacheTest, EntriesArePinned) { + Insert(100, 101); + Cache::Handle* h1 = cache_->Lookup(EncodeKey(100)); + ASSERT_EQ(101, DecodeValue(cache_->Value(h1))); + + Insert(100, 102); + Cache::Handle* h2 = cache_->Lookup(EncodeKey(100)); + ASSERT_EQ(102, DecodeValue(cache_->Value(h2))); + ASSERT_EQ(0, deleted_keys_.size()); + + cache_->Release(h1); + ASSERT_EQ(1, deleted_keys_.size()); + ASSERT_EQ(100, deleted_keys_[0]); + ASSERT_EQ(101, deleted_values_[0]); + + Erase(100); + ASSERT_EQ(-1, Lookup(100)); + ASSERT_EQ(1, deleted_keys_.size()); + + cache_->Release(h2); + ASSERT_EQ(2, deleted_keys_.size()); + ASSERT_EQ(100, deleted_keys_[1]); + ASSERT_EQ(102, deleted_values_[1]); +} + +TEST(CacheTest, EvictionPolicy) { + Insert(100, 101); + Insert(200, 201); + + // Frequently used entry must be kept around + for (int i = 0; i < kCacheSize + 100; i++) { + Insert(1000+i, 2000+i); + ASSERT_EQ(2000+i, Lookup(1000+i)); + ASSERT_EQ(101, Lookup(100)); + } + ASSERT_EQ(101, Lookup(100)); + ASSERT_EQ(-1, Lookup(200)); +} + +TEST(CacheTest, HeavyEntries) { + // Add a bunch of light and heavy entries and then count the combined + // size of items still in the cache, which must be approximately the + // same as the total capacity. + const int kLight = 1; + const int kHeavy = 10; + int added = 0; + int index = 0; + while (added < 2*kCacheSize) { + const int weight = (index & 1) ? kLight : kHeavy; + Insert(index, 1000+index, weight); + added += weight; + index++; + } + + int cached_weight = 0; + for (int i = 0; i < index; i++) { + const int weight = (i & 1 ? kLight : kHeavy); + int r = Lookup(i); + if (r >= 0) { + cached_weight += weight; + ASSERT_EQ(1000+i, r); + } + } + ASSERT_LE(cached_weight, kCacheSize + kCacheSize/10); +} + +TEST(CacheTest, NewId) { + uint64_t a = cache_->NewId(); + uint64_t b = cache_->NewId(); + ASSERT_NE(a, b); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/coding.cc b/src/leveldb/util/coding.cc new file mode 100644 index 0000000..21e3186 --- /dev/null +++ b/src/leveldb/util/coding.cc @@ -0,0 +1,194 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/coding.h" + +namespace leveldb { + +void EncodeFixed32(char* buf, uint32_t value) { + if (port::kLittleEndian) { + memcpy(buf, &value, sizeof(value)); + } else { + buf[0] = value & 0xff; + buf[1] = (value >> 8) & 0xff; + buf[2] = (value >> 16) & 0xff; + buf[3] = (value >> 24) & 0xff; + } +} + +void EncodeFixed64(char* buf, uint64_t value) { + if (port::kLittleEndian) { + memcpy(buf, &value, sizeof(value)); + } else { + buf[0] = value & 0xff; + buf[1] = (value >> 8) & 0xff; + buf[2] = (value >> 16) & 0xff; + buf[3] = (value >> 24) & 0xff; + buf[4] = (value >> 32) & 0xff; + buf[5] = (value >> 40) & 0xff; + buf[6] = (value >> 48) & 0xff; + buf[7] = (value >> 56) & 0xff; + } +} + +void PutFixed32(std::string* dst, uint32_t value) { + char buf[sizeof(value)]; + EncodeFixed32(buf, value); + dst->append(buf, sizeof(buf)); +} + +void PutFixed64(std::string* dst, uint64_t value) { + char buf[sizeof(value)]; + EncodeFixed64(buf, value); + dst->append(buf, sizeof(buf)); +} + +char* EncodeVarint32(char* dst, uint32_t v) { + // Operate on characters as unsigneds + unsigned char* ptr = reinterpret_cast(dst); + static const int B = 128; + if (v < (1<<7)) { + *(ptr++) = v; + } else if (v < (1<<14)) { + *(ptr++) = v | B; + *(ptr++) = v>>7; + } else if (v < (1<<21)) { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = v>>14; + } else if (v < (1<<28)) { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = (v>>14) | B; + *(ptr++) = v>>21; + } else { + *(ptr++) = v | B; + *(ptr++) = (v>>7) | B; + *(ptr++) = (v>>14) | B; + *(ptr++) = (v>>21) | B; + *(ptr++) = v>>28; + } + return reinterpret_cast(ptr); +} + +void PutVarint32(std::string* dst, uint32_t v) { + char buf[5]; + char* ptr = EncodeVarint32(buf, v); + dst->append(buf, ptr - buf); +} + +char* EncodeVarint64(char* dst, uint64_t v) { + static const int B = 128; + unsigned char* ptr = reinterpret_cast(dst); + while (v >= B) { + *(ptr++) = (v & (B-1)) | B; + v >>= 7; + } + *(ptr++) = static_cast(v); + return reinterpret_cast(ptr); +} + +void PutVarint64(std::string* dst, uint64_t v) { + char buf[10]; + char* ptr = EncodeVarint64(buf, v); + dst->append(buf, ptr - buf); +} + +void PutLengthPrefixedSlice(std::string* dst, const Slice& value) { + PutVarint32(dst, value.size()); + dst->append(value.data(), value.size()); +} + +int VarintLength(uint64_t v) { + int len = 1; + while (v >= 128) { + v >>= 7; + len++; + } + return len; +} + +const char* GetVarint32PtrFallback(const char* p, + const char* limit, + uint32_t* value) { + uint32_t result = 0; + for (uint32_t shift = 0; shift <= 28 && p < limit; shift += 7) { + uint32_t byte = *(reinterpret_cast(p)); + p++; + if (byte & 128) { + // More bytes are present + result |= ((byte & 127) << shift); + } else { + result |= (byte << shift); + *value = result; + return reinterpret_cast(p); + } + } + return NULL; +} + +bool GetVarint32(Slice* input, uint32_t* value) { + const char* p = input->data(); + const char* limit = p + input->size(); + const char* q = GetVarint32Ptr(p, limit, value); + if (q == NULL) { + return false; + } else { + *input = Slice(q, limit - q); + return true; + } +} + +const char* GetVarint64Ptr(const char* p, const char* limit, uint64_t* value) { + uint64_t result = 0; + for (uint32_t shift = 0; shift <= 63 && p < limit; shift += 7) { + uint64_t byte = *(reinterpret_cast(p)); + p++; + if (byte & 128) { + // More bytes are present + result |= ((byte & 127) << shift); + } else { + result |= (byte << shift); + *value = result; + return reinterpret_cast(p); + } + } + return NULL; +} + +bool GetVarint64(Slice* input, uint64_t* value) { + const char* p = input->data(); + const char* limit = p + input->size(); + const char* q = GetVarint64Ptr(p, limit, value); + if (q == NULL) { + return false; + } else { + *input = Slice(q, limit - q); + return true; + } +} + +const char* GetLengthPrefixedSlice(const char* p, const char* limit, + Slice* result) { + uint32_t len; + p = GetVarint32Ptr(p, limit, &len); + if (p == NULL) return NULL; + if (p + len > limit) return NULL; + *result = Slice(p, len); + return p + len; +} + +bool GetLengthPrefixedSlice(Slice* input, Slice* result) { + uint32_t len; + if (GetVarint32(input, &len) && + input->size() >= len) { + *result = Slice(input->data(), len); + input->remove_prefix(len); + return true; + } else { + return false; + } +} + +} // namespace leveldb diff --git a/src/leveldb/util/coding.h b/src/leveldb/util/coding.h new file mode 100644 index 0000000..3993c4a --- /dev/null +++ b/src/leveldb/util/coding.h @@ -0,0 +1,104 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Endian-neutral encoding: +// * Fixed-length numbers are encoded with least-significant byte first +// * In addition we support variable length "varint" encoding +// * Strings are encoded prefixed by their length in varint format + +#ifndef STORAGE_LEVELDB_UTIL_CODING_H_ +#define STORAGE_LEVELDB_UTIL_CODING_H_ + +#include +#include +#include +#include "leveldb/slice.h" +#include "port/port.h" + +namespace leveldb { + +// Standard Put... routines append to a string +extern void PutFixed32(std::string* dst, uint32_t value); +extern void PutFixed64(std::string* dst, uint64_t value); +extern void PutVarint32(std::string* dst, uint32_t value); +extern void PutVarint64(std::string* dst, uint64_t value); +extern void PutLengthPrefixedSlice(std::string* dst, const Slice& value); + +// Standard Get... routines parse a value from the beginning of a Slice +// and advance the slice past the parsed value. +extern bool GetVarint32(Slice* input, uint32_t* value); +extern bool GetVarint64(Slice* input, uint64_t* value); +extern bool GetLengthPrefixedSlice(Slice* input, Slice* result); + +// Pointer-based variants of GetVarint... These either store a value +// in *v and return a pointer just past the parsed value, or return +// NULL on error. These routines only look at bytes in the range +// [p..limit-1] +extern const char* GetVarint32Ptr(const char* p,const char* limit, uint32_t* v); +extern const char* GetVarint64Ptr(const char* p,const char* limit, uint64_t* v); + +// Returns the length of the varint32 or varint64 encoding of "v" +extern int VarintLength(uint64_t v); + +// Lower-level versions of Put... that write directly into a character buffer +// REQUIRES: dst has enough space for the value being written +extern void EncodeFixed32(char* dst, uint32_t value); +extern void EncodeFixed64(char* dst, uint64_t value); + +// Lower-level versions of Put... that write directly into a character buffer +// and return a pointer just past the last byte written. +// REQUIRES: dst has enough space for the value being written +extern char* EncodeVarint32(char* dst, uint32_t value); +extern char* EncodeVarint64(char* dst, uint64_t value); + +// Lower-level versions of Get... that read directly from a character buffer +// without any bounds checking. + +inline uint32_t DecodeFixed32(const char* ptr) { + if (port::kLittleEndian) { + // Load the raw bytes + uint32_t result; + memcpy(&result, ptr, sizeof(result)); // gcc optimizes this to a plain load + return result; + } else { + return ((static_cast(static_cast(ptr[0]))) + | (static_cast(static_cast(ptr[1])) << 8) + | (static_cast(static_cast(ptr[2])) << 16) + | (static_cast(static_cast(ptr[3])) << 24)); + } +} + +inline uint64_t DecodeFixed64(const char* ptr) { + if (port::kLittleEndian) { + // Load the raw bytes + uint64_t result; + memcpy(&result, ptr, sizeof(result)); // gcc optimizes this to a plain load + return result; + } else { + uint64_t lo = DecodeFixed32(ptr); + uint64_t hi = DecodeFixed32(ptr + 4); + return (hi << 32) | lo; + } +} + +// Internal routine for use by fallback path of GetVarint32Ptr +extern const char* GetVarint32PtrFallback(const char* p, + const char* limit, + uint32_t* value); +inline const char* GetVarint32Ptr(const char* p, + const char* limit, + uint32_t* value) { + if (p < limit) { + uint32_t result = *(reinterpret_cast(p)); + if ((result & 128) == 0) { + *value = result; + return p + 1; + } + } + return GetVarint32PtrFallback(p, limit, value); +} + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_CODING_H_ diff --git a/src/leveldb/util/coding_test.cc b/src/leveldb/util/coding_test.cc new file mode 100644 index 0000000..2c52b17 --- /dev/null +++ b/src/leveldb/util/coding_test.cc @@ -0,0 +1,196 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/coding.h" + +#include "util/testharness.h" + +namespace leveldb { + +class Coding { }; + +TEST(Coding, Fixed32) { + std::string s; + for (uint32_t v = 0; v < 100000; v++) { + PutFixed32(&s, v); + } + + const char* p = s.data(); + for (uint32_t v = 0; v < 100000; v++) { + uint32_t actual = DecodeFixed32(p); + ASSERT_EQ(v, actual); + p += sizeof(uint32_t); + } +} + +TEST(Coding, Fixed64) { + std::string s; + for (int power = 0; power <= 63; power++) { + uint64_t v = static_cast(1) << power; + PutFixed64(&s, v - 1); + PutFixed64(&s, v + 0); + PutFixed64(&s, v + 1); + } + + const char* p = s.data(); + for (int power = 0; power <= 63; power++) { + uint64_t v = static_cast(1) << power; + uint64_t actual; + actual = DecodeFixed64(p); + ASSERT_EQ(v-1, actual); + p += sizeof(uint64_t); + + actual = DecodeFixed64(p); + ASSERT_EQ(v+0, actual); + p += sizeof(uint64_t); + + actual = DecodeFixed64(p); + ASSERT_EQ(v+1, actual); + p += sizeof(uint64_t); + } +} + +// Test that encoding routines generate little-endian encodings +TEST(Coding, EncodingOutput) { + std::string dst; + PutFixed32(&dst, 0x04030201); + ASSERT_EQ(4, dst.size()); + ASSERT_EQ(0x01, static_cast(dst[0])); + ASSERT_EQ(0x02, static_cast(dst[1])); + ASSERT_EQ(0x03, static_cast(dst[2])); + ASSERT_EQ(0x04, static_cast(dst[3])); + + dst.clear(); + PutFixed64(&dst, 0x0807060504030201ull); + ASSERT_EQ(8, dst.size()); + ASSERT_EQ(0x01, static_cast(dst[0])); + ASSERT_EQ(0x02, static_cast(dst[1])); + ASSERT_EQ(0x03, static_cast(dst[2])); + ASSERT_EQ(0x04, static_cast(dst[3])); + ASSERT_EQ(0x05, static_cast(dst[4])); + ASSERT_EQ(0x06, static_cast(dst[5])); + ASSERT_EQ(0x07, static_cast(dst[6])); + ASSERT_EQ(0x08, static_cast(dst[7])); +} + +TEST(Coding, Varint32) { + std::string s; + for (uint32_t i = 0; i < (32 * 32); i++) { + uint32_t v = (i / 32) << (i % 32); + PutVarint32(&s, v); + } + + const char* p = s.data(); + const char* limit = p + s.size(); + for (uint32_t i = 0; i < (32 * 32); i++) { + uint32_t expected = (i / 32) << (i % 32); + uint32_t actual; + const char* start = p; + p = GetVarint32Ptr(p, limit, &actual); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(expected, actual); + ASSERT_EQ(VarintLength(actual), p - start); + } + ASSERT_EQ(p, s.data() + s.size()); +} + +TEST(Coding, Varint64) { + // Construct the list of values to check + std::vector values; + // Some special values + values.push_back(0); + values.push_back(100); + values.push_back(~static_cast(0)); + values.push_back(~static_cast(0) - 1); + for (uint32_t k = 0; k < 64; k++) { + // Test values near powers of two + const uint64_t power = 1ull << k; + values.push_back(power); + values.push_back(power-1); + values.push_back(power+1); + }; + + std::string s; + for (int i = 0; i < values.size(); i++) { + PutVarint64(&s, values[i]); + } + + const char* p = s.data(); + const char* limit = p + s.size(); + for (int i = 0; i < values.size(); i++) { + ASSERT_TRUE(p < limit); + uint64_t actual; + const char* start = p; + p = GetVarint64Ptr(p, limit, &actual); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(values[i], actual); + ASSERT_EQ(VarintLength(actual), p - start); + } + ASSERT_EQ(p, limit); + +} + +TEST(Coding, Varint32Overflow) { + uint32_t result; + std::string input("\x81\x82\x83\x84\x85\x11"); + ASSERT_TRUE(GetVarint32Ptr(input.data(), input.data() + input.size(), &result) + == NULL); +} + +TEST(Coding, Varint32Truncation) { + uint32_t large_value = (1u << 31) + 100; + std::string s; + PutVarint32(&s, large_value); + uint32_t result; + for (int len = 0; len < s.size() - 1; len++) { + ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + len, &result) == NULL); + } + ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + s.size(), &result) != NULL); + ASSERT_EQ(large_value, result); +} + +TEST(Coding, Varint64Overflow) { + uint64_t result; + std::string input("\x81\x82\x83\x84\x85\x81\x82\x83\x84\x85\x11"); + ASSERT_TRUE(GetVarint64Ptr(input.data(), input.data() + input.size(), &result) + == NULL); +} + +TEST(Coding, Varint64Truncation) { + uint64_t large_value = (1ull << 63) + 100ull; + std::string s; + PutVarint64(&s, large_value); + uint64_t result; + for (int len = 0; len < s.size() - 1; len++) { + ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + len, &result) == NULL); + } + ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + s.size(), &result) != NULL); + ASSERT_EQ(large_value, result); +} + +TEST(Coding, Strings) { + std::string s; + PutLengthPrefixedSlice(&s, Slice("")); + PutLengthPrefixedSlice(&s, Slice("foo")); + PutLengthPrefixedSlice(&s, Slice("bar")); + PutLengthPrefixedSlice(&s, Slice(std::string(200, 'x'))); + + Slice input(s); + Slice v; + ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v)); + ASSERT_EQ("", v.ToString()); + ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v)); + ASSERT_EQ("foo", v.ToString()); + ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v)); + ASSERT_EQ("bar", v.ToString()); + ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v)); + ASSERT_EQ(std::string(200, 'x'), v.ToString()); + ASSERT_EQ("", input.ToString()); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/comparator.cc b/src/leveldb/util/comparator.cc new file mode 100644 index 0000000..4b7b572 --- /dev/null +++ b/src/leveldb/util/comparator.cc @@ -0,0 +1,81 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include "leveldb/comparator.h" +#include "leveldb/slice.h" +#include "port/port.h" +#include "util/logging.h" + +namespace leveldb { + +Comparator::~Comparator() { } + +namespace { +class BytewiseComparatorImpl : public Comparator { + public: + BytewiseComparatorImpl() { } + + virtual const char* Name() const { + return "leveldb.BytewiseComparator"; + } + + virtual int Compare(const Slice& a, const Slice& b) const { + return a.compare(b); + } + + virtual void FindShortestSeparator( + std::string* start, + const Slice& limit) const { + // Find length of common prefix + size_t min_length = std::min(start->size(), limit.size()); + size_t diff_index = 0; + while ((diff_index < min_length) && + ((*start)[diff_index] == limit[diff_index])) { + diff_index++; + } + + if (diff_index >= min_length) { + // Do not shorten if one string is a prefix of the other + } else { + uint8_t diff_byte = static_cast((*start)[diff_index]); + if (diff_byte < static_cast(0xff) && + diff_byte + 1 < static_cast(limit[diff_index])) { + (*start)[diff_index]++; + start->resize(diff_index + 1); + assert(Compare(*start, limit) < 0); + } + } + } + + virtual void FindShortSuccessor(std::string* key) const { + // Find first character that can be incremented + size_t n = key->size(); + for (size_t i = 0; i < n; i++) { + const uint8_t byte = (*key)[i]; + if (byte != static_cast(0xff)) { + (*key)[i] = byte + 1; + key->resize(i+1); + return; + } + } + // *key is a run of 0xffs. Leave it alone. + } +}; +} // namespace + +static port::OnceType once = LEVELDB_ONCE_INIT; +static const Comparator* bytewise; + +static void InitModule() { + bytewise = new BytewiseComparatorImpl; +} + +const Comparator* BytewiseComparator() { + port::InitOnce(&once, InitModule); + return bytewise; +} + +} // namespace leveldb diff --git a/src/leveldb/util/crc32c.cc b/src/leveldb/util/crc32c.cc new file mode 100644 index 0000000..6db9e77 --- /dev/null +++ b/src/leveldb/util/crc32c.cc @@ -0,0 +1,332 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A portable implementation of crc32c, optimized to handle +// four bytes at a time. + +#include "util/crc32c.h" + +#include +#include "util/coding.h" + +namespace leveldb { +namespace crc32c { + +static const uint32_t table0_[256] = { + 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, + 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb, + 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, + 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, + 0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b, + 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384, + 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, + 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b, + 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, + 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, + 0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5, + 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa, + 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, + 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a, + 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, + 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, + 0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48, + 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957, + 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, + 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198, + 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, + 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, + 0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8, + 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7, + 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, + 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789, + 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, + 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, + 0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9, + 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6, + 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, + 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829, + 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, + 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, + 0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043, + 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c, + 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, + 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc, + 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, + 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, + 0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652, + 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d, + 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, + 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982, + 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, + 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, + 0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2, + 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed, + 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, + 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f, + 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, + 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, + 0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f, + 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540, + 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, + 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f, + 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, + 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, + 0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321, + 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e, + 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, + 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e, + 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, + 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351 +}; +static const uint32_t table1_[256] = { + 0x00000000, 0x13a29877, 0x274530ee, 0x34e7a899, + 0x4e8a61dc, 0x5d28f9ab, 0x69cf5132, 0x7a6dc945, + 0x9d14c3b8, 0x8eb65bcf, 0xba51f356, 0xa9f36b21, + 0xd39ea264, 0xc03c3a13, 0xf4db928a, 0xe7790afd, + 0x3fc5f181, 0x2c6769f6, 0x1880c16f, 0x0b225918, + 0x714f905d, 0x62ed082a, 0x560aa0b3, 0x45a838c4, + 0xa2d13239, 0xb173aa4e, 0x859402d7, 0x96369aa0, + 0xec5b53e5, 0xfff9cb92, 0xcb1e630b, 0xd8bcfb7c, + 0x7f8be302, 0x6c297b75, 0x58ced3ec, 0x4b6c4b9b, + 0x310182de, 0x22a31aa9, 0x1644b230, 0x05e62a47, + 0xe29f20ba, 0xf13db8cd, 0xc5da1054, 0xd6788823, + 0xac154166, 0xbfb7d911, 0x8b507188, 0x98f2e9ff, + 0x404e1283, 0x53ec8af4, 0x670b226d, 0x74a9ba1a, + 0x0ec4735f, 0x1d66eb28, 0x298143b1, 0x3a23dbc6, + 0xdd5ad13b, 0xcef8494c, 0xfa1fe1d5, 0xe9bd79a2, + 0x93d0b0e7, 0x80722890, 0xb4958009, 0xa737187e, + 0xff17c604, 0xecb55e73, 0xd852f6ea, 0xcbf06e9d, + 0xb19da7d8, 0xa23f3faf, 0x96d89736, 0x857a0f41, + 0x620305bc, 0x71a19dcb, 0x45463552, 0x56e4ad25, + 0x2c896460, 0x3f2bfc17, 0x0bcc548e, 0x186eccf9, + 0xc0d23785, 0xd370aff2, 0xe797076b, 0xf4359f1c, + 0x8e585659, 0x9dface2e, 0xa91d66b7, 0xbabffec0, + 0x5dc6f43d, 0x4e646c4a, 0x7a83c4d3, 0x69215ca4, + 0x134c95e1, 0x00ee0d96, 0x3409a50f, 0x27ab3d78, + 0x809c2506, 0x933ebd71, 0xa7d915e8, 0xb47b8d9f, + 0xce1644da, 0xddb4dcad, 0xe9537434, 0xfaf1ec43, + 0x1d88e6be, 0x0e2a7ec9, 0x3acdd650, 0x296f4e27, + 0x53028762, 0x40a01f15, 0x7447b78c, 0x67e52ffb, + 0xbf59d487, 0xacfb4cf0, 0x981ce469, 0x8bbe7c1e, + 0xf1d3b55b, 0xe2712d2c, 0xd69685b5, 0xc5341dc2, + 0x224d173f, 0x31ef8f48, 0x050827d1, 0x16aabfa6, + 0x6cc776e3, 0x7f65ee94, 0x4b82460d, 0x5820de7a, + 0xfbc3faf9, 0xe861628e, 0xdc86ca17, 0xcf245260, + 0xb5499b25, 0xa6eb0352, 0x920cabcb, 0x81ae33bc, + 0x66d73941, 0x7575a136, 0x419209af, 0x523091d8, + 0x285d589d, 0x3bffc0ea, 0x0f186873, 0x1cbaf004, + 0xc4060b78, 0xd7a4930f, 0xe3433b96, 0xf0e1a3e1, + 0x8a8c6aa4, 0x992ef2d3, 0xadc95a4a, 0xbe6bc23d, + 0x5912c8c0, 0x4ab050b7, 0x7e57f82e, 0x6df56059, + 0x1798a91c, 0x043a316b, 0x30dd99f2, 0x237f0185, + 0x844819fb, 0x97ea818c, 0xa30d2915, 0xb0afb162, + 0xcac27827, 0xd960e050, 0xed8748c9, 0xfe25d0be, + 0x195cda43, 0x0afe4234, 0x3e19eaad, 0x2dbb72da, + 0x57d6bb9f, 0x447423e8, 0x70938b71, 0x63311306, + 0xbb8de87a, 0xa82f700d, 0x9cc8d894, 0x8f6a40e3, + 0xf50789a6, 0xe6a511d1, 0xd242b948, 0xc1e0213f, + 0x26992bc2, 0x353bb3b5, 0x01dc1b2c, 0x127e835b, + 0x68134a1e, 0x7bb1d269, 0x4f567af0, 0x5cf4e287, + 0x04d43cfd, 0x1776a48a, 0x23910c13, 0x30339464, + 0x4a5e5d21, 0x59fcc556, 0x6d1b6dcf, 0x7eb9f5b8, + 0x99c0ff45, 0x8a626732, 0xbe85cfab, 0xad2757dc, + 0xd74a9e99, 0xc4e806ee, 0xf00fae77, 0xe3ad3600, + 0x3b11cd7c, 0x28b3550b, 0x1c54fd92, 0x0ff665e5, + 0x759baca0, 0x663934d7, 0x52de9c4e, 0x417c0439, + 0xa6050ec4, 0xb5a796b3, 0x81403e2a, 0x92e2a65d, + 0xe88f6f18, 0xfb2df76f, 0xcfca5ff6, 0xdc68c781, + 0x7b5fdfff, 0x68fd4788, 0x5c1aef11, 0x4fb87766, + 0x35d5be23, 0x26772654, 0x12908ecd, 0x013216ba, + 0xe64b1c47, 0xf5e98430, 0xc10e2ca9, 0xd2acb4de, + 0xa8c17d9b, 0xbb63e5ec, 0x8f844d75, 0x9c26d502, + 0x449a2e7e, 0x5738b609, 0x63df1e90, 0x707d86e7, + 0x0a104fa2, 0x19b2d7d5, 0x2d557f4c, 0x3ef7e73b, + 0xd98eedc6, 0xca2c75b1, 0xfecbdd28, 0xed69455f, + 0x97048c1a, 0x84a6146d, 0xb041bcf4, 0xa3e32483 +}; +static const uint32_t table2_[256] = { + 0x00000000, 0xa541927e, 0x4f6f520d, 0xea2ec073, + 0x9edea41a, 0x3b9f3664, 0xd1b1f617, 0x74f06469, + 0x38513ec5, 0x9d10acbb, 0x773e6cc8, 0xd27ffeb6, + 0xa68f9adf, 0x03ce08a1, 0xe9e0c8d2, 0x4ca15aac, + 0x70a27d8a, 0xd5e3eff4, 0x3fcd2f87, 0x9a8cbdf9, + 0xee7cd990, 0x4b3d4bee, 0xa1138b9d, 0x045219e3, + 0x48f3434f, 0xedb2d131, 0x079c1142, 0xa2dd833c, + 0xd62de755, 0x736c752b, 0x9942b558, 0x3c032726, + 0xe144fb14, 0x4405696a, 0xae2ba919, 0x0b6a3b67, + 0x7f9a5f0e, 0xdadbcd70, 0x30f50d03, 0x95b49f7d, + 0xd915c5d1, 0x7c5457af, 0x967a97dc, 0x333b05a2, + 0x47cb61cb, 0xe28af3b5, 0x08a433c6, 0xade5a1b8, + 0x91e6869e, 0x34a714e0, 0xde89d493, 0x7bc846ed, + 0x0f382284, 0xaa79b0fa, 0x40577089, 0xe516e2f7, + 0xa9b7b85b, 0x0cf62a25, 0xe6d8ea56, 0x43997828, + 0x37691c41, 0x92288e3f, 0x78064e4c, 0xdd47dc32, + 0xc76580d9, 0x622412a7, 0x880ad2d4, 0x2d4b40aa, + 0x59bb24c3, 0xfcfab6bd, 0x16d476ce, 0xb395e4b0, + 0xff34be1c, 0x5a752c62, 0xb05bec11, 0x151a7e6f, + 0x61ea1a06, 0xc4ab8878, 0x2e85480b, 0x8bc4da75, + 0xb7c7fd53, 0x12866f2d, 0xf8a8af5e, 0x5de93d20, + 0x29195949, 0x8c58cb37, 0x66760b44, 0xc337993a, + 0x8f96c396, 0x2ad751e8, 0xc0f9919b, 0x65b803e5, + 0x1148678c, 0xb409f5f2, 0x5e273581, 0xfb66a7ff, + 0x26217bcd, 0x8360e9b3, 0x694e29c0, 0xcc0fbbbe, + 0xb8ffdfd7, 0x1dbe4da9, 0xf7908dda, 0x52d11fa4, + 0x1e704508, 0xbb31d776, 0x511f1705, 0xf45e857b, + 0x80aee112, 0x25ef736c, 0xcfc1b31f, 0x6a802161, + 0x56830647, 0xf3c29439, 0x19ec544a, 0xbcadc634, + 0xc85da25d, 0x6d1c3023, 0x8732f050, 0x2273622e, + 0x6ed23882, 0xcb93aafc, 0x21bd6a8f, 0x84fcf8f1, + 0xf00c9c98, 0x554d0ee6, 0xbf63ce95, 0x1a225ceb, + 0x8b277743, 0x2e66e53d, 0xc448254e, 0x6109b730, + 0x15f9d359, 0xb0b84127, 0x5a968154, 0xffd7132a, + 0xb3764986, 0x1637dbf8, 0xfc191b8b, 0x595889f5, + 0x2da8ed9c, 0x88e97fe2, 0x62c7bf91, 0xc7862def, + 0xfb850ac9, 0x5ec498b7, 0xb4ea58c4, 0x11abcaba, + 0x655baed3, 0xc01a3cad, 0x2a34fcde, 0x8f756ea0, + 0xc3d4340c, 0x6695a672, 0x8cbb6601, 0x29faf47f, + 0x5d0a9016, 0xf84b0268, 0x1265c21b, 0xb7245065, + 0x6a638c57, 0xcf221e29, 0x250cde5a, 0x804d4c24, + 0xf4bd284d, 0x51fcba33, 0xbbd27a40, 0x1e93e83e, + 0x5232b292, 0xf77320ec, 0x1d5de09f, 0xb81c72e1, + 0xccec1688, 0x69ad84f6, 0x83834485, 0x26c2d6fb, + 0x1ac1f1dd, 0xbf8063a3, 0x55aea3d0, 0xf0ef31ae, + 0x841f55c7, 0x215ec7b9, 0xcb7007ca, 0x6e3195b4, + 0x2290cf18, 0x87d15d66, 0x6dff9d15, 0xc8be0f6b, + 0xbc4e6b02, 0x190ff97c, 0xf321390f, 0x5660ab71, + 0x4c42f79a, 0xe90365e4, 0x032da597, 0xa66c37e9, + 0xd29c5380, 0x77ddc1fe, 0x9df3018d, 0x38b293f3, + 0x7413c95f, 0xd1525b21, 0x3b7c9b52, 0x9e3d092c, + 0xeacd6d45, 0x4f8cff3b, 0xa5a23f48, 0x00e3ad36, + 0x3ce08a10, 0x99a1186e, 0x738fd81d, 0xd6ce4a63, + 0xa23e2e0a, 0x077fbc74, 0xed517c07, 0x4810ee79, + 0x04b1b4d5, 0xa1f026ab, 0x4bdee6d8, 0xee9f74a6, + 0x9a6f10cf, 0x3f2e82b1, 0xd50042c2, 0x7041d0bc, + 0xad060c8e, 0x08479ef0, 0xe2695e83, 0x4728ccfd, + 0x33d8a894, 0x96993aea, 0x7cb7fa99, 0xd9f668e7, + 0x9557324b, 0x3016a035, 0xda386046, 0x7f79f238, + 0x0b899651, 0xaec8042f, 0x44e6c45c, 0xe1a75622, + 0xdda47104, 0x78e5e37a, 0x92cb2309, 0x378ab177, + 0x437ad51e, 0xe63b4760, 0x0c158713, 0xa954156d, + 0xe5f54fc1, 0x40b4ddbf, 0xaa9a1dcc, 0x0fdb8fb2, + 0x7b2bebdb, 0xde6a79a5, 0x3444b9d6, 0x91052ba8 +}; +static const uint32_t table3_[256] = { + 0x00000000, 0xdd45aab8, 0xbf672381, 0x62228939, + 0x7b2231f3, 0xa6679b4b, 0xc4451272, 0x1900b8ca, + 0xf64463e6, 0x2b01c95e, 0x49234067, 0x9466eadf, + 0x8d665215, 0x5023f8ad, 0x32017194, 0xef44db2c, + 0xe964b13d, 0x34211b85, 0x560392bc, 0x8b463804, + 0x924680ce, 0x4f032a76, 0x2d21a34f, 0xf06409f7, + 0x1f20d2db, 0xc2657863, 0xa047f15a, 0x7d025be2, + 0x6402e328, 0xb9474990, 0xdb65c0a9, 0x06206a11, + 0xd725148b, 0x0a60be33, 0x6842370a, 0xb5079db2, + 0xac072578, 0x71428fc0, 0x136006f9, 0xce25ac41, + 0x2161776d, 0xfc24ddd5, 0x9e0654ec, 0x4343fe54, + 0x5a43469e, 0x8706ec26, 0xe524651f, 0x3861cfa7, + 0x3e41a5b6, 0xe3040f0e, 0x81268637, 0x5c632c8f, + 0x45639445, 0x98263efd, 0xfa04b7c4, 0x27411d7c, + 0xc805c650, 0x15406ce8, 0x7762e5d1, 0xaa274f69, + 0xb327f7a3, 0x6e625d1b, 0x0c40d422, 0xd1057e9a, + 0xaba65fe7, 0x76e3f55f, 0x14c17c66, 0xc984d6de, + 0xd0846e14, 0x0dc1c4ac, 0x6fe34d95, 0xb2a6e72d, + 0x5de23c01, 0x80a796b9, 0xe2851f80, 0x3fc0b538, + 0x26c00df2, 0xfb85a74a, 0x99a72e73, 0x44e284cb, + 0x42c2eeda, 0x9f874462, 0xfda5cd5b, 0x20e067e3, + 0x39e0df29, 0xe4a57591, 0x8687fca8, 0x5bc25610, + 0xb4868d3c, 0x69c32784, 0x0be1aebd, 0xd6a40405, + 0xcfa4bccf, 0x12e11677, 0x70c39f4e, 0xad8635f6, + 0x7c834b6c, 0xa1c6e1d4, 0xc3e468ed, 0x1ea1c255, + 0x07a17a9f, 0xdae4d027, 0xb8c6591e, 0x6583f3a6, + 0x8ac7288a, 0x57828232, 0x35a00b0b, 0xe8e5a1b3, + 0xf1e51979, 0x2ca0b3c1, 0x4e823af8, 0x93c79040, + 0x95e7fa51, 0x48a250e9, 0x2a80d9d0, 0xf7c57368, + 0xeec5cba2, 0x3380611a, 0x51a2e823, 0x8ce7429b, + 0x63a399b7, 0xbee6330f, 0xdcc4ba36, 0x0181108e, + 0x1881a844, 0xc5c402fc, 0xa7e68bc5, 0x7aa3217d, + 0x52a0c93f, 0x8fe56387, 0xedc7eabe, 0x30824006, + 0x2982f8cc, 0xf4c75274, 0x96e5db4d, 0x4ba071f5, + 0xa4e4aad9, 0x79a10061, 0x1b838958, 0xc6c623e0, + 0xdfc69b2a, 0x02833192, 0x60a1b8ab, 0xbde41213, + 0xbbc47802, 0x6681d2ba, 0x04a35b83, 0xd9e6f13b, + 0xc0e649f1, 0x1da3e349, 0x7f816a70, 0xa2c4c0c8, + 0x4d801be4, 0x90c5b15c, 0xf2e73865, 0x2fa292dd, + 0x36a22a17, 0xebe780af, 0x89c50996, 0x5480a32e, + 0x8585ddb4, 0x58c0770c, 0x3ae2fe35, 0xe7a7548d, + 0xfea7ec47, 0x23e246ff, 0x41c0cfc6, 0x9c85657e, + 0x73c1be52, 0xae8414ea, 0xcca69dd3, 0x11e3376b, + 0x08e38fa1, 0xd5a62519, 0xb784ac20, 0x6ac10698, + 0x6ce16c89, 0xb1a4c631, 0xd3864f08, 0x0ec3e5b0, + 0x17c35d7a, 0xca86f7c2, 0xa8a47efb, 0x75e1d443, + 0x9aa50f6f, 0x47e0a5d7, 0x25c22cee, 0xf8878656, + 0xe1873e9c, 0x3cc29424, 0x5ee01d1d, 0x83a5b7a5, + 0xf90696d8, 0x24433c60, 0x4661b559, 0x9b241fe1, + 0x8224a72b, 0x5f610d93, 0x3d4384aa, 0xe0062e12, + 0x0f42f53e, 0xd2075f86, 0xb025d6bf, 0x6d607c07, + 0x7460c4cd, 0xa9256e75, 0xcb07e74c, 0x16424df4, + 0x106227e5, 0xcd278d5d, 0xaf050464, 0x7240aedc, + 0x6b401616, 0xb605bcae, 0xd4273597, 0x09629f2f, + 0xe6264403, 0x3b63eebb, 0x59416782, 0x8404cd3a, + 0x9d0475f0, 0x4041df48, 0x22635671, 0xff26fcc9, + 0x2e238253, 0xf36628eb, 0x9144a1d2, 0x4c010b6a, + 0x5501b3a0, 0x88441918, 0xea669021, 0x37233a99, + 0xd867e1b5, 0x05224b0d, 0x6700c234, 0xba45688c, + 0xa345d046, 0x7e007afe, 0x1c22f3c7, 0xc167597f, + 0xc747336e, 0x1a0299d6, 0x782010ef, 0xa565ba57, + 0xbc65029d, 0x6120a825, 0x0302211c, 0xde478ba4, + 0x31035088, 0xec46fa30, 0x8e647309, 0x5321d9b1, + 0x4a21617b, 0x9764cbc3, 0xf54642fa, 0x2803e842 +}; + +// Used to fetch a naturally-aligned 32-bit word in little endian byte-order +static inline uint32_t LE_LOAD32(const uint8_t *p) { + return DecodeFixed32(reinterpret_cast(p)); +} + +uint32_t Extend(uint32_t crc, const char* buf, size_t size) { + const uint8_t *p = reinterpret_cast(buf); + const uint8_t *e = p + size; + uint32_t l = crc ^ 0xffffffffu; + +#define STEP1 do { \ + int c = (l & 0xff) ^ *p++; \ + l = table0_[c] ^ (l >> 8); \ +} while (0) +#define STEP4 do { \ + uint32_t c = l ^ LE_LOAD32(p); \ + p += 4; \ + l = table3_[c & 0xff] ^ \ + table2_[(c >> 8) & 0xff] ^ \ + table1_[(c >> 16) & 0xff] ^ \ + table0_[c >> 24]; \ +} while (0) + + // Point x at first 4-byte aligned byte in string. This might be + // just past the end of the string. + const uintptr_t pval = reinterpret_cast(p); + const uint8_t* x = reinterpret_cast(((pval + 3) >> 2) << 2); + if (x <= e) { + // Process bytes until finished or p is 4-byte aligned + while (p != x) { + STEP1; + } + } + // Process bytes 16 at a time + while ((e-p) >= 16) { + STEP4; STEP4; STEP4; STEP4; + } + // Process bytes 4 at a time + while ((e-p) >= 4) { + STEP4; + } + // Process the last few bytes + while (p != e) { + STEP1; + } +#undef STEP4 +#undef STEP1 + return l ^ 0xffffffffu; +} + +} // namespace crc32c +} // namespace leveldb diff --git a/src/leveldb/util/crc32c.h b/src/leveldb/util/crc32c.h new file mode 100644 index 0000000..1d7e5c0 --- /dev/null +++ b/src/leveldb/util/crc32c.h @@ -0,0 +1,45 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_CRC32C_H_ +#define STORAGE_LEVELDB_UTIL_CRC32C_H_ + +#include +#include + +namespace leveldb { +namespace crc32c { + +// Return the crc32c of concat(A, data[0,n-1]) where init_crc is the +// crc32c of some string A. Extend() is often used to maintain the +// crc32c of a stream of data. +extern uint32_t Extend(uint32_t init_crc, const char* data, size_t n); + +// Return the crc32c of data[0,n-1] +inline uint32_t Value(const char* data, size_t n) { + return Extend(0, data, n); +} + +static const uint32_t kMaskDelta = 0xa282ead8ul; + +// Return a masked representation of crc. +// +// Motivation: it is problematic to compute the CRC of a string that +// contains embedded CRCs. Therefore we recommend that CRCs stored +// somewhere (e.g., in files) should be masked before being stored. +inline uint32_t Mask(uint32_t crc) { + // Rotate right by 15 bits and add a constant. + return ((crc >> 15) | (crc << 17)) + kMaskDelta; +} + +// Return the crc whose masked representation is masked_crc. +inline uint32_t Unmask(uint32_t masked_crc) { + uint32_t rot = masked_crc - kMaskDelta; + return ((rot >> 17) | (rot << 15)); +} + +} // namespace crc32c +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_CRC32C_H_ diff --git a/src/leveldb/util/crc32c_test.cc b/src/leveldb/util/crc32c_test.cc new file mode 100644 index 0000000..4b957ee --- /dev/null +++ b/src/leveldb/util/crc32c_test.cc @@ -0,0 +1,72 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/crc32c.h" +#include "util/testharness.h" + +namespace leveldb { +namespace crc32c { + +class CRC { }; + +TEST(CRC, StandardResults) { + // From rfc3720 section B.4. + char buf[32]; + + memset(buf, 0, sizeof(buf)); + ASSERT_EQ(0x8a9136aa, Value(buf, sizeof(buf))); + + memset(buf, 0xff, sizeof(buf)); + ASSERT_EQ(0x62a8ab43, Value(buf, sizeof(buf))); + + for (int i = 0; i < 32; i++) { + buf[i] = i; + } + ASSERT_EQ(0x46dd794e, Value(buf, sizeof(buf))); + + for (int i = 0; i < 32; i++) { + buf[i] = 31 - i; + } + ASSERT_EQ(0x113fdb5c, Value(buf, sizeof(buf))); + + unsigned char data[48] = { + 0x01, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x18, + 0x28, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + ASSERT_EQ(0xd9963a56, Value(reinterpret_cast(data), sizeof(data))); +} + +TEST(CRC, Values) { + ASSERT_NE(Value("a", 1), Value("foo", 3)); +} + +TEST(CRC, Extend) { + ASSERT_EQ(Value("hello world", 11), + Extend(Value("hello ", 6), "world", 5)); +} + +TEST(CRC, Mask) { + uint32_t crc = Value("foo", 3); + ASSERT_NE(crc, Mask(crc)); + ASSERT_NE(crc, Mask(Mask(crc))); + ASSERT_EQ(crc, Unmask(Mask(crc))); + ASSERT_EQ(crc, Unmask(Unmask(Mask(Mask(crc))))); +} + +} // namespace crc32c +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/env.cc b/src/leveldb/util/env.cc new file mode 100644 index 0000000..c2600e9 --- /dev/null +++ b/src/leveldb/util/env.cc @@ -0,0 +1,96 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/env.h" + +namespace leveldb { + +Env::~Env() { +} + +SequentialFile::~SequentialFile() { +} + +RandomAccessFile::~RandomAccessFile() { +} + +WritableFile::~WritableFile() { +} + +Logger::~Logger() { +} + +FileLock::~FileLock() { +} + +void Log(Logger* info_log, const char* format, ...) { + if (info_log != NULL) { + va_list ap; + va_start(ap, format); + info_log->Logv(format, ap); + va_end(ap); + } +} + +static Status DoWriteStringToFile(Env* env, const Slice& data, + const std::string& fname, + bool should_sync) { + WritableFile* file; + Status s = env->NewWritableFile(fname, &file); + if (!s.ok()) { + return s; + } + s = file->Append(data); + if (s.ok() && should_sync) { + s = file->Sync(); + } + if (s.ok()) { + s = file->Close(); + } + delete file; // Will auto-close if we did not close above + if (!s.ok()) { + env->DeleteFile(fname); + } + return s; +} + +Status WriteStringToFile(Env* env, const Slice& data, + const std::string& fname) { + return DoWriteStringToFile(env, data, fname, false); +} + +Status WriteStringToFileSync(Env* env, const Slice& data, + const std::string& fname) { + return DoWriteStringToFile(env, data, fname, true); +} + +Status ReadFileToString(Env* env, const std::string& fname, std::string* data) { + data->clear(); + SequentialFile* file; + Status s = env->NewSequentialFile(fname, &file); + if (!s.ok()) { + return s; + } + static const int kBufferSize = 8192; + char* space = new char[kBufferSize]; + while (true) { + Slice fragment; + s = file->Read(kBufferSize, &fragment, space); + if (!s.ok()) { + break; + } + data->append(fragment.data(), fragment.size()); + if (fragment.empty()) { + break; + } + } + delete[] space; + delete file; + return s; +} + +EnvWrapper::~EnvWrapper() { +} + +} // namespace leveldb diff --git a/src/leveldb/util/env_posix.cc b/src/leveldb/util/env_posix.cc new file mode 100644 index 0000000..db81f56 --- /dev/null +++ b/src/leveldb/util/env_posix.cc @@ -0,0 +1,701 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +#if !defined(LEVELDB_PLATFORM_WINDOWS) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(LEVELDB_PLATFORM_ANDROID) +#include +#endif +#include "leveldb/env.h" +#include "leveldb/slice.h" +#include "port/port.h" +#include "util/logging.h" +#include "util/mutexlock.h" +#include "util/posix_logger.h" + +namespace leveldb { + +namespace { + +static Status IOError(const std::string& context, int err_number) { + return Status::IOError(context, strerror(err_number)); +} + +class PosixSequentialFile: public SequentialFile { + private: + std::string filename_; + FILE* file_; + + public: + PosixSequentialFile(const std::string& fname, FILE* f) + : filename_(fname), file_(f) { } + virtual ~PosixSequentialFile() { fclose(file_); } + + virtual Status Read(size_t n, Slice* result, char* scratch) { + Status s; + size_t r = fread_unlocked(scratch, 1, n, file_); + *result = Slice(scratch, r); + if (r < n) { + if (feof(file_)) { + // We leave status as ok if we hit the end of the file + } else { + // A partial read with an error: return a non-ok status + s = IOError(filename_, errno); + } + } + return s; + } + + virtual Status Skip(uint64_t n) { + if (fseek(file_, n, SEEK_CUR)) { + return IOError(filename_, errno); + } + return Status::OK(); + } +}; + +// pread() based random-access +class PosixRandomAccessFile: public RandomAccessFile { + private: + std::string filename_; + int fd_; + + public: + PosixRandomAccessFile(const std::string& fname, int fd) + : filename_(fname), fd_(fd) { } + virtual ~PosixRandomAccessFile() { close(fd_); } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + Status s; + ssize_t r = pread(fd_, scratch, n, static_cast(offset)); + *result = Slice(scratch, (r < 0) ? 0 : r); + if (r < 0) { + // An error: return a non-ok status + s = IOError(filename_, errno); + } + return s; + } +}; + +// Helper class to limit mmap file usage so that we do not end up +// running out virtual memory or running into kernel performance +// problems for very large databases. +class MmapLimiter { + public: + // Up to 1000 mmaps for 64-bit binaries; none for smaller pointer sizes. + MmapLimiter() { + SetAllowed(sizeof(void*) >= 8 ? 1000 : 0); + } + + // If another mmap slot is available, acquire it and return true. + // Else return false. + bool Acquire() { + if (GetAllowed() <= 0) { + return false; + } + MutexLock l(&mu_); + intptr_t x = GetAllowed(); + if (x <= 0) { + return false; + } else { + SetAllowed(x - 1); + return true; + } + } + + // Release a slot acquired by a previous call to Acquire() that returned true. + void Release() { + MutexLock l(&mu_); + SetAllowed(GetAllowed() + 1); + } + + private: + port::Mutex mu_; + port::AtomicPointer allowed_; + + intptr_t GetAllowed() const { + return reinterpret_cast(allowed_.Acquire_Load()); + } + + // REQUIRES: mu_ must be held + void SetAllowed(intptr_t v) { + allowed_.Release_Store(reinterpret_cast(v)); + } + + MmapLimiter(const MmapLimiter&); + void operator=(const MmapLimiter&); +}; + +// mmap() based random-access +class PosixMmapReadableFile: public RandomAccessFile { + private: + std::string filename_; + void* mmapped_region_; + size_t length_; + MmapLimiter* limiter_; + + public: + // base[0,length-1] contains the mmapped contents of the file. + PosixMmapReadableFile(const std::string& fname, void* base, size_t length, + MmapLimiter* limiter) + : filename_(fname), mmapped_region_(base), length_(length), + limiter_(limiter) { + } + + virtual ~PosixMmapReadableFile() { + munmap(mmapped_region_, length_); + limiter_->Release(); + } + + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + Status s; + if (offset + n > length_) { + *result = Slice(); + s = IOError(filename_, EINVAL); + } else { + *result = Slice(reinterpret_cast(mmapped_region_) + offset, n); + } + return s; + } +}; + +// We preallocate up to an extra megabyte and use memcpy to append new +// data to the file. This is safe since we either properly close the +// file before reading from it, or for log files, the reading code +// knows enough to skip zero suffixes. +class PosixMmapFile : public WritableFile { + private: + std::string filename_; + int fd_; + size_t page_size_; + size_t map_size_; // How much extra memory to map at a time + char* base_; // The mapped region + char* limit_; // Limit of the mapped region + char* dst_; // Where to write next (in range [base_,limit_]) + char* last_sync_; // Where have we synced up to + uint64_t file_offset_; // Offset of base_ in file + + // Have we done an munmap of unsynced data? + bool pending_sync_; + + // Roundup x to a multiple of y + static size_t Roundup(size_t x, size_t y) { + return ((x + y - 1) / y) * y; + } + + size_t TruncateToPageBoundary(size_t s) { + s -= (s & (page_size_ - 1)); + assert((s % page_size_) == 0); + return s; + } + + bool UnmapCurrentRegion() { + bool result = true; + if (base_ != NULL) { + if (last_sync_ < limit_) { + // Defer syncing this data until next Sync() call, if any + pending_sync_ = true; + } + if (munmap(base_, limit_ - base_) != 0) { + result = false; + } + file_offset_ += limit_ - base_; + base_ = NULL; + limit_ = NULL; + last_sync_ = NULL; + dst_ = NULL; + + // Increase the amount we map the next time, but capped at 1MB + if (map_size_ < (1<<20)) { + map_size_ *= 2; + } + } + return result; + } + + bool MapNewRegion() { + assert(base_ == NULL); + if (ftruncate(fd_, file_offset_ + map_size_) < 0) { + return false; + } + void* ptr = mmap(NULL, map_size_, PROT_READ | PROT_WRITE, MAP_SHARED, + fd_, file_offset_); + if (ptr == MAP_FAILED) { + return false; + } + base_ = reinterpret_cast(ptr); + limit_ = base_ + map_size_; + dst_ = base_; + last_sync_ = base_; + return true; + } + + public: + PosixMmapFile(const std::string& fname, int fd, size_t page_size) + : filename_(fname), + fd_(fd), + page_size_(page_size), + map_size_(Roundup(65536, page_size)), + base_(NULL), + limit_(NULL), + dst_(NULL), + last_sync_(NULL), + file_offset_(0), + pending_sync_(false) { + assert((page_size & (page_size - 1)) == 0); + } + + + ~PosixMmapFile() { + if (fd_ >= 0) { + PosixMmapFile::Close(); + } + } + + virtual Status Append(const Slice& data) { + const char* src = data.data(); + size_t left = data.size(); + while (left > 0) { + assert(base_ <= dst_); + assert(dst_ <= limit_); + size_t avail = limit_ - dst_; + if (avail == 0) { + if (!UnmapCurrentRegion() || + !MapNewRegion()) { + return IOError(filename_, errno); + } + } + + size_t n = (left <= avail) ? left : avail; + memcpy(dst_, src, n); + dst_ += n; + src += n; + left -= n; + } + return Status::OK(); + } + + virtual Status Close() { + Status s; + size_t unused = limit_ - dst_; + if (!UnmapCurrentRegion()) { + s = IOError(filename_, errno); + } else if (unused > 0) { + // Trim the extra space at the end of the file + if (ftruncate(fd_, file_offset_ - unused) < 0) { + s = IOError(filename_, errno); + } + } + + if (close(fd_) < 0) { + if (s.ok()) { + s = IOError(filename_, errno); + } + } + + fd_ = -1; + base_ = NULL; + limit_ = NULL; + return s; + } + + virtual Status Flush() { + return Status::OK(); + } + + virtual Status Sync() { + Status s; + + if (pending_sync_) { + // Some unmapped data was not synced + pending_sync_ = false; + if (fdatasync(fd_) < 0) { + s = IOError(filename_, errno); + } + } + + if (dst_ > last_sync_) { + // Find the beginnings of the pages that contain the first and last + // bytes to be synced. + size_t p1 = TruncateToPageBoundary(last_sync_ - base_); + size_t p2 = TruncateToPageBoundary(dst_ - base_ - 1); + last_sync_ = dst_; + if (msync(base_ + p1, p2 - p1 + page_size_, MS_SYNC) < 0) { + s = IOError(filename_, errno); + } + } + + return s; + } +}; + +static int LockOrUnlock(int fd, bool lock) { + errno = 0; + struct flock f; + memset(&f, 0, sizeof(f)); + f.l_type = (lock ? F_WRLCK : F_UNLCK); + f.l_whence = SEEK_SET; + f.l_start = 0; + f.l_len = 0; // Lock/unlock entire file + return fcntl(fd, F_SETLK, &f); +} + +class PosixFileLock : public FileLock { + public: + int fd_; + std::string name_; +}; + +// Set of locked files. We keep a separate set instead of just +// relying on fcntrl(F_SETLK) since fcntl(F_SETLK) does not provide +// any protection against multiple uses from the same process. +class PosixLockTable { + private: + port::Mutex mu_; + std::set locked_files_; + public: + bool Insert(const std::string& fname) { + MutexLock l(&mu_); + return locked_files_.insert(fname).second; + } + void Remove(const std::string& fname) { + MutexLock l(&mu_); + locked_files_.erase(fname); + } +}; + +class PosixEnv : public Env { + public: + PosixEnv(); + virtual ~PosixEnv() { + fprintf(stderr, "Destroying Env::Default()\n"); + exit(1); + } + + virtual Status NewSequentialFile(const std::string& fname, + SequentialFile** result) { + FILE* f = fopen(fname.c_str(), "r"); + if (f == NULL) { + *result = NULL; + return IOError(fname, errno); + } else { + *result = new PosixSequentialFile(fname, f); + return Status::OK(); + } + } + + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) { + *result = NULL; + Status s; + int fd = open(fname.c_str(), O_RDONLY); + if (fd < 0) { + s = IOError(fname, errno); + } else if (mmap_limit_.Acquire()) { + uint64_t size; + s = GetFileSize(fname, &size); + if (s.ok()) { + void* base = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (base != MAP_FAILED) { + *result = new PosixMmapReadableFile(fname, base, size, &mmap_limit_); + } else { + s = IOError(fname, errno); + } + } + close(fd); + if (!s.ok()) { + mmap_limit_.Release(); + } + } else { + *result = new PosixRandomAccessFile(fname, fd); + } + return s; + } + + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) { + Status s; + const int fd = open(fname.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0644); + if (fd < 0) { + *result = NULL; + s = IOError(fname, errno); + } else { + *result = new PosixMmapFile(fname, fd, page_size_); + } + return s; + } + + virtual bool FileExists(const std::string& fname) { + return access(fname.c_str(), F_OK) == 0; + } + + virtual Status GetChildren(const std::string& dir, + std::vector* result) { + result->clear(); + DIR* d = opendir(dir.c_str()); + if (d == NULL) { + return IOError(dir, errno); + } + struct dirent* entry; + while ((entry = readdir(d)) != NULL) { + result->push_back(entry->d_name); + } + closedir(d); + return Status::OK(); + } + + virtual Status DeleteFile(const std::string& fname) { + Status result; + if (unlink(fname.c_str()) != 0) { + result = IOError(fname, errno); + } + return result; + }; + + virtual Status CreateDir(const std::string& name) { + Status result; + if (mkdir(name.c_str(), 0755) != 0) { + result = IOError(name, errno); + } + return result; + }; + + virtual Status DeleteDir(const std::string& name) { + Status result; + if (rmdir(name.c_str()) != 0) { + result = IOError(name, errno); + } + return result; + }; + + virtual Status GetFileSize(const std::string& fname, uint64_t* size) { + Status s; + struct stat sbuf; + if (stat(fname.c_str(), &sbuf) != 0) { + *size = 0; + s = IOError(fname, errno); + } else { + *size = sbuf.st_size; + } + return s; + } + + virtual Status RenameFile(const std::string& src, const std::string& target) { + Status result; + if (rename(src.c_str(), target.c_str()) != 0) { + result = IOError(src, errno); + } + return result; + } + + virtual Status LockFile(const std::string& fname, FileLock** lock) { + *lock = NULL; + Status result; + int fd = open(fname.c_str(), O_RDWR | O_CREAT, 0644); + if (fd < 0) { + result = IOError(fname, errno); + } else if (!locks_.Insert(fname)) { + close(fd); + result = Status::IOError("lock " + fname, "already held by process"); + } else if (LockOrUnlock(fd, true) == -1) { + result = IOError("lock " + fname, errno); + close(fd); + locks_.Remove(fname); + } else { + PosixFileLock* my_lock = new PosixFileLock; + my_lock->fd_ = fd; + my_lock->name_ = fname; + *lock = my_lock; + } + return result; + } + + virtual Status UnlockFile(FileLock* lock) { + PosixFileLock* my_lock = reinterpret_cast(lock); + Status result; + if (LockOrUnlock(my_lock->fd_, false) == -1) { + result = IOError("unlock", errno); + } + locks_.Remove(my_lock->name_); + close(my_lock->fd_); + delete my_lock; + return result; + } + + virtual void Schedule(void (*function)(void*), void* arg); + + virtual void StartThread(void (*function)(void* arg), void* arg); + + virtual Status GetTestDirectory(std::string* result) { + const char* env = getenv("TEST_TMPDIR"); + if (env && env[0] != '\0') { + *result = env; + } else { + char buf[100]; + snprintf(buf, sizeof(buf), "/tmp/leveldbtest-%d", int(geteuid())); + *result = buf; + } + // Directory may already exist + CreateDir(*result); + return Status::OK(); + } + + static uint64_t gettid() { + pthread_t tid = pthread_self(); + uint64_t thread_id = 0; + memcpy(&thread_id, &tid, std::min(sizeof(thread_id), sizeof(tid))); + return thread_id; + } + + virtual Status NewLogger(const std::string& fname, Logger** result) { + FILE* f = fopen(fname.c_str(), "w"); + if (f == NULL) { + *result = NULL; + return IOError(fname, errno); + } else { + *result = new PosixLogger(f, &PosixEnv::gettid); + return Status::OK(); + } + } + + virtual uint64_t NowMicros() { + struct timeval tv; + gettimeofday(&tv, NULL); + return static_cast(tv.tv_sec) * 1000000 + tv.tv_usec; + } + + virtual void SleepForMicroseconds(int micros) { + usleep(micros); + } + + private: + void PthreadCall(const char* label, int result) { + if (result != 0) { + fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); + exit(1); + } + } + + // BGThread() is the body of the background thread + void BGThread(); + static void* BGThreadWrapper(void* arg) { + reinterpret_cast(arg)->BGThread(); + return NULL; + } + + size_t page_size_; + pthread_mutex_t mu_; + pthread_cond_t bgsignal_; + pthread_t bgthread_; + bool started_bgthread_; + + // Entry per Schedule() call + struct BGItem { void* arg; void (*function)(void*); }; + typedef std::deque BGQueue; + BGQueue queue_; + + PosixLockTable locks_; + MmapLimiter mmap_limit_; +}; + +PosixEnv::PosixEnv() : page_size_(getpagesize()), + started_bgthread_(false) { + PthreadCall("mutex_init", pthread_mutex_init(&mu_, NULL)); + PthreadCall("cvar_init", pthread_cond_init(&bgsignal_, NULL)); +} + +void PosixEnv::Schedule(void (*function)(void*), void* arg) { + PthreadCall("lock", pthread_mutex_lock(&mu_)); + + // Start background thread if necessary + if (!started_bgthread_) { + started_bgthread_ = true; + PthreadCall( + "create thread", + pthread_create(&bgthread_, NULL, &PosixEnv::BGThreadWrapper, this)); + } + + // If the queue is currently empty, the background thread may currently be + // waiting. + if (queue_.empty()) { + PthreadCall("signal", pthread_cond_signal(&bgsignal_)); + } + + // Add to priority queue + queue_.push_back(BGItem()); + queue_.back().function = function; + queue_.back().arg = arg; + + PthreadCall("unlock", pthread_mutex_unlock(&mu_)); +} + +void PosixEnv::BGThread() { + while (true) { + // Wait until there is an item that is ready to run + PthreadCall("lock", pthread_mutex_lock(&mu_)); + while (queue_.empty()) { + PthreadCall("wait", pthread_cond_wait(&bgsignal_, &mu_)); + } + + void (*function)(void*) = queue_.front().function; + void* arg = queue_.front().arg; + queue_.pop_front(); + + PthreadCall("unlock", pthread_mutex_unlock(&mu_)); + (*function)(arg); + } +} + +namespace { +struct StartThreadState { + void (*user_function)(void*); + void* arg; +}; +} +static void* StartThreadWrapper(void* arg) { + StartThreadState* state = reinterpret_cast(arg); + state->user_function(state->arg); + delete state; + return NULL; +} + +void PosixEnv::StartThread(void (*function)(void* arg), void* arg) { + pthread_t t; + StartThreadState* state = new StartThreadState; + state->user_function = function; + state->arg = arg; + PthreadCall("start thread", + pthread_create(&t, NULL, &StartThreadWrapper, state)); +} + +} // namespace + +static pthread_once_t once = PTHREAD_ONCE_INIT; +static Env* default_env; +static void InitDefaultEnv() { default_env = new PosixEnv; } + +Env* Env::Default() { + pthread_once(&once, InitDefaultEnv); + return default_env; +} + +} // namespace leveldb + +#endif diff --git a/src/leveldb/util/env_test.cc b/src/leveldb/util/env_test.cc new file mode 100644 index 0000000..b72cb44 --- /dev/null +++ b/src/leveldb/util/env_test.cc @@ -0,0 +1,104 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/env.h" + +#include "port/port.h" +#include "util/testharness.h" + +namespace leveldb { + +static const int kDelayMicros = 100000; + +class EnvPosixTest { + private: + port::Mutex mu_; + std::string events_; + + public: + Env* env_; + EnvPosixTest() : env_(Env::Default()) { } +}; + +static void SetBool(void* ptr) { + reinterpret_cast(ptr)->NoBarrier_Store(ptr); +} + +TEST(EnvPosixTest, RunImmediately) { + port::AtomicPointer called (NULL); + env_->Schedule(&SetBool, &called); + Env::Default()->SleepForMicroseconds(kDelayMicros); + ASSERT_TRUE(called.NoBarrier_Load() != NULL); +} + +TEST(EnvPosixTest, RunMany) { + port::AtomicPointer last_id (NULL); + + struct CB { + port::AtomicPointer* last_id_ptr; // Pointer to shared slot + uintptr_t id; // Order# for the execution of this callback + + CB(port::AtomicPointer* p, int i) : last_id_ptr(p), id(i) { } + + static void Run(void* v) { + CB* cb = reinterpret_cast(v); + void* cur = cb->last_id_ptr->NoBarrier_Load(); + ASSERT_EQ(cb->id-1, reinterpret_cast(cur)); + cb->last_id_ptr->Release_Store(reinterpret_cast(cb->id)); + } + }; + + // Schedule in different order than start time + CB cb1(&last_id, 1); + CB cb2(&last_id, 2); + CB cb3(&last_id, 3); + CB cb4(&last_id, 4); + env_->Schedule(&CB::Run, &cb1); + env_->Schedule(&CB::Run, &cb2); + env_->Schedule(&CB::Run, &cb3); + env_->Schedule(&CB::Run, &cb4); + + Env::Default()->SleepForMicroseconds(kDelayMicros); + void* cur = last_id.Acquire_Load(); + ASSERT_EQ(4, reinterpret_cast(cur)); +} + +struct State { + port::Mutex mu; + int val; + int num_running; +}; + +static void ThreadBody(void* arg) { + State* s = reinterpret_cast(arg); + s->mu.Lock(); + s->val += 1; + s->num_running -= 1; + s->mu.Unlock(); +} + +TEST(EnvPosixTest, StartThread) { + State state; + state.val = 0; + state.num_running = 3; + for (int i = 0; i < 3; i++) { + env_->StartThread(&ThreadBody, &state); + } + while (true) { + state.mu.Lock(); + int num = state.num_running; + state.mu.Unlock(); + if (num == 0) { + break; + } + Env::Default()->SleepForMicroseconds(kDelayMicros); + } + ASSERT_EQ(state.val, 3); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/env_win.cc b/src/leveldb/util/env_win.cc new file mode 100644 index 0000000..ef2ecae --- /dev/null +++ b/src/leveldb/util/env_win.cc @@ -0,0 +1,1031 @@ +// This file contains source that originates from: +// http://code.google.com/p/leveldbwin/source/browse/trunk/win32_impl_src/env_win32.h +// http://code.google.com/p/leveldbwin/source/browse/trunk/win32_impl_src/port_win32.cc +// Those files dont' have any explict license headers but the +// project (http://code.google.com/p/leveldbwin/) lists the 'New BSD License' +// as the license. +#if defined(LEVELDB_PLATFORM_WINDOWS) +#include + + +#include "leveldb/env.h" + +#include "port/port.h" +#include "leveldb/slice.h" +#include "util/logging.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef max +#undef max +#endif + +#ifndef va_copy +#define va_copy(d,s) ((d) = (s)) +#endif + +#if defined DeleteFile +#undef DeleteFile +#endif + +//Declarations +namespace leveldb +{ + +namespace Win32 +{ + +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +std::string GetCurrentDir(); +std::wstring GetCurrentDirW(); + +static const std::string CurrentDir = GetCurrentDir(); +static const std::wstring CurrentDirW = GetCurrentDirW(); + +std::string& ModifyPath(std::string& path); +std::wstring& ModifyPath(std::wstring& path); + +std::string GetLastErrSz(); +std::wstring GetLastErrSzW(); + +size_t GetPageSize(); + +typedef void (*ScheduleProc)(void*) ; + +struct WorkItemWrapper +{ + WorkItemWrapper(ScheduleProc proc_,void* content_); + ScheduleProc proc; + void* pContent; +}; + +DWORD WINAPI WorkItemWrapperProc(LPVOID pContent); + +class Win32SequentialFile : public SequentialFile +{ +public: + friend class Win32Env; + virtual ~Win32SequentialFile(); + virtual Status Read(size_t n, Slice* result, char* scratch); + virtual Status Skip(uint64_t n); + BOOL isEnable(); +private: + BOOL _Init(); + void _CleanUp(); + Win32SequentialFile(const std::string& fname); + std::string _filename; + ::HANDLE _hFile; + DISALLOW_COPY_AND_ASSIGN(Win32SequentialFile); +}; + +class Win32RandomAccessFile : public RandomAccessFile +{ +public: + friend class Win32Env; + virtual ~Win32RandomAccessFile(); + virtual Status Read(uint64_t offset, size_t n, Slice* result,char* scratch) const; + BOOL isEnable(); +private: + BOOL _Init(LPCWSTR path); + void _CleanUp(); + Win32RandomAccessFile(const std::string& fname); + HANDLE _hFile; + const std::string _filename; + DISALLOW_COPY_AND_ASSIGN(Win32RandomAccessFile); +}; + +class Win32MapFile : public WritableFile +{ +public: + Win32MapFile(const std::string& fname); + + ~Win32MapFile(); + virtual Status Append(const Slice& data); + virtual Status Close(); + virtual Status Flush(); + virtual Status Sync(); + BOOL isEnable(); +private: + std::string _filename; + HANDLE _hFile; + size_t _page_size; + size_t _map_size; // How much extra memory to map at a time + char* _base; // The mapped region + HANDLE _base_handle; + char* _limit; // Limit of the mapped region + char* _dst; // Where to write next (in range [base_,limit_]) + char* _last_sync; // Where have we synced up to + uint64_t _file_offset; // Offset of base_ in file + //LARGE_INTEGER file_offset_; + // Have we done an munmap of unsynced data? + bool _pending_sync; + + // Roundup x to a multiple of y + static size_t _Roundup(size_t x, size_t y); + size_t _TruncateToPageBoundary(size_t s); + bool _UnmapCurrentRegion(); + bool _MapNewRegion(); + DISALLOW_COPY_AND_ASSIGN(Win32MapFile); + BOOL _Init(LPCWSTR Path); +}; + +class Win32FileLock : public FileLock +{ +public: + friend class Win32Env; + virtual ~Win32FileLock(); + BOOL isEnable(); +private: + BOOL _Init(LPCWSTR path); + void _CleanUp(); + Win32FileLock(const std::string& fname); + HANDLE _hFile; + std::string _filename; + DISALLOW_COPY_AND_ASSIGN(Win32FileLock); +}; + +class Win32Logger : public Logger +{ +public: + friend class Win32Env; + virtual ~Win32Logger(); + virtual void Logv(const char* format, va_list ap); +private: + explicit Win32Logger(WritableFile* pFile); + WritableFile* _pFileProxy; + DISALLOW_COPY_AND_ASSIGN(Win32Logger); +}; + +class Win32Env : public Env +{ +public: + Win32Env(); + virtual ~Win32Env(); + virtual Status NewSequentialFile(const std::string& fname, + SequentialFile** result); + + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result); + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result); + + virtual bool FileExists(const std::string& fname); + + virtual Status GetChildren(const std::string& dir, + std::vector* result); + + virtual Status DeleteFile(const std::string& fname); + + virtual Status CreateDir(const std::string& dirname); + + virtual Status DeleteDir(const std::string& dirname); + + virtual Status GetFileSize(const std::string& fname, uint64_t* file_size); + + virtual Status RenameFile(const std::string& src, + const std::string& target); + + virtual Status LockFile(const std::string& fname, FileLock** lock); + + virtual Status UnlockFile(FileLock* lock); + + virtual void Schedule( + void (*function)(void* arg), + void* arg); + + virtual void StartThread(void (*function)(void* arg), void* arg); + + virtual Status GetTestDirectory(std::string* path); + + //virtual void Logv(WritableFile* log, const char* format, va_list ap); + + virtual Status NewLogger(const std::string& fname, Logger** result); + + virtual uint64_t NowMicros(); + + virtual void SleepForMicroseconds(int micros); +}; + +void ToWidePath(const std::string& value, std::wstring& target) { + wchar_t buffer[MAX_PATH]; + MultiByteToWideChar(CP_ACP, 0, value.c_str(), -1, buffer, MAX_PATH); + target = buffer; +} + +void ToNarrowPath(const std::wstring& value, std::string& target) { + char buffer[MAX_PATH]; + WideCharToMultiByte(CP_ACP, 0, value.c_str(), -1, buffer, MAX_PATH, NULL, NULL); + target = buffer; +} + +std::string GetCurrentDir() +{ + CHAR path[MAX_PATH]; + ::GetModuleFileNameA(::GetModuleHandleA(NULL),path,MAX_PATH); + *strrchr(path,'\\') = 0; + return std::string(path); +} + +std::wstring GetCurrentDirW() +{ + WCHAR path[MAX_PATH]; + ::GetModuleFileNameW(::GetModuleHandleW(NULL),path,MAX_PATH); + *wcsrchr(path,L'\\') = 0; + return std::wstring(path); +} + +std::string& ModifyPath(std::string& path) +{ + if(path[0] == '/' || path[0] == '\\'){ + path = CurrentDir + path; + } + std::replace(path.begin(),path.end(),'/','\\'); + + return path; +} + +std::wstring& ModifyPath(std::wstring& path) +{ + if(path[0] == L'/' || path[0] == L'\\'){ + path = CurrentDirW + path; + } + std::replace(path.begin(),path.end(),L'/',L'\\'); + return path; +} + +std::string GetLastErrSz() +{ + LPWSTR lpMsgBuf; + FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + 0, // Default language + (LPWSTR) &lpMsgBuf, + 0, + NULL + ); + std::string Err; + ToNarrowPath(lpMsgBuf, Err); + LocalFree( lpMsgBuf ); + return Err; +} + +std::wstring GetLastErrSzW() +{ + LPVOID lpMsgBuf; + FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + 0, // Default language + (LPWSTR) &lpMsgBuf, + 0, + NULL + ); + std::wstring Err = (LPCWSTR)lpMsgBuf; + LocalFree(lpMsgBuf); + return Err; +} + +WorkItemWrapper::WorkItemWrapper( ScheduleProc proc_,void* content_ ) : + proc(proc_),pContent(content_) +{ + +} + +DWORD WINAPI WorkItemWrapperProc(LPVOID pContent) +{ + WorkItemWrapper* item = static_cast(pContent); + ScheduleProc TempProc = item->proc; + void* arg = item->pContent; + delete item; + TempProc(arg); + return 0; +} + +size_t GetPageSize() +{ + SYSTEM_INFO si; + GetSystemInfo(&si); + return std::max(si.dwPageSize,si.dwAllocationGranularity); +} + +const size_t g_PageSize = GetPageSize(); + + +Win32SequentialFile::Win32SequentialFile( const std::string& fname ) : + _filename(fname),_hFile(NULL) +{ + _Init(); +} + +Win32SequentialFile::~Win32SequentialFile() +{ + _CleanUp(); +} + +Status Win32SequentialFile::Read( size_t n, Slice* result, char* scratch ) +{ + Status sRet; + DWORD hasRead = 0; + if(_hFile && ReadFile(_hFile,scratch,n,&hasRead,NULL) ){ + *result = Slice(scratch,hasRead); + } else { + sRet = Status::IOError(_filename, Win32::GetLastErrSz() ); + } + return sRet; +} + +Status Win32SequentialFile::Skip( uint64_t n ) +{ + Status sRet; + LARGE_INTEGER Move,NowPointer; + Move.QuadPart = n; + if(!SetFilePointerEx(_hFile,Move,&NowPointer,FILE_CURRENT)){ + sRet = Status::IOError(_filename,Win32::GetLastErrSz()); + } + return sRet; +} + +BOOL Win32SequentialFile::isEnable() +{ + return _hFile ? TRUE : FALSE; +} + +BOOL Win32SequentialFile::_Init() +{ + std::wstring path; + ToWidePath(_filename, path); + _hFile = CreateFileW(path.c_str(), + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + return _hFile ? TRUE : FALSE; +} + +void Win32SequentialFile::_CleanUp() +{ + if(_hFile){ + CloseHandle(_hFile); + _hFile = NULL; + } +} + +Win32RandomAccessFile::Win32RandomAccessFile( const std::string& fname ) : + _filename(fname),_hFile(NULL) +{ + std::wstring path; + ToWidePath(fname, path); + _Init( path.c_str() ); +} + +Win32RandomAccessFile::~Win32RandomAccessFile() +{ + _CleanUp(); +} + +Status Win32RandomAccessFile::Read(uint64_t offset,size_t n,Slice* result,char* scratch) const +{ + Status sRet; + OVERLAPPED ol = {0}; + ZeroMemory(&ol,sizeof(ol)); + ol.Offset = (DWORD)offset; + ol.OffsetHigh = (DWORD)(offset >> 32); + DWORD hasRead = 0; + if(!ReadFile(_hFile,scratch,n,&hasRead,&ol)) + sRet = Status::IOError(_filename,Win32::GetLastErrSz()); + else + *result = Slice(scratch,hasRead); + return sRet; +} + +BOOL Win32RandomAccessFile::_Init( LPCWSTR path ) +{ + BOOL bRet = FALSE; + if(!_hFile) + _hFile = ::CreateFileW(path,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,NULL); + if(!_hFile || _hFile == INVALID_HANDLE_VALUE ) + _hFile = NULL; + else + bRet = TRUE; + return bRet; +} + +BOOL Win32RandomAccessFile::isEnable() +{ + return _hFile ? TRUE : FALSE; +} + +void Win32RandomAccessFile::_CleanUp() +{ + if(_hFile){ + ::CloseHandle(_hFile); + _hFile = NULL; + } +} + +size_t Win32MapFile::_Roundup( size_t x, size_t y ) +{ + return ((x + y - 1) / y) * y; +} + +size_t Win32MapFile::_TruncateToPageBoundary( size_t s ) +{ + s -= (s & (_page_size - 1)); + assert((s % _page_size) == 0); + return s; +} + +bool Win32MapFile::_UnmapCurrentRegion() +{ + bool result = true; + if (_base != NULL) { + if (_last_sync < _limit) { + // Defer syncing this data until next Sync() call, if any + _pending_sync = true; + } + if (!UnmapViewOfFile(_base) || !CloseHandle(_base_handle)) + result = false; + _file_offset += _limit - _base; + _base = NULL; + _base_handle = NULL; + _limit = NULL; + _last_sync = NULL; + _dst = NULL; + // Increase the amount we map the next time, but capped at 1MB + if (_map_size < (1<<20)) { + _map_size *= 2; + } + } + return result; +} + +bool Win32MapFile::_MapNewRegion() +{ + assert(_base == NULL); + //LONG newSizeHigh = (LONG)((file_offset_ + map_size_) >> 32); + //LONG newSizeLow = (LONG)((file_offset_ + map_size_) & 0xFFFFFFFF); + DWORD off_hi = (DWORD)(_file_offset >> 32); + DWORD off_lo = (DWORD)(_file_offset & 0xFFFFFFFF); + LARGE_INTEGER newSize; + newSize.QuadPart = _file_offset + _map_size; + SetFilePointerEx(_hFile, newSize, NULL, FILE_BEGIN); + SetEndOfFile(_hFile); + + _base_handle = CreateFileMappingA( + _hFile, + NULL, + PAGE_READWRITE, + 0, + 0, + 0); + if (_base_handle != NULL) { + _base = (char*) MapViewOfFile(_base_handle, + FILE_MAP_ALL_ACCESS, + off_hi, + off_lo, + _map_size); + if (_base != NULL) { + _limit = _base + _map_size; + _dst = _base; + _last_sync = _base; + return true; + } + } + return false; +} + +Win32MapFile::Win32MapFile( const std::string& fname) : + _filename(fname), + _hFile(NULL), + _page_size(Win32::g_PageSize), + _map_size(_Roundup(65536, Win32::g_PageSize)), + _base(NULL), + _base_handle(NULL), + _limit(NULL), + _dst(NULL), + _last_sync(NULL), + _file_offset(0), + _pending_sync(false) +{ + std::wstring path; + ToWidePath(fname, path); + _Init(path.c_str()); + assert((Win32::g_PageSize & (Win32::g_PageSize - 1)) == 0); +} + +Status Win32MapFile::Append( const Slice& data ) +{ + const char* src = data.data(); + size_t left = data.size(); + Status s; + while (left > 0) { + assert(_base <= _dst); + assert(_dst <= _limit); + size_t avail = _limit - _dst; + if (avail == 0) { + if (!_UnmapCurrentRegion() || + !_MapNewRegion()) { + return Status::IOError("WinMmapFile.Append::UnmapCurrentRegion or MapNewRegion: ", Win32::GetLastErrSz()); + } + } + size_t n = (left <= avail) ? left : avail; + memcpy(_dst, src, n); + _dst += n; + src += n; + left -= n; + } + return s; +} + +Status Win32MapFile::Close() +{ + Status s; + size_t unused = _limit - _dst; + if (!_UnmapCurrentRegion()) { + s = Status::IOError("WinMmapFile.Close::UnmapCurrentRegion: ",Win32::GetLastErrSz()); + } else if (unused > 0) { + // Trim the extra space at the end of the file + LARGE_INTEGER newSize; + newSize.QuadPart = _file_offset - unused; + if (!SetFilePointerEx(_hFile, newSize, NULL, FILE_BEGIN)) { + s = Status::IOError("WinMmapFile.Close::SetFilePointer: ",Win32::GetLastErrSz()); + } else + SetEndOfFile(_hFile); + } + if (!CloseHandle(_hFile)) { + if (s.ok()) { + s = Status::IOError("WinMmapFile.Close::CloseHandle: ", Win32::GetLastErrSz()); + } + } + _hFile = INVALID_HANDLE_VALUE; + _base = NULL; + _base_handle = NULL; + _limit = NULL; + + return s; +} + +Status Win32MapFile::Sync() +{ + Status s; + if (_pending_sync) { + // Some unmapped data was not synced + _pending_sync = false; + if (!FlushFileBuffers(_hFile)) { + s = Status::IOError("WinMmapFile.Sync::FlushFileBuffers: ",Win32::GetLastErrSz()); + } + } + if (_dst > _last_sync) { + // Find the beginnings of the pages that contain the first and last + // bytes to be synced. + size_t p1 = _TruncateToPageBoundary(_last_sync - _base); + size_t p2 = _TruncateToPageBoundary(_dst - _base - 1); + _last_sync = _dst; + if (!FlushViewOfFile(_base + p1, p2 - p1 + _page_size)) { + s = Status::IOError("WinMmapFile.Sync::FlushViewOfFile: ",Win32::GetLastErrSz()); + } + } + return s; +} + +Status Win32MapFile::Flush() +{ + return Status::OK(); +} + +Win32MapFile::~Win32MapFile() +{ + if (_hFile != INVALID_HANDLE_VALUE) { + Win32MapFile::Close(); + } +} + +BOOL Win32MapFile::_Init( LPCWSTR Path ) +{ + DWORD Flag = PathFileExistsW(Path) ? OPEN_EXISTING : CREATE_ALWAYS; + _hFile = CreateFileW(Path, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE, + NULL, + Flag, + FILE_ATTRIBUTE_NORMAL, + NULL); + if(!_hFile || _hFile == INVALID_HANDLE_VALUE) + return FALSE; + else + return TRUE; +} + +BOOL Win32MapFile::isEnable() +{ + return _hFile ? TRUE : FALSE; +} + +Win32FileLock::Win32FileLock( const std::string& fname ) : + _hFile(NULL),_filename(fname) +{ + std::wstring path; + ToWidePath(fname, path); + _Init(path.c_str()); +} + +Win32FileLock::~Win32FileLock() +{ + _CleanUp(); +} + +BOOL Win32FileLock::_Init( LPCWSTR path ) +{ + BOOL bRet = FALSE; + if(!_hFile) + _hFile = ::CreateFileW(path,0,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); + if(!_hFile || _hFile == INVALID_HANDLE_VALUE ){ + _hFile = NULL; + } + else + bRet = TRUE; + return bRet; +} + +void Win32FileLock::_CleanUp() +{ + ::CloseHandle(_hFile); + _hFile = NULL; +} + +BOOL Win32FileLock::isEnable() +{ + return _hFile ? TRUE : FALSE; +} + +Win32Logger::Win32Logger(WritableFile* pFile) : _pFileProxy(pFile) +{ + assert(_pFileProxy); +} + +Win32Logger::~Win32Logger() +{ + if(_pFileProxy) + delete _pFileProxy; +} + +void Win32Logger::Logv( const char* format, va_list ap ) +{ + uint64_t thread_id = ::GetCurrentThreadId(); + + // We try twice: the first time with a fixed-size stack allocated buffer, + // and the second time with a much larger dynamically allocated buffer. + char buffer[500]; + for (int iter = 0; iter < 2; iter++) { + char* base; + int bufsize; + if (iter == 0) { + bufsize = sizeof(buffer); + base = buffer; + } else { + bufsize = 30000; + base = new char[bufsize]; + } + char* p = base; + char* limit = base + bufsize; + + SYSTEMTIME st; + GetLocalTime(&st); + p += snprintf(p, limit - p, + "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ", + int(st.wYear), + int(st.wMonth), + int(st.wDay), + int(st.wHour), + int(st.wMinute), + int(st.wMinute), + int(st.wMilliseconds), + static_cast(thread_id)); + + // Print the message + if (p < limit) { + va_list backup_ap; + va_copy(backup_ap, ap); + p += vsnprintf(p, limit - p, format, backup_ap); + va_end(backup_ap); + } + + // Truncate to available space if necessary + if (p >= limit) { + if (iter == 0) { + continue; // Try again with larger buffer + } else { + p = limit - 1; + } + } + + // Add newline if necessary + if (p == base || p[-1] != '\n') { + *p++ = '\n'; + } + + assert(p <= limit); + DWORD hasWritten = 0; + if(_pFileProxy){ + _pFileProxy->Append(Slice(base, p - base)); + _pFileProxy->Flush(); + } + if (base != buffer) { + delete[] base; + } + break; + } +} + +bool Win32Env::FileExists(const std::string& fname) +{ + std::string path = fname; + std::wstring wpath; + ToWidePath(ModifyPath(path), wpath); + return ::PathFileExistsW(wpath.c_str()) ? true : false; +} + +Status Win32Env::GetChildren(const std::string& dir, std::vector* result) +{ + Status sRet; + ::WIN32_FIND_DATAW wfd; + std::string path = dir; + ModifyPath(path); + path += "\\*.*"; + std::wstring wpath; + ToWidePath(path, wpath); + + ::HANDLE hFind = ::FindFirstFileW(wpath.c_str() ,&wfd); + if(hFind && hFind != INVALID_HANDLE_VALUE){ + BOOL hasNext = TRUE; + std::string child; + while(hasNext){ + ToNarrowPath(wfd.cFileName, child); + if(child != ".." && child != ".") { + result->push_back(child); + } + hasNext = ::FindNextFileW(hFind,&wfd); + } + ::FindClose(hFind); + } + else + sRet = Status::IOError(dir,"Could not get children."); + return sRet; +} + +void Win32Env::SleepForMicroseconds( int micros ) +{ + ::Sleep((micros + 999) /1000); +} + + +Status Win32Env::DeleteFile( const std::string& fname ) +{ + Status sRet; + std::string path = fname; + std::wstring wpath; + ToWidePath(ModifyPath(path), wpath); + + if(!::DeleteFileW(wpath.c_str())) { + sRet = Status::IOError(path, "Could not delete file."); + } + return sRet; +} + +Status Win32Env::GetFileSize( const std::string& fname, uint64_t* file_size ) +{ + Status sRet; + std::string path = fname; + std::wstring wpath; + ToWidePath(ModifyPath(path), wpath); + + HANDLE file = ::CreateFileW(wpath.c_str(), + GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); + LARGE_INTEGER li; + if(::GetFileSizeEx(file,&li)){ + *file_size = (uint64_t)li.QuadPart; + }else + sRet = Status::IOError(path,"Could not get the file size."); + CloseHandle(file); + return sRet; +} + +Status Win32Env::RenameFile( const std::string& src, const std::string& target ) +{ + Status sRet; + std::string src_path = src; + std::wstring wsrc_path; + ToWidePath(ModifyPath(src_path), wsrc_path); + std::string target_path = target; + std::wstring wtarget_path; + ToWidePath(ModifyPath(target_path), wtarget_path); + + if(!MoveFileW(wsrc_path.c_str(), wtarget_path.c_str() ) ){ + DWORD err = GetLastError(); + if(err == 0x000000b7){ + if(!::DeleteFileW(wtarget_path.c_str() ) ) + sRet = Status::IOError(src, "Could not rename file."); + else if(!::MoveFileW(wsrc_path.c_str(), + wtarget_path.c_str() ) ) + sRet = Status::IOError(src, "Could not rename file."); + } + } + return sRet; +} + +Status Win32Env::LockFile( const std::string& fname, FileLock** lock ) +{ + Status sRet; + std::string path = fname; + ModifyPath(path); + Win32FileLock* _lock = new Win32FileLock(path); + if(!_lock->isEnable()){ + delete _lock; + *lock = NULL; + sRet = Status::IOError(path, "Could not lock file."); + } + else + *lock = _lock; + return sRet; +} + +Status Win32Env::UnlockFile( FileLock* lock ) +{ + Status sRet; + delete lock; + return sRet; +} + +void Win32Env::Schedule( void (*function)(void* arg), void* arg ) +{ + QueueUserWorkItem(Win32::WorkItemWrapperProc, + new Win32::WorkItemWrapper(function,arg), + WT_EXECUTEDEFAULT); +} + +void Win32Env::StartThread( void (*function)(void* arg), void* arg ) +{ + ::_beginthread(function,0,arg); +} + +Status Win32Env::GetTestDirectory( std::string* path ) +{ + Status sRet; + WCHAR TempPath[MAX_PATH]; + ::GetTempPathW(MAX_PATH,TempPath); + ToNarrowPath(TempPath, *path); + path->append("leveldb\\test\\"); + ModifyPath(*path); + return sRet; +} + +uint64_t Win32Env::NowMicros() +{ +#ifndef USE_VISTA_API +#define GetTickCount64 GetTickCount +#endif + return (uint64_t)(GetTickCount64()*1000); +} + +static Status CreateDirInner( const std::string& dirname ) +{ + Status sRet; + DWORD attr = ::GetFileAttributes(dirname.c_str()); + if (attr == INVALID_FILE_ATTRIBUTES) { // doesn't exist: + std::size_t slash = dirname.find_last_of("\\"); + if (slash != std::string::npos){ + sRet = CreateDirInner(dirname.substr(0, slash)); + if (!sRet.ok()) return sRet; + } + BOOL result = ::CreateDirectory(dirname.c_str(), NULL); + if (result == FALSE) { + sRet = Status::IOError(dirname, "Could not create directory."); + return sRet; + } + } + return sRet; +} + +Status Win32Env::CreateDir( const std::string& dirname ) +{ + std::string path = dirname; + if(path[path.length() - 1] != '\\'){ + path += '\\'; + } + ModifyPath(path); + + return CreateDirInner(path); +} + +Status Win32Env::DeleteDir( const std::string& dirname ) +{ + Status sRet; + std::wstring path; + ToWidePath(dirname, path); + ModifyPath(path); + if(!::RemoveDirectoryW( path.c_str() ) ){ + sRet = Status::IOError(dirname, "Could not delete directory."); + } + return sRet; +} + +Status Win32Env::NewSequentialFile( const std::string& fname, SequentialFile** result ) +{ + Status sRet; + std::string path = fname; + ModifyPath(path); + Win32SequentialFile* pFile = new Win32SequentialFile(path); + if(pFile->isEnable()){ + *result = pFile; + }else { + delete pFile; + sRet = Status::IOError(path, Win32::GetLastErrSz()); + } + return sRet; +} + +Status Win32Env::NewRandomAccessFile( const std::string& fname, RandomAccessFile** result ) +{ + Status sRet; + std::string path = fname; + Win32RandomAccessFile* pFile = new Win32RandomAccessFile(ModifyPath(path)); + if(!pFile->isEnable()){ + delete pFile; + *result = NULL; + sRet = Status::IOError(path, Win32::GetLastErrSz()); + }else + *result = pFile; + return sRet; +} + +Status Win32Env::NewLogger( const std::string& fname, Logger** result ) +{ + Status sRet; + std::string path = fname; + Win32MapFile* pMapFile = new Win32MapFile(ModifyPath(path)); + if(!pMapFile->isEnable()){ + delete pMapFile; + *result = NULL; + sRet = Status::IOError(path,"could not create a logger."); + }else + *result = new Win32Logger(pMapFile); + return sRet; +} + +Status Win32Env::NewWritableFile( const std::string& fname, WritableFile** result ) +{ + Status sRet; + std::string path = fname; + Win32MapFile* pFile = new Win32MapFile(ModifyPath(path)); + if(!pFile->isEnable()){ + *result = NULL; + sRet = Status::IOError(fname,Win32::GetLastErrSz()); + }else + *result = pFile; + return sRet; +} + +Win32Env::Win32Env() +{ + +} + +Win32Env::~Win32Env() +{ + +} + + +} // Win32 namespace + +static port::OnceType once = LEVELDB_ONCE_INIT; +static Env* default_env; +static void InitDefaultEnv() { default_env = new Win32::Win32Env(); } + +Env* Env::Default() { + port::InitOnce(&once, InitDefaultEnv); + return default_env; +} + +} // namespace leveldb + +#endif // defined(LEVELDB_PLATFORM_WINDOWS) diff --git a/src/leveldb/util/filter_policy.cc b/src/leveldb/util/filter_policy.cc new file mode 100644 index 0000000..7b045c8 --- /dev/null +++ b/src/leveldb/util/filter_policy.cc @@ -0,0 +1,11 @@ +// Copyright (c) 2012 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/filter_policy.h" + +namespace leveldb { + +FilterPolicy::~FilterPolicy() { } + +} // namespace leveldb diff --git a/src/leveldb/util/hash.cc b/src/leveldb/util/hash.cc new file mode 100644 index 0000000..ba18180 --- /dev/null +++ b/src/leveldb/util/hash.cc @@ -0,0 +1,45 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include "util/coding.h" +#include "util/hash.h" + +namespace leveldb { + +uint32_t Hash(const char* data, size_t n, uint32_t seed) { + // Similar to murmur hash + const uint32_t m = 0xc6a4a793; + const uint32_t r = 24; + const char* limit = data + n; + uint32_t h = seed ^ (n * m); + + // Pick up four bytes at a time + while (data + 4 <= limit) { + uint32_t w = DecodeFixed32(data); + data += 4; + h += w; + h *= m; + h ^= (h >> 16); + } + + // Pick up remaining bytes + switch (limit - data) { + case 3: + h += data[2] << 16; + // fall through + case 2: + h += data[1] << 8; + // fall through + case 1: + h += data[0]; + h *= m; + h ^= (h >> r); + break; + } + return h; +} + + +} // namespace leveldb diff --git a/src/leveldb/util/hash.h b/src/leveldb/util/hash.h new file mode 100644 index 0000000..8889d56 --- /dev/null +++ b/src/leveldb/util/hash.h @@ -0,0 +1,19 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Simple hash function used for internal data structures + +#ifndef STORAGE_LEVELDB_UTIL_HASH_H_ +#define STORAGE_LEVELDB_UTIL_HASH_H_ + +#include +#include + +namespace leveldb { + +extern uint32_t Hash(const char* data, size_t n, uint32_t seed); + +} + +#endif // STORAGE_LEVELDB_UTIL_HASH_H_ diff --git a/src/leveldb/util/histogram.cc b/src/leveldb/util/histogram.cc new file mode 100644 index 0000000..bb95f58 --- /dev/null +++ b/src/leveldb/util/histogram.cc @@ -0,0 +1,139 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include "port/port.h" +#include "util/histogram.h" + +namespace leveldb { + +const double Histogram::kBucketLimit[kNumBuckets] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20, 25, 30, 35, 40, 45, + 50, 60, 70, 80, 90, 100, 120, 140, 160, 180, 200, 250, 300, 350, 400, 450, + 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000, 2500, 3000, + 3500, 4000, 4500, 5000, 6000, 7000, 8000, 9000, 10000, 12000, 14000, + 16000, 18000, 20000, 25000, 30000, 35000, 40000, 45000, 50000, 60000, + 70000, 80000, 90000, 100000, 120000, 140000, 160000, 180000, 200000, + 250000, 300000, 350000, 400000, 450000, 500000, 600000, 700000, 800000, + 900000, 1000000, 1200000, 1400000, 1600000, 1800000, 2000000, 2500000, + 3000000, 3500000, 4000000, 4500000, 5000000, 6000000, 7000000, 8000000, + 9000000, 10000000, 12000000, 14000000, 16000000, 18000000, 20000000, + 25000000, 30000000, 35000000, 40000000, 45000000, 50000000, 60000000, + 70000000, 80000000, 90000000, 100000000, 120000000, 140000000, 160000000, + 180000000, 200000000, 250000000, 300000000, 350000000, 400000000, + 450000000, 500000000, 600000000, 700000000, 800000000, 900000000, + 1000000000, 1200000000, 1400000000, 1600000000, 1800000000, 2000000000, + 2500000000.0, 3000000000.0, 3500000000.0, 4000000000.0, 4500000000.0, + 5000000000.0, 6000000000.0, 7000000000.0, 8000000000.0, 9000000000.0, + 1e200, +}; + +void Histogram::Clear() { + min_ = kBucketLimit[kNumBuckets-1]; + max_ = 0; + num_ = 0; + sum_ = 0; + sum_squares_ = 0; + for (int i = 0; i < kNumBuckets; i++) { + buckets_[i] = 0; + } +} + +void Histogram::Add(double value) { + // Linear search is fast enough for our usage in db_bench + int b = 0; + while (b < kNumBuckets - 1 && kBucketLimit[b] <= value) { + b++; + } + buckets_[b] += 1.0; + if (min_ > value) min_ = value; + if (max_ < value) max_ = value; + num_++; + sum_ += value; + sum_squares_ += (value * value); +} + +void Histogram::Merge(const Histogram& other) { + if (other.min_ < min_) min_ = other.min_; + if (other.max_ > max_) max_ = other.max_; + num_ += other.num_; + sum_ += other.sum_; + sum_squares_ += other.sum_squares_; + for (int b = 0; b < kNumBuckets; b++) { + buckets_[b] += other.buckets_[b]; + } +} + +double Histogram::Median() const { + return Percentile(50.0); +} + +double Histogram::Percentile(double p) const { + double threshold = num_ * (p / 100.0); + double sum = 0; + for (int b = 0; b < kNumBuckets; b++) { + sum += buckets_[b]; + if (sum >= threshold) { + // Scale linearly within this bucket + double left_point = (b == 0) ? 0 : kBucketLimit[b-1]; + double right_point = kBucketLimit[b]; + double left_sum = sum - buckets_[b]; + double right_sum = sum; + double pos = (threshold - left_sum) / (right_sum - left_sum); + double r = left_point + (right_point - left_point) * pos; + if (r < min_) r = min_; + if (r > max_) r = max_; + return r; + } + } + return max_; +} + +double Histogram::Average() const { + if (num_ == 0.0) return 0; + return sum_ / num_; +} + +double Histogram::StandardDeviation() const { + if (num_ == 0.0) return 0; + double variance = (sum_squares_ * num_ - sum_ * sum_) / (num_ * num_); + return sqrt(variance); +} + +std::string Histogram::ToString() const { + std::string r; + char buf[200]; + snprintf(buf, sizeof(buf), + "Count: %.0f Average: %.4f StdDev: %.2f\n", + num_, Average(), StandardDeviation()); + r.append(buf); + snprintf(buf, sizeof(buf), + "Min: %.4f Median: %.4f Max: %.4f\n", + (num_ == 0.0 ? 0.0 : min_), Median(), max_); + r.append(buf); + r.append("------------------------------------------------------\n"); + const double mult = 100.0 / num_; + double sum = 0; + for (int b = 0; b < kNumBuckets; b++) { + if (buckets_[b] <= 0.0) continue; + sum += buckets_[b]; + snprintf(buf, sizeof(buf), + "[ %7.0f, %7.0f ) %7.0f %7.3f%% %7.3f%% ", + ((b == 0) ? 0.0 : kBucketLimit[b-1]), // left + kBucketLimit[b], // right + buckets_[b], // count + mult * buckets_[b], // percentage + mult * sum); // cumulative percentage + r.append(buf); + + // Add hash marks based on percentage; 20 marks for 100%. + int marks = static_cast(20*(buckets_[b] / num_) + 0.5); + r.append(marks, '#'); + r.push_back('\n'); + } + return r; +} + +} // namespace leveldb diff --git a/src/leveldb/util/histogram.h b/src/leveldb/util/histogram.h new file mode 100644 index 0000000..1ef9f3c --- /dev/null +++ b/src/leveldb/util/histogram.h @@ -0,0 +1,42 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_HISTOGRAM_H_ +#define STORAGE_LEVELDB_UTIL_HISTOGRAM_H_ + +#include + +namespace leveldb { + +class Histogram { + public: + Histogram() { } + ~Histogram() { } + + void Clear(); + void Add(double value); + void Merge(const Histogram& other); + + std::string ToString() const; + + private: + double min_; + double max_; + double num_; + double sum_; + double sum_squares_; + + enum { kNumBuckets = 154 }; + static const double kBucketLimit[kNumBuckets]; + double buckets_[kNumBuckets]; + + double Median() const; + double Percentile(double p) const; + double Average() const; + double StandardDeviation() const; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_HISTOGRAM_H_ diff --git a/src/leveldb/util/logging.cc b/src/leveldb/util/logging.cc new file mode 100644 index 0000000..22cf278 --- /dev/null +++ b/src/leveldb/util/logging.cc @@ -0,0 +1,81 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/logging.h" + +#include +#include +#include +#include +#include "leveldb/env.h" +#include "leveldb/slice.h" + +namespace leveldb { + +void AppendNumberTo(std::string* str, uint64_t num) { + char buf[30]; + snprintf(buf, sizeof(buf), "%llu", (unsigned long long) num); + str->append(buf); +} + +void AppendEscapedStringTo(std::string* str, const Slice& value) { + for (size_t i = 0; i < value.size(); i++) { + char c = value[i]; + if (c >= ' ' && c <= '~') { + str->push_back(c); + } else { + char buf[10]; + snprintf(buf, sizeof(buf), "\\x%02x", + static_cast(c) & 0xff); + str->append(buf); + } + } +} + +std::string NumberToString(uint64_t num) { + std::string r; + AppendNumberTo(&r, num); + return r; +} + +std::string EscapeString(const Slice& value) { + std::string r; + AppendEscapedStringTo(&r, value); + return r; +} + +bool ConsumeChar(Slice* in, char c) { + if (!in->empty() && (*in)[0] == c) { + in->remove_prefix(1); + return true; + } else { + return false; + } +} + +bool ConsumeDecimalNumber(Slice* in, uint64_t* val) { + uint64_t v = 0; + int digits = 0; + while (!in->empty()) { + char c = (*in)[0]; + if (c >= '0' && c <= '9') { + ++digits; + const int delta = (c - '0'); + static const uint64_t kMaxUint64 = ~static_cast(0); + if (v > kMaxUint64/10 || + (v == kMaxUint64/10 && delta > kMaxUint64%10)) { + // Overflow + return false; + } + v = (v * 10) + delta; + in->remove_prefix(1); + } else { + break; + } + } + *val = v; + return (digits > 0); +} + +} // namespace leveldb diff --git a/src/leveldb/util/logging.h b/src/leveldb/util/logging.h new file mode 100644 index 0000000..b0c5da8 --- /dev/null +++ b/src/leveldb/util/logging.h @@ -0,0 +1,47 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Must not be included from any .h files to avoid polluting the namespace +// with macros. + +#ifndef STORAGE_LEVELDB_UTIL_LOGGING_H_ +#define STORAGE_LEVELDB_UTIL_LOGGING_H_ + +#include +#include +#include +#include "port/port.h" + +namespace leveldb { + +class Slice; +class WritableFile; + +// Append a human-readable printout of "num" to *str +extern void AppendNumberTo(std::string* str, uint64_t num); + +// Append a human-readable printout of "value" to *str. +// Escapes any non-printable characters found in "value". +extern void AppendEscapedStringTo(std::string* str, const Slice& value); + +// Return a human-readable printout of "num" +extern std::string NumberToString(uint64_t num); + +// Return a human-readable version of "value". +// Escapes any non-printable characters found in "value". +extern std::string EscapeString(const Slice& value); + +// If *in starts with "c", advances *in past the first character and +// returns true. Otherwise, returns false. +extern bool ConsumeChar(Slice* in, char c); + +// Parse a human-readable number from "*in" into *value. On success, +// advances "*in" past the consumed number and sets "*val" to the +// numeric value. Otherwise, returns false and leaves *in in an +// unspecified state. +extern bool ConsumeDecimalNumber(Slice* in, uint64_t* val); + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_LOGGING_H_ diff --git a/src/leveldb/util/mutexlock.h b/src/leveldb/util/mutexlock.h new file mode 100644 index 0000000..1ff5a9e --- /dev/null +++ b/src/leveldb/util/mutexlock.h @@ -0,0 +1,41 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_ +#define STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_ + +#include "port/port.h" +#include "port/thread_annotations.h" + +namespace leveldb { + +// Helper class that locks a mutex on construction and unlocks the mutex when +// the destructor of the MutexLock object is invoked. +// +// Typical usage: +// +// void MyClass::MyMethod() { +// MutexLock l(&mu_); // mu_ is an instance variable +// ... some complex code, possibly with multiple return paths ... +// } + +class SCOPED_LOCKABLE MutexLock { + public: + explicit MutexLock(port::Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu) + : mu_(mu) { + this->mu_->Lock(); + } + ~MutexLock() UNLOCK_FUNCTION() { this->mu_->Unlock(); } + + private: + port::Mutex *const mu_; + // No copying allowed + MutexLock(const MutexLock&); + void operator=(const MutexLock&); +}; + +} // namespace leveldb + + +#endif // STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_ diff --git a/src/leveldb/util/options.cc b/src/leveldb/util/options.cc new file mode 100644 index 0000000..76af5b9 --- /dev/null +++ b/src/leveldb/util/options.cc @@ -0,0 +1,29 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/options.h" + +#include "leveldb/comparator.h" +#include "leveldb/env.h" + +namespace leveldb { + +Options::Options() + : comparator(BytewiseComparator()), + create_if_missing(false), + error_if_exists(false), + paranoid_checks(false), + env(Env::Default()), + info_log(NULL), + write_buffer_size(4<<20), + max_open_files(1000), + block_cache(NULL), + block_size(4096), + block_restart_interval(16), + compression(kSnappyCompression), + filter_policy(NULL) { +} + + +} // namespace leveldb diff --git a/src/leveldb/util/posix_logger.h b/src/leveldb/util/posix_logger.h new file mode 100644 index 0000000..c063c2b --- /dev/null +++ b/src/leveldb/util/posix_logger.h @@ -0,0 +1,98 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Logger implementation that can be shared by all environments +// where enough Posix functionality is available. + +#ifndef STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ +#define STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ + +#include +#include +#include +#include +#include "leveldb/env.h" + +namespace leveldb { + +class PosixLogger : public Logger { + private: + FILE* file_; + uint64_t (*gettid_)(); // Return the thread id for the current thread + public: + PosixLogger(FILE* f, uint64_t (*gettid)()) : file_(f), gettid_(gettid) { } + virtual ~PosixLogger() { + fclose(file_); + } + virtual void Logv(const char* format, va_list ap) { + const uint64_t thread_id = (*gettid_)(); + + // We try twice: the first time with a fixed-size stack allocated buffer, + // and the second time with a much larger dynamically allocated buffer. + char buffer[500]; + for (int iter = 0; iter < 2; iter++) { + char* base; + int bufsize; + if (iter == 0) { + bufsize = sizeof(buffer); + base = buffer; + } else { + bufsize = 30000; + base = new char[bufsize]; + } + char* p = base; + char* limit = base + bufsize; + + struct timeval now_tv; + gettimeofday(&now_tv, NULL); + const time_t seconds = now_tv.tv_sec; + struct tm t; + localtime_r(&seconds, &t); + p += snprintf(p, limit - p, + "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ", + t.tm_year + 1900, + t.tm_mon + 1, + t.tm_mday, + t.tm_hour, + t.tm_min, + t.tm_sec, + static_cast(now_tv.tv_usec), + static_cast(thread_id)); + + // Print the message + if (p < limit) { + va_list backup_ap; + va_copy(backup_ap, ap); + p += vsnprintf(p, limit - p, format, backup_ap); + va_end(backup_ap); + } + + // Truncate to available space if necessary + if (p >= limit) { + if (iter == 0) { + continue; // Try again with larger buffer + } else { + p = limit - 1; + } + } + + // Add newline if necessary + if (p == base || p[-1] != '\n') { + *p++ = '\n'; + } + + assert(p <= limit); + fwrite(base, 1, p - base, file_); + fflush(file_); + if (base != buffer) { + delete[] base; + } + break; + } + } +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ diff --git a/src/leveldb/util/random.h b/src/leveldb/util/random.h new file mode 100644 index 0000000..0753824 --- /dev/null +++ b/src/leveldb/util/random.h @@ -0,0 +1,59 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_RANDOM_H_ +#define STORAGE_LEVELDB_UTIL_RANDOM_H_ + +#include + +namespace leveldb { + +// A very simple random number generator. Not especially good at +// generating truly random bits, but good enough for our needs in this +// package. +class Random { + private: + uint32_t seed_; + public: + explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) { } + uint32_t Next() { + static const uint32_t M = 2147483647L; // 2^31-1 + static const uint64_t A = 16807; // bits 14, 8, 7, 5, 2, 1, 0 + // We are computing + // seed_ = (seed_ * A) % M, where M = 2^31-1 + // + // seed_ must not be zero or M, or else all subsequent computed values + // will be zero or M respectively. For all other values, seed_ will end + // up cycling through every number in [1,M-1] + uint64_t product = seed_ * A; + + // Compute (product % M) using the fact that ((x << 31) % M) == x. + seed_ = static_cast((product >> 31) + (product & M)); + // The first reduction may overflow by 1 bit, so we may need to + // repeat. mod == M is not possible; using > allows the faster + // sign-bit-based test. + if (seed_ > M) { + seed_ -= M; + } + return seed_; + } + // Returns a uniformly distributed value in the range [0..n-1] + // REQUIRES: n > 0 + uint32_t Uniform(int n) { return Next() % n; } + + // Randomly returns true ~"1/n" of the time, and false otherwise. + // REQUIRES: n > 0 + bool OneIn(int n) { return (Next() % n) == 0; } + + // Skewed: pick "base" uniformly from range [0,max_log] and then + // return "base" random bits. The effect is to pick a number in the + // range [0,2^max_log-1] with exponential bias towards smaller numbers. + uint32_t Skewed(int max_log) { + return Uniform(1 << Uniform(max_log + 1)); + } +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_RANDOM_H_ diff --git a/src/leveldb/util/status.cc b/src/leveldb/util/status.cc new file mode 100644 index 0000000..a44f35b --- /dev/null +++ b/src/leveldb/util/status.cc @@ -0,0 +1,75 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include "port/port.h" +#include "leveldb/status.h" + +namespace leveldb { + +const char* Status::CopyState(const char* state) { + uint32_t size; + memcpy(&size, state, sizeof(size)); + char* result = new char[size + 5]; + memcpy(result, state, size + 5); + return result; +} + +Status::Status(Code code, const Slice& msg, const Slice& msg2) { + assert(code != kOk); + const uint32_t len1 = msg.size(); + const uint32_t len2 = msg2.size(); + const uint32_t size = len1 + (len2 ? (2 + len2) : 0); + char* result = new char[size + 5]; + memcpy(result, &size, sizeof(size)); + result[4] = static_cast(code); + memcpy(result + 5, msg.data(), len1); + if (len2) { + result[5 + len1] = ':'; + result[6 + len1] = ' '; + memcpy(result + 7 + len1, msg2.data(), len2); + } + state_ = result; +} + +std::string Status::ToString() const { + if (state_ == NULL) { + return "OK"; + } else { + char tmp[30]; + const char* type; + switch (code()) { + case kOk: + type = "OK"; + break; + case kNotFound: + type = "NotFound: "; + break; + case kCorruption: + type = "Corruption: "; + break; + case kNotSupported: + type = "Not implemented: "; + break; + case kInvalidArgument: + type = "Invalid argument: "; + break; + case kIOError: + type = "IO error: "; + break; + default: + snprintf(tmp, sizeof(tmp), "Unknown code(%d): ", + static_cast(code())); + type = tmp; + break; + } + std::string result(type); + uint32_t length; + memcpy(&length, state_, sizeof(length)); + result.append(state_ + 5, length); + return result; + } +} + +} // namespace leveldb diff --git a/src/leveldb/util/testharness.cc b/src/leveldb/util/testharness.cc new file mode 100644 index 0000000..eb1bdd5 --- /dev/null +++ b/src/leveldb/util/testharness.cc @@ -0,0 +1,77 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/testharness.h" + +#include +#include +#include +#include + +namespace leveldb { +namespace test { + +namespace { +struct Test { + const char* base; + const char* name; + void (*func)(); +}; +std::vector* tests; +} + +bool RegisterTest(const char* base, const char* name, void (*func)()) { + if (tests == NULL) { + tests = new std::vector; + } + Test t; + t.base = base; + t.name = name; + t.func = func; + tests->push_back(t); + return true; +} + +int RunAllTests() { + const char* matcher = getenv("LEVELDB_TESTS"); + + int num = 0; + if (tests != NULL) { + for (int i = 0; i < tests->size(); i++) { + const Test& t = (*tests)[i]; + if (matcher != NULL) { + std::string name = t.base; + name.push_back('.'); + name.append(t.name); + if (strstr(name.c_str(), matcher) == NULL) { + continue; + } + } + fprintf(stderr, "==== Test %s.%s\n", t.base, t.name); + (*t.func)(); + ++num; + } + } + fprintf(stderr, "==== PASSED %d tests\n", num); + return 0; +} + +std::string TmpDir() { + std::string dir; + Status s = Env::Default()->GetTestDirectory(&dir); + ASSERT_TRUE(s.ok()) << s.ToString(); + return dir; +} + +int RandomSeed() { + const char* env = getenv("TEST_RANDOM_SEED"); + int result = (env != NULL ? atoi(env) : 301); + if (result <= 0) { + result = 301; + } + return result; +} + +} // namespace test +} // namespace leveldb diff --git a/src/leveldb/util/testharness.h b/src/leveldb/util/testharness.h new file mode 100644 index 0000000..da4fe68 --- /dev/null +++ b/src/leveldb/util/testharness.h @@ -0,0 +1,138 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_TESTHARNESS_H_ +#define STORAGE_LEVELDB_UTIL_TESTHARNESS_H_ + +#include +#include +#include +#include "leveldb/env.h" +#include "leveldb/slice.h" +#include "util/random.h" + +namespace leveldb { +namespace test { + +// Run some of the tests registered by the TEST() macro. If the +// environment variable "LEVELDB_TESTS" is not set, runs all tests. +// Otherwise, runs only the tests whose name contains the value of +// "LEVELDB_TESTS" as a substring. E.g., suppose the tests are: +// TEST(Foo, Hello) { ... } +// TEST(Foo, World) { ... } +// LEVELDB_TESTS=Hello will run the first test +// LEVELDB_TESTS=o will run both tests +// LEVELDB_TESTS=Junk will run no tests +// +// Returns 0 if all tests pass. +// Dies or returns a non-zero value if some test fails. +extern int RunAllTests(); + +// Return the directory to use for temporary storage. +extern std::string TmpDir(); + +// Return a randomization seed for this run. Typically returns the +// same number on repeated invocations of this binary, but automated +// runs may be able to vary the seed. +extern int RandomSeed(); + +// An instance of Tester is allocated to hold temporary state during +// the execution of an assertion. +class Tester { + private: + bool ok_; + const char* fname_; + int line_; + std::stringstream ss_; + + public: + Tester(const char* f, int l) + : ok_(true), fname_(f), line_(l) { + } + + ~Tester() { + if (!ok_) { + fprintf(stderr, "%s:%d:%s\n", fname_, line_, ss_.str().c_str()); + exit(1); + } + } + + Tester& Is(bool b, const char* msg) { + if (!b) { + ss_ << " Assertion failure " << msg; + ok_ = false; + } + return *this; + } + + Tester& IsOk(const Status& s) { + if (!s.ok()) { + ss_ << " " << s.ToString(); + ok_ = false; + } + return *this; + } + +#define BINARY_OP(name,op) \ + template \ + Tester& name(const X& x, const Y& y) { \ + if (! (x op y)) { \ + ss_ << " failed: " << x << (" " #op " ") << y; \ + ok_ = false; \ + } \ + return *this; \ + } + + BINARY_OP(IsEq, ==) + BINARY_OP(IsNe, !=) + BINARY_OP(IsGe, >=) + BINARY_OP(IsGt, >) + BINARY_OP(IsLe, <=) + BINARY_OP(IsLt, <) +#undef BINARY_OP + + // Attach the specified value to the error message if an error has occurred + template + Tester& operator<<(const V& value) { + if (!ok_) { + ss_ << " " << value; + } + return *this; + } +}; + +#define ASSERT_TRUE(c) ::leveldb::test::Tester(__FILE__, __LINE__).Is((c), #c) +#define ASSERT_OK(s) ::leveldb::test::Tester(__FILE__, __LINE__).IsOk((s)) +#define ASSERT_EQ(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsEq((a),(b)) +#define ASSERT_NE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsNe((a),(b)) +#define ASSERT_GE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsGe((a),(b)) +#define ASSERT_GT(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsGt((a),(b)) +#define ASSERT_LE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsLe((a),(b)) +#define ASSERT_LT(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsLt((a),(b)) + +#define TCONCAT(a,b) TCONCAT1(a,b) +#define TCONCAT1(a,b) a##b + +#define TEST(base,name) \ +class TCONCAT(_Test_,name) : public base { \ + public: \ + void _Run(); \ + static void _RunIt() { \ + TCONCAT(_Test_,name) t; \ + t._Run(); \ + } \ +}; \ +bool TCONCAT(_Test_ignored_,name) = \ + ::leveldb::test::RegisterTest(#base, #name, &TCONCAT(_Test_,name)::_RunIt); \ +void TCONCAT(_Test_,name)::_Run() + +// Register the specified test. Typically not used directly, but +// invoked via the macro expansion of TEST. +extern bool RegisterTest(const char* base, const char* name, void (*func)()); + + +} // namespace test +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_TESTHARNESS_H_ diff --git a/src/leveldb/util/testutil.cc b/src/leveldb/util/testutil.cc new file mode 100644 index 0000000..538d095 --- /dev/null +++ b/src/leveldb/util/testutil.cc @@ -0,0 +1,51 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "util/testutil.h" + +#include "util/random.h" + +namespace leveldb { +namespace test { + +Slice RandomString(Random* rnd, int len, std::string* dst) { + dst->resize(len); + for (int i = 0; i < len; i++) { + (*dst)[i] = static_cast(' ' + rnd->Uniform(95)); // ' ' .. '~' + } + return Slice(*dst); +} + +std::string RandomKey(Random* rnd, int len) { + // Make sure to generate a wide variety of characters so we + // test the boundary conditions for short-key optimizations. + static const char kTestChars[] = { + '\0', '\1', 'a', 'b', 'c', 'd', 'e', '\xfd', '\xfe', '\xff' + }; + std::string result; + for (int i = 0; i < len; i++) { + result += kTestChars[rnd->Uniform(sizeof(kTestChars))]; + } + return result; +} + + +extern Slice CompressibleString(Random* rnd, double compressed_fraction, + int len, std::string* dst) { + int raw = static_cast(len * compressed_fraction); + if (raw < 1) raw = 1; + std::string raw_data; + RandomString(rnd, raw, &raw_data); + + // Duplicate the random data until we have filled "len" bytes + dst->clear(); + while (dst->size() < len) { + dst->append(raw_data); + } + dst->resize(len); + return Slice(*dst); +} + +} // namespace test +} // namespace leveldb diff --git a/src/leveldb/util/testutil.h b/src/leveldb/util/testutil.h new file mode 100644 index 0000000..824e655 --- /dev/null +++ b/src/leveldb/util/testutil.h @@ -0,0 +1,53 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_TESTUTIL_H_ +#define STORAGE_LEVELDB_UTIL_TESTUTIL_H_ + +#include "leveldb/env.h" +#include "leveldb/slice.h" +#include "util/random.h" + +namespace leveldb { +namespace test { + +// Store in *dst a random string of length "len" and return a Slice that +// references the generated data. +extern Slice RandomString(Random* rnd, int len, std::string* dst); + +// Return a random key with the specified length that may contain interesting +// characters (e.g. \x00, \xff, etc.). +extern std::string RandomKey(Random* rnd, int len); + +// Store in *dst a string of length "len" that will compress to +// "N*compressed_fraction" bytes and return a Slice that references +// the generated data. +extern Slice CompressibleString(Random* rnd, double compressed_fraction, + int len, std::string* dst); + +// A wrapper that allows injection of errors. +class ErrorEnv : public EnvWrapper { + public: + bool writable_file_error_; + int num_writable_file_errors_; + + ErrorEnv() : EnvWrapper(Env::Default()), + writable_file_error_(false), + num_writable_file_errors_(0) { } + + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result) { + if (writable_file_error_) { + ++num_writable_file_errors_; + *result = NULL; + return Status::IOError(fname, "fake error"); + } + return target()->NewWritableFile(fname, result); + } +}; + +} // namespace test +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_TESTUTIL_H_ diff --git a/src/limitedmap.h b/src/limitedmap.h new file mode 100644 index 0000000..7049d68 --- /dev/null +++ b/src/limitedmap.h @@ -0,0 +1,100 @@ +// Copyright (c) 2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_LIMITEDMAP_H +#define BITCOIN_LIMITEDMAP_H + +#include +#include + +/** STL-like map container that only keeps the N elements with the highest value. */ +template class limitedmap +{ +public: + typedef K key_type; + typedef V mapped_type; + typedef std::pair value_type; + typedef typename std::map::const_iterator const_iterator; + typedef typename std::map::size_type size_type; + +protected: + std::map map; + typedef typename std::map::iterator iterator; + std::multimap rmap; + typedef typename std::multimap::iterator rmap_iterator; + size_type nMaxSize; + +public: + limitedmap(size_type nMaxSizeIn = 0) { nMaxSize = nMaxSizeIn; } + const_iterator begin() const { return map.begin(); } + const_iterator end() const { return map.end(); } + size_type size() const { return map.size(); } + bool empty() const { return map.empty(); } + const_iterator find(const key_type& k) const { return map.find(k); } + size_type count(const key_type& k) const { return map.count(k); } + void insert(const value_type& x) + { + std::pair ret = map.insert(x); + if (ret.second) + { + if (nMaxSize && map.size() == nMaxSize) + { + map.erase(rmap.begin()->second); + rmap.erase(rmap.begin()); + } + rmap.insert(make_pair(x.second, ret.first)); + } + return; + } + void erase(const key_type& k) + { + iterator itTarget = map.find(k); + if (itTarget == map.end()) + return; + std::pair itPair = rmap.equal_range(itTarget->second); + for (rmap_iterator it = itPair.first; it != itPair.second; ++it) + if (it->second == itTarget) + { + rmap.erase(it); + map.erase(itTarget); + return; + } + // Shouldn't ever get here + assert(0); //TODO remove me + map.erase(itTarget); + } + void update(const_iterator itIn, const mapped_type& v) + { + //TODO: When we switch to C++11, use map.erase(itIn, itIn) to get the non-const iterator + iterator itTarget = map.find(itIn->first); + if (itTarget == map.end()) + return; + std::pair itPair = rmap.equal_range(itTarget->second); + for (rmap_iterator it = itPair.first; it != itPair.second; ++it) + if (it->second == itTarget) + { + rmap.erase(it); + itTarget->second = v; + rmap.insert(make_pair(v, itTarget)); + return; + } + // Shouldn't ever get here + assert(0); //TODO remove me + itTarget->second = v; + rmap.insert(make_pair(v, itTarget)); + } + size_type max_size() const { return nMaxSize; } + size_type max_size(size_type s) + { + if (s) + while (map.size() > s) + { + map.erase(rmap.begin()->second); + rmap.erase(rmap.begin()); + } + nMaxSize = s; + return nMaxSize; + } +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..eaddd83 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,4693 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "alert.h" +#include "checkpoints.h" +#include "db.h" +#include "txdb.h" +#include "net.h" +#include "init.h" +#include "util.h" // using DoubleToNumeratorDenominator +#include "ui_interface.h" +#include "checkqueue.h" +#include +#include +#include +#include +#include + + +// +// Global state +// + +using namespace std; +using namespace boost; +CCriticalSection cs_setpwalletRegistered; +set setpwalletRegistered; + +CCriticalSection cs_main; + +CTxMemPool mempool; +unsigned int nTransactionsUpdated = 0; + +map mapBlockIndex; +uint256 hashGenesisBlock("0x00000326fcb9e2d71672332c1cf1e525c08323d247cad5b16b3bbf71d497aa01"); +static CBigNum bnProofOfWorkLimit(~uint256(0) >> 20); +static CBigNum bnStartDifficulty(~uint256(0) >> 27); +CBlockIndex* pindexGenesisBlock = NULL; +int nBestHeight = -1; +uint256 nBestChainWork = 0; +uint256 nBestInvalidWork = 0; +uint256 hashBestChain = 0; +CBlockIndex* pindexBest = NULL; +set setBlockIndexValid; // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed +int64 nTimeBestReceived = 0; +int nScriptCheckThreads = 0; +bool fImporting = false; +bool fReindex = false; +bool fBenchmark = false; +bool fTxIndex = false; +unsigned int nCoinCacheSize = 5000; + +/** Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) */ +int64 CTransaction::nMinTxFee = 0.00001 * COIN; +/** Fees smaller than this (in satoshi) are considered zero fee (for relaying) */ +int64 CTransaction::nMinRelayTxFee = 0.00001 * COIN; + +CMedianFilter cPeerBlockCounts(8, 0); // Amount of blocks that other nodes claim to have + +map mapOrphanBlocks; +multimap mapOrphanBlocksByPrev; + +map mapOrphanTransactions; +map > mapOrphanTransactionsByPrev; + +// Constant stuff for coinbase transactions we create: +CScript COINBASE_FLAGS; + +const string strMessageMagic = "CryptoLottoToken Signed Message:\n"; + +double dHashesPerSec = 0.0; +int64 nHPSTimerStart = 0; + +// Settings +int64 nTransactionFee = 0; +int64 nMinimumInputValue = DUST_HARD_LIMIT; + + +////////////////////////////////////////////////////////////////////////////// +// +// dispatching functions +// + +// These functions dispatch to one or all registered wallets + + +void RegisterWallet(CWallet* pwalletIn) +{ + { + LOCK(cs_setpwalletRegistered); + setpwalletRegistered.insert(pwalletIn); + } +} + +void UnregisterWallet(CWallet* pwalletIn) +{ + { + LOCK(cs_setpwalletRegistered); + setpwalletRegistered.erase(pwalletIn); + } +} + +// get the wallet transaction with the given hash (if it exists) +bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + if (pwallet->GetTransaction(hashTx,wtx)) + return true; + return false; +} + +// erases transaction with the given hash from all wallets +void static EraseFromWallets(uint256 hash) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->EraseFromWallet(hash); +} + +// make sure all wallets know about the given transaction, in the given block +void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->AddToWalletIfInvolvingMe(hash, tx, pblock, fUpdate); +} + +// notify wallets about a new best chain +void static SetBestChain(const CBlockLocator& loc) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->SetBestChain(loc); +} + +// notify wallets about an updated transaction +void static UpdatedTransaction(const uint256& hashTx) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->UpdatedTransaction(hashTx); +} + +// dump all wallets +void static PrintWallets(const CBlock& block) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->PrintWallet(block); +} + +// notify wallets about an incoming inventory (for request counts) +void static Inventory(const uint256& hash) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->Inventory(hash); +} + +// ask wallets to resend their transactions +void static ResendWalletTransactions() +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->ResendWalletTransactions(); +} + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CCoinsView implementations +// + +bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) { return false; } +bool CCoinsView::SetCoins(const uint256 &txid, const CCoins &coins) { return false; } +bool CCoinsView::HaveCoins(const uint256 &txid) { return false; } +CBlockIndex *CCoinsView::GetBestBlock() { return NULL; } +bool CCoinsView::SetBestBlock(CBlockIndex *pindex) { return false; } +bool CCoinsView::BatchWrite(const std::map &mapCoins, CBlockIndex *pindex) { return false; } +bool CCoinsView::GetStats(CCoinsStats &stats) { return false; } + + +CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { } +bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) { return base->GetCoins(txid, coins); } +bool CCoinsViewBacked::SetCoins(const uint256 &txid, const CCoins &coins) { return base->SetCoins(txid, coins); } +bool CCoinsViewBacked::HaveCoins(const uint256 &txid) { return base->HaveCoins(txid); } +CBlockIndex *CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); } +bool CCoinsViewBacked::SetBestBlock(CBlockIndex *pindex) { return base->SetBestBlock(pindex); } +void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } +bool CCoinsViewBacked::BatchWrite(const std::map &mapCoins, CBlockIndex *pindex) { return base->BatchWrite(mapCoins, pindex); } +bool CCoinsViewBacked::GetStats(CCoinsStats &stats) { return base->GetStats(stats); } + +CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), pindexTip(NULL) { } + +bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) { + if (cacheCoins.count(txid)) { + coins = cacheCoins[txid]; + return true; + } + if (base->GetCoins(txid, coins)) { + cacheCoins[txid] = coins; + return true; + } + return false; +} + +std::map::iterator CCoinsViewCache::FetchCoins(const uint256 &txid) { + std::map::iterator it = cacheCoins.lower_bound(txid); + if (it != cacheCoins.end() && it->first == txid) + return it; + CCoins tmp; + if (!base->GetCoins(txid,tmp)) + return cacheCoins.end(); + std::map::iterator ret = cacheCoins.insert(it, std::make_pair(txid, CCoins())); + tmp.swap(ret->second); + return ret; +} + +CCoins &CCoinsViewCache::GetCoins(const uint256 &txid) { + std::map::iterator it = FetchCoins(txid); + assert(it != cacheCoins.end()); + return it->second; +} + +bool CCoinsViewCache::SetCoins(const uint256 &txid, const CCoins &coins) { + cacheCoins[txid] = coins; + return true; +} + +bool CCoinsViewCache::HaveCoins(const uint256 &txid) { + return FetchCoins(txid) != cacheCoins.end(); +} + +CBlockIndex *CCoinsViewCache::GetBestBlock() { + if (pindexTip == NULL) + pindexTip = base->GetBestBlock(); + return pindexTip; +} + +bool CCoinsViewCache::SetBestBlock(CBlockIndex *pindex) { + pindexTip = pindex; + return true; +} + +bool CCoinsViewCache::BatchWrite(const std::map &mapCoins, CBlockIndex *pindex) { + for (std::map::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) + cacheCoins[it->first] = it->second; + pindexTip = pindex; + return true; +} + +bool CCoinsViewCache::Flush() { + bool fOk = base->BatchWrite(cacheCoins, pindexTip); + if (fOk) + cacheCoins.clear(); + return fOk; +} + +unsigned int CCoinsViewCache::GetCacheSize() { + return cacheCoins.size(); +} + +/** CCoinsView that brings transactions from a memorypool into view. + It does not check for spendings by memory pool transactions. */ +CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } + +bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) { + if (base->GetCoins(txid, coins)) + return true; + if (mempool.exists(txid)) { + const CTransaction &tx = mempool.lookup(txid); + coins = CCoins(tx, MEMPOOL_HEIGHT); + return true; + } + return false; +} + +bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) { + return mempool.exists(txid) || base->HaveCoins(txid); +} + +CCoinsViewCache *pcoinsTip = NULL; +CBlockTreeDB *pblocktree = NULL; + +////////////////////////////////////////////////////////////////////////////// +// +// mapOrphanTransactions +// + +bool AddOrphanTx(const CTransaction& tx) +{ + uint256 hash = tx.GetHash(); + if (mapOrphanTransactions.count(hash)) + return false; + + // Ignore big transactions, to avoid a + // send-big-orphans memory exhaustion attack. If a peer has a legitimate + // large transaction with a missing parent then we assume + // it will rebroadcast it later, after the parent transaction(s) + // have been mined or received. + // 10,000 orphans, each of which is at most 5,000 bytes big is + // at most 500 megabytes of orphans: + unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); + if (sz > 5000) + { + printf("ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString().c_str()); + return false; + } + + mapOrphanTransactions[hash] = tx; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash); + + printf("stored orphan tx %s (mapsz %"PRIszu")\n", hash.ToString().c_str(), + mapOrphanTransactions.size()); + return true; +} + +void static EraseOrphanTx(uint256 hash) +{ + if (!mapOrphanTransactions.count(hash)) + return; + const CTransaction& tx = mapOrphanTransactions[hash]; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + mapOrphanTransactionsByPrev[txin.prevout.hash].erase(hash); + if (mapOrphanTransactionsByPrev[txin.prevout.hash].empty()) + mapOrphanTransactionsByPrev.erase(txin.prevout.hash); + } + mapOrphanTransactions.erase(hash); +} + +unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) +{ + unsigned int nEvicted = 0; + while (mapOrphanTransactions.size() > nMaxOrphans) + { + // Evict a random orphan: + uint256 randomhash = GetRandHash(); + map::iterator it = mapOrphanTransactions.lower_bound(randomhash); + if (it == mapOrphanTransactions.end()) + it = mapOrphanTransactions.begin(); + EraseOrphanTx(it->first); + ++nEvicted; + } + return nEvicted; +} + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CTransaction / CTxOut +// + +bool CTxOut::IsDust() const +{ + // CryptoLottoToken: IsDust() detection disabled, allows any valid dust to be relayed. + // The fees imposed on each dust txo is considered sufficient spam deterrant. + return false; +} + +bool CTransaction::IsStandard(string& strReason) const +{ + if (nVersion > CTransaction::CURRENT_VERSION || nVersion < 1) { + strReason = "version"; + return false; + } + + if (!IsFinal()) { + strReason = "not-final"; + return false; + } + + // Extremely large transactions with lots of inputs can cost the network + // almost as much to process as they cost the sender in fees, because + // computing signature hashes is O(ninputs*txsize). Limiting transactions + // to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks. + unsigned int sz = this->GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); + if (sz >= MAX_STANDARD_TX_SIZE) { + strReason = "tx-size"; + return false; + } + + BOOST_FOREACH(const CTxIn& txin, vin) + { + // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG + // pay-to-script-hash, which is 3 ~80-byte signatures, 3 + // ~65-byte public keys, plus a few script ops. + if (txin.scriptSig.size() > 500) { + strReason = "scriptsig-size"; + return false; + } + if (!txin.scriptSig.IsPushOnly()) { + strReason = "scriptsig-not-pushonly"; + return false; + } + } + BOOST_FOREACH(const CTxOut& txout, vout) { + if (!::IsStandard(txout.scriptPubKey)) { + strReason = "scriptpubkey"; + return false; + } + if (txout.IsDust()) { + strReason = "dust"; + return false; + } + } + return true; +} + +// +// Check transaction inputs, and make sure any +// pay-to-script-hash transactions are evaluating IsStandard scripts +// +// Why bother? To avoid denial-of-service attacks; an attacker +// can submit a standard HASH... OP_EQUAL transaction, +// which will get accepted into blocks. The redemption +// script can be anything; an attacker could use a very +// expensive-to-check-upon-redemption script like: +// DUP CHECKSIG DROP ... repeated 100 times... OP_1 +// +bool CTransaction::AreInputsStandard(CCoinsViewCache& mapInputs) const +{ + if (IsCoinBase()) + return true; // Coinbases don't use vin normally + + for (unsigned int i = 0; i < vin.size(); i++) + { + const CTxOut& prev = GetOutputFor(vin[i], mapInputs); + + vector > vSolutions; + txnouttype whichType; + // get the scriptPubKey corresponding to this input: + const CScript& prevScript = prev.scriptPubKey; + if (!Solver(prevScript, whichType, vSolutions)) + return false; + int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions); + if (nArgsExpected < 0) + return false; + + // Transactions with extra stuff in their scriptSigs are + // non-standard. Note that this EvalScript() call will + // be quick, because if there are any operations + // beside "push data" in the scriptSig the + // IsStandard() call returns false + vector > stack; + if (!EvalScript(stack, vin[i].scriptSig, *this, i, false, 0)) + return false; + + if (whichType == TX_SCRIPTHASH) + { + if (stack.empty()) + return false; + CScript subscript(stack.back().begin(), stack.back().end()); + vector > vSolutions2; + txnouttype whichType2; + if (!Solver(subscript, whichType2, vSolutions2)) + return false; + if (whichType2 == TX_SCRIPTHASH) + return false; + + int tmpExpected; + tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2); + if (tmpExpected < 0) + return false; + nArgsExpected += tmpExpected; + } + + if (stack.size() != (unsigned int)nArgsExpected) + return false; + } + + return true; +} + +unsigned int CTransaction::GetLegacySigOpCount() const +{ + unsigned int nSigOps = 0; + BOOST_FOREACH(const CTxIn& txin, vin) + { + nSigOps += txin.scriptSig.GetSigOpCount(false); + } + BOOST_FOREACH(const CTxOut& txout, vout) + { + nSigOps += txout.scriptPubKey.GetSigOpCount(false); + } + return nSigOps; +} + + +int CMerkleTx::SetMerkleBranch(const CBlock* pblock) +{ + CBlock blockTmp; + + if (pblock == NULL) { + CCoins coins; + if (pcoinsTip->GetCoins(GetHash(), coins)) { + CBlockIndex *pindex = FindBlockByHeight(coins.nHeight); + if (pindex) { + if (!blockTmp.ReadFromDisk(pindex)) + return 0; + pblock = &blockTmp; + } + } + } + + if (pblock) { + // Update the tx's hashBlock + hashBlock = pblock->GetHash(); + + // Locate the transaction + for (nIndex = 0; nIndex < (int)pblock->vtx.size(); nIndex++) + if (pblock->vtx[nIndex] == *(CTransaction*)this) + break; + if (nIndex == (int)pblock->vtx.size()) + { + vMerkleBranch.clear(); + nIndex = -1; + printf("ERROR: SetMerkleBranch() : couldn't find tx in block\n"); + return 0; + } + + // Fill in merkle branch + vMerkleBranch = pblock->GetMerkleBranch(nIndex); + } + + // Is the tx in a block that's in the main chain + map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi == mapBlockIndex.end()) + return 0; + CBlockIndex* pindex = (*mi).second; + if (!pindex || !pindex->IsInMainChain()) + return 0; + + return pindexBest->nHeight - pindex->nHeight + 1; +} + + + + + + + +bool CTransaction::CheckTransaction(CValidationState &state) const +{ + // Basic checks that don't depend on any context + if (vin.empty()) + return state.DoS(10, error("CTransaction::CheckTransaction() : vin empty")); + if (vout.empty()) + return state.DoS(10, error("CTransaction::CheckTransaction() : vout empty")); + // Size limits + if (::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) + return state.DoS(100, error("CTransaction::CheckTransaction() : size limits failed")); + + // Check for negative or overflow output values + int64 nValueOut = 0; + BOOST_FOREACH(const CTxOut& txout, vout) + { + if (txout.nValue < 0) + return state.DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative")); + if (txout.nValue > MAX_MONEY) + return state.DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high")); + nValueOut += txout.nValue; + if (!MoneyRange(nValueOut)) + return state.DoS(100, error("CTransaction::CheckTransaction() : txout total out of range")); + } + + // Check for duplicate inputs + set vInOutPoints; + BOOST_FOREACH(const CTxIn& txin, vin) + { + if (vInOutPoints.count(txin.prevout)) + return state.DoS(100, error("CTransaction::CheckTransaction() : duplicate inputs")); + vInOutPoints.insert(txin.prevout); + } + + if (IsCoinBase()) + { + if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) + return state.DoS(100, error("CTransaction::CheckTransaction() : coinbase script size")); + } + else + { + BOOST_FOREACH(const CTxIn& txin, vin) + if (txin.prevout.IsNull()) + return state.DoS(10, error("CTransaction::CheckTransaction() : prevout is null")); + } + + return true; +} + +int64 CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree, + enum GetMinFee_mode mode) const +{ + // Base fee is either nMinTxFee or nMinRelayTxFee + int64 nBaseFee = (mode == GMF_RELAY) ? nMinRelayTxFee : nMinTxFee; + + unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION); + unsigned int nNewBlockSize = nBlockSize + nBytes; + int64 nMinFee = (1 + (int64)nBytes / 1000) * nBaseFee; + + if (fAllowFree) + { + if (nBlockSize == 1) + { + // Transactions under 10K are free + // (about 4500bc if made of 50bc inputs) + if (nBytes < 10000) + nMinFee = 0; + } + else + { + // Free transaction area + if (nNewBlockSize < 27000) + nMinFee = 0; + } +#if 0 + // There is a free transaction area in blocks created by most miners, + // * If we are relaying we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 1000 + // to be considered to fall into this category. We don't want to encourage sending + // multiple transactions instead of one big transaction to avoid fees. + // * If we are creating a transaction we allow transactions up to 5,000 bytes + // to be considered safe and assume they can likely make it into this section. + if (nBytes < (mode == GMF_SEND ? 5000 : (DEFAULT_BLOCK_PRIORITY_SIZE - 1000))) + nMinFee = 0; +#endif + } + + // CryptoLottoToken + // To limit dust spam, add nBaseFee for each output less than DUST_SOFT_LIMIT + BOOST_FOREACH(const CTxOut& txout, vout) + if (txout.nValue < DUST_SOFT_LIMIT) + nMinFee += nBaseFee; + + // Raise the price as the block approaches full + if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2) + { + if (nNewBlockSize >= MAX_BLOCK_SIZE_GEN) + return MAX_MONEY; + nMinFee *= MAX_BLOCK_SIZE_GEN / (MAX_BLOCK_SIZE_GEN - nNewBlockSize); + } + + if (!MoneyRange(nMinFee)) + nMinFee = MAX_MONEY; + return nMinFee; +} + +void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins) +{ + LOCK(cs); + + std::map::iterator it = mapNextTx.lower_bound(COutPoint(hashTx, 0)); + + // iterate over all COutPoints in mapNextTx whose hash equals the provided hashTx + while (it != mapNextTx.end() && it->first.hash == hashTx) { + coins.Spend(it->first.n); // and remove those outputs from coins + it++; + } +} + +bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckInputs, bool fLimitFree, + bool* pfMissingInputs) +{ + if (pfMissingInputs) + *pfMissingInputs = false; + + if (!tx.CheckTransaction(state)) + return error("CTxMemPool::accept() : CheckTransaction failed"); + + // Coinbase is only valid in a block, not as a loose transaction + if (tx.IsCoinBase()) + return state.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx")); + + // To help v0.1.5 clients who would see it as a negative number + if ((int64)tx.nLockTime > std::numeric_limits::max()) + return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet"); + + // Rather not work on nonstandard transactions (unless -testnet) + string strNonStd; + if (!fTestNet && !tx.IsStandard(strNonStd)) + return error("CTxMemPool::accept() : nonstandard transaction (%s)", + strNonStd.c_str()); + + // is it already in the memory pool? + uint256 hash = tx.GetHash(); + { + LOCK(cs); + if (mapTx.count(hash)) + return false; + } + + // Check for conflicts with in-memory transactions + CTransaction* ptxOld = NULL; + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + COutPoint outpoint = tx.vin[i].prevout; + if (mapNextTx.count(outpoint)) + { + // Disable replacement feature for now + return false; + + // Allow replacing with a newer version of the same transaction + if (i != 0) + return false; + ptxOld = mapNextTx[outpoint].ptx; + if (ptxOld->IsFinal()) + return false; + if (!tx.IsNewerThan(*ptxOld)) + return false; + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + COutPoint outpoint = tx.vin[i].prevout; + if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld) + return false; + } + break; + } + } + + if (fCheckInputs) + { + CCoinsView dummy; + CCoinsViewCache view(dummy); + + { + LOCK(cs); + CCoinsViewMemPool viewMemPool(*pcoinsTip, *this); + view.SetBackend(viewMemPool); + + // do we already have it? + if (view.HaveCoins(hash)) + return false; + + // do all inputs exist? + // Note that this does not check for the presence of actual outputs (see the next check for that), + // only helps filling in pfMissingInputs (to determine missing vs spent). + BOOST_FOREACH(const CTxIn txin, tx.vin) { + if (!view.HaveCoins(txin.prevout.hash)) { + if (pfMissingInputs) + *pfMissingInputs = true; + return false; + } + } + + // are the actual inputs available? + if (!tx.HaveInputs(view)) + return state.Invalid(error("CTxMemPool::accept() : inputs already spent")); + + // Bring the best block into scope + view.GetBestBlock(); + + // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool + view.SetBackend(dummy); + } + + // Check for non-standard pay-to-script-hash in inputs + if (!tx.AreInputsStandard(view) && !fTestNet) + return error("CTxMemPool::accept() : nonstandard transaction input"); + + // Note: if you modify this code to accept non-standard transactions, then + // you should add code here to check that the transaction does a + // reasonable number of ECDSA signature verifications. + + int64 nFees = tx.GetValueIn(view)-tx.GetValueOut(); + unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + + // Don't accept it if it can't get into a block + int64 txMinFee = tx.GetMinFee(1000, true, GMF_RELAY); + if (fLimitFree && nFees < txMinFee) + return error("CTxMemPool::accept() : not enough fees %s, %"PRI64d" < %"PRI64d, + hash.ToString().c_str(), + nFees, txMinFee); + + // Continuously rate-limit free transactions + // This mitigates 'penny-flooding' -- sending thousands of free transactions just to + // be annoying or make others' transactions take longer to confirm. + if (fLimitFree && nFees < CTransaction::nMinRelayTxFee) + { + static double dFreeCount; + static int64 nLastTime; + int64 nNow = GetTime(); + + LOCK(cs); + + // Use an exponentially decaying ~10-minute window: + dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); + nLastTime = nNow; + // -limitfreerelay unit is thousand-bytes-per-minute + // At default rate it would take over a month to fill 1GB + if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000) + return error("CTxMemPool::accept() : free transaction rejected by rate limiter"); + if (fDebug) + printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); + dFreeCount += nSize; + } + + // Check against previous transactions + // This is done last to help prevent CPU exhaustion denial-of-service attacks. + if (!tx.CheckInputs(state, view, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC)) + { + return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().c_str()); + } + } + + // Store transaction in memory + { + LOCK(cs); + if (ptxOld) + { + printf("CTxMemPool::accept() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); + remove(*ptxOld); + } + addUnchecked(hash, tx); + } + + ///// are we sure this is ok when loading transactions or restoring block txes + // If updated, erase old tx from wallet + if (ptxOld) + EraseFromWallets(ptxOld->GetHash()); + SyncWithWallets(hash, tx, NULL, true); + + return true; +} + +bool CTransaction::AcceptToMemoryPool(CValidationState &state, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs) +{ + try { + return mempool.accept(state, *this, fCheckInputs, fLimitFree, pfMissingInputs); + } catch(std::runtime_error &e) { + return state.Abort(_("System error: ") + e.what()); + } +} + +bool CTxMemPool::addUnchecked(const uint256& hash, const CTransaction &tx) +{ + // Add to memory pool without checking anything. Don't call this directly, + // call CTxMemPool::accept to properly check the transaction first. + { + mapTx[hash] = tx; + for (unsigned int i = 0; i < tx.vin.size(); i++) + mapNextTx[tx.vin[i].prevout] = CInPoint(&mapTx[hash], i); + nTransactionsUpdated++; + } + return true; +} + + +bool CTxMemPool::remove(const CTransaction &tx, bool fRecursive) +{ + // Remove transaction from memory pool + { + LOCK(cs); + uint256 hash = tx.GetHash(); + if (fRecursive) { + for (unsigned int i = 0; i < tx.vout.size(); i++) { + std::map::iterator it = mapNextTx.find(COutPoint(hash, i)); + if (it != mapNextTx.end()) + remove(*it->second.ptx, true); + } + } + if (mapTx.count(hash)) + { + BOOST_FOREACH(const CTxIn& txin, tx.vin) + mapNextTx.erase(txin.prevout); + mapTx.erase(hash); + nTransactionsUpdated++; + } + } + return true; +} + +bool CTxMemPool::removeConflicts(const CTransaction &tx) +{ + // Remove transactions which depend on inputs of tx, recursively + LOCK(cs); + BOOST_FOREACH(const CTxIn &txin, tx.vin) { + std::map::iterator it = mapNextTx.find(txin.prevout); + if (it != mapNextTx.end()) { + const CTransaction &txConflict = *it->second.ptx; + if (txConflict != tx) + remove(txConflict, true); + } + } + return true; +} + +void CTxMemPool::clear() +{ + LOCK(cs); + mapTx.clear(); + mapNextTx.clear(); + ++nTransactionsUpdated; +} + +void CTxMemPool::queryHashes(std::vector& vtxid) +{ + vtxid.clear(); + + LOCK(cs); + vtxid.reserve(mapTx.size()); + for (map::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) + vtxid.push_back((*mi).first); +} + + + + +int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const +{ + if (hashBlock == 0 || nIndex == -1) + return 0; + + // Find the block it claims to be in + map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi == mapBlockIndex.end()) + return 0; + CBlockIndex* pindex = (*mi).second; + if (!pindex || !pindex->IsInMainChain()) + return 0; + + // Make sure the merkle branch connects to this block + if (!fMerkleVerified) + { + if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) + return 0; + fMerkleVerified = true; + } + + pindexRet = pindex; + return pindexBest->nHeight - pindex->nHeight + 1; +} + + +int CMerkleTx::GetBlocksToMaturity() const +{ + if (!IsCoinBase()) + return 0; + return max(0, (COINBASE_MATURITY+20) - GetDepthInMainChain()); +} + + +bool CMerkleTx::AcceptToMemoryPool(bool fCheckInputs, bool fLimitFree) +{ + CValidationState state; + return CTransaction::AcceptToMemoryPool(state, fCheckInputs, fLimitFree); +} + + + +bool CWalletTx::AcceptWalletTransaction(bool fCheckInputs) +{ + { + LOCK(mempool.cs); + // Add previous supporting transactions first + BOOST_FOREACH(CMerkleTx& tx, vtxPrev) + { + if (!tx.IsCoinBase()) + { + uint256 hash = tx.GetHash(); + if (!mempool.exists(hash) && pcoinsTip->HaveCoins(hash)) + tx.AcceptToMemoryPool(fCheckInputs, false); + } + } + return AcceptToMemoryPool(fCheckInputs, false); + } + return false; +} + + +// Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock +bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow) +{ + CBlockIndex *pindexSlow = NULL; + { + LOCK(cs_main); + { + LOCK(mempool.cs); + if (mempool.exists(hash)) + { + txOut = mempool.lookup(hash); + return true; + } + } + + if (fTxIndex) { + CDiskTxPos postx; + if (pblocktree->ReadTxIndex(hash, postx)) { + CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION); + CBlockHeader header; + try { + file >> header; + fseek(file, postx.nTxOffset, SEEK_CUR); + file >> txOut; + } catch (std::exception &e) { + return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); + } + hashBlock = header.GetHash(); + if (txOut.GetHash() != hash) + return error("%s() : txid mismatch", __PRETTY_FUNCTION__); + return true; + } + } + + if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it + int nHeight = -1; + { + CCoinsViewCache &view = *pcoinsTip; + CCoins coins; + if (view.GetCoins(hash, coins)) + nHeight = coins.nHeight; + } + if (nHeight > 0) + pindexSlow = FindBlockByHeight(nHeight); + } + } + + if (pindexSlow) { + CBlock block; + if (block.ReadFromDisk(pindexSlow)) { + BOOST_FOREACH(const CTransaction &tx, block.vtx) { + if (tx.GetHash() == hash) { + txOut = tx; + hashBlock = pindexSlow->GetBlockHash(); + return true; + } + } + } + } + + return false; +} + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CBlock and CBlockIndex +// + +static CBlockIndex* pblockindexFBBHLast; +CBlockIndex* FindBlockByHeight(int nHeight) +{ + CBlockIndex *pblockindex; + if (nHeight < nBestHeight / 2) + pblockindex = pindexGenesisBlock; + else + pblockindex = pindexBest; + if (pblockindexFBBHLast && abs(nHeight - pblockindex->nHeight) > abs(nHeight - pblockindexFBBHLast->nHeight)) + pblockindex = pblockindexFBBHLast; + while (pblockindex->nHeight > nHeight) + pblockindex = pblockindex->pprev; + while (pblockindex->nHeight < nHeight) + pblockindex = pblockindex->pnext; + pblockindexFBBHLast = pblockindex; + return pblockindex; +} + +bool CBlock::ReadFromDisk(const CBlockIndex* pindex) +{ + if (!ReadFromDisk(pindex->GetBlockPos())) + return false; + if (GetHash() != pindex->GetBlockHash()) + return error("CBlock::ReadFromDisk() : GetHash() doesn't match index"); + return true; +} + +uint256 static GetOrphanRoot(const CBlockHeader* pblock) +{ + // Work back to the first block in the orphan chain + while (mapOrphanBlocks.count(pblock->hashPrevBlock)) + pblock = mapOrphanBlocks[pblock->hashPrevBlock]; + return pblock->GetHash(); +} + +int static generateMTRandom(unsigned int s, int range) +{ + boost::mt19937 gen(s); + boost::uniform_int<> dist(1, range); + return dist(gen); +} + +int64 static GetBlockValue(int nHeight, int64 nFees, uint256 prevHash) +{ + int64 nSubsidy = 100 * COIN; + + if(nHeight == 1) + { + nSubsidy = 1000000 * COIN; + } + // Subsidy halving + nSubsidy >>= (nHeight / 300000); + + if (nHeight > 300000*64) + return nFees; + + return nSubsidy + nFees; +} + +// Legacy +static const int64 nTargetTimespan = 4 * 60 * 60; +static const int64 nTargetSpacing = 3 * 60; + +// +// minimum amount of work that could possibly be required nTime after +// minimum work required was nBase +// +unsigned int ComputeMinWork(unsigned int nBase, int64 nTime) +{ + // Testnet has min-difficulty blocks + // after nTargetSpacing*2 time between blocks: + if (fTestNet && nTime > nTargetSpacing*2) + return bnProofOfWorkLimit.GetCompact(); + + CBigNum bnResult; + bnResult.SetCompact(nBase); + while (nTime > 0 && bnResult < bnProofOfWorkLimit) + { + // Maximum 400% adjustment... + bnResult *= 4; + // ... in best-case exactly 4-times-normal target time + nTime -= nTargetTimespan*4; + } + if (bnResult > bnProofOfWorkLimit) + bnResult = bnProofOfWorkLimit; + return bnResult.GetCompact(); +} + +unsigned int static DarkGravityWave(const CBlockIndex* pindexLast, const CBlockHeader *pblock) { + /* current difficulty formula, dash - DarkGravity v3, written by Evan Duffield - evan@dashpay.io */ + const CBlockIndex *BlockLastSolved = pindexLast; + const CBlockIndex *BlockReading = pindexLast; + int64_t nActualTimespan = 0; + int64_t LastBlockTime = 0; + int64_t PastBlocksMin = 24; + int64_t PastBlocksMax = 24; + int64_t CountBlocks = 0; + CBigNum PastDifficultyAverage; + CBigNum PastDifficultyAveragePrev; + + if (BlockLastSolved == NULL || BlockLastSolved->nHeight == 0 || BlockLastSolved->nHeight < PastBlocksMin) { + return bnProofOfWorkLimit.GetCompact(); + } + + for (unsigned int i = 1; BlockReading && BlockReading->nHeight > 0; i++) { + if (PastBlocksMax > 0 && i > PastBlocksMax) { break; } + CountBlocks++; + + if(CountBlocks <= PastBlocksMin) { + if (CountBlocks == 1) { PastDifficultyAverage.SetCompact(BlockReading->nBits); } + else { PastDifficultyAverage = ((PastDifficultyAveragePrev * CountBlocks)+(CBigNum().SetCompact(BlockReading->nBits))) / (CountBlocks+1); } + PastDifficultyAveragePrev = PastDifficultyAverage; + } + + if(LastBlockTime > 0){ + int64_t Diff = (LastBlockTime - BlockReading->GetBlockTime()); + nActualTimespan += Diff; + } + LastBlockTime = BlockReading->GetBlockTime(); + + if (BlockReading->pprev == NULL) { assert(BlockReading); break; } + BlockReading = BlockReading->pprev; + } + + CBigNum bnNew(PastDifficultyAverage); + + int64_t _nTargetTimespan = CountBlocks*nTargetSpacing; + + if (nActualTimespan < _nTargetTimespan/3) + nActualTimespan = _nTargetTimespan/3; + if (nActualTimespan > _nTargetTimespan*3) + nActualTimespan = _nTargetTimespan*3; + + // Retarget + bnNew *= nActualTimespan; + bnNew /= _nTargetTimespan; + + if (bnNew > bnProofOfWorkLimit){ + bnNew = bnProofOfWorkLimit; + } + + return bnNew.GetCompact(); +} + +unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock) +{ + return DarkGravityWave(pindexLast, pblock); +} + +bool CheckProofOfWork(uint256 hash, unsigned int nBits) +{ + CBigNum bnTarget; + bnTarget.SetCompact(nBits); + + // Check range + if (bnTarget <= 0 || bnTarget > bnProofOfWorkLimit) + return error("CheckProofOfWork() : nBits below minimum work"); + + // Check proof of work matches claimed amount + + if (hash > bnTarget.getuint256()) + return error("CheckProofOfWork() : hash doesn't match nBits"); + + return true; +} + +// Return maximum amount of blocks that other nodes claim to have +int GetNumBlocksOfPeers() +{ + return std::max(cPeerBlockCounts.median(), Checkpoints::GetTotalBlocksEstimate()); +} + +bool IsInitialBlockDownload() +{ + if (pindexBest == NULL || fImporting || fReindex || nBestHeight < Checkpoints::GetTotalBlocksEstimate()) + return true; + static int64 nLastUpdate; + static CBlockIndex* pindexLastBest; + if (pindexBest != pindexLastBest) + { + pindexLastBest = pindexBest; + nLastUpdate = GetTime(); + } + return (GetTime() - nLastUpdate < 10 && + pindexBest->GetBlockTime() < GetTime() - 24 * 60 * 60); +} + +void static InvalidChainFound(CBlockIndex* pindexNew) +{ + if (pindexNew->nChainWork > nBestInvalidWork) + { + nBestInvalidWork = pindexNew->nChainWork; + pblocktree->WriteBestInvalidWork(CBigNum(nBestInvalidWork)); + uiInterface.NotifyBlocksChanged(); + } + printf("InvalidChainFound: invalid block=%s height=%d log2_work=%.8g date=%s\n", + pindexNew->GetBlockHash().ToString().c_str(), pindexNew->nHeight, + log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", + pindexNew->GetBlockTime()).c_str()); + printf("InvalidChainFound: current best=%s height=%d log2_work=%.8g date=%s\n", + hashBestChain.ToString().c_str(), nBestHeight, log(nBestChainWork.getdouble())/log(2.0), + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str()); + if (pindexBest && nBestInvalidWork > nBestChainWork + (pindexBest->GetBlockWork() * 6).getuint256()) + printf("InvalidChainFound: Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.\n"); +} + +void static InvalidBlockFound(CBlockIndex *pindex) { + pindex->nStatus |= BLOCK_FAILED_VALID; + pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex)); + setBlockIndexValid.erase(pindex); + InvalidChainFound(pindex); + if (pindex->pnext) { + CValidationState stateDummy; + ConnectBestBlock(stateDummy); // reorganise away from the failed block + } +} + +bool ConnectBestBlock(CValidationState &state) { + do { + CBlockIndex *pindexNewBest; + + { + std::set::reverse_iterator it = setBlockIndexValid.rbegin(); + if (it == setBlockIndexValid.rend()) + return true; + pindexNewBest = *it; + } + + if (pindexNewBest == pindexBest || (pindexBest && pindexNewBest->nChainWork == pindexBest->nChainWork)) + return true; // nothing to do + + // check ancestry + CBlockIndex *pindexTest = pindexNewBest; + std::vector vAttach; + do { + if (pindexTest->nStatus & BLOCK_FAILED_MASK) { + // mark descendants failed + CBlockIndex *pindexFailed = pindexNewBest; + while (pindexTest != pindexFailed) { + pindexFailed->nStatus |= BLOCK_FAILED_CHILD; + setBlockIndexValid.erase(pindexFailed); + pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexFailed)); + pindexFailed = pindexFailed->pprev; + } + InvalidChainFound(pindexNewBest); + break; + } + + if (pindexBest == NULL || pindexTest->nChainWork > pindexBest->nChainWork) + vAttach.push_back(pindexTest); + + if (pindexTest->pprev == NULL || pindexTest->pnext != NULL) { + reverse(vAttach.begin(), vAttach.end()); + BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) { + boost::this_thread::interruption_point(); + try { + if (!SetBestChain(state, pindexSwitch)) + return false; + } catch(std::runtime_error &e) { + return state.Abort(_("System error: ") + e.what()); + } + } + return true; + } + pindexTest = pindexTest->pprev; + } while(true); + } while(true); +} + +void CBlockHeader::UpdateTime(const CBlockIndex* pindexPrev) +{ + nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + + // Updating time can change work required on testnet: + if (fTestNet) + nBits = GetNextWorkRequired(pindexPrev, this); +} + + + + + + + + + + + +const CTxOut &CTransaction::GetOutputFor(const CTxIn& input, CCoinsViewCache& view) +{ + const CCoins &coins = view.GetCoins(input.prevout.hash); + assert(coins.IsAvailable(input.prevout.n)); + return coins.vout[input.prevout.n]; +} + +int64 CTransaction::GetValueIn(CCoinsViewCache& inputs) const +{ + if (IsCoinBase()) + return 0; + + int64 nResult = 0; + for (unsigned int i = 0; i < vin.size(); i++) + nResult += GetOutputFor(vin[i], inputs).nValue; + + return nResult; +} + +unsigned int CTransaction::GetP2SHSigOpCount(CCoinsViewCache& inputs) const +{ + if (IsCoinBase()) + return 0; + + unsigned int nSigOps = 0; + for (unsigned int i = 0; i < vin.size(); i++) + { + const CTxOut &prevout = GetOutputFor(vin[i], inputs); + if (prevout.scriptPubKey.IsPayToScriptHash()) + nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig); + } + return nSigOps; +} + +void CTransaction::UpdateCoins(CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) const +{ + // mark inputs spent + if (!IsCoinBase()) { + BOOST_FOREACH(const CTxIn &txin, vin) { + CCoins &coins = inputs.GetCoins(txin.prevout.hash); + CTxInUndo undo; + assert(coins.Spend(txin.prevout, undo)); + txundo.vprevout.push_back(undo); + } + } + + // add outputs + assert(inputs.SetCoins(txhash, CCoins(*this, nHeight))); +} + +bool CTransaction::HaveInputs(CCoinsViewCache &inputs) const +{ + if (!IsCoinBase()) { + // first check whether information about the prevout hash is available + for (unsigned int i = 0; i < vin.size(); i++) { + const COutPoint &prevout = vin[i].prevout; + if (!inputs.HaveCoins(prevout.hash)) + return false; + } + + // then check whether the actual outputs are available + for (unsigned int i = 0; i < vin.size(); i++) { + const COutPoint &prevout = vin[i].prevout; + const CCoins &coins = inputs.GetCoins(prevout.hash); + if (!coins.IsAvailable(prevout.n)) + return false; + } + } + return true; +} + +bool CScriptCheck::operator()() const { + const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; + if (!VerifyScript(scriptSig, scriptPubKey, *ptxTo, nIn, nFlags, nHashType)) + return error("CScriptCheck() : %s VerifySignature failed", ptxTo->GetHash().ToString().c_str()); + return true; +} + +bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType) +{ + return CScriptCheck(txFrom, txTo, nIn, flags, nHashType)(); +} + +bool CTransaction::CheckInputs(CValidationState &state, CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector *pvChecks) const +{ + if (!IsCoinBase()) + { + if (pvChecks) + pvChecks->reserve(vin.size()); + + // This doesn't trigger the DoS code on purpose; if it did, it would make it easier + // for an attacker to attempt to split the network. + if (!HaveInputs(inputs)) + return state.Invalid(error("CheckInputs() : %s inputs unavailable", GetHash().ToString().c_str())); + + // While checking, GetBestBlock() refers to the parent block. + // This is also true for mempool checks. + int nSpendHeight = inputs.GetBestBlock()->nHeight + 1; + int64 nValueIn = 0; + int64 nFees = 0; + for (unsigned int i = 0; i < vin.size(); i++) + { + const COutPoint &prevout = vin[i].prevout; + const CCoins &coins = inputs.GetCoins(prevout.hash); + + // If prev is coinbase, check that it's matured + if (coins.IsCoinBase()) { + if (nSpendHeight - coins.nHeight < COINBASE_MATURITY) + return state.Invalid(error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight)); + } + + // Check for negative or overflow input values + nValueIn += coins.vout[prevout.n].nValue; + if (!MoneyRange(coins.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) + return state.DoS(100, error("CheckInputs() : txin values out of range")); + + } + + if (nValueIn < GetValueOut()) + return state.DoS(100, error("CheckInputs() : %s value in < value out", GetHash().ToString().c_str())); + + // Tally transaction fees + int64 nTxFee = nValueIn - GetValueOut(); + if (nTxFee < 0) + return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", GetHash().ToString().c_str())); + nFees += nTxFee; + if (!MoneyRange(nFees)) + return state.DoS(100, error("CheckInputs() : nFees out of range")); + + // The first loop above does all the inexpensive checks. + // Only if ALL inputs pass do we perform expensive ECDSA signature checks. + // Helps prevent CPU exhaustion attacks. + + // Skip ECDSA signature verification when connecting blocks + // before the last block chain checkpoint. This is safe because block merkle hashes are + // still computed and checked, and any change will be caught at the next checkpoint. + if (fScriptChecks) { + for (unsigned int i = 0; i < vin.size(); i++) { + const COutPoint &prevout = vin[i].prevout; + const CCoins &coins = inputs.GetCoins(prevout.hash); + + // Verify signature + CScriptCheck check(coins, *this, i, flags, 0); + if (pvChecks) { + pvChecks->push_back(CScriptCheck()); + check.swap(pvChecks->back()); + } else if (!check()) { + if (flags & SCRIPT_VERIFY_STRICTENC) { + // For now, check whether the failure was caused by non-canonical + // encodings or not; if so, don't trigger DoS protection. + CScriptCheck check(coins, *this, i, flags & (~SCRIPT_VERIFY_STRICTENC), 0); + if (check()) + return state.Invalid(); + } + return state.DoS(100,false); + } + } + } + } + + return true; +} + + + + +bool CBlock::DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &view, bool *pfClean) +{ + assert(pindex == view.GetBestBlock()); + + if (pfClean) + *pfClean = false; + + bool fClean = true; + + CBlockUndo blockUndo; + CDiskBlockPos pos = pindex->GetUndoPos(); + if (pos.IsNull()) + return error("DisconnectBlock() : no undo data available"); + if (!blockUndo.ReadFromDisk(pos, pindex->pprev->GetBlockHash())) + return error("DisconnectBlock() : failure reading undo data"); + + if (blockUndo.vtxundo.size() + 1 != vtx.size()) + return error("DisconnectBlock() : block and undo data inconsistent"); + + // undo transactions in reverse order + for (int i = vtx.size() - 1; i >= 0; i--) { + const CTransaction &tx = vtx[i]; + uint256 hash = tx.GetHash(); + + // check that all outputs are available + if (!view.HaveCoins(hash)) { + fClean = fClean && error("DisconnectBlock() : outputs still spent? database corrupted"); + view.SetCoins(hash, CCoins()); + } + CCoins &outs = view.GetCoins(hash); + + CCoins outsBlock = CCoins(tx, pindex->nHeight); + // The CCoins serialization does not serialize negative numbers. + // No network rules currently depend on the version here, so an inconsistency is harmless + // but it must be corrected before txout nversion ever influences a network rule. + if (outsBlock.nVersion < 0) + outs.nVersion = outsBlock.nVersion; + if (outs != outsBlock) + fClean = fClean && error("DisconnectBlock() : added transaction mismatch? database corrupted"); + + // remove outputs + outs = CCoins(); + + // restore inputs + if (i > 0) { // not coinbases + const CTxUndo &txundo = blockUndo.vtxundo[i-1]; + if (txundo.vprevout.size() != tx.vin.size()) + return error("DisconnectBlock() : transaction and undo data inconsistent"); + for (unsigned int j = tx.vin.size(); j-- > 0;) { + const COutPoint &out = tx.vin[j].prevout; + const CTxInUndo &undo = txundo.vprevout[j]; + CCoins coins; + view.GetCoins(out.hash, coins); // this can fail if the prevout was already entirely spent + if (undo.nHeight != 0) { + // undo data contains height: this is the last output of the prevout tx being spent + if (!coins.IsPruned()) + fClean = fClean && error("DisconnectBlock() : undo data overwriting existing transaction"); + coins = CCoins(); + coins.fCoinBase = undo.fCoinBase; + coins.nHeight = undo.nHeight; + coins.nVersion = undo.nVersion; + } else { + if (coins.IsPruned()) + fClean = fClean && error("DisconnectBlock() : undo data adding output to missing transaction"); + } + if (coins.IsAvailable(out.n)) + fClean = fClean && error("DisconnectBlock() : undo data overwriting existing output"); + if (coins.vout.size() < out.n+1) + coins.vout.resize(out.n+1); + coins.vout[out.n] = undo.txout; + if (!view.SetCoins(out.hash, coins)) + return error("DisconnectBlock() : cannot restore coin inputs"); + } + } + } + + // move best block pointer to prevout block + view.SetBestBlock(pindex->pprev); + + if (pfClean) { + *pfClean = fClean; + return true; + } else { + return fClean; + } +} + +void static FlushBlockFile(bool fFinalize = false) +{ + LOCK(cs_LastBlockFile); + + CDiskBlockPos posOld(nLastBlockFile, 0); + + FILE *fileOld = OpenBlockFile(posOld); + if (fileOld) { + if (fFinalize) + TruncateFile(fileOld, infoLastBlockFile.nSize); + FileCommit(fileOld); + fclose(fileOld); + } + + fileOld = OpenUndoFile(posOld); + if (fileOld) { + if (fFinalize) + TruncateFile(fileOld, infoLastBlockFile.nUndoSize); + FileCommit(fileOld); + fclose(fileOld); + } +} + +bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize); + +static CCheckQueue scriptcheckqueue(128); + +void ThreadScriptCheck() { + RenameThread("bitcoin-scriptch"); + scriptcheckqueue.Thread(); +} + +bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck) +{ + // Check it again in case a previous version let a bad block in + if (!CheckBlock(state, !fJustCheck, !fJustCheck)) + return false; + + // verify that the view's current state corresponds to the previous block + assert(pindex->pprev == view.GetBestBlock()); + + // Special case for the genesis block, skipping connection of its transactions + // (its coinbase is unspendable) + if (GetHash() == hashGenesisBlock) { + view.SetBestBlock(pindex); + pindexGenesisBlock = pindex; + return true; + } + + bool fScriptChecks = pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(); + + // Do not allow blocks that contain transactions which 'overwrite' older transactions, + // unless those are already completely spent. + // If such overwrites are allowed, coinbases and transactions depending upon those + // can be duplicated to remove the ability to spend the first instance -- even after + // being sent to another address. + // See BIP30 and http://r6.ca/blog/20120206T005236Z.html for more information. + // This logic is not necessary for memory pool transactions, as AcceptToMemoryPool + // already refuses previously-known transaction ids entirely. + // This rule was originally applied all blocks whose timestamp was after October 1, 2012, 0:00 UTC. + // Now that the whole chain is irreversibly beyond that time it is applied to all blocks, + // this prevents exploiting the issue against nodes in their initial block download. + bool fEnforceBIP30 = true; + + if (fEnforceBIP30) { + for (unsigned int i=0; inTime >= nBIP16SwitchTime); + + unsigned int flags = SCRIPT_VERIFY_NOCACHE | + (fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE); + + CBlockUndo blockundo; + + CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); + + int64 nStart = GetTimeMicros(); + int64 nFees = 0; + int nInputs = 0; + unsigned int nSigOps = 0; + CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(vtx.size())); + std::vector > vPos; + vPos.reserve(vtx.size()); + for (unsigned int i=0; i MAX_BLOCK_SIGOPS) + return state.DoS(100, error("ConnectBlock() : too many sigops")); + + if (!tx.IsCoinBase()) + { + if (!tx.HaveInputs(view)) + return state.DoS(100, error("ConnectBlock() : inputs missing/spent")); + + if (fStrictPayToScriptHash) + { + // Add in sigops done by pay-to-script-hash inputs; + // this is to prevent a "rogue miner" from creating + // an incredibly-expensive-to-validate block. + nSigOps += tx.GetP2SHSigOpCount(view); + if (nSigOps > MAX_BLOCK_SIGOPS) + return state.DoS(100, error("ConnectBlock() : too many sigops")); + } + + nFees += tx.GetValueIn(view)-tx.GetValueOut(); + + std::vector vChecks; + if (!tx.CheckInputs(state, view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL)) + return false; + control.Add(vChecks); + } + + CTxUndo txundo; + tx.UpdateCoins(state, view, txundo, pindex->nHeight, GetTxHash(i)); + if (!tx.IsCoinBase()) + blockundo.vtxundo.push_back(txundo); + + vPos.push_back(std::make_pair(GetTxHash(i), pos)); + pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); + } + int64 nTime = GetTimeMicros() - nStart; + if (fBenchmark) + printf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)vtx.size(), 0.001 * nTime, 0.001 * nTime / vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1)); + + uint256 prevHash = 0; + if(pindex->pprev) + { + prevHash = pindex->pprev->GetBlockHash(); + } + + if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees, prevHash)) + return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees, prevHash))); + + if (!control.Wait()) + return state.DoS(100, false); + int64 nTime2 = GetTimeMicros() - nStart; + if (fBenchmark) + printf("- Verify %u txins: %.2fms (%.3fms/txin)\n", nInputs - 1, 0.001 * nTime2, nInputs <= 1 ? 0 : 0.001 * nTime2 / (nInputs-1)); + + if (fJustCheck) + return true; + + // Write undo information to disk + if (pindex->GetUndoPos().IsNull() || (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) + { + if (pindex->GetUndoPos().IsNull()) { + CDiskBlockPos pos; + if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) + return error("ConnectBlock() : FindUndoPos failed"); + if (!blockundo.WriteToDisk(pos, pindex->pprev->GetBlockHash())) + return state.Abort(_("Failed to write undo data")); + + // update nUndoPos in block index + pindex->nUndoPos = pos.nPos; + pindex->nStatus |= BLOCK_HAVE_UNDO; + } + + pindex->nStatus = (pindex->nStatus & ~BLOCK_VALID_MASK) | BLOCK_VALID_SCRIPTS; + + CDiskBlockIndex blockindex(pindex); + if (!pblocktree->WriteBlockIndex(blockindex)) + return state.Abort(_("Failed to write block index")); + } + + if (fTxIndex) + if (!pblocktree->WriteTxIndex(vPos)) + return state.Abort(_("Failed to write transaction index")); + + // add this block to the view's block chain + assert(view.SetBestBlock(pindex)); + + // Watch for transactions paying to me + for (unsigned int i=0; inHeight > pfork->nHeight) { + plonger = plonger->pprev; + assert(plonger != NULL); + } + if (pfork == plonger) + break; + pfork = pfork->pprev; + assert(pfork != NULL); + } + + // List of what to disconnect (typically nothing) + vector vDisconnect; + for (CBlockIndex* pindex = view.GetBestBlock(); pindex != pfork; pindex = pindex->pprev) + vDisconnect.push_back(pindex); + + // List of what to connect (typically only pindexNew) + vector vConnect; + for (CBlockIndex* pindex = pindexNew; pindex != pfork; pindex = pindex->pprev) + vConnect.push_back(pindex); + reverse(vConnect.begin(), vConnect.end()); + + if (vDisconnect.size() > 0) { + printf("REORGANIZE: Disconnect %"PRIszu" blocks; %s..\n", vDisconnect.size(), pfork->GetBlockHash().ToString().c_str()); + printf("REORGANIZE: Connect %"PRIszu" blocks; ..%s\n", vConnect.size(), pindexNew->GetBlockHash().ToString().c_str()); + } + + // Disconnect shorter branch + vector vResurrect; + BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) { + CBlock block; + if (!block.ReadFromDisk(pindex)) + return state.Abort(_("Failed to read block")); + int64 nStart = GetTimeMicros(); + if (!block.DisconnectBlock(state, pindex, view)) + return error("SetBestBlock() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().c_str()); + if (fBenchmark) + printf("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); + + // Queue memory transactions to resurrect. + // We only do this for blocks after the last checkpoint (reorganisation before that + // point should only happen with -reindex/-loadblock, or a misbehaving peer. + BOOST_FOREACH(const CTransaction& tx, block.vtx) + if (!tx.IsCoinBase() && pindex->nHeight > Checkpoints::GetTotalBlocksEstimate()) + vResurrect.push_back(tx); + } + + // Connect longer branch + vector vDelete; + BOOST_FOREACH(CBlockIndex *pindex, vConnect) { + CBlock block; + if (!block.ReadFromDisk(pindex)) + return state.Abort(_("Failed to read block")); + int64 nStart = GetTimeMicros(); + if (!block.ConnectBlock(state, pindex, view)) { + if (state.IsInvalid()) { + InvalidChainFound(pindexNew); + InvalidBlockFound(pindex); + } + return error("SetBestBlock() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().c_str()); + } + if (fBenchmark) + printf("- Connect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); + + // Queue memory transactions to delete + BOOST_FOREACH(const CTransaction& tx, block.vtx) + vDelete.push_back(tx); + } + + // Flush changes to global coin state + int64 nStart = GetTimeMicros(); + int nModified = view.GetCacheSize(); + assert(view.Flush()); + int64 nTime = GetTimeMicros() - nStart; + if (fBenchmark) + printf("- Flush %i transactions: %.2fms (%.4fms/tx)\n", nModified, 0.001 * nTime, 0.001 * nTime / nModified); + + // Make sure it's successfully written to disk before changing memory structure + bool fIsInitialDownload = IsInitialBlockDownload(); + if (!fIsInitialDownload || pcoinsTip->GetCacheSize() > nCoinCacheSize) { + // 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())) + return state.Error(); + FlushBlockFile(); + pblocktree->Sync(); + if (!pcoinsTip->Flush()) + return state.Abort(_("Failed to write to coin database")); + } + + // At this point, all changes have been done to the database. + // Proceed by updating the memory structures. + + // Disconnect shorter branch + BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) + if (pindex->pprev) + pindex->pprev->pnext = NULL; + + // Connect longer branch + BOOST_FOREACH(CBlockIndex* pindex, vConnect) + if (pindex->pprev) + pindex->pprev->pnext = pindex; + + // Resurrect memory transactions that were in the disconnected branch + BOOST_FOREACH(CTransaction& tx, vResurrect) { + // ignore validation errors in resurrected transactions + CValidationState stateDummy; + if (!tx.AcceptToMemoryPool(stateDummy, true, false)) + mempool.remove(tx, true); + } + + // Delete redundant memory transactions that are in the connected branch + BOOST_FOREACH(CTransaction& tx, vDelete) { + mempool.remove(tx); + mempool.removeConflicts(tx); + } + + // Update best block in wallet (so we can detect restored wallets) + if ((pindexNew->nHeight % 20160) == 0 || (!fIsInitialDownload && (pindexNew->nHeight % 144) == 0)) + { + const CBlockLocator locator(pindexNew); + ::SetBestChain(locator); + } + + // New best block + hashBestChain = pindexNew->GetBlockHash(); + pindexBest = pindexNew; + pblockindexFBBHLast = NULL; + nBestHeight = pindexBest->nHeight; + nBestChainWork = pindexNew->nChainWork; + nTimeBestReceived = GetTime(); + nTransactionsUpdated++; + printf("SetBestChain: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f\n", + hashBestChain.ToString().c_str(), nBestHeight, log(nBestChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx, + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str(), + Checkpoints::GuessVerificationProgress(pindexBest)); + + // Check the version of the last 100 blocks to see if we need to upgrade: + if (!fIsInitialDownload) + { + int nUpgraded = 0; + const CBlockIndex* pindex = pindexBest; + for (int i = 0; i < 100 && pindex != NULL; i++) + { + if (pindex->nVersion > CBlock::CURRENT_VERSION) + ++nUpgraded; + pindex = pindex->pprev; + } + if (nUpgraded > 0) + printf("SetBestChain: %d of last 100 blocks above version %d\n", nUpgraded, CBlock::CURRENT_VERSION); + if (nUpgraded > 100/2) + // strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user: + strMiscWarning = _("Warning: This version is obsolete, upgrade required!"); + } + + std::string strCmd = GetArg("-blocknotify", ""); + + if (!fIsInitialDownload && !strCmd.empty()) + { + boost::replace_all(strCmd, "%s", hashBestChain.GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } + + return true; +} + + +bool CBlock::AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos) +{ + // Check for duplicate + uint256 hash = GetHash(); + if (mapBlockIndex.count(hash)) + return state.Invalid(error("AddToBlockIndex() : %s already exists", hash.ToString().c_str())); + + // Construct new block index object + CBlockIndex* pindexNew = new CBlockIndex(*this); + assert(pindexNew); + map::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; + pindexNew->phashBlock = &((*mi).first); + map::iterator miPrev = mapBlockIndex.find(hashPrevBlock); + if (miPrev != mapBlockIndex.end()) + { + pindexNew->pprev = (*miPrev).second; + pindexNew->nHeight = pindexNew->pprev->nHeight + 1; + } + pindexNew->nTx = vtx.size(); + pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + pindexNew->GetBlockWork().getuint256(); + pindexNew->nChainTx = (pindexNew->pprev ? pindexNew->pprev->nChainTx : 0) + pindexNew->nTx; + pindexNew->nFile = pos.nFile; + pindexNew->nDataPos = pos.nPos; + pindexNew->nUndoPos = 0; + pindexNew->nStatus = BLOCK_VALID_TRANSACTIONS | BLOCK_HAVE_DATA; + setBlockIndexValid.insert(pindexNew); + + if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew))) + return state.Abort(_("Failed to write block index")); + + // New best? + if (!ConnectBestBlock(state)) + return false; + + if (pindexNew == pindexBest) + { + // Notify UI to display prev block's coinbase if it was ours + static uint256 hashPrevBestCoinBase; + UpdatedTransaction(hashPrevBestCoinBase); + hashPrevBestCoinBase = GetTxHash(0); + } + + if (!pblocktree->Flush()) + return state.Abort(_("Failed to sync block index")); + + uiInterface.NotifyBlocksChanged(); + return true; +} + + +bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime, bool fKnown = false) +{ + bool fUpdatedLast = false; + + LOCK(cs_LastBlockFile); + + if (fKnown) { + if (nLastBlockFile != pos.nFile) { + nLastBlockFile = pos.nFile; + infoLastBlockFile.SetNull(); + pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); + fUpdatedLast = true; + } + } else { + while (infoLastBlockFile.nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { + printf("Leaving block file %i: %s\n", nLastBlockFile, infoLastBlockFile.ToString().c_str()); + FlushBlockFile(true); + nLastBlockFile++; + infoLastBlockFile.SetNull(); + pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); // check whether data for the new file somehow already exist; can fail just fine + fUpdatedLast = true; + } + pos.nFile = nLastBlockFile; + pos.nPos = infoLastBlockFile.nSize; + } + + infoLastBlockFile.nSize += nAddSize; + infoLastBlockFile.AddBlock(nHeight, nTime); + + if (!fKnown) { + unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; + unsigned int nNewChunks = (infoLastBlockFile.nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; + if (nNewChunks > nOldChunks) { + if (CheckDiskSpace(nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos)) { + FILE *file = OpenBlockFile(pos); + if (file) { + printf("Pre-allocating up to position 0x%x in blk%05u.dat\n", nNewChunks * BLOCKFILE_CHUNK_SIZE, pos.nFile); + AllocateFileRange(file, pos.nPos, nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos); + fclose(file); + } + } + else + return state.Error(); + } + } + + if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile)) + return state.Abort(_("Failed to write file info")); + if (fUpdatedLast) + pblocktree->WriteLastBlockFile(nLastBlockFile); + + return true; +} + +bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize) +{ + pos.nFile = nFile; + + LOCK(cs_LastBlockFile); + + unsigned int nNewSize; + if (nFile == nLastBlockFile) { + pos.nPos = infoLastBlockFile.nUndoSize; + nNewSize = (infoLastBlockFile.nUndoSize += nAddSize); + if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile)) + return state.Abort(_("Failed to write block info")); + } else { + CBlockFileInfo info; + if (!pblocktree->ReadBlockFileInfo(nFile, info)) + return state.Abort(_("Failed to read block info")); + pos.nPos = info.nUndoSize; + nNewSize = (info.nUndoSize += nAddSize); + if (!pblocktree->WriteBlockFileInfo(nFile, info)) + return state.Abort(_("Failed to write block info")); + } + + unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; + unsigned int nNewChunks = (nNewSize + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; + if (nNewChunks > nOldChunks) { + if (CheckDiskSpace(nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos)) { + FILE *file = OpenUndoFile(pos); + if (file) { + printf("Pre-allocating up to position 0x%x in rev%05u.dat\n", nNewChunks * UNDOFILE_CHUNK_SIZE, pos.nFile); + AllocateFileRange(file, pos.nPos, nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos); + fclose(file); + } + } + else + return state.Error(); + } + + return true; +} + + +bool CBlock::CheckBlock(CValidationState &state, bool fCheckPOW, bool fCheckMerkleRoot) const +{ + // These are checks that are independent of context + // that can be verified before saving an orphan block. + + // Size limits + if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) + return state.DoS(100, error("CheckBlock() : size limits failed")); + + // Check proof of work matches claimed amount + if (fCheckPOW && !CheckProofOfWork(GetHash(), nBits)) + return state.DoS(50, error("CheckBlock() : proof of work failed")); + + // Check timestamp + if (GetBlockTime() > GetAdjustedTime() + 30 * 60) + return state.Invalid(error("CheckBlock() : block timestamp too far in the future")); + + // First transaction must be coinbase, the rest must not be + if (vtx.empty() || !vtx[0].IsCoinBase()) + return state.DoS(100, error("CheckBlock() : first tx is not coinbase")); + for (unsigned int i = 1; i < vtx.size(); i++) + if (vtx[i].IsCoinBase()) + return state.DoS(100, error("CheckBlock() : more than one coinbase")); + + // Check transactions + BOOST_FOREACH(const CTransaction& tx, vtx) + if (!tx.CheckTransaction(state)) + return error("CheckBlock() : CheckTransaction failed"); + + // Build the merkle tree already. We need it anyway later, and it makes the + // block cache the transaction hashes, which means they don't need to be + // recalculated many times during this block's validation. + BuildMerkleTree(); + + // Check for duplicate txids. This is caught by ConnectInputs(), + // but catching it earlier avoids a potential DoS attack: + set uniqueTx; + for (unsigned int i=0; i MAX_BLOCK_SIGOPS) + return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); + + // Check merkle root + if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree()) + return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); + + return true; +} + +bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp) +{ + // Check for duplicate + uint256 hash = GetHash(); + if (mapBlockIndex.count(hash)) + return state.Invalid(error("AcceptBlock() : block already in mapBlockIndex")); + + // Get prev block index + CBlockIndex* pindexPrev = NULL; + int nHeight = 0; + if (hash != hashGenesisBlock) { + map::iterator mi = mapBlockIndex.find(hashPrevBlock); + if (mi == mapBlockIndex.end()) + return state.DoS(10, error("AcceptBlock() : prev block not found")); + pindexPrev = (*mi).second; + nHeight = pindexPrev->nHeight+1; + + // Check proof of work + if (nBits != GetNextWorkRequired(pindexPrev, this)) + return state.DoS(100, error("AcceptBlock() : incorrect proof of work")); + + // Check timestamp against prev + if (GetBlockTime() <= pindexPrev->GetMedianTimePast()) + return state.Invalid(error("AcceptBlock() : block's timestamp is too early")); + + // Check that all transactions are finalized + BOOST_FOREACH(const CTransaction& tx, vtx) + if (!tx.IsFinal(nHeight, GetBlockTime())) + return state.DoS(10, error("AcceptBlock() : contains a non-final transaction")); + + // Check that the block chain matches the known block chain up to a checkpoint + if (!Checkpoints::CheckBlock(nHeight, hash)) + return state.DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight)); + + // Don't accept any forks from the main chain prior to last checkpoint + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); + if (pcheckpoint && nHeight < pcheckpoint->nHeight) + return state.DoS(100, error("AcceptBlock() : forked chain older than last checkpoint (height %d)", nHeight)); + + // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: + if (nVersion < 2) + { + if ((!fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) || + (fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100))) + { + return state.Invalid(error("AcceptBlock() : rejected nVersion=1 block")); + } + } + // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height + if (nVersion >= 2) + { + // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): + if ((!fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 750, 1000)) || + (fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 51, 100))) + { + CScript expect = CScript() << nHeight; + if (vtx[0].vin[0].scriptSig.size() < expect.size() || + !std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin())) + return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase")); + } + } + } + + // Write block to history file + try { + unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION); + CDiskBlockPos blockPos; + if (dbp != NULL) + blockPos = *dbp; + if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, nTime, dbp != NULL)) + return error("AcceptBlock() : FindBlockPos failed"); + if (dbp == NULL) + if (!WriteToDisk(blockPos)) + return state.Abort(_("Failed to write block")); + if (!AddToBlockIndex(state, blockPos)) + return error("AcceptBlock() : AddToBlockIndex failed"); + } catch(std::runtime_error &e) { + return state.Abort(_("System error: ") + e.what()); + } + + // Relay inventory, but don't relay old inventory during initial block download + int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); + if (hashBestChain == hash) + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) + pnode->PushInventory(CInv(MSG_BLOCK, hash)); + } + + return true; +} + +bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck) +{ + // CryptoLottoToken: temporarily disable v2 block lockin until we are ready for v2 transition + return false; + unsigned int nFound = 0; + for (unsigned int i = 0; i < nToCheck && nFound < nRequired && pstart != NULL; i++) + { + if (pstart->nVersion >= minVersion) + ++nFound; + pstart = pstart->pprev; + } + return (nFound >= nRequired); +} + +bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) +{ + // Check for duplicate + uint256 hash = pblock->GetHash(); + if (mapBlockIndex.count(hash)) + return state.Invalid(error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString().c_str())); + if (mapOrphanBlocks.count(hash)) + return state.Invalid(error("ProcessBlock() : already have block (orphan) %s", hash.ToString().c_str())); + + // Preliminary checks + if (!pblock->CheckBlock(state)) + return error("ProcessBlock() : CheckBlock FAILED"); + + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); + if (pcheckpoint && pblock->hashPrevBlock != hashBestChain) + { + // Extra checks to prevent "fill up memory by spamming with bogus blocks" + int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime; + if (deltaTime < 0) + { + return state.DoS(100, error("ProcessBlock() : block with timestamp before last checkpoint")); + } + CBigNum bnNewBlock; + bnNewBlock.SetCompact(pblock->nBits); + CBigNum bnRequired; + bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime)); + if (bnNewBlock > bnRequired) + { + return state.DoS(100, error("ProcessBlock() : block with too little proof-of-work")); + } + } + + + // If we don't already have its previous block, shunt it off to holding area until we get it + if (pblock->hashPrevBlock != 0 && !mapBlockIndex.count(pblock->hashPrevBlock)) + { + printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().c_str()); + + // Accept orphans as long as there is a node to request its parents from + if (pfrom) { + CBlock* pblock2 = new CBlock(*pblock); + mapOrphanBlocks.insert(make_pair(hash, pblock2)); + mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2)); + + // Ask this guy to fill in what we're missing + pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(pblock2)); + } + return true; + } + + // Store to disk + if (!pblock->AcceptBlock(state, dbp)) + return error("ProcessBlock() : AcceptBlock FAILED"); + + // Recursively process any orphan blocks that depended on this one + vector vWorkQueue; + vWorkQueue.push_back(hash); + for (unsigned int i = 0; i < vWorkQueue.size(); i++) + { + uint256 hashPrev = vWorkQueue[i]; + for (multimap::iterator mi = mapOrphanBlocksByPrev.lower_bound(hashPrev); + mi != mapOrphanBlocksByPrev.upper_bound(hashPrev); + ++mi) + { + CBlock* pblockOrphan = (*mi).second; + // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan resolution (that is, feeding people an invalid block based on LegitBlockX in order to get anyone relaying LegitBlockX banned) + CValidationState stateDummy; + if (pblockOrphan->AcceptBlock(stateDummy)) + vWorkQueue.push_back(pblockOrphan->GetHash()); + mapOrphanBlocks.erase(pblockOrphan->GetHash()); + delete pblockOrphan; + } + mapOrphanBlocksByPrev.erase(hashPrev); + } + + printf("ProcessBlock: ACCEPTED\n"); + return true; +} + + + + + + + + +CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter) +{ + header = block.GetBlockHeader(); + + vector vMatch; + vector vHashes; + + vMatch.reserve(block.vtx.size()); + vHashes.reserve(block.vtx.size()); + + for (unsigned int i = 0; i < block.vtx.size(); i++) + { + uint256 hash = block.vtx[i].GetHash(); + if (filter.IsRelevantAndUpdate(block.vtx[i], hash)) + { + vMatch.push_back(true); + vMatchedTxn.push_back(make_pair(i, hash)); + } + else + vMatch.push_back(false); + vHashes.push_back(hash); + } + + txn = CPartialMerkleTree(vHashes, vMatch); +} + + + + + + + + +uint256 CPartialMerkleTree::CalcHash(int height, unsigned int pos, const std::vector &vTxid) { + if (height == 0) { + // hash at height 0 is the txids themself + return vTxid[pos]; + } else { + // calculate left hash + uint256 left = CalcHash(height-1, pos*2, vTxid), right; + // calculate right hash if not beyong the end of the array - copy left hash otherwise1 + if (pos*2+1 < CalcTreeWidth(height-1)) + right = CalcHash(height-1, pos*2+1, vTxid); + else + right = left; + // combine subhashes + return Hash(BEGIN(left), END(left), BEGIN(right), END(right)); + } +} + +void CPartialMerkleTree::TraverseAndBuild(int height, unsigned int pos, const std::vector &vTxid, const std::vector &vMatch) { + // determine whether this node is the parent of at least one matched txid + bool fParentOfMatch = false; + for (unsigned int p = pos << height; p < (pos+1) << height && p < nTransactions; p++) + fParentOfMatch |= vMatch[p]; + // store as flag bit + vBits.push_back(fParentOfMatch); + if (height==0 || !fParentOfMatch) { + // if at height 0, or nothing interesting below, store hash and stop + vHash.push_back(CalcHash(height, pos, vTxid)); + } else { + // otherwise, don't store any hash, but descend into the subtrees + TraverseAndBuild(height-1, pos*2, vTxid, vMatch); + if (pos*2+1 < CalcTreeWidth(height-1)) + TraverseAndBuild(height-1, pos*2+1, vTxid, vMatch); + } +} + +uint256 CPartialMerkleTree::TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector &vMatch) { + if (nBitsUsed >= vBits.size()) { + // overflowed the bits array - failure + fBad = true; + return 0; + } + bool fParentOfMatch = vBits[nBitsUsed++]; + if (height==0 || !fParentOfMatch) { + // if at height 0, or nothing interesting below, use stored hash and do not descend + if (nHashUsed >= vHash.size()) { + // overflowed the hash array - failure + fBad = true; + return 0; + } + const uint256 &hash = vHash[nHashUsed++]; + if (height==0 && fParentOfMatch) // in case of height 0, we have a matched txid + vMatch.push_back(hash); + return hash; + } else { + // otherwise, descend into the subtrees to extract matched txids and hashes + uint256 left = TraverseAndExtract(height-1, pos*2, nBitsUsed, nHashUsed, vMatch), right; + if (pos*2+1 < CalcTreeWidth(height-1)) + right = TraverseAndExtract(height-1, pos*2+1, nBitsUsed, nHashUsed, vMatch); + else + right = left; + // and combine them before returning + return Hash(BEGIN(left), END(left), BEGIN(right), END(right)); + } +} + +CPartialMerkleTree::CPartialMerkleTree(const std::vector &vTxid, const std::vector &vMatch) : nTransactions(vTxid.size()), fBad(false) { + // reset state + vBits.clear(); + vHash.clear(); + + // calculate height of tree + int nHeight = 0; + while (CalcTreeWidth(nHeight) > 1) + nHeight++; + + // traverse the partial tree + TraverseAndBuild(nHeight, 0, vTxid, vMatch); +} + +CPartialMerkleTree::CPartialMerkleTree() : nTransactions(0), fBad(true) {} + +uint256 CPartialMerkleTree::ExtractMatches(std::vector &vMatch) { + vMatch.clear(); + // An empty set will not work + if (nTransactions == 0) + return 0; + // check for excessively high numbers of transactions + if (nTransactions > MAX_BLOCK_SIZE / 60) // 60 is the lower bound for the size of a serialized CTransaction + return 0; + // there can never be more hashes provided than one for every txid + if (vHash.size() > nTransactions) + return 0; + // there must be at least one bit per node in the partial tree, and at least one node per hash + if (vBits.size() < vHash.size()) + return 0; + // calculate height of tree + int nHeight = 0; + while (CalcTreeWidth(nHeight) > 1) + nHeight++; + // traverse the partial tree + unsigned int nBitsUsed = 0, nHashUsed = 0; + uint256 hashMerkleRoot = TraverseAndExtract(nHeight, 0, nBitsUsed, nHashUsed, vMatch); + // verify that no problems occured during the tree traversal + if (fBad) + return 0; + // verify that all bits were consumed (except for the padding caused by serializing it as a byte sequence) + if ((nBitsUsed+7)/8 != (vBits.size()+7)/8) + return 0; + // verify that all hashes were consumed + if (nHashUsed != vHash.size()) + return 0; + return hashMerkleRoot; +} + + + + + + + +bool AbortNode(const std::string &strMessage) { + strMiscWarning = strMessage; + printf("*** %s\n", strMessage.c_str()); + uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return false; +} + +bool CheckDiskSpace(uint64 nAdditionalBytes) +{ + uint64 nFreeBytesAvailable = filesystem::space(GetDataDir()).available; + + // Check for nMinDiskSpace bytes (currently 50MB) + if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) + return AbortNode(_("Error: Disk space is low!")); + + return true; +} + +CCriticalSection cs_LastBlockFile; +CBlockFileInfo infoLastBlockFile; +int nLastBlockFile = 0; + +FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly) +{ + if (pos.IsNull()) + return NULL; + boost::filesystem::path path = GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile); + boost::filesystem::create_directories(path.parent_path()); + FILE* file = fopen(path.string().c_str(), "rb+"); + if (!file && !fReadOnly) + file = fopen(path.string().c_str(), "wb+"); + if (!file) { + printf("Unable to open file %s\n", path.string().c_str()); + return NULL; + } + if (pos.nPos) { + if (fseek(file, pos.nPos, SEEK_SET)) { + printf("Unable to seek to position %u of %s\n", pos.nPos, path.string().c_str()); + fclose(file); + return NULL; + } + } + return file; +} + +FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly) { + return OpenDiskFile(pos, "blk", fReadOnly); +} + +FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) { + return OpenDiskFile(pos, "rev", fReadOnly); +} + +CBlockIndex * InsertBlockIndex(uint256 hash) +{ + if (hash == 0) + return NULL; + + // Return existing + map::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + return (*mi).second; + + // Create new + CBlockIndex* pindexNew = new CBlockIndex(); + if (!pindexNew) + throw runtime_error("LoadBlockIndex() : new CBlockIndex failed"); + mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; + pindexNew->phashBlock = &((*mi).first); + + return pindexNew; +} + +bool static LoadBlockIndexDB() +{ + if (!pblocktree->LoadBlockIndexGuts()) + return false; + + boost::this_thread::interruption_point(); + + // Calculate nChainWork + vector > vSortedByHeight; + vSortedByHeight.reserve(mapBlockIndex.size()); + BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) + { + CBlockIndex* pindex = item.second; + vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex)); + } + sort(vSortedByHeight.begin(), vSortedByHeight.end()); + BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight) + { + CBlockIndex* pindex = item.second; + pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + pindex->GetBlockWork().getuint256(); + pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx; + if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS && !(pindex->nStatus & BLOCK_FAILED_MASK)) + setBlockIndexValid.insert(pindex); + } + + // Load block file info + pblocktree->ReadLastBlockFile(nLastBlockFile); + printf("LoadBlockIndexDB(): last block file = %i\n", nLastBlockFile); + if (pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile)) + printf("LoadBlockIndexDB(): last block file info: %s\n", infoLastBlockFile.ToString().c_str()); + + // Load nBestInvalidWork, OK if it doesn't exist + CBigNum bnBestInvalidWork; + pblocktree->ReadBestInvalidWork(bnBestInvalidWork); + nBestInvalidWork = bnBestInvalidWork.getuint256(); + + // Check whether we need to continue reindexing + bool fReindexing = false; + pblocktree->ReadReindexing(fReindexing); + fReindex |= fReindexing; + + // Check whether we have a transaction index + pblocktree->ReadFlag("txindex", fTxIndex); + printf("LoadBlockIndexDB(): transaction index %s\n", fTxIndex ? "enabled" : "disabled"); + + // Load hashBestChain pointer to end of best chain + pindexBest = pcoinsTip->GetBestBlock(); + if (pindexBest == NULL) + return true; + hashBestChain = pindexBest->GetBlockHash(); + nBestHeight = pindexBest->nHeight; + nBestChainWork = pindexBest->nChainWork; + + // set 'next' pointers in best chain + CBlockIndex *pindex = pindexBest; + while(pindex != NULL && pindex->pprev != NULL) { + CBlockIndex *pindexPrev = pindex->pprev; + pindexPrev->pnext = pindex; + pindex = pindexPrev; + } + printf("LoadBlockIndexDB(): hashBestChain=%s height=%d date=%s\n", + hashBestChain.ToString().c_str(), nBestHeight, + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str()); + + return true; +} + +bool VerifyDB(int nCheckLevel, int nCheckDepth) +{ + if (pindexBest == NULL || pindexBest->pprev == NULL) + return true; + + // Verify blocks in the best chain + if (nCheckDepth <= 0) + nCheckDepth = 1000000000; // suffices until the year 19000 + if (nCheckDepth > nBestHeight) + nCheckDepth = nBestHeight; + nCheckLevel = std::max(0, std::min(4, nCheckLevel)); + printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); + CCoinsViewCache coins(*pcoinsTip, true); + CBlockIndex* pindexState = pindexBest; + CBlockIndex* pindexFailure = NULL; + int nGoodTransactions = 0; + CValidationState state; + for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) + { + boost::this_thread::interruption_point(); + if (pindex->nHeight < nBestHeight-nCheckDepth) + break; + CBlock block; + // check level 0: read from disk + if (!block.ReadFromDisk(pindex)) + return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + // check level 1: verify block validity + if (nCheckLevel >= 1 && !block.CheckBlock(state)) + return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + // check level 2: verify undo validity + if (nCheckLevel >= 2 && pindex) { + CBlockUndo undo; + CDiskBlockPos pos = pindex->GetUndoPos(); + if (!pos.IsNull()) { + if (!undo.ReadFromDisk(pos, pindex->pprev->GetBlockHash())) + return error("VerifyDB() : *** found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + } + } + // check level 3: check for inconsistencies during memory-only disconnect of tip blocks + if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= 2*nCoinCacheSize + 32000) { + bool fClean = true; + if (!block.DisconnectBlock(state, pindex, coins, &fClean)) + return error("VerifyDB() : *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + pindexState = pindex->pprev; + if (!fClean) { + nGoodTransactions = 0; + pindexFailure = pindex; + } else + nGoodTransactions += block.vtx.size(); + } + } + if (pindexFailure) + return error("VerifyDB() : *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", pindexBest->nHeight - pindexFailure->nHeight + 1, nGoodTransactions); + + // check level 4: try reconnecting blocks + if (nCheckLevel >= 4) { + CBlockIndex *pindex = pindexState; + while (pindex != pindexBest) { + boost::this_thread::interruption_point(); + pindex = pindex->pnext; + CBlock block; + if (!block.ReadFromDisk(pindex)) + return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + if (!block.ConnectBlock(state, pindex, coins)) + return error("VerifyDB() : *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + } + } + + printf("No coin database inconsistencies in last %i blocks (%i transactions)\n", pindexBest->nHeight - pindexState->nHeight, nGoodTransactions); + + return true; +} + +void UnloadBlockIndex() +{ + mapBlockIndex.clear(); + setBlockIndexValid.clear(); + pindexGenesisBlock = NULL; + nBestHeight = 0; + nBestChainWork = 0; + nBestInvalidWork = 0; + hashBestChain = 0; + pindexBest = NULL; +} + +bool LoadBlockIndex() +{ + if (fTestNet) + { + pchMessageStart[0] = 0xbc; + pchMessageStart[1] = 0xad; + pchMessageStart[2] = 0xaf; + pchMessageStart[3] = 0xc4; + + hashGenesisBlock = uint256("0xb78197f0e175697646db1f738edc1ffdcb30588ebe70e7e16026489076577061"); + } + + // + // Load block index from databases + // + if (!fReindex && !LoadBlockIndexDB()) + return false; + + return true; +} + + +bool InitBlockIndex() { + // Check whether we're already initialized + if (pindexGenesisBlock != NULL) + return true; + + // Use the provided setting for -txindex in the new database + fTxIndex = GetBoolArg("-txindex", false); + pblocktree->WriteFlag("txindex", fTxIndex); + printf("Initializing databases...\n"); + + // Only add the genesis block if not reindexing (in which case we reuse the one already on disk) + if (!fReindex) { + + // Genesis block + const char* pszTimestamp = "CLT Genesis phrase"; + CTransaction txNew; + txNew.vin.resize(1); + txNew.vout.resize(1); + txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) << vector((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); + txNew.vout[0].nValue = 1 * COIN; + txNew.vout[0].scriptPubKey = CScript() << ParseHex("045d4a201dd76decf67f2338ce5ecb29ea0429f339a7a2e2d5125851a4849dccbf64a58d1b469c4ce4a26b4806905c936988a075f0a93af946b99b657c1202d2fc") << OP_CHECKSIG; + CBlock block; + block.vtx.push_back(txNew); + block.hashPrevBlock = 0; + block.hashMerkleRoot = block.BuildMerkleTree(); + block.nVersion = 1; + block.nTime = 1474659365; + block.nBits = 0x1e0ffff0; + block.nNonce = 709267; + + if (fTestNet) + { + block.nTime = 1388880557; + block.nNonce = 387006691; + } + + if (false && (block.GetHash() != hashGenesisBlock)) { + block.nNonce = 0; + + // This will figure out a valid hash and Nonce if you're + // creating a different genesis block: + uint256 hashTarget = CBigNum().SetCompact(block.nBits).getuint256(); + while (block.GetHash() > hashTarget) + { + ++block.nNonce; + if (block.nNonce == 0) + { + printf("NONCE WRAPPED, incrementing time"); + ++block.nTime; + } + // if (block.nNonce % 10000 == 0) + // { + // printf("nonce %08u: hash = %s \n", block.nNonce, block.GetHash().ToString().c_str()); + // } + } + } + + //// debug print + uint256 hash = block.GetHash(); + printf("%s\n", hash.ToString().c_str()); + printf("%s\n", hashGenesisBlock.ToString().c_str()); + printf("%s\n", block.hashMerkleRoot.ToString().c_str()); + + assert(block.hashMerkleRoot == uint256("0x32832a2dfbffa0f71e16397f10c38569bd6eb2af3f7f860f0b8012bc9473ff38")); + block.print(); + assert(hash == hashGenesisBlock); + + // Start new block file + try { + unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); + CDiskBlockPos blockPos; + CValidationState state; + if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.nTime)) + return error("LoadBlockIndex() : FindBlockPos failed"); + if (!block.WriteToDisk(blockPos)) + return error("LoadBlockIndex() : writing genesis block to disk failed"); + if (!block.AddToBlockIndex(state, blockPos)) + return error("LoadBlockIndex() : genesis block not accepted"); + } catch(std::runtime_error &e) { + return error("LoadBlockIndex() : failed to initialize block database: %s", e.what()); + } + } + + return true; +} + + + +void PrintBlockTree() +{ + // pre-compute tree structure + map > mapNext; + for (map::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) + { + CBlockIndex* pindex = (*mi).second; + mapNext[pindex->pprev].push_back(pindex); + // test + //while (rand() % 3 == 0) + // mapNext[pindex->pprev].push_back(pindex); + } + + vector > vStack; + vStack.push_back(make_pair(0, pindexGenesisBlock)); + + int nPrevCol = 0; + while (!vStack.empty()) + { + int nCol = vStack.back().first; + CBlockIndex* pindex = vStack.back().second; + vStack.pop_back(); + + // print split or gap + if (nCol > nPrevCol) + { + for (int i = 0; i < nCol-1; i++) + printf("| "); + printf("|\\\n"); + } + else if (nCol < nPrevCol) + { + for (int i = 0; i < nCol; i++) + printf("| "); + printf("|\n"); + } + nPrevCol = nCol; + + // print columns + for (int i = 0; i < nCol; i++) + printf("| "); + + // print item + CBlock block; + block.ReadFromDisk(pindex); + printf("%d (blk%05u.dat:0x%x) %s tx %"PRIszu"", + pindex->nHeight, + pindex->GetBlockPos().nFile, pindex->GetBlockPos().nPos, + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", block.GetBlockTime()).c_str(), + block.vtx.size()); + + PrintWallets(block); + + // put the main time-chain first + vector& vNext = mapNext[pindex]; + for (unsigned int i = 0; i < vNext.size(); i++) + { + if (vNext[i]->pnext) + { + swap(vNext[0], vNext[i]); + break; + } + } + + // iterate children + for (unsigned int i = 0; i < vNext.size(); i++) + vStack.push_back(make_pair(nCol+i, vNext[i])); + } +} + +bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) +{ + int64 nStart = GetTimeMillis(); + + int nLoaded = 0; + try { + CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION); + uint64 nStartByte = 0; + if (dbp) { + // (try to) skip already indexed part + CBlockFileInfo info; + if (pblocktree->ReadBlockFileInfo(dbp->nFile, info)) { + nStartByte = info.nSize; + blkdat.Seek(info.nSize); + } + } + uint64 nRewind = blkdat.GetPos(); + while (blkdat.good() && !blkdat.eof()) { + boost::this_thread::interruption_point(); + + blkdat.SetPos(nRewind); + nRewind++; // start one byte further next time, in case of failure + blkdat.SetLimit(); // remove former limit + unsigned int nSize = 0; + try { + // locate a header + unsigned char buf[4]; + blkdat.FindByte(pchMessageStart[0]); + nRewind = blkdat.GetPos()+1; + blkdat >> FLATDATA(buf); + if (memcmp(buf, pchMessageStart, 4)) + continue; + // read size + blkdat >> nSize; + if (nSize < 80 || nSize > MAX_BLOCK_SIZE) + continue; + } catch (std::exception &e) { + // no valid block header found; don't complain + break; + } + try { + // read block + uint64 nBlockPos = blkdat.GetPos(); + blkdat.SetLimit(nBlockPos + nSize); + CBlock block; + blkdat >> block; + nRewind = blkdat.GetPos(); + + // process block + if (nBlockPos >= nStartByte) { + LOCK(cs_main); + if (dbp) + dbp->nPos = nBlockPos; + CValidationState state; + if (ProcessBlock(state, NULL, &block, dbp)) + nLoaded++; + if (state.IsError()) + break; + } + } catch (std::exception &e) { + printf("%s() : Deserialize or I/O error caught during load\n", __PRETTY_FUNCTION__); + } + } + fclose(fileIn); + } catch(std::runtime_error &e) { + AbortNode(_("Error: system error: ") + e.what()); + } + if (nLoaded > 0) + printf("Loaded %i blocks from external file in %"PRI64d"ms\n", nLoaded, GetTimeMillis() - nStart); + return nLoaded > 0; +} + + + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CAlert +// + +extern map mapAlerts; +extern CCriticalSection cs_mapAlerts; + +string GetWarnings(string strFor) +{ + int nPriority = 0; + string strStatusBar; + string strRPC; + + if (GetBoolArg("-testsafemode")) + strRPC = "test"; + + if (!CLIENT_VERSION_IS_RELEASE) + strStatusBar = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"); + + // Misc warnings like out of disk space and clock is wrong + if (strMiscWarning != "") + { + nPriority = 1000; + strStatusBar = strMiscWarning; + } + + // Longer invalid proof-of-work chain + if (pindexBest && nBestInvalidWork > nBestChainWork + (pindexBest->GetBlockWork() * 6).getuint256()) + { + nPriority = 2000; + strStatusBar = strRPC = _("Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade."); + } + + // Alerts + { + LOCK(cs_mapAlerts); + BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) + { + const CAlert& alert = item.second; + if (alert.AppliesToMe() && alert.nPriority > nPriority) + { + nPriority = alert.nPriority; + strStatusBar = alert.strStatusBar; + } + } + } + + if (strFor == "statusbar") + return strStatusBar; + else if (strFor == "rpc") + return strRPC; + assert(!"GetWarnings() : invalid parameter"); + return "error"; +} + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// Messages +// + + +bool static AlreadyHave(const CInv& inv) +{ + switch (inv.type) + { + case MSG_TX: + { + bool txInMap = false; + { + LOCK(mempool.cs); + txInMap = mempool.exists(inv.hash); + } + return txInMap || mapOrphanTransactions.count(inv.hash) || + pcoinsTip->HaveCoins(inv.hash); + } + case MSG_BLOCK: + return mapBlockIndex.count(inv.hash) || + mapOrphanBlocks.count(inv.hash); + } + // Don't know what it is, just say we already got one + return true; +} + + + + +// The message start string is designed to be unlikely to occur in normal data. +// The characters are rarely used upper ASCII, not valid as UTF-8, and produce +// a large 4-byte int at any alignment. pchmess0agestart +unsigned char pchMessageStart[4] = { 0xc1, 0xa7, 0xee, 0xc2 }; + + +void static ProcessGetData(CNode* pfrom) +{ + std::deque::iterator it = pfrom->vRecvGetData.begin(); + + vector vNotFound; + + while (it != pfrom->vRecvGetData.end()) { + // Don't bother if send buffer is too full to respond anyway + if (pfrom->nSendSize >= SendBufferSize()) + break; + + // Don't waste work on slow peers until they catch up on the blocks we + // give them. 80 bytes is just the size of a block header - obviously + // the minimum we might return. + if (pfrom->nBlocksRequested * 80 > pfrom->nSendBytes) + break; + + const CInv &inv = *it; + { + boost::this_thread::interruption_point(); + it++; + + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) + { + bool send = true; + map::iterator mi = mapBlockIndex.find(inv.hash); + pfrom->nBlocksRequested++; + if (mi != mapBlockIndex.end()) + { + // If the requested block is at a height below our last + // checkpoint, only serve it if it's in the checkpointed chain + int nHeight = ((*mi).second)->nHeight; + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); + if (pcheckpoint && nHeight < pcheckpoint->nHeight) { + if (!((*mi).second)->IsInMainChain()) + { + printf("ProcessGetData(): ignoring request for old block that isn't in the main chain\n"); + send = false; + } + } + } else { + send = false; + } + if (send) + { + // Send block from disk + CBlock block; + block.ReadFromDisk((*mi).second); + if (inv.type == MSG_BLOCK) + pfrom->PushMessage("block", block); + else // MSG_FILTERED_BLOCK) + { + LOCK(pfrom->cs_filter); + if (pfrom->pfilter) + { + CMerkleBlock merkleBlock(block, *pfrom->pfilter); + pfrom->PushMessage("merkleblock", merkleBlock); + // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see + // This avoids hurting performance by pointlessly requiring a round-trip + // Note that there is currently no way for a node to request any single transactions we didnt send here - + // they must either disconnect and retry or request the full block. + // Thus, the protocol spec specified allows for us to provide duplicate txn here, + // however we MUST always provide at least what the remote peer needs + typedef std::pair PairType; + BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn) + if (!pfrom->setInventoryKnown.count(CInv(MSG_TX, pair.second))) + pfrom->PushMessage("tx", block.vtx[pair.first]); + } + // else + // no response + } + + // Trigger them to send a getblocks request for the next batch of inventory + if (inv.hash == pfrom->hashContinue) + { + // Bypass PushInventory, this must send even if redundant, + // and we want it right after the last block so they don't + // wait for other stuff first. + vector vInv; + vInv.push_back(CInv(MSG_BLOCK, hashBestChain)); + pfrom->PushMessage("inv", vInv); + pfrom->hashContinue = 0; + } + } + } + else if (inv.IsKnownType()) + { + // Send stream from relay memory + bool pushed = false; + { + LOCK(cs_mapRelay); + map::iterator mi = mapRelay.find(inv); + if (mi != mapRelay.end()) { + pfrom->PushMessage(inv.GetCommand(), (*mi).second); + pushed = true; + } + } + if (!pushed && inv.type == MSG_TX) { + LOCK(mempool.cs); + if (mempool.exists(inv.hash)) { + CTransaction tx = mempool.lookup(inv.hash); + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << tx; + pfrom->PushMessage("tx", ss); + pushed = true; + } + } + if (!pushed) { + vNotFound.push_back(inv); + } + } + + // Track requests for our stuff. + Inventory(inv.hash); + + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) + break; + } + } + + pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it); + + if (!vNotFound.empty()) { + // Let the peer know that we didn't find what it asked for, so it doesn't + // have to wait around forever. Currently only SPV clients actually care + // about this message: it's needed when they are recursively walking the + // dependencies of relevant unconfirmed transactions. SPV clients want to + // do that because they want to know about (and store and rebroadcast and + // risk analyze) the dependencies of transactions relevant to them, without + // having to download the entire memory pool. + pfrom->PushMessage("notfound", vNotFound); + } +} + +bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) +{ + RandAddSeedPerfmon(); + if (fDebug) + printf("received: %s (%"PRIszu" bytes)\n", strCommand.c_str(), vRecv.size()); + if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) + { + printf("dropmessagestest DROPPING RECV MESSAGE\n"); + return true; + } + + + + + + if (strCommand == "version") + { + // Each connection can only send one version message + if (pfrom->nVersion != 0) + { + pfrom->Misbehaving(1); + return false; + } + + int64 nTime; + CAddress addrMe; + CAddress addrFrom; + uint64 nNonce = 1; + vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe; + if (pfrom->nVersion < MIN_PEER_PROTO_VERSION) + { + // disconnect from peers older than this proto version + printf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString().c_str(), pfrom->nVersion); + pfrom->fDisconnect = true; + return false; + } + + if (pfrom->nVersion == 10300) + pfrom->nVersion = 300; + if (!vRecv.empty()) + vRecv >> addrFrom >> nNonce; + if (!vRecv.empty()) { + vRecv >> pfrom->strSubVer; + pfrom->cleanSubVer = SanitizeString(pfrom->strSubVer); + } + if (!vRecv.empty()) + vRecv >> pfrom->nStartingHeight; + if (!vRecv.empty()) + vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message + else + pfrom->fRelayTxes = true; + + if (pfrom->fInbound && addrMe.IsRoutable()) + { + pfrom->addrLocal = addrMe; + SeenLocal(addrMe); + } + + // Disconnect if we connected to ourself + if (nNonce == nLocalHostNonce && nNonce > 1) + { + printf("connected to self at %s, disconnecting\n", pfrom->addr.ToString().c_str()); + pfrom->fDisconnect = true; + return true; + } + + // Be shy and don't send version until we hear + if (pfrom->fInbound) + pfrom->PushVersion(); + + pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); + + AddTimeData(pfrom->addr, nTime); + + // Change version + pfrom->PushMessage("verack"); + pfrom->ssSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); + + if (!pfrom->fInbound) + { + // Advertise our address + if (!fNoListen && !IsInitialBlockDownload()) + { + CAddress addr = GetLocalAddress(&pfrom->addr); + if (addr.IsRoutable()) + pfrom->PushAddress(addr); + } + + // Get recent addresses + if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || addrman.size() < 1000) + { + pfrom->PushMessage("getaddr"); + pfrom->fGetAddr = true; + } + addrman.Good(pfrom->addr); + } else { + if (((CNetAddr)pfrom->addr) == (CNetAddr)addrFrom) + { + addrman.Add(addrFrom, addrFrom); + addrman.Good(addrFrom); + } + } + + // Relay alerts + { + LOCK(cs_mapAlerts); + BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) + item.second.RelayTo(pfrom); + } + + pfrom->fSuccessfullyConnected = true; + + printf("receive version message: %s: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", pfrom->cleanSubVer.c_str(), pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString().c_str(), addrFrom.ToString().c_str(), pfrom->addr.ToString().c_str()); + + cPeerBlockCounts.input(pfrom->nStartingHeight); + } + + + else if (pfrom->nVersion == 0) + { + // Must have a version message before anything else + pfrom->Misbehaving(1); + return false; + } + + + else if (strCommand == "verack") + { + pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); + } + + + else if (strCommand == "addr") + { + vector vAddr; + vRecv >> vAddr; + + // Don't want addr from older versions unless seeding + if (pfrom->nVersion < CADDR_TIME_VERSION && addrman.size() > 1000) + return true; + if (vAddr.size() > 1000) + { + pfrom->Misbehaving(20); + return error("message addr size() = %"PRIszu"", vAddr.size()); + } + + // Store the new addresses + vector vAddrOk; + int64 nNow = GetAdjustedTime(); + int64 nSince = nNow - 10 * 60; + BOOST_FOREACH(CAddress& addr, vAddr) + { + boost::this_thread::interruption_point(); + + if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) + addr.nTime = nNow - 5 * 24 * 60 * 60; + pfrom->AddAddressKnown(addr); + bool fReachable = IsReachable(addr); + if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable()) + { + // Relay to a limited number of other nodes + { + LOCK(cs_vNodes); + // Use deterministic randomness to send to the same nodes for 24 hours + // at a time so the setAddrKnowns of the chosen nodes prevent repeats + static uint256 hashSalt; + if (hashSalt == 0) + hashSalt = GetRandHash(); + uint64 hashAddr = addr.GetHash(); + uint256 hashRand = hashSalt ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60)); + hashRand = Hash(BEGIN(hashRand), END(hashRand)); + multimap mapMix; + BOOST_FOREACH(CNode* pnode, vNodes) + { + if (pnode->nVersion < CADDR_TIME_VERSION) + continue; + unsigned int nPointer; + memcpy(&nPointer, &pnode, sizeof(nPointer)); + uint256 hashKey = hashRand ^ nPointer; + hashKey = Hash(BEGIN(hashKey), END(hashKey)); + mapMix.insert(make_pair(hashKey, pnode)); + } + int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s) + for (multimap::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) + ((*mi).second)->PushAddress(addr); + } + } + // Do not store addresses outside our network + if (fReachable) + vAddrOk.push_back(addr); + } + addrman.Add(vAddrOk, pfrom->addr, 2 * 60 * 60); + if (vAddr.size() < 1000) + pfrom->fGetAddr = false; + if (pfrom->fOneShot) + pfrom->fDisconnect = true; + } + + + else if (strCommand == "inv") + { + vector vInv; + vRecv >> vInv; + if (vInv.size() > MAX_INV_SZ) + { + pfrom->Misbehaving(20); + return error("message inv size() = %"PRIszu"", vInv.size()); + } + + // find last block in inv vector + unsigned int nLastBlock = (unsigned int)(-1); + for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { + if (vInv[vInv.size() - 1 - nInv].type == MSG_BLOCK) { + nLastBlock = vInv.size() - 1 - nInv; + break; + } + } + for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) + { + const CInv &inv = vInv[nInv]; + + boost::this_thread::interruption_point(); + pfrom->AddInventoryKnown(inv); + + bool fAlreadyHave = AlreadyHave(inv); + if (fDebug) + printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new"); + + if (!fAlreadyHave) { + if (!fImporting && !fReindex) + pfrom->AskFor(inv); + } else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) { + pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash])); + } else if (nInv == nLastBlock) { + // In case we are on a very long side-chain, it is possible that we already have + // the last block in an inv bundle sent in response to getblocks. Try to detect + // this situation and push another getblocks to continue. + pfrom->PushGetBlocks(mapBlockIndex[inv.hash], uint256(0)); + if (fDebug) + printf("force request: %s\n", inv.ToString().c_str()); + } + + // Track requests for our stuff + Inventory(inv.hash); + } + } + + + else if (strCommand == "getdata") + { + vector vInv; + vRecv >> vInv; + if (vInv.size() > MAX_INV_SZ) + { + pfrom->Misbehaving(20); + return error("message getdata size() = %"PRIszu"", vInv.size()); + } + + if (fDebugNet || (vInv.size() != 1)) + printf("received getdata (%"PRIszu" invsz)\n", vInv.size()); + + if ((fDebugNet && vInv.size() > 0) || (vInv.size() == 1)) + printf("received getdata for: %s\n", vInv[0].ToString().c_str()); + + pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end()); + ProcessGetData(pfrom); + } + + + else if (strCommand == "getblocks") + { + CBlockLocator locator; + uint256 hashStop; + vRecv >> locator >> hashStop; + + // Find the last block the caller has in the main chain + CBlockIndex* pindex = locator.GetBlockIndex(); + + // Send the rest of the chain + if (pindex) + pindex = pindex->pnext; + int nLimit = 500; + printf("getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().c_str(), nLimit); + for (; pindex; pindex = pindex->pnext) + { + if (pindex->GetBlockHash() == hashStop) + { + printf(" getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + break; + } + pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); + if (--nLimit <= 0) + { + // When this block is requested, we'll send an inv that'll make them + // getblocks the next batch of inventory. + printf(" getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + pfrom->hashContinue = pindex->GetBlockHash(); + break; + } + } + } + + + else if (strCommand == "getheaders") + { + CBlockLocator locator; + uint256 hashStop; + vRecv >> locator >> hashStop; + + CBlockIndex* pindex = NULL; + if (locator.IsNull()) + { + // If locator is null, return the hashStop block + map::iterator mi = mapBlockIndex.find(hashStop); + if (mi == mapBlockIndex.end()) + return true; + pindex = (*mi).second; + } + else + { + // Find the last block the caller has in the main chain + pindex = locator.GetBlockIndex(); + if (pindex) + pindex = pindex->pnext; + } + + // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end + vector vHeaders; + int nLimit = 2000; + printf("getheaders %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().c_str()); + for (; pindex; pindex = pindex->pnext) + { + vHeaders.push_back(pindex->GetBlockHeader()); + if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) + break; + } + pfrom->PushMessage("headers", vHeaders); + } + + + else if (strCommand == "tx") + { + vector vWorkQueue; + vector vEraseQueue; + CDataStream vMsg(vRecv); + CTransaction tx; + vRecv >> tx; + + CInv inv(MSG_TX, tx.GetHash()); + pfrom->AddInventoryKnown(inv); + + bool fMissingInputs = false; + CValidationState state; + if (tx.AcceptToMemoryPool(state, true, true, &fMissingInputs)) + { + RelayTransaction(tx, inv.hash); + mapAlreadyAskedFor.erase(inv); + vWorkQueue.push_back(inv.hash); + vEraseQueue.push_back(inv.hash); + + printf("AcceptToMemoryPool: %s %s : accepted %s (poolsz %"PRIszu")\n", + pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str(), + tx.GetHash().ToString().c_str(), + mempool.mapTx.size()); + + // Recursively process any orphan transactions that depended on this one + for (unsigned int i = 0; i < vWorkQueue.size(); i++) + { + uint256 hashPrev = vWorkQueue[i]; + for (set::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin(); + mi != mapOrphanTransactionsByPrev[hashPrev].end(); + ++mi) + { + const uint256& orphanHash = *mi; + const CTransaction& orphanTx = mapOrphanTransactions[orphanHash]; + bool fMissingInputs2 = false; + // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan + // resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get + // anyone relaying LegitTxX banned) + CValidationState stateDummy; + + if (tx.AcceptToMemoryPool(stateDummy, true, true, &fMissingInputs2)) + { + printf(" accepted orphan tx %s\n", orphanHash.ToString().c_str()); + RelayTransaction(orphanTx, orphanHash); + mapAlreadyAskedFor.erase(CInv(MSG_TX, orphanHash)); + vWorkQueue.push_back(orphanHash); + vEraseQueue.push_back(orphanHash); + } + else if (!fMissingInputs2) + { + // invalid or too-little-fee orphan + vEraseQueue.push_back(orphanHash); + printf(" removed orphan tx %s\n", orphanHash.ToString().c_str()); + } + } + } + + BOOST_FOREACH(uint256 hash, vEraseQueue) + EraseOrphanTx(hash); + } + else if (fMissingInputs) + { + AddOrphanTx(tx); + + // DoS prevention: do not allow mapOrphanTransactions to grow unbounded + unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS); + if (nEvicted > 0) + printf("mapOrphan overflow, removed %u tx\n", nEvicted); + } + int nDoS = 0; + if (state.IsInvalid(nDoS)) + { + printf("%s from %s %s was not accepted into the memory pool\n", tx.GetHash().ToString().c_str(), + pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str()); + if (nDoS > 0) + pfrom->Misbehaving(nDoS); + } + } + + + else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing + { + CBlock block; + vRecv >> block; + + printf("received block %s\n", block.GetHash().ToString().c_str()); + // block.print(); + + CInv inv(MSG_BLOCK, block.GetHash()); + pfrom->AddInventoryKnown(inv); + + CValidationState state; + if (ProcessBlock(state, pfrom, &block) || state.CorruptionPossible()) + mapAlreadyAskedFor.erase(inv); + int nDoS = 0; + if (state.IsInvalid(nDoS)) + if (nDoS > 0) + pfrom->Misbehaving(nDoS); + } + + + else if (strCommand == "getaddr") + { + pfrom->vAddrToSend.clear(); + vector vAddr = addrman.GetAddr(); + BOOST_FOREACH(const CAddress &addr, vAddr) + pfrom->PushAddress(addr); + } + + + else if (strCommand == "mempool") + { + std::vector vtxid; + LOCK2(mempool.cs, pfrom->cs_filter); + mempool.queryHashes(vtxid); + vector vInv; + BOOST_FOREACH(uint256& hash, vtxid) { + CInv inv(MSG_TX, hash); + if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(mempool.lookup(hash), hash)) || + (!pfrom->pfilter)) + vInv.push_back(inv); + if (vInv.size() == MAX_INV_SZ) + break; + } + if (vInv.size() > 0) + pfrom->PushMessage("inv", vInv); + } + + + else if (strCommand == "ping") + { + if (pfrom->nVersion > BIP0031_VERSION) + { + uint64 nonce = 0; + vRecv >> nonce; + // Echo the message back with the nonce. This allows for two useful features: + // + // 1) A remote node can quickly check if the connection is operational + // 2) Remote nodes can measure the latency of the network thread. If this node + // is overloaded it won't respond to pings quickly and the remote node can + // avoid sending us more work, like chain download requests. + // + // The nonce stops the remote getting confused between different pings: without + // it, if the remote node sends a ping once per second and this node takes 5 + // seconds to respond to each, the 5th ping the remote sends would appear to + // return very quickly. + pfrom->PushMessage("pong", nonce); + } + } + + + else if (strCommand == "alert") + { + CAlert alert; + vRecv >> alert; + + uint256 alertHash = alert.GetHash(); + if (pfrom->setKnown.count(alertHash) == 0) + { + if (alert.ProcessAlert()) + { + // Relay + pfrom->setKnown.insert(alertHash); + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + alert.RelayTo(pnode); + } + } + else { + // Small DoS penalty so peers that send us lots of + // duplicate/expired/invalid-signature/whatever alerts + // eventually get banned. + // This isn't a Misbehaving(100) (immediate ban) because the + // peer might be an older or different implementation with + // a different signature key, etc. + pfrom->Misbehaving(10); + } + } + } + + + else if (!fBloomFilters && + (strCommand == "filterload" || + strCommand == "filteradd" || + strCommand == "filterclear")) + { + pfrom->CloseSocketDisconnect(); + return error("peer %s attempted to set a bloom filter even though we do not advertise that service", + pfrom->addr.ToString().c_str()); + } + + else if (strCommand == "filterload") + { + CBloomFilter filter; + vRecv >> filter; + + if (!filter.IsWithinSizeConstraints()) + // There is no excuse for sending a too-large filter + pfrom->Misbehaving(100); + else + { + LOCK(pfrom->cs_filter); + delete pfrom->pfilter; + pfrom->pfilter = new CBloomFilter(filter); + pfrom->pfilter->UpdateEmptyFull(); + } + pfrom->fRelayTxes = true; + } + + + else if (strCommand == "filteradd") + { + vector vData; + vRecv >> vData; + + // Nodes must NEVER send a data item > 520 bytes (the max size for a script data object, + // and thus, the maximum size any matched object can have) in a filteradd message + if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) + { + pfrom->Misbehaving(100); + } else { + LOCK(pfrom->cs_filter); + if (pfrom->pfilter) + pfrom->pfilter->insert(vData); + else + pfrom->Misbehaving(100); + } + } + + + else if (strCommand == "filterclear") + { + LOCK(pfrom->cs_filter); + delete pfrom->pfilter; + pfrom->pfilter = new CBloomFilter(); + pfrom->fRelayTxes = true; + } + + + else + { + // Ignore unknown commands for extensibility + } + + + // Update the last seen time for this node's address + if (pfrom->fNetworkNode) + if (strCommand == "version" || strCommand == "addr" || strCommand == "inv" || strCommand == "getdata" || strCommand == "ping") + AddressCurrentlyConnected(pfrom->addr); + + + return true; +} + +// requires LOCK(cs_vRecvMsg) +bool ProcessMessages(CNode* pfrom) +{ + //if (fDebug) + // printf("ProcessMessages(%zu messages)\n", pfrom->vRecvMsg.size()); + + // + // Message format + // (4) message start + // (12) command + // (4) size + // (4) checksum + // (x) data + // + bool fOk = true; + + if (!pfrom->vRecvGetData.empty()) + ProcessGetData(pfrom); + + // this maintains the order of responses + if (!pfrom->vRecvGetData.empty()) return fOk; + + std::deque::iterator it = pfrom->vRecvMsg.begin(); + while (!pfrom->fDisconnect && it != pfrom->vRecvMsg.end()) { + // Don't bother if send buffer is too full to respond anyway + if (pfrom->nSendSize >= SendBufferSize()) + break; + + // get next message + CNetMessage& msg = *it; + + //if (fDebug) + // printf("ProcessMessages(message %u msgsz, %zu bytes, complete:%s)\n", + // msg.hdr.nMessageSize, msg.vRecv.size(), + // msg.complete() ? "Y" : "N"); + + // end, if an incomplete message is found + if (!msg.complete()) + break; + + // at this point, any failure means we can delete the current message + it++; + + // Scan for message start + if (memcmp(msg.hdr.pchMessageStart, pchMessageStart, sizeof(pchMessageStart)) != 0) { + printf("\n\nPROCESSMESSAGE: INVALID MESSAGESTART\n\n"); + fOk = false; + break; + } + + // Read header + CMessageHeader& hdr = msg.hdr; + if (!hdr.IsValid()) + { + printf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str()); + continue; + } + string strCommand = hdr.GetCommand(); + + // Message size + unsigned int nMessageSize = hdr.nMessageSize; + + // Checksum + CDataStream& vRecv = msg.vRecv; + uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); + unsigned int nChecksum = 0; + memcpy(&nChecksum, &hash, sizeof(nChecksum)); + if (nChecksum != hdr.nChecksum) + { + printf("ProcessMessages(%s, %u bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", + strCommand.c_str(), nMessageSize, nChecksum, hdr.nChecksum); + continue; + } + + // Process message + bool fRet = false; + try + { + { + LOCK(cs_main); + fRet = ProcessMessage(pfrom, strCommand, vRecv); + } + boost::this_thread::interruption_point(); + } + catch (std::ios_base::failure& e) + { + if (strstr(e.what(), "end of data")) + { + // Allow exceptions from under-length message on vRecv + printf("ProcessMessages(%s, %u bytes) : Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand.c_str(), nMessageSize, e.what()); + } + else if (strstr(e.what(), "size too large")) + { + // Allow exceptions from over-long size + printf("ProcessMessages(%s, %u bytes) : Exception '%s' caught\n", strCommand.c_str(), nMessageSize, e.what()); + } + else + { + PrintExceptionContinue(&e, "ProcessMessages()"); + } + } + catch (boost::thread_interrupted) { + throw; + } + catch (std::exception& e) { + PrintExceptionContinue(&e, "ProcessMessages()"); + } catch (...) { + PrintExceptionContinue(NULL, "ProcessMessages()"); + } + + if (!fRet) + printf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand.c_str(), nMessageSize); + + break; + } + + // In case the connection got shut down, its receive buffer was wiped + if (!pfrom->fDisconnect) + pfrom->vRecvMsg.erase(pfrom->vRecvMsg.begin(), it); + + return fOk; +} + + +bool SendMessages(CNode* pto, bool fSendTrickle) +{ + TRY_LOCK(cs_main, lockMain); + if (lockMain) { + // Don't send anything until we get their version message + if (pto->nVersion == 0) + return true; + + // Keep-alive ping. We send a nonce of zero because we don't use it anywhere + // right now. + if (pto->nLastSend && GetTime() - pto->nLastSend > 30 * 60 && pto->vSendMsg.empty()) { + uint64 nonce = 0; + if (pto->nVersion > BIP0031_VERSION) + pto->PushMessage("ping", nonce); + else + pto->PushMessage("ping"); + } + + // Start block sync + if (pto->fStartSync && !fImporting && !fReindex) { + pto->fStartSync = false; + pto->PushGetBlocks(pindexBest, uint256(0)); + } + + // Resend wallet transactions that haven't gotten in a block yet + // Except during reindex, importing and IBD, when old wallet + // transactions become unconfirmed and spams other nodes. + if (!fReindex && !fImporting && !IsInitialBlockDownload()) + { + ResendWalletTransactions(); + } + + // Address refresh broadcast + static int64 nLastRebroadcast; + if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60)) + { + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + // Periodically clear setAddrKnown to allow refresh broadcasts + if (nLastRebroadcast) + pnode->setAddrKnown.clear(); + + // Rebroadcast our address + if (!fNoListen) + { + CAddress addr = GetLocalAddress(&pnode->addr); + if (addr.IsRoutable()) + pnode->PushAddress(addr); + } + } + } + nLastRebroadcast = GetTime(); + } + + // + // Message: addr + // + if (fSendTrickle) + { + vector vAddr; + vAddr.reserve(pto->vAddrToSend.size()); + BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend) + { + // returns true if wasn't already contained in the set + if (pto->setAddrKnown.insert(addr).second) + { + vAddr.push_back(addr); + // receiver rejects addr messages larger than 1000 + if (vAddr.size() >= 1000) + { + pto->PushMessage("addr", vAddr); + vAddr.clear(); + } + } + } + pto->vAddrToSend.clear(); + if (!vAddr.empty()) + pto->PushMessage("addr", vAddr); + } + + + // + // Message: inventory + // + vector vInv; + vector vInvWait; + { + LOCK(pto->cs_inventory); + vInv.reserve(pto->vInventoryToSend.size()); + vInvWait.reserve(pto->vInventoryToSend.size()); + BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend) + { + if (pto->setInventoryKnown.count(inv)) + continue; + + // trickle out tx inv to protect privacy + if (inv.type == MSG_TX && !fSendTrickle) + { + // 1/4 of tx invs blast to all immediately + static uint256 hashSalt; + if (hashSalt == 0) + hashSalt = GetRandHash(); + uint256 hashRand = inv.hash ^ hashSalt; + hashRand = Hash(BEGIN(hashRand), END(hashRand)); + bool fTrickleWait = ((hashRand & 3) != 0); + + // always trickle our own transactions + if (!fTrickleWait) + { + CWalletTx wtx; + if (GetTransaction(inv.hash, wtx)) + if (wtx.fFromMe) + fTrickleWait = true; + } + + if (fTrickleWait) + { + vInvWait.push_back(inv); + continue; + } + } + + // returns true if wasn't already contained in the set + if (pto->setInventoryKnown.insert(inv).second) + { + vInv.push_back(inv); + if (vInv.size() >= 1000) + { + pto->PushMessage("inv", vInv); + vInv.clear(); + } + } + } + pto->vInventoryToSend = vInvWait; + } + if (!vInv.empty()) + pto->PushMessage("inv", vInv); + + + // + // Message: getdata + // + vector vGetData; + int64 nNow = GetTime() * 1000000; + while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) + { + const CInv& inv = (*pto->mapAskFor.begin()).second; + if (!AlreadyHave(inv)) + { + if (fDebugNet) + printf("sending getdata: %s\n", inv.ToString().c_str()); + vGetData.push_back(inv); + if (vGetData.size() >= 1000) + { + pto->PushMessage("getdata", vGetData); + vGetData.clear(); + } + } + pto->mapAskFor.erase(pto->mapAskFor.begin()); + } + if (!vGetData.empty()) + pto->PushMessage("getdata", vGetData); + + } + return true; +} + + + + + + + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CryptoLottoTokenMiner +// + +int static FormatHashBlocks(void* pbuffer, unsigned int len) +{ + unsigned char* pdata = (unsigned char*)pbuffer; + unsigned int blocks = 1 + ((len + 8) / 64); + unsigned char* pend = pdata + 64 * blocks; + memset(pdata + len, 0, 64 * blocks - len); + pdata[len] = 0x80; + unsigned int bits = len * 8; + pend[-1] = (bits >> 0) & 0xff; + pend[-2] = (bits >> 8) & 0xff; + pend[-3] = (bits >> 16) & 0xff; + pend[-4] = (bits >> 24) & 0xff; + return blocks; +} + +static const unsigned int pSHA256InitState[8] = +{0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; + +void SHA256Transform(void* pstate, void* pinput, const void* pinit) +{ + SHA256_CTX ctx; + unsigned char data[64]; + + SHA256_Init(&ctx); + + for (int i = 0; i < 16; i++) + ((uint32_t*)data)[i] = ByteReverse(((uint32_t*)pinput)[i]); + + for (int i = 0; i < 8; i++) + ctx.h[i] = ((uint32_t*)pinit)[i]; + + SHA256_Update(&ctx, data, sizeof(data)); + for (int i = 0; i < 8; i++) + ((uint32_t*)pstate)[i] = ctx.h[i]; +} + +// Some explaining would be appreciated +class COrphan +{ +public: + CTransaction* ptx; + set setDependsOn; + double dPriority; + double dFeePerKb; + + COrphan(CTransaction* ptxIn) + { + ptx = ptxIn; + dPriority = dFeePerKb = 0; + } + + void print() const + { + printf("COrphan(hash=%s, dPriority=%.1f, dFeePerKb=%.1f)\n", + ptx->GetHash().ToString().c_str(), dPriority, dFeePerKb); + BOOST_FOREACH(uint256 hash, setDependsOn) + printf(" setDependsOn %s\n", hash.ToString().c_str()); + } +}; + + +uint64 nLastBlockTx = 0; +uint64 nLastBlockSize = 0; + +// We want to sort transactions by priority and fee, so: +typedef boost::tuple TxPriority; +class TxPriorityCompare +{ + bool byFee; +public: + TxPriorityCompare(bool _byFee) : byFee(_byFee) { } + bool operator()(const TxPriority& a, const TxPriority& b) + { + if (byFee) + { + if (a.get<1>() == b.get<1>()) + return a.get<0>() < b.get<0>(); + return a.get<1>() < b.get<1>(); + } + else + { + if (a.get<0>() == b.get<0>()) + return a.get<1>() < b.get<1>(); + return a.get<0>() < b.get<0>(); + } + } +}; + +CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) +{ + // Create new block + auto_ptr pblocktemplate(new CBlockTemplate()); + if(!pblocktemplate.get()) + return NULL; + CBlock *pblock = &pblocktemplate->block; // pointer for convenience + + // Create coinbase tx + CTransaction txNew; + txNew.vin.resize(1); + txNew.vin[0].prevout.SetNull(); + txNew.vout.resize(1); + txNew.vout[0].scriptPubKey = scriptPubKeyIn; + + // Add our coinbase tx as first transaction + pblock->vtx.push_back(txNew); + pblocktemplate->vTxFees.push_back(-1); // updated at end + pblocktemplate->vTxSigOps.push_back(-1); // updated at end + + // Largest block you're willing to create: + unsigned int nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE); + // Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity: + nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize)); + + // How much of the block should be dedicated to high-priority transactions, + // included regardless of the fees they pay + unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", DEFAULT_BLOCK_PRIORITY_SIZE); + nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize); + + // Minimum block size you want to create; block will be filled with free transactions + // until there are no more or the block reaches this size: + unsigned int nBlockMinSize = GetArg("-blockminsize", 0); + nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize); + + // Collect memory pool transactions into the block + int64 nFees = 0; + { + LOCK2(cs_main, mempool.cs); + CBlockIndex* pindexPrev = pindexBest; + CCoinsViewCache view(*pcoinsTip, true); + + // Priority order to process transactions + list vOrphan; // list memory doesn't move + map > mapDependers; + bool fPrintPriority = GetBoolArg("-printpriority"); + + // This vector will be sorted into a priority queue: + vector vecPriority; + vecPriority.reserve(mempool.mapTx.size()); + for (map::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi) + { + CTransaction& tx = (*mi).second; + if (tx.IsCoinBase() || !tx.IsFinal()) + continue; + + COrphan* porphan = NULL; + double dPriority = 0; + int64 nTotalIn = 0; + bool fMissingInputs = false; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + // Read prev transaction + if (!view.HaveCoins(txin.prevout.hash)) + { + // This should never happen; all transactions in the memory + // pool should connect to either transactions in the chain + // or other transactions in the memory pool. + if (!mempool.mapTx.count(txin.prevout.hash)) + { + printf("ERROR: mempool transaction missing input\n"); + if (fDebug) assert("mempool transaction missing input" == 0); + fMissingInputs = true; + if (porphan) + vOrphan.pop_back(); + break; + } + + // Has to wait for dependencies + if (!porphan) + { + // Use list for automatic deletion + vOrphan.push_back(COrphan(&tx)); + porphan = &vOrphan.back(); + } + mapDependers[txin.prevout.hash].push_back(porphan); + porphan->setDependsOn.insert(txin.prevout.hash); + nTotalIn += mempool.mapTx[txin.prevout.hash].vout[txin.prevout.n].nValue; + continue; + } + const CCoins &coins = view.GetCoins(txin.prevout.hash); + + int64 nValueIn = coins.vout[txin.prevout.n].nValue; + nTotalIn += nValueIn; + + int nConf = pindexPrev->nHeight - coins.nHeight + 1; + + dPriority += (double)nValueIn * nConf; + } + if (fMissingInputs) continue; + + // Priority is sum(valuein * age) / txsize + unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + dPriority /= nTxSize; + + // This is a more accurate fee-per-kilobyte than is used by the client code, because the + // client code rounds up the size to the nearest 1K. That's good, because it gives an + // incentive to create smaller transactions. + double dFeePerKb = double(nTotalIn-tx.GetValueOut()) / (double(nTxSize)/1000.0); + + if (porphan) + { + porphan->dPriority = dPriority; + porphan->dFeePerKb = dFeePerKb; + } + else + vecPriority.push_back(TxPriority(dPriority, dFeePerKb, &(*mi).second)); + } + + // Collect transactions into block + uint64 nBlockSize = 1000; + uint64 nBlockTx = 0; + int nBlockSigOps = 100; + bool fSortedByFee = (nBlockPrioritySize <= 0); + + TxPriorityCompare comparer(fSortedByFee); + std::make_heap(vecPriority.begin(), vecPriority.end(), comparer); + + while (!vecPriority.empty()) + { + // Take highest priority transaction off the priority queue: + double dPriority = vecPriority.front().get<0>(); + double dFeePerKb = vecPriority.front().get<1>(); + CTransaction& tx = *(vecPriority.front().get<2>()); + + std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer); + vecPriority.pop_back(); + + // Size limits + unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + if (nBlockSize + nTxSize >= nBlockMaxSize) + continue; + + // Legacy limits on sigOps: + unsigned int nTxSigOps = tx.GetLegacySigOpCount(); + if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) + continue; + + // Skip free transactions if we're past the minimum block size: + if (fSortedByFee && (dFeePerKb < CTransaction::nMinTxFee) && (nBlockSize + nTxSize >= nBlockMinSize)) + continue; + + // Prioritize by fee once past the priority size or we run out of high-priority + // transactions: + if (!fSortedByFee && + ((nBlockSize + nTxSize >= nBlockPrioritySize) || (dPriority < COIN * 576 / 250))) + { + fSortedByFee = true; + comparer = TxPriorityCompare(fSortedByFee); + std::make_heap(vecPriority.begin(), vecPriority.end(), comparer); + } + + if (!tx.HaveInputs(view)) + continue; + + int64 nTxFees = tx.GetValueIn(view)-tx.GetValueOut(); + + nTxSigOps += tx.GetP2SHSigOpCount(view); + if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) + continue; + + CValidationState state; + if (!tx.CheckInputs(state, view, true, SCRIPT_VERIFY_P2SH)) + continue; + + CTxUndo txundo; + uint256 hash = tx.GetHash(); + tx.UpdateCoins(state, view, txundo, pindexPrev->nHeight+1, hash); + + // Added + pblock->vtx.push_back(tx); + pblocktemplate->vTxFees.push_back(nTxFees); + pblocktemplate->vTxSigOps.push_back(nTxSigOps); + nBlockSize += nTxSize; + ++nBlockTx; + nBlockSigOps += nTxSigOps; + nFees += nTxFees; + + if (fPrintPriority) + { + printf("priority %.1f feeperkb %.1f txid %s\n", + dPriority, dFeePerKb, tx.GetHash().ToString().c_str()); + } + + // Add transactions that depend on this one to the priority queue + if (mapDependers.count(hash)) + { + BOOST_FOREACH(COrphan* porphan, mapDependers[hash]) + { + if (!porphan->setDependsOn.empty()) + { + porphan->setDependsOn.erase(hash); + if (porphan->setDependsOn.empty()) + { + vecPriority.push_back(TxPriority(porphan->dPriority, porphan->dFeePerKb, porphan->ptx)); + std::push_heap(vecPriority.begin(), vecPriority.end(), comparer); + } + } + } + } + } + + nLastBlockTx = nBlockTx; + nLastBlockSize = nBlockSize; + printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize); + + pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees, pindexPrev->GetBlockHash()); + pblocktemplate->vTxFees[0] = -nFees; + + // Fill in header + pblock->hashPrevBlock = pindexPrev->GetBlockHash(); + pblock->UpdateTime(pindexPrev); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock); + pblock->nNonce = 0; + pblock->vtx[0].vin[0].scriptSig = CScript() << OP_0 << OP_0; + pblocktemplate->vTxSigOps[0] = pblock->vtx[0].GetLegacySigOpCount(); + + CBlockIndex indexDummy(*pblock); + indexDummy.pprev = pindexPrev; + indexDummy.nHeight = pindexPrev->nHeight + 1; + CCoinsViewCache viewNew(*pcoinsTip, true); + CValidationState state; + if (!pblock->ConnectBlock(state, &indexDummy, viewNew, true)) + throw std::runtime_error("CreateNewBlock() : ConnectBlock failed"); + } + + return pblocktemplate.release(); +} + +CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey) +{ + CPubKey pubkey; + if (!reservekey.GetReservedKey(pubkey)) + return NULL; + + CScript scriptPubKey = CScript() << pubkey << OP_CHECKSIG; + return CreateNewBlock(scriptPubKey); +} + +void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce) +{ + // Update nExtraNonce + static uint256 hashPrevBlock; + if (hashPrevBlock != pblock->hashPrevBlock) + { + nExtraNonce = 0; + hashPrevBlock = pblock->hashPrevBlock; + } + ++nExtraNonce; + unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2 + pblock->vtx[0].vin[0].scriptSig = (CScript() << nHeight << CBigNum(nExtraNonce)) + COINBASE_FLAGS; + assert(pblock->vtx[0].vin[0].scriptSig.size() <= 100); + + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); +} + + +void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1) +{ + // + // Pre-build hash buffers + // + struct + { + struct unnamed2 + { + int nVersion; + uint256 hashPrevBlock; + uint256 hashMerkleRoot; + unsigned int nTime; + unsigned int nBits; + unsigned int nNonce; + } + block; + unsigned char pchPadding0[64]; + uint256 hash1; + unsigned char pchPadding1[64]; + } + tmp; + memset(&tmp, 0, sizeof(tmp)); + + tmp.block.nVersion = pblock->nVersion; + tmp.block.hashPrevBlock = pblock->hashPrevBlock; + tmp.block.hashMerkleRoot = pblock->hashMerkleRoot; + tmp.block.nTime = pblock->nTime; + tmp.block.nBits = pblock->nBits; + tmp.block.nNonce = pblock->nNonce; + + FormatHashBlocks(&tmp.block, sizeof(tmp.block)); + FormatHashBlocks(&tmp.hash1, sizeof(tmp.hash1)); + + // Byte swap all the input buffer + for (unsigned int i = 0; i < sizeof(tmp)/4; i++) + ((unsigned int*)&tmp)[i] = ByteReverse(((unsigned int*)&tmp)[i]); + + // Precalc the first half of the first hash, which stays constant + SHA256Transform(pmidstate, &tmp.block, pSHA256InitState); + + memcpy(pdata, &tmp.block, 128); + memcpy(phash1, &tmp.hash1, 64); +} + + +bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) +{ + uint256 hash = pblock->GetHash(); + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + + if (hash > hashTarget) + return false; + + //// debug print + printf("CryptoLottoTokenMiner:\n"); + printf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str()); + pblock->print(); + printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str()); + + // Found a solution + { + LOCK(cs_main); + if (pblock->hashPrevBlock != hashBestChain) + return error("CryptoLottoTokenMiner : generated block is stale"); + + // Remove key from key pool + reservekey.KeepKey(); + + // Track how many getdata requests this block gets + { + LOCK(wallet.cs_wallet); + wallet.mapRequestCount[pblock->GetHash()] = 0; + } + + // Process this block the same as if we had received it from another node + CValidationState state; + if (!ProcessBlock(state, NULL, pblock)) + return error("CryptoLottoTokenMiner : ProcessBlock, block not accepted"); + } + + return true; +} + +void static CryptoLottoTokenMiner(CWallet *pwallet) +{ + printf("CryptoLottoTokenMiner not supported!\n"); + SetThreadPriority(THREAD_PRIORITY_LOWEST); + RenameThread("CryptoLottoToken-miner"); + + // Each thread has its own key and counter + CReserveKey reservekey(pwallet); + unsigned int nExtraNonce = 0; + + try { loop { + while (1 == 1) //while (vNodes.empty()) + MilliSleep(1000); + } } + catch (boost::thread_interrupted) + { + printf("CryptoLottoTokenMiner terminated\n"); + throw; + } +} + +void GenerateBitcoins(bool fGenerate, CWallet* pwallet) +{ + static boost::thread_group* minerThreads = NULL; + + int nThreads = GetArg("-genproclimit", -1); + if (nThreads < 0) + nThreads = boost::thread::hardware_concurrency(); + + if (minerThreads != NULL) + { + minerThreads->interrupt_all(); + delete minerThreads; + minerThreads = NULL; + } + + if (nThreads == 0 || !fGenerate) + return; + + minerThreads = new boost::thread_group(); + for (int i = 0; i < nThreads; i++) + minerThreads->create_thread(boost::bind(&CryptoLottoTokenMiner, pwallet)); +} + +// Amount compression: +// * If the amount is 0, output 0 +// * first, divide the amount (in base units) by the largest power of 10 possible; call the exponent e (e is max 9) +// * if e<9, the last digit of the resulting number cannot be 0; store it as d, and drop it (divide by 10) +// * call the result n +// * output 1 + 10*(9*n + d - 1) + e +// * if e==9, we only know the resulting number is not zero, so output 1 + 10*(n - 1) + 9 +// (this is decodable, as d is in [1-9] and e is in [0-9]) + +uint64 CTxOutCompressor::CompressAmount(uint64 n) +{ + if (n == 0) + return 0; + int e = 0; + while (((n % 10) == 0) && e < 9) { + n /= 10; + e++; + } + if (e < 9) { + int d = (n % 10); + assert(d >= 1 && d <= 9); + n /= 10; + return 1 + (n*9 + d - 1)*10 + e; + } else { + return 1 + (n - 1)*10 + 9; + } +} + +uint64 CTxOutCompressor::DecompressAmount(uint64 x) +{ + // x = 0 OR x = 1+10*(9*n + d - 1) + e OR x = 1+10*(n - 1) + 9 + if (x == 0) + return 0; + x--; + // x = 10*(9*n + d - 1) + e + int e = x % 10; + x /= 10; + uint64 n = 0; + if (e < 9) { + // x = 9*n + d - 1 + int d = (x % 9) + 1; + x /= 9; + // x = n + n = x*10 + d; + } else { + n = x+1; + } + while (e) { + n *= 10; + e--; + } + return n; +} + + +class CMainCleanup +{ +public: + CMainCleanup() {} + ~CMainCleanup() { + // block headers + std::map::iterator it1 = mapBlockIndex.begin(); + for (; it1 != mapBlockIndex.end(); it1++) + delete (*it1).second; + mapBlockIndex.clear(); + + // orphan blocks + std::map::iterator it2 = mapOrphanBlocks.begin(); + for (; it2 != mapOrphanBlocks.end(); it2++) + delete (*it2).second; + mapOrphanBlocks.clear(); + + // orphan transactions + mapOrphanTransactions.clear(); + } +} instance_of_cmaincleanup; diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..ea16d58 --- /dev/null +++ b/src/main.h @@ -0,0 +1,2292 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_MAIN_H +#define BITCOIN_MAIN_H + +#include "bignum.h" +#include "sync.h" +#include "net.h" +#include "script.h" + +#include + +class CWallet; +class CBlock; +class CBlockIndex; +class CKeyItem; +class CReserveKey; + +class CAddress; +class CInv; +class CNode; + +// gravity update + +struct CBlockIndexWorkComparator; + +/** The maximum allowed size for a serialized block, in bytes (network rule) */ +static const unsigned int MAX_BLOCK_SIZE = 1000000; // 1000KB block hard limit +/** Obsolete: maximum size for mined blocks */ +static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2; // 500KB block soft limit +/** Default for -blockmaxsize, maximum size for mined blocks **/ +static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 250000; +/** Default for -blockprioritysize, maximum space for zero/low-fee transactions **/ +static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = 17000; +/** The maximum size for transactions we're willing to relay/mine */ +static const unsigned int MAX_STANDARD_TX_SIZE = 100000; +/** The maximum allowed number of signature check operations in a block (network rule) */ +static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; +/** The maximum number of orphan transactions kept in memory */ +static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100; +/** The maximum number of entries in an 'inv' protocol message */ +static const unsigned int MAX_INV_SZ = 50000; +/** The maximum size of a blk?????.dat file (since 0.8) */ +static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB +/** The pre-allocation chunk size for blk?????.dat files (since 0.8) */ +static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB +/** The pre-allocation chunk size for rev?????.dat files (since 0.8) */ +static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB +/** Fake height value used in CCoins to signify they are only in the memory pool (since 0.8) */ +static const unsigned int MEMPOOL_HEIGHT = 0x7FFFFFFF; +/** Dust Soft Limit, allowed with additional fee per output */ +static const int64 DUST_SOFT_LIMIT = 0.1 * COIN; +/** Dust Hard Limit, ignored as wallet inputs (mininput default) */ +static const int64 DUST_HARD_LIMIT = 0.001 * COIN; +/** No amount larger than this (in satoshi) is valid */ +static const int64 MAX_MONEY = 25000000 * COIN; // CryptoLottoToken: maximum of N coins (given some randomness), max transaction 10,000,000,000 for now +inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } +/** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */ +static const int COINBASE_MATURITY = 60; +/** Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp. */ +static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC +/** Maximum number of script-checking threads allowed */ +static const int MAX_SCRIPTCHECK_THREADS = 16; + +static const int DIFF_FILTER_THRESHOLD_TESTNET = 30000; +static const int DIFF_FILTER_THRESHOLD = 30000; + +#ifdef USE_UPNP +static const int fHaveUPnP = true; +#else +static const int fHaveUPnP = false; +#endif + + +extern CScript COINBASE_FLAGS; + + + + + + +extern CCriticalSection cs_main; +extern std::map mapBlockIndex; +extern std::set setBlockIndexValid; +extern uint256 hashGenesisBlock; +extern CBlockIndex* pindexGenesisBlock; +extern int nBestHeight; +extern uint256 nBestChainWork; +extern uint256 nBestInvalidWork; +extern uint256 hashBestChain; +extern CBlockIndex* pindexBest; +extern unsigned int nTransactionsUpdated; +extern uint64 nLastBlockTx; +extern uint64 nLastBlockSize; +extern const std::string strMessageMagic; +extern double dHashesPerSec; +extern int64 nHPSTimerStart; +extern int64 nTimeBestReceived; +extern CCriticalSection cs_setpwalletRegistered; +extern std::set setpwalletRegistered; +extern unsigned char pchMessageStart[4]; +extern bool fImporting; +extern bool fReindex; +extern bool fBenchmark; +extern int nScriptCheckThreads; +extern bool fTxIndex; +extern unsigned int nCoinCacheSize; + +// Settings +extern int64 nTransactionFee; +extern int64 nMinimumInputValue; + +// Minimum disk space required - used in CheckDiskSpace() +static const uint64 nMinDiskSpace = 52428800; + + +class CReserveKey; +class CCoinsDB; +class CBlockTreeDB; +struct CDiskBlockPos; +class CCoins; +class CTxUndo; +class CCoinsView; +class CCoinsViewCache; +class CScriptCheck; +class CValidationState; + +struct CBlockTemplate; + +/** Register a wallet to receive updates from core */ +void RegisterWallet(CWallet* pwalletIn); +/** Unregister a wallet from core */ +void UnregisterWallet(CWallet* pwalletIn); +/** Push an updated transaction to all registered wallets */ +void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false); +/** Process an incoming block */ +bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp = NULL); +/** Check whether enough disk space is available for an incoming block */ +bool CheckDiskSpace(uint64 nAdditionalBytes = 0); +/** Open a block file (blk?????.dat) */ +FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false); +/** Open an undo file (rev?????.dat) */ +FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false); +/** Import blocks from an external file */ +bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp = NULL); +/** Initialize a new block tree database + block data on disk */ +bool InitBlockIndex(); +/** Load the block tree and coins database from disk */ +bool LoadBlockIndex(); +/** Unload database information */ +void UnloadBlockIndex(); +/** Verify consistency of the block and coin databases */ +bool VerifyDB(int nCheckLevel, int nCheckDepth); +/** Print the loaded block tree */ +void PrintBlockTree(); +/** Find a block by height in the currently-connected chain */ +CBlockIndex* FindBlockByHeight(int nHeight); +/** Process protocol messages received from a given node */ +bool ProcessMessages(CNode* pfrom); +/** Send queued protocol messages to be sent to a give node */ +bool SendMessages(CNode* pto, bool fSendTrickle); +/** Run an instance of the script checking thread */ +void ThreadScriptCheck(); +/** Run the miner threads */ +void GenerateBitcoins(bool fGenerate, CWallet* pwallet); +/** Generate a new block, without valid proof-of-work */ +CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn); +CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey); +/** Modify the extranonce in a block */ +void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce); +/** Do mining precalculation */ +void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1); +/** Check mined block */ +bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey); +/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */ +bool CheckProofOfWork(uint256 hash, unsigned int nBits); +/** Calculate the minimum amount of work a received block needs, without knowing its direct parent */ +unsigned int ComputeMinWork(unsigned int nBase, int64 nTime); +/** Get the number of active peers */ +int GetNumBlocksOfPeers(); +/** 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); +/** Retrieve a transaction (from memory pool, or from disk, if possible) */ +bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false); +/** Connect/disconnect blocks until pindexNew is the new tip of the active block chain */ +bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew); +/** Find the best known block, and make it the tip of the block chain */ +bool ConnectBestBlock(CValidationState &state); +/** Create a new block index entry for a given block hash */ +CBlockIndex * InsertBlockIndex(uint256 hash); +/** Verify a signature */ +bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); +/** Abort with a message */ +bool AbortNode(const std::string &msg); + + + + + + + + + + + +bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut); + +struct CDiskBlockPos +{ + int nFile; + unsigned int nPos; + + IMPLEMENT_SERIALIZE( + READWRITE(VARINT(nFile)); + READWRITE(VARINT(nPos)); + ) + + CDiskBlockPos() { + SetNull(); + } + + CDiskBlockPos(int nFileIn, unsigned int nPosIn) { + nFile = nFileIn; + nPos = nPosIn; + } + + friend bool operator==(const CDiskBlockPos &a, const CDiskBlockPos &b) { + return (a.nFile == b.nFile && a.nPos == b.nPos); + } + + friend bool operator!=(const CDiskBlockPos &a, const CDiskBlockPos &b) { + return !(a == b); + } + + void SetNull() { nFile = -1; nPos = 0; } + bool IsNull() const { return (nFile == -1); } +}; + +struct CDiskTxPos : public CDiskBlockPos +{ + unsigned int nTxOffset; // after header + + IMPLEMENT_SERIALIZE( + READWRITE(*(CDiskBlockPos*)this); + READWRITE(VARINT(nTxOffset)); + ) + + CDiskTxPos(const CDiskBlockPos &blockIn, unsigned int nTxOffsetIn) : CDiskBlockPos(blockIn.nFile, blockIn.nPos), nTxOffset(nTxOffsetIn) { + } + + CDiskTxPos() { + SetNull(); + } + + void SetNull() { + CDiskBlockPos::SetNull(); + nTxOffset = 0; + } +}; + + +/** An inpoint - a combination of a transaction and an index n into its vin */ +class CInPoint +{ +public: + CTransaction* ptx; + unsigned int n; + + CInPoint() { SetNull(); } + CInPoint(CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; } + void SetNull() { ptx = NULL; n = (unsigned int) -1; } + bool IsNull() const { return (ptx == NULL && n == (unsigned int) -1); } +}; + + + +/** An outpoint - a combination of a transaction hash and an index n into its vout */ +class COutPoint +{ +public: + uint256 hash; + unsigned int n; + + COutPoint() { SetNull(); } + COutPoint(uint256 hashIn, unsigned int nIn) { hash = hashIn; n = nIn; } + IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); ) + void SetNull() { hash = 0; n = (unsigned int) -1; } + bool IsNull() const { return (hash == 0 && n == (unsigned int) -1); } + + friend bool operator<(const COutPoint& a, const COutPoint& b) + { + return (a.hash < b.hash || (a.hash == b.hash && a.n < b.n)); + } + + friend bool operator==(const COutPoint& a, const COutPoint& b) + { + return (a.hash == b.hash && a.n == b.n); + } + + friend bool operator!=(const COutPoint& a, const COutPoint& b) + { + return !(a == b); + } + + std::string ToString() const + { + return strprintf("COutPoint(%s, %u)", hash.ToString().c_str(), n); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + +/** An input of a transaction. It contains the location of the previous + * transaction's output that it claims and a signature that matches the + * output's public key. + */ +class CTxIn +{ +public: + COutPoint prevout; + CScript scriptSig; + unsigned int nSequence; + + CTxIn() + { + nSequence = std::numeric_limits::max(); + } + + explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits::max()) + { + prevout = prevoutIn; + scriptSig = scriptSigIn; + nSequence = nSequenceIn; + } + + CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits::max()) + { + prevout = COutPoint(hashPrevTx, nOut); + scriptSig = scriptSigIn; + nSequence = nSequenceIn; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(prevout); + READWRITE(scriptSig); + READWRITE(nSequence); + ) + + bool IsFinal() const + { + return (nSequence == std::numeric_limits::max()); + } + + friend bool operator==(const CTxIn& a, const CTxIn& b) + { + return (a.prevout == b.prevout && + a.scriptSig == b.scriptSig && + a.nSequence == b.nSequence); + } + + friend bool operator!=(const CTxIn& a, const CTxIn& b) + { + return !(a == b); + } + + std::string ToString() const + { + std::string str; + str += "CTxIn("; + str += prevout.ToString(); + if (prevout.IsNull()) + str += strprintf(", coinbase %s", HexStr(scriptSig).c_str()); + else + str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24).c_str()); + if (nSequence != std::numeric_limits::max()) + str += strprintf(", nSequence=%u", nSequence); + str += ")"; + return str; + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + +/** An output of a transaction. It contains the public key that the next input + * must be able to sign with to claim it. + */ +class CTxOut +{ +public: + int64 nValue; + CScript scriptPubKey; + + CTxOut() + { + SetNull(); + } + + CTxOut(int64 nValueIn, CScript scriptPubKeyIn) + { + nValue = nValueIn; + scriptPubKey = scriptPubKeyIn; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(nValue); + READWRITE(scriptPubKey); + ) + + void SetNull() + { + nValue = -1; + scriptPubKey.clear(); + } + + bool IsNull() const + { + return (nValue == -1); + } + + uint256 GetHash() const + { + return SerializeHash(*this); + } + + friend bool operator==(const CTxOut& a, const CTxOut& b) + { + return (a.nValue == b.nValue && + a.scriptPubKey == b.scriptPubKey); + } + + friend bool operator!=(const CTxOut& a, const CTxOut& b) + { + return !(a == b); + } + + bool IsDust() const; + + std::string ToString() const + { + if (scriptPubKey.size() < 6) + return "CTxOut(error)"; + return strprintf("CTxOut(nValue=%"PRI64d".%08"PRI64d", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30).c_str()); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + +enum GetMinFee_mode +{ + GMF_BLOCK, + GMF_RELAY, + GMF_SEND, +}; + +/** The basic transaction that is broadcasted on the network and contained in + * blocks. A transaction can contain multiple inputs and outputs. + */ +class CTransaction +{ +public: + static int64 nMinTxFee; + static int64 nMinRelayTxFee; + static const int CURRENT_VERSION=1; + int nVersion; + std::vector vin; + std::vector vout; + unsigned int nLockTime; + + CTransaction() + { + SetNull(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(vin); + READWRITE(vout); + READWRITE(nLockTime); + ) + + void SetNull() + { + nVersion = CTransaction::CURRENT_VERSION; + vin.clear(); + vout.clear(); + nLockTime = 0; + } + + bool IsNull() const + { + return (vin.empty() && vout.empty()); + } + + uint256 GetHash() const + { + return SerializeHash(*this); + } + + bool IsFinal(int nBlockHeight=0, int64 nBlockTime=0) const + { + // Time based nLockTime implemented in 0.1.6 + if (nLockTime == 0) + return true; + if (nBlockHeight == 0) + nBlockHeight = nBestHeight; + if (nBlockTime == 0) + nBlockTime = GetAdjustedTime(); + if ((int64)nLockTime < ((int64)nLockTime < LOCKTIME_THRESHOLD ? (int64)nBlockHeight : nBlockTime)) + return true; + BOOST_FOREACH(const CTxIn& txin, vin) + if (!txin.IsFinal()) + return false; + return true; + } + + bool IsNewerThan(const CTransaction& old) const + { + if (vin.size() != old.vin.size()) + return false; + for (unsigned int i = 0; i < vin.size(); i++) + if (vin[i].prevout != old.vin[i].prevout) + return false; + + bool fNewer = false; + unsigned int nLowest = std::numeric_limits::max(); + for (unsigned int i = 0; i < vin.size(); i++) + { + if (vin[i].nSequence != old.vin[i].nSequence) + { + if (vin[i].nSequence <= nLowest) + { + fNewer = false; + nLowest = vin[i].nSequence; + } + if (old.vin[i].nSequence < nLowest) + { + fNewer = true; + nLowest = old.vin[i].nSequence; + } + } + } + return fNewer; + } + + bool IsCoinBase() const + { + return (vin.size() == 1 && vin[0].prevout.IsNull()); + } + + /** Check for standard transaction types + @return True if all outputs (scriptPubKeys) use only standard transaction forms + */ + bool IsStandard(std::string& strReason) const; + bool IsStandard() const + { + std::string strReason; + return IsStandard(strReason); + } + + /** Check for standard transaction types + @param[in] mapInputs Map of previous transactions that have outputs we're spending + @return True if all inputs (scriptSigs) use only standard transaction forms + */ + bool AreInputsStandard(CCoinsViewCache& mapInputs) const; + + /** Count ECDSA signature operations the old-fashioned (pre-0.6) way + @return number of sigops this transaction's outputs will produce when spent + */ + unsigned int GetLegacySigOpCount() const; + + /** Count ECDSA signature operations in pay-to-script-hash inputs. + + @param[in] mapInputs Map of previous transactions that have outputs we're spending + @return maximum number of sigops required to validate this transaction's inputs + */ + unsigned int GetP2SHSigOpCount(CCoinsViewCache& mapInputs) const; + + /** Amount of bitcoins spent by this transaction. + @return sum of all outputs (note: does not include fees) + */ + int64 GetValueOut() const + { + int64 nValueOut = 0; + BOOST_FOREACH(const CTxOut& txout, vout) + { + nValueOut += txout.nValue; + if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) + throw std::runtime_error("CTransaction::GetValueOut() : value out of range"); + } + return nValueOut; + } + + /** Amount of bitcoins coming in to this transaction + Note that lightweight clients may not know anything besides the hash of previous transactions, + so may not be able to calculate this. + + @param[in] mapInputs Map of previous transactions that have outputs we're spending + @return Sum of value of all inputs (scriptSigs) + */ + int64 GetValueIn(CCoinsViewCache& mapInputs) const; + + static bool AllowFree(double dPriority) + { + // Large (in bytes) low-priority (new, small-coin) transactions + // need a fee. + return dPriority > 2 * COIN * (60 * 24 / 1.5) / 250; // CryptoLottoToken: Priority cutoff is 2 CryptoLottoToken day / 250 bytes. + } + +// Apply the effects of this transaction on the UTXO set represented by view +void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash); + + int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=true, enum GetMinFee_mode mode=GMF_BLOCK) const; + + friend bool operator==(const CTransaction& a, const CTransaction& b) + { + return (a.nVersion == b.nVersion && + a.vin == b.vin && + a.vout == b.vout && + a.nLockTime == b.nLockTime); + } + + friend bool operator!=(const CTransaction& a, const CTransaction& b) + { + return !(a == b); + } + + + std::string ToString() const + { + std::string str; + str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%"PRIszu", vout.size=%"PRIszu", nLockTime=%u)\n", + GetHash().ToString().c_str(), + nVersion, + vin.size(), + vout.size(), + nLockTime); + for (unsigned int i = 0; i < vin.size(); i++) + str += " " + vin[i].ToString() + "\n"; + for (unsigned int i = 0; i < vout.size(); i++) + str += " " + vout[i].ToString() + "\n"; + return str; + } + + void print() const + { + printf("%s", ToString().c_str()); + } + + + // Check whether all prevouts of this transaction are present in the UTXO set represented by view + bool HaveInputs(CCoinsViewCache &view) const; + + // Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts) + // This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it + // instead of being performed inline. + bool CheckInputs(CValidationState &state, CCoinsViewCache &view, bool fScriptChecks = true, + unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, + std::vector *pvChecks = NULL) const; + + // Apply the effects of this transaction on the UTXO set represented by view + void UpdateCoins(CValidationState &state, CCoinsViewCache &view, CTxUndo &txundo, int nHeight, const uint256 &txhash) const; + + // Context-independent validity checks + bool CheckTransaction(CValidationState &state) const; + + // Try to accept this transaction into the memory pool + bool AcceptToMemoryPool(CValidationState &state, bool fCheckInputs=true, bool fLimitFree = true, bool* pfMissingInputs=NULL); + +protected: + static const CTxOut &GetOutputFor(const CTxIn& input, CCoinsViewCache& mapInputs); +}; + +/** wrapper for CTxOut that provides a more compact serialization */ +class CTxOutCompressor +{ +private: + CTxOut &txout; + +public: + static uint64 CompressAmount(uint64 nAmount); + static uint64 DecompressAmount(uint64 nAmount); + + CTxOutCompressor(CTxOut &txoutIn) : txout(txoutIn) { } + + IMPLEMENT_SERIALIZE(({ + if (!fRead) { + uint64 nVal = CompressAmount(txout.nValue); + READWRITE(VARINT(nVal)); + } else { + uint64 nVal = 0; + READWRITE(VARINT(nVal)); + txout.nValue = DecompressAmount(nVal); + } + CScriptCompressor cscript(REF(txout.scriptPubKey)); + READWRITE(cscript); + });) +}; + +/** Undo information for a CTxIn + * + * Contains the prevout's CTxOut being spent, and if this was the + * last output of the affected transaction, its metadata as well + * (coinbase or not, height, transaction version) + */ +class CTxInUndo +{ +public: + CTxOut txout; // the txout data before being spent + bool fCoinBase; // if the outpoint was the last unspent: whether it belonged to a coinbase + unsigned int nHeight; // if the outpoint was the last unspent: its height + int nVersion; // if the outpoint was the last unspent: its version + + CTxInUndo() : txout(), fCoinBase(false), nHeight(0), nVersion(0) {} + CTxInUndo(const CTxOut &txoutIn, bool fCoinBaseIn = false, unsigned int nHeightIn = 0, int nVersionIn = 0) : txout(txoutIn), fCoinBase(fCoinBaseIn), nHeight(nHeightIn), nVersion(nVersionIn) { } + + unsigned int GetSerializeSize(int nType, int nVersion) const { + return ::GetSerializeSize(VARINT(nHeight*2+(fCoinBase ? 1 : 0)), nType, nVersion) + + (nHeight > 0 ? ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion) : 0) + + ::GetSerializeSize(CTxOutCompressor(REF(txout)), nType, nVersion); + } + + template + void Serialize(Stream &s, int nType, int nVersion) const { + ::Serialize(s, VARINT(nHeight*2+(fCoinBase ? 1 : 0)), nType, nVersion); + if (nHeight > 0) + ::Serialize(s, VARINT(this->nVersion), nType, nVersion); + ::Serialize(s, CTxOutCompressor(REF(txout)), nType, nVersion); + } + + template + void Unserialize(Stream &s, int nType, int nVersion) { + unsigned int nCode = 0; + ::Unserialize(s, VARINT(nCode), nType, nVersion); + nHeight = nCode / 2; + fCoinBase = nCode & 1; + if (nHeight > 0) + ::Unserialize(s, VARINT(this->nVersion), nType, nVersion); + ::Unserialize(s, REF(CTxOutCompressor(REF(txout))), nType, nVersion); + } +}; + +/** Undo information for a CTransaction */ +class CTxUndo +{ +public: + // undo information for all txins + std::vector vprevout; + + IMPLEMENT_SERIALIZE( + READWRITE(vprevout); + ) +}; + +/** Undo information for a CBlock */ +class CBlockUndo +{ +public: + std::vector vtxundo; // for all but the coinbase + + IMPLEMENT_SERIALIZE( + READWRITE(vtxundo); + ) + + bool WriteToDisk(CDiskBlockPos &pos, const uint256 &hashBlock) + { + // Open history file to append + CAutoFile fileout = CAutoFile(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); + if (!fileout) + return error("CBlockUndo::WriteToDisk() : OpenUndoFile failed"); + + // Write index header + unsigned int nSize = fileout.GetSerializeSize(*this); + fileout << FLATDATA(pchMessageStart) << nSize; + + // Write undo data + long fileOutPos = ftell(fileout); + if (fileOutPos < 0) + return error("CBlockUndo::WriteToDisk() : ftell failed"); + pos.nPos = (unsigned int)fileOutPos; + fileout << *this; + + // calculate & write checksum + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + hasher << hashBlock; + hasher << *this; + fileout << hasher.GetHash(); + + // Flush stdio buffers and commit to disk before returning + fflush(fileout); + if (!IsInitialBlockDownload()) + FileCommit(fileout); + + return true; + } + + bool ReadFromDisk(const CDiskBlockPos &pos, const uint256 &hashBlock) + { + // Open history file to read + CAutoFile filein = CAutoFile(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); + if (!filein) + return error("CBlockUndo::ReadFromDisk() : OpenBlockFile failed"); + + // Read block + uint256 hashChecksum; + try { + filein >> *this; + filein >> hashChecksum; + } + catch (std::exception &e) { + return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); + } + + // Verify checksum + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + hasher << hashBlock; + hasher << *this; + if (hashChecksum != hasher.GetHash()) + return error("CBlockUndo::ReadFromDisk() : checksum mismatch"); + + return true; + } +}; + +/** pruned version of CTransaction: only retains metadata and unspent transaction outputs + * + * Serialized format: + * - VARINT(nVersion) + * - VARINT(nCode) + * - unspentness bitvector, for vout[2] and further; least significant byte first + * - the non-spent CTxOuts (via CTxOutCompressor) + * - VARINT(nHeight) + * + * The nCode value consists of: + * - bit 1: IsCoinBase() + * - bit 2: vout[0] is not spent + * - bit 4: vout[1] is not spent + * - The higher bits encode N, the number of non-zero bytes in the following bitvector. + * - In case both bit 2 and bit 4 are unset, they encode N-1, as there must be at + * least one non-spent output). + * + * Example: 0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e + * <><><--------------------------------------------><----> + * | \ | / + * version code vout[1] height + * + * - version = 1 + * - code = 4 (vout[1] is not spent, and 0 non-zero bytes of bitvector follow) + * - unspentness bitvector: as 0 non-zero bytes follow, it has length 0 + * - vout[1]: 835800816115944e077fe7c803cfa57f29b36bf87c1d35 + * * 8358: compact amount representation for 60000000000 (600 BTC) + * * 00: special txout type pay-to-pubkey-hash + * * 816115944e077fe7c803cfa57f29b36bf87c1d35: address uint160 + * - height = 203998 + * + * + * Example: 0109044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b + * <><><--><--------------------------------------------------><----------------------------------------------><----> + * / \ \ | | / + * version code unspentness vout[4] vout[16] height + * + * - version = 1 + * - code = 9 (coinbase, neither vout[0] or vout[1] are unspent, + * 2 (1, +1 because both bit 2 and bit 4 are unset) non-zero bitvector bytes follow) + * - unspentness bitvector: bits 2 (0x04) and 14 (0x4000) are set, so vout[2+2] and vout[14+2] are unspent + * - vout[4]: 86ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4ee + * * 86ef97d579: compact amount representation for 234925952 (2.35 BTC) + * * 00: special txout type pay-to-pubkey-hash + * * 61b01caab50f1b8e9c50a5057eb43c2d9563a4ee: address uint160 + * - vout[16]: bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4 + * * bbd123: compact amount representation for 110397 (0.001 BTC) + * * 00: special txout type pay-to-pubkey-hash + * * 8c988f1a4a4de2161e0f50aac7f17e7f9555caa4: address uint160 + * - height = 120891 + */ +class CCoins +{ +public: + // whether transaction is a coinbase + bool fCoinBase; + + // unspent transaction outputs; spent outputs are .IsNull(); spent outputs at the end of the array are dropped + std::vector vout; + + // at which height this transaction was included in the active block chain + int nHeight; + + // version of the CTransaction; accesses to this value should probably check for nHeight as well, + // as new tx version will probably only be introduced at certain heights + int nVersion; + + // construct a CCoins from a CTransaction, at a given height + CCoins(const CTransaction &tx, int nHeightIn) : fCoinBase(tx.IsCoinBase()), vout(tx.vout), nHeight(nHeightIn), nVersion(tx.nVersion) { } + + // empty constructor + CCoins() : fCoinBase(false), vout(0), nHeight(0), nVersion(0) { } + + // remove spent outputs at the end of vout + void Cleanup() { + while (vout.size() > 0 && vout.back().IsNull()) + vout.pop_back(); + if (vout.empty()) + std::vector().swap(vout); + } + + void swap(CCoins &to) { + std::swap(to.fCoinBase, fCoinBase); + to.vout.swap(vout); + std::swap(to.nHeight, nHeight); + std::swap(to.nVersion, nVersion); + } + + // equality test + friend bool operator==(const CCoins &a, const CCoins &b) { + return a.fCoinBase == b.fCoinBase && + a.nHeight == b.nHeight && + a.nVersion == b.nVersion && + a.vout == b.vout; + } + friend bool operator!=(const CCoins &a, const CCoins &b) { + return !(a == b); + } + + // calculate number of bytes for the bitmask, and its number of non-zero bytes + // each bit in the bitmask represents the availability of one output, but the + // availabilities of the first two outputs are encoded separately + void CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const { + unsigned int nLastUsedByte = 0; + for (unsigned int b = 0; 2+b*8 < vout.size(); b++) { + bool fZero = true; + for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) { + if (!vout[2+b*8+i].IsNull()) { + fZero = false; + continue; + } + } + if (!fZero) { + nLastUsedByte = b + 1; + nNonzeroBytes++; + } + } + nBytes += nLastUsedByte; + } + + bool IsCoinBase() const { + return fCoinBase; + } + + unsigned int GetSerializeSize(int nType, int nVersion) const { + unsigned int nSize = 0; + unsigned int nMaskSize = 0, nMaskCode = 0; + CalcMaskSize(nMaskSize, nMaskCode); + bool fFirst = vout.size() > 0 && !vout[0].IsNull(); + bool fSecond = vout.size() > 1 && !vout[1].IsNull(); + assert(fFirst || fSecond || nMaskCode); + unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); + // version + nSize += ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion); + // size of header code + nSize += ::GetSerializeSize(VARINT(nCode), nType, nVersion); + // spentness bitmask + nSize += nMaskSize; + // txouts themself + for (unsigned int i = 0; i < vout.size(); i++) + if (!vout[i].IsNull()) + nSize += ::GetSerializeSize(CTxOutCompressor(REF(vout[i])), nType, nVersion); + // height + nSize += ::GetSerializeSize(VARINT(nHeight), nType, nVersion); + return nSize; + } + + template + void Serialize(Stream &s, int nType, int nVersion) const { + unsigned int nMaskSize = 0, nMaskCode = 0; + CalcMaskSize(nMaskSize, nMaskCode); + bool fFirst = vout.size() > 0 && !vout[0].IsNull(); + bool fSecond = vout.size() > 1 && !vout[1].IsNull(); + assert(fFirst || fSecond || nMaskCode); + unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); + // version + ::Serialize(s, VARINT(this->nVersion), nType, nVersion); + // header code + ::Serialize(s, VARINT(nCode), nType, nVersion); + // spentness bitmask + for (unsigned int b = 0; b + void Unserialize(Stream &s, int nType, int nVersion) { + unsigned int nCode = 0; + // version + ::Unserialize(s, VARINT(this->nVersion), nType, nVersion); + // header code + ::Unserialize(s, VARINT(nCode), nType, nVersion); + fCoinBase = nCode & 1; + std::vector vAvail(2, false); + vAvail[0] = nCode & 2; + vAvail[1] = nCode & 4; + unsigned int nMaskCode = (nCode / 8) + ((nCode & 6) != 0 ? 0 : 1); + // spentness bitmask + while (nMaskCode > 0) { + unsigned char chAvail = 0; + ::Unserialize(s, chAvail, nType, nVersion); + for (unsigned int p = 0; p < 8; p++) { + bool f = (chAvail & (1 << p)) != 0; + vAvail.push_back(f); + } + if (chAvail != 0) + nMaskCode--; + } + // txouts themself + vout.assign(vAvail.size(), CTxOut()); + for (unsigned int i = 0; i < vAvail.size(); i++) { + if (vAvail[i]) + ::Unserialize(s, REF(CTxOutCompressor(vout[i])), nType, nVersion); + } + // coinbase height + ::Unserialize(s, VARINT(nHeight), nType, nVersion); + Cleanup(); + } + + // mark an outpoint spent, and construct undo information + bool Spend(const COutPoint &out, CTxInUndo &undo) { + if (out.n >= vout.size()) + return false; + if (vout[out.n].IsNull()) + return false; + undo = CTxInUndo(vout[out.n]); + vout[out.n].SetNull(); + Cleanup(); + if (vout.size() == 0) { + undo.nHeight = nHeight; + undo.fCoinBase = fCoinBase; + undo.nVersion = this->nVersion; + } + return true; + } + + // mark a vout spent + bool Spend(int nPos) { + CTxInUndo undo; + COutPoint out(0, nPos); + return Spend(out, undo); + } + + // check whether a particular output is still available + bool IsAvailable(unsigned int nPos) const { + return (nPos < vout.size() && !vout[nPos].IsNull()); + } + + // check whether the entire CCoins is spent + // note that only !IsPruned() CCoins can be serialized + bool IsPruned() const { + BOOST_FOREACH(const CTxOut &out, vout) + if (!out.IsNull()) + return false; + return true; + } +}; + +/** Closure representing one script verification + * Note that this stores references to the spending transaction */ +class CScriptCheck +{ +private: + CScript scriptPubKey; + const CTransaction *ptxTo; + unsigned int nIn; + unsigned int nFlags; + int nHashType; + +public: + CScriptCheck() {} + CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, int nHashTypeIn) : + scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey), + ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), nHashType(nHashTypeIn) { } + + bool operator()() const; + + void swap(CScriptCheck &check) { + scriptPubKey.swap(check.scriptPubKey); + std::swap(ptxTo, check.ptxTo); + std::swap(nIn, check.nIn); + std::swap(nFlags, check.nFlags); + std::swap(nHashType, check.nHashType); + } +}; + +/** A transaction with a merkle branch linking it to the block chain. */ +class CMerkleTx : public CTransaction +{ +public: + uint256 hashBlock; + std::vector vMerkleBranch; + int nIndex; + + // memory only + mutable bool fMerkleVerified; + + + CMerkleTx() + { + Init(); + } + + CMerkleTx(const CTransaction& txIn) : CTransaction(txIn) + { + Init(); + } + + void Init() + { + hashBlock = 0; + nIndex = -1; + fMerkleVerified = false; + } + + + IMPLEMENT_SERIALIZE + ( + nSerSize += SerReadWrite(s, *(CTransaction*)this, nType, nVersion, ser_action); + nVersion = this->nVersion; + READWRITE(hashBlock); + READWRITE(vMerkleBranch); + READWRITE(nIndex); + ) + + + int SetMerkleBranch(const CBlock* pblock=NULL); + int GetDepthInMainChain(CBlockIndex* &pindexRet) const; + int GetDepthInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); } + bool IsInMainChain() const { return GetDepthInMainChain() > 0; } + int GetBlocksToMaturity() const; + bool AcceptToMemoryPool(bool fCheckInputs=true, bool fLimitFree=true); +}; + + + + + +/** Data structure that represents a partial merkle tree. + * + * It respresents a subset of the txid's of a known block, in a way that + * allows recovery of the list of txid's and the merkle root, in an + * authenticated way. + * + * The encoding works as follows: we traverse the tree in depth-first order, + * storing a bit for each traversed node, signifying whether the node is the + * parent of at least one matched leaf txid (or a matched txid itself). In + * case we are at the leaf level, or this bit is 0, its merkle node hash is + * stored, and its children are not explorer further. Otherwise, no hash is + * stored, but we recurse into both (or the only) child branch. During + * decoding, the same depth-first traversal is performed, consuming bits and + * hashes as they written during encoding. + * + * The serialization is fixed and provides a hard guarantee about the + * encoded size: + * + * SIZE <= 10 + ceil(32.25*N) + * + * Where N represents the number of leaf nodes of the partial tree. N itself + * is bounded by: + * + * N <= total_transactions + * N <= 1 + matched_transactions*tree_height + * + * The serialization format: + * - uint32 total_transactions (4 bytes) + * - varint number of hashes (1-3 bytes) + * - uint256[] hashes in depth-first order (<= 32*N bytes) + * - varint number of bytes of flag bits (1-3 bytes) + * - byte[] flag bits, packed per 8 in a byte, least significant bit first (<= 2*N-1 bits) + * The size constraints follow from this. + */ +class CPartialMerkleTree +{ +protected: + // the total number of transactions in the block + unsigned int nTransactions; + + // node-is-parent-of-matched-txid bits + std::vector vBits; + + // txids and internal hashes + std::vector vHash; + + // flag set when encountering invalid data + bool fBad; + + // helper function to efficiently calculate the number of nodes at given height in the merkle tree + unsigned int CalcTreeWidth(int height) { + return (nTransactions+(1 << height)-1) >> height; + } + + // calculate the hash of a node in the merkle tree (at leaf level: the txid's themself) + uint256 CalcHash(int height, unsigned int pos, const std::vector &vTxid); + + // recursive function that traverses tree nodes, storing the data as bits and hashes + void TraverseAndBuild(int height, unsigned int pos, const std::vector &vTxid, const std::vector &vMatch); + + // recursive function that traverses tree nodes, consuming the bits and hashes produced by TraverseAndBuild. + // it returns the hash of the respective node. + uint256 TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector &vMatch); + +public: + + // serialization implementation + IMPLEMENT_SERIALIZE( + READWRITE(nTransactions); + READWRITE(vHash); + std::vector vBytes; + if (fRead) { + READWRITE(vBytes); + CPartialMerkleTree &us = *(const_cast(this)); + us.vBits.resize(vBytes.size() * 8); + for (unsigned int p = 0; p < us.vBits.size(); p++) + us.vBits[p] = (vBytes[p / 8] & (1 << (p % 8))) != 0; + us.fBad = false; + } else { + vBytes.resize((vBits.size()+7)/8); + for (unsigned int p = 0; p < vBits.size(); p++) + vBytes[p / 8] |= vBits[p] << (p % 8); + READWRITE(vBytes); + } + ) + + // Construct a partial merkle tree from a list of transaction id's, and a mask that selects a subset of them + CPartialMerkleTree(const std::vector &vTxid, const std::vector &vMatch); + + CPartialMerkleTree(); + + // extract the matching txid's represented by this partial merkle tree. + // returns the merkle root, or 0 in case of failure + uint256 ExtractMatches(std::vector &vMatch); +}; + + +/** Nodes collect new transactions into a block, hash them into a hash tree, + * and scan through nonce values to make the block's hash satisfy proof-of-work + * requirements. When they solve the proof-of-work, they broadcast the block + * to everyone and the block is added to the block chain. The first transaction + * in the block is a special one that creates a new coin owned by the creator + * of the block. + */ +class CBlockHeader +{ +public: + // header + static const int CURRENT_VERSION=2; + int nVersion; + uint256 hashPrevBlock; + uint256 hashMerkleRoot; + unsigned int nTime; + unsigned int nBits; + unsigned int nNonce; + + CBlockHeader() + { + SetNull(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(hashPrevBlock); + READWRITE(hashMerkleRoot); + READWRITE(nTime); + READWRITE(nBits); + READWRITE(nNonce); + ) + + void SetNull() + { + nVersion = CBlockHeader::CURRENT_VERSION; + hashPrevBlock = 0; + hashMerkleRoot = 0; + nTime = 0; + nBits = 0; + nNonce = 0; + } + + bool IsNull() const + { + return (nBits == 0); + } + + uint256 GetHash() const + { + return Hash(BEGIN(nVersion), END(nNonce)); + } + + int64 GetBlockTime() const + { + return (int64)nTime; + } + + void UpdateTime(const CBlockIndex* pindexPrev); +}; + +class CBlock : public CBlockHeader +{ +public: + // network and disk + std::vector vtx; + + // memory only + mutable std::vector vMerkleTree; + + CBlock() + { + SetNull(); + } + + CBlock(const CBlockHeader &header) + { + SetNull(); + *((CBlockHeader*)this) = header; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(*(CBlockHeader*)this); + READWRITE(vtx); + ) + + void SetNull() + { + CBlockHeader::SetNull(); + vtx.clear(); + vMerkleTree.clear(); + } + + //uint256 GetPoWHash() const + //{ + // uint256 thash; + // scrypt_1024_1_1_256(BEGIN(nVersion), BEGIN(thash)); + // return thash; + //} + + CBlockHeader GetBlockHeader() const + { + CBlockHeader block; + block.nVersion = nVersion; + block.hashPrevBlock = hashPrevBlock; + block.hashMerkleRoot = hashMerkleRoot; + block.nTime = nTime; + block.nBits = nBits; + block.nNonce = nNonce; + return block; + } + + uint256 BuildMerkleTree() const + { + vMerkleTree.clear(); + BOOST_FOREACH(const CTransaction& tx, vtx) + vMerkleTree.push_back(tx.GetHash()); + int j = 0; + for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) + { + for (int i = 0; i < nSize; i += 2) + { + int i2 = std::min(i+1, nSize-1); + vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]), + BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2]))); + } + j += nSize; + } + return (vMerkleTree.empty() ? 0 : vMerkleTree.back()); + } + + const uint256 &GetTxHash(unsigned int nIndex) const { + assert(vMerkleTree.size() > 0); // BuildMerkleTree must have been called first + assert(nIndex < vtx.size()); + return vMerkleTree[nIndex]; + } + + std::vector GetMerkleBranch(int nIndex) const + { + if (vMerkleTree.empty()) + BuildMerkleTree(); + std::vector vMerkleBranch; + int j = 0; + for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) + { + int i = std::min(nIndex^1, nSize-1); + vMerkleBranch.push_back(vMerkleTree[j+i]); + nIndex >>= 1; + j += nSize; + } + return vMerkleBranch; + } + + static uint256 CheckMerkleBranch(uint256 hash, const std::vector& vMerkleBranch, int nIndex) + { + if (nIndex == -1) + return 0; + BOOST_FOREACH(const uint256& otherside, vMerkleBranch) + { + if (nIndex & 1) + hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash)); + else + hash = Hash(BEGIN(hash), END(hash), BEGIN(otherside), END(otherside)); + nIndex >>= 1; + } + return hash; + } + + bool WriteToDisk(CDiskBlockPos &pos) + { + // Open history file to append + CAutoFile fileout = CAutoFile(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); + if (!fileout) + return error("CBlock::WriteToDisk() : OpenBlockFile failed"); + + // Write index header + unsigned int nSize = fileout.GetSerializeSize(*this); + fileout << FLATDATA(pchMessageStart) << nSize; + + // Write block + long fileOutPos = ftell(fileout); + if (fileOutPos < 0) + return error("CBlock::WriteToDisk() : ftell failed"); + pos.nPos = (unsigned int)fileOutPos; + fileout << *this; + + // Flush stdio buffers and commit to disk before returning + fflush(fileout); + if (!IsInitialBlockDownload()) + FileCommit(fileout); + + return true; + } + + bool ReadFromDisk(const CDiskBlockPos &pos) + { + SetNull(); + + // Open history file to read + CAutoFile filein = CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); + if (!filein) + return error("CBlock::ReadFromDisk() : OpenBlockFile failed"); + + // Read block + try { + filein >> *this; + } + catch (std::exception &e) { + return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); + } + + // Check the header + if (!CheckProofOfWork(GetHash(), nBits)) + return error("CBlock::ReadFromDisk() : errors in block header"); + + return true; + } + + + + void print() const + { + printf("CBlock(hash=%s, input=%s, PoW=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%"PRIszu")\n", + GetHash().ToString().c_str(), + HexStr(BEGIN(nVersion),BEGIN(nVersion)+80,false).c_str(), + GetHash().ToString().c_str(), + nVersion, + hashPrevBlock.ToString().c_str(), + hashMerkleRoot.ToString().c_str(), + nTime, nBits, nNonce, + vtx.size()); + for (unsigned int i = 0; i < vtx.size(); i++) + { + printf(" "); + vtx[i].print(); + } + printf(" vMerkleTree: "); + for (unsigned int i = 0; i < vMerkleTree.size(); i++) + printf("%s ", vMerkleTree[i].ToString().c_str()); + printf("\n"); + } + + + /** Undo the effects of this block (with given index) on the UTXO set represented by coins. + * In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean + * will be true if no problems were found. Otherwise, the return value will be false in case + * of problems. Note that in any case, coins may be modified. */ + bool DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &coins, bool *pfClean = NULL); + + // Apply the effects of this block (with given index) on the UTXO set represented by coins + bool ConnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &coins, bool fJustCheck=false); + + // Read a block from disk + bool ReadFromDisk(const CBlockIndex* pindex); + + // Add this block to the block index, and if necessary, switch the active block chain to this + bool AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos); + + // Context-independent validity checks + bool CheckBlock(CValidationState &state, bool fCheckPOW=true, bool fCheckMerkleRoot=true) const; + + // Store block on disk + // if dbp is provided, the file is known to already reside on disk + bool AcceptBlock(CValidationState &state, CDiskBlockPos *dbp = NULL); +}; + + + + + +class CBlockFileInfo +{ +public: + unsigned int nBlocks; // number of blocks stored in file + unsigned int nSize; // number of used bytes of block file + unsigned int nUndoSize; // number of used bytes in the undo file + unsigned int nHeightFirst; // lowest height of block in file + unsigned int nHeightLast; // highest height of block in file + uint64 nTimeFirst; // earliest time of block in file + uint64 nTimeLast; // latest time of block in file + + IMPLEMENT_SERIALIZE( + READWRITE(VARINT(nBlocks)); + READWRITE(VARINT(nSize)); + READWRITE(VARINT(nUndoSize)); + READWRITE(VARINT(nHeightFirst)); + READWRITE(VARINT(nHeightLast)); + READWRITE(VARINT(nTimeFirst)); + READWRITE(VARINT(nTimeLast)); + ) + + void SetNull() { + nBlocks = 0; + nSize = 0; + nUndoSize = 0; + nHeightFirst = 0; + nHeightLast = 0; + nTimeFirst = 0; + nTimeLast = 0; + } + + CBlockFileInfo() { + SetNull(); + } + + std::string ToString() const { + return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst).c_str(), DateTimeStrFormat("%Y-%m-%d", nTimeLast).c_str()); + } + + // update statistics (does not update nSize) + void AddBlock(unsigned int nHeightIn, uint64 nTimeIn) { + if (nBlocks==0 || nHeightFirst > nHeightIn) + nHeightFirst = nHeightIn; + if (nBlocks==0 || nTimeFirst > nTimeIn) + nTimeFirst = nTimeIn; + nBlocks++; + if (nHeightIn > nHeightFirst) + nHeightLast = nHeightIn; + if (nTimeIn > nTimeLast) + nTimeLast = nTimeIn; + } +}; + +extern CCriticalSection cs_LastBlockFile; +extern CBlockFileInfo infoLastBlockFile; +extern int nLastBlockFile; + +enum BlockStatus { + BLOCK_VALID_UNKNOWN = 0, + BLOCK_VALID_HEADER = 1, // parsed, version ok, hash satisfies claimed PoW, 1 <= vtx count <= max, timestamp not in future + BLOCK_VALID_TREE = 2, // parent found, difficulty matches, timestamp >= median previous, checkpoint + BLOCK_VALID_TRANSACTIONS = 3, // only first tx is coinbase, 2 <= coinbase input script length <= 100, transactions valid, no duplicate txids, sigops, size, merkle root + BLOCK_VALID_CHAIN = 4, // outputs do not overspend inputs, no double spends, coinbase output ok, immature coinbase spends, BIP30 + BLOCK_VALID_SCRIPTS = 5, // scripts/signatures ok + BLOCK_VALID_MASK = 7, + + BLOCK_HAVE_DATA = 8, // full block available in blk*.dat + BLOCK_HAVE_UNDO = 16, // undo data available in rev*.dat + BLOCK_HAVE_MASK = 24, + + BLOCK_FAILED_VALID = 32, // stage after last reached validness failed + BLOCK_FAILED_CHILD = 64, // descends from failed block + BLOCK_FAILED_MASK = 96 +}; + +/** The block chain is a tree shaped structure starting with the + * genesis block at the root, with each block potentially having multiple + * candidates to be the next block. pprev and pnext link a path through the + * main/longest chain. A blockindex may have multiple pprev pointing back + * to it, but pnext will only point forward to the longest branch, or will + * be null if the block is not part of the longest chain. + */ +class CBlockIndex +{ +public: + // pointer to the hash of the block, if any. memory is owned by this CBlockIndex + const uint256* phashBlock; + + // pointer to the index of the predecessor of this block + CBlockIndex* pprev; + + // (memory only) pointer to the index of the *active* successor of this block + CBlockIndex* pnext; + + // height of the entry in the chain. The genesis block has height 0 + int nHeight; + + // Which # file this block is stored in (blk?????.dat) + int nFile; + + // Byte offset within blk?????.dat where this block's data is stored + unsigned int nDataPos; + + // Byte offset within rev?????.dat where this block's undo data is stored + unsigned int nUndoPos; + + // (memory only) Total amount of work (expected number of hashes) in the chain up to and including this block + uint256 nChainWork; + + // Number of transactions in this block. + // Note: in a potential headers-first mode, this number cannot be relied upon + unsigned int nTx; + + // (memory only) Number of transactions in the chain up to and including this block + unsigned int nChainTx; // change to 64-bit type when necessary; won't happen before 2030 + + // Verification status of this block. See enum BlockStatus + unsigned int nStatus; + + // block header + int nVersion; + uint256 hashMerkleRoot; + unsigned int nTime; + unsigned int nBits; + unsigned int nNonce; + + + CBlockIndex() + { + phashBlock = NULL; + pprev = NULL; + pnext = NULL; + nHeight = 0; + nFile = 0; + nDataPos = 0; + nUndoPos = 0; + nChainWork = 0; + nTx = 0; + nChainTx = 0; + nStatus = 0; + + nVersion = 0; + hashMerkleRoot = 0; + nTime = 0; + nBits = 0; + nNonce = 0; + } + + CBlockIndex(CBlockHeader& block) + { + phashBlock = NULL; + pprev = NULL; + pnext = NULL; + nHeight = 0; + nFile = 0; + nDataPos = 0; + nUndoPos = 0; + nChainWork = 0; + nTx = 0; + nChainTx = 0; + nStatus = 0; + + nVersion = block.nVersion; + hashMerkleRoot = block.hashMerkleRoot; + nTime = block.nTime; + nBits = block.nBits; + nNonce = block.nNonce; + } + + CDiskBlockPos GetBlockPos() const { + CDiskBlockPos ret; + if (nStatus & BLOCK_HAVE_DATA) { + ret.nFile = nFile; + ret.nPos = nDataPos; + } + return ret; + } + + CDiskBlockPos GetUndoPos() const { + CDiskBlockPos ret; + if (nStatus & BLOCK_HAVE_UNDO) { + ret.nFile = nFile; + ret.nPos = nUndoPos; + } + return ret; + } + + CBlockHeader GetBlockHeader() const + { + CBlockHeader block; + block.nVersion = nVersion; + if (pprev) + block.hashPrevBlock = pprev->GetBlockHash(); + block.hashMerkleRoot = hashMerkleRoot; + block.nTime = nTime; + block.nBits = nBits; + block.nNonce = nNonce; + return block; + } + + uint256 GetBlockHash() const + { + return *phashBlock; + } + + int64 GetBlockTime() const + { + return (int64)nTime; + } + + CBigNum GetBlockWork() const + { + CBigNum bnTarget; + bnTarget.SetCompact(nBits); + if (bnTarget <= 0) + return 0; + return (CBigNum(1)<<256) / (bnTarget+1); + } + + bool IsInMainChain() const + { + return (pnext || this == pindexBest); + } + + bool CheckIndex() const + { + /** Scrypt is used for block proof-of-work, but for purposes of performance the index internally uses sha256. + * This check was considered unneccessary given the other safeguards like the genesis and checkpoints. */ + return true; // return CheckProofOfWork(GetBlockHash(), nBits); + } + + enum { nMedianTimeSpan=5 }; + + int64 GetMedianTimePast() const + { + int64 pmedian[nMedianTimeSpan]; + int64* pbegin = &pmedian[nMedianTimeSpan]; + int64* pend = &pmedian[nMedianTimeSpan]; + + const CBlockIndex* pindex = this; + for (int i = 0; i < nMedianTimeSpan && pindex; i++, pindex = pindex->pprev) + *(--pbegin) = pindex->GetBlockTime(); + + std::sort(pbegin, pend); + return pbegin[(pend - pbegin)/2]; + } + + int64 GetMedianTime() const + { + const CBlockIndex* pindex = this; + for (int i = 0; i < nMedianTimeSpan/2; i++) + { + if (!pindex->pnext) + return GetBlockTime(); + pindex = pindex->pnext; + } + return pindex->GetMedianTimePast(); + } + + /** + * Returns true if there are nRequired or more blocks of minVersion or above + * in the last nToCheck blocks, starting at pstart and going backwards. + */ + static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, + unsigned int nRequired, unsigned int nToCheck); + + std::string ToString() const + { + return strprintf("CBlockIndex(pprev=%p, pnext=%p, nHeight=%d, merkle=%s, hashBlock=%s)", + pprev, pnext, nHeight, + hashMerkleRoot.ToString().c_str(), + GetBlockHash().ToString().c_str()); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + +struct CBlockIndexWorkComparator +{ + bool operator()(CBlockIndex *pa, CBlockIndex *pb) { + if (pa->nChainWork > pb->nChainWork) return false; + if (pa->nChainWork < pb->nChainWork) return true; + + if (pa->GetBlockHash() < pb->GetBlockHash()) return false; + if (pa->GetBlockHash() > pb->GetBlockHash()) return true; + + return false; // identical blocks + } +}; + + + +/** Used to marshal pointers into hashes for db storage. */ +class CDiskBlockIndex : public CBlockIndex +{ +public: + uint256 hashPrev; + + CDiskBlockIndex() { + hashPrev = 0; + } + + explicit CDiskBlockIndex(CBlockIndex* pindex) : CBlockIndex(*pindex) { + hashPrev = (pprev ? pprev->GetBlockHash() : 0); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(VARINT(nVersion)); + + READWRITE(VARINT(nHeight)); + READWRITE(VARINT(nStatus)); + READWRITE(VARINT(nTx)); + if (nStatus & (BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO)) + READWRITE(VARINT(nFile)); + if (nStatus & BLOCK_HAVE_DATA) + READWRITE(VARINT(nDataPos)); + if (nStatus & BLOCK_HAVE_UNDO) + READWRITE(VARINT(nUndoPos)); + + // block header + READWRITE(this->nVersion); + READWRITE(hashPrev); + READWRITE(hashMerkleRoot); + READWRITE(nTime); + READWRITE(nBits); + READWRITE(nNonce); + ) + + uint256 GetBlockHash() const + { + CBlockHeader block; + block.nVersion = nVersion; + block.hashPrevBlock = hashPrev; + block.hashMerkleRoot = hashMerkleRoot; + block.nTime = nTime; + block.nBits = nBits; + block.nNonce = nNonce; + return block.GetHash(); + } + + + std::string ToString() const + { + std::string str = "CDiskBlockIndex("; + str += CBlockIndex::ToString(); + str += strprintf("\n hashBlock=%s, hashPrev=%s)", + GetBlockHash().ToString().c_str(), + hashPrev.ToString().c_str()); + return str; + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + +/** 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; + bool corruptionPossible; +public: + CValidationState() : mode(MODE_VALID), nDoS(0), corruptionPossible(false) {} + bool DoS(int level, bool ret = false, bool corruptionIn = false) { + if (mode == MODE_ERROR) + return ret; + nDoS += level; + mode = MODE_INVALID; + corruptionPossible = corruptionIn; + return ret; + } + bool Invalid(bool ret = false) { + return DoS(0, ret); + } + bool Error() { + mode = MODE_ERROR; + return false; + } + bool Abort(const std::string &msg) { + AbortNode(msg); + return Error(); + } + bool IsValid() { + return mode == MODE_VALID; + } + bool IsInvalid() { + return mode == MODE_INVALID; + } + bool IsError() { + return mode == MODE_ERROR; + } + bool IsInvalid(int &nDoSOut) { + if (IsInvalid()) { + nDoSOut = nDoS; + return true; + } + return false; + } + bool CorruptionPossible() { + return corruptionPossible; + } +}; + + + + + + + +/** Describes a place in the block chain to another node such that if the + * other node doesn't have the same branch, it can find a recent common trunk. + * The further back it is, the further before the fork it may be. + */ +class CBlockLocator +{ +protected: + std::vector vHave; +public: + + CBlockLocator() + { + } + + explicit CBlockLocator(const CBlockIndex* pindex) + { + Set(pindex); + } + + explicit CBlockLocator(uint256 hashBlock) + { + std::map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end()) + Set((*mi).second); + } + + CBlockLocator(const std::vector& vHaveIn) + { + vHave = vHaveIn; + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vHave); + ) + + void SetNull() + { + vHave.clear(); + } + + bool IsNull() + { + return vHave.empty(); + } + + void Set(const CBlockIndex* pindex) + { + vHave.clear(); + int nStep = 1; + while (pindex) + { + vHave.push_back(pindex->GetBlockHash()); + + // Exponentially larger steps back + for (int i = 0; pindex && i < nStep; i++) + pindex = pindex->pprev; + if (vHave.size() > 10) + nStep *= 2; + } + vHave.push_back(hashGenesisBlock); + } + + int GetDistanceBack() + { + // Retrace how far back it was in the sender's branch + int nDistance = 0; + int nStep = 1; + BOOST_FOREACH(const uint256& hash, vHave) + { + std::map::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + return nDistance; + } + nDistance += nStep; + if (nDistance > 10) + nStep *= 2; + } + return nDistance; + } + + CBlockIndex* GetBlockIndex() + { + // Find the first block the caller has in the main chain + BOOST_FOREACH(const uint256& hash, vHave) + { + std::map::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + return pindex; + } + } + return pindexGenesisBlock; + } + + uint256 GetBlockHash() + { + // Find the first block the caller has in the main chain + BOOST_FOREACH(const uint256& hash, vHave) + { + std::map::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + return hash; + } + } + return hashGenesisBlock; + } + + int GetHeight() + { + CBlockIndex* pindex = GetBlockIndex(); + if (!pindex) + return 0; + return pindex->nHeight; + } +}; + + + + + + + + +class CTxMemPool +{ +public: + mutable CCriticalSection cs; + std::map mapTx; + std::map mapNextTx; + + bool accept(CValidationState &state, CTransaction &tx, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs); + bool addUnchecked(const uint256& hash, const CTransaction &tx); + bool remove(const CTransaction &tx, bool fRecursive = false); + bool removeConflicts(const CTransaction &tx); + void clear(); + void queryHashes(std::vector& vtxid); + void pruneSpent(const uint256& hash, CCoins &coins); + + unsigned long size() + { + LOCK(cs); + return mapTx.size(); + } + + bool exists(uint256 hash) + { + return (mapTx.count(hash) != 0); + } + + CTransaction& lookup(uint256 hash) + { + return mapTx[hash]; + } +}; + +extern CTxMemPool mempool; + +struct CCoinsStats +{ + int nHeight; + uint256 hashBlock; + uint64 nTransactions; + uint64 nTransactionOutputs; + uint64 nSerializedSize; + uint256 hashSerialized; + int64 nTotalAmount; + + CCoinsStats() : nHeight(0), hashBlock(0), nTransactions(0), nTransactionOutputs(0), nSerializedSize(0), hashSerialized(0), nTotalAmount(0) {} +}; + +/** Abstract view on the open txout dataset. */ +class CCoinsView +{ +public: + // Retrieve the CCoins (unspent transaction outputs) for a given txid + virtual bool GetCoins(const uint256 &txid, CCoins &coins); + + // Modify the CCoins for a given txid + virtual bool SetCoins(const uint256 &txid, const CCoins &coins); + + // Just check whether we have data for a given txid. + // This may (but cannot always) return true for fully spent transactions + virtual bool HaveCoins(const uint256 &txid); + + // Retrieve the block index whose state this CCoinsView currently represents + virtual CBlockIndex *GetBestBlock(); + + // Modify the currently active block index + virtual bool SetBestBlock(CBlockIndex *pindex); + + // Do a bulk modification (multiple SetCoins + one SetBestBlock) + virtual bool BatchWrite(const std::map &mapCoins, CBlockIndex *pindex); + + // Calculate statistics about the unspent transaction output set + virtual bool GetStats(CCoinsStats &stats); + + // As we use CCoinsViews polymorphically, have a virtual destructor + virtual ~CCoinsView() {} +}; + +/** CCoinsView backed by another CCoinsView */ +class CCoinsViewBacked : public CCoinsView +{ +protected: + CCoinsView *base; + +public: + CCoinsViewBacked(CCoinsView &viewIn); + bool GetCoins(const uint256 &txid, CCoins &coins); + bool SetCoins(const uint256 &txid, const CCoins &coins); + bool HaveCoins(const uint256 &txid); + CBlockIndex *GetBestBlock(); + bool SetBestBlock(CBlockIndex *pindex); + void SetBackend(CCoinsView &viewIn); + bool BatchWrite(const std::map &mapCoins, CBlockIndex *pindex); + bool GetStats(CCoinsStats &stats); +}; + +/** CCoinsView that adds a memory cache for transactions to another CCoinsView */ +class CCoinsViewCache : public CCoinsViewBacked +{ +protected: + CBlockIndex *pindexTip; + std::map cacheCoins; + +public: + CCoinsViewCache(CCoinsView &baseIn, bool fDummy = false); + + // Standard CCoinsView methods + bool GetCoins(const uint256 &txid, CCoins &coins); + bool SetCoins(const uint256 &txid, const CCoins &coins); + bool HaveCoins(const uint256 &txid); + CBlockIndex *GetBestBlock(); + bool SetBestBlock(CBlockIndex *pindex); + bool BatchWrite(const std::map &mapCoins, CBlockIndex *pindex); + + // Return a modifiable reference to a CCoins. Check HaveCoins first. + // Many methods explicitly require a CCoinsViewCache because of this method, to reduce + // copying. + CCoins &GetCoins(const uint256 &txid); + + // Push the modifications applied to this cache to its base. + // Failure to call this method before destruction will cause the changes to be forgotten. + bool Flush(); + + // Calculate the size of the cache (in number of transactions) + unsigned int GetCacheSize(); + +private: + std::map::iterator FetchCoins(const uint256 &txid); +}; + +/** CCoinsView that brings transactions from a memorypool into view. + It does not check for spendings by memory pool transactions. */ +class CCoinsViewMemPool : public CCoinsViewBacked +{ +protected: + CTxMemPool &mempool; + +public: + CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn); + bool GetCoins(const uint256 &txid, CCoins &coins); + bool HaveCoins(const uint256 &txid); +}; + +/** Global variable that points to the active CCoinsView (protected by cs_main) */ +extern CCoinsViewCache *pcoinsTip; + +/** Global variable that points to the active block tree (protected by cs_main) */ +extern CBlockTreeDB *pblocktree; + +struct CBlockTemplate +{ + CBlock block; + std::vector vTxFees; + std::vector vTxSigOps; +}; + +#if defined(_M_IX86) || defined(__i386__) || defined(__i386) || defined(_M_X64) || defined(__x86_64__) || defined(_M_AMD64) +extern unsigned int cpuid_edx; +#endif + + + + + +/** Used to relay blocks as header + vector + * to filtered nodes. + */ +class CMerkleBlock +{ +public: + // Public only for unit testing + CBlockHeader header; + CPartialMerkleTree txn; + +public: + // Public only for unit testing and relay testing + // (not relayed) + std::vector > vMatchedTxn; + + // Create from a CBlock, filtering transactions according to filter + // Note that this will call IsRelevantAndUpdate on the filter for each transaction, + // thus the filter will likely be modified. + CMerkleBlock(const CBlock& block, CBloomFilter& filter); + + IMPLEMENT_SERIALIZE + ( + READWRITE(header); + READWRITE(txn); + ) +}; + +#endif diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw new file mode 100644 index 0000000..0fcc67b --- /dev/null +++ b/src/makefile.linux-mingw @@ -0,0 +1,137 @@ +# Copyright (c) 2009-2010 Satoshi Nakamoto +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +DEPSDIR:=/usr/i686-w64-mingw32 + +CC := i686-w64-mingw32-gcc +CXX := i686-w64-mingw32-g++ + +USE_UPNP:=0 +USE_IPV6:=1 + +INCLUDEPATHS= \ + -I"$(CURDIR)" \ + -I"$(CURDIR)"/obj \ + -I"$(DEPSDIR)/include" \ + -I"$(DEPSDIR)" + +LIBPATHS= \ + -L"$(DEPSDIR)/lib" + +LIBS= \ + $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a \ + -l boost_system-mt-s \ + -l boost_filesystem-mt-s \ + -l boost_program_options-mt-s \ + -l boost_thread_win32-mt-s \ + -l boost_chrono-mt-s \ + -l db_cxx \ + -l ssl \ + -l crypto + +DEFS=-D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE +DEBUGFLAGS=-g +xCXXFLAGS=-O2 -w -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) $(CXXFLAGS) +# enable: ASLR, DEP and large address aware +xLDFLAGS=-Wl,--dynamicbase -Wl,--nxcompat -Wl,--large-address-aware -static-libgcc -static-libstdc++ $(LDFLAGS) + +TESTDEFS = -DTEST_DATA_DIR=$(abspath test/data) + +ifndef USE_UPNP + override USE_UPNP = - +endif +ifneq (${USE_UPNP}, -) + LIBPATHS += -L"$(DEPSDIR)/miniupnpc" + LIBS += -l miniupnpc -l iphlpapi + DEFS += -DSTATICLIB -DUSE_UPNP=$(USE_UPNP) +endif + +ifneq (${USE_IPV6}, -) + DEFS += -DUSE_IPV6=$(USE_IPV6) +endif + +LIBS += -l mingwthrd -l kernel32 -l user32 -l gdi32 -l comdlg32 -l winspool -l winmm -l shell32 -l comctl32 -l ole32 -l oleaut32 -l uuid -l rpcrt4 -l advapi32 -l ws2_32 -l mswsock -l shlwapi + +# TODO: make the mingw builds smarter about dependencies, like the linux/osx builds are +HEADERS = $(wildcard *.h) + +OBJS= \ + leveldb/libleveldb.a \ + obj/alert.o \ + obj/version.o \ + obj/checkpoints.o \ + obj/netbase.o \ + obj/addrman.o \ + obj/crypter.o \ + obj/key.o \ + obj/db.o \ + obj/init.o \ + obj/irc.o \ + obj/keystore.o \ + obj/main.o \ + obj/net.o \ + obj/protocol.o \ + obj/bitcoinrpc.o \ + obj/rpcdump.o \ + obj/rpcnet.o \ + obj/rpcmining.o \ + obj/rpcwallet.o \ + obj/rpcblockchain.o \ + obj/rpcrawtransaction.o \ + obj/script.o \ + obj/sync.o \ + obj/util.o \ + obj/wallet.o \ + obj/walletdb.o \ + obj/noui.o \ + obj/hash.o \ + obj/bloom.o \ + obj/leveldb.o \ + obj/txdb.o + +ifdef USE_SSE2 +DEFS += -DUSE_SSE2 +OBJS_SSE2= obj/scrypt-sse2.o +OBJS += $(OBJS_SSE2) +endif + +all: CryptoLottoTokend.exe + +DEFS += -I"$(CURDIR)/leveldb/include" +DEFS += -I"$(CURDIR)/leveldb/helpers" +leveldb/libleveldb.a: + @echo "Building LevelDB ..." && cd leveldb && TARGET_OS=OS_WINDOWS_CROSSCOMPILE $(MAKE) CC=$(CC) CXX=$(CXX) OPT="$(xCXXFLAGS)" libleveldb.a libmemenv.a && i686-w64-mingw32-ranlib libleveldb.a && i686-w64-mingw32-ranlib libmemenv.a && cd .. + +obj/build.h: FORCE + /bin/sh ../share/genbuild.sh obj/build.h +version.cpp: obj/build.h +DEFS += -DHAVE_BUILD_INFO + +obj/%-sse2.o: %-sse2.cpp + $(CXX) -c $(xCXXFLAGS) -msse2 -mstackrealign -o $@ $< + +obj/%.o: %.cpp $(HEADERS) + $(CXX) -c $(xCXXFLAGS) -o $@ $< + +CryptoLottoTokend.exe: $(OBJS:obj/%=obj/%) + $(CXX) $(xCXXFLAGS) $(xLDFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) + +TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp)) + +obj-test/%.o: test/%.cpp $(HEADERS) + $(CXX) -c $(TESTDEFS) $(xCXXFLAGS) -o $@ $< + +test_CryptoLottoToken.exe: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%)) + $(CXX) $(xCXXFLAGS) $(xLDFLAGS) -o $@ $(LIBPATHS) $^ -lboost_unit_test_framework-mt-s $(LIBS) + + +clean: + -rm -f obj/*.o + -rm -f CryptoLottoTokend.exe + -rm -f obj-test/*.o + -rm -f test_CryptoLottoToken.exe + -rm -f obj/build.h + cd leveldb && TARGET_OS=OS_WINDOWS_CROSSCOMPILE $(MAKE) clean && cd .. + +FORCE: diff --git a/src/makefile.mingw b/src/makefile.mingw new file mode 100644 index 0000000..47e6e67 --- /dev/null +++ b/src/makefile.mingw @@ -0,0 +1,157 @@ +# Copyright (c) 2009-2010 Satoshi Nakamoto +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# Makefile for the MinGW g++ compiler/toolchain +# +# Assumes Berkeley DB, Boost, and OpenSSL have all been compiled and installed +# into /usr/local (/usr/local/include, /usr/local/lib). +# +# If dependencies are somewhere else, run 'make DEPSDIR=/path/' +# +# Boost libraries are given wacky names that include the particular version of +# boost you're using; set BOOST_SUFFIX appropriately. +# +# 'make clean' assumes it is running inside a MSYS shell, and uses 'rm' +# to remove files. + +CXX ?= g++ + +USE_UPNP:=- +USE_IPV6:=1 + +DEPSDIR?=/c/deps +BOOST_SUFFIX?=-mgw46-mt-sd-1_53 + +INCLUDEPATHS= \ + -I"$(CURDIR)" \ + -I"$(DEPSDIR)/include" \ + -I"$(DEPSDIR)/boost" \ + -I"$(DEPSDIR)/ssl/include" \ + -I"$(DEPSDIR)/db/build_unix" \ + -I"$(DEPSDIR)/miniupnpc" + +LIBPATHS= \ + -L"$(CURDIR)/leveldb" \ + -L"$(DEPSDIR)/lib" \ + -L"$(DEPSDIR)/boost/stage/lib" \ + -L"$(DEPSDIR)/ssl" \ + -L"$(DEPSDIR)/db/build_unix" \ + -L"$(CURDIR)/leveldb" + + +LIBS= \ + -l leveldb \ + -l memenv \ + -l boost_system$(BOOST_SUFFIX) \ + -l boost_filesystem$(BOOST_SUFFIX) \ + -l boost_program_options$(BOOST_SUFFIX) \ + -l boost_thread$(BOOST_SUFFIX) \ + -l boost_chrono$(BOOST_SUFFIX) \ + -l db_cxx \ + -l ssl \ + -l crypto + +DEFS=-D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB -DBOOST_SPIRIT_THREADSAFE +DEBUGFLAGS=-g +CFLAGS=-fexceptions -mthreads -O2 -w -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) +# enable: ASLR, DEP and large address aware +LDFLAGS=-Wl,--dynamicbase -Wl,--nxcompat -Wl,--large-address-aware + +TESTDEFS = -DTEST_DATA_DIR=$(abspath test/data) + +ifndef USE_UPNP + override USE_UPNP = - +endif +ifneq (${USE_UPNP}, -) + LIBS += -l miniupnpc -l iphlpapi + DEFS += -DSTATICLIB -DUSE_UPNP=$(USE_UPNP) +endif + +ifneq (${USE_IPV6}, -) + DEFS += -DUSE_IPV6=$(USE_IPV6) +endif + +LIBS += -l mingwthrd -l kernel32 -l user32 -l gdi32 -l comdlg32 -l winspool -l winmm -l shell32 -l comctl32 -l ole32 -l oleaut32 -l uuid -l rpcrt4 -l advapi32 -l ws2_32 -l mswsock -l shlwapi + +# TODO: make the mingw builds smarter about dependencies, like the linux/osx builds are +HEADERS = $(wildcard *.h) + +OBJS= \ + leveldb/libleveldb.a \ + obj/alert.o \ + obj/version.o \ + obj/checkpoints.o \ + obj/netbase.o \ + obj/addrman.o \ + obj/crypter.o \ + obj/key.o \ + obj/db.o \ + obj/init.o \ + obj/irc.o \ + obj/keystore.o \ + obj/main.o \ + obj/net.o \ + obj/protocol.o \ + obj/bitcoinrpc.o \ + obj/rpcdump.o \ + obj/rpcnet.o \ + obj/rpcmining.o \ + obj/rpcwallet.o \ + obj/rpcblockchain.o \ + obj/rpcrawtransaction.o \ + obj/script.o \ + obj/sync.o \ + obj/util.o \ + obj/wallet.o \ + obj/walletdb.o \ + obj/hash.o \ + obj/bloom.o \ + obj/noui.o \ + obj/leveldb.o \ + obj/txdb.o + +ifdef USE_SSE2 +DEFS += -DUSE_SSE2 +OBJS_SSE2= obj/scrypt-sse2.o +OBJS += $(OBJS_SSE2) +endif + +all: CryptoLottoTokend.exe + +test check: test_CryptoLottoToken.exe FORCE + test_CryptoLottoToken.exe + +# +# LevelDB support +# +DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) +DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) + +leveldb/libleveldb.a: + cd leveldb && $(MAKE) CC=$(CC) CXX=$(CXX) OPT="$(CFLAGS)" TARGET_OS=NATIVE_WINDOWS libleveldb.a libmemenv.a && cd .. + +obj/%-sse2.o: %-sse2.cpp + $(CXX) -c $(CFLAGS) -msse2 -mstackrealign -o $@ $< + +obj/%.o: %.cpp $(HEADERS) + $(CXX) -c $(CFLAGS) -o $@ $< + +CryptoLottoTokend.exe: $(OBJS:obj/%=obj/%) + $(CXX) $(CFLAGS) $(LDFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) + +TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp)) + +obj-test/%.o: test/%.cpp $(HEADERS) + $(CXX) -c $(TESTDEFS) $(CFLAGS) -o $@ $< + +test_CryptoLottoToken.exe: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%)) + $(CXX) $(CFLAGS) $(LDFLAGS) -o $@ $(LIBPATHS) $^ -lboost_unit_test_framework$(BOOST_SUFFIX) $(LIBS) + +clean: + rm -f CryptoLottoTokend.exe test_CryptoLottoToken.exe + rm -f obj/* + rm -f obj-test/* + cd leveldb && $(MAKE) TARGET_OS=NATIVE_WINDOWS clean && cd .. + +FORCE: diff --git a/src/makefile.osx b/src/makefile.osx new file mode 100644 index 0000000..762eeb8 --- /dev/null +++ b/src/makefile.osx @@ -0,0 +1,195 @@ +# -*- mode: Makefile; -*- +# Copyright (c) 2011 Bitcoin Developers +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# Mac OS X makefile for bitcoin +# Originally by Laszlo Hanyecz (solar@heliacal.net) + +CXX=llvm-g++ +DEPSDIR=/usr/local +DB4DIR=/usr/local/opt/berkeley-db4 +OPENSSLDIR=/usr/local/opt/openssl + +INCLUDEPATHS= \ + -I"$(CURDIR)" \ + -I"$(CURDIR)/obj" \ + -I"$(DEPSDIR)/include" \ + -I"$(DB4DIR)/include" \ + -I"$(OPENSSLDIR)/include" + +LIBPATHS= \ + -L"$(DEPSDIR)/lib" \ + -L"$(DB4DIR)/lib" \ + -L"$(OPENSSLDIR)/lib" + +USE_UPNP:=1 +USE_IPV6:=1 + +LIBS= -dead_strip + +TESTDEFS = -DTEST_DATA_DIR=$(abspath test/data) + +ifdef STATIC +# Build STATIC if you are redistributing the bitcoind binary +TESTLIBS += \ + $(DEPSDIR)/lib/libboost_unit_test_framework-mt.a +LIBS += \ + $(DB4DIR)/lib/libdb_cxx-4.8.a \ + $(DEPSDIR)/lib/libboost_system-mt.a \ + $(DEPSDIR)/lib/libboost_filesystem-mt.a \ + $(DEPSDIR)/lib/libboost_program_options-mt.a \ + $(DEPSDIR)/lib/libboost_thread-mt.a \ + $(DEPSDIR)/lib/libboost_chrono-mt.a \ + $(OPENSSLDIR)/lib/libssl.a \ + $(OPENSSLDIR)/lib/libcrypto.a \ + -lz +else +TESTLIBS += \ + -lboost_unit_test_framework-mt +LIBS += \ + -ldb_cxx-4.8 \ + -lboost_system-mt \ + -lboost_filesystem-mt \ + -lboost_program_options-mt \ + -lboost_thread-mt \ + -lboost_chrono-mt \ + -lssl \ + -lcrypto \ + -lz +TESTDEFS += -DBOOST_TEST_DYN_LINK +endif + +DEFS=-DMAC_OSX -DMSG_NOSIGNAL=0 -DBOOST_SPIRIT_THREADSAFE + +ifdef RELEASE +# Compile for maximum compatibility and smallest size. +# This requires that dependencies are compiled +# the same way. +CFLAGS = -mmacosx-version-min=10.5 -arch i386 -O3 +else +DEBUGFLAGS = -g +endif + +# ppc doesn't work because we don't support big-endian +CFLAGS += -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter \ + $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) + +OBJS= \ + leveldb/libleveldb.a \ + obj/alert.o \ + obj/version.o \ + obj/checkpoints.o \ + obj/netbase.o \ + obj/addrman.o \ + obj/crypter.o \ + obj/key.o \ + obj/db.o \ + obj/init.o \ + obj/irc.o \ + obj/keystore.o \ + obj/main.o \ + obj/net.o \ + obj/protocol.o \ + obj/bitcoinrpc.o \ + obj/rpcdump.o \ + obj/rpcnet.o \ + obj/rpcmining.o \ + obj/rpcwallet.o \ + obj/rpcblockchain.o \ + obj/rpcrawtransaction.o \ + obj/script.o \ + obj/sync.o \ + obj/util.o \ + obj/wallet.o \ + obj/walletdb.o \ + obj/hash.o \ + obj/bloom.o \ + obj/noui.o \ + obj/leveldb.o \ + obj/txdb.o + +ifdef USE_SSE2 +DEFS += -DUSE_SSE2 +OBJS_SSE2= obj/scrypt-sse2.o +OBJS += $(OBJS_SSE2) +endif + +ifndef USE_UPNP + override USE_UPNP = - +endif +ifneq (${USE_UPNP}, -) + DEFS += -DUSE_UPNP=$(USE_UPNP) +ifdef STATIC + LIBS += $(DEPSDIR)/lib/libminiupnpc.a +else + LIBS += -lminiupnpc +endif +endif + +ifneq (${USE_IPV6}, -) + DEFS += -DUSE_IPV6=$(USE_IPV6) +endif + +all: CryptoLottoTokend + +test check: test_CryptoLottoToken FORCE + ./test_CryptoLottoToken + +# +# LevelDB support +# +LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a +DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) +DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) +leveldb/libleveldb.a: + @echo "Building LevelDB ..." && cd leveldb && $(MAKE) CC=$(CC) CXX=$(CXX) OPT="$(CFLAGS)" libleveldb.a libmemenv.a && cd .. + +# auto-generated dependencies: +-include obj/*.P +-include obj-test/*.P + +obj/build.h: FORCE + /bin/sh ../share/genbuild.sh obj/build.h +version.cpp: obj/build.h +DEFS += -DHAVE_BUILD_INFO + +obj/%-sse2.o: %-sse2.cpp + $(CXX) -c $(CFLAGS) -msse2 -MMD -MF $(@:%.o=%.d) -o $@ $< + @cp $(@:%.o=%.d) $(@:%.o=%.P); \ + sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ + -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ + rm -f $(@:%.o=%.d) + +obj/%.o: %.cpp + $(CXX) -c $(CFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $< + @cp $(@:%.o=%.d) $(@:%.o=%.P); \ + sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ + -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ + rm -f $(@:%.o=%.d) + +CryptoLottoTokend: $(OBJS:obj/%=obj/%) + $(CXX) $(CFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) + +TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp)) + +obj-test/%.o: test/%.cpp + $(CXX) -c $(TESTDEFS) $(CFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $< + @cp $(@:%.o=%.d) $(@:%.o=%.P); \ + sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ + -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ + rm -f $(@:%.o=%.d) + +test_CryptoLottoToken: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%)) + $(CXX) $(CFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) $(TESTLIBS) + +clean: + -rm -f CryptoLottoTokend test_CryptoLottoToken + -rm -f obj/*.o + -rm -f obj-test/*.o + -rm -f obj/*.P + -rm -f obj-test/*.P + -rm -f obj/build.h + -cd leveldb && $(MAKE) clean || true + +FORCE: diff --git a/src/makefile.unix b/src/makefile.unix new file mode 100644 index 0000000..8c4cfc0 --- /dev/null +++ b/src/makefile.unix @@ -0,0 +1,216 @@ +# Copyright (c) 2009-2010 Satoshi Nakamoto +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# :=0 --> UPnP support turned off by default at runtime +# :=1 --> UPnP support turned on by default at runtime +# :=- --> No UPnP support - miniupnp not required +USE_UPNP:=0 + +# :=1 --> Enable IPv6 support +# :=0 --> Disable IPv6 support +USE_IPV6:=1 + +LINK:=$(CXX) + +DEFS=-DBOOST_SPIRIT_THREADSAFE -D_FILE_OFFSET_BITS=64 + +DEFS += $(addprefix -I,$(CURDIR) $(CURDIR)/obj $(BOOST_INCLUDE_PATH) $(BDB_INCLUDE_PATH) $(OPENSSL_INCLUDE_PATH)) +LIBS = $(addprefix -L,$(BOOST_LIB_PATH) $(BDB_LIB_PATH) $(OPENSSL_LIB_PATH)) + +TESTDEFS = -DTEST_DATA_DIR=$(abspath test/data) + +LMODE = dynamic +LMODE2 = dynamic +ifdef STATIC + LMODE = static + ifeq (${STATIC}, all) + LMODE2 = static + endif +else + TESTDEFS += -DBOOST_TEST_DYN_LINK +endif + +# for boost 1.37, add -mt to the boost libraries +LIBS += \ + -Wl,-B$(LMODE) \ + -l boost_system$(BOOST_LIB_SUFFIX) \ + -l boost_filesystem$(BOOST_LIB_SUFFIX) \ + -l boost_program_options$(BOOST_LIB_SUFFIX) \ + -l boost_thread$(BOOST_LIB_SUFFIX) \ + -l db_cxx$(BDB_LIB_SUFFIX) \ + -l ssl \ + -l crypto + +TESTLIBS += \ + -Wl,-B$(LMODE) \ + -l boost_unit_test_framework$(BOOST_LIB_SUFFIX) + +ifndef USE_UPNP + override USE_UPNP = - +endif +ifneq (${USE_UPNP}, -) + LIBS += -l miniupnpc + DEFS += -DUSE_UPNP=$(USE_UPNP) +endif + +ifneq (${USE_IPV6}, -) + DEFS += -DUSE_IPV6=$(USE_IPV6) +endif + +LIBS+= \ + -Wl,-B$(LMODE2) \ + -l z \ + -l dl \ + -l pthread + + +# Hardening +# Make some classes of vulnerabilities unexploitable in case one is discovered. +# + # This is a workaround for Ubuntu bug #691722, the default -fstack-protector causes + # -fstack-protector-all to be ignored unless -fno-stack-protector is used first. + # see: https://bugs.launchpad.net/ubuntu/+source/gcc-4.5/+bug/691722 + HARDENING=-fno-stack-protector + + # Stack Canaries + # Put numbers at the beginning of each stack frame and check that they are the same. + # If a stack buffer if overflowed, it writes over the canary number and then on return + # when that number is checked, it won't be the same and the program will exit with + # a "Stack smashing detected" error instead of being exploited. + HARDENING+=-fstack-protector-all -Wstack-protector + + # Make some important things such as the global offset table read only as soon as + # the dynamic linker is finished building it. This will prevent overwriting of addresses + # which would later be jumped to. + LDHARDENING+=-Wl,-z,relro -Wl,-z,now + + # Build position independent code to take advantage of Address Space Layout Randomization + # offered by some kernels. + # see doc/build-unix.txt for more information. + ifdef PIE + HARDENING+=-fPIE + LDHARDENING+=-pie + endif + + # -D_FORTIFY_SOURCE=2 does some checking for potentially exploitable code patterns in + # the source such overflowing a statically defined buffer. + HARDENING+=-D_FORTIFY_SOURCE=2 +# + + +DEBUGFLAGS=-g + +# CXXFLAGS can be specified on the make command line, so we use xCXXFLAGS that only +# adds some defaults in front. Unfortunately, CXXFLAGS=... $(CXXFLAGS) does not work. +xCXXFLAGS=-O2 -pthread -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter \ + $(DEBUGFLAGS) $(DEFS) $(HARDENING) $(CXXFLAGS) + +# LDFLAGS can be specified on the make command line, so we use xLDFLAGS that only +# adds some defaults in front. Unfortunately, LDFLAGS=... $(LDFLAGS) does not work. +xLDFLAGS=$(LDHARDENING) $(LDFLAGS) + +OBJS= \ + leveldb/libleveldb.a \ + obj/alert.o \ + obj/version.o \ + obj/checkpoints.o \ + obj/netbase.o \ + obj/addrman.o \ + obj/crypter.o \ + obj/key.o \ + obj/db.o \ + obj/init.o \ + obj/irc.o \ + obj/keystore.o \ + obj/main.o \ + obj/net.o \ + obj/protocol.o \ + obj/bitcoinrpc.o \ + obj/rpcdump.o \ + obj/rpcnet.o \ + obj/rpcmining.o \ + obj/rpcwallet.o \ + obj/rpcblockchain.o \ + obj/rpcrawtransaction.o \ + obj/script.o \ + obj/sync.o \ + obj/util.o \ + obj/wallet.o \ + obj/walletdb.o \ + obj/hash.o \ + obj/bloom.o \ + obj/noui.o \ + obj/leveldb.o \ + obj/txdb.o + + +ifdef USE_SSE2 +DEFS += -DUSE_SSE2 +OBJS_SSE2= obj/scrypt-sse2.o +OBJS += $(OBJS_SSE2) +endif + +all: CryptoLottoTokend + +test check: test_CryptoLottoToken FORCE + ./test_CryptoLottoToken + +# +# LevelDB support +# +MAKEOVERRIDES = +LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a +DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) +DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) +leveldb/libleveldb.a: + @echo "Building LevelDB ..." && cd leveldb && $(MAKE) CC=$(CC) CXX=$(CXX) OPT="$(xCXXFLAGS)" libleveldb.a libmemenv.a && cd .. + +# auto-generated dependencies: +-include obj/*.P +-include obj-test/*.P + +obj/build.h: FORCE + /bin/sh ../share/genbuild.sh obj/build.h +version.cpp: obj/build.h +DEFS += -DHAVE_BUILD_INFO + +obj/%-sse2.o: %-sse2.cpp + $(CXX) -c $(xCXXFLAGS) -msse2 -MMD -MF $(@:%.o=%.d) -o $@ $< + @cp $(@:%.o=%.d) $(@:%.o=%.P); \ + sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ + -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ + rm -f $(@:%.o=%.d) + +obj/%.o: %.cpp + $(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $< + @cp $(@:%.o=%.d) $(@:%.o=%.P); \ + sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ + -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ + rm -f $(@:%.o=%.d) + +CryptoLottoTokend: $(OBJS:obj/%=obj/%) + $(LINK) $(xCXXFLAGS) -o $@ $^ $(xLDFLAGS) $(LIBS) + +TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp)) + +obj-test/%.o: test/%.cpp + $(CXX) -c $(TESTDEFS) $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $< + @cp $(@:%.o=%.d) $(@:%.o=%.P); \ + sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ + -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ + rm -f $(@:%.o=%.d) + +test_CryptoLottoToken: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%)) + $(LINK) $(xCXXFLAGS) -o $@ $(LIBPATHS) $^ $(TESTLIBS) $(xLDFLAGS) $(LIBS) + +clean: + -rm -f CryptoLottoTokend test_CryptoLottoToken + -rm -f obj/*.o + -rm -f obj-test/*.o + -rm -f obj/*.P + -rm -f obj-test/*.P + -rm -f obj/build.h + -cd leveldb && $(MAKE) clean || true + +FORCE: diff --git a/src/mruset.h b/src/mruset.h new file mode 100644 index 0000000..a527351 --- /dev/null +++ b/src/mruset.h @@ -0,0 +1,64 @@ +// Copyright (c) 2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_MRUSET_H +#define BITCOIN_MRUSET_H + +#include +#include + +/** STL-like set container that only keeps the most recent N elements. */ +template class mruset +{ +public: + typedef T key_type; + typedef T value_type; + typedef typename std::set::iterator iterator; + typedef typename std::set::const_iterator const_iterator; + typedef typename std::set::size_type size_type; + +protected: + std::set set; + std::deque queue; + size_type nMaxSize; + +public: + mruset(size_type nMaxSizeIn = 0) { nMaxSize = nMaxSizeIn; } + iterator begin() const { return set.begin(); } + iterator end() const { return set.end(); } + size_type size() const { return set.size(); } + bool empty() const { return set.empty(); } + iterator find(const key_type& k) const { return set.find(k); } + size_type count(const key_type& k) const { return set.count(k); } + bool inline friend operator==(const mruset& a, const mruset& b) { return a.set == b.set; } + bool inline friend operator==(const mruset& a, const std::set& b) { return a.set == b; } + bool inline friend operator<(const mruset& a, const mruset& b) { return a.set < b.set; } + std::pair insert(const key_type& x) + { + std::pair ret = set.insert(x); + if (ret.second) + { + if (nMaxSize && queue.size() == nMaxSize) + { + set.erase(queue.front()); + queue.pop_front(); + } + queue.push_back(x); + } + return ret; + } + size_type max_size() const { return nMaxSize; } + size_type max_size(size_type s) + { + if (s) + while (queue.size() > s) + { + set.erase(queue.front()); + queue.pop_front(); + } + nMaxSize = s; + return nMaxSize; + } +}; + +#endif diff --git a/src/net.cpp b/src/net.cpp new file mode 100644 index 0000000..a96e9f3 --- /dev/null +++ b/src/net.cpp @@ -0,0 +1,1905 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "irc.h" +#include "db.h" +#include "net.h" +#include "init.h" +#include "addrman.h" +#include "ui_interface.h" +#include "script.h" + +#ifdef WIN32 +#include +#endif + +#ifdef USE_UPNP +#include +#include +#include +#include +#endif + +// Dump addresses to peers.dat every 15 minutes (900s) +#define DUMP_ADDRESSES_INTERVAL 900 + +using namespace std; +using namespace boost; + +static const int MAX_OUTBOUND_CONNECTIONS = 8; + +bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false); + + +struct LocalServiceInfo { + int nScore; + int nPort; +}; + +// +// Global state variables +// +bool fDiscover = true; +uint64 nLocalServices = NODE_NETWORK; +static CCriticalSection cs_mapLocalHost; +static map mapLocalHost; +static bool vfReachable[NET_MAX] = {}; +static bool vfLimited[NET_MAX] = {}; +static CNode* pnodeLocalHost = NULL; +static CNode* pnodeSync = NULL; +uint64 nLocalHostNonce = 0; +static std::vector vhListenSocket; +CAddrMan addrman; +int nMaxConnections = 125; + +vector vNodes; +CCriticalSection cs_vNodes; +map mapRelay; +deque > vRelayExpiration; +CCriticalSection cs_mapRelay; +limitedmap mapAlreadyAskedFor(MAX_INV_SZ); + +static deque vOneShots; +CCriticalSection cs_vOneShots; + +set setservAddNodeAddresses; +CCriticalSection cs_setservAddNodeAddresses; + +vector vAddedNodes; +CCriticalSection cs_vAddedNodes; + +static CSemaphore *semOutbound = NULL; + +void AddOneShot(string strDest) +{ + LOCK(cs_vOneShots); + vOneShots.push_back(strDest); +} + +unsigned short GetListenPort() +{ + return (unsigned short)(GetArg("-port", GetDefaultPort())); +} + +void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) +{ + // Filter out duplicate requests + if (pindexBegin == pindexLastGetBlocksBegin && hashEnd == hashLastGetBlocksEnd) + return; + pindexLastGetBlocksBegin = pindexBegin; + hashLastGetBlocksEnd = hashEnd; + + PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); +} + +// find 'best' local address for a particular peer +bool GetLocal(CService& addr, const CNetAddr *paddrPeer) +{ + if (fNoListen) + return false; + + int nBestScore = -1; + int nBestReachability = -1; + { + LOCK(cs_mapLocalHost); + for (map::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++) + { + int nScore = (*it).second.nScore; + int nReachability = (*it).first.GetReachabilityFrom(paddrPeer); + if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore)) + { + addr = CService((*it).first, (*it).second.nPort); + nBestReachability = nReachability; + nBestScore = nScore; + } + } + } + return nBestScore >= 0; +} + +// get best local address for a particular peer as a CAddress +CAddress GetLocalAddress(const CNetAddr *paddrPeer) +{ + CAddress ret(CService("0.0.0.0",0),0); + CService addr; + if (GetLocal(addr, paddrPeer)) + { + ret = CAddress(addr); + ret.nServices = nLocalServices; + ret.nTime = GetAdjustedTime(); + } + return ret; +} + +bool RecvLine(SOCKET hSocket, string& strLine) +{ + strLine = ""; + loop + { + char c; + int nBytes = recv(hSocket, &c, 1, 0); + if (nBytes > 0) + { + if (c == '\n') + continue; + if (c == '\r') + return true; + strLine += c; + if (strLine.size() >= 9000) + return true; + } + else if (nBytes <= 0) + { + boost::this_thread::interruption_point(); + if (nBytes < 0) + { + int nErr = WSAGetLastError(); + if (nErr == WSAEMSGSIZE) + continue; + if (nErr == WSAEWOULDBLOCK || nErr == WSAEINTR || nErr == WSAEINPROGRESS) + { + MilliSleep(10); + continue; + } + } + if (!strLine.empty()) + return true; + if (nBytes == 0) + { + // socket closed + printf("socket closed\n"); + return false; + } + else + { + // socket error + int nErr = WSAGetLastError(); + printf("recv failed: %d\n", nErr); + return false; + } + } + } +} + +// used when scores of local addresses may have changed +// pushes better local address to peers +void static AdvertizeLocal() +{ + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + if (pnode->fSuccessfullyConnected) + { + CAddress addrLocal = GetLocalAddress(&pnode->addr); + if (addrLocal.IsRoutable() && (CService)addrLocal != (CService)pnode->addrLocal) + { + pnode->PushAddress(addrLocal); + pnode->addrLocal = addrLocal; + } + } + } +} + +void SetReachable(enum Network net, bool fFlag) +{ + LOCK(cs_mapLocalHost); + vfReachable[net] = fFlag; + if (net == NET_IPV6 && fFlag) + vfReachable[NET_IPV4] = true; +} + +// learn a new local address +bool AddLocal(const CService& addr, int nScore) +{ + if (!addr.IsRoutable()) + return false; + + if (!fDiscover && nScore < LOCAL_MANUAL) + return false; + + if (IsLimited(addr)) + return false; + + printf("AddLocal(%s,%i)\n", addr.ToString().c_str(), nScore); + + { + LOCK(cs_mapLocalHost); + bool fAlready = mapLocalHost.count(addr) > 0; + LocalServiceInfo &info = mapLocalHost[addr]; + if (!fAlready || nScore >= info.nScore) { + info.nScore = nScore + (fAlready ? 1 : 0); + info.nPort = addr.GetPort(); + } + SetReachable(addr.GetNetwork()); + } + + AdvertizeLocal(); + + return true; +} + +bool AddLocal(const CNetAddr &addr, int nScore) +{ + return AddLocal(CService(addr, GetListenPort()), nScore); +} + +/** Make a particular network entirely off-limits (no automatic connects to it) */ +void SetLimited(enum Network net, bool fLimited) +{ + if (net == NET_UNROUTABLE) + return; + LOCK(cs_mapLocalHost); + vfLimited[net] = fLimited; +} + +bool IsLimited(enum Network net) +{ + LOCK(cs_mapLocalHost); + return vfLimited[net]; +} + +bool IsLimited(const CNetAddr &addr) +{ + return IsLimited(addr.GetNetwork()); +} + +/** vote for a local address */ +bool SeenLocal(const CService& addr) +{ + { + LOCK(cs_mapLocalHost); + if (mapLocalHost.count(addr) == 0) + return false; + mapLocalHost[addr].nScore++; + } + + AdvertizeLocal(); + + return true; +} + +/** check whether a given address is potentially local */ +bool IsLocal(const CService& addr) +{ + LOCK(cs_mapLocalHost); + return mapLocalHost.count(addr) > 0; +} + +/** check whether a given address is in a network we can probably connect to */ +bool IsReachable(const CNetAddr& addr) +{ + LOCK(cs_mapLocalHost); + enum Network net = addr.GetNetwork(); + return vfReachable[net] && !vfLimited[net]; +} + +bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const char* pszKeyword, CNetAddr& ipRet) +{ + SOCKET hSocket; + if (!ConnectSocket(addrConnect, hSocket)) + return error("GetMyExternalIP() : connection to %s failed", addrConnect.ToString().c_str()); + + send(hSocket, pszGet, strlen(pszGet), MSG_NOSIGNAL); + + string strLine; + while (RecvLine(hSocket, strLine)) + { + if (strLine.empty()) // HTTP response is separated from headers by blank line + { + loop + { + if (!RecvLine(hSocket, strLine)) + { + closesocket(hSocket); + return false; + } + if (pszKeyword == NULL) + break; + if (strLine.find(pszKeyword) != string::npos) + { + strLine = strLine.substr(strLine.find(pszKeyword) + strlen(pszKeyword)); + break; + } + } + closesocket(hSocket); + if (strLine.find("<") != string::npos) + strLine = strLine.substr(0, strLine.find("<")); + strLine = strLine.substr(strspn(strLine.c_str(), " \t\n\r")); + while (strLine.size() > 0 && isspace(strLine[strLine.size()-1])) + strLine.resize(strLine.size()-1); + CService addr(strLine,0,true); + printf("GetMyExternalIP() received [%s] %s\n", strLine.c_str(), addr.ToString().c_str()); + if (!addr.IsValid() || !addr.IsRoutable()) + return false; + ipRet.SetIP(addr); + return true; + } + } + closesocket(hSocket); + return error("GetMyExternalIP() : connection closed"); +} + +bool GetMyExternalIP(CNetAddr& ipRet) +{ + CService addrConnect; + const char* pszGet; + const char* pszKeyword; + + for (int nLookup = 0; nLookup <= 1; nLookup++) + for (int nHost = 1; nHost <= 2; nHost++) + { + // We should be phasing out our use of sites like these. If we need + // replacements, we should ask for volunteers to put this simple + // php file on their web server that prints the client IP: + // + if (nHost == 1) + { + addrConnect = CService("91.198.22.70", 80); // checkip.dyndns.org + + if (nLookup == 1) + { + CService addrIP("checkip.dyndns.org", 80, true); + if (addrIP.IsValid()) + addrConnect = addrIP; + } + + pszGet = "GET / HTTP/1.1\r\n" + "Host: checkip.dyndns.org\r\n" + "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n" + "Connection: close\r\n" + "\r\n"; + + pszKeyword = "Address:"; + } + else if (nHost == 2) + { + addrConnect = CService("74.208.43.192", 80); // www.showmyip.com + + if (nLookup == 1) + { + CService addrIP("www.showmyip.com", 80, true); + if (addrIP.IsValid()) + addrConnect = addrIP; + } + + pszGet = "GET /simple/ HTTP/1.1\r\n" + "Host: www.showmyip.com\r\n" + "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n" + "Connection: close\r\n" + "\r\n"; + + pszKeyword = NULL; // Returns just IP address + } + + if (GetMyExternalIP2(addrConnect, pszGet, pszKeyword, ipRet)) + return true; + } + + return false; +} + +void ThreadGetMyExternalIP(void* parg) +{ + // Make this thread recognisable as the external IP detection thread + RenameThread("bitcoin-ext-ip"); + + CNetAddr addrLocalHost; + if (GetMyExternalIP(addrLocalHost)) + { + printf("GetMyExternalIP() returned %s\n", addrLocalHost.ToStringIP().c_str()); + AddLocal(addrLocalHost, LOCAL_HTTP); + } +} + + + + + +void AddressCurrentlyConnected(const CService& addr) +{ + addrman.Connected(addr); +} + + + + + + + +CNode* FindNode(const CNetAddr& ip) +{ + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + if ((CNetAddr)pnode->addr == ip) + return (pnode); + return NULL; +} + +CNode* FindNode(std::string addrName) +{ + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode->addrName == addrName) + return (pnode); + return NULL; +} + +CNode* FindNode(const CService& addr) +{ + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + if ((CService)pnode->addr == addr) + return (pnode); + return NULL; +} + +CNode* ConnectNode(CAddress addrConnect, const char *pszDest) +{ + if (pszDest == NULL) { + if (IsLocal(addrConnect)) + return NULL; + + // Look for an existing connection + CNode* pnode = FindNode((CService)addrConnect); + if (pnode) + { + pnode->AddRef(); + return pnode; + } + } + + + /// debug print + printf("trying connection %s lastseen=%.1fhrs\n", + pszDest ? pszDest : addrConnect.ToString().c_str(), + pszDest ? 0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); + + // Connect + SOCKET hSocket; + if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, GetDefaultPort()) : ConnectSocket(addrConnect, hSocket)) + { + addrman.Attempt(addrConnect); + + /// debug print + printf("connected %s\n", pszDest ? pszDest : addrConnect.ToString().c_str()); + + // Set to non-blocking +#ifdef WIN32 + u_long nOne = 1; + if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR) + printf("ConnectSocket() : ioctlsocket non-blocking setting failed, error %d\n", WSAGetLastError()); +#else + if (fcntl(hSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) + printf("ConnectSocket() : fcntl non-blocking setting failed, error %d\n", errno); +#endif + + // Add node + CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false); + pnode->AddRef(); + + { + LOCK(cs_vNodes); + vNodes.push_back(pnode); + } + + pnode->nTimeConnected = GetTime(); + return pnode; + } + else + { + return NULL; + } +} + +void CNode::CloseSocketDisconnect() +{ + fDisconnect = true; + if (hSocket != INVALID_SOCKET) + { + printf("disconnecting node %s\n", addrName.c_str()); + closesocket(hSocket); + hSocket = INVALID_SOCKET; + } + + // in case this fails, we'll empty the recv buffer when the CNode is deleted + TRY_LOCK(cs_vRecvMsg, lockRecv); + if (lockRecv) + vRecvMsg.clear(); + + // if this was the sync node, we'll need a new one + if (this == pnodeSync) + pnodeSync = NULL; +} + +void CNode::Cleanup() +{ +} + + +void CNode::PushVersion() +{ + /// when NTP implemented, change to just nTime = GetAdjustedTime() + int64 nTime = (fInbound ? GetAdjustedTime() : GetTime()); + CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0))); + CAddress addrMe = GetLocalAddress(&addr); + RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); + printf("send version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString().c_str(), addrYou.ToString().c_str(), addr.ToString().c_str()); + PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe, + nLocalHostNonce, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector()), nBestHeight); +} + + + + + +std::map CNode::setBanned; +CCriticalSection CNode::cs_setBanned; + +void CNode::ClearBanned() +{ + setBanned.clear(); +} + +bool CNode::IsBanned(CNetAddr ip) +{ + bool fResult = false; + { + LOCK(cs_setBanned); + std::map::iterator i = setBanned.find(ip); + if (i != setBanned.end()) + { + int64 t = (*i).second; + if (GetTime() < t) + fResult = true; + } + } + return fResult; +} + +bool CNode::Misbehaving(int howmuch) +{ + if (addr.IsLocal()) + { + printf("Warning: Local node %s misbehaving (delta: %d)!\n", addrName.c_str(), howmuch); + return false; + } + + nMisbehavior += howmuch; + if (nMisbehavior >= GetArg("-banscore", 100)) + { + int64 banTime = GetTime()+GetArg("-bantime", 60*60*24); // Default 24-hour ban + printf("Misbehaving: %s (%d -> %d) DISCONNECTING\n", addr.ToString().c_str(), nMisbehavior-howmuch, nMisbehavior); + { + LOCK(cs_setBanned); + if (setBanned[addr] < banTime) + setBanned[addr] = banTime; + } + CloseSocketDisconnect(); + return true; + } else + printf("Misbehaving: %s (%d -> %d)\n", addr.ToString().c_str(), nMisbehavior-howmuch, nMisbehavior); + return false; +} + +#undef X +#define X(name) stats.name = name +void CNode::copyStats(CNodeStats &stats) +{ + X(nServices); + X(nLastSend); + X(nLastRecv); + X(nTimeConnected); + X(addrName); + X(nVersion); + X(cleanSubVer); + X(fInbound); + X(nStartingHeight); + X(nMisbehavior); + X(nSendBytes); + X(nRecvBytes); + X(nBlocksRequested); + stats.fSyncNode = (this == pnodeSync); +} +#undef X + +// requires LOCK(cs_vRecvMsg) +bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes) +{ + while (nBytes > 0) { + + // get current incomplete message, or create a new one + if (vRecvMsg.empty() || + vRecvMsg.back().complete()) + vRecvMsg.push_back(CNetMessage(SER_NETWORK, nRecvVersion)); + + CNetMessage& msg = vRecvMsg.back(); + + // absorb network data + int handled; + if (!msg.in_data) + handled = msg.readHeader(pch, nBytes); + else + handled = msg.readData(pch, nBytes); + + if (handled < 0) + return false; + + pch += handled; + nBytes -= handled; + } + + return true; +} + +int CNetMessage::readHeader(const char *pch, unsigned int nBytes) +{ + // copy data to temporary parsing buffer + unsigned int nRemaining = 24 - nHdrPos; + unsigned int nCopy = std::min(nRemaining, nBytes); + + memcpy(&hdrbuf[nHdrPos], pch, nCopy); + nHdrPos += nCopy; + + // if header incomplete, exit + if (nHdrPos < 24) + return nCopy; + + // deserialize to CMessageHeader + try { + hdrbuf >> hdr; + } + catch (std::exception &e) { + return -1; + } + + // reject messages larger than MAX_SIZE + if (hdr.nMessageSize > MAX_SIZE) + return -1; + + // switch state to reading message data + in_data = true; + vRecv.resize(hdr.nMessageSize); + + return nCopy; +} + +int CNetMessage::readData(const char *pch, unsigned int nBytes) +{ + unsigned int nRemaining = hdr.nMessageSize - nDataPos; + unsigned int nCopy = std::min(nRemaining, nBytes); + + memcpy(&vRecv[nDataPos], pch, nCopy); + nDataPos += nCopy; + + return nCopy; +} + + + + + + + + + +// requires LOCK(cs_vSend) +void SocketSendData(CNode *pnode) +{ + std::deque::iterator it = pnode->vSendMsg.begin(); + + while (it != pnode->vSendMsg.end()) { + const CSerializeData &data = *it; + assert(data.size() > pnode->nSendOffset); + int nBytes = send(pnode->hSocket, &data[pnode->nSendOffset], data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT); + if (nBytes > 0) { + pnode->nLastSend = GetTime(); + pnode->nSendBytes += nBytes; + pnode->nSendOffset += nBytes; + if (pnode->nSendOffset == data.size()) { + pnode->nSendOffset = 0; + pnode->nSendSize -= data.size(); + it++; + } else { + // could not send full message; stop sending more + break; + } + } else { + if (nBytes < 0) { + // error + int nErr = WSAGetLastError(); + if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) + { + printf("socket send error %d\n", nErr); + pnode->CloseSocketDisconnect(); + } + } + // couldn't send anything at all + break; + } + } + + if (it == pnode->vSendMsg.end()) { + assert(pnode->nSendOffset == 0); + assert(pnode->nSendSize == 0); + } + pnode->vSendMsg.erase(pnode->vSendMsg.begin(), it); +} + +static list vNodesDisconnected; + +void ThreadSocketHandler() +{ + unsigned int nPrevNodeCount = 0; + loop + { + // + // Disconnect nodes + // + { + LOCK(cs_vNodes); + // Disconnect unused nodes + vector vNodesCopy = vNodes; + BOOST_FOREACH(CNode* pnode, vNodesCopy) + { + if (pnode->fDisconnect || + (pnode->GetRefCount() <= 0 && pnode->vRecvMsg.empty() && pnode->nSendSize == 0 && pnode->ssSend.empty())) + { + // remove from vNodes + vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); + + // release outbound grant (if any) + pnode->grantOutbound.Release(); + + // close socket and cleanup + pnode->CloseSocketDisconnect(); + pnode->Cleanup(); + + // hold in disconnected pool until all refs are released + if (pnode->fNetworkNode || pnode->fInbound) + pnode->Release(); + vNodesDisconnected.push_back(pnode); + } + } + + // Delete disconnected nodes + list vNodesDisconnectedCopy = vNodesDisconnected; + BOOST_FOREACH(CNode* pnode, vNodesDisconnectedCopy) + { + // wait until threads are done using it + if (pnode->GetRefCount() <= 0) + { + bool fDelete = false; + { + TRY_LOCK(pnode->cs_vSend, lockSend); + if (lockSend) + { + TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); + if (lockRecv) + { + TRY_LOCK(pnode->cs_inventory, lockInv); + if (lockInv) + fDelete = true; + } + } + } + if (fDelete) + { + vNodesDisconnected.remove(pnode); + delete pnode; + } + } + } + } + if (vNodes.size() != nPrevNodeCount) + { + nPrevNodeCount = vNodes.size(); + uiInterface.NotifyNumConnectionsChanged(vNodes.size()); + } + + + // + // Find which sockets have data to receive + // + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 50000; // frequency to poll pnode->vSend + + fd_set fdsetRecv; + fd_set fdsetSend; + fd_set fdsetError; + FD_ZERO(&fdsetRecv); + FD_ZERO(&fdsetSend); + FD_ZERO(&fdsetError); + SOCKET hSocketMax = 0; + bool have_fds = false; + + BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) { + FD_SET(hListenSocket, &fdsetRecv); + hSocketMax = max(hSocketMax, hListenSocket); + have_fds = true; + } + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + if (pnode->hSocket == INVALID_SOCKET) + continue; + FD_SET(pnode->hSocket, &fdsetError); + hSocketMax = max(hSocketMax, pnode->hSocket); + have_fds = true; + + // Implement the following logic: + // * If there is data to send, select() for sending data. As this only + // happens when optimistic write failed, we choose to first drain the + // write buffer in this case before receiving more. This avoids + // needlessly queueing received data, if the remote peer is not themselves + // receiving data. This means properly utilizing TCP flow control signalling. + // * Otherwise, if there is no (complete) message in the receive buffer, + // or there is space left in the buffer, select() for receiving data. + // * (if neither of the above applies, there is certainly one message + // in the receiver buffer ready to be processed). + // Together, that means that at least one of the following is always possible, + // so we don't deadlock: + // * We send some data. + // * We wait for data to be received (and disconnect after timeout). + // * We process a message in the buffer (message handler thread). + { + TRY_LOCK(pnode->cs_vSend, lockSend); + if (lockSend && !pnode->vSendMsg.empty()) { + FD_SET(pnode->hSocket, &fdsetSend); + continue; + } + } + { + TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); + if (lockRecv && ( + pnode->vRecvMsg.empty() || !pnode->vRecvMsg.front().complete() || + pnode->GetTotalRecvSize() <= ReceiveFloodSize())) + FD_SET(pnode->hSocket, &fdsetRecv); + } + } + } + + int nSelect = select(have_fds ? hSocketMax + 1 : 0, + &fdsetRecv, &fdsetSend, &fdsetError, &timeout); + boost::this_thread::interruption_point(); + + if (nSelect == SOCKET_ERROR) + { + if (have_fds) + { + int nErr = WSAGetLastError(); + printf("socket select error %d\n", nErr); + for (unsigned int i = 0; i <= hSocketMax; i++) + FD_SET(i, &fdsetRecv); + } + FD_ZERO(&fdsetSend); + FD_ZERO(&fdsetError); + MilliSleep(timeout.tv_usec/1000); + } + + + // + // Accept new connections + // + BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) + if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv)) + { +#ifdef USE_IPV6 + struct sockaddr_storage sockaddr; +#else + struct sockaddr sockaddr; +#endif + socklen_t len = sizeof(sockaddr); + SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); + CAddress addr; + int nInbound = 0; + + if (hSocket != INVALID_SOCKET) + if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) + printf("Warning: Unknown socket family\n"); + + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode->fInbound) + nInbound++; + } + + if (hSocket == INVALID_SOCKET) + { + int nErr = WSAGetLastError(); + if (nErr != WSAEWOULDBLOCK) + printf("socket error accept failed: %d\n", nErr); + } + else if (nInbound >= nMaxConnections - MAX_OUTBOUND_CONNECTIONS) + { + { + LOCK(cs_setservAddNodeAddresses); + if (!setservAddNodeAddresses.count(addr)) + closesocket(hSocket); + } + } + else if (CNode::IsBanned(addr)) + { + printf("connection from %s dropped (banned)\n", addr.ToString().c_str()); + closesocket(hSocket); + } + else + { + printf("accepted connection %s\n", addr.ToString().c_str()); + CNode* pnode = new CNode(hSocket, addr, "", true); + pnode->AddRef(); + { + LOCK(cs_vNodes); + vNodes.push_back(pnode); + } + } + } + + + // + // Service each socket + // + vector vNodesCopy; + { + LOCK(cs_vNodes); + vNodesCopy = vNodes; + BOOST_FOREACH(CNode* pnode, vNodesCopy) + pnode->AddRef(); + } + BOOST_FOREACH(CNode* pnode, vNodesCopy) + { + boost::this_thread::interruption_point(); + + // + // Receive + // + if (pnode->hSocket == INVALID_SOCKET) + continue; + if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError)) + { + TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); + if (lockRecv) + { + { + // typical socket buffer is 8K-64K + char pchBuf[0x10000]; + int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); + if (nBytes > 0) + { + if (!pnode->ReceiveMsgBytes(pchBuf, nBytes)) + pnode->CloseSocketDisconnect(); + pnode->nLastRecv = GetTime(); + pnode->nRecvBytes += nBytes; + } + else if (nBytes == 0) + { + // socket closed gracefully + if (!pnode->fDisconnect) + printf("socket closed\n"); + pnode->CloseSocketDisconnect(); + } + else if (nBytes < 0) + { + // error + int nErr = WSAGetLastError(); + if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) + { + if (!pnode->fDisconnect) + printf("socket recv error %d\n", nErr); + pnode->CloseSocketDisconnect(); + } + } + } + } + } + + // + // Send + // + if (pnode->hSocket == INVALID_SOCKET) + continue; + if (FD_ISSET(pnode->hSocket, &fdsetSend)) + { + TRY_LOCK(pnode->cs_vSend, lockSend); + if (lockSend) + SocketSendData(pnode); + } + + // + // Inactivity checking + // + if (pnode->vSendMsg.empty()) + pnode->nLastSendEmpty = GetTime(); + if (GetTime() - pnode->nTimeConnected > 60) + { + if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) + { + printf("socket no message in first 60 seconds, %d %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0); + pnode->fDisconnect = true; + } + else if (GetTime() - pnode->nLastSend > 90*60 && GetTime() - pnode->nLastSendEmpty > 90*60) + { + printf("socket not sending\n"); + pnode->fDisconnect = true; + } + else if (GetTime() - pnode->nLastRecv > 90*60) + { + printf("socket inactivity timeout\n"); + pnode->fDisconnect = true; + } + } + } + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodesCopy) + pnode->Release(); + } + + MilliSleep(10); + } +} + + + + + + + + + +#ifdef USE_UPNP + +void ThreadMapPort() +{ + std::string port = strprintf("%u", GetListenPort()); + const char * multicastif = 0; + const char * minissdpdpath = 0; + struct UPNPDev * devlist = 0; + char lanaddr[64]; + +#ifndef UPNPDISCOVER_SUCCESS + /* miniupnpc 1.5 */ + devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0); +#else + /* miniupnpc 1.6 */ + int error = 0; + devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error); +#endif + + struct UPNPUrls urls; + struct IGDdatas data; + int r; + + r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); + if (r == 1) + { + if (fDiscover) { + char externalIPAddress[40]; + r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress); + if(r != UPNPCOMMAND_SUCCESS) + printf("UPnP: GetExternalIPAddress() returned %d\n", r); + else + { + if(externalIPAddress[0]) + { + printf("UPnP: ExternalIPAddress = %s\n", externalIPAddress); + AddLocal(CNetAddr(externalIPAddress), LOCAL_UPNP); + } + else + printf("UPnP: GetExternalIPAddress failed.\n"); + } + } + + string strDesc = "CryptoLottoToken " + FormatFullVersion(); + + try { + loop { +#ifndef UPNPDISCOVER_SUCCESS + /* miniupnpc 1.5 */ + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, + port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0); +#else + /* miniupnpc 1.6 */ + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, + port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); +#endif + + if(r!=UPNPCOMMAND_SUCCESS) + printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", + port.c_str(), port.c_str(), lanaddr, r, strupnperror(r)); + else + printf("UPnP Port Mapping successful.\n");; + + MilliSleep(20*60*1000); // Refresh every 20 minutes + } + } + catch (boost::thread_interrupted) + { + r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); + printf("UPNP_DeletePortMapping() returned : %d\n", r); + freeUPNPDevlist(devlist); devlist = 0; + FreeUPNPUrls(&urls); + throw; + } + } else { + printf("No valid UPnP IGDs found\n"); + freeUPNPDevlist(devlist); devlist = 0; + if (r != 0) + FreeUPNPUrls(&urls); + } +} + +void MapPort(bool fUseUPnP) +{ + static boost::thread* upnp_thread = NULL; + + if (fUseUPnP) + { + if (upnp_thread) { + upnp_thread->interrupt(); + upnp_thread->join(); + delete upnp_thread; + } + upnp_thread = new boost::thread(boost::bind(&TraceThread >, "upnp", &ThreadMapPort)); + } + else if (upnp_thread) { + upnp_thread->interrupt(); + upnp_thread->join(); + delete upnp_thread; + upnp_thread = NULL; + } +} + +#else +void MapPort(bool) +{ + // Intentionally left blank. +} +#endif + + + + + + + + + +// DNS seeds +// Each pair gives a source name and a seed name. +// The first name is used as information source for addrman. +// The second name should resolve to a list of seed addresses. +static const char *strMainNetDNSSeed[][2] = { + {"195.34.100.2","195.34.100.2"}, + {NULL, NULL} +}; + +static const char *strTestNetDNSSeed[][2] = { + {NULL, NULL} +}; + +void ThreadDNSAddressSeed() +{ + static const char *(*strDNSSeed)[2] = fTestNet ? strTestNetDNSSeed : strMainNetDNSSeed; + + int found = 0; + + printf("Loading addresses from DNS seeds (could take a while)\n"); + + for (unsigned int seed_idx = 0; strDNSSeed[seed_idx][0] != NULL; seed_idx++) { + if (HaveNameProxy()) { + AddOneShot(strDNSSeed[seed_idx][1]); + } else { + vector vaddr; + vector vAdd; + if (LookupHost(strDNSSeed[seed_idx][1], vaddr)) + { + BOOST_FOREACH(CNetAddr& ip, vaddr) + { + int nOneDay = 24*3600; + CAddress addr = CAddress(CService(ip, GetDefaultPort())); + addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old + vAdd.push_back(addr); + found++; + } + } + addrman.Add(vAdd, CNetAddr(strDNSSeed[seed_idx][0], true)); + } + } + + printf("%d addresses found from DNS seeds\n", found); +} + + + + + + + + + + + + +unsigned int pnSeed[] = +{ + 0x119caa6b +// 0x92B9B572, 0xA2F3716E, 0x5F551D90 +}; + +void DumpAddresses() +{ + int64 nStart = GetTimeMillis(); + + CAddrDB adb; + adb.Write(addrman); + + printf("Flushed %d addresses to peers.dat %"PRI64d"ms\n", + addrman.size(), GetTimeMillis() - nStart); +} + +void static ProcessOneShot() +{ + string strDest; + { + LOCK(cs_vOneShots); + if (vOneShots.empty()) + return; + strDest = vOneShots.front(); + vOneShots.pop_front(); + } + CAddress addr; + CSemaphoreGrant grant(*semOutbound, true); + if (grant) { + if (!OpenNetworkConnection(addr, &grant, strDest.c_str(), true)) + AddOneShot(strDest); + } +} + +void ThreadOpenConnections() +{ + // Connect to specific addresses + if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) + { + for (int64 nLoop = 0;; nLoop++) + { + ProcessOneShot(); + BOOST_FOREACH(string strAddr, mapMultiArgs["-connect"]) + { + CAddress addr; + OpenNetworkConnection(addr, NULL, strAddr.c_str()); + for (int i = 0; i < 10 && i < nLoop; i++) + { + MilliSleep(500); + } + } + MilliSleep(500); + } + } + + // Initiate network connections + int64 nStart = GetTime(); + loop + { + ProcessOneShot(); + + MilliSleep(500); + + CSemaphoreGrant grant(*semOutbound); + boost::this_thread::interruption_point(); + + // Add seed nodes if IRC isn't working + if (addrman.size()==0 && (GetTime() - nStart > 60) && !fTestNet) + { + std::vector vAdd; + for (unsigned int i = 0; i < ARRAYLEN(pnSeed); i++) + { + // It'll only connect to one or two seed nodes because once it connects, + // it'll get a pile of addresses with newer timestamps. + // Seed nodes are given a random 'last seen time' of between one and two + // weeks ago. + const int64 nOneWeek = 7*24*60*60; + struct in_addr ip; + memcpy(&ip, &pnSeed[i], sizeof(ip)); + CAddress addr(CService(ip, GetDefaultPort())); + addr.nTime = GetTime()-GetRand(nOneWeek)-nOneWeek; + vAdd.push_back(addr); + } + addrman.Add(vAdd, CNetAddr("127.0.0.1")); + } + + // + // Choose an address to connect to based on most recently seen + // + CAddress addrConnect; + + // Only connect out to one peer per network group (/16 for IPv4). + // Do this here so we don't have to critsect vNodes inside mapAddresses critsect. + int nOutbound = 0; + set > setConnected; + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) { + if (!pnode->fInbound) { + setConnected.insert(pnode->addr.GetGroup()); + nOutbound++; + } + } + } + + int64 nANow = GetAdjustedTime(); + + int nTries = 0; + loop + { + // use an nUnkBias between 10 (no outgoing connections) and 90 (8 outgoing connections) + CAddress addr = addrman.Select(10 + min(nOutbound,8)*10); + + // if we selected an invalid address, restart + if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr)) + break; + + // If we didn't find an appropriate destination after trying 100 addresses fetched from addrman, + // stop this loop, and let the outer loop run again (which sleeps, adds seed nodes, recalculates + // already-connected network ranges, ...) before trying new addrman addresses. + nTries++; + if (nTries > 100) + break; + + if (IsLimited(addr)) + continue; + + // only consider very recently tried nodes after 30 failed attempts + if (nANow - addr.nLastTry < 600 && nTries < 30) + continue; + + // do not allow non-default ports, unless after 50 invalid addresses selected already + if (addr.GetPort() != GetDefaultPort() && nTries < 50) + continue; + + addrConnect = addr; + break; + } + + if (addrConnect.IsValid()) + OpenNetworkConnection(addrConnect, &grant); + } +} + +void ThreadOpenAddedConnections() +{ + { + LOCK(cs_vAddedNodes); + vAddedNodes = mapMultiArgs["-addnode"]; + } + + if (HaveNameProxy()) { + while(true) { + list lAddresses(0); + { + LOCK(cs_vAddedNodes); + BOOST_FOREACH(string& strAddNode, vAddedNodes) + lAddresses.push_back(strAddNode); + } + BOOST_FOREACH(string& strAddNode, lAddresses) { + CAddress addr; + CSemaphoreGrant grant(*semOutbound); + OpenNetworkConnection(addr, &grant, strAddNode.c_str()); + MilliSleep(500); + } + MilliSleep(120000); // Retry every 2 minutes + } + } + + for (unsigned int i = 0; true; i++) + { + list lAddresses(0); + { + LOCK(cs_vAddedNodes); + BOOST_FOREACH(string& strAddNode, vAddedNodes) + lAddresses.push_back(strAddNode); + } + + list > lservAddressesToAdd(0); + BOOST_FOREACH(string& strAddNode, lAddresses) + { + vector vservNode(0); + if(Lookup(strAddNode.c_str(), vservNode, GetDefaultPort(), fNameLookup, 0)) + { + lservAddressesToAdd.push_back(vservNode); + { + LOCK(cs_setservAddNodeAddresses); + BOOST_FOREACH(CService& serv, vservNode) + setservAddNodeAddresses.insert(serv); + } + } + } + // Attempt to connect to each IP for each addnode entry until at least one is successful per addnode entry + // (keeping in mind that addnode entries can have many IPs if fNameLookup) + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + for (list >::iterator it = lservAddressesToAdd.begin(); it != lservAddressesToAdd.end(); it++) + BOOST_FOREACH(CService& addrNode, *(it)) + if (pnode->addr == addrNode) + { + it = lservAddressesToAdd.erase(it); + it--; + break; + } + } + BOOST_FOREACH(vector& vserv, lservAddressesToAdd) + { + CSemaphoreGrant grant(*semOutbound); + OpenNetworkConnection(CAddress(vserv[i % vserv.size()]), &grant); + MilliSleep(500); + } + MilliSleep(120000); // Retry every 2 minutes + } +} + +// if successful, this moves the passed grant to the constructed node +bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound, const char *strDest, bool fOneShot) +{ + // + // Initiate outbound network connection + // + boost::this_thread::interruption_point(); + if (!strDest) + if (IsLocal(addrConnect) || + FindNode((CNetAddr)addrConnect) || CNode::IsBanned(addrConnect) || + FindNode(addrConnect.ToStringIPPort().c_str())) + return false; + if (strDest && FindNode(strDest)) + return false; + + CNode* pnode = ConnectNode(addrConnect, strDest); + boost::this_thread::interruption_point(); + + if (!pnode) + return false; + if (grantOutbound) + grantOutbound->MoveTo(pnode->grantOutbound); + pnode->fNetworkNode = true; + if (fOneShot) + pnode->fOneShot = true; + + return true; +} + + +// for now, use a very simple selection metric: the node from which we received +// most recently +double static NodeSyncScore(const CNode *pnode) { + return -pnode->nLastRecv; +} + +void static StartSync(const vector &vNodes) { + CNode *pnodeNewSync = NULL; + double dBestScore = 0; + + // fImporting and fReindex are accessed out of cs_main here, but only + // as an optimization - they are checked again in SendMessages. + if (fImporting || fReindex) + return; + + // Iterate over all nodes + BOOST_FOREACH(CNode* pnode, vNodes) { + // check preconditions for allowing a sync + if (!pnode->fClient && !pnode->fOneShot && + !pnode->fDisconnect && pnode->fSuccessfullyConnected && + (pnode->nStartingHeight > (nBestHeight - 144)) && + (pnode->nVersion < NOBLKS_VERSION_START || pnode->nVersion >= NOBLKS_VERSION_END)) { + // if ok, compare node's score with the best so far + double dScore = NodeSyncScore(pnode); + if (pnodeNewSync == NULL || dScore > dBestScore) { + pnodeNewSync = pnode; + dBestScore = dScore; + } + } + } + // if a new sync candidate was found, start sync! + if (pnodeNewSync) { + pnodeNewSync->fStartSync = true; + pnodeSync = pnodeNewSync; + } +} + +void ThreadMessageHandler() +{ + SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL); + while (true) + { + bool fHaveSyncNode = false; + + vector vNodesCopy; + { + LOCK(cs_vNodes); + vNodesCopy = vNodes; + BOOST_FOREACH(CNode* pnode, vNodesCopy) { + pnode->AddRef(); + if (pnode == pnodeSync) + fHaveSyncNode = true; + } + } + + if (!fHaveSyncNode) + StartSync(vNodesCopy); + + // Poll the connected nodes for messages + CNode* pnodeTrickle = NULL; + if (!vNodesCopy.empty()) + pnodeTrickle = vNodesCopy[GetRand(vNodesCopy.size())]; + + bool fSleep = true; + + BOOST_FOREACH(CNode* pnode, vNodesCopy) + { + if (pnode->fDisconnect) + continue; + + // Receive messages + { + TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); + if (lockRecv) + { + if (!ProcessMessages(pnode)) + pnode->CloseSocketDisconnect(); + + if (pnode->nSendSize < SendBufferSize()) + { + if (!pnode->vRecvGetData.empty() || (!pnode->vRecvMsg.empty() && pnode->vRecvMsg[0].complete())) + { + fSleep = false; + } + } + } + } + boost::this_thread::interruption_point(); + + // Send messages + { + TRY_LOCK(pnode->cs_vSend, lockSend); + if (lockSend) + SendMessages(pnode, pnode == pnodeTrickle); + } + boost::this_thread::interruption_point(); + } + + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodesCopy) + pnode->Release(); + } + + if (fSleep) + MilliSleep(100); + } +} + + + + + + +bool BindListenPort(const CService &addrBind, string& strError) +{ + strError = ""; + int nOne = 1; + + // Create socket for listening for incoming connections +#ifdef USE_IPV6 + struct sockaddr_storage sockaddr; +#else + struct sockaddr sockaddr; +#endif + socklen_t len = sizeof(sockaddr); + if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len)) + { + strError = strprintf("Error: bind address family for %s not supported", addrBind.ToString().c_str()); + printf("%s\n", strError.c_str()); + return false; + } + + SOCKET hListenSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP); + if (hListenSocket == INVALID_SOCKET) + { + strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %d)", WSAGetLastError()); + printf("%s\n", strError.c_str()); + return false; + } + +#ifdef SO_NOSIGPIPE + // Different way of disabling SIGPIPE on BSD + setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int)); +#endif + +#ifndef WIN32 + // Allow binding if the port is still in TIME_WAIT state after + // the program was closed and restarted. Not an issue on windows. + setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int)); +#endif + + +#ifdef WIN32 + // Set to non-blocking, incoming connections will also inherit this + if (ioctlsocket(hListenSocket, FIONBIO, (u_long*)&nOne) == SOCKET_ERROR) +#else + if (fcntl(hListenSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) +#endif + { + strError = strprintf("Error: Couldn't set properties on socket for incoming connections (error %d)", WSAGetLastError()); + printf("%s\n", strError.c_str()); + return false; + } + +#ifdef USE_IPV6 + // some systems don't have IPV6_V6ONLY but are always v6only; others do have the option + // and enable it by default or not. Try to enable it, if possible. + if (addrBind.IsIPv6()) { +#ifdef IPV6_V6ONLY +#ifdef WIN32 + setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&nOne, sizeof(int)); +#else + setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&nOne, sizeof(int)); +#endif +#endif +#ifdef WIN32 + int nProtLevel = 10 /* PROTECTION_LEVEL_UNRESTRICTED */; + int nParameterId = 23 /* IPV6_PROTECTION_LEVEl */; + // this call is allowed to fail + setsockopt(hListenSocket, IPPROTO_IPV6, nParameterId, (const char*)&nProtLevel, sizeof(int)); +#endif + } +#endif + + if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR) + { + int nErr = WSAGetLastError(); + if (nErr == WSAEADDRINUSE) + strError = strprintf(_("Unable to bind to %s on this computer. CryptoLottoToken is probably already running."), addrBind.ToString().c_str()); + else + strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %d, %s)"), addrBind.ToString().c_str(), nErr, strerror(nErr)); + printf("%s\n", strError.c_str()); + return false; + } + printf("Bound to %s\n", addrBind.ToString().c_str()); + + // Listen for incoming connections + if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) + { + strError = strprintf("Error: Listening for incoming connections failed (listen returned error %d)", WSAGetLastError()); + printf("%s\n", strError.c_str()); + return false; + } + + vhListenSocket.push_back(hListenSocket); + + if (addrBind.IsRoutable() && fDiscover) + AddLocal(addrBind, LOCAL_BIND); + + return true; +} + +void static Discover() +{ + if (!fDiscover) + return; + +#ifdef WIN32 + // Get local host IP + char pszHostName[1000] = ""; + if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) + { + vector vaddr; + if (LookupHost(pszHostName, vaddr)) + { + BOOST_FOREACH (const CNetAddr &addr, vaddr) + { + AddLocal(addr, LOCAL_IF); + } + } + } +#else + // Get local host ip + struct ifaddrs* myaddrs; + if (getifaddrs(&myaddrs) == 0) + { + for (struct ifaddrs* ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr == NULL) continue; + if ((ifa->ifa_flags & IFF_UP) == 0) continue; + if (strcmp(ifa->ifa_name, "lo") == 0) continue; + if (strcmp(ifa->ifa_name, "lo0") == 0) continue; + if (ifa->ifa_addr->sa_family == AF_INET) + { + struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr); + CNetAddr addr(s4->sin_addr); + if (AddLocal(addr, LOCAL_IF)) + printf("IPv4 %s: %s\n", ifa->ifa_name, addr.ToString().c_str()); + } +#ifdef USE_IPV6 + else if (ifa->ifa_addr->sa_family == AF_INET6) + { + struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_addr); + CNetAddr addr(s6->sin6_addr); + if (AddLocal(addr, LOCAL_IF)) + printf("IPv6 %s: %s\n", ifa->ifa_name, addr.ToString().c_str()); + } +#endif + } + freeifaddrs(myaddrs); + } +#endif + + // Don't use external IPv4 discovery, when -onlynet="IPv6" + if (!IsLimited(NET_IPV4)) + NewThread(ThreadGetMyExternalIP, NULL); +} + +void StartNode(boost::thread_group& threadGroup) +{ + if (semOutbound == NULL) { + // initialize semaphore + int nMaxOutbound = min(MAX_OUTBOUND_CONNECTIONS, nMaxConnections); + semOutbound = new CSemaphore(nMaxOutbound); + } + + if (pnodeLocalHost == NULL) + pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), nLocalServices)); + + Discover(); + + // + // Start threads + // + + if (!GetBoolArg("-dnsseed", true)) + printf("DNS seeding disabled\n"); + else + threadGroup.create_thread(boost::bind(&TraceThread >, "dnsseed", &ThreadDNSAddressSeed)); + +#ifdef USE_UPNP + // Map ports with UPnP + MapPort(GetBoolArg("-upnp", USE_UPNP)); +#endif + + // Get addresses from IRC and advertise ours + threadGroup.create_thread(boost::bind(&TraceThread, "irc", &ThreadIRCSeed)); + + // Send and receive from sockets, accept connections + threadGroup.create_thread(boost::bind(&TraceThread, "net", &ThreadSocketHandler)); + + // Initiate outbound connections from -addnode + threadGroup.create_thread(boost::bind(&TraceThread, "addcon", &ThreadOpenAddedConnections)); + + // Initiate outbound connections + threadGroup.create_thread(boost::bind(&TraceThread, "opencon", &ThreadOpenConnections)); + + // Process messages + threadGroup.create_thread(boost::bind(&TraceThread, "msghand", &ThreadMessageHandler)); + + // Dump network addresses + threadGroup.create_thread(boost::bind(&LoopForever, "dumpaddr", &DumpAddresses, DUMP_ADDRESSES_INTERVAL * 1000)); +} + +bool StopNode() +{ + printf("StopNode()\n"); + GenerateBitcoins(false, NULL); + MapPort(false); + nTransactionsUpdated++; + if (semOutbound) + for (int i=0; ipost(); + MilliSleep(50); + DumpAddresses(); + + return true; +} + +class CNetCleanup +{ +public: + CNetCleanup() + { + } + ~CNetCleanup() + { + // Close sockets + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode->hSocket != INVALID_SOCKET) + closesocket(pnode->hSocket); + BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) + if (hListenSocket != INVALID_SOCKET) + if (closesocket(hListenSocket) == SOCKET_ERROR) + printf("closesocket(hListenSocket) failed with error %d\n", WSAGetLastError()); + + // clean up some globals (to help leak detection) + BOOST_FOREACH(CNode *pnode, vNodes) + delete pnode; + BOOST_FOREACH(CNode *pnode, vNodesDisconnected) + delete pnode; + vNodes.clear(); + vNodesDisconnected.clear(); + delete semOutbound; + semOutbound = NULL; + delete pnodeLocalHost; + pnodeLocalHost = NULL; + +#ifdef WIN32 + // Shutdown Windows Sockets + WSACleanup(); +#endif + } +} +instance_of_cnetcleanup; + + + + + + + +void RelayTransaction(const CTransaction& tx, const uint256& hash) +{ + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(10000); + ss << tx; + RelayTransaction(tx, hash, ss); +} + +void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataStream& ss) +{ + CInv inv(MSG_TX, hash); + { + LOCK(cs_mapRelay); + // Expire old relay messages + while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime()) + { + mapRelay.erase(vRelayExpiration.front().second); + vRelayExpiration.pop_front(); + } + + // Save original serialized message so newer versions are preserved + mapRelay.insert(std::make_pair(inv, ss)); + vRelayExpiration.push_back(std::make_pair(GetTime() + 15 * 60, inv)); + } + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + if(!pnode->fRelayTxes) + continue; + LOCK(pnode->cs_filter); + if (pnode->pfilter) + { + if (pnode->pfilter->IsRelevantAndUpdate(tx, hash)) + pnode->PushInventory(inv); + } else + pnode->PushInventory(inv); + } +} diff --git a/src/net.h b/src/net.h new file mode 100644 index 0000000..a3033d7 --- /dev/null +++ b/src/net.h @@ -0,0 +1,645 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_NET_H +#define BITCOIN_NET_H + +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif + +#include "mruset.h" +#include "limitedmap.h" +#include "netbase.h" +#include "protocol.h" +#include "addrman.h" +#include "hash.h" +#include "bloom.h" + +class CNode; +class CBlockIndex; +extern int nBestHeight; + + + +inline unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); } +inline unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); } + +void AddOneShot(std::string strDest); +bool RecvLine(SOCKET hSocket, std::string& strLine); +bool GetMyExternalIP(CNetAddr& ipRet); +void AddressCurrentlyConnected(const CService& addr); +CNode* FindNode(const CNetAddr& ip); +CNode* FindNode(const CService& ip); +CNode* ConnectNode(CAddress addrConnect, const char *strDest = NULL); +void MapPort(bool fUseUPnP); +unsigned short GetListenPort(); +bool BindListenPort(const CService &bindAddr, std::string& strError=REF(std::string())); +void StartNode(boost::thread_group& threadGroup); +bool StopNode(); +void SocketSendData(CNode *pnode); + +enum +{ + LOCAL_NONE, // unknown + LOCAL_IF, // address a local interface listens on + LOCAL_BIND, // address explicit bound to + LOCAL_UPNP, // address reported by UPnP + LOCAL_IRC, // address reported by IRC (deprecated) + LOCAL_HTTP, // address reported by whatismyip.com and similar + LOCAL_MANUAL, // address explicitly specified (-externalip=) + + LOCAL_MAX +}; + +void SetLimited(enum Network net, bool fLimited = true); +bool IsLimited(enum Network net); +bool IsLimited(const CNetAddr& addr); +bool AddLocal(const CService& addr, int nScore = LOCAL_NONE); +bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE); +bool SeenLocal(const CService& addr); +bool IsLocal(const CService& addr); +bool GetLocal(CService &addr, const CNetAddr *paddrPeer = NULL); +bool IsReachable(const CNetAddr &addr); +void SetReachable(enum Network net, bool fFlag = true); +CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL); + + +extern bool fDiscover; +extern uint64 nLocalServices; +extern uint64 nLocalHostNonce; +extern CAddrMan addrman; +extern int nMaxConnections; + +extern std::vector vNodes; +extern CCriticalSection cs_vNodes; +extern std::map mapRelay; +extern std::deque > vRelayExpiration; +extern CCriticalSection cs_mapRelay; +extern limitedmap mapAlreadyAskedFor; + +extern std::vector vAddedNodes; +extern CCriticalSection cs_vAddedNodes; + + + + +class CNodeStats +{ +public: + uint64 nServices; + int64 nLastSend; + int64 nLastRecv; + int64 nTimeConnected; + std::string addrName; + int nVersion; + std::string cleanSubVer; + bool fInbound; + int nStartingHeight; + int nMisbehavior; + uint64 nSendBytes; + uint64 nRecvBytes; + uint64 nBlocksRequested; + bool fSyncNode; +}; + + + + +class CNetMessage { +public: + bool in_data; // parsing header (false) or data (true) + + CDataStream hdrbuf; // partially received header + CMessageHeader hdr; // complete header + unsigned int nHdrPos; + + CDataStream vRecv; // received message data + unsigned int nDataPos; + + CNetMessage(int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), vRecv(nTypeIn, nVersionIn) { + hdrbuf.resize(24); + in_data = false; + nHdrPos = 0; + nDataPos = 0; + } + + bool complete() const + { + if (!in_data) + return false; + return (hdr.nMessageSize == nDataPos); + } + + void SetVersion(int nVersionIn) + { + hdrbuf.SetVersion(nVersionIn); + vRecv.SetVersion(nVersionIn); + } + + int readHeader(const char *pch, unsigned int nBytes); + int readData(const char *pch, unsigned int nBytes); +}; + + + + + +/** Information about a peer */ +class CNode +{ +public: + // socket + uint64 nServices; + SOCKET hSocket; + CDataStream ssSend; + size_t nSendSize; // total size of all vSendMsg entries + size_t nSendOffset; // offset inside the first vSendMsg already sent + uint64 nSendBytes; + std::deque vSendMsg; + CCriticalSection cs_vSend; + + std::deque vRecvGetData; + std::deque vRecvMsg; + CCriticalSection cs_vRecvMsg; + uint64 nRecvBytes; + int nRecvVersion; + + int64 nLastSend; + int64 nLastRecv; + int64 nLastSendEmpty; + int64 nTimeConnected; + uint64 nBlocksRequested; + CAddress addr; + std::string addrName; + CService addrLocal; + int nVersion; + // strSubVer is whatever byte array we read from the wire. However, this field is intended + // to be printed out, displayed to humans in various forms and so on. So we sanitize it and + // store the sanitized version in cleanSubVer. The original should be used when dealing with + // the network or wire types and the cleaned string used when displayed or logged. + std::string strSubVer, cleanSubVer; + bool fOneShot; + bool fClient; + bool fInbound; + bool fNetworkNode; + bool fSuccessfullyConnected; + bool fDisconnect; + // We use fRelayTxes for two purposes - + // a) it allows us to not relay tx invs before receiving the peer's version message + // b) the peer may tell us in their version message that we should not relay tx invs + // until they have initialized their bloom filter. + bool fRelayTxes; + CSemaphoreGrant grantOutbound; + CCriticalSection cs_filter; + CBloomFilter* pfilter; + int nRefCount; +protected: + + // Denial-of-service detection/prevention + // Key is IP address, value is banned-until-time + static std::map setBanned; + static CCriticalSection cs_setBanned; + int nMisbehavior; + +public: + uint256 hashContinue; + CBlockIndex* pindexLastGetBlocksBegin; + uint256 hashLastGetBlocksEnd; + int nStartingHeight; + bool fStartSync; + + // flood relay + std::vector vAddrToSend; + std::set setAddrKnown; + bool fGetAddr; + std::set setKnown; + + // inventory based relay + mruset setInventoryKnown; + std::vector vInventoryToSend; + CCriticalSection cs_inventory; + std::multimap mapAskFor; + + CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn = "", bool fInboundIn=false) : ssSend(SER_NETWORK, INIT_PROTO_VERSION) + { + nServices = 0; + hSocket = hSocketIn; + nRecvVersion = INIT_PROTO_VERSION; + nLastSend = 0; + nLastRecv = 0; + nSendBytes = 0; + nRecvBytes = 0; + nLastSendEmpty = GetTime(); + nTimeConnected = GetTime(); + nBlocksRequested = 0; + addr = addrIn; + addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; + nVersion = 0; + strSubVer = ""; + fOneShot = false; + fClient = false; // set by version message + fInbound = fInboundIn; + fNetworkNode = false; + fSuccessfullyConnected = false; + fDisconnect = false; + nRefCount = 0; + nSendSize = 0; + nSendOffset = 0; + hashContinue = 0; + pindexLastGetBlocksBegin = 0; + hashLastGetBlocksEnd = 0; + nStartingHeight = -1; + fStartSync = false; + fGetAddr = false; + nMisbehavior = 0; + fRelayTxes = false; + setInventoryKnown.max_size(SendBufferSize() / 1000); + pfilter = new CBloomFilter(); + + // Be shy and don't send version until we hear + if (hSocket != INVALID_SOCKET && !fInbound) + PushVersion(); + } + + ~CNode() + { + if (hSocket != INVALID_SOCKET) + { + closesocket(hSocket); + hSocket = INVALID_SOCKET; + } + if (pfilter) + delete pfilter; + } + +private: + CNode(const CNode&); + void operator=(const CNode&); +public: + + + int GetRefCount() + { + assert(nRefCount >= 0); + return nRefCount; + } + + // requires LOCK(cs_vRecvMsg) + unsigned int GetTotalRecvSize() + { + unsigned int total = 0; + BOOST_FOREACH(const CNetMessage &msg, vRecvMsg) + total += msg.vRecv.size() + 24; + return total; + } + + // requires LOCK(cs_vRecvMsg) + bool ReceiveMsgBytes(const char *pch, unsigned int nBytes); + + // requires LOCK(cs_vRecvMsg) + void SetRecvVersion(int nVersionIn) + { + nRecvVersion = nVersionIn; + BOOST_FOREACH(CNetMessage &msg, vRecvMsg) + msg.SetVersion(nVersionIn); + } + + CNode* AddRef() + { + nRefCount++; + return this; + } + + void Release() + { + nRefCount--; + } + + + + void AddAddressKnown(const CAddress& addr) + { + setAddrKnown.insert(addr); + } + + void PushAddress(const CAddress& addr) + { + // Known checking here is only to save space from duplicates. + // SendMessages will filter it again for knowns that were added + // after addresses were pushed. + if (addr.IsValid() && !setAddrKnown.count(addr)) + vAddrToSend.push_back(addr); + } + + + void AddInventoryKnown(const CInv& inv) + { + { + LOCK(cs_inventory); + setInventoryKnown.insert(inv); + } + } + + void PushInventory(const CInv& inv) + { + { + LOCK(cs_inventory); + if (!setInventoryKnown.count(inv)) + vInventoryToSend.push_back(inv); + } + } + + void AskFor(const CInv& inv) + { + // We're using mapAskFor as a priority queue, + // the key is the earliest time the request can be sent + int64 nRequestTime; + limitedmap::const_iterator it = mapAlreadyAskedFor.find(inv); + if (it != mapAlreadyAskedFor.end()) + nRequestTime = it->second; + else + nRequestTime = 0; + if (fDebugNet) + printf("askfor %s %"PRI64d" (%s)\n", inv.ToString().c_str(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000).c_str()); + + // Make sure not to reuse time indexes to keep things in the same order + int64 nNow = (GetTime() - 1) * 1000000; + static int64 nLastTime; + ++nLastTime; + nNow = std::max(nNow, nLastTime); + nLastTime = nNow; + + // Each retry is 2 minutes after the last + nRequestTime = std::max(nRequestTime + 2 * 60 * 1000000, nNow); + if (it != mapAlreadyAskedFor.end()) + mapAlreadyAskedFor.update(it, nRequestTime); + else + mapAlreadyAskedFor.insert(std::make_pair(inv, nRequestTime)); + mapAskFor.insert(std::make_pair(nRequestTime, inv)); + } + + + + // TODO: Document the postcondition of this function. Is cs_vSend locked? + void BeginMessage(const char* pszCommand) EXCLUSIVE_LOCK_FUNCTION(cs_vSend) + { + ENTER_CRITICAL_SECTION(cs_vSend); + assert(ssSend.size() == 0); + ssSend << CMessageHeader(pszCommand, 0); + if (fDebug) + printf("sending: %s ", pszCommand); + } + + // TODO: Document the precondition of this function. Is cs_vSend locked? + void AbortMessage() UNLOCK_FUNCTION(cs_vSend) + { + ssSend.clear(); + + LEAVE_CRITICAL_SECTION(cs_vSend); + + if (fDebug) + printf("(aborted)\n"); + } + + // TODO: Document the precondition of this function. Is cs_vSend locked? + void EndMessage() UNLOCK_FUNCTION(cs_vSend) + { + if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) + { + printf("dropmessages DROPPING SEND MESSAGE\n"); + AbortMessage(); + return; + } + + if (ssSend.size() == 0) + return; + + // Set the size + unsigned int nSize = ssSend.size() - CMessageHeader::HEADER_SIZE; + memcpy((char*)&ssSend[CMessageHeader::MESSAGE_SIZE_OFFSET], &nSize, sizeof(nSize)); + + // Set the checksum + uint256 hash = Hash(ssSend.begin() + CMessageHeader::HEADER_SIZE, ssSend.end()); + unsigned int nChecksum = 0; + memcpy(&nChecksum, &hash, sizeof(nChecksum)); + assert(ssSend.size () >= CMessageHeader::CHECKSUM_OFFSET + sizeof(nChecksum)); + memcpy((char*)&ssSend[CMessageHeader::CHECKSUM_OFFSET], &nChecksum, sizeof(nChecksum)); + + if (fDebug) { + printf("(%d bytes)\n", nSize); + } + + std::deque::iterator it = vSendMsg.insert(vSendMsg.end(), CSerializeData()); + ssSend.GetAndClear(*it); + nSendSize += (*it).size(); + + // If write queue empty, attempt "optimistic write" + if (it == vSendMsg.begin()) + SocketSendData(this); + + LEAVE_CRITICAL_SECTION(cs_vSend); + } + + void PushVersion(); + + + void PushMessage(const char* pszCommand) + { + try + { + BeginMessage(pszCommand); + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1) + { + try + { + BeginMessage(pszCommand); + ssSend << a1; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2) + { + try + { + BeginMessage(pszCommand); + ssSend << a1 << a2; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3) + { + try + { + BeginMessage(pszCommand); + ssSend << a1 << a2 << a3; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4) + { + try + { + BeginMessage(pszCommand); + ssSend << a1 << a2 << a3 << a4; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5) + { + try + { + BeginMessage(pszCommand); + ssSend << a1 << a2 << a3 << a4 << a5; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6) + { + try + { + BeginMessage(pszCommand); + ssSend << a1 << a2 << a3 << a4 << a5 << a6; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7) + { + try + { + BeginMessage(pszCommand); + ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8) + { + try + { + BeginMessage(pszCommand); + ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8, const T9& a9) + { + try + { + BeginMessage(pszCommand); + ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8 << a9; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + void PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd); + bool IsSubscribed(unsigned int nChannel); + void Subscribe(unsigned int nChannel, unsigned int nHops=0); + void CancelSubscribe(unsigned int nChannel); + void CloseSocketDisconnect(); + void Cleanup(); + + + // Denial-of-service detection/prevention + // The idea is to detect peers that are behaving + // badly and disconnect/ban them, but do it in a + // one-coding-mistake-won't-shatter-the-entire-network + // way. + // IMPORTANT: There should be nothing I can give a + // node that it will forward on that will make that + // node's peers drop it. If there is, an attacker + // can isolate a node and/or try to split the network. + // Dropping a node for sending stuff that is invalid + // now but might be valid in a later version is also + // dangerous, because it can cause a network split + // between nodes running old code and nodes running + // new code. + static void ClearBanned(); // needed for unit testing + static bool IsBanned(CNetAddr ip); + bool Misbehaving(int howmuch); // 1 == a little, 100 == a lot + void copyStats(CNodeStats &stats); +}; + + + +class CTransaction; +void RelayTransaction(const CTransaction& tx, const uint256& hash); +void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataStream& ss); + +#endif diff --git a/src/netbase.cpp b/src/netbase.cpp new file mode 100644 index 0000000..d969516 --- /dev/null +++ b/src/netbase.cpp @@ -0,0 +1,1139 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "netbase.h" +#include "util.h" +#include "sync.h" +#include "hash.h" + +#ifndef WIN32 +#include +#endif + +#include // for to_lower() +#include // for startswith() and endswith() + +using namespace std; + +// Settings +static proxyType proxyInfo[NET_MAX]; +static proxyType nameproxyInfo; +static CCriticalSection cs_proxyInfos; +int nConnectTimeout = 5000; +bool fNameLookup = false; + +static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; + +enum Network ParseNetwork(std::string net) { + boost::to_lower(net); + if (net == "ipv4") return NET_IPV4; + if (net == "ipv6") return NET_IPV6; + if (net == "tor") return NET_TOR; + return NET_UNROUTABLE; +} + +void SplitHostPort(std::string in, int &portOut, std::string &hostOut) { + size_t colon = in.find_last_of(':'); + // if a : is found, and it either follows a [...], or no other : is in the string, treat it as port separator + bool fHaveColon = colon != in.npos; + bool fBracketed = fHaveColon && (in[0]=='[' && in[colon-1]==']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe + bool fMultiColon = fHaveColon && (in.find_last_of(':',colon-1) != in.npos); + if (fHaveColon && (colon==0 || fBracketed || !fMultiColon)) { + char *endp = NULL; + int n = strtol(in.c_str() + colon + 1, &endp, 10); + if (endp && *endp == 0 && n >= 0) { + in = in.substr(0, colon); + if (n > 0 && n < 0x10000) + portOut = n; + } + } + if (in.size()>0 && in[0] == '[' && in[in.size()-1] == ']') + hostOut = in.substr(1, in.size()-2); + else + hostOut = in; +} + +bool static LookupIntern(const char *pszName, std::vector& vIP, unsigned int nMaxSolutions, bool fAllowLookup) +{ + vIP.clear(); + + { + CNetAddr addr; + if (addr.SetSpecial(std::string(pszName))) { + vIP.push_back(addr); + return true; + } + } + + struct addrinfo aiHint; + memset(&aiHint, 0, sizeof(struct addrinfo)); + + aiHint.ai_socktype = SOCK_STREAM; + aiHint.ai_protocol = IPPROTO_TCP; +#ifdef USE_IPV6 + aiHint.ai_family = AF_UNSPEC; +#else + aiHint.ai_family = AF_INET; +#endif +#ifdef WIN32 + aiHint.ai_flags = fAllowLookup ? 0 : AI_NUMERICHOST; +#else + aiHint.ai_flags = fAllowLookup ? AI_ADDRCONFIG : AI_NUMERICHOST; +#endif + struct addrinfo *aiRes = NULL; + int nErr = getaddrinfo(pszName, NULL, &aiHint, &aiRes); + if (nErr) + return false; + + struct addrinfo *aiTrav = aiRes; + while (aiTrav != NULL && (nMaxSolutions == 0 || vIP.size() < nMaxSolutions)) + { + if (aiTrav->ai_family == AF_INET) + { + assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in)); + vIP.push_back(CNetAddr(((struct sockaddr_in*)(aiTrav->ai_addr))->sin_addr)); + } + +#ifdef USE_IPV6 + if (aiTrav->ai_family == AF_INET6) + { + assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in6)); + vIP.push_back(CNetAddr(((struct sockaddr_in6*)(aiTrav->ai_addr))->sin6_addr)); + } +#endif + + aiTrav = aiTrav->ai_next; + } + + freeaddrinfo(aiRes); + + return (vIP.size() > 0); +} + +bool LookupHost(const char *pszName, std::vector& vIP, unsigned int nMaxSolutions, bool fAllowLookup) +{ + std::string strHost(pszName); + if (strHost.empty()) + return false; + if (boost::algorithm::starts_with(strHost, "[") && boost::algorithm::ends_with(strHost, "]")) + { + strHost = strHost.substr(1, strHost.size() - 2); + } + + return LookupIntern(strHost.c_str(), vIP, nMaxSolutions, fAllowLookup); +} + +bool LookupHostNumeric(const char *pszName, std::vector& vIP, unsigned int nMaxSolutions) +{ + return LookupHost(pszName, vIP, nMaxSolutions, false); +} + +bool Lookup(const char *pszName, std::vector& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions) +{ + if (pszName[0] == 0) + return false; + int port = portDefault; + std::string hostname = ""; + SplitHostPort(std::string(pszName), port, hostname); + + std::vector vIP; + bool fRet = LookupIntern(hostname.c_str(), vIP, nMaxSolutions, fAllowLookup); + if (!fRet) + return false; + vAddr.resize(vIP.size()); + for (unsigned int i = 0; i < vIP.size(); i++) + vAddr[i] = CService(vIP[i], port); + return true; +} + +bool Lookup(const char *pszName, CService& addr, int portDefault, bool fAllowLookup) +{ + std::vector vService; + bool fRet = Lookup(pszName, vService, portDefault, fAllowLookup, 1); + if (!fRet) + return false; + addr = vService[0]; + return true; +} + +bool LookupNumeric(const char *pszName, CService& addr, int portDefault) +{ + return Lookup(pszName, addr, portDefault, false); +} + +bool static Socks4(const CService &addrDest, SOCKET& hSocket) +{ + printf("SOCKS4 connecting %s\n", addrDest.ToString().c_str()); + if (!addrDest.IsIPv4()) + { + closesocket(hSocket); + return error("Proxy destination is not IPv4"); + } + char pszSocks4IP[] = "\4\1\0\0\0\0\0\0user"; + struct sockaddr_in addr; + socklen_t len = sizeof(addr); + if (!addrDest.GetSockAddr((struct sockaddr*)&addr, &len) || addr.sin_family != AF_INET) + { + closesocket(hSocket); + return error("Cannot get proxy destination address"); + } + memcpy(pszSocks4IP + 2, &addr.sin_port, 2); + memcpy(pszSocks4IP + 4, &addr.sin_addr, 4); + char* pszSocks4 = pszSocks4IP; + int nSize = sizeof(pszSocks4IP); + + int ret = send(hSocket, pszSocks4, nSize, MSG_NOSIGNAL); + if (ret != nSize) + { + closesocket(hSocket); + return error("Error sending to proxy"); + } + char pchRet[8]; + if (recv(hSocket, pchRet, 8, 0) != 8) + { + closesocket(hSocket); + return error("Error reading proxy response"); + } + if (pchRet[1] != 0x5a) + { + closesocket(hSocket); + if (pchRet[1] != 0x5b) + printf("ERROR: Proxy returned error %d\n", pchRet[1]); + return false; + } + printf("SOCKS4 connected %s\n", addrDest.ToString().c_str()); + return true; +} + +bool static Socks5(string strDest, int port, SOCKET& hSocket) +{ + printf("SOCKS5 connecting %s\n", strDest.c_str()); + if (strDest.size() > 255) + { + closesocket(hSocket); + return error("Hostname too long"); + } + char pszSocks5Init[] = "\5\1\0"; + ssize_t nSize = sizeof(pszSocks5Init) - 1; + + ssize_t ret = send(hSocket, pszSocks5Init, nSize, MSG_NOSIGNAL); + if (ret != nSize) + { + closesocket(hSocket); + return error("Error sending to proxy"); + } + char pchRet1[2]; + if (recv(hSocket, pchRet1, 2, 0) != 2) + { + closesocket(hSocket); + return error("Error reading proxy response"); + } + if (pchRet1[0] != 0x05 || pchRet1[1] != 0x00) + { + closesocket(hSocket); + return error("Proxy failed to initialize"); + } + string strSocks5("\5\1"); + strSocks5 += '\000'; strSocks5 += '\003'; + strSocks5 += static_cast(std::min((int)strDest.size(), 255)); + strSocks5 += strDest; + strSocks5 += static_cast((port >> 8) & 0xFF); + strSocks5 += static_cast((port >> 0) & 0xFF); + ret = send(hSocket, strSocks5.c_str(), strSocks5.size(), MSG_NOSIGNAL); + if (ret != (ssize_t)strSocks5.size()) + { + closesocket(hSocket); + return error("Error sending to proxy"); + } + char pchRet2[4]; + if (recv(hSocket, pchRet2, 4, 0) != 4) + { + closesocket(hSocket); + return error("Error reading proxy response"); + } + if (pchRet2[0] != 0x05) + { + closesocket(hSocket); + return error("Proxy failed to accept request"); + } + if (pchRet2[1] != 0x00) + { + closesocket(hSocket); + switch (pchRet2[1]) + { + case 0x01: return error("Proxy error: general failure"); + case 0x02: return error("Proxy error: connection not allowed"); + case 0x03: return error("Proxy error: network unreachable"); + case 0x04: return error("Proxy error: host unreachable"); + case 0x05: return error("Proxy error: connection refused"); + case 0x06: return error("Proxy error: TTL expired"); + case 0x07: return error("Proxy error: protocol error"); + case 0x08: return error("Proxy error: address type not supported"); + default: return error("Proxy error: unknown"); + } + } + if (pchRet2[2] != 0x00) + { + closesocket(hSocket); + return error("Error: malformed proxy response"); + } + char pchRet3[256]; + switch (pchRet2[3]) + { + case 0x01: ret = recv(hSocket, pchRet3, 4, 0) != 4; break; + case 0x04: ret = recv(hSocket, pchRet3, 16, 0) != 16; break; + case 0x03: + { + ret = recv(hSocket, pchRet3, 1, 0) != 1; + if (ret) + return error("Error reading from proxy"); + int nRecv = pchRet3[0]; + ret = recv(hSocket, pchRet3, nRecv, 0) != nRecv; + break; + } + default: closesocket(hSocket); return error("Error: malformed proxy response"); + } + if (ret) + { + closesocket(hSocket); + return error("Error reading from proxy"); + } + if (recv(hSocket, pchRet3, 2, 0) != 2) + { + closesocket(hSocket); + return error("Error reading from proxy"); + } + printf("SOCKS5 connected %s\n", strDest.c_str()); + return true; +} + +bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int nTimeout) +{ + hSocketRet = INVALID_SOCKET; + +#ifdef USE_IPV6 + struct sockaddr_storage sockaddr; +#else + struct sockaddr sockaddr; +#endif + socklen_t len = sizeof(sockaddr); + if (!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len)) { + printf("Cannot connect to %s: unsupported network\n", addrConnect.ToString().c_str()); + return false; + } + + SOCKET hSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP); + if (hSocket == INVALID_SOCKET) + return false; +#ifdef SO_NOSIGPIPE + int set = 1; + setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int)); +#endif + +#ifdef WIN32 + u_long fNonblock = 1; + if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR) +#else + int fFlags = fcntl(hSocket, F_GETFL, 0); + if (fcntl(hSocket, F_SETFL, fFlags | O_NONBLOCK) == -1) +#endif + { + closesocket(hSocket); + return false; + } + + if (connect(hSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR) + { + // WSAEINVAL is here because some legacy version of winsock uses it + if (WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINVAL) + { + struct timeval timeout; + timeout.tv_sec = nTimeout / 1000; + timeout.tv_usec = (nTimeout % 1000) * 1000; + + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(hSocket, &fdset); + int nRet = select(hSocket + 1, NULL, &fdset, NULL, &timeout); + if (nRet == 0) + { + printf("connection timeout\n"); + closesocket(hSocket); + return false; + } + if (nRet == SOCKET_ERROR) + { + printf("select() for connection failed: %i\n",WSAGetLastError()); + closesocket(hSocket); + return false; + } + socklen_t nRetSize = sizeof(nRet); +#ifdef WIN32 + if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, (char*)(&nRet), &nRetSize) == SOCKET_ERROR) +#else + if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, &nRet, &nRetSize) == SOCKET_ERROR) +#endif + { + printf("getsockopt() for connection failed: %i\n",WSAGetLastError()); + closesocket(hSocket); + return false; + } + if (nRet != 0) + { + printf("connect() failed after select(): %s\n",strerror(nRet)); + closesocket(hSocket); + return false; + } + } +#ifdef WIN32 + else if (WSAGetLastError() != WSAEISCONN) +#else + else +#endif + { + printf("connect() failed: %i\n",WSAGetLastError()); + closesocket(hSocket); + return false; + } + } + + // this isn't even strictly necessary + // CNode::ConnectNode immediately turns the socket back to non-blocking + // but we'll turn it back to blocking just in case +#ifdef WIN32 + fNonblock = 0; + if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR) +#else + fFlags = fcntl(hSocket, F_GETFL, 0); + if (fcntl(hSocket, F_SETFL, fFlags & ~O_NONBLOCK) == SOCKET_ERROR) +#endif + { + closesocket(hSocket); + return false; + } + + hSocketRet = hSocket; + return true; +} + +bool SetProxy(enum Network net, CService addrProxy, int nSocksVersion) { + assert(net >= 0 && net < NET_MAX); + if (nSocksVersion != 0 && nSocksVersion != 4 && nSocksVersion != 5) + return false; + if (nSocksVersion != 0 && !addrProxy.IsValid()) + return false; + LOCK(cs_proxyInfos); + proxyInfo[net] = std::make_pair(addrProxy, nSocksVersion); + return true; +} + +bool GetProxy(enum Network net, proxyType &proxyInfoOut) { + assert(net >= 0 && net < NET_MAX); + LOCK(cs_proxyInfos); + if (!proxyInfo[net].second) + return false; + proxyInfoOut = proxyInfo[net]; + return true; +} + +bool SetNameProxy(CService addrProxy, int nSocksVersion) { + if (nSocksVersion != 0 && nSocksVersion != 5) + return false; + if (nSocksVersion != 0 && !addrProxy.IsValid()) + return false; + LOCK(cs_proxyInfos); + nameproxyInfo = std::make_pair(addrProxy, nSocksVersion); + return true; +} + +bool GetNameProxy(proxyType &nameproxyInfoOut) { + LOCK(cs_proxyInfos); + if (!nameproxyInfo.second) + return false; + nameproxyInfoOut = nameproxyInfo; + return true; +} + +bool HaveNameProxy() { + LOCK(cs_proxyInfos); + return nameproxyInfo.second != 0; +} + +bool IsProxy(const CNetAddr &addr) { + LOCK(cs_proxyInfos); + for (int i = 0; i < NET_MAX; i++) { + if (proxyInfo[i].second && (addr == (CNetAddr)proxyInfo[i].first)) + return true; + } + return false; +} + +bool ConnectSocket(const CService &addrDest, SOCKET& hSocketRet, int nTimeout) +{ + proxyType proxy; + + // no proxy needed + if (!GetProxy(addrDest.GetNetwork(), proxy)) + return ConnectSocketDirectly(addrDest, hSocketRet, nTimeout); + + SOCKET hSocket = INVALID_SOCKET; + + // first connect to proxy server + if (!ConnectSocketDirectly(proxy.first, hSocket, nTimeout)) + return false; + + // do socks negotiation + switch (proxy.second) { + case 4: + if (!Socks4(addrDest, hSocket)) + return false; + break; + case 5: + if (!Socks5(addrDest.ToStringIP(), addrDest.GetPort(), hSocket)) + return false; + break; + default: + return false; + } + + hSocketRet = hSocket; + return true; +} + +bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout) +{ + string strDest; + int port = portDefault; + SplitHostPort(string(pszDest), port, strDest); + + SOCKET hSocket = INVALID_SOCKET; + + proxyType nameproxy; + GetNameProxy(nameproxy); + + CService addrResolved(CNetAddr(strDest, fNameLookup && !nameproxy.second), port); + if (addrResolved.IsValid()) { + addr = addrResolved; + return ConnectSocket(addr, hSocketRet, nTimeout); + } + addr = CService("0.0.0.0:0"); + if (!nameproxy.second) + return false; + if (!ConnectSocketDirectly(nameproxy.first, hSocket, nTimeout)) + return false; + + switch(nameproxy.second) { + default: + case 4: return false; + case 5: + if (!Socks5(strDest, port, hSocket)) + return false; + break; + } + + hSocketRet = hSocket; + return true; +} + +void CNetAddr::Init() +{ + memset(ip, 0, sizeof(ip)); +} + +void CNetAddr::SetIP(const CNetAddr& ipIn) +{ + memcpy(ip, ipIn.ip, sizeof(ip)); +} + +static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43}; + +bool CNetAddr::SetSpecial(const std::string &strName) +{ + if (strName.size()>6 && strName.substr(strName.size() - 6, 6) == ".onion") { + std::vector vchAddr = DecodeBase32(strName.substr(0, strName.size() - 6).c_str()); + if (vchAddr.size() != 16-sizeof(pchOnionCat)) + return false; + memcpy(ip, pchOnionCat, sizeof(pchOnionCat)); + for (unsigned int i=0; i<16-sizeof(pchOnionCat); i++) + ip[i + sizeof(pchOnionCat)] = vchAddr[i]; + return true; + } + return false; +} + +CNetAddr::CNetAddr() +{ + Init(); +} + +CNetAddr::CNetAddr(const struct in_addr& ipv4Addr) +{ + memcpy(ip, pchIPv4, 12); + memcpy(ip+12, &ipv4Addr, 4); +} + +#ifdef USE_IPV6 +CNetAddr::CNetAddr(const struct in6_addr& ipv6Addr) +{ + memcpy(ip, &ipv6Addr, 16); +} +#endif + +CNetAddr::CNetAddr(const char *pszIp, bool fAllowLookup) +{ + Init(); + std::vector vIP; + if (LookupHost(pszIp, vIP, 1, fAllowLookup)) + *this = vIP[0]; +} + +CNetAddr::CNetAddr(const std::string &strIp, bool fAllowLookup) +{ + Init(); + std::vector vIP; + if (LookupHost(strIp.c_str(), vIP, 1, fAllowLookup)) + *this = vIP[0]; +} + +unsigned int CNetAddr::GetByte(int n) const +{ + return ip[15-n]; +} + +bool CNetAddr::IsIPv4() const +{ + return (memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0); +} + +bool CNetAddr::IsIPv6() const +{ + return (!IsIPv4() && !IsTor()); +} + +bool CNetAddr::IsRFC1918() const +{ + return IsIPv4() && ( + GetByte(3) == 10 || + (GetByte(3) == 192 && GetByte(2) == 168) || + (GetByte(3) == 172 && (GetByte(2) >= 16 && GetByte(2) <= 31))); +} + +bool CNetAddr::IsRFC3927() const +{ + return IsIPv4() && (GetByte(3) == 169 && GetByte(2) == 254); +} + +bool CNetAddr::IsRFC3849() const +{ + return GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x0D && GetByte(12) == 0xB8; +} + +bool CNetAddr::IsRFC3964() const +{ + return (GetByte(15) == 0x20 && GetByte(14) == 0x02); +} + +bool CNetAddr::IsRFC6052() const +{ + static const unsigned char pchRFC6052[] = {0,0x64,0xFF,0x9B,0,0,0,0,0,0,0,0}; + return (memcmp(ip, pchRFC6052, sizeof(pchRFC6052)) == 0); +} + +bool CNetAddr::IsRFC4380() const +{ + return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0 && GetByte(12) == 0); +} + +bool CNetAddr::IsRFC4862() const +{ + static const unsigned char pchRFC4862[] = {0xFE,0x80,0,0,0,0,0,0}; + return (memcmp(ip, pchRFC4862, sizeof(pchRFC4862)) == 0); +} + +bool CNetAddr::IsRFC4193() const +{ + return ((GetByte(15) & 0xFE) == 0xFC); +} + +bool CNetAddr::IsRFC6145() const +{ + static const unsigned char pchRFC6145[] = {0,0,0,0,0,0,0,0,0xFF,0xFF,0,0}; + return (memcmp(ip, pchRFC6145, sizeof(pchRFC6145)) == 0); +} + +bool CNetAddr::IsRFC4843() const +{ + return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x10); +} + +bool CNetAddr::IsTor() const +{ + return (memcmp(ip, pchOnionCat, sizeof(pchOnionCat)) == 0); +} + +bool CNetAddr::IsLocal() const +{ + // IPv4 loopback + if (IsIPv4() && (GetByte(3) == 127 || GetByte(3) == 0)) + return true; + + // IPv6 loopback (::1/128) + static const unsigned char pchLocal[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; + if (memcmp(ip, pchLocal, 16) == 0) + return true; + + return false; +} + +bool CNetAddr::IsMulticast() const +{ + return (IsIPv4() && (GetByte(3) & 0xF0) == 0xE0) + || (GetByte(15) == 0xFF); +} + +bool CNetAddr::IsValid() const +{ + // Cleanup 3-byte shifted addresses caused by garbage in size field + // of addr messages from versions before 0.2.9 checksum. + // Two consecutive addr messages look like this: + // header20 vectorlen3 addr26 addr26 addr26 header20 vectorlen3 addr26 addr26 addr26... + // so if the first length field is garbled, it reads the second batch + // of addr misaligned by 3 bytes. + if (memcmp(ip, pchIPv4+3, sizeof(pchIPv4)-3) == 0) + return false; + + // unspecified IPv6 address (::/128) + unsigned char ipNone[16] = {}; + if (memcmp(ip, ipNone, 16) == 0) + return false; + + // documentation IPv6 address + if (IsRFC3849()) + return false; + + if (IsIPv4()) + { + // INADDR_NONE + uint32_t ipNone = INADDR_NONE; + if (memcmp(ip+12, &ipNone, 4) == 0) + return false; + + // 0 + ipNone = 0; + if (memcmp(ip+12, &ipNone, 4) == 0) + return false; + } + + return true; +} + +bool CNetAddr::IsRoutable() const +{ + return IsValid() && !(IsRFC1918() || IsRFC3927() || IsRFC4862() || (IsRFC4193() && !IsTor()) || IsRFC4843() || IsLocal()); +} + +enum Network CNetAddr::GetNetwork() const +{ + if (!IsRoutable()) + return NET_UNROUTABLE; + + if (IsIPv4()) + return NET_IPV4; + + if (IsTor()) + return NET_TOR; + + return NET_IPV6; +} + +std::string CNetAddr::ToStringIP() const +{ + if (IsTor()) + return EncodeBase32(&ip[6], 10) + ".onion"; + CService serv(*this, 0); +#ifdef USE_IPV6 + struct sockaddr_storage sockaddr; +#else + struct sockaddr sockaddr; +#endif + socklen_t socklen = sizeof(sockaddr); + if (serv.GetSockAddr((struct sockaddr*)&sockaddr, &socklen)) { + char name[1025] = ""; + if (!getnameinfo((const struct sockaddr*)&sockaddr, socklen, name, sizeof(name), NULL, 0, NI_NUMERICHOST)) + return std::string(name); + } + if (IsIPv4()) + return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0)); + else + return strprintf("%x:%x:%x:%x:%x:%x:%x:%x", + GetByte(15) << 8 | GetByte(14), GetByte(13) << 8 | GetByte(12), + GetByte(11) << 8 | GetByte(10), GetByte(9) << 8 | GetByte(8), + GetByte(7) << 8 | GetByte(6), GetByte(5) << 8 | GetByte(4), + GetByte(3) << 8 | GetByte(2), GetByte(1) << 8 | GetByte(0)); +} + +std::string CNetAddr::ToString() const +{ + return ToStringIP(); +} + +bool operator==(const CNetAddr& a, const CNetAddr& b) +{ + return (memcmp(a.ip, b.ip, 16) == 0); +} + +bool operator!=(const CNetAddr& a, const CNetAddr& b) +{ + return (memcmp(a.ip, b.ip, 16) != 0); +} + +bool operator<(const CNetAddr& a, const CNetAddr& b) +{ + return (memcmp(a.ip, b.ip, 16) < 0); +} + +bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const +{ + if (!IsIPv4()) + return false; + memcpy(pipv4Addr, ip+12, 4); + return true; +} + +#ifdef USE_IPV6 +bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const +{ + memcpy(pipv6Addr, ip, 16); + return true; +} +#endif + +// get canonical identifier of an address' group +// no two connections will be attempted to addresses with the same group +std::vector CNetAddr::GetGroup() const +{ + std::vector vchRet; + int nClass = NET_IPV6; + int nStartByte = 0; + int nBits = 16; + + // all local addresses belong to the same group + if (IsLocal()) + { + nClass = 255; + nBits = 0; + } + + // all unroutable addresses belong to the same group + if (!IsRoutable()) + { + nClass = NET_UNROUTABLE; + nBits = 0; + } + // for IPv4 addresses, '1' + the 16 higher-order bits of the IP + // includes mapped IPv4, SIIT translated IPv4, and the well-known prefix + else if (IsIPv4() || IsRFC6145() || IsRFC6052()) + { + nClass = NET_IPV4; + nStartByte = 12; + } + // for 6to4 tunnelled addresses, use the encapsulated IPv4 address + else if (IsRFC3964()) + { + nClass = NET_IPV4; + nStartByte = 2; + } + // for Teredo-tunnelled IPv6 addresses, use the encapsulated IPv4 address + else if (IsRFC4380()) + { + vchRet.push_back(NET_IPV4); + vchRet.push_back(GetByte(3) ^ 0xFF); + vchRet.push_back(GetByte(2) ^ 0xFF); + return vchRet; + } + else if (IsTor()) + { + nClass = NET_TOR; + nStartByte = 6; + nBits = 4; + } + // for he.net, use /36 groups + else if (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x04 && GetByte(12) == 0x70) + nBits = 36; + // for the rest of the IPv6 network, use /32 groups + else + nBits = 32; + + vchRet.push_back(nClass); + while (nBits >= 8) + { + vchRet.push_back(GetByte(15 - nStartByte)); + nStartByte++; + nBits -= 8; + } + if (nBits > 0) + vchRet.push_back(GetByte(15 - nStartByte) | ((1 << nBits) - 1)); + + return vchRet; +} + +uint64 CNetAddr::GetHash() const +{ + uint256 hash = Hash(&ip[0], &ip[16]); + uint64 nRet; + memcpy(&nRet, &hash, sizeof(nRet)); + return nRet; +} + +void CNetAddr::print() const +{ + printf("CNetAddr(%s)\n", ToString().c_str()); +} + +// private extensions to enum Network, only returned by GetExtNetwork, +// and only used in GetReachabilityFrom +static const int NET_UNKNOWN = NET_MAX + 0; +static const int NET_TEREDO = NET_MAX + 1; +int static GetExtNetwork(const CNetAddr *addr) +{ + if (addr == NULL) + return NET_UNKNOWN; + if (addr->IsRFC4380()) + return NET_TEREDO; + return addr->GetNetwork(); +} + +/** Calculates a metric for how reachable (*this) is from a given partner */ +int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const +{ + enum Reachability { + REACH_UNREACHABLE, + REACH_DEFAULT, + REACH_TEREDO, + REACH_IPV6_WEAK, + REACH_IPV4, + REACH_IPV6_STRONG, + REACH_PRIVATE + }; + + if (!IsRoutable()) + return REACH_UNREACHABLE; + + int ourNet = GetExtNetwork(this); + int theirNet = GetExtNetwork(paddrPartner); + bool fTunnel = IsRFC3964() || IsRFC6052() || IsRFC6145(); + + switch(theirNet) { + case NET_IPV4: + switch(ourNet) { + default: return REACH_DEFAULT; + case NET_IPV4: return REACH_IPV4; + } + case NET_IPV6: + switch(ourNet) { + default: return REACH_DEFAULT; + case NET_TEREDO: return REACH_TEREDO; + case NET_IPV4: return REACH_IPV4; + case NET_IPV6: return fTunnel ? REACH_IPV6_WEAK : REACH_IPV6_STRONG; // only prefer giving our IPv6 address if it's not tunnelled + } + case NET_TOR: + switch(ourNet) { + default: return REACH_DEFAULT; + case NET_IPV4: return REACH_IPV4; // Tor users can connect to IPv4 as well + case NET_TOR: return REACH_PRIVATE; + } + case NET_TEREDO: + switch(ourNet) { + default: return REACH_DEFAULT; + case NET_TEREDO: return REACH_TEREDO; + case NET_IPV6: return REACH_IPV6_WEAK; + case NET_IPV4: return REACH_IPV4; + } + case NET_UNKNOWN: + case NET_UNROUTABLE: + default: + switch(ourNet) { + default: return REACH_DEFAULT; + case NET_TEREDO: return REACH_TEREDO; + case NET_IPV6: return REACH_IPV6_WEAK; + case NET_IPV4: return REACH_IPV4; + case NET_TOR: return REACH_PRIVATE; // either from Tor, or don't care about our address + } + } +} + +void CService::Init() +{ + port = 0; +} + +CService::CService() +{ + Init(); +} + +CService::CService(const CNetAddr& cip, unsigned short portIn) : CNetAddr(cip), port(portIn) +{ +} + +CService::CService(const struct in_addr& ipv4Addr, unsigned short portIn) : CNetAddr(ipv4Addr), port(portIn) +{ +} + +#ifdef USE_IPV6 +CService::CService(const struct in6_addr& ipv6Addr, unsigned short portIn) : CNetAddr(ipv6Addr), port(portIn) +{ +} +#endif + +CService::CService(const struct sockaddr_in& addr) : CNetAddr(addr.sin_addr), port(ntohs(addr.sin_port)) +{ + assert(addr.sin_family == AF_INET); +} + +#ifdef USE_IPV6 +CService::CService(const struct sockaddr_in6 &addr) : CNetAddr(addr.sin6_addr), port(ntohs(addr.sin6_port)) +{ + assert(addr.sin6_family == AF_INET6); +} +#endif + +bool CService::SetSockAddr(const struct sockaddr *paddr) +{ + switch (paddr->sa_family) { + case AF_INET: + *this = CService(*(const struct sockaddr_in*)paddr); + return true; +#ifdef USE_IPV6 + case AF_INET6: + *this = CService(*(const struct sockaddr_in6*)paddr); + return true; +#endif + default: + return false; + } +} + +CService::CService(const char *pszIpPort, bool fAllowLookup) +{ + Init(); + CService ip; + if (Lookup(pszIpPort, ip, 0, fAllowLookup)) + *this = ip; +} + +CService::CService(const char *pszIpPort, int portDefault, bool fAllowLookup) +{ + Init(); + CService ip; + if (Lookup(pszIpPort, ip, portDefault, fAllowLookup)) + *this = ip; +} + +CService::CService(const std::string &strIpPort, bool fAllowLookup) +{ + Init(); + CService ip; + if (Lookup(strIpPort.c_str(), ip, 0, fAllowLookup)) + *this = ip; +} + +CService::CService(const std::string &strIpPort, int portDefault, bool fAllowLookup) +{ + Init(); + CService ip; + if (Lookup(strIpPort.c_str(), ip, portDefault, fAllowLookup)) + *this = ip; +} + +unsigned short CService::GetPort() const +{ + return port; +} + +bool operator==(const CService& a, const CService& b) +{ + return (CNetAddr)a == (CNetAddr)b && a.port == b.port; +} + +bool operator!=(const CService& a, const CService& b) +{ + return (CNetAddr)a != (CNetAddr)b || a.port != b.port; +} + +bool operator<(const CService& a, const CService& b) +{ + return (CNetAddr)a < (CNetAddr)b || ((CNetAddr)a == (CNetAddr)b && a.port < b.port); +} + +bool CService::GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const +{ + if (IsIPv4()) { + if (*addrlen < (socklen_t)sizeof(struct sockaddr_in)) + return false; + *addrlen = sizeof(struct sockaddr_in); + struct sockaddr_in *paddrin = (struct sockaddr_in*)paddr; + memset(paddrin, 0, *addrlen); + if (!GetInAddr(&paddrin->sin_addr)) + return false; + paddrin->sin_family = AF_INET; + paddrin->sin_port = htons(port); + return true; + } +#ifdef USE_IPV6 + if (IsIPv6()) { + if (*addrlen < (socklen_t)sizeof(struct sockaddr_in6)) + return false; + *addrlen = sizeof(struct sockaddr_in6); + struct sockaddr_in6 *paddrin6 = (struct sockaddr_in6*)paddr; + memset(paddrin6, 0, *addrlen); + if (!GetIn6Addr(&paddrin6->sin6_addr)) + return false; + paddrin6->sin6_family = AF_INET6; + paddrin6->sin6_port = htons(port); + return true; + } +#endif + return false; +} + +std::vector CService::GetKey() const +{ + std::vector vKey; + vKey.resize(18); + memcpy(&vKey[0], ip, 16); + vKey[16] = port / 0x100; + vKey[17] = port & 0x0FF; + return vKey; +} + +std::string CService::ToStringPort() const +{ + return strprintf("%u", port); +} + +std::string CService::ToStringIPPort() const +{ + if (IsIPv4() || IsTor()) { + return ToStringIP() + ":" + ToStringPort(); + } else { + return "[" + ToStringIP() + "]:" + ToStringPort(); + } +} + +std::string CService::ToString() const +{ + return ToStringIPPort(); +} + +void CService::print() const +{ + printf("CService(%s)\n", ToString().c_str()); +} + +void CService::SetPort(unsigned short portIn) +{ + port = portIn; +} diff --git a/src/netbase.h b/src/netbase.h new file mode 100644 index 0000000..e4ec4ef --- /dev/null +++ b/src/netbase.h @@ -0,0 +1,151 @@ +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_NETBASE_H +#define BITCOIN_NETBASE_H + +#include +#include + +#include "serialize.h" +#include "compat.h" + +extern int nConnectTimeout; + +#ifdef WIN32 +// In MSVC, this is defined as a macro, undefine it to prevent a compile and link error +#undef SetPort +#endif + +enum Network +{ + NET_UNROUTABLE, + NET_IPV4, + NET_IPV6, + NET_TOR, + + NET_MAX, +}; + +extern int nConnectTimeout; +extern bool fNameLookup; + +/** IP address (IPv6, or IPv4 using mapped IPv6 range (::FFFF:0:0/96)) */ +class CNetAddr +{ + protected: + unsigned char ip[16]; // in network byte order + + public: + CNetAddr(); + CNetAddr(const struct in_addr& ipv4Addr); + explicit CNetAddr(const char *pszIp, bool fAllowLookup = false); + explicit CNetAddr(const std::string &strIp, bool fAllowLookup = false); + void Init(); + void SetIP(const CNetAddr& ip); + bool SetSpecial(const std::string &strName); // for Tor addresses + bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0) + bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor) + bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12) + bool IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32) + bool IsRFC3927() const; // IPv4 autoconfig (169.254.0.0/16) + bool IsRFC3964() const; // IPv6 6to4 tunnelling (2002::/16) + bool IsRFC4193() const; // IPv6 unique local (FC00::/15) + bool IsRFC4380() const; // IPv6 Teredo tunnelling (2001::/32) + bool IsRFC4843() const; // IPv6 ORCHID (2001:10::/28) + bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64) + bool IsRFC6052() const; // IPv6 well-known prefix (64:FF9B::/96) + bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96) + bool IsTor() const; + bool IsLocal() const; + bool IsRoutable() const; + bool IsValid() const; + bool IsMulticast() const; + enum Network GetNetwork() const; + std::string ToString() const; + std::string ToStringIP() const; + unsigned int GetByte(int n) const; + uint64 GetHash() const; + bool GetInAddr(struct in_addr* pipv4Addr) const; + std::vector GetGroup() const; + int GetReachabilityFrom(const CNetAddr *paddrPartner = NULL) const; + void print() const; + +#ifdef USE_IPV6 + CNetAddr(const struct in6_addr& pipv6Addr); + bool GetIn6Addr(struct in6_addr* pipv6Addr) const; +#endif + + friend bool operator==(const CNetAddr& a, const CNetAddr& b); + friend bool operator!=(const CNetAddr& a, const CNetAddr& b); + friend bool operator<(const CNetAddr& a, const CNetAddr& b); + + IMPLEMENT_SERIALIZE + ( + READWRITE(FLATDATA(ip)); + ) +}; + +/** A combination of a network address (CNetAddr) and a (TCP) port */ +class CService : public CNetAddr +{ + protected: + unsigned short port; // host order + + public: + CService(); + CService(const CNetAddr& ip, unsigned short port); + CService(const struct in_addr& ipv4Addr, unsigned short port); + CService(const struct sockaddr_in& addr); + explicit CService(const char *pszIpPort, int portDefault, bool fAllowLookup = false); + explicit CService(const char *pszIpPort, bool fAllowLookup = false); + explicit CService(const std::string& strIpPort, int portDefault, bool fAllowLookup = false); + explicit CService(const std::string& strIpPort, bool fAllowLookup = false); + void Init(); + void SetPort(unsigned short portIn); + unsigned short GetPort() const; + bool GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const; + bool SetSockAddr(const struct sockaddr* paddr); + friend bool operator==(const CService& a, const CService& b); + friend bool operator!=(const CService& a, const CService& b); + friend bool operator<(const CService& a, const CService& b); + std::vector GetKey() const; + std::string ToString() const; + std::string ToStringPort() const; + std::string ToStringIPPort() const; + void print() const; + +#ifdef USE_IPV6 + CService(const struct in6_addr& ipv6Addr, unsigned short port); + CService(const struct sockaddr_in6& addr); +#endif + + IMPLEMENT_SERIALIZE + ( + CService* pthis = const_cast(this); + READWRITE(FLATDATA(ip)); + unsigned short portN = htons(port); + READWRITE(portN); + if (fRead) + pthis->port = ntohs(portN); + ) +}; + +typedef std::pair proxyType; + +enum Network ParseNetwork(std::string net); +void SplitHostPort(std::string in, int &portOut, std::string &hostOut); +bool SetProxy(enum Network net, CService addrProxy, int nSocksVersion = 5); +bool GetProxy(enum Network net, proxyType &proxyInfoOut); +bool IsProxy(const CNetAddr &addr); +bool SetNameProxy(CService addrProxy, int nSocksVersion = 5); +bool HaveNameProxy(); +bool LookupHost(const char *pszName, std::vector& vIP, unsigned int nMaxSolutions = 0, bool fAllowLookup = true); +bool LookupHostNumeric(const char *pszName, std::vector& vIP, unsigned int nMaxSolutions = 0); +bool Lookup(const char *pszName, CService& addr, int portDefault = 0, bool fAllowLookup = true); +bool Lookup(const char *pszName, std::vector& vAddr, int portDefault = 0, bool fAllowLookup = true, unsigned int nMaxSolutions = 0); +bool LookupNumeric(const char *pszName, CService& addr, int portDefault = 0); +bool ConnectSocket(const CService &addr, SOCKET& hSocketRet, int nTimeout = nConnectTimeout); +bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault = 0, int nTimeout = nConnectTimeout); + +#endif diff --git a/src/noui.cpp b/src/noui.cpp new file mode 100644 index 0000000..c0e00c4 --- /dev/null +++ b/src/noui.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "ui_interface.h" +#include "init.h" +#include "bitcoinrpc.h" + +#include + +static bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) +{ + std::string strCaption; + // Check for usage of predefined caption + switch (style) { + case CClientUIInterface::MSG_ERROR: + strCaption += _("Error"); + break; + case CClientUIInterface::MSG_WARNING: + strCaption += _("Warning"); + break; + case CClientUIInterface::MSG_INFORMATION: + strCaption += _("Information"); + break; + default: + strCaption += caption; // Use supplied caption (can be empty) + } + + printf("%s: %s\n", strCaption.c_str(), message.c_str()); + fprintf(stderr, "%s: %s\n", strCaption.c_str(), message.c_str()); + return false; +} + +static bool noui_ThreadSafeAskFee(int64 /*nFeeRequired*/) +{ + return true; +} + +static void noui_InitMessage(const std::string &message) +{ + printf("init message: %s\n", message.c_str()); +} + +void noui_connect() +{ + // Connect bitcoind signal handlers + uiInterface.ThreadSafeMessageBox.connect(noui_ThreadSafeMessageBox); + uiInterface.ThreadSafeAskFee.connect(noui_ThreadSafeAskFee); + uiInterface.InitMessage.connect(noui_InitMessage); +} diff --git a/src/obj-test/.gitignore b/src/obj-test/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/src/obj-test/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/src/obj/.gitignore b/src/obj/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/src/obj/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/src/protocol.cpp b/src/protocol.cpp new file mode 100644 index 0000000..88bbe49 --- /dev/null +++ b/src/protocol.cpp @@ -0,0 +1,152 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "protocol.h" +#include "util.h" +#include "netbase.h" +#include "main.h" + +#ifndef WIN32 +# include +#endif + +static const char* ppszTypeName[] = +{ + "ERROR", + "tx", + "block", + "filtered block" +}; + +CMessageHeader::CMessageHeader() +{ + memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); + memset(pchCommand, 0, sizeof(pchCommand)); + pchCommand[1] = 1; + nMessageSize = -1; + nChecksum = 0; +} + +CMessageHeader::CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn) +{ + memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); + strncpy(pchCommand, pszCommand, COMMAND_SIZE); + nMessageSize = nMessageSizeIn; + nChecksum = 0; +} + +std::string CMessageHeader::GetCommand() const +{ + if (pchCommand[COMMAND_SIZE-1] == 0) + return std::string(pchCommand, pchCommand + strlen(pchCommand)); + else + return std::string(pchCommand, pchCommand + COMMAND_SIZE); +} + +bool CMessageHeader::IsValid() const +{ + // Check start string + if (memcmp(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)) != 0) + return false; + + // Check the command string for errors + for (const char* p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; p1++) + { + if (*p1 == 0) + { + // Must be all zeros after the first zero + for (; p1 < pchCommand + COMMAND_SIZE; p1++) + if (*p1 != 0) + return false; + } + else if (*p1 < ' ' || *p1 > 0x7E) + return false; + } + + // Message size + if (nMessageSize > MAX_SIZE) + { + printf("CMessageHeader::IsValid() : (%s, %u bytes) nMessageSize > MAX_SIZE\n", GetCommand().c_str(), nMessageSize); + return false; + } + + return true; +} + + + +CAddress::CAddress() : CService() +{ + Init(); +} + +CAddress::CAddress(CService ipIn, uint64 nServicesIn) : CService(ipIn) +{ + Init(); + nServices = nServicesIn; +} + +void CAddress::Init() +{ + nServices = NODE_NETWORK; + nTime = 100000000; + nLastTry = 0; +} + +CInv::CInv() +{ + type = 0; + hash = 0; +} + +CInv::CInv(int typeIn, const uint256& hashIn) +{ + type = typeIn; + hash = hashIn; +} + +CInv::CInv(const std::string& strType, const uint256& hashIn) +{ + unsigned int i; + for (i = 1; i < ARRAYLEN(ppszTypeName); i++) + { + if (strType == ppszTypeName[i]) + { + type = i; + break; + } + } + if (i == ARRAYLEN(ppszTypeName)) + throw std::out_of_range(strprintf("CInv::CInv(string, uint256) : unknown type '%s'", strType.c_str())); + hash = hashIn; +} + +bool operator<(const CInv& a, const CInv& b) +{ + return (a.type < b.type || (a.type == b.type && a.hash < b.hash)); +} + +bool CInv::IsKnownType() const +{ + return (type >= 1 && type < (int)ARRAYLEN(ppszTypeName)); +} + +const char* CInv::GetCommand() const +{ + if (!IsKnownType()) + throw std::out_of_range(strprintf("CInv::GetCommand() : type=%d unknown type", type)); + return ppszTypeName[type]; +} + +std::string CInv::ToString() const +{ + return strprintf("%s %s", GetCommand(), hash.ToString().c_str()); +} + +void CInv::print() const +{ + printf("CInv(%s)\n", ToString().c_str()); +} + diff --git a/src/protocol.h b/src/protocol.h new file mode 100644 index 0000000..9110193 --- /dev/null +++ b/src/protocol.h @@ -0,0 +1,148 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef __cplusplus +# error This header can only be compiled as C++. +#endif + +#ifndef __INCLUDED_PROTOCOL_H__ +#define __INCLUDED_PROTOCOL_H__ + +#include "serialize.h" +#include "netbase.h" +#include +#include "uint256.h" + +extern bool fTestNet; +static inline unsigned short GetDefaultPort(const bool testnet = fTestNet) +{ + return testnet ? 19157 : 9157; +} + + +extern unsigned char pchMessageStart[4]; + +/** Message header. + * (4) message start. + * (12) command. + * (4) size. + * (4) checksum. + */ +class CMessageHeader +{ + public: + CMessageHeader(); + CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn); + + std::string GetCommand() const; + bool IsValid() const; + + IMPLEMENT_SERIALIZE + ( + READWRITE(FLATDATA(pchMessageStart)); + READWRITE(FLATDATA(pchCommand)); + READWRITE(nMessageSize); + READWRITE(nChecksum); + ) + + // TODO: make private (improves encapsulation) + public: + enum { + MESSAGE_START_SIZE=sizeof(::pchMessageStart), + COMMAND_SIZE=12, + MESSAGE_SIZE_SIZE=sizeof(int), + CHECKSUM_SIZE=sizeof(int), + + MESSAGE_SIZE_OFFSET=MESSAGE_START_SIZE+COMMAND_SIZE, + CHECKSUM_OFFSET=MESSAGE_SIZE_OFFSET+MESSAGE_SIZE_SIZE, + HEADER_SIZE=MESSAGE_START_SIZE+COMMAND_SIZE+MESSAGE_SIZE_SIZE+CHECKSUM_SIZE + }; + char pchMessageStart[MESSAGE_START_SIZE]; + char pchCommand[COMMAND_SIZE]; + unsigned int nMessageSize; + unsigned int nChecksum; +}; + +/** nServices flags */ +enum +{ + NODE_NETWORK = (1 << 0), + NODE_BLOOM = (1 << 1), +}; + +/** A CService with information about it as peer */ +class CAddress : public CService +{ + public: + CAddress(); + explicit CAddress(CService ipIn, uint64 nServicesIn=NODE_NETWORK); + + void Init(); + + IMPLEMENT_SERIALIZE + ( + CAddress* pthis = const_cast(this); + CService* pip = (CService*)pthis; + if (fRead) + pthis->Init(); + if (nType & SER_DISK) + READWRITE(nVersion); + if ((nType & SER_DISK) || + (nVersion >= CADDR_TIME_VERSION && !(nType & SER_GETHASH))) + READWRITE(nTime); + READWRITE(nServices); + READWRITE(*pip); + ) + + void print() const; + + // TODO: make private (improves encapsulation) + public: + uint64 nServices; + + // disk and network only + unsigned int nTime; + + // memory only + int64 nLastTry; +}; + +/** inv message data */ +class CInv +{ + public: + CInv(); + CInv(int typeIn, const uint256& hashIn); + CInv(const std::string& strType, const uint256& hashIn); + + IMPLEMENT_SERIALIZE + ( + READWRITE(type); + READWRITE(hash); + ) + + friend bool operator<(const CInv& a, const CInv& b); + + bool IsKnownType() const; + const char* GetCommand() const; + std::string ToString() const; + void print() const; + + // TODO: make private (improves encapsulation) + public: + int type; + uint256 hash; +}; + +enum +{ + MSG_TX = 1, + MSG_BLOCK, + // Nodes may always request a MSG_FILTERED_BLOCK in a getdata, however, + // MSG_FILTERED_BLOCK should not appear in any invs except as a part of getdata. + MSG_FILTERED_BLOCK, +}; + +#endif // __INCLUDED_PROTOCOL_H__ diff --git a/src/qt/aboutdialog.cpp b/src/qt/aboutdialog.cpp new file mode 100644 index 0000000..6097261 --- /dev/null +++ b/src/qt/aboutdialog.cpp @@ -0,0 +1,37 @@ +#include "aboutdialog.h" +#include "ui_aboutdialog.h" + +#include "clientmodel.h" +#include "clientversion.h" + +// Copyright year (2009-this) +// Todo: update this when changing our copyright comments in the source +const int ABOUTDIALOG_COPYRIGHT_YEAR = 2013; + +AboutDialog::AboutDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::AboutDialog) +{ + ui->setupUi(this); + + // Set current copyright year + ui->copyrightLabel->setText(tr("Copyright") + QString(" © 2009-%1 ").arg(COPYRIGHT_YEAR) + tr("The Bitcoin developers") + QString("
") + tr("Copyright") + QString(" © ") + tr("2011-%1 The Litecoin developers").arg(ABOUTDIALOG_COPYRIGHT_YEAR) + QString("
") + tr("Copyright") + QString(" © ") + tr("2014-YYYY The CryptoLottoToken developers")); +} + +void AboutDialog::setModel(ClientModel *model) +{ + if(model) + { + ui->versionLabel->setText(model->formatFullVersion()); + } +} + +AboutDialog::~AboutDialog() +{ + delete ui; +} + +void AboutDialog::on_buttonBox_accepted() +{ + close(); +} diff --git a/src/qt/aboutdialog.h b/src/qt/aboutdialog.h new file mode 100644 index 0000000..33b1437 --- /dev/null +++ b/src/qt/aboutdialog.h @@ -0,0 +1,29 @@ +#ifndef ABOUTDIALOG_H +#define ABOUTDIALOG_H + +#include + +namespace Ui { + class AboutDialog; +} +class ClientModel; + +/** "About" dialog box */ +class AboutDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AboutDialog(QWidget *parent = 0); + ~AboutDialog(); + + void setModel(ClientModel *model); + +private: + Ui::AboutDialog *ui; + +private slots: + void on_buttonBox_accepted(); +}; + +#endif // ABOUTDIALOG_H diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp new file mode 100644 index 0000000..476217b --- /dev/null +++ b/src/qt/addressbookpage.cpp @@ -0,0 +1,395 @@ +#include "addressbookpage.h" +#include "ui_addressbookpage.h" + +#include "addresstablemodel.h" +#include "optionsmodel.h" +#include "bitcoingui.h" +#include "editaddressdialog.h" +#include "csvmodelwriter.h" +#include "guiutil.h" + +#ifdef USE_QRCODE +#include "qrcodedialog.h" +#endif + +#include +#include +#include +#include + +AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) : + QDialog(parent), + ui(new Ui::AddressBookPage), + model(0), + optionsModel(0), + mode(mode), + tab(tab) +{ + ui->setupUi(this); + +#ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac + ui->newAddress->setIcon(QIcon()); + ui->copyAddress->setIcon(QIcon()); + ui->deleteAddress->setIcon(QIcon()); + ui->verifyMessage->setIcon(QIcon()); + ui->signMessage->setIcon(QIcon()); + ui->exportButton->setIcon(QIcon()); +#endif + +#ifndef USE_QRCODE + ui->showQRCode->setVisible(false); +#endif + + switch(mode) + { + case ForSending: + connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(accept())); + ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); + ui->tableView->setFocus(); + ui->exportButton->hide(); + break; + case ForEditing: + ui->buttonBox->setVisible(false); + break; + } + switch(tab) + { + case SendingTab: + ui->labelExplanation->setText(tr("These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins.")); + ui->deleteAddress->setVisible(true); + ui->signMessage->setVisible(false); + break; + case ReceivingTab: + ui->labelExplanation->setText(tr("These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you.")); + ui->deleteAddress->setVisible(false); + ui->signMessage->setVisible(true); + break; + } + + // Context menu actions + QAction *copyAddressAction = new QAction(ui->copyAddress->text(), this); + QAction *copyLabelAction = new QAction(tr("Copy &Label"), this); + QAction *editAction = new QAction(tr("&Edit"), this); + QAction *sendCoinsAction = new QAction(tr("Send &Coins"), this); + QAction *showQRCodeAction = new QAction(ui->showQRCode->text(), this); + QAction *signMessageAction = new QAction(ui->signMessage->text(), this); + QAction *verifyMessageAction = new QAction(ui->verifyMessage->text(), this); + deleteAction = new QAction(ui->deleteAddress->text(), this); + + // Build context menu + contextMenu = new QMenu(); + contextMenu->addAction(copyAddressAction); + contextMenu->addAction(copyLabelAction); + contextMenu->addAction(editAction); + if(tab == SendingTab) + contextMenu->addAction(deleteAction); + contextMenu->addSeparator(); + if(tab == SendingTab) + contextMenu->addAction(sendCoinsAction); +#ifdef USE_QRCODE + contextMenu->addAction(showQRCodeAction); +#endif + if(tab == ReceivingTab) + contextMenu->addAction(signMessageAction); + else if(tab == SendingTab) + contextMenu->addAction(verifyMessageAction); + + // Connect signals for context menu actions + connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(on_copyAddress_clicked())); + connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(onCopyLabelAction())); + connect(editAction, SIGNAL(triggered()), this, SLOT(onEditAction())); + connect(deleteAction, SIGNAL(triggered()), this, SLOT(on_deleteAddress_clicked())); + connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(onSendCoinsAction())); + connect(showQRCodeAction, SIGNAL(triggered()), this, SLOT(on_showQRCode_clicked())); + connect(signMessageAction, SIGNAL(triggered()), this, SLOT(on_signMessage_clicked())); + connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(on_verifyMessage_clicked())); + + connect(ui->tableView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint))); + + // Pass through accept action from button box + connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept())); +} + +AddressBookPage::~AddressBookPage() +{ + delete ui; +} + +void AddressBookPage::setModel(AddressTableModel *model) +{ + this->model = model; + if(!model) + return; + + proxyModel = new QSortFilterProxyModel(this); + proxyModel->setSourceModel(model); + proxyModel->setDynamicSortFilter(true); + proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); + proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + switch(tab) + { + case ReceivingTab: + // Receive filter + proxyModel->setFilterRole(AddressTableModel::TypeRole); + proxyModel->setFilterFixedString(AddressTableModel::Receive); + break; + case SendingTab: + // Send filter + proxyModel->setFilterRole(AddressTableModel::TypeRole); + proxyModel->setFilterFixedString(AddressTableModel::Send); + break; + } + ui->tableView->setModel(proxyModel); + ui->tableView->sortByColumn(0, Qt::AscendingOrder); + + // Set column widths +#if QT_VERSION < 0x050000 + ui->tableView->horizontalHeader()->setResizeMode(AddressTableModel::Label, QHeaderView::Stretch); + ui->tableView->horizontalHeader()->setResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents); +#else + ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Label, QHeaderView::Stretch); + ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents); +#endif + + connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(selectionChanged())); + + // Select row for newly created address + connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(selectNewAddress(QModelIndex,int,int))); + + selectionChanged(); +} + +void AddressBookPage::setOptionsModel(OptionsModel *optionsModel) +{ + this->optionsModel = optionsModel; +} + +void AddressBookPage::on_copyAddress_clicked() +{ + GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Address); +} + +void AddressBookPage::onCopyLabelAction() +{ + GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Label); +} + +void AddressBookPage::onEditAction() +{ + if(!ui->tableView->selectionModel()) + return; + QModelIndexList indexes = ui->tableView->selectionModel()->selectedRows(); + if(indexes.isEmpty()) + return; + + EditAddressDialog dlg( + tab == SendingTab ? + EditAddressDialog::EditSendingAddress : + EditAddressDialog::EditReceivingAddress); + dlg.setModel(model); + QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0)); + dlg.loadRow(origIndex.row()); + dlg.exec(); +} + +void AddressBookPage::on_signMessage_clicked() +{ + QTableView *table = ui->tableView; + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + + foreach (QModelIndex index, indexes) + { + QString address = index.data().toString(); + emit signMessage(address); + } +} + +void AddressBookPage::on_verifyMessage_clicked() +{ + QTableView *table = ui->tableView; + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + + foreach (QModelIndex index, indexes) + { + QString address = index.data().toString(); + emit verifyMessage(address); + } +} + +void AddressBookPage::onSendCoinsAction() +{ + QTableView *table = ui->tableView; + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + + foreach (QModelIndex index, indexes) + { + QString address = index.data().toString(); + emit sendCoins(address); + } +} + +void AddressBookPage::on_newAddress_clicked() +{ + if(!model) + return; + + EditAddressDialog dlg( + tab == SendingTab ? + EditAddressDialog::NewSendingAddress : + EditAddressDialog::NewReceivingAddress, this); + dlg.setModel(model); + if(dlg.exec()) + { + newAddressToSelect = dlg.getAddress(); + } +} + +void AddressBookPage::on_deleteAddress_clicked() +{ + QTableView *table = ui->tableView; + if(!table->selectionModel()) + return; + + QModelIndexList indexes = table->selectionModel()->selectedRows(); + if(!indexes.isEmpty()) + { + table->model()->removeRow(indexes.at(0).row()); + } +} + +void AddressBookPage::selectionChanged() +{ + // Set button states based on selected tab and selection + QTableView *table = ui->tableView; + if(!table->selectionModel()) + return; + + if(table->selectionModel()->hasSelection()) + { + switch(tab) + { + case SendingTab: + // In sending tab, allow deletion of selection + ui->deleteAddress->setEnabled(true); + ui->deleteAddress->setVisible(true); + deleteAction->setEnabled(true); + ui->signMessage->setEnabled(false); + ui->signMessage->setVisible(false); + ui->verifyMessage->setEnabled(true); + ui->verifyMessage->setVisible(true); + break; + case ReceivingTab: + // Deleting receiving addresses, however, is not allowed + ui->deleteAddress->setEnabled(false); + ui->deleteAddress->setVisible(false); + deleteAction->setEnabled(false); + ui->signMessage->setEnabled(true); + ui->signMessage->setVisible(true); + ui->verifyMessage->setEnabled(false); + ui->verifyMessage->setVisible(false); + break; + } + ui->copyAddress->setEnabled(true); + ui->showQRCode->setEnabled(true); + } + else + { + ui->deleteAddress->setEnabled(false); + ui->showQRCode->setEnabled(false); + ui->copyAddress->setEnabled(false); + ui->signMessage->setEnabled(false); + ui->verifyMessage->setEnabled(false); + } +} + +void AddressBookPage::done(int retval) +{ + QTableView *table = ui->tableView; + if(!table->selectionModel() || !table->model()) + return; + // When this is a tab/widget and not a model dialog, ignore "done" + if(mode == ForEditing) + return; + + // Figure out which address was selected, and return it + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + + foreach (QModelIndex index, indexes) + { + QVariant address = table->model()->data(index); + returnValue = address.toString(); + } + + if(returnValue.isEmpty()) + { + // If no address entry selected, return rejected + retval = Rejected; + } + + QDialog::done(retval); +} + +void AddressBookPage::on_exportButton_clicked() +{ + // CSV is currently the only supported format + QString filename = GUIUtil::getSaveFileName( + this, + tr("Export Address Book Data"), QString(), + tr("Comma separated file (*.csv)")); + + if (filename.isNull()) return; + + CSVModelWriter writer(filename); + + // name, column, role + writer.setModel(proxyModel); + writer.addColumn("Label", AddressTableModel::Label, Qt::EditRole); + writer.addColumn("Address", AddressTableModel::Address, Qt::EditRole); + + if(!writer.write()) + { + QMessageBox::critical(this, tr("Error exporting"), tr("Could not write to file %1.").arg(filename), + QMessageBox::Abort, QMessageBox::Abort); + } +} + +void AddressBookPage::on_showQRCode_clicked() +{ +#ifdef USE_QRCODE + QTableView *table = ui->tableView; + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + + foreach (QModelIndex index, indexes) + { + QString address = index.data().toString(); + QString label = index.sibling(index.row(), 0).data(Qt::EditRole).toString(); + + QRCodeDialog *dialog = new QRCodeDialog(address, label, tab == ReceivingTab, this); + dialog->setModel(optionsModel); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->show(); + } +#endif +} + +void AddressBookPage::contextualMenu(const QPoint &point) +{ + QModelIndex index = ui->tableView->indexAt(point); + if(index.isValid()) + { + contextMenu->exec(QCursor::pos()); + } +} + +void AddressBookPage::selectNewAddress(const QModelIndex &parent, int begin, int /*end*/) +{ + QModelIndex idx = proxyModel->mapFromSource(model->index(begin, AddressTableModel::Address, parent)); + if(idx.isValid() && (idx.data(Qt::EditRole).toString() == newAddressToSelect)) + { + // Select row of newly created address, once + ui->tableView->setFocus(); + ui->tableView->selectRow(idx.row()); + newAddressToSelect.clear(); + } +} diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h new file mode 100644 index 0000000..34465aa --- /dev/null +++ b/src/qt/addressbookpage.h @@ -0,0 +1,94 @@ +#ifndef ADDRESSBOOKPAGE_H +#define ADDRESSBOOKPAGE_H + +#include + +namespace Ui { + class AddressBookPage; +} +class AddressTableModel; +class OptionsModel; + +QT_BEGIN_NAMESPACE +class QTableView; +class QItemSelection; +class QSortFilterProxyModel; +class QMenu; +class QModelIndex; +QT_END_NAMESPACE + +/** Widget that shows a list of sending or receiving addresses. + */ +class AddressBookPage : public QDialog +{ + Q_OBJECT + +public: + enum Tabs { + SendingTab = 0, + ReceivingTab = 1 + }; + + enum Mode { + ForSending, /**< Open address book to pick address for sending */ + ForEditing /**< Open address book for editing */ + }; + + explicit AddressBookPage(Mode mode, Tabs tab, QWidget *parent = 0); + ~AddressBookPage(); + + void setModel(AddressTableModel *model); + void setOptionsModel(OptionsModel *optionsModel); + const QString &getReturnValue() const { return returnValue; } + +public slots: + void done(int retval); + +private: + Ui::AddressBookPage *ui; + AddressTableModel *model; + OptionsModel *optionsModel; + Mode mode; + Tabs tab; + QString returnValue; + QSortFilterProxyModel *proxyModel; + QMenu *contextMenu; + QAction *deleteAction; // to be able to explicitly disable it + QString newAddressToSelect; + +private slots: + /** Delete currently selected address entry */ + void on_deleteAddress_clicked(); + /** Create a new address for receiving coins and / or add a new address book entry */ + void on_newAddress_clicked(); + /** Copy address of currently selected address entry to clipboard */ + void on_copyAddress_clicked(); + /** Open the sign message tab in the Sign/Verify Message dialog with currently selected address */ + void on_signMessage_clicked(); + /** Open the verify message tab in the Sign/Verify Message dialog with currently selected address */ + void on_verifyMessage_clicked(); + /** Open send coins dialog for currently selected address (no button) */ + void onSendCoinsAction(); + /** Generate a QR Code from the currently selected address */ + void on_showQRCode_clicked(); + /** Copy label of currently selected address entry to clipboard (no button) */ + void onCopyLabelAction(); + /** Edit currently selected address entry (no button) */ + void onEditAction(); + /** Export button clicked */ + void on_exportButton_clicked(); + + /** Set button states based on selected tab and selection */ + void selectionChanged(); + /** Spawn contextual menu (right mouse menu) for address book entry */ + void contextualMenu(const QPoint &point); + /** New entry/entries were added to address table */ + void selectNewAddress(const QModelIndex &parent, int begin, int /*end*/); + +signals: + void signMessage(QString addr); + void verifyMessage(QString addr); + void sendCoins(QString addr); +}; + +#endif // ADDRESSBOOKPAGE_H diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp new file mode 100644 index 0000000..1801444 --- /dev/null +++ b/src/qt/addresstablemodel.cpp @@ -0,0 +1,426 @@ +#include "addresstablemodel.h" + +#include "guiutil.h" +#include "walletmodel.h" + +#include "wallet.h" +#include "base58.h" + +#include + +const QString AddressTableModel::Send = "S"; +const QString AddressTableModel::Receive = "R"; + +struct AddressTableEntry +{ + enum Type { + Sending, + Receiving + }; + + Type type; + QString label; + QString address; + + AddressTableEntry() {} + AddressTableEntry(Type type, const QString &label, const QString &address): + type(type), label(label), address(address) {} +}; + +struct AddressTableEntryLessThan +{ + bool operator()(const AddressTableEntry &a, const AddressTableEntry &b) const + { + return a.address < b.address; + } + bool operator()(const AddressTableEntry &a, const QString &b) const + { + return a.address < b; + } + bool operator()(const QString &a, const AddressTableEntry &b) const + { + return a < b.address; + } +}; + +// Private implementation +class AddressTablePriv +{ +public: + CWallet *wallet; + QList cachedAddressTable; + AddressTableModel *parent; + + AddressTablePriv(CWallet *wallet, AddressTableModel *parent): + wallet(wallet), parent(parent) {} + + void refreshAddressTable() + { + cachedAddressTable.clear(); + { + LOCK(wallet->cs_wallet); + BOOST_FOREACH(const PAIRTYPE(CTxDestination, std::string)& item, wallet->mapAddressBook) + { + const CBitcoinAddress& address = item.first; + const std::string& strName = item.second; + bool fMine = IsMine(*wallet, address.Get()); + cachedAddressTable.append(AddressTableEntry(fMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending, + QString::fromStdString(strName), + QString::fromStdString(address.ToString()))); + } + } + // qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order + qSort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan()); + } + + void updateEntry(const QString &address, const QString &label, bool isMine, int status) + { + // Find address / label in model + QList::iterator lower = qLowerBound( + cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan()); + QList::iterator upper = qUpperBound( + cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan()); + int lowerIndex = (lower - cachedAddressTable.begin()); + int upperIndex = (upper - cachedAddressTable.begin()); + bool inModel = (lower != upper); + AddressTableEntry::Type newEntryType = isMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending; + + switch(status) + { + case CT_NEW: + if(inModel) + { + OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_NOW, but entry is already in model\n"); + break; + } + parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex); + cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address)); + parent->endInsertRows(); + break; + case CT_UPDATED: + if(!inModel) + { + OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_UPDATED, but entry is not in model\n"); + break; + } + lower->type = newEntryType; + lower->label = label; + parent->emitDataChanged(lowerIndex); + break; + case CT_DELETED: + if(!inModel) + { + OutputDebugStringF("Warning: AddressTablePriv::updateEntry: Got CT_DELETED, but entry is not in model\n"); + break; + } + parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); + cachedAddressTable.erase(lower, upper); + parent->endRemoveRows(); + break; + } + } + + int size() + { + return cachedAddressTable.size(); + } + + AddressTableEntry *index(int idx) + { + if(idx >= 0 && idx < cachedAddressTable.size()) + { + return &cachedAddressTable[idx]; + } + else + { + return 0; + } + } +}; + +AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) : + QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0) +{ + columns << tr("Label") << tr("Address"); + priv = new AddressTablePriv(wallet, this); + priv->refreshAddressTable(); +} + +AddressTableModel::~AddressTableModel() +{ + delete priv; +} + +int AddressTableModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return priv->size(); +} + +int AddressTableModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return columns.length(); +} + +QVariant AddressTableModel::data(const QModelIndex &index, int role) const +{ + if(!index.isValid()) + return QVariant(); + + AddressTableEntry *rec = static_cast(index.internalPointer()); + + if(role == Qt::DisplayRole || role == Qt::EditRole) + { + switch(index.column()) + { + case Label: + if(rec->label.isEmpty() && role == Qt::DisplayRole) + { + return tr("(no label)"); + } + else + { + return rec->label; + } + case Address: + return rec->address; + } + } + else if (role == Qt::FontRole) + { + QFont font; + if(index.column() == Address) + { + font = GUIUtil::bitcoinAddressFont(); + } + return font; + } + else if (role == TypeRole) + { + switch(rec->type) + { + case AddressTableEntry::Sending: + return Send; + case AddressTableEntry::Receiving: + return Receive; + default: break; + } + } + return QVariant(); +} + +bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if(!index.isValid()) + return false; + AddressTableEntry *rec = static_cast(index.internalPointer()); + + editStatus = OK; + + if(role == Qt::EditRole) + { + switch(index.column()) + { + case Label: + // Do nothing, if old label == new label + if(rec->label == value.toString()) + { + editStatus = NO_CHANGES; + return false; + } + wallet->SetAddressBookName(CBitcoinAddress(rec->address.toStdString()).Get(), value.toString().toStdString()); + break; + case Address: + // Do nothing, if old address == new address + if(CBitcoinAddress(rec->address.toStdString()) == CBitcoinAddress(value.toString().toStdString())) + { + editStatus = NO_CHANGES; + return false; + } + // Refuse to set invalid address, set error status and return false + else if(!walletModel->validateAddress(value.toString())) + { + editStatus = INVALID_ADDRESS; + return false; + } + // Check for duplicate addresses to prevent accidental deletion of addresses, if you try + // to paste an existing address over another address (with a different label) + else if(wallet->mapAddressBook.count(CBitcoinAddress(value.toString().toStdString()).Get())) + { + editStatus = DUPLICATE_ADDRESS; + return false; + } + // Double-check that we're not overwriting a receiving address + else if(rec->type == AddressTableEntry::Sending) + { + { + LOCK(wallet->cs_wallet); + // Remove old entry + wallet->DelAddressBookName(CBitcoinAddress(rec->address.toStdString()).Get()); + // Add new entry with new address + wallet->SetAddressBookName(CBitcoinAddress(value.toString().toStdString()).Get(), rec->label.toStdString()); + } + } + break; + } + return true; + } + return false; +} + +QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(orientation == Qt::Horizontal) + { + if(role == Qt::DisplayRole) + { + return columns[section]; + } + } + return QVariant(); +} + +Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const +{ + if(!index.isValid()) + return 0; + AddressTableEntry *rec = static_cast(index.internalPointer()); + + Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled; + // Can edit address and label for sending addresses, + // and only label for receiving addresses. + if(rec->type == AddressTableEntry::Sending || + (rec->type == AddressTableEntry::Receiving && index.column()==Label)) + { + retval |= Qt::ItemIsEditable; + } + return retval; +} + +QModelIndex AddressTableModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + AddressTableEntry *data = priv->index(row); + if(data) + { + return createIndex(row, column, priv->index(row)); + } + else + { + return QModelIndex(); + } +} + +void AddressTableModel::updateEntry(const QString &address, const QString &label, bool isMine, int status) +{ + // Update address book model from Bitcoin core + priv->updateEntry(address, label, isMine, status); +} + +QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address) +{ + std::string strLabel = label.toStdString(); + std::string strAddress = address.toStdString(); + + editStatus = OK; + + if(type == Send) + { + if(!walletModel->validateAddress(address)) + { + editStatus = INVALID_ADDRESS; + return QString(); + } + // Check for duplicate addresses + { + LOCK(wallet->cs_wallet); + if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress).Get())) + { + editStatus = DUPLICATE_ADDRESS; + return QString(); + } + } + } + else if(type == Receive) + { + // Generate a new address to associate with given label + WalletModel::UnlockContext ctx(walletModel->requestUnlock()); + if(!ctx.isValid()) + { + // Unlock wallet failed or was cancelled + editStatus = WALLET_UNLOCK_FAILURE; + return QString(); + } + CPubKey newKey; + if(!wallet->GetKeyFromPool(newKey, true)) + { + editStatus = KEY_GENERATION_FAILURE; + return QString(); + } + strAddress = CBitcoinAddress(newKey.GetID()).ToString(); + } + else + { + return QString(); + } + + // Add entry + { + LOCK(wallet->cs_wallet); + wallet->SetAddressBookName(CBitcoinAddress(strAddress).Get(), strLabel); + } + return QString::fromStdString(strAddress); +} + +bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent) +{ + Q_UNUSED(parent); + AddressTableEntry *rec = priv->index(row); + if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving) + { + // Can only remove one row at a time, and cannot remove rows not in model. + // Also refuse to remove receiving addresses. + return false; + } + { + LOCK(wallet->cs_wallet); + wallet->DelAddressBookName(CBitcoinAddress(rec->address.toStdString()).Get()); + } + return true; +} + +/* Look up label for address in address book, if not found return empty string. + */ +QString AddressTableModel::labelForAddress(const QString &address) const +{ + { + LOCK(wallet->cs_wallet); + CBitcoinAddress address_parsed(address.toStdString()); + std::map::iterator mi = wallet->mapAddressBook.find(address_parsed.Get()); + if (mi != wallet->mapAddressBook.end()) + { + return QString::fromStdString(mi->second); + } + } + return QString(); +} + +int AddressTableModel::lookupAddress(const QString &address) const +{ + QModelIndexList lst = match(index(0, Address, QModelIndex()), + Qt::EditRole, address, 1, Qt::MatchExactly); + if(lst.isEmpty()) + { + return -1; + } + else + { + return lst.at(0).row(); + } +} + +void AddressTableModel::emitDataChanged(int idx) +{ + emit dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex())); +} diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h new file mode 100644 index 0000000..48baff5 --- /dev/null +++ b/src/qt/addresstablemodel.h @@ -0,0 +1,93 @@ +#ifndef ADDRESSTABLEMODEL_H +#define ADDRESSTABLEMODEL_H + +#include +#include + +class AddressTablePriv; +class CWallet; +class WalletModel; + +/** + Qt model of the address book in the core. This allows views to access and modify the address book. + */ +class AddressTableModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + explicit AddressTableModel(CWallet *wallet, WalletModel *parent = 0); + ~AddressTableModel(); + + enum ColumnIndex { + Label = 0, /**< User specified label */ + Address = 1 /**< Bitcoin address */ + }; + + enum RoleIndex { + TypeRole = Qt::UserRole /**< Type of address (#Send or #Receive) */ + }; + + /** Return status of edit/insert operation */ + enum EditStatus { + OK, /**< Everything ok */ + NO_CHANGES, /**< No changes were made during edit operation */ + INVALID_ADDRESS, /**< Unparseable address */ + DUPLICATE_ADDRESS, /**< Address already in address book */ + WALLET_UNLOCK_FAILURE, /**< Wallet could not be unlocked to create new receiving address */ + KEY_GENERATION_FAILURE /**< Generating a new public key for a receiving address failed */ + }; + + static const QString Send; /**< Specifies send address */ + static const QString Receive; /**< Specifies receive address */ + + /** @name Methods overridden from QAbstractTableModel + @{*/ + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, int role); + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QModelIndex index(int row, int column, const QModelIndex &parent) const; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + Qt::ItemFlags flags(const QModelIndex &index) const; + /*@}*/ + + /* Add an address to the model. + Returns the added address on success, and an empty string otherwise. + */ + QString addRow(const QString &type, const QString &label, const QString &address); + + /* Look up label for address in address book, if not found return empty string. + */ + QString labelForAddress(const QString &address) const; + + /* Look up row index of an address in the model. + Return -1 if not found. + */ + int lookupAddress(const QString &address) const; + + EditStatus getEditStatus() const { return editStatus; } + +private: + WalletModel *walletModel; + CWallet *wallet; + AddressTablePriv *priv; + QStringList columns; + EditStatus editStatus; + + /** Notify listeners that data changed. */ + void emitDataChanged(int index); + +signals: + void defaultAddressChanged(const QString &address); + +public slots: + /* Update address list from core. + */ + void updateEntry(const QString &address, const QString &label, bool isMine, int status); + + friend class AddressTablePriv; +}; + +#endif // ADDRESSTABLEMODEL_H diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp new file mode 100644 index 0000000..f6224ea --- /dev/null +++ b/src/qt/askpassphrasedialog.cpp @@ -0,0 +1,248 @@ +#include "askpassphrasedialog.h" +#include "ui_askpassphrasedialog.h" + +#include "guiconstants.h" +#include "walletmodel.h" + +#include +#include +#include + +AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget *parent) : + QDialog(parent), + ui(new Ui::AskPassphraseDialog), + mode(mode), + model(0), + fCapsLock(false) +{ + ui->setupUi(this); + ui->passEdit1->setMaxLength(MAX_PASSPHRASE_SIZE); + ui->passEdit2->setMaxLength(MAX_PASSPHRASE_SIZE); + ui->passEdit3->setMaxLength(MAX_PASSPHRASE_SIZE); + + // Setup Caps Lock detection. + ui->passEdit1->installEventFilter(this); + ui->passEdit2->installEventFilter(this); + ui->passEdit3->installEventFilter(this); + + switch(mode) + { + case Encrypt: // Ask passphrase x2 + ui->passLabel1->hide(); + ui->passEdit1->hide(); + ui->warningLabel->setText(tr("Enter the new passphrase to the wallet.
Please use a passphrase of 10 or more random characters, or eight or more words.")); + setWindowTitle(tr("Encrypt wallet")); + break; + case Unlock: // Ask passphrase + ui->warningLabel->setText(tr("This operation needs your wallet passphrase to unlock the wallet.")); + ui->passLabel2->hide(); + ui->passEdit2->hide(); + ui->passLabel3->hide(); + ui->passEdit3->hide(); + setWindowTitle(tr("Unlock wallet")); + break; + case Decrypt: // Ask passphrase + ui->warningLabel->setText(tr("This operation needs your wallet passphrase to decrypt the wallet.")); + ui->passLabel2->hide(); + ui->passEdit2->hide(); + ui->passLabel3->hide(); + ui->passEdit3->hide(); + setWindowTitle(tr("Decrypt wallet")); + break; + case ChangePass: // Ask old passphrase + new passphrase x2 + setWindowTitle(tr("Change passphrase")); + ui->warningLabel->setText(tr("Enter the old and new passphrase to the wallet.")); + break; + } + + textChanged(); + connect(ui->passEdit1, SIGNAL(textChanged(QString)), this, SLOT(textChanged())); + connect(ui->passEdit2, SIGNAL(textChanged(QString)), this, SLOT(textChanged())); + connect(ui->passEdit3, SIGNAL(textChanged(QString)), this, SLOT(textChanged())); +} + +AskPassphraseDialog::~AskPassphraseDialog() +{ + // Attempt to overwrite text so that they do not linger around in memory + ui->passEdit1->setText(QString(" ").repeated(ui->passEdit1->text().size())); + ui->passEdit2->setText(QString(" ").repeated(ui->passEdit2->text().size())); + ui->passEdit3->setText(QString(" ").repeated(ui->passEdit3->text().size())); + delete ui; +} + +void AskPassphraseDialog::setModel(WalletModel *model) +{ + this->model = model; +} + +void AskPassphraseDialog::accept() +{ + SecureString oldpass, newpass1, newpass2; + if(!model) + return; + oldpass.reserve(MAX_PASSPHRASE_SIZE); + newpass1.reserve(MAX_PASSPHRASE_SIZE); + newpass2.reserve(MAX_PASSPHRASE_SIZE); + // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) + // Alternately, find a way to make this input mlock()'d to begin with. + oldpass.assign(ui->passEdit1->text().toStdString().c_str()); + newpass1.assign(ui->passEdit2->text().toStdString().c_str()); + newpass2.assign(ui->passEdit3->text().toStdString().c_str()); + + switch(mode) + { + case Encrypt: { + if(newpass1.empty() || newpass2.empty()) + { + // Cannot encrypt with empty passphrase + break; + } + QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm wallet encryption"), + tr("Warning: If you encrypt your wallet and lose your passphrase, you will LOSE ALL OF YOUR CryptoLottoTokenS!") + "

" + tr("Are you sure you wish to encrypt your wallet?"), + QMessageBox::Yes|QMessageBox::Cancel, + QMessageBox::Cancel); + if(retval == QMessageBox::Yes) + { + if(newpass1 == newpass2) + { + if(model->setWalletEncrypted(true, newpass1)) + { + QMessageBox::warning(this, tr("Wallet encrypted"), + "" + + tr("CryptoLottoToken will close now to finish the encryption process. " + "Remember that encrypting your wallet cannot fully protect " + "your CryptoLottoTokens from being stolen by malware infecting your computer.") + + "

" + + tr("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.") + + "
"); + QApplication::quit(); + } + else + { + QMessageBox::critical(this, tr("Wallet encryption failed"), + tr("Wallet encryption failed due to an internal error. Your wallet was not encrypted.")); + } + QDialog::accept(); // Success + } + else + { + QMessageBox::critical(this, tr("Wallet encryption failed"), + tr("The supplied passphrases do not match.")); + } + } + else + { + QDialog::reject(); // Cancelled + } + } break; + case Unlock: + if(!model->setWalletLocked(false, oldpass)) + { + QMessageBox::critical(this, tr("Wallet unlock failed"), + tr("The passphrase entered for the wallet decryption was incorrect.")); + } + else + { + QDialog::accept(); // Success + } + break; + case Decrypt: + if(!model->setWalletEncrypted(false, oldpass)) + { + QMessageBox::critical(this, tr("Wallet decryption failed"), + tr("The passphrase entered for the wallet decryption was incorrect.")); + } + else + { + QDialog::accept(); // Success + } + break; + case ChangePass: + if(newpass1 == newpass2) + { + if(model->changePassphrase(oldpass, newpass1)) + { + QMessageBox::information(this, tr("Wallet encrypted"), + tr("Wallet passphrase was successfully changed.")); + QDialog::accept(); // Success + } + else + { + QMessageBox::critical(this, tr("Wallet encryption failed"), + tr("The passphrase entered for the wallet decryption was incorrect.")); + } + } + else + { + QMessageBox::critical(this, tr("Wallet encryption failed"), + tr("The supplied passphrases do not match.")); + } + break; + } +} + +void AskPassphraseDialog::textChanged() +{ + // Validate input, set Ok button to enabled when acceptable + bool acceptable = false; + switch(mode) + { + case Encrypt: // New passphrase x2 + acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty(); + break; + case Unlock: // Old passphrase x1 + case Decrypt: + acceptable = !ui->passEdit1->text().isEmpty(); + break; + case ChangePass: // Old passphrase x1, new passphrase x2 + acceptable = !ui->passEdit1->text().isEmpty() && !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty(); + break; + } + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(acceptable); +} + +bool AskPassphraseDialog::event(QEvent *event) +{ + // Detect Caps Lock key press. + if (event->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast(event); + if (ke->key() == Qt::Key_CapsLock) { + fCapsLock = !fCapsLock; + } + if (fCapsLock) { + ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!")); + } else { + ui->capsLabel->clear(); + } + } + return QWidget::event(event); +} + +bool AskPassphraseDialog::eventFilter(QObject *object, QEvent *event) +{ + /* Detect Caps Lock. + * There is no good OS-independent way to check a key state in Qt, but we + * can detect Caps Lock by checking for the following condition: + * Shift key is down and the result is a lower case character, or + * Shift key is not down and the result is an upper case character. + */ + if (event->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast(event); + QString str = ke->text(); + if (str.length() != 0) { + const QChar *psz = str.unicode(); + bool fShift = (ke->modifiers() & Qt::ShiftModifier) != 0; + if ((fShift && *psz >= 'a' && *psz <= 'z') || (!fShift && *psz >= 'A' && *psz <= 'Z')) { + fCapsLock = true; + ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!")); + } else if (psz->isLetter()) { + fCapsLock = false; + ui->capsLabel->clear(); + } + } + } + return QDialog::eventFilter(object, event); +} diff --git a/src/qt/askpassphrasedialog.h b/src/qt/askpassphrasedialog.h new file mode 100644 index 0000000..9df002d --- /dev/null +++ b/src/qt/askpassphrasedialog.h @@ -0,0 +1,44 @@ +#ifndef ASKPASSPHRASEDIALOG_H +#define ASKPASSPHRASEDIALOG_H + +#include + +namespace Ui { + class AskPassphraseDialog; +} +class WalletModel; + +/** Multifunctional dialog to ask for passphrases. Used for encryption, unlocking, and changing the passphrase. + */ +class AskPassphraseDialog : public QDialog +{ + Q_OBJECT + +public: + enum Mode { + Encrypt, /**< Ask passphrase twice and encrypt */ + Unlock, /**< Ask passphrase and unlock */ + ChangePass, /**< Ask old passphrase + new passphrase twice */ + Decrypt /**< Ask passphrase and decrypt wallet */ + }; + + explicit AskPassphraseDialog(Mode mode, QWidget *parent = 0); + ~AskPassphraseDialog(); + + void accept(); + + void setModel(WalletModel *model); + +private: + Ui::AskPassphraseDialog *ui; + Mode mode; + WalletModel *model; + bool fCapsLock; + +private slots: + void textChanged(); + bool event(QEvent *event); + bool eventFilter(QObject *object, QEvent *event); +}; + +#endif // ASKPASSPHRASEDIALOG_H diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp new file mode 100644 index 0000000..7e11e88 --- /dev/null +++ b/src/qt/bitcoin.cpp @@ -0,0 +1,310 @@ +/* + * W.J. van der Laan 2011-2012 + */ + +#include + +#include "bitcoingui.h" +#include "clientmodel.h" +#include "walletmodel.h" +#include "optionsmodel.h" +#include "guiutil.h" +#include "guiconstants.h" +#include "init.h" +#include "util.h" +#include "ui_interface.h" +#include "paymentserver.h" +#include "splashscreen.h" + +#include +#if QT_VERSION < 0x050000 +#include +#endif +#include +#include +#include +#include + +#ifdef Q_OS_MAC +#include "macdockiconhandler.h" +#endif + +#if defined(BITCOIN_NEED_QT_PLUGINS) && !defined(_BITCOIN_QT_PLUGINS_INCLUDED) +#define _BITCOIN_QT_PLUGINS_INCLUDED +#define __INSURE__ +#include +Q_IMPORT_PLUGIN(qcncodecs) +Q_IMPORT_PLUGIN(qjpcodecs) +Q_IMPORT_PLUGIN(qtwcodecs) +Q_IMPORT_PLUGIN(qkrcodecs) +Q_IMPORT_PLUGIN(qtaccessiblewidgets) +#endif + +// Declare meta types used for QMetaObject::invokeMethod +Q_DECLARE_METATYPE(bool*) + +// Need a global reference for the notifications to find the GUI +static BitcoinGUI *guiref; +static SplashScreen *splashref; + +static bool ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) +{ + // Message from network thread + if(guiref) + { + bool modal = (style & CClientUIInterface::MODAL); + bool ret = false; + // In case of modal message, use blocking connection to wait for user to click a button + QMetaObject::invokeMethod(guiref, "message", + modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection, + Q_ARG(QString, QString::fromStdString(caption)), + Q_ARG(QString, QString::fromStdString(message)), + Q_ARG(unsigned int, style), + Q_ARG(bool*, &ret)); + return ret; + } + else + { + printf("%s: %s\n", caption.c_str(), message.c_str()); + fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str()); + return false; + } +} + +static bool ThreadSafeAskFee(int64 nFeeRequired) +{ + if(!guiref) + return false; + if(nFeeRequired < CTransaction::nMinTxFee || nFeeRequired <= nTransactionFee || fDaemon) + return true; + + bool payFee = false; + + QMetaObject::invokeMethod(guiref, "askFee", GUIUtil::blockingGUIThreadConnection(), + Q_ARG(qint64, nFeeRequired), + Q_ARG(bool*, &payFee)); + + return payFee; +} + +static void InitMessage(const std::string &message) +{ + if(splashref) + { + splashref->showMessage(QString::fromStdString(message), Qt::AlignBottom|Qt::AlignHCenter, QColor(55,55,55)); + qApp->processEvents(); + } + printf("init message: %s\n", message.c_str()); +} + +/* + Translate string to current locale using Qt. + */ +static std::string Translate(const char* psz) +{ + return QCoreApplication::translate("bitcoin-core", psz).toStdString(); +} + +/* Handle runaway exceptions. Shows a message box with the problem and quits the program. + */ +static void handleRunawayException(std::exception *e) +{ + PrintExceptionContinue(e, "Runaway exception"); + QMessageBox::critical(0, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit.") + QString("\n\n") + QString::fromStdString(strMiscWarning)); + exit(1); +} + +#ifndef BITCOIN_QT_TEST +int main(int argc, char *argv[]) +{ + // Command-line options take precedence: + ParseParameters(argc, argv); + +#if QT_VERSION < 0x050000 + // Internal string conversion is all UTF-8 + QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); + QTextCodec::setCodecForCStrings(QTextCodec::codecForTr()); +#endif + + Q_INIT_RESOURCE(bitcoin); + QApplication app(argc, argv); + + // Register meta types used for QMetaObject::invokeMethod + qRegisterMetaType< bool* >(); + + // Do this early as we don't want to bother initializing if we are just calling IPC + // ... but do it after creating app, so QCoreApplication::arguments is initialized: + if (PaymentServer::ipcSendCommandLine()) + exit(0); + PaymentServer* paymentServer = new PaymentServer(&app); + + // Install global event filter that makes sure that long tooltips can be word-wrapped + app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app)); + + // ... then bitcoin.conf: + if (!boost::filesystem::is_directory(GetDataDir(false))) + { + // This message can not be translated, as translation is not initialized yet + // (which not yet possible because lang=XX can be overridden in bitcoin.conf in the data directory) + QMessageBox::critical(0, "CryptoLottoToken", + QString("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(mapArgs["-datadir"]))); + return 1; + } + ReadConfigFile(mapArgs, mapMultiArgs); + + // Application identification (must be set before OptionsModel is initialized, + // as it is used to locate QSettings) + QApplication::setOrganizationName("CryptoLottoToken"); + QApplication::setOrganizationDomain("CryptoLottoToken-noexist-domain.org"); + if(GetBoolArg("-testnet")) // Separate UI settings for testnet + QApplication::setApplicationName("CryptoLottoToken-Qt-testnet"); + else + QApplication::setApplicationName("CryptoLottoToken-Qt"); + + // ... then GUI settings: + OptionsModel optionsModel; + + // Get desired locale (e.g. "de_DE") from command line or use system locale + QString lang_territory = QString::fromStdString(GetArg("-lang", QLocale::system().name().toStdString())); + QString lang = lang_territory; + // Convert to "de" only by truncating "_DE" + lang.truncate(lang_territory.lastIndexOf('_')); + + QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator; + // Load language files for configured locale: + // - First load the translator for the base language, without territory + // - Then load the more specific locale translator + + // Load e.g. qt_de.qm + if (qtTranslatorBase.load("qt_" + lang, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) + app.installTranslator(&qtTranslatorBase); + + // Load e.g. qt_de_DE.qm + if (qtTranslator.load("qt_" + lang_territory, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) + app.installTranslator(&qtTranslator); + + // Load e.g. bitcoin_de.qm (shortcut "de" needs to be defined in bitcoin.qrc) + if (translatorBase.load(lang, ":/translations/")) + app.installTranslator(&translatorBase); + + // Load e.g. bitcoin_de_DE.qm (shortcut "de_DE" needs to be defined in bitcoin.qrc) + if (translator.load(lang_territory, ":/translations/")) + app.installTranslator(&translator); + + // Subscribe to global signals from core + uiInterface.ThreadSafeMessageBox.connect(ThreadSafeMessageBox); + uiInterface.ThreadSafeAskFee.connect(ThreadSafeAskFee); + uiInterface.InitMessage.connect(InitMessage); + uiInterface.Translate.connect(Translate); + + // Show help message immediately after parsing command-line options (for "-lang") and setting locale, + // but before showing splash screen. + if (mapArgs.count("-?") || mapArgs.count("--help")) + { + GUIUtil::HelpMessageBox help; + help.showOrPrint(); + return 1; + } + +#ifdef Q_OS_MAC + // on mac, also change the icon now because it would look strange to have a testnet splash (green) and a std app icon (orange) + if(GetBoolArg("-testnet")) { + MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin_testnet")); + } +#endif + + SplashScreen splash(QPixmap(), 0); + if (GetBoolArg("-splash", true) && !GetBoolArg("-min")) + { + splash.show(); + splash.setAutoFillBackground(true); + splashref = &splash; + } + + app.processEvents(); + app.setQuitOnLastWindowClosed(false); + + try + { +#ifndef Q_OS_MAC + // Regenerate startup link, to fix links to old versions + // OSX: makes no sense on mac and might also scan/mount external (and sleeping) volumes (can take up some secs) + if (GUIUtil::GetStartOnSystemStartup()) + GUIUtil::SetStartOnSystemStartup(true); +#endif + + boost::thread_group threadGroup; + + BitcoinGUI window; + guiref = &window; + + QTimer* pollShutdownTimer = new QTimer(guiref); + QObject::connect(pollShutdownTimer, SIGNAL(timeout()), guiref, SLOT(detectShutdown())); + pollShutdownTimer->start(200); + + if(AppInit2(threadGroup)) + { + { + // Put this in a block, so that the Model objects are cleaned up before + // calling Shutdown(). + + optionsModel.Upgrade(); // Must be done after AppInit2 + + if (splashref) + splash.finish(&window); + + ClientModel clientModel(&optionsModel); + WalletModel *walletModel = 0; + if(pwalletMain) + walletModel = new WalletModel(pwalletMain, &optionsModel); + + window.setClientModel(&clientModel); + if(walletModel) + { + window.addWallet("~Default", walletModel); + window.setCurrentWallet("~Default"); + } + + // If -min option passed, start window minimized. + if(GetBoolArg("-min")) + { + window.showMinimized(); + } + else + { + window.show(); + } + + // Now that initialization/startup is done, process any command-line + // bitcoin: URIs + QObject::connect(paymentServer, SIGNAL(receivedURI(QString)), &window, SLOT(handleURI(QString))); + QTimer::singleShot(100, paymentServer, SLOT(uiReady())); + + app.exec(); + + window.hide(); + window.setClientModel(0); + window.removeAllWallets(); + guiref = 0; + delete walletModel; + } + // Shutdown the core and its threads, but don't exit Bitcoin-Qt here + threadGroup.interrupt_all(); + threadGroup.join_all(); + Shutdown(); + } + else + { + threadGroup.interrupt_all(); + threadGroup.join_all(); + Shutdown(); + return 1; + } + } catch (std::exception& e) { + handleRunawayException(&e); + } catch (...) { + handleRunawayException(NULL); + } + return 0; +} +#endif // BITCOIN_QT_TEST diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc new file mode 100644 index 0000000..5e284b7 --- /dev/null +++ b/src/qt/bitcoin.qrc @@ -0,0 +1,102 @@ + + + res/icons/bitcoin.png + res/icons/address-book.png + res/icons/quit.png + res/icons/send.png + res/icons/bitcoin.png + res/icons/connect0_16.png + res/icons/connect1_16.png + res/icons/connect2_16.png + res/icons/connect3_16.png + res/icons/connect4_16.png + res/icons/transaction0.png + res/icons/transaction2.png + res/icons/clock1.png + res/icons/clock2.png + res/icons/clock3.png + res/icons/clock4.png + res/icons/clock5.png + res/icons/configure.png + res/icons/receive.png + res/icons/editpaste.png + res/icons/editcopy.png + res/icons/add.png + res/icons/bitcoin.png + res/icons/bitcoin.png + res/icons/edit.png + res/icons/history.png + res/icons/overview.png + res/icons/export.png + res/icons/synced.png + res/icons/remove.png + res/icons/tx_mined.png + res/icons/tx_input.png + res/icons/tx_output.png + res/icons/tx_inout.png + res/icons/lock_closed.png + res/icons/lock_open.png + res/icons/key.png + res/icons/filesave.png + res/icons/qrcode.png + res/icons/debugwindow.png + + + res/images/about.png + res/images/splash.png + res/images/splash.png + res/icons/bitcoin.png + + + res/movies/update_spinner.mng + + + locale/bitcoin_af_ZA.qm + locale/bitcoin_ar.qm + locale/bitcoin_bg.qm + locale/bitcoin_bs.ts + locale/bitcoin_ca.ts + locale/bitcoin_ca_ES.qm + locale/bitcoin_cs.qm + locale/bitcoin_cy.ts + locale/bitcoin_da.qm + locale/bitcoin_de.qm + locale/bitcoin_el_GR.qm + locale/bitcoin_en.qm + locale/bitcoin_eo.ts + locale/bitcoin_es.qm + locale/bitcoin_es_CL.qm + locale/bitcoin_et.qm + locale/bitcoin_eu_ES.qm + locale/bitcoin_fa.qm + locale/bitcoin_fa_IR.qm + locale/bitcoin_fi.qm + locale/bitcoin_fr.qm + locale/bitcoin_fr_CA.qm + locale/bitcoin_gu_IN.ts + locale/bitcoin_he.qm + locale/bitcoin_hi_IN.ts + locale/bitcoin_hr.qm + locale/bitcoin_hu.qm + locale/bitcoin_it.qm + locale/bitcoin_ja.ts + locale/bitcoin_la.ts + locale/bitcoin_lt.qm + locale/bitcoin_lv_LV.ts + locale/bitcoin_nb.qm + locale/bitcoin_nl.qm + locale/bitcoin_pl.qm + locale/bitcoin_pt_BR.qm + locale/bitcoin_pt_PT.qm + locale/bitcoin_ro_RO.qm + locale/bitcoin_ru.qm + locale/bitcoin_sk.qm + locale/bitcoin_sr.qm + locale/bitcoin_sv.qm + locale/bitcoin_th_TH.ts + locale/bitcoin_tr.qm + locale/bitcoin_uk.qm + locale/bitcoin_zh_CN.qm + locale/bitcoin_zh_TW.qm + + diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp new file mode 100644 index 0000000..5136ea0 --- /dev/null +++ b/src/qt/bitcoinaddressvalidator.cpp @@ -0,0 +1,77 @@ +#include "bitcoinaddressvalidator.h" + +/* Base58 characters are: + "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" + + This is: + - All numbers except for '0' + - All upper-case letters except for 'I' and 'O' + - All lower-case letters except for 'l' + + User friendly Base58 input can map + - 'l' and 'I' to '1' + - '0' and 'O' to 'o' +*/ + +BitcoinAddressValidator::BitcoinAddressValidator(QObject *parent) : + QValidator(parent) +{ +} + +QValidator::State BitcoinAddressValidator::validate(QString &input, int &pos) const +{ + // Correction + for(int idx=0; idx= '0' && ch<='9') || + (ch >= 'a' && ch<='z') || + (ch >= 'A' && ch<='Z')) && + ch != 'l' && ch != 'I' && ch != '0' && ch != 'O') + { + // Alphanumeric and not a 'forbidden' character + } + else + { + state = QValidator::Invalid; + } + } + + // Empty address is "intermediate" input + if(input.isEmpty()) + { + state = QValidator::Intermediate; + } + + return state; +} diff --git a/src/qt/bitcoinaddressvalidator.h b/src/qt/bitcoinaddressvalidator.h new file mode 100644 index 0000000..b7f4dfe --- /dev/null +++ b/src/qt/bitcoinaddressvalidator.h @@ -0,0 +1,21 @@ +#ifndef BITCOINADDRESSVALIDATOR_H +#define BITCOINADDRESSVALIDATOR_H + +#include + +/** Base58 entry widget validator. + Corrects near-miss characters and refuses characters that are not part of base58. + */ +class BitcoinAddressValidator : public QValidator +{ + Q_OBJECT + +public: + explicit BitcoinAddressValidator(QObject *parent = 0); + + State validate(QString &input, int &pos) const; + + static const int MaxAddressLength = 35; +}; + +#endif // BITCOINADDRESSVALIDATOR_H diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp new file mode 100644 index 0000000..b12e296 --- /dev/null +++ b/src/qt/bitcoinamountfield.cpp @@ -0,0 +1,169 @@ +#include "bitcoinamountfield.h" + +#include "qvaluecombobox.h" +#include "bitcoinunits.h" +#include "guiconstants.h" + +#include +#include +#include +#include +#include // for qPow() + +BitcoinAmountField::BitcoinAmountField(QWidget *parent): + QWidget(parent), amount(0), currentUnit(-1) +{ + amount = new QDoubleSpinBox(this); + amount->setLocale(QLocale::c()); + amount->setDecimals(8); + amount->installEventFilter(this); + amount->setMaximumWidth(170); + amount->setSingleStep(0.001); + + QHBoxLayout *layout = new QHBoxLayout(this); + layout->addWidget(amount); + unit = new QValueComboBox(this); + unit->setModel(new BitcoinUnits(this)); + layout->addWidget(unit); + layout->addStretch(1); + layout->setContentsMargins(0,0,0,0); + + setLayout(layout); + + setFocusPolicy(Qt::TabFocus); + setFocusProxy(amount); + + // If one if the widgets changes, the combined content changes as well + connect(amount, SIGNAL(valueChanged(QString)), this, SIGNAL(textChanged())); + connect(unit, SIGNAL(currentIndexChanged(int)), this, SLOT(unitChanged(int))); + + // Set default based on configuration + unitChanged(unit->currentIndex()); +} + +void BitcoinAmountField::setText(const QString &text) +{ + if (text.isEmpty()) + amount->clear(); + else + amount->setValue(text.toDouble()); +} + +void BitcoinAmountField::clear() +{ + amount->clear(); + unit->setCurrentIndex(0); +} + +bool BitcoinAmountField::validate() +{ + bool valid = true; + if (amount->value() == 0.0) + valid = false; + if (valid && !BitcoinUnits::parse(currentUnit, text(), 0)) + valid = false; + + setValid(valid); + + return valid; +} + +void BitcoinAmountField::setValid(bool valid) +{ + if (valid) + amount->setStyleSheet(""); + else + amount->setStyleSheet(STYLE_INVALID); +} + +QString BitcoinAmountField::text() const +{ + if (amount->text().isEmpty()) + return QString(); + else + return amount->text(); +} + +bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event) +{ + if (event->type() == QEvent::FocusIn) + { + // Clear invalid flag on focus + setValid(true); + } + else if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) + { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Comma) + { + // Translate a comma into a period + QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count()); + QApplication::sendEvent(object, &periodKeyEvent); + return true; + } + } + return QWidget::eventFilter(object, event); +} + +QWidget *BitcoinAmountField::setupTabChain(QWidget *prev) +{ + QWidget::setTabOrder(prev, amount); + return amount; +} + +qint64 BitcoinAmountField::value(bool *valid_out) const +{ + qint64 val_out = 0; + bool valid = BitcoinUnits::parse(currentUnit, text(), &val_out); + if(valid_out) + { + *valid_out = valid; + } + return val_out; +} + +void BitcoinAmountField::setValue(qint64 value) +{ + setText(BitcoinUnits::format(currentUnit, value)); +} + +void BitcoinAmountField::unitChanged(int idx) +{ + // Use description tooltip for current unit for the combobox + unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString()); + + // Determine new unit ID + int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt(); + + // Parse current value and convert to new unit + bool valid = false; + qint64 currentValue = value(&valid); + + currentUnit = newUnit; + + // Set max length after retrieving the value, to prevent truncation + amount->setDecimals(BitcoinUnits::decimals(currentUnit)); + amount->setMaximum(qPow(10, BitcoinUnits::amountDigits(currentUnit)) - qPow(10, -amount->decimals())); + + if(currentUnit == BitcoinUnits::uBTC) + amount->setSingleStep(0.01); + else + amount->setSingleStep(0.001); + + if(valid) + { + // If value was valid, re-place it in the widget with the new unit + setValue(currentValue); + } + else + { + // If current value is invalid, just clear field + setText(""); + } + setValid(true); +} + +void BitcoinAmountField::setDisplayUnit(int newUnit) +{ + unit->setValue(newUnit); +} diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h new file mode 100644 index 0000000..dacbb39 --- /dev/null +++ b/src/qt/bitcoinamountfield.h @@ -0,0 +1,61 @@ +#ifndef BITCOINAMOUNTFIELD_H +#define BITCOINAMOUNTFIELD_H + +#include + +QT_BEGIN_NAMESPACE +class QDoubleSpinBox; +class QValueComboBox; +QT_END_NAMESPACE + +/** Widget for entering bitcoin amounts. + */ +class BitcoinAmountField: public QWidget +{ + Q_OBJECT + + Q_PROPERTY(qint64 value READ value WRITE setValue NOTIFY textChanged USER true) + +public: + explicit BitcoinAmountField(QWidget *parent = 0); + + qint64 value(bool *valid=0) const; + void setValue(qint64 value); + + /** Mark current value as invalid in UI. */ + void setValid(bool valid); + /** Perform input validation, mark field as invalid if entered value is not valid. */ + bool validate(); + + /** Change unit used to display amount. */ + void setDisplayUnit(int unit); + + /** Make field empty and ready for new input. */ + void clear(); + + /** Qt messes up the tab chain by default in some cases (issue https://bugreports.qt-project.org/browse/QTBUG-10907), + in these cases we have to set it up manually. + */ + QWidget *setupTabChain(QWidget *prev); + +signals: + void textChanged(); + +protected: + /** Intercept focus-in event and ',' key presses */ + bool eventFilter(QObject *object, QEvent *event); + +private: + QDoubleSpinBox *amount; + QValueComboBox *unit; + int currentUnit; + + void setText(const QString &text); + QString text() const; + +private slots: + void unitChanged(int idx); + +}; + +#endif // BITCOINAMOUNTFIELD_H diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp new file mode 100644 index 0000000..269daaa --- /dev/null +++ b/src/qt/bitcoingui.cpp @@ -0,0 +1,846 @@ +/* + * Qt4 bitcoin GUI. + * + * W.J. van der Laan 2011-2012 + * The Bitcoin Developers 2011-2012 + */ + +#include + +#include "bitcoingui.h" + +#include "transactiontablemodel.h" +#include "optionsdialog.h" +#include "aboutdialog.h" +#include "clientmodel.h" +#include "walletmodel.h" +#include "walletframe.h" +#include "optionsmodel.h" +#include "transactiondescdialog.h" +#include "bitcoinunits.h" +#include "guiconstants.h" +#include "notificator.h" +#include "guiutil.h" +#include "rpcconsole.h" +#include "ui_interface.h" +#include "wallet.h" +#include "init.h" + +#ifdef Q_OS_MAC +#include "macdockiconhandler.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if QT_VERSION < 0x050000 +#include +#endif +#include +#include +#include +#include +#include + +#include + +const QString BitcoinGUI::DEFAULT_WALLET = "~Default"; + +BitcoinGUI::BitcoinGUI(QWidget *parent) : + QMainWindow(parent), + clientModel(0), + encryptWalletAction(0), + changePassphraseAction(0), + aboutQtAction(0), + trayIcon(0), + notificator(0), + rpcConsole(0), + prevBlocks(0) +{ + restoreWindowGeometry(); + setWindowTitle(tr("CryptoLottoToken") + " - " + tr("Wallet")); +#ifndef Q_OS_MAC + QApplication::setWindowIcon(QIcon(":icons/bitcoin")); + setWindowIcon(QIcon(":icons/bitcoin")); +#else + setUnifiedTitleAndToolBarOnMac(true); + QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); +#endif + // Create wallet frame and make it the central widget + walletFrame = new WalletFrame(this); + setCentralWidget(walletFrame); + + //specify a new font. + //QFont newFont("Comic Sans MS", 10); + + //set font of application + //QApplication::setFont(newFont); + + // Accept D&D of URIs + setAcceptDrops(true); + + // Create actions for the toolbar, menu bar and tray/dock icon + // Needs walletFrame to be initialized + createActions(); + + // Create application menu bar + createMenuBar(); + + // Create the toolbars + createToolBars(); + + // Create system tray icon and notification + createTrayIcon(); + + // Create status bar + statusBar(); + + // Status bar notification icons + QFrame *frameBlocks = new QFrame(); + frameBlocks->setContentsMargins(0,0,0,0); + frameBlocks->setMinimumWidth(56); + frameBlocks->setMaximumWidth(56); + QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks); + frameBlocksLayout->setContentsMargins(3,0,3,0); + frameBlocksLayout->setSpacing(3); + labelEncryptionIcon = new QLabel(); + labelConnectionsIcon = new QLabel(); + labelBlocksIcon = new QLabel(); + frameBlocksLayout->addStretch(); + frameBlocksLayout->addWidget(labelEncryptionIcon); + frameBlocksLayout->addStretch(); + frameBlocksLayout->addWidget(labelConnectionsIcon); + frameBlocksLayout->addStretch(); + frameBlocksLayout->addWidget(labelBlocksIcon); + frameBlocksLayout->addStretch(); + + // Progress bar and label for blocks download + progressBarLabel = new QLabel(); + progressBarLabel->setVisible(false); + progressBar = new QProgressBar(); + progressBar->setAlignment(Qt::AlignCenter); + progressBar->setVisible(false); + + // Override style sheet for progress bar for styles that have a segmented progress bar, + // as they make the text unreadable (workaround for issue #1071) + // See https://qt-project.org/doc/qt-4.8/gallery.html + QString curStyle = QApplication::style()->metaObject()->className(); + if(curStyle == "QWindowsStyle" || curStyle == "QWindowsXPStyle") + { + progressBar->setStyleSheet("QProgressBar { background-color: #e8e8e8; border: 1px solid grey; border-radius: 7px; padding: 1px; text-align: center; } QProgressBar::chunk { background: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #FF8000, stop: 1 orange); border-radius: 7px; margin: 0px; }"); + } + + statusBar()->addWidget(progressBarLabel); + statusBar()->addWidget(progressBar); + statusBar()->addPermanentWidget(frameBlocks); + + syncIconMovie = new QMovie(":/movies/update_spinner", "mng", this); + + rpcConsole = new RPCConsole(this); + connect(openRPCConsoleAction, SIGNAL(triggered()), rpcConsole, SLOT(show())); + + // Install event filter to be able to catch status tip events (QEvent::StatusTip) + this->installEventFilter(this); + + // Initially wallet actions should be disabled + setWalletActionsEnabled(false); +} + +BitcoinGUI::~BitcoinGUI() +{ + saveWindowGeometry(); + if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu) + trayIcon->hide(); +#ifdef Q_OS_MAC + delete appMenuBar; + MacDockIconHandler::instance()->setMainWindow(NULL); +#endif +} + +void BitcoinGUI::createActions() +{ + QActionGroup *tabGroup = new QActionGroup(this); + + overviewAction = new QAction(QIcon(":/icons/overview"), tr("&Summary"), this); + overviewAction->setStatusTip(tr("Show general overview of wallet")); + overviewAction->setToolTip(overviewAction->statusTip()); + overviewAction->setCheckable(true); + overviewAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_1)); + tabGroup->addAction(overviewAction); + + sendCoinsAction = new QAction(QIcon(":/icons/send"), tr("&Send Coins"), this); + sendCoinsAction->setStatusTip(tr("Send coins to a CryptoLottoToken address")); + sendCoinsAction->setToolTip(sendCoinsAction->statusTip()); + sendCoinsAction->setCheckable(true); + sendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_2)); + tabGroup->addAction(sendCoinsAction); + + receiveCoinsAction = new QAction(QIcon(":/icons/receiving_addresses"), tr("&Receive Coins"), this); + receiveCoinsAction->setStatusTip(tr("Show the list of addresses for receiving payments")); + receiveCoinsAction->setToolTip(receiveCoinsAction->statusTip()); + receiveCoinsAction->setCheckable(true); + receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3)); + tabGroup->addAction(receiveCoinsAction); + + historyAction = new QAction(QIcon(":/icons/history"), tr("&Transactions"), this); + historyAction->setStatusTip(tr("Browse transaction history")); + historyAction->setToolTip(historyAction->statusTip()); + historyAction->setCheckable(true); + historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4)); + tabGroup->addAction(historyAction); + + addressBookAction = new QAction(QIcon(":/icons/address-book"), tr("&Address Book"), this); + addressBookAction->setStatusTip(tr("Edit the list of stored addresses and labels for sending")); + addressBookAction->setToolTip(addressBookAction->statusTip()); + addressBookAction->setCheckable(true); + addressBookAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_5)); + tabGroup->addAction(addressBookAction); + + connect(overviewAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(overviewAction, SIGNAL(triggered()), this, SLOT(gotoOverviewPage())); + connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(gotoSendCoinsPage())); + connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage())); + connect(historyAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage())); + connect(addressBookAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(addressBookAction, SIGNAL(triggered()), this, SLOT(gotoAddressBookPage())); + + quitAction = new QAction(QIcon(":/icons/quit"), tr("E&xit"), this); + quitAction->setStatusTip(tr("Quit application")); + quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); + quitAction->setMenuRole(QAction::QuitRole); + aboutAction = new QAction(QIcon(":/icons/bitcoin"), tr("&About CryptoLottoToken"), this); + aboutAction->setStatusTip(tr("Show information about CryptoLottoToken")); + aboutAction->setMenuRole(QAction::AboutRole); + aboutQtAction = new QAction(QIcon(":/trolltech/qmessagebox/images/qtlogo-64.png"), tr("About &Qt"), this); + aboutQtAction->setStatusTip(tr("Show information about Qt")); + aboutQtAction->setMenuRole(QAction::AboutQtRole); + optionsAction = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); + optionsAction->setStatusTip(tr("Modify configuration options for CryptoLottoToken")); + optionsAction->setMenuRole(QAction::PreferencesRole); + toggleHideAction = new QAction(QIcon(":/icons/bitcoin"), tr("&Show / Hide"), this); + toggleHideAction->setStatusTip(tr("Show or hide the main Window")); + + encryptWalletAction = new QAction(QIcon(":/icons/lock_closed"), tr("&Encrypt Wallet..."), this); + encryptWalletAction->setStatusTip(tr("Encrypt the private keys that belong to your wallet")); + encryptWalletAction->setCheckable(true); + backupWalletAction = new QAction(QIcon(":/icons/filesave"), tr("&Backup Wallet..."), this); + backupWalletAction->setStatusTip(tr("Backup wallet to another location")); + changePassphraseAction = new QAction(QIcon(":/icons/key"), tr("&Change Passphrase..."), this); + changePassphraseAction->setStatusTip(tr("Change the passphrase used for wallet encryption")); + signMessageAction = new QAction(QIcon(":/icons/edit"), tr("Sign &message..."), this); + signMessageAction->setStatusTip(tr("Sign messages with your CryptoLottoToken addresses to prove you own them")); + verifyMessageAction = new QAction(QIcon(":/icons/transaction_0"), tr("&Verify message..."), this); + verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified CryptoLottoToken addresses")); + + openRPCConsoleAction = new QAction(QIcon(":/icons/debugwindow"), tr("&Debug window"), this); + openRPCConsoleAction->setStatusTip(tr("Open debugging and diagnostic console")); + + connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); + connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked())); + connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt())); + connect(optionsAction, SIGNAL(triggered()), this, SLOT(optionsClicked())); + connect(toggleHideAction, SIGNAL(triggered()), this, SLOT(toggleHidden())); + connect(encryptWalletAction, SIGNAL(triggered(bool)), walletFrame, SLOT(encryptWallet(bool))); + connect(backupWalletAction, SIGNAL(triggered()), walletFrame, SLOT(backupWallet())); + connect(changePassphraseAction, SIGNAL(triggered()), walletFrame, SLOT(changePassphrase())); + connect(signMessageAction, SIGNAL(triggered()), this, SLOT(gotoSignMessageTab())); + connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(gotoVerifyMessageTab())); +} + +void BitcoinGUI::createMenuBar() +{ +#ifdef Q_OS_MAC + // Create a decoupled menu bar on Mac which stays even if the window is closed + appMenuBar = new QMenuBar(); +#else + // Get the main window's menu bar on other platforms + appMenuBar = menuBar(); +#endif + + // Configure the menus + QMenu *file = appMenuBar->addMenu(tr("&File")); + file->addAction(backupWalletAction); + file->addAction(signMessageAction); + file->addAction(verifyMessageAction); + file->addSeparator(); + file->addAction(quitAction); + + QMenu *settings = appMenuBar->addMenu(tr("&Settings")); + settings->addAction(encryptWalletAction); + settings->addAction(changePassphraseAction); + settings->addSeparator(); + settings->addAction(optionsAction); + + QMenu *help = appMenuBar->addMenu(tr("&Help")); + help->addAction(openRPCConsoleAction); + help->addSeparator(); + help->addAction(aboutAction); + help->addAction(aboutQtAction); +} + +void BitcoinGUI::createToolBars() +{ + QToolBar *toolbar = addToolBar(tr("Tabs toolbar")); + toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + toolbar->addAction(overviewAction); + toolbar->addAction(sendCoinsAction); + toolbar->addAction(receiveCoinsAction); + toolbar->addAction(historyAction); + toolbar->addAction(addressBookAction); +} + +void BitcoinGUI::setClientModel(ClientModel *clientModel) +{ + this->clientModel = clientModel; + if(clientModel) + { + // Replace some strings and icons, when using the testnet + if(clientModel->isTestNet()) + { + setWindowTitle(windowTitle() + QString(" ") + tr("[testnet]")); +#ifndef Q_OS_MAC + QApplication::setWindowIcon(QIcon(":icons/bitcoin_testnet")); + setWindowIcon(QIcon(":icons/bitcoin_testnet")); +#else + MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin_testnet")); +#endif + if(trayIcon) + { + // Just attach " [testnet]" to the existing tooltip + trayIcon->setToolTip(trayIcon->toolTip() + QString(" ") + tr("[testnet]")); + trayIcon->setIcon(QIcon(":/icons/toolbar_testnet")); + } + + toggleHideAction->setIcon(QIcon(":/icons/toolbar_testnet")); + aboutAction->setIcon(QIcon(":/icons/toolbar_testnet")); + } + + // Create system tray menu (or setup the dock menu) that late to prevent users from calling actions, + // while the client has not yet fully loaded + createTrayIconMenu(); + + // Keep up to date with client + setNumConnections(clientModel->getNumConnections()); + connect(clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); + + setNumBlocks(clientModel->getNumBlocks(), clientModel->getNumBlocksOfPeers()); + connect(clientModel, SIGNAL(numBlocksChanged(int,int)), this, SLOT(setNumBlocks(int,int))); + + // Receive and report messages from network/worker thread + connect(clientModel, SIGNAL(message(QString,QString,unsigned int)), this, SLOT(message(QString,QString,unsigned int))); + + rpcConsole->setClientModel(clientModel); + walletFrame->setClientModel(clientModel); + } +} + +bool BitcoinGUI::addWallet(const QString& name, WalletModel *walletModel) +{ + setWalletActionsEnabled(true); + return walletFrame->addWallet(name, walletModel); +} + +bool BitcoinGUI::setCurrentWallet(const QString& name) +{ + return walletFrame->setCurrentWallet(name); +} + +void BitcoinGUI::removeAllWallets() +{ + setWalletActionsEnabled(false); + walletFrame->removeAllWallets(); +} + +void BitcoinGUI::setWalletActionsEnabled(bool enabled) +{ + overviewAction->setEnabled(enabled); + sendCoinsAction->setEnabled(enabled); + receiveCoinsAction->setEnabled(enabled); + historyAction->setEnabled(enabled); + encryptWalletAction->setEnabled(enabled); + backupWalletAction->setEnabled(enabled); + changePassphraseAction->setEnabled(enabled); + signMessageAction->setEnabled(enabled); + verifyMessageAction->setEnabled(enabled); + addressBookAction->setEnabled(enabled); +} + +void BitcoinGUI::createTrayIcon() +{ +#ifndef Q_OS_MAC + trayIcon = new QSystemTrayIcon(this); + + trayIcon->setToolTip(tr("CryptoLottoToken client")); + trayIcon->setIcon(QIcon(":/icons/toolbar")); + trayIcon->show(); +#endif + + notificator = new Notificator(qApp->applicationName(), trayIcon, this); +} + +void BitcoinGUI::createTrayIconMenu() +{ + QMenu *trayIconMenu; +#ifndef Q_OS_MAC + // return if trayIcon is unset (only on non-Mac OSes) + if (!trayIcon) + return; + + trayIconMenu = new QMenu(this); + trayIcon->setContextMenu(trayIconMenu); + + connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); +#else + // Note: On Mac, the dock icon is used to provide the tray's functionality. + MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance(); + dockIconHandler->setMainWindow((QMainWindow *)this); + trayIconMenu = dockIconHandler->dockMenu(); +#endif + + // Configuration of the tray icon (or dock icon) icon menu + trayIconMenu->addAction(toggleHideAction); + trayIconMenu->addSeparator(); + trayIconMenu->addAction(sendCoinsAction); + trayIconMenu->addAction(receiveCoinsAction); + trayIconMenu->addSeparator(); + trayIconMenu->addAction(signMessageAction); + trayIconMenu->addAction(verifyMessageAction); + trayIconMenu->addSeparator(); + trayIconMenu->addAction(optionsAction); + trayIconMenu->addAction(openRPCConsoleAction); +#ifndef Q_OS_MAC // This is built-in on Mac + trayIconMenu->addSeparator(); + trayIconMenu->addAction(quitAction); +#endif +} + +#ifndef Q_OS_MAC +void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason) +{ + if(reason == QSystemTrayIcon::Trigger) + { + // Click on system tray icon triggers show/hide of the main window + toggleHideAction->trigger(); + } +} +#endif + +void BitcoinGUI::saveWindowGeometry() +{ + QSettings settings; + settings.setValue("nWindowPos", pos()); + settings.setValue("nWindowSize", size()); +} + +void BitcoinGUI::restoreWindowGeometry() +{ + QSettings settings; + QPoint pos = settings.value("nWindowPos").toPoint(); + QSize size = settings.value("nWindowSize", QSize(850, 550)).toSize(); + if (!pos.x() && !pos.y()) + { + QRect screen = QApplication::desktop()->screenGeometry(); + pos.setX((screen.width()-size.width())/2); + pos.setY((screen.height()-size.height())/2); + } + resize(size); + move(pos); +} + +void BitcoinGUI::optionsClicked() +{ + if(!clientModel || !clientModel->getOptionsModel()) + return; + OptionsDialog dlg; + dlg.setModel(clientModel->getOptionsModel()); + dlg.exec(); +} + +void BitcoinGUI::aboutClicked() +{ + AboutDialog dlg; + dlg.setModel(clientModel); + dlg.exec(); +} + +void BitcoinGUI::gotoOverviewPage() +{ + if (walletFrame) walletFrame->gotoOverviewPage(); +} + +void BitcoinGUI::gotoHistoryPage() +{ + if (walletFrame) walletFrame->gotoHistoryPage(); +} + +void BitcoinGUI::gotoAddressBookPage() +{ + if (walletFrame) walletFrame->gotoAddressBookPage(); +} + +void BitcoinGUI::gotoReceiveCoinsPage() +{ + if (walletFrame) walletFrame->gotoReceiveCoinsPage(); +} + +void BitcoinGUI::gotoSendCoinsPage(QString addr) +{ + if (walletFrame) walletFrame->gotoSendCoinsPage(addr); +} + +void BitcoinGUI::gotoSignMessageTab(QString addr) +{ + if (walletFrame) walletFrame->gotoSignMessageTab(addr); +} + +void BitcoinGUI::gotoVerifyMessageTab(QString addr) +{ + if (walletFrame) walletFrame->gotoVerifyMessageTab(addr); +} + +void BitcoinGUI::setNumConnections(int count) +{ + QString icon; + switch(count) + { + case 0: icon = ":/icons/connect_0"; break; + case 1: case 2: case 3: icon = ":/icons/connect_1"; break; + case 4: case 5: case 6: icon = ":/icons/connect_2"; break; + case 7: case 8: case 9: icon = ":/icons/connect_3"; break; + default: icon = ":/icons/connect_4"; break; + } + labelConnectionsIcon->setPixmap(QIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelConnectionsIcon->setToolTip(tr("%n active connection(s) to CryptoLottoToken network", "", count)); +} + +void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) +{ + // Prevent orphan statusbar messages (e.g. hover Quit in main menu, wait until chain-sync starts -> garbelled text) + statusBar()->clearMessage(); + + // Acquire current block source + enum BlockSource blockSource = clientModel->getBlockSource(); + switch (blockSource) { + case BLOCK_SOURCE_NETWORK: + progressBarLabel->setText(tr("Synchronizing with network...")); + break; + case BLOCK_SOURCE_DISK: + progressBarLabel->setText(tr("Importing blocks from disk...")); + break; + case BLOCK_SOURCE_REINDEX: + progressBarLabel->setText(tr("Reindexing blocks on disk...")); + break; + case BLOCK_SOURCE_NONE: + // Case: not Importing, not Reindexing and no network connection + progressBarLabel->setText(tr("No block source available...")); + break; + } + + QString tooltip; + + QDateTime lastBlockDate = clientModel->getLastBlockDate(); + QDateTime currentDate = QDateTime::currentDateTime(); + int secs = lastBlockDate.secsTo(currentDate); + + if(count < nTotalBlocks) + { + tooltip = tr("Processed %1 of %2 (estimated) blocks of transaction history.").arg(count).arg(nTotalBlocks); + } + else + { + tooltip = tr("Processed %1 blocks of transaction history.").arg(count); + } + + // Set icon state: spinning if catching up, tick otherwise + if(secs < 90*60 && count >= nTotalBlocks) + { + tooltip = tr("Up to date") + QString(".
") + tooltip; + labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + + walletFrame->showOutOfSyncWarning(false); + + progressBarLabel->setVisible(false); + progressBar->setVisible(false); + } + else + { + // Represent time from last generated block in human readable text + QString timeBehindText; + if(secs < 48*60*60) + { + timeBehindText = tr("%n hour(s)","",secs/(60*60)); + } + else if(secs < 14*24*60*60) + { + timeBehindText = tr("%n day(s)","",secs/(24*60*60)); + } + else + { + timeBehindText = tr("%n week(s)","",secs/(7*24*60*60)); + } + + progressBarLabel->setVisible(true); + progressBar->setFormat(tr("%1 behind").arg(timeBehindText)); + progressBar->setMaximum(1000000000); + progressBar->setValue(clientModel->getVerificationProgress() * 1000000000.0 + 0.5); + progressBar->setVisible(true); + + tooltip = tr("Catching up...") + QString("
") + tooltip; + labelBlocksIcon->setMovie(syncIconMovie); + if(count != prevBlocks) + syncIconMovie->jumpToNextFrame(); + prevBlocks = count; + + walletFrame->showOutOfSyncWarning(true); + + tooltip += QString("
"); + tooltip += tr("Last received block was generated %1 ago.").arg(timeBehindText); + tooltip += QString("
"); + tooltip += tr("Transactions after this will not yet be visible."); + } + + // Don't word-wrap this (fixed-width) tooltip + tooltip = QString("") + tooltip + QString(""); + + labelBlocksIcon->setToolTip(tooltip); + progressBarLabel->setToolTip(tooltip); + progressBar->setToolTip(tooltip); +} + +void BitcoinGUI::message(const QString &title, const QString &message, unsigned int style, bool *ret) +{ + QString strTitle = tr("CryptoLottoToken"); // default title + // Default to information icon + int nMBoxIcon = QMessageBox::Information; + int nNotifyIcon = Notificator::Information; + + // Override title based on style + QString msgType; + switch (style) { + case CClientUIInterface::MSG_ERROR: + msgType = tr("Error"); + break; + case CClientUIInterface::MSG_WARNING: + msgType = tr("Warning"); + break; + case CClientUIInterface::MSG_INFORMATION: + msgType = tr("Information"); + break; + default: + msgType = title; // Use supplied title + } + if (!msgType.isEmpty()) + strTitle += " - " + msgType; + + // Check for error/warning icon + if (style & CClientUIInterface::ICON_ERROR) { + nMBoxIcon = QMessageBox::Critical; + nNotifyIcon = Notificator::Critical; + } + else if (style & CClientUIInterface::ICON_WARNING) { + nMBoxIcon = QMessageBox::Warning; + nNotifyIcon = Notificator::Warning; + } + + // Display message + if (style & CClientUIInterface::MODAL) { + // Check for buttons, use OK as default, if none was supplied + QMessageBox::StandardButton buttons; + if (!(buttons = (QMessageBox::StandardButton)(style & CClientUIInterface::BTN_MASK))) + buttons = QMessageBox::Ok; + + QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons); + int r = mBox.exec(); + if (ret != NULL) + *ret = r == QMessageBox::Ok; + } + else + notificator->notify((Notificator::Class)nNotifyIcon, strTitle, message); +} + +void BitcoinGUI::changeEvent(QEvent *e) +{ + QMainWindow::changeEvent(e); +#ifndef Q_OS_MAC // Ignored on Mac + if(e->type() == QEvent::WindowStateChange) + { + if(clientModel && clientModel->getOptionsModel()->getMinimizeToTray()) + { + QWindowStateChangeEvent *wsevt = static_cast(e); + if(!(wsevt->oldState() & Qt::WindowMinimized) && isMinimized()) + { + QTimer::singleShot(0, this, SLOT(hide())); + e->ignore(); + } + } + } +#endif +} + +void BitcoinGUI::closeEvent(QCloseEvent *event) +{ + if(clientModel) + { +#ifndef Q_OS_MAC // Ignored on Mac + if(!clientModel->getOptionsModel()->getMinimizeToTray() && + !clientModel->getOptionsModel()->getMinimizeOnClose()) + { + QApplication::quit(); + } +#endif + } + QMainWindow::closeEvent(event); +} + +void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) +{ + QString strMessage = tr("This transaction is over the size limit. You can still send it for a fee of %1, " + "which goes to the nodes that process your transaction and helps to support the network. " + "Do you want to pay the fee?").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nFeeRequired)); + QMessageBox::StandardButton retval = QMessageBox::question( + this, tr("Confirm transaction fee"), strMessage, + QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes); + *payFee = (retval == QMessageBox::Yes); +} + +void BitcoinGUI::incomingTransaction(const QString& date, int unit, qint64 amount, const QString& type, const QString& address) +{ + // On new transaction, make an info balloon + message((amount)<0 ? tr("Sent transaction") : tr("Incoming transaction"), + tr("Date: %1\n" + "Amount: %2\n" + "Type: %3\n" + "Address: %4\n") + .arg(date) + .arg(BitcoinUnits::formatWithUnit(unit, amount, true)) + .arg(type) + .arg(address), CClientUIInterface::MSG_INFORMATION); +} + +void BitcoinGUI::dragEnterEvent(QDragEnterEvent *event) +{ + // Accept only URIs + if(event->mimeData()->hasUrls()) + event->acceptProposedAction(); +} + +void BitcoinGUI::dropEvent(QDropEvent *event) +{ + if(event->mimeData()->hasUrls()) + { + int nValidUrisFound = 0; + QList uris = event->mimeData()->urls(); + foreach(const QUrl &uri, uris) + { + if (walletFrame->handleURI(uri.toString())) + nValidUrisFound++; + } + + // if valid URIs were found + if (nValidUrisFound) + walletFrame->gotoSendCoinsPage(); + else + message(tr("URI handling"), tr("URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters."), + CClientUIInterface::ICON_WARNING); + } + + event->acceptProposedAction(); +} + +bool BitcoinGUI::eventFilter(QObject *object, QEvent *event) +{ + // Catch status tip events + if (event->type() == QEvent::StatusTip) + { + // Prevent adding text from setStatusTip(), if we currently use the status bar for displaying other stuff + if (progressBarLabel->isVisible() || progressBar->isVisible()) + return true; + } + return QMainWindow::eventFilter(object, event); +} + +void BitcoinGUI::handleURI(QString strURI) +{ + // URI has to be valid + if (!walletFrame->handleURI(strURI)) + message(tr("URI handling"), tr("URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters."), + CClientUIInterface::ICON_WARNING); +} + +void BitcoinGUI::setEncryptionStatus(int status) +{ + switch(status) + { + case WalletModel::Unencrypted: + labelEncryptionIcon->hide(); + encryptWalletAction->setChecked(false); + changePassphraseAction->setEnabled(false); + encryptWalletAction->setEnabled(true); + break; + case WalletModel::Unlocked: + labelEncryptionIcon->show(); + labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently unlocked")); + encryptWalletAction->setChecked(true); + changePassphraseAction->setEnabled(true); + encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported + break; + case WalletModel::Locked: + labelEncryptionIcon->show(); + labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_closed").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently locked")); + encryptWalletAction->setChecked(true); + changePassphraseAction->setEnabled(true); + encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported + break; + } +} + +void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden) +{ + // activateWindow() (sometimes) helps with keyboard focus on Windows + if (isHidden()) + { + show(); + activateWindow(); + } + else if (isMinimized()) + { + showNormal(); + activateWindow(); + } + else if (GUIUtil::isObscured(this)) + { + raise(); + activateWindow(); + } + else if(fToggleHidden) + hide(); +} + +void BitcoinGUI::toggleHidden() +{ + showNormalIfMinimized(true); +} + +void BitcoinGUI::detectShutdown() +{ + if (ShutdownRequested()) + QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection); +} diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h new file mode 100644 index 0000000..5656af4 --- /dev/null +++ b/src/qt/bitcoingui.h @@ -0,0 +1,200 @@ +#ifndef BITCOINGUI_H +#define BITCOINGUI_H + +#include +#include +#include + +class TransactionTableModel; +class WalletFrame; +class WalletView; +class ClientModel; +class WalletModel; +class WalletStack; +class TransactionView; +class OverviewPage; +class AddressBookPage; +class SendCoinsDialog; +class SignVerifyMessageDialog; +class Notificator; +class RPCConsole; + +class CWallet; + +QT_BEGIN_NAMESPACE +class QLabel; +class QModelIndex; +class QProgressBar; +class QStackedWidget; +class QUrl; +class QListWidget; +class QPushButton; +class QAction; +QT_END_NAMESPACE + +/** + Bitcoin GUI main class. This class represents the main window of the Bitcoin UI. It communicates with both the client and + wallet models to give the user an up-to-date view of the current core state. +*/ +class BitcoinGUI : public QMainWindow +{ + Q_OBJECT + +public: + static const QString DEFAULT_WALLET; + + explicit BitcoinGUI(QWidget *parent = 0); + ~BitcoinGUI(); + + /** Set the client model. + The client model represents the part of the core that communicates with the P2P network, and is wallet-agnostic. + */ + void setClientModel(ClientModel *clientModel); + /** Set the wallet model. + The wallet model represents a bitcoin wallet, and offers access to the list of transactions, address book and sending + functionality. + */ + + bool addWallet(const QString& name, WalletModel *walletModel); + bool setCurrentWallet(const QString& name); + + void removeAllWallets(); + + /** Used by WalletView to allow access to needed QActions */ + // Todo: Use Qt signals for these + QAction * getOverviewAction() { return overviewAction; } + QAction * getHistoryAction() { return historyAction; } + QAction * getAddressBookAction() { return addressBookAction; } + QAction * getReceiveCoinsAction() { return receiveCoinsAction; } + QAction * getSendCoinsAction() { return sendCoinsAction; } + +protected: + void changeEvent(QEvent *e); + void closeEvent(QCloseEvent *event); + void dragEnterEvent(QDragEnterEvent *event); + void dropEvent(QDropEvent *event); + bool eventFilter(QObject *object, QEvent *event); + +private: + ClientModel *clientModel; + WalletFrame *walletFrame; + + QLabel *labelEncryptionIcon; + QLabel *labelConnectionsIcon; + QLabel *labelBlocksIcon; + QLabel *progressBarLabel; + QProgressBar *progressBar; + + QMenuBar *appMenuBar; + QAction *overviewAction; + QAction *historyAction; + QAction *quitAction; + QAction *sendCoinsAction; + QAction *addressBookAction; + QAction *signMessageAction; + QAction *verifyMessageAction; + QAction *aboutAction; + QAction *receiveCoinsAction; + QAction *optionsAction; + QAction *toggleHideAction; + QAction *encryptWalletAction; + QAction *backupWalletAction; + QAction *changePassphraseAction; + QAction *aboutQtAction; + QAction *openRPCConsoleAction; + + QSystemTrayIcon *trayIcon; + Notificator *notificator; + TransactionView *transactionView; + RPCConsole *rpcConsole; + + QMovie *syncIconMovie; + /** Keep track of previous number of blocks, to detect progress */ + int prevBlocks; + + /** Create the main UI actions. */ + void createActions(); + /** Create the menu bar and sub-menus. */ + void createMenuBar(); + /** Create the toolbars */ + void createToolBars(); + /** Create system tray icon and notification */ + void createTrayIcon(); + /** Create system tray menu (or setup the dock menu) */ + void createTrayIconMenu(); + /** Save window size and position */ + void saveWindowGeometry(); + /** Restore window size and position */ + void restoreWindowGeometry(); + /** Enable or disable all wallet-related actions */ + void setWalletActionsEnabled(bool enabled); + +public slots: + /** Set number of connections shown in the UI */ + void setNumConnections(int count); + /** Set number of blocks shown in the UI */ + void setNumBlocks(int count, int nTotalBlocks); + /** Set the encryption status as shown in the UI. + @param[in] status current encryption status + @see WalletModel::EncryptionStatus + */ + void setEncryptionStatus(int status); + + /** Notify the user of an event from the core network or transaction handling code. + @param[in] title the message box / notification title + @param[in] message the displayed text + @param[in] style modality and style definitions (icon and used buttons - buttons only for message boxes) + @see CClientUIInterface::MessageBoxFlags + @param[in] ret pointer to a bool that will be modified to whether Ok was clicked (modal only) + */ + void message(const QString &title, const QString &message, unsigned int style, bool *ret = NULL); + /** Asks the user whether to pay the transaction fee or to cancel the transaction. + It is currently not possible to pass a return value to another thread through + BlockingQueuedConnection, so an indirected pointer is used. + https://bugreports.qt-project.org/browse/QTBUG-10440 + + @param[in] nFeeRequired the required fee + @param[out] payFee true to pay the fee, false to not pay the fee + */ + void askFee(qint64 nFeeRequired, bool *payFee); + void handleURI(QString strURI); + + /** Show incoming transaction notification for new transactions. */ + void incomingTransaction(const QString& date, int unit, qint64 amount, const QString& type, const QString& address); + +private slots: + /** Switch to overview (home) page */ + void gotoOverviewPage(); + /** Switch to history (transactions) page */ + void gotoHistoryPage(); + /** Switch to address book page */ + void gotoAddressBookPage(); + /** Switch to receive coins page */ + void gotoReceiveCoinsPage(); + /** Switch to send coins page */ + void gotoSendCoinsPage(QString addr = ""); + + /** Show Sign/Verify Message dialog and switch to sign message tab */ + void gotoSignMessageTab(QString addr = ""); + /** Show Sign/Verify Message dialog and switch to verify message tab */ + void gotoVerifyMessageTab(QString addr = ""); + + /** Show configuration dialog */ + void optionsClicked(); + /** Show about dialog */ + void aboutClicked(); +#ifndef Q_OS_MAC + /** Handle tray icon clicked */ + void trayIconActivated(QSystemTrayIcon::ActivationReason reason); +#endif + + /** Show window if hidden, unminimize when minimized, rise when obscured or show if hidden and fToggleHidden is true */ + void showNormalIfMinimized(bool fToggleHidden = false); + /** Simply calls showNormalIfMinimized(true) for use in SLOT() macro */ + void toggleHidden(); + + /** called by a timer to check if fRequestShutdown has been set **/ + void detectShutdown(); +}; + +#endif // BITCOINGUI_H diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp new file mode 100644 index 0000000..459ac61 --- /dev/null +++ b/src/qt/bitcoinstrings.cpp @@ -0,0 +1,217 @@ +#include +// Automatically generated by extract_strings.py +#ifdef __GNUC__ +#define UNUSED __attribute__((unused)) +#else +#define UNUSED +#endif +static const char UNUSED *bitcoin_strings[] = { +QT_TRANSLATE_NOOP("bitcoin-core", "" +"%s, you must set a rpcpassword in the configuration file:\n" +"%s\n" +"It is recommended you use the following random password:\n" +"rpcuser=CryptoLottoTokenrpc\n" +"rpcpassword=%s\n" +"(you do not need to remember this password)\n" +"The username and password MUST NOT be the same.\n" +"If the file does not exist, create it with owner-readable-only file " +"permissions.\n" +"It is also recommended to set alertnotify so you are notified of problems;\n" +"for example: alertnotify=echo %%s | mail -s \"CryptoLottoToken Alert\" admin@foo.com\n"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:" +"@STRENGTH)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"An error occurred while setting up the RPC port %u for listening on IPv4: %s"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"An error occurred while setting up the RPC port %u for listening on IPv6, " +"falling back to IPv4: %s"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Bind to given address and always listen on it. Use [host]:port notation for " +"IPv6"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already " +"running."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"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."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Error: This transaction requires a transaction fee of at least %s because of " +"its amount, complexity, or use of recently received funds!"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Execute command when a relevant alert is received (%s in cmd is replaced by " +"message)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Execute command when a wallet transaction changes (%s in cmd is replaced by " +"TxID)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Execute command when the best block changes (%s in cmd is replaced by block " +"hash)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Listen for JSON-RPC connections on (default: 9154 or testnet: 19154)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Number of seconds to keep misbehaving peers from reconnecting (default: " +"86400)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Set maximum size of high-priority/low-fee transactions in bytes (default: " +"27000)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Set the number of script verification threads (up to 16, 0 = auto, <0 = " +"leave that many cores free, default: 0)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"This is a pre-release test build - use at your own risk - do not use for " +"mining or merchant applications"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Unable to bind to %s on this computer. CryptoLottoToken is probably already running."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Warning: -paytxfee is set very high! This is the transaction fee you will " +"pay if you send a transaction."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Warning: Displayed transactions may not be correct! You may need to upgrade, " +"or other nodes may need to upgrade."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Warning: Please check that your computer's date and time are correct! If " +"your clock is wrong CryptoLottoToken will not work properly."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Warning: error reading wallet.dat! All keys read correctly, but transaction " +"data or address book entries might be missing or incorrect."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as " +"wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect " +"you should restore from a backup."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"When creating transactions, ignore inputs with value less than this " +"(default: 1000000)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"You must set rpcpassword= in the configuration file:\n" +"%s\n" +"If the file does not exist, create it with owner-readable-only file " +"permissions."), +QT_TRANSLATE_NOOP("bitcoin-core", "Accept command line and JSON-RPC commands"), +QT_TRANSLATE_NOOP("bitcoin-core", "Accept connections from outside (default: 1 if no -proxy or -connect)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Add a node to connect to and attempt to keep the connection open"), +QT_TRANSLATE_NOOP("bitcoin-core", "Allow DNS lookups for -addnode, -seednode and -connect"), +QT_TRANSLATE_NOOP("bitcoin-core", "Allow JSON-RPC connections from specified IP address"), +QT_TRANSLATE_NOOP("bitcoin-core", "Allow peers to set bloom filters (default: 1)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Attempt to recover private keys from a corrupt wallet.dat"), +QT_TRANSLATE_NOOP("bitcoin-core", "Block creation options:"), +QT_TRANSLATE_NOOP("bitcoin-core", "Cannot downgrade wallet"), +QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -bind address: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -externalip address: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Cannot write default address"), +QT_TRANSLATE_NOOP("bitcoin-core", "Connect only to the specified node(s)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Connect through socks proxy"), +QT_TRANSLATE_NOOP("bitcoin-core", "Connect to a node to retrieve peer addresses, and disconnect"), +QT_TRANSLATE_NOOP("bitcoin-core", "Corrupted block database detected"), +QT_TRANSLATE_NOOP("bitcoin-core", "Discover own IP address (default: 1 when listening and no -externalip)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Do you want to rebuild the block database now?"), +QT_TRANSLATE_NOOP("bitcoin-core", "CryptoLottoToken version"), +QT_TRANSLATE_NOOP("bitcoin-core", "Done loading"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error initializing block database"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error initializing wallet database environment %s!"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error loading block database"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat: Wallet corrupted"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error opening block database"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error: Disk space is low!"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error: Wallet locked, unable to create transaction!"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error: system error: "), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to listen on any port. Use -listen=0 if you want this."), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to read block info"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to read block"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to sync block index"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write block index"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write block info"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write block"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write file info"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write to coin database"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write transaction index"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write undo data"), +QT_TRANSLATE_NOOP("bitcoin-core", "Fee per KB to add to transactions you send"), +QT_TRANSLATE_NOOP("bitcoin-core", "Find peers using DNS lookup (default: 1 unless -connect)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Find peers using internet relay chat (default: 0)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Generate coins (default: 0)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Get help for a command"), +QT_TRANSLATE_NOOP("bitcoin-core", "How many blocks to check at startup (default: 288, 0 = all)"), +QT_TRANSLATE_NOOP("bitcoin-core", "How thorough the block verification is (0-4, default: 3)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Imports blocks from external blk000??.dat file"), +QT_TRANSLATE_NOOP("bitcoin-core", "Incorrect or no genesis block found. Wrong datadir for network?"), +QT_TRANSLATE_NOOP("bitcoin-core", "Information"), +QT_TRANSLATE_NOOP("bitcoin-core", "Insufficient funds"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -proxy address: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -tor address: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -mininput=: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -minrelaytxfee=: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -mintxfee=: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee=: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount"), +QT_TRANSLATE_NOOP("bitcoin-core", "List commands"), +QT_TRANSLATE_NOOP("bitcoin-core", "Listen for connections on (default: 9157 or testnet: 19157)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Loading addresses..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Loading block index..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Loading wallet..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Maintain a full transaction index (default: 0)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Maintain at most connections to peers (default: 125)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Maximum per-connection receive buffer, *1000 bytes (default: 5000)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Maximum per-connection send buffer, *1000 bytes (default: 1000)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Not enough file descriptors available."), +QT_TRANSLATE_NOOP("bitcoin-core", "Only accept block chain matching built-in checkpoints (default: 1)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Only connect to nodes in network (IPv4, IPv6 or Tor)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Options:"), +QT_TRANSLATE_NOOP("bitcoin-core", "Output extra debugging information. Implies all other -debug* options"), +QT_TRANSLATE_NOOP("bitcoin-core", "Output extra network debugging information"), +QT_TRANSLATE_NOOP("bitcoin-core", "Password for JSON-RPC connections"), +QT_TRANSLATE_NOOP("bitcoin-core", "Prepend debug output with timestamp (default: 1)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Rebuild block chain index from current blk000??.dat files"), +QT_TRANSLATE_NOOP("bitcoin-core", "Rescan the block chain for missing wallet transactions"), +QT_TRANSLATE_NOOP("bitcoin-core", "Rescanning..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Run in the background as a daemon and accept commands"), +QT_TRANSLATE_NOOP("bitcoin-core", "SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Select the version of socks proxy to use (4-5, default: 5)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Send command to -server or CryptoLottoTokend"), +QT_TRANSLATE_NOOP("bitcoin-core", "Send commands to node running on (default: 127.0.0.1)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Send trace/debug info to console instead of debug.log file"), +QT_TRANSLATE_NOOP("bitcoin-core", "Send trace/debug info to debugger"), +QT_TRANSLATE_NOOP("bitcoin-core", "Server certificate file (default: server.cert)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Server private key (default: server.pem)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Set database cache size in megabytes (default: 25)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Set key pool size to (default: 100)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Set maximum block size in bytes (default: 250000)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Set minimum block size in bytes (default: 0)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Set the number of threads to service RPC calls (default: 4)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Shrink debug.log file on client startup (default: 1 when no -debug)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Signing transaction failed"), +QT_TRANSLATE_NOOP("bitcoin-core", "Specify configuration file (default: CryptoLottoToken.conf)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Specify connection timeout in milliseconds (default: 5000)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Specify data directory"), +QT_TRANSLATE_NOOP("bitcoin-core", "Specify pid file (default: CryptoLottoTokend.pid)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Specify your own public address"), +QT_TRANSLATE_NOOP("bitcoin-core", "System error: "), +QT_TRANSLATE_NOOP("bitcoin-core", "This help message"), +QT_TRANSLATE_NOOP("bitcoin-core", "Threshold for disconnecting misbehaving peers (default: 100)"), +QT_TRANSLATE_NOOP("bitcoin-core", "To use the %s option"), +QT_TRANSLATE_NOOP("bitcoin-core", "Transaction amount too small"), +QT_TRANSLATE_NOOP("bitcoin-core", "Transaction amounts must be positive"), +QT_TRANSLATE_NOOP("bitcoin-core", "Transaction too large"), +QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer (bind returned error %d, %s)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Unknown -socks proxy version requested: %i"), +QT_TRANSLATE_NOOP("bitcoin-core", "Unknown network specified in -onlynet: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Upgrade wallet to latest format"), +QT_TRANSLATE_NOOP("bitcoin-core", "Usage:"), +QT_TRANSLATE_NOOP("bitcoin-core", "Use OpenSSL (https) for JSON-RPC connections"), +QT_TRANSLATE_NOOP("bitcoin-core", "Use UPnP to map the listening port (default: 0)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Use UPnP to map the listening port (default: 1 when listening)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Use proxy to reach tor hidden services (default: same as -proxy)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Use the test network"), +QT_TRANSLATE_NOOP("bitcoin-core", "Username for JSON-RPC connections"), +QT_TRANSLATE_NOOP("bitcoin-core", "Verifying blocks..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Verifying wallet..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Wallet needed to be rewritten: restart CryptoLottoToken to complete"), +QT_TRANSLATE_NOOP("bitcoin-core", "Warning"), +QT_TRANSLATE_NOOP("bitcoin-core", "Warning: This version is obsolete, upgrade required!"), +QT_TRANSLATE_NOOP("bitcoin-core", "You need to rebuild the database using -reindex to change -txindex"), +QT_TRANSLATE_NOOP("bitcoin-core", "wallet.dat corrupt, salvage failed"), +}; diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp new file mode 100644 index 0000000..7dc1794 --- /dev/null +++ b/src/qt/bitcoinunits.cpp @@ -0,0 +1,181 @@ +#include "bitcoinunits.h" + +#include + +BitcoinUnits::BitcoinUnits(QObject *parent): + QAbstractListModel(parent), + unitlist(availableUnits()) +{ +} + +QList BitcoinUnits::availableUnits() +{ + QList unitlist; + unitlist.append(BTC); + unitlist.append(mBTC); + unitlist.append(uBTC); + return unitlist; +} + +bool BitcoinUnits::valid(int unit) +{ + switch(unit) + { + case BTC: + case mBTC: + case uBTC: + return true; + default: + return false; + } +} + +QString BitcoinUnits::name(int unit) +{ + switch(unit) + { + case BTC: return QString("CLT"); + case mBTC: return QString("mCLT"); + case uBTC: return QString::fromUtf8("μCLT"); + default: return QString("???"); + } +} + +QString BitcoinUnits::description(int unit) +{ + switch(unit) + { + case BTC: return QString("CryptoLottoTokens"); + case mBTC: return QString("Milli-CryptoLottoTokens (1 / 1,000)"); + case uBTC: return QString("Micro-CryptoLottoTokens (1 / 1,000,000)"); + default: return QString("???"); + } +} + +qint64 BitcoinUnits::factor(int unit) +{ + switch(unit) + { + case BTC: return 100000000; + case mBTC: return 100000; + case uBTC: return 100; + default: return 100000000; + } +} + +int BitcoinUnits::amountDigits(int unit) +{ + switch(unit) + { + case BTC: return 8; // 21,000,000 (# digits, without commas) + case mBTC: return 11; // 21,000,000,000 + case uBTC: return 14; // 21,000,000,000,000 + default: return 0; + } +} + +int BitcoinUnits::decimals(int unit) +{ + switch(unit) + { + case BTC: return 8; + case mBTC: return 5; + case uBTC: return 2; + default: return 0; + } +} + +QString BitcoinUnits::format(int unit, qint64 n, bool fPlus) +{ + // Note: not using straight sprintf here because we do NOT want + // localized number formatting. + if(!valid(unit)) + return QString(); // Refuse to format invalid unit + qint64 coin = factor(unit); + int num_decimals = decimals(unit); + qint64 n_abs = (n > 0 ? n : -n); + qint64 quotient = n_abs / coin; + qint64 remainder = n_abs % coin; + QString quotient_str = QString::number(quotient); + QString remainder_str = QString::number(remainder).rightJustified(num_decimals, '0'); + + // Right-trim excess zeros after the decimal point + int nTrim = 0; + for (int i = remainder_str.size()-1; i>=2 && (remainder_str.at(i) == '0'); --i) + ++nTrim; + remainder_str.chop(nTrim); + + if (n < 0) + quotient_str.insert(0, '-'); + else if (fPlus && n > 0) + quotient_str.insert(0, '+'); + return quotient_str + QString(".") + remainder_str; +} + +QString BitcoinUnits::formatWithUnit(int unit, qint64 amount, bool plussign) +{ + return format(unit, amount, plussign) + QString(" ") + name(unit); +} + +bool BitcoinUnits::parse(int unit, const QString &value, qint64 *val_out) +{ + if(!valid(unit) || value.isEmpty()) + return false; // Refuse to parse invalid unit or empty string + int num_decimals = decimals(unit); + QStringList parts = value.split("."); + + if(parts.size() > 2) + { + return false; // More than one dot + } + QString whole = parts[0]; + QString decimals; + + if(parts.size() > 1) + { + decimals = parts[1]; + } + if(decimals.size() > num_decimals) + { + return false; // Exceeds max precision + } + bool ok = false; + QString str = whole + decimals.leftJustified(num_decimals, '0'); + + if(str.size() > 18) + { + return false; // Longer numbers will exceed 63 bits + } + qint64 retvalue = str.toLongLong(&ok); + if(val_out) + { + *val_out = retvalue; + } + return ok; +} + +int BitcoinUnits::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return unitlist.size(); +} + +QVariant BitcoinUnits::data(const QModelIndex &index, int role) const +{ + int row = index.row(); + if(row >= 0 && row < unitlist.size()) + { + Unit unit = unitlist.at(row); + switch(role) + { + case Qt::EditRole: + case Qt::DisplayRole: + return QVariant(name(unit)); + case Qt::ToolTipRole: + return QVariant(description(unit)); + case UnitRole: + return QVariant(static_cast(unit)); + } + } + return QVariant(); +} diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h new file mode 100644 index 0000000..6e96cef --- /dev/null +++ b/src/qt/bitcoinunits.h @@ -0,0 +1,69 @@ +#ifndef BITCOINUNITS_H +#define BITCOINUNITS_H + +#include +#include + +/** Bitcoin unit definitions. Encapsulates parsing and formatting + and serves as list model for drop-down selection boxes. +*/ +class BitcoinUnits: public QAbstractListModel +{ + Q_OBJECT + +public: + explicit BitcoinUnits(QObject *parent); + + /** Bitcoin units. + @note Source: https://en.bitcoin.it/wiki/Units . Please add only sensible ones + */ + enum Unit + { + BTC, + mBTC, + uBTC + }; + + //! @name Static API + //! Unit conversion and formatting + ///@{ + + //! Get list of units, for drop-down box + static QList availableUnits(); + //! Is unit ID valid? + static bool valid(int unit); + //! Short name + static QString name(int unit); + //! Longer description + static QString description(int unit); + //! Number of Satoshis (1e-8) per unit + static qint64 factor(int unit); + //! Number of amount digits (to represent max number of coins) + static int amountDigits(int unit); + //! Number of decimals left + static int decimals(int unit); + //! Format as string + static QString format(int unit, qint64 amount, bool plussign=false); + //! Format as string (with unit) + static QString formatWithUnit(int unit, qint64 amount, bool plussign=false); + //! Parse string to coin amount + static bool parse(int unit, const QString &value, qint64 *val_out); + ///@} + + //! @name AbstractListModel implementation + //! List model for unit drop-down selection box. + ///@{ + enum RoleIndex { + /** Unit identifier */ + UnitRole = Qt::UserRole + }; + int rowCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + ///@} + +private: + QList unitlist; +}; +typedef BitcoinUnits::Unit BitcoinUnit; + +#endif // BITCOINUNITS_H diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp new file mode 100644 index 0000000..e8d99a8 --- /dev/null +++ b/src/qt/clientmodel.cpp @@ -0,0 +1,209 @@ +#include "clientmodel.h" + +#include "guiconstants.h" +#include "optionsmodel.h" +#include "addresstablemodel.h" +#include "transactiontablemodel.h" + +#include "alert.h" +#include "main.h" +#include "checkpoints.h" +#include "ui_interface.h" + +#include +#include + +static const int64 nClientStartupTime = GetTime(); + +ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) : + QObject(parent), optionsModel(optionsModel), + cachedNumBlocks(0), cachedNumBlocksOfPeers(0), + cachedReindexing(0), cachedImporting(0), + numBlocksAtStartup(-1), pollTimer(0) +{ + pollTimer = new QTimer(this); + pollTimer->setInterval(MODEL_UPDATE_DELAY); + pollTimer->start(); + connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer())); + + subscribeToCoreSignals(); +} + +ClientModel::~ClientModel() +{ + unsubscribeFromCoreSignals(); +} + +int ClientModel::getNumConnections() const +{ + return vNodes.size(); +} + +int ClientModel::getNumBlocks() const +{ + return nBestHeight; +} + +int ClientModel::getNumBlocksAtStartup() +{ + if (numBlocksAtStartup == -1) numBlocksAtStartup = getNumBlocks(); + return numBlocksAtStartup; +} + +QDateTime ClientModel::getLastBlockDate() const +{ + if (pindexBest) + return QDateTime::fromTime_t(pindexBest->GetBlockTime()); + else if(!isTestNet()) + return QDateTime::fromTime_t(1231006505); // Genesis block's time + else + return QDateTime::fromTime_t(1296688602); // Genesis block's time (testnet) +} + +double ClientModel::getVerificationProgress() const +{ + return Checkpoints::GuessVerificationProgress(pindexBest); +} + +void ClientModel::updateTimer() +{ + // Some quantities (such as number of blocks) change so fast that we don't want to be notified for each change. + // Periodically check and update with a timer. + int newNumBlocks = getNumBlocks(); + int newNumBlocksOfPeers = getNumBlocksOfPeers(); + + // check for changed number of blocks we have, number of blocks peers claim to have, reindexing state and importing state + if (cachedNumBlocks != newNumBlocks || cachedNumBlocksOfPeers != newNumBlocksOfPeers || + cachedReindexing != fReindex || cachedImporting != fImporting) + { + cachedNumBlocks = newNumBlocks; + cachedNumBlocksOfPeers = newNumBlocksOfPeers; + cachedReindexing = fReindex; + cachedImporting = fImporting; + + // ensure we return the maximum of newNumBlocksOfPeers and newNumBlocks to not create weird displays in the GUI + emit numBlocksChanged(newNumBlocks, std::max(newNumBlocksOfPeers, newNumBlocks)); + } +} + +void ClientModel::updateNumConnections(int numConnections) +{ + emit numConnectionsChanged(numConnections); +} + +void ClientModel::updateAlert(const QString &hash, int status) +{ + // Show error message notification for new alert + if(status == CT_NEW) + { + uint256 hash_256; + hash_256.SetHex(hash.toStdString()); + CAlert alert = CAlert::getAlertByHash(hash_256); + if(!alert.IsNull()) + { + emit message(tr("Network Alert"), QString::fromStdString(alert.strStatusBar), CClientUIInterface::ICON_ERROR); + } + } + + emit alertsChanged(getStatusBarWarnings()); +} + +bool ClientModel::isTestNet() const +{ + return fTestNet; +} + +bool ClientModel::inInitialBlockDownload() const +{ + return IsInitialBlockDownload(); +} + +enum BlockSource ClientModel::getBlockSource() const +{ + if (fReindex) + return BLOCK_SOURCE_REINDEX; + else if (fImporting) + return BLOCK_SOURCE_DISK; + else if (getNumConnections() > 0) + return BLOCK_SOURCE_NETWORK; + + return BLOCK_SOURCE_NONE; +} + +int ClientModel::getNumBlocksOfPeers() const +{ + return GetNumBlocksOfPeers(); +} + +QString ClientModel::getStatusBarWarnings() const +{ + return QString::fromStdString(GetWarnings("statusbar")); +} + +OptionsModel *ClientModel::getOptionsModel() +{ + return optionsModel; +} + +QString ClientModel::formatFullVersion() const +{ + return QString::fromStdString(FormatFullVersion()); +} + +QString ClientModel::formatBuildDate() const +{ + return QString::fromStdString(CLIENT_DATE); +} + +bool ClientModel::isReleaseVersion() const +{ + return CLIENT_VERSION_IS_RELEASE; +} + +QString ClientModel::clientName() const +{ + return QString::fromStdString(CLIENT_NAME); +} + +QString ClientModel::formatClientStartupTime() const +{ + return QDateTime::fromTime_t(nClientStartupTime).toString(); +} + +// Handlers for core signals +static void NotifyBlocksChanged(ClientModel *clientmodel) +{ + // This notification is too frequent. Don't trigger a signal. + // Don't remove it, though, as it might be useful later. +} + +static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConnections) +{ + // Too noisy: OutputDebugStringF("NotifyNumConnectionsChanged %i\n", newNumConnections); + QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection, + Q_ARG(int, newNumConnections)); +} + +static void NotifyAlertChanged(ClientModel *clientmodel, const uint256 &hash, ChangeType status) +{ + OutputDebugStringF("NotifyAlertChanged %s status=%i\n", hash.GetHex().c_str(), status); + QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection, + Q_ARG(QString, QString::fromStdString(hash.GetHex())), + Q_ARG(int, status)); +} + +void ClientModel::subscribeToCoreSignals() +{ + // Connect signals to client + uiInterface.NotifyBlocksChanged.connect(boost::bind(NotifyBlocksChanged, this)); + uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1)); + uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this, _1, _2)); +} + +void ClientModel::unsubscribeFromCoreSignals() +{ + // Disconnect signals from client + uiInterface.NotifyBlocksChanged.disconnect(boost::bind(NotifyBlocksChanged, this)); + uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1)); + uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this, _1, _2)); +} diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h new file mode 100644 index 0000000..f9d491a --- /dev/null +++ b/src/qt/clientmodel.h @@ -0,0 +1,87 @@ +#ifndef CLIENTMODEL_H +#define CLIENTMODEL_H + +#include + +class OptionsModel; +class AddressTableModel; +class TransactionTableModel; +class CWallet; + +QT_BEGIN_NAMESPACE +class QDateTime; +class QTimer; +QT_END_NAMESPACE + +enum BlockSource { + BLOCK_SOURCE_NONE, + BLOCK_SOURCE_REINDEX, + BLOCK_SOURCE_DISK, + BLOCK_SOURCE_NETWORK +}; + +/** Model for Bitcoin network client. */ +class ClientModel : public QObject +{ + Q_OBJECT + +public: + explicit ClientModel(OptionsModel *optionsModel, QObject *parent = 0); + ~ClientModel(); + + OptionsModel *getOptionsModel(); + + int getNumConnections() const; + int getNumBlocks() const; + int getNumBlocksAtStartup(); + + double getVerificationProgress() const; + QDateTime getLastBlockDate() const; + + //! Return true if client connected to testnet + bool isTestNet() const; + //! Return true if core is doing initial block download + bool inInitialBlockDownload() const; + //! Return true if core is importing blocks + enum BlockSource getBlockSource() const; + //! Return conservative estimate of total number of blocks, or 0 if unknown + int getNumBlocksOfPeers() const; + //! Return warnings to be displayed in status bar + QString getStatusBarWarnings() const; + + QString formatFullVersion() const; + QString formatBuildDate() const; + bool isReleaseVersion() const; + QString clientName() const; + QString formatClientStartupTime() const; + +private: + OptionsModel *optionsModel; + + int cachedNumBlocks; + int cachedNumBlocksOfPeers; + bool cachedReindexing; + bool cachedImporting; + + int numBlocksAtStartup; + + QTimer *pollTimer; + + void subscribeToCoreSignals(); + void unsubscribeFromCoreSignals(); + +signals: + void numConnectionsChanged(int count); + void numBlocksChanged(int count, int countOfPeers); + void alertsChanged(const QString &warnings); + + //! Asynchronous message notification + void message(const QString &title, const QString &message, unsigned int style); + +public slots: + void updateTimer(); + void updateNumConnections(int numConnections); + void updateAlert(const QString &hash, int status); +}; + +#endif // CLIENTMODEL_H diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp new file mode 100644 index 0000000..1eab03e --- /dev/null +++ b/src/qt/coincontroldialog.cpp @@ -0,0 +1,773 @@ +#include "coincontroldialog.h" +#include "ui_coincontroldialog.h" + +#include "init.h" +#include "bitcoinunits.h" +#include "walletmodel.h" +#include "addresstablemodel.h" +#include "optionsmodel.h" +#include "guiutil.h" +#include "coincontrol.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +QList CoinControlDialog::payAmounts; +CCoinControl* CoinControlDialog::coinControl = new CCoinControl(); + +CoinControlDialog::CoinControlDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::CoinControlDialog), + model(0) +{ + ui->setupUi(this); + + // context menu actions + QAction *copyAddressAction = new QAction(tr("Copy address"), this); + QAction *copyLabelAction = new QAction(tr("Copy label"), this); + QAction *copyAmountAction = new QAction(tr("Copy amount"), this); + copyTransactionHashAction = new QAction(tr("Copy transaction ID"), this); // we need to enable/disable this + lockAction = new QAction(tr("Lock unspent"), this); // we need to enable/disable this + unlockAction = new QAction(tr("Unlock unspent"), this); // we need to enable/disable this + + // context menu + contextMenu = new QMenu(); + contextMenu->addAction(copyAddressAction); + contextMenu->addAction(copyLabelAction); + contextMenu->addAction(copyAmountAction); + contextMenu->addAction(copyTransactionHashAction); + contextMenu->addSeparator(); + contextMenu->addAction(lockAction); + contextMenu->addAction(unlockAction); + + // context menu signals + connect(ui->treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showMenu(QPoint))); + connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress())); + connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel())); + connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount())); + connect(copyTransactionHashAction, SIGNAL(triggered()), this, SLOT(copyTransactionHash())); + connect(lockAction, SIGNAL(triggered()), this, SLOT(lockCoin())); + connect(unlockAction, SIGNAL(triggered()), this, SLOT(unlockCoin())); + + // clipboard actions + QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this); + QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this); + QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this); + QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this); + QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this); + QAction *clipboardPriorityAction = new QAction(tr("Copy priority"), this); + QAction *clipboardLowOutputAction = new QAction(tr("Copy low output"), this); + QAction *clipboardChangeAction = new QAction(tr("Copy change"), this); + + connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(clipboardQuantity())); + connect(clipboardAmountAction, SIGNAL(triggered()), this, SLOT(clipboardAmount())); + connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(clipboardFee())); + connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(clipboardAfterFee())); + connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(clipboardBytes())); + connect(clipboardPriorityAction, SIGNAL(triggered()), this, SLOT(clipboardPriority())); + connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(clipboardLowOutput())); + connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(clipboardChange())); + + ui->labelCoinControlQuantity->addAction(clipboardQuantityAction); + ui->labelCoinControlAmount->addAction(clipboardAmountAction); + ui->labelCoinControlFee->addAction(clipboardFeeAction); + ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction); + ui->labelCoinControlBytes->addAction(clipboardBytesAction); + ui->labelCoinControlPriority->addAction(clipboardPriorityAction); + ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction); + ui->labelCoinControlChange->addAction(clipboardChangeAction); + + // toggle tree/list mode + connect(ui->radioTreeMode, SIGNAL(toggled(bool)), this, SLOT(radioTreeMode(bool))); + connect(ui->radioListMode, SIGNAL(toggled(bool)), this, SLOT(radioListMode(bool))); + + // click on checkbox + connect(ui->treeWidget, SIGNAL(itemChanged( QTreeWidgetItem*, int)), this, SLOT(viewItemChanged( QTreeWidgetItem*, int))); + + // click on header +#if QT_VERSION < 0x050000 + ui->treeWidget->header()->setClickable(true); +#else + ui->treeWidget->header()->setSectionsClickable(true); +#endif + connect(ui->treeWidget->header(), SIGNAL(sectionClicked(int)), this, SLOT(headerSectionClicked(int))); + + // ok button + connect(ui->buttonBox, SIGNAL(clicked( QAbstractButton*)), this, SLOT(buttonBoxClicked(QAbstractButton*))); + + // (un)select all + connect(ui->pushButtonSelectAll, SIGNAL(clicked()), this, SLOT(buttonSelectAllClicked())); + + ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 84); + ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 100); + ui->treeWidget->setColumnWidth(COLUMN_LABEL, 170); + ui->treeWidget->setColumnWidth(COLUMN_ADDRESS, 290); + ui->treeWidget->setColumnWidth(COLUMN_DATE, 110); + ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 100); + ui->treeWidget->setColumnWidth(COLUMN_PRIORITY, 100); + ui->treeWidget->setColumnHidden(COLUMN_TXHASH, true); // store transacton hash in this column, but dont show it + ui->treeWidget->setColumnHidden(COLUMN_VOUT_INDEX, true); // store vout index in this column, but dont show it + ui->treeWidget->setColumnHidden(COLUMN_AMOUNT_INT64, true); // store amount int64 in this column, but dont show it + ui->treeWidget->setColumnHidden(COLUMN_PRIORITY_INT64, true); // store priority int64 in this column, but dont show it + + // default view is sorted by amount desc + sortView(COLUMN_AMOUNT_INT64, Qt::DescendingOrder); +} + +CoinControlDialog::~CoinControlDialog() +{ + delete ui; +} + +void CoinControlDialog::setModel(WalletModel *model) +{ + this->model = model; + + if(model && model->getOptionsModel() && model->getAddressTableModel()) + { + updateView(); + updateLabelLocked(); + CoinControlDialog::updateLabels(model, this); + } +} + +// helper function str_pad +QString CoinControlDialog::strPad(QString s, int nPadLength, QString sPadding) +{ + while (s.length() < nPadLength) + s = sPadding + s; + + return s; +} + +// ok button +void CoinControlDialog::buttonBoxClicked(QAbstractButton* button) +{ + if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) + done(QDialog::Accepted); // closes the dialog +} + +// (un)select all +void CoinControlDialog::buttonSelectAllClicked() +{ + Qt::CheckState state = Qt::Checked; + for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) + { + if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != Qt::Unchecked) + { + state = Qt::Unchecked; + break; + } + } + ui->treeWidget->setEnabled(false); + for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) + if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != state) + ui->treeWidget->topLevelItem(i)->setCheckState(COLUMN_CHECKBOX, state); + ui->treeWidget->setEnabled(true); + if (state == Qt::Unchecked) + coinControl->UnSelectAll(); // just to be sure + CoinControlDialog::updateLabels(model, this); +} + +// context menu +void CoinControlDialog::showMenu(const QPoint &point) +{ + QTreeWidgetItem *item = ui->treeWidget->itemAt(point); + if(item) + { + contextMenuItem = item; + + // disable some items (like Copy Transaction ID, lock, unlock) for tree roots in context menu + if (item->text(COLUMN_TXHASH).length() == 64) // transaction hash is 64 characters (this means its a child node, so its not a parent node in tree mode) + { + copyTransactionHashAction->setEnabled(true); + if (model->isLockedCoin(uint256(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt())) + { + lockAction->setEnabled(false); + unlockAction->setEnabled(true); + } + else + { + lockAction->setEnabled(true); + unlockAction->setEnabled(false); + } + } + else // this means click on parent node in tree mode -> disable all + { + copyTransactionHashAction->setEnabled(false); + lockAction->setEnabled(false); + unlockAction->setEnabled(false); + } + + // show context menu + contextMenu->exec(QCursor::pos()); + } +} + +// context menu action: copy amount +void CoinControlDialog::copyAmount() +{ + GUIUtil::setClipboard(contextMenuItem->text(COLUMN_AMOUNT)); +} + +// context menu action: copy label +void CoinControlDialog::copyLabel() +{ + if (ui->radioTreeMode->isChecked() && contextMenuItem->text(COLUMN_LABEL).length() == 0 && contextMenuItem->parent()) + GUIUtil::setClipboard(contextMenuItem->parent()->text(COLUMN_LABEL)); + else + GUIUtil::setClipboard(contextMenuItem->text(COLUMN_LABEL)); +} + +// context menu action: copy address +void CoinControlDialog::copyAddress() +{ + if (ui->radioTreeMode->isChecked() && contextMenuItem->text(COLUMN_ADDRESS).length() == 0 && contextMenuItem->parent()) + GUIUtil::setClipboard(contextMenuItem->parent()->text(COLUMN_ADDRESS)); + else + GUIUtil::setClipboard(contextMenuItem->text(COLUMN_ADDRESS)); +} + +// context menu action: copy transaction id +void CoinControlDialog::copyTransactionHash() +{ + GUIUtil::setClipboard(contextMenuItem->text(COLUMN_TXHASH)); +} + +// context menu action: lock coin +void CoinControlDialog::lockCoin() +{ + if (contextMenuItem->checkState(COLUMN_CHECKBOX) == Qt::Checked) + contextMenuItem->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); + + COutPoint outpt(uint256(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt()); + model->lockCoin(outpt); + contextMenuItem->setDisabled(true); + contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon(":/icons/lock_closed")); + updateLabelLocked(); +} + +// context menu action: unlock coin +void CoinControlDialog::unlockCoin() +{ + COutPoint outpt(uint256(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt()); + model->unlockCoin(outpt); + contextMenuItem->setDisabled(false); + contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon()); + updateLabelLocked(); +} + +// copy label "Quantity" to clipboard +void CoinControlDialog::clipboardQuantity() +{ + GUIUtil::setClipboard(ui->labelCoinControlQuantity->text()); +} + +// copy label "Amount" to clipboard +void CoinControlDialog::clipboardAmount() +{ + GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" "))); +} + +// copy label "Fee" to clipboard +void CoinControlDialog::clipboardFee() +{ + GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" "))); +} + +// copy label "After fee" to clipboard +void CoinControlDialog::clipboardAfterFee() +{ + GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" "))); +} + +// copy label "Bytes" to clipboard +void CoinControlDialog::clipboardBytes() +{ + GUIUtil::setClipboard(ui->labelCoinControlBytes->text()); +} + +// copy label "Priority" to clipboard +void CoinControlDialog::clipboardPriority() +{ + GUIUtil::setClipboard(ui->labelCoinControlPriority->text()); +} + +// copy label "Low output" to clipboard +void CoinControlDialog::clipboardLowOutput() +{ + GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text()); +} + +// copy label "Change" to clipboard +void CoinControlDialog::clipboardChange() +{ + GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" "))); +} + +// treeview: sort +void CoinControlDialog::sortView(int column, Qt::SortOrder order) +{ + sortColumn = column; + sortOrder = order; + ui->treeWidget->sortItems(column, order); + ui->treeWidget->header()->setSortIndicator((sortColumn == COLUMN_AMOUNT_INT64 ? COLUMN_AMOUNT : (sortColumn == COLUMN_PRIORITY_INT64 ? COLUMN_PRIORITY : sortColumn)), sortOrder); +} + +// treeview: clicked on header +void CoinControlDialog::headerSectionClicked(int logicalIndex) +{ + if (logicalIndex == COLUMN_CHECKBOX) // click on most left column -> do nothing + { + ui->treeWidget->header()->setSortIndicator((sortColumn == COLUMN_AMOUNT_INT64 ? COLUMN_AMOUNT : (sortColumn == COLUMN_PRIORITY_INT64 ? COLUMN_PRIORITY : sortColumn)), sortOrder); + } + else + { + if (logicalIndex == COLUMN_AMOUNT) // sort by amount + logicalIndex = COLUMN_AMOUNT_INT64; + + if (logicalIndex == COLUMN_PRIORITY) // sort by priority + logicalIndex = COLUMN_PRIORITY_INT64; + + if (sortColumn == logicalIndex) + sortOrder = ((sortOrder == Qt::AscendingOrder) ? Qt::DescendingOrder : Qt::AscendingOrder); + else + { + sortColumn = logicalIndex; + sortOrder = ((sortColumn == COLUMN_AMOUNT_INT64 || sortColumn == COLUMN_PRIORITY_INT64 || sortColumn == COLUMN_DATE || sortColumn == COLUMN_CONFIRMATIONS) ? Qt::DescendingOrder : Qt::AscendingOrder); // if amount,date,conf,priority then default => desc, else default => asc + } + + sortView(sortColumn, sortOrder); + } +} + +// toggle tree mode +void CoinControlDialog::radioTreeMode(bool checked) +{ + if (checked && model) + updateView(); +} + +// toggle list mode +void CoinControlDialog::radioListMode(bool checked) +{ + if (checked && model) + updateView(); +} + +// checkbox clicked by user +void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column) +{ + if (column == COLUMN_CHECKBOX && item->text(COLUMN_TXHASH).length() == 64) // transaction hash is 64 characters (this means its a child node, so its not a parent node in tree mode) + { + COutPoint outpt(uint256(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt()); + + if (item->checkState(COLUMN_CHECKBOX) == Qt::Unchecked) + coinControl->UnSelect(outpt); + else if (item->isDisabled()) // locked (this happens if "check all" through parent node) + item->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); + else + coinControl->Select(outpt); + + // selection changed -> update labels + if (ui->treeWidget->isEnabled()) // do not update on every click for (un)select all + CoinControlDialog::updateLabels(model, this); + } +} + +// helper function, return human readable label for priority number +QString CoinControlDialog::getPriorityLabel(double dPriority) +{ + if (CTransaction::AllowFree(dPriority)) // at least medium + { + if (CTransaction::AllowFree(dPriority / 10000)) return tr("highest"); + else if (CTransaction::AllowFree(dPriority / 1000)) return tr("high"); + else if (CTransaction::AllowFree(dPriority / 100)) return tr("medium-high"); + else return tr("medium"); + } + else + { + if (CTransaction::AllowFree(dPriority * 100)) return tr("low-medium"); + else if (CTransaction::AllowFree(dPriority * 10000)) return tr("low"); + else return tr("lowest"); + } +} + +// shows count of locked unspent outputs +void CoinControlDialog::updateLabelLocked() +{ + vector vOutpts; + model->listLockedCoins(vOutpts); + if (vOutpts.size() > 0) + { + ui->labelLocked->setText(tr("(%1 locked)").arg(vOutpts.size())); + ui->labelLocked->setVisible(true); + } + else ui->labelLocked->setVisible(false); +} + +void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) +{ + if (!model) return; + + // nPayAmount + qint64 nPayAmount = 0; + bool fLowOutput = false; + bool fDust = false; + unsigned int nQuantityDust = 0; + CTransaction txDummy; + foreach(const qint64 &amount, CoinControlDialog::payAmounts) + { + nPayAmount += amount; + + if (amount > 0) + { + if (amount < CENT) { + fLowOutput = true; + nQuantityDust++; + } + + CTxOut txout(amount, (CScript)vector(24, 0)); + txDummy.vout.push_back(txout); + if (txout.IsDust()) + fDust = true; + } + } + + QString sPriorityLabel = ""; + int64 nAmount = 0; + int64 nPayFee = 0; + int64 nAfterFee = 0; + int64 nChange = 0; + unsigned int nBytes = 0; + unsigned int nBytesInputs = 0; + double dPriority = 0; + double dPriorityInputs = 0; + unsigned int nQuantity = 0; + + vector vCoinControl; + vector vOutputs; + coinControl->ListSelected(vCoinControl); + model->getOutputs(vCoinControl, vOutputs); + + BOOST_FOREACH(const COutput& out, vOutputs) + { + // unselect already spent, very unlikely scenario, this could happen when selected are spent elsewhere, like rpc or another computer + if (out.tx->IsSpent(out.i)) + { + uint256 txhash = out.tx->GetHash(); + COutPoint outpt(txhash, out.i); + coinControl->UnSelect(outpt); + continue; + } + + // Quantity + nQuantity++; + + // Amount + nAmount += out.tx->vout[out.i].nValue; + + // Priority + dPriorityInputs += (double)out.tx->vout[out.i].nValue * (out.nDepth+1); + + // Bytes + CTxDestination address; + if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) + { + CPubKey pubkey; + CKeyID *keyid = boost::get(&address); + if (keyid && model->getPubKey(*keyid, pubkey)) + nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); + else + nBytesInputs += 148; // in all error cases, simply assume 148 here + } + else nBytesInputs += 148; + } + + // calculation + if (nQuantity > 0) + { + // Bytes + nBytes = nBytesInputs + ((CoinControlDialog::payAmounts.size() > 0 ? CoinControlDialog::payAmounts.size() + 1 : 2) * 34) + 10; // always assume +1 output for change here + + // Priority + dPriority = dPriorityInputs / nBytes; + sPriorityLabel = CoinControlDialog::getPriorityLabel(dPriority); + + // Fee + int64 nFee = nTransactionFee * (1 + (int64)nBytes / 1000); + + // Min Fee + int64 nMinFee = CTransaction::nMinTxFee * (1 + (int64)nBytes / 1000) + CTransaction::nMinTxFee * nQuantityDust; + if (CTransaction::AllowFree(dPriority) && nBytes < 5000) + nMinFee = 0; + + nPayFee = max(nFee, nMinFee); + + if (nPayAmount > 0) + { + nChange = nAmount - nPayFee - nPayAmount; + + // require CTransaction::nMinTxFee if any output is less than 0.01 + if (nPayFee < CTransaction::nMinTxFee && fLowOutput) + { + nChange = nChange + nPayFee - CTransaction::nMinTxFee; + nPayFee = CTransaction::nMinTxFee * nQuantityDust; + } + + // if sub-cent change is required, the fee must be raised to at least CTransaction::nMinTxFee + if (nPayFee < CTransaction::nMinTxFee && nChange > 0 && nChange < CENT) + { + if (nChange < CTransaction::nMinTxFee) // change < 0.0001 => simply move all change to fees + { + nPayFee += nChange; + nChange = 0; + } + else + { + nChange = nChange + nPayFee - CTransaction::nMinTxFee; + nPayFee = CTransaction::nMinTxFee; + } + } + + // Never create dust outputs; if we would, just add the dust to the fee. + if (nChange > 0 && nChange < CENT) + { + CTxOut txout(nChange, (CScript)vector(24, 0)); + if (txout.IsDust()) + { + nPayFee += nChange; + nChange = 0; + } + } + + if (nChange == 0) + nBytes -= 34; + } + + // after fee + nAfterFee = nAmount - nPayFee; + if (nAfterFee < 0) + nAfterFee = 0; + } + + // actually update labels + int nDisplayUnit = BitcoinUnits::BTC; + if (model && model->getOptionsModel()) + nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); + + QLabel *l1 = dialog->findChild("labelCoinControlQuantity"); + QLabel *l2 = dialog->findChild("labelCoinControlAmount"); + QLabel *l3 = dialog->findChild("labelCoinControlFee"); + QLabel *l4 = dialog->findChild("labelCoinControlAfterFee"); + QLabel *l5 = dialog->findChild("labelCoinControlBytes"); + QLabel *l6 = dialog->findChild("labelCoinControlPriority"); + QLabel *l7 = dialog->findChild("labelCoinControlLowOutput"); + QLabel *l8 = dialog->findChild("labelCoinControlChange"); + + // enable/disable "low output" and "change" + dialog->findChild("labelCoinControlLowOutputText")->setEnabled(nPayAmount > 0); + dialog->findChild("labelCoinControlLowOutput") ->setEnabled(nPayAmount > 0); + dialog->findChild("labelCoinControlChangeText") ->setEnabled(nPayAmount > 0); + dialog->findChild("labelCoinControlChange") ->setEnabled(nPayAmount > 0); + + // stats + l1->setText(QString::number(nQuantity)); // Quantity + l2->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAmount)); // Amount + l3->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nPayFee)); // Fee + l4->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAfterFee)); // After Fee + l5->setText(((nBytes > 0) ? "~" : "") + QString::number(nBytes)); // Bytes + l6->setText(sPriorityLabel); // Priority + l7->setText((fLowOutput ? (fDust ? tr("DUST") : tr("yes")) : tr("no"))); // Low Output / Dust + l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange)); // Change + + // turn labels "red" + l5->setStyleSheet((nBytes >= 5000) ? "color:red;" : ""); // Bytes >= 5000 + l6->setStyleSheet((!CTransaction::AllowFree(dPriority)) ? "color:red;" : ""); // Priority < "medium" + l7->setStyleSheet((fLowOutput) ? "color:red;" : ""); // Low Output = "yes" + l8->setStyleSheet((nChange > 0 && nChange < CENT) ? "color:red;" : ""); // Change < 0.01BTC + + // tool tips + l5->setToolTip(tr("This label turns red, if the transaction size is bigger than 5000 bytes.\n\n This means a fee of at least %1 per kb is required.\n\n Can vary +/- 1 Byte per input.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::nMinTxFee))); + l6->setToolTip(tr("Transactions with higher priority get more likely into a block.\n\nThis label turns red, if the priority is smaller than \"medium\".\n\n This means a fee of at least %1 per kb is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::nMinTxFee))); + l7->setToolTip(tr("This label turns red, if any recipient receives an amount smaller than %1.\n\n This means a fee of at least %2 is required. \n\n Amounts below 0.546 times the minimum relay fee are shown as DUST.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)).arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::nMinTxFee))); + l8->setToolTip(tr("This label turns red, if the change is smaller than %1.\n\n This means a fee of at least %2 is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)).arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::nMinTxFee))); + dialog->findChild("labelCoinControlBytesText") ->setToolTip(l5->toolTip()); + dialog->findChild("labelCoinControlPriorityText") ->setToolTip(l6->toolTip()); + dialog->findChild("labelCoinControlLowOutputText")->setToolTip(l7->toolTip()); + dialog->findChild("labelCoinControlChangeText") ->setToolTip(l8->toolTip()); + + // Insufficient funds + QLabel *label = dialog->findChild("labelCoinControlInsuffFunds"); + if (label) + label->setVisible(nChange < 0); +} + +void CoinControlDialog::updateView() +{ + bool treeMode = ui->radioTreeMode->isChecked(); + + ui->treeWidget->clear(); + ui->treeWidget->setEnabled(false); // performance, otherwise updateLabels would be called for every checked checkbox + ui->treeWidget->setAlternatingRowColors(!treeMode); + QFlags flgCheckbox=Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; + QFlags flgTristate=Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate; + + int nDisplayUnit = BitcoinUnits::BTC; + if (model && model->getOptionsModel()) + nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); + + map > mapCoins; + model->listCoins(mapCoins); + + BOOST_FOREACH(PAIRTYPE(QString, vector) coins, mapCoins) + { + QTreeWidgetItem *itemWalletAddress = new QTreeWidgetItem(); + QString sWalletAddress = coins.first; + QString sWalletLabel = ""; + if (model->getAddressTableModel()) + sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress); + if (sWalletLabel.length() == 0) + sWalletLabel = tr("(no label)"); + + if (treeMode) + { + // wallet address + ui->treeWidget->addTopLevelItem(itemWalletAddress); + + itemWalletAddress->setFlags(flgTristate); + itemWalletAddress->setCheckState(COLUMN_CHECKBOX,Qt::Unchecked); + + for (int i = 0; i < ui->treeWidget->columnCount(); i++) + itemWalletAddress->setBackground(i, QColor(248, 247, 246)); + + // label + itemWalletAddress->setText(COLUMN_LABEL, sWalletLabel); + + // address + itemWalletAddress->setText(COLUMN_ADDRESS, sWalletAddress); + } + + int64 nSum = 0; + double dPrioritySum = 0; + int nChildren = 0; + int nInputSum = 0; + BOOST_FOREACH(const COutput& out, coins.second) + { + int nInputSize = 148; // 180 if uncompressed public key + nSum += out.tx->vout[out.i].nValue; + nChildren++; + + QTreeWidgetItem *itemOutput; + if (treeMode) itemOutput = new QTreeWidgetItem(itemWalletAddress); + else itemOutput = new QTreeWidgetItem(ui->treeWidget); + itemOutput->setFlags(flgCheckbox); + itemOutput->setCheckState(COLUMN_CHECKBOX,Qt::Unchecked); + + // address + CTxDestination outputAddress; + QString sAddress = ""; + if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, outputAddress)) + { + sAddress = CBitcoinAddress(outputAddress).ToString().c_str(); + + // if listMode or change => show bitcoin address. In tree mode, address is not shown again for direct wallet address outputs + if (!treeMode || (!(sAddress == sWalletAddress))) + itemOutput->setText(COLUMN_ADDRESS, sAddress); + + CPubKey pubkey; + CKeyID *keyid = boost::get(&outputAddress); + if (keyid && model->getPubKey(*keyid, pubkey) && !pubkey.IsCompressed()) + nInputSize = 180; + } + + // label + if (!(sAddress == sWalletAddress)) // change + { + // tooltip from where the change comes from + itemOutput->setToolTip(COLUMN_LABEL, tr("change from %1 (%2)").arg(sWalletLabel).arg(sWalletAddress)); + itemOutput->setText(COLUMN_LABEL, tr("(change)")); + } + else if (!treeMode) + { + QString sLabel = ""; + if (model->getAddressTableModel()) + sLabel = model->getAddressTableModel()->labelForAddress(sAddress); + if (sLabel.length() == 0) + sLabel = tr("(no label)"); + itemOutput->setText(COLUMN_LABEL, sLabel); + } + + // amount + itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->vout[out.i].nValue)); + itemOutput->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(out.tx->vout[out.i].nValue), 15, " ")); // padding so that sorting works correctly + + // date + itemOutput->setText(COLUMN_DATE, QDateTime::fromTime_t(out.tx->GetTxTime()).toString("yy-MM-dd hh:mm")); + + // confirmations + itemOutput->setText(COLUMN_CONFIRMATIONS, strPad(QString::number(out.nDepth), 8, " ")); + + // priority + double dPriority = ((double)out.tx->vout[out.i].nValue / (nInputSize + 78)) * (out.nDepth+1); // 78 = 2 * 34 + 10 + itemOutput->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPriority)); + itemOutput->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64)dPriority), 20, " ")); + dPrioritySum += (double)out.tx->vout[out.i].nValue * (out.nDepth+1); + nInputSum += nInputSize; + + // transaction hash + uint256 txhash = out.tx->GetHash(); + itemOutput->setText(COLUMN_TXHASH, txhash.GetHex().c_str()); + + // vout index + itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(out.i)); + + // disable locked coins + if (model->isLockedCoin(txhash, out.i)) + { + COutPoint outpt(txhash, out.i); + coinControl->UnSelect(outpt); // just to be sure + itemOutput->setDisabled(true); + itemOutput->setIcon(COLUMN_CHECKBOX, QIcon(":/icons/lock_closed")); + } + + // set checkbox + if (coinControl->IsSelected(txhash, out.i)) + itemOutput->setCheckState(COLUMN_CHECKBOX,Qt::Checked); + } + + // amount + if (treeMode) + { + dPrioritySum = dPrioritySum / (nInputSum + 78); + itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")"); + itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum)); + itemWalletAddress->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(nSum), 15, " ")); + itemWalletAddress->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPrioritySum)); + itemWalletAddress->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64)dPrioritySum), 20, " ")); + } + } + + // expand all partially selected + if (treeMode) + { + for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) + if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked) + ui->treeWidget->topLevelItem(i)->setExpanded(true); + } + + // sort view + sortView(sortColumn, sortOrder); + ui->treeWidget->setEnabled(true); +} diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h new file mode 100644 index 0000000..9903bd4 --- /dev/null +++ b/src/qt/coincontroldialog.h @@ -0,0 +1,92 @@ +#ifndef COINCONTROLDIALOG_H +#define COINCONTROLDIALOG_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Ui { + class CoinControlDialog; +} +class WalletModel; +class CCoinControl; + +class CoinControlDialog : public QDialog +{ + Q_OBJECT + +public: + explicit CoinControlDialog(QWidget *parent = 0); + ~CoinControlDialog(); + + void setModel(WalletModel *model); + + // static because also called from sendcoinsdialog + static void updateLabels(WalletModel*, QDialog*); + static QString getPriorityLabel(double); + + static QList payAmounts; + static CCoinControl *coinControl; + +private: + Ui::CoinControlDialog *ui; + WalletModel *model; + int sortColumn; + Qt::SortOrder sortOrder; + + QMenu *contextMenu; + QTreeWidgetItem *contextMenuItem; + QAction *copyTransactionHashAction; + QAction *lockAction; + QAction *unlockAction; + + QString strPad(QString, int, QString); + void sortView(int, Qt::SortOrder); + void updateView(); + + enum + { + COLUMN_CHECKBOX, + COLUMN_AMOUNT, + COLUMN_LABEL, + COLUMN_ADDRESS, + COLUMN_DATE, + COLUMN_CONFIRMATIONS, + COLUMN_PRIORITY, + COLUMN_TXHASH, + COLUMN_VOUT_INDEX, + COLUMN_AMOUNT_INT64, + COLUMN_PRIORITY_INT64 + }; + +private slots: + void showMenu(const QPoint &); + void copyAmount(); + void copyLabel(); + void copyAddress(); + void copyTransactionHash(); + void lockCoin(); + void unlockCoin(); + void clipboardQuantity(); + void clipboardAmount(); + void clipboardFee(); + void clipboardAfterFee(); + void clipboardBytes(); + void clipboardPriority(); + void clipboardLowOutput(); + void clipboardChange(); + void radioTreeMode(bool); + void radioListMode(bool); + void viewItemChanged(QTreeWidgetItem*, int); + void headerSectionClicked(int); + void buttonBoxClicked(QAbstractButton*); + void buttonSelectAllClicked(); + void updateLabelLocked(); +}; + +#endif // COINCONTROLDIALOG_H diff --git a/src/qt/coincontroltreewidget.cpp b/src/qt/coincontroltreewidget.cpp new file mode 100644 index 0000000..aa75a49 --- /dev/null +++ b/src/qt/coincontroltreewidget.cpp @@ -0,0 +1,28 @@ +#include "coincontroltreewidget.h" +#include "coincontroldialog.h" + +CoinControlTreeWidget::CoinControlTreeWidget(QWidget *parent) : + QTreeWidget(parent) +{ + +} + +void CoinControlTreeWidget::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Space) // press spacebar -> select checkbox + { + event->ignore(); + int COLUMN_CHECKBOX = 0; + this->currentItem()->setCheckState(COLUMN_CHECKBOX, ((this->currentItem()->checkState(COLUMN_CHECKBOX) == Qt::Checked) ? Qt::Unchecked : Qt::Checked)); + } + else if (event->key() == Qt::Key_Escape) // press esc -> close dialog + { + event->ignore(); + CoinControlDialog *coinControlDialog = (CoinControlDialog*)this->parentWidget(); + coinControlDialog->done(QDialog::Accepted); + } + else + { + this->QTreeWidget::keyPressEvent(event); + } +} \ No newline at end of file diff --git a/src/qt/coincontroltreewidget.h b/src/qt/coincontroltreewidget.h new file mode 100644 index 0000000..d981f72 --- /dev/null +++ b/src/qt/coincontroltreewidget.h @@ -0,0 +1,17 @@ +#ifndef COINCONTROLTREEWIDGET_H +#define COINCONTROLTREEWIDGET_H + +#include +#include + +class CoinControlTreeWidget : public QTreeWidget { +Q_OBJECT + +public: + explicit CoinControlTreeWidget(QWidget *parent = 0); + +protected: + virtual void keyPressEvent(QKeyEvent *event); +}; + +#endif // COINCONTROLTREEWIDGET_H \ No newline at end of file diff --git a/src/qt/csvmodelwriter.cpp b/src/qt/csvmodelwriter.cpp new file mode 100644 index 0000000..8a50bba --- /dev/null +++ b/src/qt/csvmodelwriter.cpp @@ -0,0 +1,88 @@ +#include "csvmodelwriter.h" + +#include +#include +#include + +CSVModelWriter::CSVModelWriter(const QString &filename, QObject *parent) : + QObject(parent), + filename(filename), model(0) +{ +} + +void CSVModelWriter::setModel(const QAbstractItemModel *model) +{ + this->model = model; +} + +void CSVModelWriter::addColumn(const QString &title, int column, int role) +{ + Column col; + col.title = title; + col.column = column; + col.role = role; + + columns.append(col); +} + +static void writeValue(QTextStream &f, const QString &value) +{ + QString escaped = value; + escaped.replace('"', "\"\""); + f << "\"" << escaped << "\""; +} + +static void writeSep(QTextStream &f) +{ + f << ","; +} + +static void writeNewline(QTextStream &f) +{ + f << "\n"; +} + +bool CSVModelWriter::write() +{ + QFile file(filename); + if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) + return false; + QTextStream out(&file); + + int numRows = 0; + if(model) + { + numRows = model->rowCount(); + } + + // Header row + for(int i=0; iindex(j, columns[i].column).data(columns[i].role); + writeValue(out, data.toString()); + } + writeNewline(out); + } + + file.close(); + + return file.error() == QFile::NoError; +} + diff --git a/src/qt/csvmodelwriter.h b/src/qt/csvmodelwriter.h new file mode 100644 index 0000000..c4504ee --- /dev/null +++ b/src/qt/csvmodelwriter.h @@ -0,0 +1,42 @@ +#ifndef CSVMODELWRITER_H +#define CSVMODELWRITER_H + +#include +#include + +QT_BEGIN_NAMESPACE +class QAbstractItemModel; +QT_END_NAMESPACE + +/** Export a Qt table model to a CSV file. This is useful for analyzing or post-processing the data in + a spreadsheet. + */ +class CSVModelWriter : public QObject +{ + Q_OBJECT + +public: + explicit CSVModelWriter(const QString &filename, QObject *parent = 0); + + void setModel(const QAbstractItemModel *model); + void addColumn(const QString &title, int column, int role=Qt::EditRole); + + /** Perform export of the model to CSV. + @returns true on success, false otherwise + */ + bool write(); + +private: + QString filename; + const QAbstractItemModel *model; + + struct Column + { + QString title; + int column; + int role; + }; + QList columns; +}; + +#endif // CSVMODELWRITER_H diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp new file mode 100644 index 0000000..072a562 --- /dev/null +++ b/src/qt/editaddressdialog.cpp @@ -0,0 +1,137 @@ +#include "editaddressdialog.h" +#include "ui_editaddressdialog.h" + +#include "addresstablemodel.h" +#include "guiutil.h" + +#include +#include + +EditAddressDialog::EditAddressDialog(Mode mode, QWidget *parent) : + QDialog(parent), + ui(new Ui::EditAddressDialog), mapper(0), mode(mode), model(0) +{ + ui->setupUi(this); + + GUIUtil::setupAddressWidget(ui->addressEdit, this); + + switch(mode) + { + case NewReceivingAddress: + setWindowTitle(tr("New receiving address")); + ui->addressEdit->setEnabled(false); + break; + case NewSendingAddress: + setWindowTitle(tr("New sending address")); + break; + case EditReceivingAddress: + setWindowTitle(tr("Edit receiving address")); + ui->addressEdit->setEnabled(false); + break; + case EditSendingAddress: + setWindowTitle(tr("Edit sending address")); + break; + } + + mapper = new QDataWidgetMapper(this); + mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); +} + +EditAddressDialog::~EditAddressDialog() +{ + delete ui; +} + +void EditAddressDialog::setModel(AddressTableModel *model) +{ + this->model = model; + if(!model) + return; + + mapper->setModel(model); + mapper->addMapping(ui->labelEdit, AddressTableModel::Label); + mapper->addMapping(ui->addressEdit, AddressTableModel::Address); +} + +void EditAddressDialog::loadRow(int row) +{ + mapper->setCurrentIndex(row); +} + +bool EditAddressDialog::saveCurrentRow() +{ + if(!model) + return false; + + switch(mode) + { + case NewReceivingAddress: + case NewSendingAddress: + address = model->addRow( + mode == NewSendingAddress ? AddressTableModel::Send : AddressTableModel::Receive, + ui->labelEdit->text(), + ui->addressEdit->text()); + break; + case EditReceivingAddress: + case EditSendingAddress: + if(mapper->submit()) + { + address = ui->addressEdit->text(); + } + break; + } + return !address.isEmpty(); +} + +void EditAddressDialog::accept() +{ + if(!model) + return; + + if(!saveCurrentRow()) + { + switch(model->getEditStatus()) + { + case AddressTableModel::OK: + // Failed with unknown reason. Just reject. + break; + case AddressTableModel::NO_CHANGES: + // No changes were made during edit operation. Just reject. + break; + case AddressTableModel::INVALID_ADDRESS: + QMessageBox::warning(this, windowTitle(), + tr("The entered address \"%1\" is not a valid CryptoLottoToken address.").arg(ui->addressEdit->text()), + QMessageBox::Ok, QMessageBox::Ok); + break; + case AddressTableModel::DUPLICATE_ADDRESS: + QMessageBox::warning(this, windowTitle(), + tr("The entered address \"%1\" is already in the address book.").arg(ui->addressEdit->text()), + QMessageBox::Ok, QMessageBox::Ok); + break; + case AddressTableModel::WALLET_UNLOCK_FAILURE: + QMessageBox::critical(this, windowTitle(), + tr("Could not unlock wallet."), + QMessageBox::Ok, QMessageBox::Ok); + break; + case AddressTableModel::KEY_GENERATION_FAILURE: + QMessageBox::critical(this, windowTitle(), + tr("New key generation failed."), + QMessageBox::Ok, QMessageBox::Ok); + break; + + } + return; + } + QDialog::accept(); +} + +QString EditAddressDialog::getAddress() const +{ + return address; +} + +void EditAddressDialog::setAddress(const QString &address) +{ + this->address = address; + ui->addressEdit->setText(address); +} diff --git a/src/qt/editaddressdialog.h b/src/qt/editaddressdialog.h new file mode 100644 index 0000000..44e5023 --- /dev/null +++ b/src/qt/editaddressdialog.h @@ -0,0 +1,52 @@ +#ifndef EDITADDRESSDIALOG_H +#define EDITADDRESSDIALOG_H + +#include + +namespace Ui { + class EditAddressDialog; +} +class AddressTableModel; + +QT_BEGIN_NAMESPACE +class QDataWidgetMapper; +QT_END_NAMESPACE + +/** Dialog for editing an address and associated information. + */ +class EditAddressDialog : public QDialog +{ + Q_OBJECT + +public: + enum Mode { + NewReceivingAddress, + NewSendingAddress, + EditReceivingAddress, + EditSendingAddress + }; + + explicit EditAddressDialog(Mode mode, QWidget *parent = 0); + ~EditAddressDialog(); + + void setModel(AddressTableModel *model); + void loadRow(int row); + + QString getAddress() const; + void setAddress(const QString &address); + +public slots: + void accept(); + +private: + bool saveCurrentRow(); + + Ui::EditAddressDialog *ui; + QDataWidgetMapper *mapper; + Mode mode; + AddressTableModel *model; + + QString address; +}; + +#endif // EDITADDRESSDIALOG_H diff --git a/src/qt/forms/aboutdialog.ui b/src/qt/forms/aboutdialog.ui new file mode 100644 index 0000000..a62ebb5 --- /dev/null +++ b/src/qt/forms/aboutdialog.ui @@ -0,0 +1,191 @@ + + + AboutDialog + + + + 0 + 0 + 593 + 360 + + + + About CryptoLottoToken + + + + + + + 0 + 0 + + + + :/images/about + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + IBeamCursor + + + <b>CryptoLottoToken</b> version + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + IBeamCursor + + + 0.3.666-beta + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + IBeamCursor + + + Copyright &copy; 2009-YYYY The Bitcoin developers +Copyright &copy; 2011-YYYY The CryptoLottoToken developers +Copyright &copy; 2013 CryptoLottoToken Developers + + + Qt::RichText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + IBeamCursor + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + + + buttonBox + accepted() + AboutDialog + accept() + + + 360 + 308 + + + 157 + 274 + + + + + buttonBox + rejected() + AboutDialog + reject() + + + 428 + 308 + + + 286 + 274 + + + + + diff --git a/src/qt/forms/addressbookpage.ui b/src/qt/forms/addressbookpage.ui new file mode 100644 index 0000000..98b7fdb --- /dev/null +++ b/src/qt/forms/addressbookpage.ui @@ -0,0 +1,186 @@ + + + AddressBookPage + + + + 0 + 0 + 760 + 380 + + + + Address Book + + + + + + Qt::PlainText + + + true + + + + + + + Qt::CustomContextMenu + + + Double-click to edit address or label + + + false + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + false + + + + + + + + + Create a new address + + + &New Address + + + + :/icons/add:/icons/add + + + + + + + Copy the currently selected address to the system clipboard + + + &Copy Address + + + + :/icons/editcopy:/icons/editcopy + + + + + + + Show &QR Code + + + + :/icons/qrcode:/icons/qrcode + + + + + + + Sign a message to prove you own a CryptoLottoToken address + + + Sign &Message + + + + :/icons/edit:/icons/edit + + + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + + + &Verify Message + + + + :/icons/transaction_0:/icons/transaction_0 + + + + + + + Delete the currently selected address from the list + + + &Delete + + + + :/icons/remove:/icons/remove + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Export the data in the current tab to a file + + + &Export + + + + :/icons/export:/icons/export + + + + + + + + 0 + 0 + + + + QDialogButtonBox::Ok + + + + + + + + + + + + diff --git a/src/qt/forms/askpassphrasedialog.ui b/src/qt/forms/askpassphrasedialog.ui new file mode 100644 index 0000000..2516904 --- /dev/null +++ b/src/qt/forms/askpassphrasedialog.ui @@ -0,0 +1,151 @@ + + + AskPassphraseDialog + + + + 0 + 0 + 598 + 198 + + + + + 0 + 0 + + + + + 550 + 0 + + + + Passphrase Dialog + + + + + + Qt::RichText + + + true + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Enter passphrase + + + + + + + QLineEdit::Password + + + + + + + New passphrase + + + + + + + QLineEdit::Password + + + + + + + Repeat new passphrase + + + + + + + QLineEdit::Password + + + + + + + + 75 + true + + + + + + + Qt::AlignCenter + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + AskPassphraseDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AskPassphraseDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/qt/forms/coincontroldialog.ui b/src/qt/forms/coincontroldialog.ui new file mode 100644 index 0000000..58f6557 --- /dev/null +++ b/src/qt/forms/coincontroldialog.ui @@ -0,0 +1,556 @@ + + + CoinControlDialog + + + + 0 + 0 + 1000 + 500 + + + + Coin Control + + + + + + 0 + + + 10 + + + + + 10 + + + 10 + + + 6 + + + 6 + + + + + font-weight:bold; + + + Quantity: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + font-weight:bold; + + + Bytes: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + 10 + + + 10 + + + 6 + + + 6 + + + + + font-weight:bold; + + + Amount: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 BTC + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + font-weight:bold; + + + Priority: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + 10 + + + 10 + + + 6 + + + 6 + + + + + font-weight:bold; + + + Fee: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 BTC + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + false + + + font-weight:bold; + + + Low Output: + + + + + + + false + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + no + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + 10 + + + 10 + + + 6 + + + 6 + + + + + font-weight:bold; + + + After Fee: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 BTC + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + false + + + font-weight:bold; + + + Change: + + + + + + + false + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 BTC + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + + + 0 + 40 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + 10 + 0 + 781 + 41 + + + + + 14 + + + + + + 0 + 0 + + + + (un)select all + + + + + + + + 0 + 0 + + + + Tree mode + + + true + + + + + + + + 0 + 0 + + + + List mode + + + + + + + (1 locked) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::CustomContextMenu + + + false + + + 11 + + + true + + + false + + + + + + + + + Amount + + + + + Label + + + + + Address + + + + + Date + + + + + Confirmations + + + Confirmed + + + + + Priority + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + CoinControlTreeWidget + QTreeWidget +
coincontroltreewidget.h
+
+
+ + +
diff --git a/src/qt/forms/editaddressdialog.ui b/src/qt/forms/editaddressdialog.ui new file mode 100644 index 0000000..b4a4c1b --- /dev/null +++ b/src/qt/forms/editaddressdialog.ui @@ -0,0 +1,105 @@ + + + EditAddressDialog + + + + 0 + 0 + 457 + 126 + + + + Edit Address + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + &Label + + + labelEdit + + + + + + + The label associated with this address book entry + + + + + + + &Address + + + addressEdit + + + + + + + The address associated with this address book entry. This can only be modified for sending addresses. + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + EditAddressDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + EditAddressDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui new file mode 100644 index 0000000..a9ca968 --- /dev/null +++ b/src/qt/forms/optionsdialog.ui @@ -0,0 +1,490 @@ + + + OptionsDialog + + + + 0 + 0 + 540 + 380 + + + + Options + + + true + + + + + + QTabWidget::North + + + 0 + + + + &Main + + + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + Qt::PlainText + + + true + + + + + + + + + Pay transaction &fee + + + Qt::PlainText + + + transactionFee + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Automatically start CryptoLottoToken after logging in to the system. + + + &Start CryptoLottoToken on system login + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Reset all client options to default. + + + &Reset Options + + + false + + + + + + + + + + &Network + + + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + + + Map port using &UPnP + + + + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + + + &Connect through SOCKS proxy: + + + + + + + + + Proxy &IP: + + + Qt::PlainText + + + proxyIp + + + + + + + + 140 + 16777215 + + + + IP address of the proxy (e.g. 127.0.0.1) + + + + + + + &Port: + + + Qt::PlainText + + + proxyPort + + + + + + + + 55 + 16777215 + + + + Port of the proxy (e.g. 9050) + + + + + + + SOCKS &Version: + + + Qt::PlainText + + + socksVersion + + + + + + + SOCKS version of the proxy (e.g. 5) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + &Window + + + + + + Show only a tray icon after minimizing the window. + + + &Minimize to the tray instead of the taskbar + + + + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + + + M&inimize on close + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + &Display + + + + + + + + User Interface &language: + + + Qt::PlainText + + + lang + + + + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + + + + + + + + + + + &Unit to show amounts in: + + + Qt::PlainText + + + unit + + + + + + + Choose the default subdivision unit to show in the interface and when sending coins. + + + + + + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + &Display addresses in transaction list + + + + + + + Whether to show coin control features or not. + + + Display coin &control features (experts only!) + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 48 + + + + + + + + + 75 + true + + + + + + + Qt::PlainText + + + true + + + + + + + Qt::Horizontal + + + + 40 + 48 + + + + + + + + &OK + + + + + + + &Cancel + + + false + + + + + + + &Apply + + + false + + + + + + + + + + BitcoinAmountField + QSpinBox +
bitcoinamountfield.h
+
+ + QValueComboBox + QComboBox +
qvaluecombobox.h
+
+ + QValidatedLineEdit + QLineEdit +
qvalidatedlineedit.h
+
+
+ + +
diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui new file mode 100644 index 0000000..65f10e6 --- /dev/null +++ b/src/qt/forms/overviewpage.ui @@ -0,0 +1,327 @@ + + + OverviewPage + + + + 0 + 0 + 573 + 342 + + + + Form + + + + + + false + + + background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop:0 #F0D0A0, stop:1 #F8D488); color:#000000; + + + true + + + 3 + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + 75 + true + + + + Wallet + + + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + + + QLabel { color: red; } + + + (out of sync) + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + 12 + + + 12 + + + + + Balance: + + + + + + + + 75 + true + + + + IBeamCursor + + + Your current balance + + + 0 CLT + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Unconfirmed: + + + + + + + + 75 + true + + + + IBeamCursor + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + + + 0 CLT + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Immature: + + + + + + + + 75 + true + + + + Mined balance that has not yet matured + + + 0 CLT + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + :/images/wallet_bgcoin + + + false + + + Qt::AlignCenter + + + -2 + + + + + + + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + <b>Recent transactions</b> + + + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + + + QLabel { color: red; } + + + (out of sync) + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QListView { background: transparent; } + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + QAbstractItemView::NoSelection + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + diff --git a/src/qt/forms/qrcodedialog.ui b/src/qt/forms/qrcodedialog.ui new file mode 100644 index 0000000..52e9db3 --- /dev/null +++ b/src/qt/forms/qrcodedialog.ui @@ -0,0 +1,212 @@ + + + QRCodeDialog + + + + 0 + 0 + 340 + 530 + + + + QR Code Dialog + + + + + + + 0 + 0 + + + + + 300 + 300 + + + + Qt::PlainText + + + Qt::AlignCenter + + + true + + + + + + + + 0 + 0 + + + + + 0 + 50 + + + + true + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + true + + + Request Payment + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Label: + + + Qt::PlainText + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + lnLabel + + + + + + + + + + Message: + + + Qt::PlainText + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + lnMessage + + + + + + + + + + + 0 + 0 + + + + Amount: + + + Qt::PlainText + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + lnReqAmount + + + + + + + false + + + + 80 + 0 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + &Save As... + + + + + + + + + + + + + BitcoinAmountField + QSpinBox +
bitcoinamountfield.h
+
+
+ + + + chkReqPayment + clicked(bool) + lnReqAmount + setEnabled(bool) + + + 92 + 285 + + + 98 + 311 + + + + +
diff --git a/src/qt/forms/rpcconsole.ui b/src/qt/forms/rpcconsole.ui new file mode 100644 index 0000000..15c4a2f --- /dev/null +++ b/src/qt/forms/rpcconsole.ui @@ -0,0 +1,456 @@ + + + RPCConsole + + + + 0 + 0 + 740 + 450 + + + + CryptoLottoToken - Debug window + + + + + + 0 + + + + &Information + + + + 12 + + + + + + 75 + true + + + + CryptoLottoToken Core + + + + + + + Client name + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Client version + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Using OpenSSL version + + + 10 + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Build date + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Startup time + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + 75 + true + + + + Network + + + + + + + Number of connections + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + On testnet + + + + + + + false + + + + + + + + + + + 75 + true + + + + Block chain + + + + + + + Current number of blocks + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Estimated total blocks + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Last block time + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Qt::Vertical + + + + 20 + 20 + + + + + + + + + 75 + true + + + + Debug log file + + + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + &Open + + + false + + + + + + + + 75 + true + + + + Command-line options + + + + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + &Show + + + false + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + &Console + + + + 3 + + + + + + 0 + 100 + + + + true + + + false + + + 2 + + + + + + + 3 + + + + + > + + + + + + + + + + + 24 + 24 + + + + Clear console + + + + + + + :/icons/remove:/icons/remove + + + Ctrl+L + + + false + + + + + + + + + + + + + + + + diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui new file mode 100644 index 0000000..c95efc3 --- /dev/null +++ b/src/qt/forms/sendcoinsdialog.ui @@ -0,0 +1,776 @@ + + + SendCoinsDialog + + + + 0 + 0 + 850 + 400 + + + + Send Coins + + + + 8 + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + -1 + + + 0 + + + 0 + + + 0 + + + 6 + + + + + 0 + + + 10 + + + 10 + + + + + 15 + + + + + + 0 + 0 + + + + + 75 + true + + + + font-weight:bold; + + + Coin Control Features + + + + + + + + + 8 + + + 10 + + + + + + + + Inputs... + + + + + + + automatically selected + + + 5 + + + + + + + + 75 + true + + + + color:red;font-weight:bold; + + + Insufficient funds! + + + 5 + + + + + + + Qt::Horizontal + + + + 40 + 1 + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + 0 + + + + + 20 + + + 0 + + + 10 + + + + + 10 + + + 14 + + + 10 + + + 4 + + + 6 + + + + + font-weight:bold; + + + Quantity: + + + 0 + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0 + + + 0 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + font-weight:bold; + + + Bytes: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + 10 + + + 14 + + + 6 + + + 4 + + + 6 + + + + + font-weight:bold; + + + Amount: + + + 0 + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 BTC + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + font-weight:bold; + + + Priority: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + medium + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + 10 + + + 14 + + + 6 + + + 4 + + + 6 + + + + + font-weight:bold; + + + Fee: + + + 0 + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 BTC + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + font-weight:bold; + + + Low Output: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + no + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + 10 + + + 14 + + + 6 + + + 4 + + + 6 + + + + + font-weight:bold; + + + After Fee: + + + 0 + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 BTC + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + font-weight:bold; + + + Change: + + + + + + + + Monospace + 10 + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 BTC + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + + + + + 12 + + + QLayout::SetDefaultConstraint + + + 5 + + + 5 + + + + + custom change address + + + + + + + false + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + 3 + + + + + + + + + Qt::Vertical + + + + 800 + 1 + + + + + + + + + + + + + true + + + + + 0 + 0 + 830 + 165 + + + + + 0 + + + + + 6 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + Send to multiple recipients at once + + + Add &Recipient + + + + :/icons/add:/icons/add + + + false + + + + + + + + 0 + 0 + + + + Remove all transaction fields + + + Clear &All + + + + :/icons/remove:/icons/remove + + + 300 + + + false + + + + + + + 3 + + + + + Balance: + + + + + + + IBeamCursor + + + 123.456 LTC + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 150 + 0 + + + + Confirm the send action + + + S&end + + + + :/icons/send:/icons/send + + + true + + + + + + + + + + + + diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui new file mode 100644 index 0000000..28553c5 --- /dev/null +++ b/src/qt/forms/sendcoinsentry.ui @@ -0,0 +1,159 @@ + + + SendCoinsEntry + + + + 0 + 0 + 729 + 136 + + + + Form + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + 12 + + + + + A&mount: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + payAmount + + + + + + + Pay &To: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + payTo + + + + + + + + + + &Label: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + addAsLabel + + + + + + + 0 + + + + + The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) + + + 34 + + + + + + + Choose address from address book + + + + + + + :/icons/address-book:/icons/address-book + + + Alt+A + + + + + + + Paste address from clipboard + + + + + + + :/icons/editpaste:/icons/editpaste + + + Alt+P + + + + + + + Remove this recipient + + + + + + + :/icons/remove:/icons/remove + + + + + + + + + Enter a label for this address to add it to your address book + + + + + + + + BitcoinAmountField + QLineEdit +
bitcoinamountfield.h
+ 1 +
+ + QValidatedLineEdit + QLineEdit +
qvalidatedlineedit.h
+
+
+ + + + +
diff --git a/src/qt/forms/signverifymessagedialog.ui b/src/qt/forms/signverifymessagedialog.ui new file mode 100644 index 0000000..2609405 --- /dev/null +++ b/src/qt/forms/signverifymessagedialog.ui @@ -0,0 +1,396 @@ + + + SignVerifyMessageDialog + + + + 0 + 0 + 700 + 380 + + + + Signatures - Sign / Verify a Message + + + true + + + + + + 0 + + + + &Sign Message + + + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + Qt::PlainText + + + true + + + + + + + 0 + + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + 34 + + + + + + + Choose an address from the address book + + + + + + + :/icons/address-book:/icons/address-book + + + Alt+A + + + false + + + + + + + Paste address from clipboard + + + + + + + :/icons/editpaste:/icons/editpaste + + + Alt+P + + + false + + + + + + + + + Enter the message you want to sign here + + + + + + + Signature + + + Qt::PlainText + + + + + + + 0 + + + + + + true + + + + true + + + + + + + Copy the current signature to the system clipboard + + + + + + + :/icons/editcopy:/icons/editcopy + + + false + + + + + + + + + + + Sign the message to prove you own this CryptoLottoToken address + + + Sign &Message + + + + :/icons/edit:/icons/edit + + + false + + + + + + + Reset all sign message fields + + + Clear &All + + + + :/icons/remove:/icons/remove + + + false + + + + + + + Qt::Horizontal + + + + 40 + 48 + + + + + + + + + 75 + true + + + + + + + true + + + + + + + Qt::Horizontal + + + + 40 + 48 + + + + + + + + + + + &Verify Message + + + + + + Enter the signing 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. + + + Qt::PlainText + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + 0 + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + 34 + + + + + + + Choose an address from the address book + + + + + + + :/icons/address-book:/icons/address-book + + + Alt+A + + + false + + + + + + + + + + + + + + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + + + Verify &Message + + + + :/icons/transaction_0:/icons/transaction_0 + + + false + + + + + + + Reset all verify message fields + + + Clear &All + + + + :/icons/remove:/icons/remove + + + false + + + + + + + Qt::Horizontal + + + + 40 + 48 + + + + + + + + + 75 + true + + + + + + + true + + + + + + + Qt::Horizontal + + + + 40 + 48 + + + + + + + + + + + + + + + QValidatedLineEdit + QLineEdit +
qvalidatedlineedit.h
+
+
+ + + + +
diff --git a/src/qt/forms/transactiondescdialog.ui b/src/qt/forms/transactiondescdialog.ui new file mode 100644 index 0000000..b38dffc --- /dev/null +++ b/src/qt/forms/transactiondescdialog.ui @@ -0,0 +1,74 @@ + + + TransactionDescDialog + + + + 0 + 0 + 620 + 250 + + + + Transaction details + + + + + + This pane shows a detailed description of the transaction + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox + accepted() + TransactionDescDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + TransactionDescDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h new file mode 100644 index 0000000..9241783 --- /dev/null +++ b/src/qt/guiconstants.h @@ -0,0 +1,34 @@ +#ifndef GUICONSTANTS_H +#define GUICONSTANTS_H + +/* Milliseconds between model updates */ +static const int MODEL_UPDATE_DELAY = 250; + +/* AskPassphraseDialog -- Maximum passphrase length */ +static const int MAX_PASSPHRASE_SIZE = 1024; + +/* BitcoinGUI -- Size of icons in status bar */ +static const int STATUSBAR_ICONSIZE = 16; + +/* Invalid field background style */ +#define STYLE_INVALID "background:#FF8080" + +/* Transaction list -- unconfirmed transaction */ +#define COLOR_UNCONFIRMED QColor(128, 128, 128) +/* Transaction list -- negative amount */ +#define COLOR_NEGATIVE QColor(255, 0, 0) +/* Transaction list -- bare address (without label) */ +#define COLOR_BAREADDRESS QColor(140, 140, 140) + +/* Tooltips longer than this (in characters) are converted into rich text, + so that they can be word-wrapped. + */ +static const int TOOLTIP_WRAP_THRESHOLD = 80; + +/* Maximum allowed URI length */ +static const int MAX_URI_LENGTH = 255; + +/* QRCodeDialog -- size of exported QR Code image */ +#define EXPORT_IMAGE_SIZE 256 + +#endif // GUICONSTANTS_H diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp new file mode 100644 index 0000000..bb840df --- /dev/null +++ b/src/qt/guiutil.cpp @@ -0,0 +1,535 @@ +#include + +#include "guiutil.h" + +#include "bitcoinaddressvalidator.h" +#include "walletmodel.h" +#include "bitcoinunits.h" + +#include "util.h" +#include "init.h" + +#include +#include +#include +#include +#if QT_VERSION >= 0x050000 +#include +#else +#include +#endif +#include // for Qt::mightBeRichText +#include +#include +#include +#include +#include + +#include +#include + +#ifdef WIN32 +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0501 +#ifdef _WIN32_IE +#undef _WIN32_IE +#endif +#define _WIN32_IE 0x0501 +#define WIN32_LEAN_AND_MEAN 1 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include "shlwapi.h" +#include "shlobj.h" +#include "shellapi.h" +#endif + +namespace GUIUtil { + +QString dateTimeStr(const QDateTime &date) +{ + return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm"); +} + +QString dateTimeStr(qint64 nTime) +{ + return dateTimeStr(QDateTime::fromTime_t((qint32)nTime)); +} + +QFont bitcoinAddressFont() +{ + QFont font("Monospace"); + font.setStyleHint(QFont::TypeWriter); + return font; +} + +void setupAddressWidget(QLineEdit *widget, QWidget *parent) +{ + widget->setMaxLength(BitcoinAddressValidator::MaxAddressLength); + widget->setValidator(new BitcoinAddressValidator(parent)); + widget->setFont(bitcoinAddressFont()); +} + +void setupAmountWidget(QLineEdit *widget, QWidget *parent) +{ + QDoubleValidator *amountValidator = new QDoubleValidator(parent); + amountValidator->setDecimals(8); + amountValidator->setBottom(0.0); + widget->setValidator(amountValidator); + widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter); +} + +bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out) +{ + // return if URI is not valid or is no bitcoin URI + if(!uri.isValid() || uri.scheme() != QString("CryptoLottoToken")) + return false; + + SendCoinsRecipient rv; + rv.address = uri.path(); + rv.amount = 0; + +#if QT_VERSION < 0x050000 + QList > items = uri.queryItems(); +#else + QUrlQuery uriQuery(uri); + QList > items = uriQuery.queryItems(); +#endif + for (QList >::iterator i = items.begin(); i != items.end(); i++) + { + bool fShouldReturnFalse = false; + if (i->first.startsWith("req-")) + { + i->first.remove(0, 4); + fShouldReturnFalse = true; + } + + if (i->first == "label") + { + rv.label = i->second; + fShouldReturnFalse = false; + } + else if (i->first == "amount") + { + if(!i->second.isEmpty()) + { + if(!BitcoinUnits::parse(BitcoinUnits::BTC, i->second, &rv.amount)) + { + return false; + } + } + fShouldReturnFalse = false; + } + + if (fShouldReturnFalse) + return false; + } + if(out) + { + *out = rv; + } + return true; +} + +bool parseBitcoinURI(QString uri, SendCoinsRecipient *out) +{ + // Convert bitcoin:// to bitcoin: + // + // Cannot handle this later, because bitcoin:// will cause Qt to see the part after // as host, + // which will lower-case it (and thus invalidate the address). + if(uri.startsWith("CryptoLottoToken://")) + { + uri.replace(0, 11, "CryptoLottoToken:"); + } + QUrl uriInstance(uri); + return parseBitcoinURI(uriInstance, out); +} + +QString HtmlEscape(const QString& str, bool fMultiLine) +{ +#if QT_VERSION < 0x050000 + QString escaped = Qt::escape(str); +#else + QString escaped = str.toHtmlEscaped(); +#endif + if(fMultiLine) + { + escaped = escaped.replace("\n", "
\n"); + } + return escaped; +} + +QString HtmlEscape(const std::string& str, bool fMultiLine) +{ + return HtmlEscape(QString::fromStdString(str), fMultiLine); +} + +void copyEntryData(QAbstractItemView *view, int column, int role) +{ + if(!view || !view->selectionModel()) + return; + QModelIndexList selection = view->selectionModel()->selectedRows(column); + + if(!selection.isEmpty()) + { + // Copy first item (global clipboard) + QApplication::clipboard()->setText(selection.at(0).data(role).toString(), QClipboard::Clipboard); + // Copy first item (global mouse selection for e.g. X11 - NOP on Windows) + QApplication::clipboard()->setText(selection.at(0).data(role).toString(), QClipboard::Selection); + } +} + +void setClipboard(const QString& str) +{ + QApplication::clipboard()->setText(str, QClipboard::Clipboard); + QApplication::clipboard()->setText(str, QClipboard::Selection); +} + +QString getSaveFileName(QWidget *parent, const QString &caption, + const QString &dir, + const QString &filter, + QString *selectedSuffixOut) +{ + QString selectedFilter; + QString myDir; + if(dir.isEmpty()) // Default to user documents location + { +#if QT_VERSION < 0x050000 + myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); +#else + myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); +#endif + } + else + { + myDir = dir; + } + QString result = QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter); + + /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */ + QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]"); + QString selectedSuffix; + if(filter_re.exactMatch(selectedFilter)) + { + selectedSuffix = filter_re.cap(1); + } + + /* Add suffix if needed */ + QFileInfo info(result); + if(!result.isEmpty()) + { + if(info.suffix().isEmpty() && !selectedSuffix.isEmpty()) + { + /* No suffix specified, add selected suffix */ + if(!result.endsWith(".")) + result.append("."); + result.append(selectedSuffix); + } + } + + /* Return selected suffix if asked to */ + if(selectedSuffixOut) + { + *selectedSuffixOut = selectedSuffix; + } + return result; +} + +Qt::ConnectionType blockingGUIThreadConnection() +{ + if(QThread::currentThread() != qApp->thread()) + { + return Qt::BlockingQueuedConnection; + } + else + { + return Qt::DirectConnection; + } +} + +bool checkPoint(const QPoint &p, const QWidget *w) +{ + QWidget *atW = QApplication::widgetAt(w->mapToGlobal(p)); + if (!atW) return false; + return atW->topLevelWidget() == w; +} + +bool isObscured(QWidget *w) +{ + return !(checkPoint(QPoint(0, 0), w) + && checkPoint(QPoint(w->width() - 1, 0), w) + && checkPoint(QPoint(0, w->height() - 1), w) + && checkPoint(QPoint(w->width() - 1, w->height() - 1), w) + && checkPoint(QPoint(w->width() / 2, w->height() / 2), w)); +} + +void openDebugLogfile() +{ + boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; + + /* Open debug.log with the associated application */ + if (boost::filesystem::exists(pathDebug)) + QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(pathDebug.string()))); +} + +ToolTipToRichTextFilter::ToolTipToRichTextFilter(int size_threshold, QObject *parent) : + QObject(parent), size_threshold(size_threshold) +{ + +} + +bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt) +{ + if(evt->type() == QEvent::ToolTipChange) + { + QWidget *widget = static_cast(obj); + QString tooltip = widget->toolTip(); + if(tooltip.size() > size_threshold && !tooltip.startsWith("") && !Qt::mightBeRichText(tooltip)) + { + // Prefix to make sure Qt detects this as rich text + // Escape the current message as HTML and replace \n by
+ tooltip = "" + HtmlEscape(tooltip, true); + widget->setToolTip(tooltip); + return true; + } + } + return QObject::eventFilter(obj, evt); +} + +#ifdef WIN32 +boost::filesystem::path static StartupShortcutPath() +{ + return GetSpecialFolderPath(CSIDL_STARTUP) / "CryptoLottoToken.lnk"; +} + +bool GetStartOnSystemStartup() +{ + // check for Bitcoin.lnk + return boost::filesystem::exists(StartupShortcutPath()); +} + +bool SetStartOnSystemStartup(bool fAutoStart) +{ + // If the shortcut exists already, remove it for updating + boost::filesystem::remove(StartupShortcutPath()); + + if (fAutoStart) + { + CoInitialize(NULL); + + // Get a pointer to the IShellLink interface. + IShellLink* psl = NULL; + HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, + CLSCTX_INPROC_SERVER, IID_IShellLink, + reinterpret_cast(&psl)); + + if (SUCCEEDED(hres)) + { + // Get the current executable path + TCHAR pszExePath[MAX_PATH]; + GetModuleFileName(NULL, pszExePath, sizeof(pszExePath)); + + TCHAR pszArgs[5] = TEXT("-min"); + + // Set the path to the shortcut target + psl->SetPath(pszExePath); + PathRemoveFileSpec(pszExePath); + psl->SetWorkingDirectory(pszExePath); + psl->SetShowCmd(SW_SHOWMINNOACTIVE); + psl->SetArguments(pszArgs); + + // Query IShellLink for the IPersistFile interface for + // saving the shortcut in persistent storage. + IPersistFile* ppf = NULL; + hres = psl->QueryInterface(IID_IPersistFile, + reinterpret_cast(&ppf)); + if (SUCCEEDED(hres)) + { + WCHAR pwsz[MAX_PATH]; + // Ensure that the string is ANSI. + MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().string().c_str(), -1, pwsz, MAX_PATH); + // Save the link by calling IPersistFile::Save. + hres = ppf->Save(pwsz, TRUE); + ppf->Release(); + psl->Release(); + CoUninitialize(); + return true; + } + psl->Release(); + } + CoUninitialize(); + return false; + } + return true; +} + +#elif defined(LINUX) + +// Follow the Desktop Application Autostart Spec: +// http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html + +boost::filesystem::path static GetAutostartDir() +{ + namespace fs = boost::filesystem; + + char* pszConfigHome = getenv("XDG_CONFIG_HOME"); + if (pszConfigHome) return fs::path(pszConfigHome) / "autostart"; + char* pszHome = getenv("HOME"); + if (pszHome) return fs::path(pszHome) / ".config" / "autostart"; + return fs::path(); +} + +boost::filesystem::path static GetAutostartFilePath() +{ + return GetAutostartDir() / "CryptoLottoToken.desktop"; +} + +bool GetStartOnSystemStartup() +{ + boost::filesystem::ifstream optionFile(GetAutostartFilePath()); + if (!optionFile.good()) + return false; + // Scan through file for "Hidden=true": + std::string line; + while (!optionFile.eof()) + { + getline(optionFile, line); + if (line.find("Hidden") != std::string::npos && + line.find("true") != std::string::npos) + return false; + } + optionFile.close(); + + return true; +} + +bool SetStartOnSystemStartup(bool fAutoStart) +{ + if (!fAutoStart) + boost::filesystem::remove(GetAutostartFilePath()); + else + { + char pszExePath[MAX_PATH+1]; + memset(pszExePath, 0, sizeof(pszExePath)); + if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1) + return false; + + boost::filesystem::create_directories(GetAutostartDir()); + + boost::filesystem::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc); + if (!optionFile.good()) + return false; + // Write a bitcoin.desktop file to the autostart directory: + optionFile << "[Desktop Entry]\n"; + optionFile << "Type=Application\n"; + optionFile << "Name=CryptoLottoToken\n"; + optionFile << "Exec=" << pszExePath << " -min\n"; + optionFile << "Terminal=false\n"; + optionFile << "Hidden=false\n"; + optionFile.close(); + } + return true; +} + +#elif defined(Q_OS_MAC) +// based on: https://github.com/Mozketo/LaunchAtLoginController/blob/master/LaunchAtLoginController.m + +#include +#include + +LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl); +LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl) +{ + // loop through the list of startup items and try to find the bitcoin app + CFArrayRef listSnapshot = LSSharedFileListCopySnapshot(list, NULL); + for(int i = 0; i < CFArrayGetCount(listSnapshot); i++) { + LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(listSnapshot, i); + UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes; + CFURLRef currentItemURL = NULL; + LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, NULL); + if(currentItemURL && CFEqual(currentItemURL, findUrl)) { + // found + CFRelease(currentItemURL); + return item; + } + if(currentItemURL) { + CFRelease(currentItemURL); + } + } + return NULL; +} + +bool GetStartOnSystemStartup() +{ + CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle()); + LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); + LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl); + return !!foundItem; // return boolified object +} + +bool SetStartOnSystemStartup(bool fAutoStart) +{ + CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle()); + LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); + LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl); + + if(fAutoStart && !foundItem) { + // add bitcoin app to startup item list + LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst, NULL, NULL, bitcoinAppUrl, NULL, NULL); + } + else if(!fAutoStart && foundItem) { + // remove item + LSSharedFileListItemRemove(loginItems, foundItem); + } + return true; +} +#else + +bool GetStartOnSystemStartup() { return false; } +bool SetStartOnSystemStartup(bool fAutoStart) { return false; } + +#endif + +HelpMessageBox::HelpMessageBox(QWidget *parent) : + QMessageBox(parent) +{ + header = tr("CryptoLottoToken-Qt") + " " + tr("version") + " " + + QString::fromStdString(FormatFullVersion()) + "\n\n" + + tr("Usage:") + "\n" + + " CryptoLottoToken-qt [" + tr("command-line options") + "] " + "\n"; + + coreOptions = QString::fromStdString(HelpMessage()); + + uiOptions = tr("UI options") + ":\n" + + " -lang= " + tr("Set language, for example \"de_DE\" (default: system locale)") + "\n" + + " -min " + tr("Start minimized") + "\n" + + " -splash " + tr("Show splash screen on startup (default: 1)") + "\n"; + + setWindowTitle(tr("CryptoLottoToken-Qt")); + setTextFormat(Qt::PlainText); + // setMinimumWidth is ignored for QMessageBox so put in non-breaking spaces to make it wider. + setText(header + QString(QChar(0x2003)).repeated(50)); + setDetailedText(coreOptions + "\n" + uiOptions); +} + +void HelpMessageBox::printToConsole() +{ + // On other operating systems, the expected action is to print the message to the console. + QString strUsage = header + "\n" + coreOptions + "\n" + uiOptions; + fprintf(stdout, "%s", strUsage.toStdString().c_str()); +} + +void HelpMessageBox::showOrPrint() +{ +#if defined(WIN32) + // On Windows, show a message box, as there is no stderr/stdout in windowed applications + exec(); +#else + // On other operating systems, print help text to console + printToConsole(); +#endif +} + +} // namespace GUIUtil diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h new file mode 100644 index 0000000..738c906 --- /dev/null +++ b/src/qt/guiutil.h @@ -0,0 +1,123 @@ +#ifndef GUIUTIL_H +#define GUIUTIL_H + +#include +#include +#include + +class SendCoinsRecipient; + +QT_BEGIN_NAMESPACE +class QFont; +class QLineEdit; +class QWidget; +class QDateTime; +class QUrl; +class QAbstractItemView; +QT_END_NAMESPACE + +/** Utility functions used by the Bitcoin Qt UI. + */ +namespace GUIUtil +{ + // Create human-readable string from date + QString dateTimeStr(const QDateTime &datetime); + QString dateTimeStr(qint64 nTime); + + // Render Bitcoin addresses in monospace font + QFont bitcoinAddressFont(); + + // Set up widgets for address and amounts + void setupAddressWidget(QLineEdit *widget, QWidget *parent); + void setupAmountWidget(QLineEdit *widget, QWidget *parent); + + // Parse "bitcoin:" URI into recipient object, return true on successful parsing + // See Bitcoin URI definition discussion here: https://bitcointalk.org/index.php?topic=33490.0 + bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out); + bool parseBitcoinURI(QString uri, SendCoinsRecipient *out); + + // HTML escaping for rich text controls + QString HtmlEscape(const QString& str, bool fMultiLine=false); + QString HtmlEscape(const std::string& str, bool fMultiLine=false); + + /** Copy a field of the currently selected entry of a view to the clipboard. 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 + */ + void copyEntryData(QAbstractItemView *view, int column, int role=Qt::EditRole); + + void setClipboard(const QString& str); + + /** Get save filename, mimics QFileDialog::getSaveFileName, except that it appends a default suffix + when no suffix is provided by the user. + + @param[in] parent Parent window (or 0) + @param[in] caption Window caption (or empty, for default) + @param[in] dir Starting directory (or empty, to default to documents directory) + @param[in] filter Filter specification such as "Comma Separated Files (*.csv)" + @param[out] selectedSuffixOut Pointer to return the suffix (file type) that was selected (or 0). + Can be useful when choosing the save file format based on suffix. + */ + QString getSaveFileName(QWidget *parent=0, const QString &caption=QString(), + const QString &dir=QString(), const QString &filter=QString(), + QString *selectedSuffixOut=0); + + /** Get connection type to call object slot in GUI thread with invokeMethod. The call will be blocking. + + @returns If called from the GUI thread, return a Qt::DirectConnection. + If called from another thread, return a Qt::BlockingQueuedConnection. + */ + Qt::ConnectionType blockingGUIThreadConnection(); + + // Determine whether a widget is hidden behind other windows + bool isObscured(QWidget *w); + + // Open debug.log + void openDebugLogfile(); + + /** Qt event filter that intercepts ToolTipChange events, and replaces the tooltip with a rich text + representation if needed. This assures that Qt can word-wrap long tooltip messages. + Tooltips longer than the provided size threshold (in characters) are wrapped. + */ + class ToolTipToRichTextFilter : public QObject + { + Q_OBJECT + + public: + explicit ToolTipToRichTextFilter(int size_threshold, QObject *parent = 0); + + protected: + bool eventFilter(QObject *obj, QEvent *evt); + + private: + int size_threshold; + }; + + bool GetStartOnSystemStartup(); + bool SetStartOnSystemStartup(bool fAutoStart); + + /** Help message for Bitcoin-Qt, shown with --help. */ + class HelpMessageBox : public QMessageBox + { + Q_OBJECT + + public: + HelpMessageBox(QWidget *parent = 0); + + /** Show message box or print help message to standard output, based on operating system. */ + void showOrPrint(); + + /** Print help message to console */ + void printToConsole(); + + private: + QString header; + QString coreOptions; + QString uiOptions; + }; + +} // namespace GUIUtil + +#endif // GUIUTIL_H diff --git a/src/qt/locale/bitcoin_af_ZA.qm b/src/qt/locale/bitcoin_af_ZA.qm new file mode 100644 index 0000000..1dc3765 Binary files /dev/null and b/src/qt/locale/bitcoin_af_ZA.qm differ diff --git a/src/qt/locale/bitcoin_af_ZA.ts b/src/qt/locale/bitcoin_af_ZA.ts new file mode 100644 index 0000000..237f418 --- /dev/null +++ b/src/qt/locale/bitcoin_af_ZA.ts @@ -0,0 +1,2917 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> weergawe + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + Adres Boek + + + + Double-click to edit address or label + Dubbel-klik om die adres of etiket te wysig + + + + Create a new address + Skep 'n nuwe adres + + + + Copy the currently selected address to the system clipboard + Maak 'n kopie van die huidige adres na die stelsel klipbord + + + + &New Address + + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + + + + + &Copy Address + + + + + Show &QR Code + + + + + Sign a message to prove you own a CryptoLottoToken address + + + + + Sign &Message + Teken &Boodskap + + + + Delete the currently selected address from the list + + + + + Export the data in the current tab to a file + + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + + + + + &Verify Message + + + + + &Delete + &Verwyder + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + + + + + &Edit + + + + + Send &Coins + Stuur &Muntstukke + + + + Export Address Book Data + Voer die Adresboek Data Uit + + + + Comma separated file (*.csv) + + + + + Error exporting + Fout uitvoering + + + + Could not write to file %1. + Kon nie na die %1 lêer skryf nie + + + + AddressTableModel + + + Label + Etiket + + + + Address + Adres + + + + (no label) + (geen etiket) + + + + AskPassphraseDialog + + + Passphrase Dialog + + + + + Enter passphrase + Tik Wagwoord in + + + + New passphrase + Nuwe wagwoord + + + + Repeat new passphrase + Herhaal nuwe wagwoord + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Tik die nuwe wagwoord vir die beursie in.<br/>Gebruik asseblief 'n wagwoord van <b>ten minste 10 ewekansige karakters</b>, of <b>agt (8) of meer woorde.</b> + + + + Encrypt wallet + Enkripteer beursie + + + + This operation needs your wallet passphrase to unlock the wallet. + Hierdie operasie benodig 'n wagwoord om die beursie oop te sluit. + + + + Unlock wallet + Sluit beursie oop + + + + This operation needs your wallet passphrase to decrypt the wallet. + Hierdie operasie benodig 'n wagwoord om die beursie oop te sluit. + + + + Decrypt wallet + Sluit beursie oop + + + + Change passphrase + Verander wagwoord + + + + Enter the old and new passphrase to the wallet. + Tik asseblief die ou en nuwe wagwoord vir die beursie in. + + + + Confirm wallet encryption + Bevestig beursie enkripsie. + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + + + + + Are you sure you wish to encrypt your wallet? + + + + + 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. + + + + + + Warning: The Caps Lock key is on! + + + + + + Wallet encrypted + Die beursie is nou bewaak + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + + + + + + + + Wallet encryption failed + Die beursie kon nie bewaak word nie + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Beursie bewaaking het misluk as gevolg van 'n interne fout. Die beursie is nie bewaak nie! + + + + + The supplied passphrases do not match. + Die wagwoord stem nie ooreen nie + + + + Wallet unlock failed + Beursie oopsluiting het misluk + + + + + + The passphrase entered for the wallet decryption was incorrect. + Die wagwoord wat ingetik was om die beursie oop te sluit, was verkeerd. + + + + Wallet decryption failed + Beursie dekripsie het misluk + + + + Wallet passphrase was successfully changed. + + + + + BitcoinGUI + + + Sign &message... + + + + + Synchronizing with network... + Sinchroniseer met die netwerk ... + + + + &Overview + &Oorsig + + + + Show general overview of wallet + Wys algemene oorsig van die beursie + + + + &Transactions + &Transaksies + + + + Browse transaction history + Besoek transaksie geskiedenis + + + + Edit the list of stored addresses and labels for sending + Wysig die lys van gestoorde adresse en etikette + + + + Show the list of addresses for receiving payments + Wys die lys van adresse vir die ontvangs van betalings + + + + E&xit + S&luit af + + + + Quit application + Sluit af + + + + Show information about CryptoLottoToken + Wys inligting oor CryptoLottoToken + + + + About &Qt + + + + + Show information about Qt + + + + + &Options... + &Opsies + + + + &Encrypt Wallet... + + + + + &Backup Wallet... + + + + + &Change Passphrase... + + + + + Importing blocks from disk... + + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + + + + + Modify configuration options for CryptoLottoToken + + + + + Backup wallet to another location + + + + + Change the passphrase used for wallet encryption + + + + + &Debug window + + + + + Open debugging and diagnostic console + + + + + &Verify message... + + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Beursie + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + + + + + &Show / Hide + + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + &Lêer + + + + &Settings + &Instellings + + + + &Help + &Hulp + + + + Tabs toolbar + Blad nutsbalk + + + + + [testnet] + + + + + CryptoLottoToken client + + + + + %n active connection(s) to CryptoLottoToken network + + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + %1 agter + + + + Last received block was generated %1 ago. + Ontvangs van laaste blok is %1 terug. + + + + Transactions after this will not yet be visible. + + + + + Error + Fout + + + + Warning + + + + + Information + Informasie + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + + + + + Catching up... + + + + + Confirm transaction fee + + + + + Sent transaction + + + + + Incoming transaction + + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + + + + + + URI handling + + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + + + + + EditAddressDialog + + + Edit Address + + + + + &Label + + + + + The label associated with this address book entry + + + + + &Address + + + + + The address associated with this address book entry. This can only be modified for sending addresses. + + + + + New receiving address + + + + + New sending address + + + + + Edit receiving address + + + + + Edit sending address + + + + + The entered address "%1" is already in the address book. + + + + + The entered address "%1" is not a valid CryptoLottoToken address. + + + + + Could not unlock wallet. + + + + + New key generation failed. + + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + + + + + version + + + + + Usage: + Gebruik: + + + + command-line options + + + + + UI options + + + + + Set language, for example "de_DE" (default: system locale) + + + + + Start minimized + + + + + Show splash screen on startup (default: 1) + + + + + OptionsDialog + + + Options + + + + + &Main + + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + + + + + Automatically start CryptoLottoToken after logging in to the system. + + + + + &Start CryptoLottoToken on system login + + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + + + + + Map port using &UPnP + + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + + + + + &Connect through SOCKS proxy: + + + + + Proxy &IP: + + + + + IP address of the proxy (e.g. 127.0.0.1) + + + + + &Port: + + + + + Port of the proxy (e.g. 9050) + + + + + SOCKS &Version: + + + + + SOCKS version of the proxy (e.g. 5) + + + + + &Window + + + + + Show only a tray icon after minimizing the window. + + + + + &Minimize to the tray instead of the taskbar + + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + + + + + M&inimize on close + + + + + &Display + + + + + User Interface &language: + + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + + + + + &Unit to show amounts in: + + + + + Choose the default subdivision unit to show in the interface and when sending coins. + + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + + + &Display addresses in transaction list + + + + + &OK + + + + + &Cancel + + + + + &Apply + + + + + default + + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + + + + + + This setting will take effect after restarting CryptoLottoToken. + + + + + The supplied proxy address is invalid. + + + + + OverviewPage + + + Form + + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + + + + + Balance: + + + + + Unconfirmed: + + + + + Wallet + Beursie + + + + Immature: + + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + + + + + Your current balance + + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + + + + + + out of sync + + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + + + + + Request Payment + + + + + Amount: + + + + + Label: + + + + + Message: + + + + + &Save As... + + + + + Error encoding URI into QR Code. + + + + + The entered amount is invalid, please check. + + + + + Resulting URI too long, try to reduce the text for label / message. + + + + + Save QR Code + + + + + PNG Images (*.png) + + + + + RPCConsole + + + Client name + + + + + + + + + + + + + + N/A + + + + + Client version + + + + + &Information + + + + + Using OpenSSL version + + + + + Startup time + + + + + Network + + + + + Number of connections + + + + + On testnet + + + + + Block chain + + + + + Current number of blocks + + + + + Estimated total blocks + + + + + Last block time + + + + + &Open + + + + + Command-line options + + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + + &Show + + + + + &Console + + + + + Build date + + + + + CryptoLottoToken - Debug window + + + + + CryptoLottoToken Core + + + + + Debug log file + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + + + + + Welcome to the CryptoLottoToken RPC console. + + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + + + + + Type <b>help</b> for an overview of available commands. + + + + + SendCoinsDialog + + + + + + + + + + Send Coins + + + + + Send to multiple recipients at once + + + + + Add &Recipient + + + + + Remove all transaction fields + + + + + Clear &All + + + + + Balance: + + + + + 123.456 BTC + + + + + Confirm the send action + + + + + S&end + S&tuur + + + + <b>%1</b> to %2 (%3) + + + + + Confirm send coins + + + + + Are you sure you want to send %1? + + + + + and + + + + + The recipient address is not valid, please recheck. + + + + + The amount to pay must be larger than 0. + + + + + The amount exceeds your balance. + + + + + The total exceeds your balance when the %1 transaction fee is included. + + + + + Duplicate address found, can only send to each address once per send operation. + + + + + Error: Transaction creation failed! + + + + + 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. + + + + + SendCoinsEntry + + + Form + + + + + A&mount: + + + + + Pay &To: + + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Die adres waarheen die betaling gestuur moet word (b.v. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + + + + + &Label: + + + + + Choose address from address book + + + + + Alt+A + + + + + Paste address from clipboard + + + + + Alt+P + + + + + Remove this recipient + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + + + + + &Sign Message + &Teken boodskap + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Choose an address from the address book + + + + + + Alt+A + + + + + Paste address from clipboard + + + + + Alt+P + + + + + Enter the message you want to sign here + + + + + Signature + Handtekening + + + + Copy the current signature to the system clipboard + + + + + Sign the message to prove you own this CryptoLottoToken address + + + + + Sign &Message + Teken &Boodskap + + + + Reset all sign message fields + + + + + + Clear &All + + + + + &Verify Message + + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + + + + + Verify &Message + + + + + Reset all verify message fields + + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Click "Sign Message" to generate signature + + + + + Enter CryptoLottoToken signature + + + + + + The entered address is invalid. + + + + + + + + Please check the address and try again. + + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + + + + + Private key for the entered address is not available. + + + + + Message signing failed. + + + + + Message signed. + + + + + The signature could not be decoded. + + + + + + Please check the signature and try again. + + + + + The signature did not match the message digest. + + + + + Message verification failed. + + + + + Message verified. + + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + + + + + TransactionDesc + + + Open until %1 + + + + + %1/offline + + + + + %1/unconfirmed + + + + + %1 confirmations + + + + + Status + + + + + , broadcast through %n node(s) + + + + + Date + Datum + + + + Source + + + + + Generated + + + + + + From + Van + + + + + + To + Na + + + + + own address + eie adres + + + + label + etiket + + + + + + + + Credit + Krediet + + + + matures in %n more block(s) + + + + + not accepted + nie aanvaar nie + + + + + + + Debit + Debiet + + + + Transaction fee + Transaksie fooi + + + + Net amount + Netto bedrag + + + + Message + Boodskap + + + + Comment + + + + + Transaction ID + Transaksie ID + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + + + + + Debug information + + + + + Transaction + + + + + Inputs + + + + + Amount + Bedrag + + + + true + waar + + + + false + onwaar + + + + , has not been successfully broadcast yet + + + + + Open for %n more block(s) + + + + + unknown + + + + + TransactionDescDialog + + + Transaction details + + + + + This pane shows a detailed description of the transaction + + + + + TransactionTableModel + + + Date + Datum + + + + Type + Tipe + + + + Address + Adres + + + + Amount + Bedrag + + + + Open for %n more block(s) + + + + + Open until %1 + + + + + Offline (%1 confirmations) + + + + + Unconfirmed (%1 of %2 confirmations) + + + + + Confirmed (%1 confirmations) + + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + + + + + Generated but not accepted + + + + + Received with + Ontvang met + + + + Received from + + + + + Sent to + Gestuur na + + + + Payment to yourself + + + + + Mined + Gemyn + + + + (n/a) + (n.v.t) + + + + Transaction status. Hover over this field to show number of confirmations. + + + + + Date and time that the transaction was received. + Datum en tyd wat die transaksie ontvang was. + + + + Type of transaction. + Tipe transaksie. + + + + Destination address of transaction. + + + + + Amount removed from or added to balance. + + + + + TransactionView + + + + All + Alles + + + + Today + Vandag + + + + This week + Hierdie week + + + + This month + Hierdie maand + + + + Last month + Verlede maand + + + + This year + Hierdie jaar + + + + Range... + + + + + Received with + Ontvang met + + + + Sent to + Gestuur na + + + + To yourself + Aan/na jouself + + + + Mined + Gemyn + + + + Other + Ander + + + + Enter address or label to search + + + + + Min amount + Min bedrag + + + + Copy address + Maak kopie van adres + + + + Copy label + + + + + Copy amount + + + + + Copy transaction ID + + + + + Edit label + + + + + Show transaction details + + + + + Export Transaction Data + + + + + Comma separated file (*.csv) + + + + + Confirmed + + + + + Date + Datum + + + + Type + Tipe + + + + Label + Etiket + + + + Address + Adres + + + + Amount + Bedrag + + + + ID + ID + + + + Error exporting + Fout uitvoering + + + + Could not write to file %1. + Kon nie na die %1 lêer skryf nie + + + + Range: + + + + + to + + + + + WalletModel + + + Send Coins + + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + CryptoLottoToken weergawe + + + + Usage: + Gebruik: + + + + Send command to -server or CryptoLottoTokend + + + + + List commands + + + + + Get help for a command + + + + + Options: + + + + + Specify configuration file (default: CryptoLottoToken.conf) + + + + + Specify pid file (default: CryptoLottoTokend.pid) + + + + + Specify data directory + + + + + Set database cache size in megabytes (default: 25) + + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + + + + + Maintain at most <n> connections to peers (default: 125) + + + + + Connect to a node to retrieve peer addresses, and disconnect + + + + + Specify your own public address + + + + + Threshold for disconnecting misbehaving peers (default: 100) + + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332) + + + + + Accept command line and JSON-RPC commands + + + + + Run in the background as a daemon and accept commands + + + + + Use the test network + + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + + + + + Connect only to the specified node(s) + + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + Fout: Hardeskyf spasie is baie laag! + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + Informasie + + + + Invalid -tor address: '%s' + + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + + + + + Select the version of socks proxy to use (4-5, default: 5) + + + + + Send trace/debug info to console instead of debug.log file + + + + + Send trace/debug info to debugger + + + + + Set maximum block size in bytes (default: 250000) + + + + + Set minimum block size in bytes (default: 0) + + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + + + + + System error: + Sisteem fout: + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + + + + + Use UPnP to map the listening port (default: 1 when listening) + + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + + + + + Allow JSON-RPC connections from specified IP address + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + + + + + Upgrade wallet to latest format + + + + + Set key pool size to <n> (default: 100) + + + + + Rescan the block chain for missing wallet transactions + + + + + Use OpenSSL (https) for JSON-RPC connections + + + + + Server certificate file (default: server.cert) + + + + + Server private key (default: server.pem) + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + + This help message + + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + + + + + Connect through socks proxy + + + + + Allow DNS lookups for -addnode, -seednode and -connect + + + + + Loading addresses... + Laai adresse... + + + + Error loading wallet.dat: Wallet corrupted + + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + + + + + Error loading wallet.dat + + + + + Invalid -proxy address: '%s' + + + + + Unknown network specified in -onlynet: '%s' + + + + + Unknown -socks proxy version requested: %i + + + + + Cannot resolve -bind address: '%s' + + + + + Cannot resolve -externalip address: '%s' + + + + + Invalid amount for -paytxfee=<amount>: '%s' + + + + + Invalid amount + + + + + Insufficient funds + + + + + Loading block index... + Laai blok indeks... + + + + Add a node to connect to and attempt to keep the connection open + + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + + + + + Fee per KB to add to transactions you send + + + + + Loading wallet... + Laai beursie... + + + + Cannot downgrade wallet + + + + + Cannot write default address + + + + + Rescanning... + + + + + Done loading + Klaar gelaai + + + + To use the %s option + + + + + Error + Fout + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + + + + diff --git a/src/qt/locale/bitcoin_ar.qm b/src/qt/locale/bitcoin_ar.qm new file mode 100644 index 0000000..cbc27f5 Binary files /dev/null and b/src/qt/locale/bitcoin_ar.qm differ diff --git a/src/qt/locale/bitcoin_ar.ts b/src/qt/locale/bitcoin_ar.ts new file mode 100644 index 0000000..b3e0697 --- /dev/null +++ b/src/qt/locale/bitcoin_ar.ts @@ -0,0 +1,2917 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + عن CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + نسخة <b>CryptoLottoToken</b> + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + دفتر العناوين + + + + Double-click to edit address or label + أنقر على الماوس مرتين لتعديل عنوان + + + + Create a new address + قم بعمل عنوان جديد + + + + Copy the currently selected address to the system clipboard + قم بنسخ القوانين المختارة لحافظة النظام + + + + &New Address + + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + + + + + &Copy Address + + + + + Show &QR Code + + + + + Sign a message to prove you own a CryptoLottoToken address + + + + + Sign &Message + + + + + Delete the currently selected address from the list + + + + + Export the data in the current tab to a file + + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + + + + + &Verify Message + + + + + &Delete + &أمسح + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + + + + + &Edit + + + + + Send &Coins + + + + + Export Address Book Data + + + + + Comma separated file (*.csv) + + + + + Error exporting + + + + + Could not write to file %1. + + + + + AddressTableModel + + + Label + + + + + Address + + + + + (no label) + + + + + AskPassphraseDialog + + + Passphrase Dialog + + + + + Enter passphrase + + + + + New passphrase + + + + + Repeat new passphrase + + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + + + + + Encrypt wallet + + + + + This operation needs your wallet passphrase to unlock the wallet. + + + + + Unlock wallet + + + + + This operation needs your wallet passphrase to decrypt the wallet. + + + + + Decrypt wallet + + + + + Change passphrase + + + + + Enter the old and new passphrase to the wallet. + + + + + Confirm wallet encryption + + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + + + + + Are you sure you wish to encrypt your wallet? + + + + + 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. + + + + + + Warning: The Caps Lock key is on! + + + + + + Wallet encrypted + + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + + + + + + + + Wallet encryption failed + + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + + + + + + The supplied passphrases do not match. + + + + + Wallet unlock failed + + + + + + + The passphrase entered for the wallet decryption was incorrect. + + + + + Wallet decryption failed + + + + + Wallet passphrase was successfully changed. + + + + + BitcoinGUI + + + Sign &message... + + + + + Synchronizing with network... + + + + + &Overview + + + + + Show general overview of wallet + + + + + &Transactions + + + + + Browse transaction history + + + + + Edit the list of stored addresses and labels for sending + + + + + Show the list of addresses for receiving payments + + + + + E&xit + + + + + Quit application + + + + + Show information about CryptoLottoToken + + + + + About &Qt + + + + + Show information about Qt + + + + + &Options... + + + + + &Encrypt Wallet... + + + + + &Backup Wallet... + + + + + &Change Passphrase... + + + + + Importing blocks from disk... + + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + + + + + Modify configuration options for CryptoLottoToken + + + + + Backup wallet to another location + + + + + Change the passphrase used for wallet encryption + + + + + &Debug window + + + + + Open debugging and diagnostic console + + + + + &Verify message... + + + + + + CryptoLottoToken + + + + + Wallet + + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + + + + + &Show / Hide + + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + + + + + &Settings + + + + + &Help + + + + + Tabs toolbar + + + + + + [testnet] + + + + + CryptoLottoToken client + + + + + %n active connection(s) to CryptoLottoToken network + + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + + + + + Warning + + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + + + + + Catching up... + + + + + Confirm transaction fee + + + + + Sent transaction + + + + + Incoming transaction + + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + + + + + + URI handling + + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + + + + + EditAddressDialog + + + Edit Address + + + + + &Label + + + + + The label associated with this address book entry + + + + + &Address + + + + + The address associated with this address book entry. This can only be modified for sending addresses. + + + + + New receiving address + + + + + New sending address + + + + + Edit receiving address + + + + + Edit sending address + + + + + The entered address "%1" is already in the address book. + + + + + The entered address "%1" is not a valid CryptoLottoToken address. + + + + + Could not unlock wallet. + + + + + New key generation failed. + + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + + + + + version + + + + + Usage: + + + + + command-line options + + + + + UI options + + + + + Set language, for example "de_DE" (default: system locale) + + + + + Start minimized + + + + + Show splash screen on startup (default: 1) + + + + + OptionsDialog + + + Options + + + + + &Main + + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + + + + + Automatically start CryptoLottoToken after logging in to the system. + + + + + &Start CryptoLottoToken on system login + + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + + + + + Map port using &UPnP + + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + + + + + &Connect through SOCKS proxy: + + + + + Proxy &IP: + + + + + IP address of the proxy (e.g. 127.0.0.1) + + + + + &Port: + + + + + Port of the proxy (e.g. 9050) + + + + + SOCKS &Version: + + + + + SOCKS version of the proxy (e.g. 5) + + + + + &Window + + + + + Show only a tray icon after minimizing the window. + + + + + &Minimize to the tray instead of the taskbar + + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + + + + + M&inimize on close + + + + + &Display + + + + + User Interface &language: + + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + + + + + &Unit to show amounts in: + + + + + Choose the default subdivision unit to show in the interface and when sending coins. + + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + + + &Display addresses in transaction list + + + + + &OK + + + + + &Cancel + + + + + &Apply + + + + + default + + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + + + + + + This setting will take effect after restarting CryptoLottoToken. + + + + + The supplied proxy address is invalid. + + + + + OverviewPage + + + Form + + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + + + + + Balance: + + + + + Unconfirmed: + + + + + Wallet + + + + + Immature: + + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + + + + + Your current balance + + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + + + + + + out of sync + + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + + + + + Request Payment + + + + + Amount: + + + + + Label: + + + + + Message: + + + + + &Save As... + + + + + Error encoding URI into QR Code. + + + + + The entered amount is invalid, please check. + + + + + Resulting URI too long, try to reduce the text for label / message. + + + + + Save QR Code + + + + + PNG Images (*.png) + + + + + RPCConsole + + + Client name + + + + + + + + + + + + + + N/A + + + + + Client version + + + + + &Information + + + + + Using OpenSSL version + + + + + Startup time + + + + + Network + + + + + Number of connections + + + + + On testnet + + + + + Block chain + + + + + Current number of blocks + + + + + Estimated total blocks + + + + + Last block time + + + + + &Open + + + + + Command-line options + + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + + &Show + + + + + &Console + + + + + Build date + + + + + CryptoLottoToken - Debug window + + + + + CryptoLottoToken Core + + + + + Debug log file + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + + + + + Welcome to the CryptoLottoToken RPC console. + + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + + + + + Type <b>help</b> for an overview of available commands. + + + + + SendCoinsDialog + + + + + + + + + + Send Coins + + + + + Send to multiple recipients at once + + + + + Add &Recipient + + + + + Remove all transaction fields + + + + + Clear &All + + + + + Balance: + + + + + 123.456 BTC + + + + + Confirm the send action + + + + + S&end + + + + + <b>%1</b> to %2 (%3) + + + + + Confirm send coins + + + + + Are you sure you want to send %1? + + + + + and + + + + + The recipient address is not valid, please recheck. + + + + + The amount to pay must be larger than 0. + + + + + The amount exceeds your balance. + + + + + The total exceeds your balance when the %1 transaction fee is included. + + + + + Duplicate address found, can only send to each address once per send operation. + + + + + Error: Transaction creation failed! + + + + + 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. + + + + + SendCoinsEntry + + + Form + + + + + A&mount: + + + + + Pay &To: + + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + + + + + &Label: + + + + + Choose address from address book + + + + + Alt+A + + + + + Paste address from clipboard + + + + + Alt+P + + + + + Remove this recipient + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + + + + + &Sign Message + + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Choose an address from the address book + + + + + + Alt+A + + + + + Paste address from clipboard + + + + + Alt+P + + + + + Enter the message you want to sign here + + + + + Signature + + + + + Copy the current signature to the system clipboard + + + + + Sign the message to prove you own this CryptoLottoToken address + + + + + Sign &Message + + + + + Reset all sign message fields + + + + + + Clear &All + + + + + &Verify Message + + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + + + + + Verify &Message + + + + + Reset all verify message fields + + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Click "Sign Message" to generate signature + + + + + Enter CryptoLottoToken signature + + + + + + The entered address is invalid. + + + + + + + + Please check the address and try again. + + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + + + + + Private key for the entered address is not available. + + + + + Message signing failed. + + + + + Message signed. + + + + + The signature could not be decoded. + + + + + + Please check the signature and try again. + + + + + The signature did not match the message digest. + + + + + Message verification failed. + + + + + Message verified. + + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + + + + + TransactionDesc + + + Open until %1 + + + + + %1/offline + + + + + %1/unconfirmed + + + + + %1 confirmations + + + + + Status + + + + + , broadcast through %n node(s) + + + + + Date + + + + + Source + + + + + Generated + + + + + + From + + + + + + + To + + + + + + own address + + + + + label + + + + + + + + + Credit + + + + + matures in %n more block(s) + + + + + not accepted + + + + + + + + Debit + + + + + Transaction fee + + + + + Net amount + + + + + Message + + + + + Comment + + + + + Transaction ID + + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + + + + + Debug information + + + + + Transaction + + + + + Inputs + + + + + Amount + + + + + true + + + + + false + + + + + , has not been successfully broadcast yet + + + + + Open for %n more block(s) + + + + + unknown + + + + + TransactionDescDialog + + + Transaction details + + + + + This pane shows a detailed description of the transaction + + + + + TransactionTableModel + + + Date + + + + + Type + + + + + Address + + + + + Amount + + + + + Open for %n more block(s) + + + + + Open until %1 + + + + + Offline (%1 confirmations) + + + + + Unconfirmed (%1 of %2 confirmations) + + + + + Confirmed (%1 confirmations) + + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + + + + + Generated but not accepted + + + + + Received with + + + + + Received from + + + + + Sent to + + + + + Payment to yourself + + + + + Mined + + + + + (n/a) + + + + + Transaction status. Hover over this field to show number of confirmations. + + + + + Date and time that the transaction was received. + + + + + Type of transaction. + + + + + Destination address of transaction. + + + + + Amount removed from or added to balance. + + + + + TransactionView + + + + All + + + + + Today + + + + + This week + + + + + This month + + + + + Last month + + + + + This year + + + + + Range... + + + + + Received with + + + + + Sent to + + + + + To yourself + + + + + Mined + + + + + Other + + + + + Enter address or label to search + + + + + Min amount + + + + + Copy address + + + + + Copy label + + + + + Copy amount + + + + + Copy transaction ID + + + + + Edit label + + + + + Show transaction details + + + + + Export Transaction Data + + + + + Comma separated file (*.csv) + + + + + Confirmed + + + + + Date + + + + + Type + + + + + Label + + + + + Address + + + + + Amount + + + + + ID + + + + + Error exporting + + + + + Could not write to file %1. + + + + + Range: + + + + + to + + + + + WalletModel + + + Send Coins + + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + + + + + Usage: + + + + + Send command to -server or CryptoLottoTokend + + + + + List commands + + + + + Get help for a command + + + + + Options: + + + + + Specify configuration file (default: CryptoLottoToken.conf) + + + + + Specify pid file (default: CryptoLottoTokend.pid) + + + + + Specify data directory + + + + + Set database cache size in megabytes (default: 25) + + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + + + + + Maintain at most <n> connections to peers (default: 125) + + + + + Connect to a node to retrieve peer addresses, and disconnect + + + + + Specify your own public address + + + + + Threshold for disconnecting misbehaving peers (default: 100) + + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + + + + + Accept command line and JSON-RPC commands + + + + + Run in the background as a daemon and accept commands + + + + + Use the test network + + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + + + + + Connect only to the specified node(s) + + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + + + + + Select the version of socks proxy to use (4-5, default: 5) + + + + + Send trace/debug info to console instead of debug.log file + + + + + Send trace/debug info to debugger + + + + + Set maximum block size in bytes (default: 250000) + + + + + Set minimum block size in bytes (default: 0) + + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + + + + + Use UPnP to map the listening port (default: 1 when listening) + + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + + + + + Allow JSON-RPC connections from specified IP address + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + + + + + Upgrade wallet to latest format + + + + + Set key pool size to <n> (default: 100) + + + + + Rescan the block chain for missing wallet transactions + + + + + Use OpenSSL (https) for JSON-RPC connections + + + + + Server certificate file (default: server.cert) + + + + + Server private key (default: server.pem) + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + + This help message + + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + + + + + Connect through socks proxy + + + + + Allow DNS lookups for -addnode, -seednode and -connect + + + + + Loading addresses... + + + + + Error loading wallet.dat: Wallet corrupted + + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + + + + + Error loading wallet.dat + + + + + Invalid -proxy address: '%s' + + + + + Unknown network specified in -onlynet: '%s' + + + + + Unknown -socks proxy version requested: %i + + + + + Cannot resolve -bind address: '%s' + + + + + Cannot resolve -externalip address: '%s' + + + + + Invalid amount for -paytxfee=<amount>: '%s' + + + + + Invalid amount + + + + + Insufficient funds + + + + + Loading block index... + + + + + Add a node to connect to and attempt to keep the connection open + + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + + + + + Fee per KB to add to transactions you send + + + + + Loading wallet... + + + + + Cannot downgrade wallet + + + + + Cannot write default address + + + + + Rescanning... + + + + + Done loading + + + + + To use the %s option + + + + + Error + + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_bg.qm b/src/qt/locale/bitcoin_bg.qm new file mode 100644 index 0000000..8d78e6e Binary files /dev/null and b/src/qt/locale/bitcoin_bg.qm differ diff --git a/src/qt/locale/bitcoin_bg.ts b/src/qt/locale/bitcoin_bg.ts new file mode 100644 index 0000000..b1f6e64 --- /dev/null +++ b/src/qt/locale/bitcoin_bg.ts @@ -0,0 +1,2922 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + За CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> версия + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +Това е експериментален софтуер. + +Разпространява се под MIT/X11 софтуерен лиценз, виж COPYING или http://www.opensource.org/licenses/mit-license.php. + +Използван е софтуер, разработен от OpenSSL Project за употреба в OpenSSL Toolkit (http://www.openssl.org/), криптографски софтуер разработен от Eric Young (eay@cryptsoft.com) и UPnP софтуер разработен от Thomas Bernard. + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + Адреси + + + + Double-click to edit address or label + Двоен клик за редакция на адрес или име + + + + Create a new address + Създава нов адрес + + + + Copy the currently selected address to the system clipboard + Копира избрания адрес + + + + &New Address + &Нов адрес + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Това са вашите CryptoLottoToken адреси за получаване на плащания. За по-лесно проследяване на плащанията и повишена анонимност можете да използвате нов адрес за всяко плащане. + + + + &Copy Address + &Копирай + + + + Show &QR Code + Покажи &QR код + + + + Sign a message to prove you own a CryptoLottoToken address + Подпишете съобщение като доказателство, че притежавате определен адрес + + + + Sign &Message + Подпиши &съобщение + + + + Delete the currently selected address from the list + Изтрий избрания адрес от списъка + + + + Export the data in the current tab to a file + Запишете данните от текущия раздел във файл + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Проверете съобщение, за да сте сигурни че е подписано с определен CryptoLottoToken адрес + + + + &Verify Message + &Провери съобщение + + + + &Delete + &Изтрий + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + Копирай &име + + + + &Edit + &Редактирай + + + + Send &Coins + + + + + Export Address Book Data + Запазване на адреси + + + + Comma separated file (*.csv) + CSV файл (*.csv) + + + + Error exporting + Грешка при записа + + + + Could not write to file %1. + Неуспешен запис в %1. + + + + AddressTableModel + + + Label + Име + + + + Address + Адрес + + + + (no label) + (без име) + + + + AskPassphraseDialog + + + Passphrase Dialog + + + + + Enter passphrase + Парола + + + + New passphrase + Нова парола + + + + Repeat new passphrase + Още веднъж + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Въведете нова парола за портфейла.<br/>Моля използвайте <b>поне 10 случайни символа</b> или <b>8 или повече думи</b>. + + + + Encrypt wallet + Криптиране на портфейла + + + + This operation needs your wallet passphrase to unlock the wallet. + Тази операция изисква Вашата парола за отключване на портфейла. + + + + Unlock wallet + Отключване на портфейла + + + + This operation needs your wallet passphrase to decrypt the wallet. + Тази операция изисква Вашата парола за декриптиране на портфейла. + + + + Decrypt wallet + Декриптиране на портфейла + + + + Change passphrase + Смяна на паролата + + + + Enter the old and new passphrase to the wallet. + Въведете текущата и новата парола за портфейла. + + + + Confirm wallet encryption + Потвърждаване на криптирането + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + + + + + Are you sure you wish to encrypt your wallet? + + + + + 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. + + + + + + Warning: The Caps Lock key is on! + + + + + + Wallet encrypted + Портфейлът е криптиран + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + + + + + + + + Wallet encryption failed + Криптирането беше неуспешно + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Криптирането на портфейла беше неуспешно поради неизвестен проблем. Портфейлът не е криптиран. + + + + + The supplied passphrases do not match. + Паролите не съвпадат + + + + Wallet unlock failed + Отключването беше неуспешно + + + + + + The passphrase entered for the wallet decryption was incorrect. + Паролата въведена за декриптиране на портфейла е грешна. + + + + Wallet decryption failed + Декриптирането беше неуспешно + + + + Wallet passphrase was successfully changed. + Паролата на портфейла беше променена успешно. + + + + BitcoinGUI + + + Sign &message... + Подписване на &съобщение... + + + + Synchronizing with network... + Синхронизиране с мрежата... + + + + &Overview + &Баланс + + + + Show general overview of wallet + Обобщена информация за портфейла + + + + &Transactions + &Транзакции + + + + Browse transaction history + История на входящите и изходящи транзакции + + + + Edit the list of stored addresses and labels for sending + Редактиране на адреси + + + + Show the list of addresses for receiving payments + Списък на адресите за получаване + + + + E&xit + Из&ход + + + + Quit application + Затваря приложението + + + + Show information about CryptoLottoToken + Показва информация за CryptoLottoToken + + + + About &Qt + За &Qt + + + + Show information about Qt + + + + + &Options... + &Опции... + + + + &Encrypt Wallet... + &Криптиране на портфейла... + + + + &Backup Wallet... + &Запазване на портфейла... + + + + &Change Passphrase... + &Смяна на паролата... + + + + Importing blocks from disk... + + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + Изпращане към CryptoLottoToken адрес + + + + Modify configuration options for CryptoLottoToken + + + + + Backup wallet to another location + + + + + Change the passphrase used for wallet encryption + Променя паролата за портфейла + + + + &Debug window + + + + + Open debugging and diagnostic console + + + + + &Verify message... + &Проверка на съобщение... + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Портфейл + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + &За CryptoLottoToken + + + + &Show / Hide + + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + &Файл + + + + &Settings + &Настройки + + + + &Help + &Помощ + + + + Tabs toolbar + Раздели + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + + + + + %n active connection(s) to CryptoLottoToken network + %n връзка към CryptoLottoToken мрежата%n връзки към CryptoLottoToken мрежата + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + + + + + Warning + + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + Синхронизиран + + + + Catching up... + Зарежда блокове... + + + + Confirm transaction fee + Потвърждение за такса + + + + Sent transaction + Изходяща транзакция + + + + Incoming transaction + Входяща транзакция + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + + + + + + URI handling + + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Портфейлът е <b>криптиран</b> и <b>отключен</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Портфейлът е <b>криптиран</b> и <b>заключен</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + + + + + EditAddressDialog + + + Edit Address + Редактиране на адрес + + + + &Label + &Име + + + + The label associated with this address book entry + Името свързано с този запис в списъка с адреси + + + + &Address + &Адрес + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Адресът свързан с този запис в списъка с адреси. Може да се променя само за изходящи адреси. + + + + New receiving address + Нов адрес за получаване + + + + New sending address + Нов адрес за изпращане + + + + Edit receiving address + Редактиране на входящ адрес + + + + Edit sending address + Редактиране на изходящ адрес + + + + The entered address "%1" is already in the address book. + Вече има адрес "%1" в списъка с адреси. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + "%1" не е валиден CryptoLottoToken адрес. + + + + Could not unlock wallet. + Отключването на портфейла беше неуспешно. + + + + New key generation failed. + Създаването на ключ беше неуспешно. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + + + + + version + + + + + Usage: + + + + + command-line options + + + + + UI options + UI Опции + + + + Set language, for example "de_DE" (default: system locale) + + + + + Start minimized + + + + + Show splash screen on startup (default: 1) + + + + + OptionsDialog + + + Options + Опции + + + + &Main + &Основни + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + &Такса за изходяща транзакция + + + + Automatically start CryptoLottoToken after logging in to the system. + + + + + &Start CryptoLottoToken on system login + &Пускане на CryptoLottoToken при вход в системата + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + &Мрежа + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Автоматично отваряне на входящия CryptoLottoToken порт. Работи само с рутери поддържащи UPnP. + + + + Map port using &UPnP + Отваряне на входящия порт чрез &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + + + + + &Connect through SOCKS proxy: + + + + + Proxy &IP: + + + + + IP address of the proxy (e.g. 127.0.0.1) + IP адрес на прокси сървъра (например 127.0.0.1) + + + + &Port: + + + + + Port of the proxy (e.g. 9050) + + + + + SOCKS &Version: + + + + + SOCKS version of the proxy (e.g. 5) + + + + + &Window + &Прозорец + + + + Show only a tray icon after minimizing the window. + След минимизиране ще е видима само иконата в системния трей. + + + + &Minimize to the tray instead of the taskbar + &Минимизиране в системния трей + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + При затваряне на прозореца приложението остава минимизирано. Ако изберете тази опция, приложението може да се затвори само чрез Изход в менюто. + + + + M&inimize on close + М&инимизиране при затваряне + + + + &Display + &Интерфейс + + + + User Interface &language: + Език: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + Промяната на езика ще влезе в сила след рестартиране на CryptoLottoToken. + + + + &Unit to show amounts in: + Мерни единици: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Изберете единиците, показвани по подразбиране в интерфейса. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Ще се показват адресите в списъка с транзакции независимо от наличието на кратко име. + + + + &Display addresses in transaction list + Показвай и адресите в списъка с транзакции + + + + &OK + + + + + &Cancel + + + + + &Apply + + + + + default + + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + + + + + + This setting will take effect after restarting CryptoLottoToken. + + + + + The supplied proxy address is invalid. + Прокси адресът е невалиден. + + + + OverviewPage + + + Form + Форма + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + + + + + Balance: + Баланс: + + + + Unconfirmed: + Непотвърдени: + + + + Wallet + Портфейл + + + + Immature: + + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + <b>Последни транзакции</b> + + + + Your current balance + Вашият текущ баланс + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Сборът на все още непотвърдените транзакции, които не са част от текущия баланс + + + + + out of sync + несинхронизиран + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + + + + + Request Payment + Изискай плащане + + + + Amount: + Сума: + + + + Label: + Име: + + + + Message: + Съобщение: + + + + &Save As... + &Запази като... + + + + Error encoding URI into QR Code. + Грешка при създаването на QR Code от URI. + + + + The entered amount is invalid, please check. + Въведената сума е невалидна, моля проверете. + + + + Resulting URI too long, try to reduce the text for label / message. + + + + + Save QR Code + + + + + PNG Images (*.png) + + + + + RPCConsole + + + Client name + + + + + + + + + + + + + + N/A + N/A + + + + Client version + + + + + &Information + + + + + Using OpenSSL version + + + + + Startup time + + + + + Network + Мрежа + + + + Number of connections + + + + + On testnet + + + + + Block chain + + + + + Current number of blocks + + + + + Estimated total blocks + + + + + Last block time + + + + + &Open + + + + + Command-line options + + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + + &Show + + + + + &Console + + + + + Build date + + + + + CryptoLottoToken - Debug window + + + + + CryptoLottoToken Core + + + + + Debug log file + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + Изчисти конзолата + + + + Welcome to the CryptoLottoToken RPC console. + + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Използвайте стрелки надолу и нагореза разглеждане на историятаот команди и <b>Ctrl-L</b> за изчистване на конзолата. + + + + Type <b>help</b> for an overview of available commands. + + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Изпращане + + + + Send to multiple recipients at once + Изпращане към повече от един получател + + + + Add &Recipient + Добави &получател + + + + Remove all transaction fields + Изчистване на всички полета + + + + Clear &All + &Изчисти + + + + Balance: + Баланс: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Потвърдете изпращането + + + + S&end + И&зпрати + + + + <b>%1</b> to %2 (%3) + <b>%1</b> на %2 (%3) + + + + Confirm send coins + Потвърждаване + + + + Are you sure you want to send %1? + Сигурни ли сте, че искате да изпратите %1? + + + + and + и + + + + The recipient address is not valid, please recheck. + Невалиден адрес на получателя. + + + + The amount to pay must be larger than 0. + Сумата трябва да е по-голяма от 0. + + + + The amount exceeds your balance. + + + + + The total exceeds your balance when the %1 transaction fee is included. + + + + + Duplicate address found, can only send to each address once per send operation. + + + + + Error: Transaction creation failed! + + + + + 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. + Грешка: транзакцията беше отхвърлена. Това е възможно ако част от парите в портфейла са вече похарчени, например при паралелно използване на копие на wallet.dat + + + + SendCoinsEntry + + + Form + Форма + + + + A&mount: + С&ума: + + + + Pay &To: + Плати &На: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + Въведете име за този адрес, за да го добавите в списъка с адреси + + + + &Label: + &Име: + + + + Choose address from address book + Изберете от списъка с адреси + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Вмъкни от клипборда + + + + Alt+P + Alt+P + + + + Remove this recipient + Махни този получател + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Въведете CryptoLottoToken адрес (например Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Подпиши / Провери съобщение + + + + &Sign Message + &Подпиши + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Можете да подпишете съобщение като доказателство, че притежавате определен адрес. Бъдете внимателни и не подписвайте съобщения, които биха разкрили лична информация без вашето съгласие. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Адресът, с който ще подпишете съобщението (например Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Изберете от списъка с адреси + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Вмъкни от клипборда + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Въведете съобщението тук + + + + Signature + + + + + Copy the current signature to the system clipboard + Копиране на текущия подпис + + + + Sign the message to prove you own this CryptoLottoToken address + Подпишете съобщение като доказателство, че притежавате определен адрес + + + + Sign &Message + + + + + Reset all sign message fields + + + + + + Clear &All + &Изчисти + + + + &Verify Message + &Провери + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Адресът, с който е подписано съобщението (например Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Проверете съобщение, за да сте сигурни че е подписано с определен CryptoLottoToken адрес + + + + Verify &Message + + + + + Reset all verify message fields + + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Въведете CryptoLottoToken адрес (например Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Натиснете "Подписване на съобщение" за да създадете подпис + + + + Enter CryptoLottoToken signature + CryptoLottoToken подпис + + + + + The entered address is invalid. + Въведеният адрес е невалиден. + + + + + + + Please check the address and try again. + Моля проверете адреса и опитайте отново. + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + + + + + Private key for the entered address is not available. + Не е наличен частният ключ за въведеният адрес. + + + + Message signing failed. + Подписването на съобщение бе неуспешно. + + + + Message signed. + Съобщението е подписано. + + + + The signature could not be decoded. + Подписът не може да бъде декодиран. + + + + + Please check the signature and try again. + Проверете подписа и опитайте отново. + + + + The signature did not match the message digest. + Подписът не отговаря на комбинацията от съобщение и адрес. + + + + Message verification failed. + Проверката на съобщението беше неуспешна. + + + + Message verified. + Съобщението е потвърдено. + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + Подлежи на промяна до %1 + + + + %1/offline + %1/офлайн + + + + %1/unconfirmed + %1/непотвърдени + + + + %1 confirmations + включена в %1 блока + + + + Status + Статус + + + + , broadcast through %n node(s) + + + + + Date + Дата + + + + Source + Източник + + + + Generated + Издадени + + + + + From + От + + + + + + To + За + + + + + own address + собствен адрес + + + + label + име + + + + + + + + Credit + Кредит + + + + matures in %n more block(s) + + + + + not accepted + + + + + + + + Debit + Дебит + + + + Transaction fee + Такса + + + + Net amount + Сума нето + + + + Message + Съобщение + + + + Comment + Коментар + + + + Transaction ID + ID + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + + + + + Debug information + + + + + Transaction + Транзакция + + + + Inputs + + + + + Amount + Сума + + + + true + true + + + + false + false + + + + , has not been successfully broadcast yet + , все още не е изпратено + + + + Open for %n more block(s) + + + + + unknown + неизвестен + + + + TransactionDescDialog + + + Transaction details + Транзакция + + + + This pane shows a detailed description of the transaction + Описание на транзакцията + + + + TransactionTableModel + + + Date + Дата + + + + Type + Тип + + + + Address + Адрес + + + + Amount + Сума + + + + Open for %n more block(s) + + + + + Open until %1 + Подлежи на промяна до %1 + + + + Offline (%1 confirmations) + Офлайн (%1 потвърждения) + + + + Unconfirmed (%1 of %2 confirmations) + Непотвърдени (%1 от %2 потвърждения) + + + + Confirmed (%1 confirmations) + Потвърдени (%1 потвърждения) + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + Блокът не е получен от останалите участници и най-вероятно няма да бъде одобрен. + + + + Generated but not accepted + + + + + Received with + Получени с + + + + Received from + + + + + Sent to + Изпратени на + + + + Payment to yourself + + + + + Mined + Емитирани + + + + (n/a) + (n/a) + + + + Transaction status. Hover over this field to show number of confirmations. + Състояние на транзакцията. Задръжте върху това поле за брой потвърждения. + + + + Date and time that the transaction was received. + Дата и час на получаване. + + + + Type of transaction. + Тип на транзакцията. + + + + Destination address of transaction. + Получател на транзакцията. + + + + Amount removed from or added to balance. + Сума извадена или добавена към баланса. + + + + TransactionView + + + + All + Всички + + + + Today + Днес + + + + This week + Тази седмица + + + + This month + Този месец + + + + Last month + Предния месец + + + + This year + Тази година + + + + Range... + От - до... + + + + Received with + Получени + + + + Sent to + Изпратени на + + + + To yourself + Собствени + + + + Mined + Емитирани + + + + Other + Други + + + + Enter address or label to search + Търсене по адрес или име + + + + Min amount + Минимална сума + + + + Copy address + Копирай адрес + + + + Copy label + Копирай име + + + + Copy amount + + + + + Copy transaction ID + + + + + Edit label + Редактирай име + + + + Show transaction details + + + + + Export Transaction Data + + + + + Comma separated file (*.csv) + CSV файл (*.csv) + + + + Confirmed + Потвърдени + + + + Date + Дата + + + + Type + Тип + + + + Label + Име + + + + Address + Адрес + + + + Amount + Сума + + + + ID + + + + + Error exporting + Грешка при записа + + + + Could not write to file %1. + Неуспешен запис в %1. + + + + Range: + От: + + + + to + до + + + + WalletModel + + + Send Coins + Изпращане + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + Запишете данните от текущия раздел във файл + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + CryptoLottoToken версия + + + + Usage: + + + + + Send command to -server or CryptoLottoTokend + + + + + List commands + + + + + Get help for a command + + + + + Options: + Опции: + + + + Specify configuration file (default: CryptoLottoToken.conf) + + + + + Specify pid file (default: CryptoLottoTokend.pid) + + + + + Specify data directory + + + + + Set database cache size in megabytes (default: 25) + + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + + + + + Maintain at most <n> connections to peers (default: 125) + + + + + Connect to a node to retrieve peer addresses, and disconnect + + + + + Specify your own public address + + + + + Threshold for disconnecting misbehaving peers (default: 100) + + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + + + + + Accept command line and JSON-RPC commands + + + + + Run in the background as a daemon and accept commands + + + + + Use the test network + + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + + + + + Connect only to the specified node(s) + + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + Невалиден -tor адрес: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + + + + + Select the version of socks proxy to use (4-5, default: 5) + + + + + Send trace/debug info to console instead of debug.log file + + + + + Send trace/debug info to debugger + + + + + Set maximum block size in bytes (default: 250000) + + + + + Set minimum block size in bytes (default: 0) + + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + + + + + Use UPnP to map the listening port (default: 1 when listening) + + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + + + + + Allow JSON-RPC connections from specified IP address + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + + + + + Upgrade wallet to latest format + + + + + Set key pool size to <n> (default: 100) + + + + + Rescan the block chain for missing wallet transactions + + + + + Use OpenSSL (https) for JSON-RPC connections + + + + + Server certificate file (default: server.cert) + + + + + Server private key (default: server.pem) + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + + This help message + + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + + + + + Connect through socks proxy + + + + + Allow DNS lookups for -addnode, -seednode and -connect + + + + + Loading addresses... + Зареждане на адресите... + + + + Error loading wallet.dat: Wallet corrupted + + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + + + + + Error loading wallet.dat + + + + + Invalid -proxy address: '%s' + Невалиден -proxy address: '%s' + + + + Unknown network specified in -onlynet: '%s' + + + + + Unknown -socks proxy version requested: %i + + + + + Cannot resolve -bind address: '%s' + + + + + Cannot resolve -externalip address: '%s' + + + + + Invalid amount for -paytxfee=<amount>: '%s' + + + + + Invalid amount + + + + + Insufficient funds + + + + + Loading block index... + Зареждане на блок индекса... + + + + Add a node to connect to and attempt to keep the connection open + + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + + + + + Fee per KB to add to transactions you send + + + + + Loading wallet... + Зареждане на портфейла... + + + + Cannot downgrade wallet + + + + + Cannot write default address + + + + + Rescanning... + Преразглеждане на последовтелността от блокове... + + + + Done loading + Зареждането е завършено + + + + To use the %s option + + + + + Error + Грешка + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_bs.ts b/src/qt/locale/bitcoin_bs.ts new file mode 100644 index 0000000..ed86d51 --- /dev/null +++ b/src/qt/locale/bitcoin_bs.ts @@ -0,0 +1,2917 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + O CryptoLottoTokenu + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> verzija + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + Adresar + + + + Double-click to edit address or label + + + + + Create a new address + + + + + Copy the currently selected address to the system clipboard + + + + + &New Address + + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + + + + + &Copy Address + + + + + Show &QR Code + + + + + Sign a message to prove you own a CryptoLottoToken address + + + + + Sign &Message + + + + + Delete the currently selected address from the list + + + + + Export the data in the current tab to a file + + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + + + + + &Verify Message + + + + + &Delete + + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + + + + + &Edit + + + + + Send &Coins + + + + + Export Address Book Data + + + + + Comma separated file (*.csv) + + + + + Error exporting + + + + + Could not write to file %1. + + + + + AddressTableModel + + + Label + + + + + Address + + + + + (no label) + + + + + AskPassphraseDialog + + + Passphrase Dialog + + + + + Enter passphrase + + + + + New passphrase + + + + + Repeat new passphrase + + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + + + + + Encrypt wallet + + + + + This operation needs your wallet passphrase to unlock the wallet. + + + + + Unlock wallet + + + + + This operation needs your wallet passphrase to decrypt the wallet. + + + + + Decrypt wallet + + + + + Change passphrase + + + + + Enter the old and new passphrase to the wallet. + + + + + Confirm wallet encryption + + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + + + + + Are you sure you wish to encrypt your wallet? + + + + + 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. + + + + + + Warning: The Caps Lock key is on! + + + + + + Wallet encrypted + + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + + + + + + + + Wallet encryption failed + + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + + + + + + The supplied passphrases do not match. + + + + + Wallet unlock failed + + + + + + + The passphrase entered for the wallet decryption was incorrect. + + + + + Wallet decryption failed + + + + + Wallet passphrase was successfully changed. + + + + + BitcoinGUI + + + Sign &message... + + + + + Synchronizing with network... + + + + + &Overview + + + + + Show general overview of wallet + + + + + &Transactions + + + + + Browse transaction history + + + + + Edit the list of stored addresses and labels for sending + + + + + Show the list of addresses for receiving payments + + + + + E&xit + + + + + Quit application + + + + + Show information about CryptoLottoToken + + + + + About &Qt + + + + + Show information about Qt + + + + + &Options... + + + + + &Encrypt Wallet... + + + + + &Backup Wallet... + + + + + &Change Passphrase... + + + + + Importing blocks from disk... + + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + + + + + Modify configuration options for CryptoLottoToken + + + + + Backup wallet to another location + + + + + Change the passphrase used for wallet encryption + + + + + &Debug window + + + + + Open debugging and diagnostic console + + + + + &Verify message... + + + + + + CryptoLottoToken + + + + + Wallet + + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + + + + + &Show / Hide + + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + + + + + &Settings + + + + + &Help + + + + + Tabs toolbar + + + + + + [testnet] + + + + + CryptoLottoToken client + + + + + %n active connection(s) to CryptoLottoToken network + + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + + + + + Warning + + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + + + + + Catching up... + + + + + Confirm transaction fee + + + + + Sent transaction + + + + + Incoming transaction + + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + + + + + + URI handling + + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + + + + + EditAddressDialog + + + Edit Address + + + + + &Label + + + + + The label associated with this address book entry + + + + + &Address + + + + + The address associated with this address book entry. This can only be modified for sending addresses. + + + + + New receiving address + + + + + New sending address + + + + + Edit receiving address + + + + + Edit sending address + + + + + The entered address "%1" is already in the address book. + + + + + The entered address "%1" is not a valid CryptoLottoToken address. + + + + + Could not unlock wallet. + + + + + New key generation failed. + + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + + + + + version + + + + + Usage: + + + + + command-line options + + + + + UI options + + + + + Set language, for example "de_DE" (default: system locale) + + + + + Start minimized + + + + + Show splash screen on startup (default: 1) + + + + + OptionsDialog + + + Options + + + + + &Main + + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + + + + + Automatically start CryptoLottoToken after logging in to the system. + + + + + &Start CryptoLottoToken on system login + + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + + + + + Map port using &UPnP + + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + + + + + &Connect through SOCKS proxy: + + + + + Proxy &IP: + + + + + IP address of the proxy (e.g. 127.0.0.1) + + + + + &Port: + + + + + Port of the proxy (e.g. 9050) + + + + + SOCKS &Version: + + + + + SOCKS version of the proxy (e.g. 5) + + + + + &Window + + + + + Show only a tray icon after minimizing the window. + + + + + &Minimize to the tray instead of the taskbar + + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + + + + + M&inimize on close + + + + + &Display + + + + + User Interface &language: + + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + + + + + &Unit to show amounts in: + + + + + Choose the default subdivision unit to show in the interface and when sending coins. + + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + + + &Display addresses in transaction list + + + + + &OK + + + + + &Cancel + + + + + &Apply + + + + + default + + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + + + + + + This setting will take effect after restarting CryptoLottoToken. + + + + + The supplied proxy address is invalid. + + + + + OverviewPage + + + Form + + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + + + + + Balance: + + + + + Unconfirmed: + + + + + Wallet + + + + + Immature: + + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + + + + + Your current balance + + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + + + + + + out of sync + + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + + + + + Request Payment + + + + + Amount: + + + + + Label: + + + + + Message: + + + + + &Save As... + + + + + Error encoding URI into QR Code. + + + + + The entered amount is invalid, please check. + + + + + Resulting URI too long, try to reduce the text for label / message. + + + + + Save QR Code + + + + + PNG Images (*.png) + + + + + RPCConsole + + + Client name + + + + + + + + + + + + + + N/A + + + + + Client version + + + + + &Information + + + + + Using OpenSSL version + + + + + Startup time + + + + + Network + + + + + Number of connections + + + + + On testnet + + + + + Block chain + + + + + Current number of blocks + + + + + Estimated total blocks + + + + + Last block time + + + + + &Open + + + + + Command-line options + + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + + &Show + + + + + &Console + + + + + Build date + + + + + CryptoLottoToken - Debug window + + + + + CryptoLottoToken Core + + + + + Debug log file + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + + + + + Welcome to the CryptoLottoToken RPC console. + + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + + + + + Type <b>help</b> for an overview of available commands. + + + + + SendCoinsDialog + + + + + + + + + + Send Coins + + + + + Send to multiple recipients at once + + + + + Add &Recipient + + + + + Remove all transaction fields + + + + + Clear &All + + + + + Balance: + + + + + 123.456 BTC + + + + + Confirm the send action + + + + + S&end + + + + + <b>%1</b> to %2 (%3) + + + + + Confirm send coins + + + + + Are you sure you want to send %1? + + + + + and + + + + + The recipient address is not valid, please recheck. + + + + + The amount to pay must be larger than 0. + + + + + The amount exceeds your balance. + + + + + The total exceeds your balance when the %1 transaction fee is included. + + + + + Duplicate address found, can only send to each address once per send operation. + + + + + Error: Transaction creation failed! + + + + + 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. + + + + + SendCoinsEntry + + + Form + + + + + A&mount: + + + + + Pay &To: + + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + + + + + &Label: + + + + + Choose address from address book + + + + + Alt+A + + + + + Paste address from clipboard + + + + + Alt+P + + + + + Remove this recipient + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + + + + + &Sign Message + + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Choose an address from the address book + + + + + + Alt+A + + + + + Paste address from clipboard + + + + + Alt+P + + + + + Enter the message you want to sign here + + + + + Signature + + + + + Copy the current signature to the system clipboard + + + + + Sign the message to prove you own this CryptoLottoToken address + + + + + Sign &Message + + + + + Reset all sign message fields + + + + + + Clear &All + + + + + &Verify Message + + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + + + + + Verify &Message + + + + + Reset all verify message fields + + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Click "Sign Message" to generate signature + + + + + Enter CryptoLottoToken signature + + + + + + The entered address is invalid. + + + + + + + + Please check the address and try again. + + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + + + + + Private key for the entered address is not available. + + + + + Message signing failed. + + + + + Message signed. + + + + + The signature could not be decoded. + + + + + + Please check the signature and try again. + + + + + The signature did not match the message digest. + + + + + Message verification failed. + + + + + Message verified. + + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + + + + + TransactionDesc + + + Open until %1 + + + + + %1/offline + + + + + %1/unconfirmed + + + + + %1 confirmations + + + + + Status + + + + + , broadcast through %n node(s) + + + + + Date + + + + + Source + + + + + Generated + + + + + + From + + + + + + + To + + + + + + own address + + + + + label + + + + + + + + + Credit + + + + + matures in %n more block(s) + + + + + not accepted + + + + + + + + Debit + + + + + Transaction fee + + + + + Net amount + + + + + Message + + + + + Comment + + + + + Transaction ID + + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + + + + + Debug information + + + + + Transaction + + + + + Inputs + + + + + Amount + + + + + true + + + + + false + + + + + , has not been successfully broadcast yet + + + + + Open for %n more block(s) + + + + + unknown + + + + + TransactionDescDialog + + + Transaction details + + + + + This pane shows a detailed description of the transaction + + + + + TransactionTableModel + + + Date + + + + + Type + + + + + Address + + + + + Amount + + + + + Open for %n more block(s) + + + + + Open until %1 + + + + + Offline (%1 confirmations) + + + + + Unconfirmed (%1 of %2 confirmations) + + + + + Confirmed (%1 confirmations) + + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + + + + + Generated but not accepted + + + + + Received with + + + + + Received from + + + + + Sent to + + + + + Payment to yourself + + + + + Mined + + + + + (n/a) + + + + + Transaction status. Hover over this field to show number of confirmations. + + + + + Date and time that the transaction was received. + + + + + Type of transaction. + + + + + Destination address of transaction. + + + + + Amount removed from or added to balance. + + + + + TransactionView + + + + All + + + + + Today + + + + + This week + + + + + This month + + + + + Last month + + + + + This year + + + + + Range... + + + + + Received with + + + + + Sent to + + + + + To yourself + + + + + Mined + + + + + Other + + + + + Enter address or label to search + + + + + Min amount + + + + + Copy address + + + + + Copy label + + + + + Copy amount + + + + + Copy transaction ID + + + + + Edit label + + + + + Show transaction details + + + + + Export Transaction Data + + + + + Comma separated file (*.csv) + + + + + Confirmed + + + + + Date + + + + + Type + + + + + Label + + + + + Address + + + + + Amount + + + + + ID + + + + + Error exporting + + + + + Could not write to file %1. + + + + + Range: + + + + + to + + + + + WalletModel + + + Send Coins + + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + + + + + Usage: + + + + + Send command to -server or CryptoLottoTokend + + + + + List commands + + + + + Get help for a command + + + + + Options: + + + + + Specify configuration file (default: CryptoLottoToken.conf) + + + + + Specify pid file (default: CryptoLottoTokend.pid) + + + + + Specify data directory + + + + + Set database cache size in megabytes (default: 25) + + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + + + + + Maintain at most <n> connections to peers (default: 125) + + + + + Connect to a node to retrieve peer addresses, and disconnect + + + + + Specify your own public address + + + + + Threshold for disconnecting misbehaving peers (default: 100) + + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + + + + + Accept command line and JSON-RPC commands + + + + + Run in the background as a daemon and accept commands + + + + + Use the test network + + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + + + + + Connect only to the specified node(s) + + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + + + + + Select the version of socks proxy to use (4-5, default: 5) + + + + + Send trace/debug info to console instead of debug.log file + + + + + Send trace/debug info to debugger + + + + + Set maximum block size in bytes (default: 250000) + + + + + Set minimum block size in bytes (default: 0) + + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + + + + + Use UPnP to map the listening port (default: 1 when listening) + + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + + + + + Allow JSON-RPC connections from specified IP address + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + + + + + Upgrade wallet to latest format + + + + + Set key pool size to <n> (default: 100) + + + + + Rescan the block chain for missing wallet transactions + + + + + Use OpenSSL (https) for JSON-RPC connections + + + + + Server certificate file (default: server.cert) + + + + + Server private key (default: server.pem) + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + + This help message + + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + + + + + Connect through socks proxy + + + + + Allow DNS lookups for -addnode, -seednode and -connect + + + + + Loading addresses... + + + + + Error loading wallet.dat: Wallet corrupted + + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + + + + + Error loading wallet.dat + + + + + Invalid -proxy address: '%s' + + + + + Unknown network specified in -onlynet: '%s' + + + + + Unknown -socks proxy version requested: %i + + + + + Cannot resolve -bind address: '%s' + + + + + Cannot resolve -externalip address: '%s' + + + + + Invalid amount for -paytxfee=<amount>: '%s' + + + + + Invalid amount + + + + + Insufficient funds + + + + + Loading block index... + + + + + Add a node to connect to and attempt to keep the connection open + + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + + + + + Fee per KB to add to transactions you send + + + + + Loading wallet... + + + + + Cannot downgrade wallet + + + + + Cannot write default address + + + + + Rescanning... + + + + + Done loading + + + + + To use the %s option + + + + + Error + + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_ca.ts b/src/qt/locale/bitcoin_ca.ts new file mode 100644 index 0000000..2fb60d0 --- /dev/null +++ b/src/qt/locale/bitcoin_ca.ts @@ -0,0 +1,2917 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + A prop de CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + Llibreta d'adreçes + + + + Double-click to edit address or label + + + + + Create a new address + + + + + Copy the currently selected address to the system clipboard + Copia la selecció actual al porta-retalls del sistema + + + + &New Address + + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + + + + + &Copy Address + + + + + Show &QR Code + + + + + Sign a message to prove you own a CryptoLottoToken address + + + + + Sign &Message + + + + + Delete the currently selected address from the list + + + + + Export the data in the current tab to a file + + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + + + + + &Verify Message + + + + + &Delete + + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + + + + + &Edit + + + + + Send &Coins + + + + + Export Address Book Data + + + + + Comma separated file (*.csv) + + + + + Error exporting + + + + + Could not write to file %1. + + + + + AddressTableModel + + + Label + + + + + Address + + + + + (no label) + + + + + AskPassphraseDialog + + + Passphrase Dialog + + + + + Enter passphrase + + + + + New passphrase + + + + + Repeat new passphrase + + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + + + + + Encrypt wallet + + + + + This operation needs your wallet passphrase to unlock the wallet. + + + + + Unlock wallet + + + + + This operation needs your wallet passphrase to decrypt the wallet. + + + + + Decrypt wallet + + + + + Change passphrase + + + + + Enter the old and new passphrase to the wallet. + + + + + Confirm wallet encryption + + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + + + + + Are you sure you wish to encrypt your wallet? + + + + + 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. + + + + + + Warning: The Caps Lock key is on! + + + + + + Wallet encrypted + + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + + + + + + + + Wallet encryption failed + + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + + + + + + The supplied passphrases do not match. + + + + + Wallet unlock failed + + + + + + + The passphrase entered for the wallet decryption was incorrect. + + + + + Wallet decryption failed + + + + + Wallet passphrase was successfully changed. + + + + + BitcoinGUI + + + Sign &message... + + + + + Synchronizing with network... + + + + + &Overview + + + + + Show general overview of wallet + + + + + &Transactions + + + + + Browse transaction history + + + + + Edit the list of stored addresses and labels for sending + + + + + Show the list of addresses for receiving payments + + + + + E&xit + + + + + Quit application + + + + + Show information about CryptoLottoToken + + + + + About &Qt + + + + + Show information about Qt + + + + + &Options... + + + + + &Encrypt Wallet... + + + + + &Backup Wallet... + + + + + &Change Passphrase... + + + + + Importing blocks from disk... + + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + + + + + Modify configuration options for CryptoLottoToken + + + + + Backup wallet to another location + + + + + Change the passphrase used for wallet encryption + + + + + &Debug window + + + + + Open debugging and diagnostic console + + + + + &Verify message... + + + + + + CryptoLottoToken + + + + + Wallet + + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + + + + + &Show / Hide + + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + + + + + &Settings + + + + + &Help + + + + + Tabs toolbar + + + + + + [testnet] + + + + + CryptoLottoToken client + + + + + %n active connection(s) to CryptoLottoToken network + + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + + + + + Warning + + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + + + + + Catching up... + + + + + Confirm transaction fee + + + + + Sent transaction + + + + + Incoming transaction + + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + + + + + + URI handling + + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + + + + + EditAddressDialog + + + Edit Address + + + + + &Label + + + + + The label associated with this address book entry + + + + + &Address + + + + + The address associated with this address book entry. This can only be modified for sending addresses. + + + + + New receiving address + + + + + New sending address + + + + + Edit receiving address + + + + + Edit sending address + + + + + The entered address "%1" is already in the address book. + + + + + The entered address "%1" is not a valid CryptoLottoToken address. + + + + + Could not unlock wallet. + + + + + New key generation failed. + + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + + + + + version + + + + + Usage: + + + + + command-line options + + + + + UI options + + + + + Set language, for example "de_DE" (default: system locale) + + + + + Start minimized + + + + + Show splash screen on startup (default: 1) + + + + + OptionsDialog + + + Options + + + + + &Main + + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + + + + + Automatically start CryptoLottoToken after logging in to the system. + + + + + &Start CryptoLottoToken on system login + + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + + + + + Map port using &UPnP + + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + + + + + &Connect through SOCKS proxy: + + + + + Proxy &IP: + + + + + IP address of the proxy (e.g. 127.0.0.1) + + + + + &Port: + + + + + Port of the proxy (e.g. 9050) + + + + + SOCKS &Version: + + + + + SOCKS version of the proxy (e.g. 5) + + + + + &Window + + + + + Show only a tray icon after minimizing the window. + + + + + &Minimize to the tray instead of the taskbar + + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + + + + + M&inimize on close + + + + + &Display + + + + + User Interface &language: + + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + + + + + &Unit to show amounts in: + + + + + Choose the default subdivision unit to show in the interface and when sending coins. + + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + + + &Display addresses in transaction list + + + + + &OK + + + + + &Cancel + + + + + &Apply + + + + + default + + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + + + + + + This setting will take effect after restarting CryptoLottoToken. + + + + + The supplied proxy address is invalid. + + + + + OverviewPage + + + Form + + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + + + + + Balance: + + + + + Unconfirmed: + + + + + Wallet + + + + + Immature: + + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + + + + + Your current balance + + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + + + + + + out of sync + + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + + + + + Request Payment + + + + + Amount: + + + + + Label: + + + + + Message: + + + + + &Save As... + + + + + Error encoding URI into QR Code. + + + + + The entered amount is invalid, please check. + + + + + Resulting URI too long, try to reduce the text for label / message. + + + + + Save QR Code + + + + + PNG Images (*.png) + + + + + RPCConsole + + + Client name + + + + + + + + + + + + + + N/A + + + + + Client version + + + + + &Information + + + + + Using OpenSSL version + + + + + Startup time + + + + + Network + + + + + Number of connections + + + + + On testnet + + + + + Block chain + + + + + Current number of blocks + + + + + Estimated total blocks + + + + + Last block time + + + + + &Open + + + + + Command-line options + + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + + &Show + + + + + &Console + + + + + Build date + + + + + CryptoLottoToken - Debug window + + + + + CryptoLottoToken Core + + + + + Debug log file + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + + + + + Welcome to the CryptoLottoToken RPC console. + + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + + + + + Type <b>help</b> for an overview of available commands. + + + + + SendCoinsDialog + + + + + + + + + + Send Coins + + + + + Send to multiple recipients at once + + + + + Add &Recipient + + + + + Remove all transaction fields + + + + + Clear &All + + + + + Balance: + + + + + 123.456 BTC + + + + + Confirm the send action + + + + + S&end + + + + + <b>%1</b> to %2 (%3) + + + + + Confirm send coins + + + + + Are you sure you want to send %1? + + + + + and + + + + + The recipient address is not valid, please recheck. + + + + + The amount to pay must be larger than 0. + + + + + The amount exceeds your balance. + + + + + The total exceeds your balance when the %1 transaction fee is included. + + + + + Duplicate address found, can only send to each address once per send operation. + + + + + Error: Transaction creation failed! + + + + + 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. + + + + + SendCoinsEntry + + + Form + + + + + A&mount: + + + + + Pay &To: + + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + + + + + &Label: + + + + + Choose address from address book + + + + + Alt+A + + + + + Paste address from clipboard + + + + + Alt+P + + + + + Remove this recipient + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + + + + + &Sign Message + + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Choose an address from the address book + + + + + + Alt+A + + + + + Paste address from clipboard + + + + + Alt+P + + + + + Enter the message you want to sign here + + + + + Signature + + + + + Copy the current signature to the system clipboard + + + + + Sign the message to prove you own this CryptoLottoToken address + + + + + Sign &Message + + + + + Reset all sign message fields + + + + + + Clear &All + + + + + &Verify Message + + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + + + + + Verify &Message + + + + + Reset all verify message fields + + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Click "Sign Message" to generate signature + + + + + Enter CryptoLottoToken signature + + + + + + The entered address is invalid. + + + + + + + + Please check the address and try again. + + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + + + + + Private key for the entered address is not available. + + + + + Message signing failed. + + + + + Message signed. + + + + + The signature could not be decoded. + + + + + + Please check the signature and try again. + + + + + The signature did not match the message digest. + + + + + Message verification failed. + + + + + Message verified. + + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + + + + + TransactionDesc + + + Open until %1 + + + + + %1/offline + + + + + %1/unconfirmed + + + + + %1 confirmations + + + + + Status + + + + + , broadcast through %n node(s) + + + + + Date + + + + + Source + + + + + Generated + + + + + + From + + + + + + + To + + + + + + own address + + + + + label + + + + + + + + + Credit + + + + + matures in %n more block(s) + + + + + not accepted + + + + + + + + Debit + + + + + Transaction fee + + + + + Net amount + + + + + Message + + + + + Comment + + + + + Transaction ID + + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + + + + + Debug information + + + + + Transaction + + + + + Inputs + + + + + Amount + + + + + true + + + + + false + + + + + , has not been successfully broadcast yet + + + + + Open for %n more block(s) + + + + + unknown + + + + + TransactionDescDialog + + + Transaction details + + + + + This pane shows a detailed description of the transaction + + + + + TransactionTableModel + + + Date + + + + + Type + + + + + Address + + + + + Amount + + + + + Open for %n more block(s) + + + + + Open until %1 + + + + + Offline (%1 confirmations) + + + + + Unconfirmed (%1 of %2 confirmations) + + + + + Confirmed (%1 confirmations) + + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + + + + + Generated but not accepted + + + + + Received with + + + + + Received from + + + + + Sent to + + + + + Payment to yourself + + + + + Mined + + + + + (n/a) + + + + + Transaction status. Hover over this field to show number of confirmations. + + + + + Date and time that the transaction was received. + + + + + Type of transaction. + + + + + Destination address of transaction. + + + + + Amount removed from or added to balance. + + + + + TransactionView + + + + All + + + + + Today + + + + + This week + + + + + This month + + + + + Last month + + + + + This year + + + + + Range... + + + + + Received with + + + + + Sent to + + + + + To yourself + + + + + Mined + + + + + Other + + + + + Enter address or label to search + + + + + Min amount + + + + + Copy address + + + + + Copy label + + + + + Copy amount + + + + + Copy transaction ID + + + + + Edit label + + + + + Show transaction details + + + + + Export Transaction Data + + + + + Comma separated file (*.csv) + + + + + Confirmed + + + + + Date + + + + + Type + + + + + Label + + + + + Address + + + + + Amount + + + + + ID + + + + + Error exporting + + + + + Could not write to file %1. + + + + + Range: + + + + + to + + + + + WalletModel + + + Send Coins + + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + + + + + Usage: + + + + + Send command to -server or CryptoLottoTokend + + + + + List commands + + + + + Get help for a command + + + + + Options: + + + + + Specify configuration file (default: CryptoLottoToken.conf) + + + + + Specify pid file (default: CryptoLottoTokend.pid) + + + + + Specify data directory + + + + + Set database cache size in megabytes (default: 25) + + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + + + + + Maintain at most <n> connections to peers (default: 125) + + + + + Connect to a node to retrieve peer addresses, and disconnect + + + + + Specify your own public address + + + + + Threshold for disconnecting misbehaving peers (default: 100) + + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + + + + + Accept command line and JSON-RPC commands + + + + + Run in the background as a daemon and accept commands + + + + + Use the test network + + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + + + + + Connect only to the specified node(s) + + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + + + + + Select the version of socks proxy to use (4-5, default: 5) + + + + + Send trace/debug info to console instead of debug.log file + + + + + Send trace/debug info to debugger + + + + + Set maximum block size in bytes (default: 250000) + + + + + Set minimum block size in bytes (default: 0) + + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + + + + + Use UPnP to map the listening port (default: 1 when listening) + + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + + + + + Allow JSON-RPC connections from specified IP address + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + + + + + Upgrade wallet to latest format + + + + + Set key pool size to <n> (default: 100) + + + + + Rescan the block chain for missing wallet transactions + + + + + Use OpenSSL (https) for JSON-RPC connections + + + + + Server certificate file (default: server.cert) + + + + + Server private key (default: server.pem) + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + + This help message + + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + + + + + Connect through socks proxy + + + + + Allow DNS lookups for -addnode, -seednode and -connect + + + + + Loading addresses... + + + + + Error loading wallet.dat: Wallet corrupted + + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + + + + + Error loading wallet.dat + + + + + Invalid -proxy address: '%s' + + + + + Unknown network specified in -onlynet: '%s' + + + + + Unknown -socks proxy version requested: %i + + + + + Cannot resolve -bind address: '%s' + + + + + Cannot resolve -externalip address: '%s' + + + + + Invalid amount for -paytxfee=<amount>: '%s' + + + + + Invalid amount + + + + + Insufficient funds + + + + + Loading block index... + + + + + Add a node to connect to and attempt to keep the connection open + + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + + + + + Fee per KB to add to transactions you send + + + + + Loading wallet... + + + + + Cannot downgrade wallet + + + + + Cannot write default address + + + + + Rescanning... + + + + + Done loading + + + + + To use the %s option + + + + + Error + + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_ca_ES.qm b/src/qt/locale/bitcoin_ca_ES.qm new file mode 100644 index 0000000..7ed68ae Binary files /dev/null and b/src/qt/locale/bitcoin_ca_ES.qm differ diff --git a/src/qt/locale/bitcoin_ca_ES.ts b/src/qt/locale/bitcoin_ca_ES.ts new file mode 100644 index 0000000..4f91bc4 --- /dev/null +++ b/src/qt/locale/bitcoin_ca_ES.ts @@ -0,0 +1,2917 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + Sobre CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> versió + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + \n Aquest és software experimental.\n\n Distribuït sota llicència de software MIT/11, veure l'arxiu COPYING o http://www.opensource.org/licenses/mit-license.php.\n\nAquest producte inclou software desarrollat pel projecte OpenSSL per a l'ús de OppenSSL Toolkit (http://www.openssl.org/) i de softwqre criptogràfic escrit per l'Eric Young (eay@cryptsoft.com) i software UPnP escrit per en Thomas Bernard. + + + + Copyright + Copyright + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + Llibreta d'adreces + + + + Double-click to edit address or label + Feu doble clic per editar l'adreça o l'etiqueta + + + + Create a new address + Crear una nova adreça + + + + Copy the currently selected address to the system clipboard + Copiar l'adreça seleccionada al porta-retalls del sistema + + + + &New Address + &Nova adreça + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Aquestes són les teves adreces CryptoLottoToken per a rebre pagaments. Pot interesar-te proveïr diferents adreces a cadascun dels enviadors així pots identificar qui et va pagant. + + + + &Copy Address + &Copiar adreça + + + + Show &QR Code + Mostrar codi &QR + + + + Sign a message to prove you own a CryptoLottoToken address + Signa el missatge per provar que ets propietari de l'adreça CryptoLottoToken + + + + Sign &Message + Signar &Missatge + + + + Delete the currently selected address from the list + Esborrar l'adreça sel·leccionada + + + + Export the data in the current tab to a file + + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Verificar un missatge per asegurar-se que ha estat signat amb una adreça CryptoLottoToken específica + + + + &Verify Message + &Verificar el missatge + + + + &Delete + &Esborrar + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + Aquestes són la seva adreça de CryptoLottoToken per enviar els pagaments. Sempre revisi la quantitat i l'adreça del destinatari abans transferència de monedes. + + + + Copy &Label + Copiar &Etiqueta + + + + &Edit + &Editar + + + + Send &Coins + Enviar &Monedes + + + + Export Address Book Data + Exporta llibreta d'adreces + + + + Comma separated file (*.csv) + Arxiu de separació per comes (*.csv) + + + + Error exporting + Error en l'exportació + + + + Could not write to file %1. + No s'ha pogut escriure a l'arxiu %1. + + + + AddressTableModel + + + Label + Etiqueta + + + + Address + Adreça + + + + (no label) + (sense etiqueta) + + + + AskPassphraseDialog + + + Passphrase Dialog + Dialeg de contrasenya + + + + Enter passphrase + Introdueix contrasenya + + + + New passphrase + Nova contrasenya + + + + Repeat new passphrase + Repeteix la nova contrasenya + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Introdueixi la nova contrasenya al moneder<br/>Si us plau useu una contrasenya de <b>10 o més caracters aleatoris</b>, o <b>vuit o més paraules</b>. + + + + Encrypt wallet + Xifrar la cartera + + + + This operation needs your wallet passphrase to unlock the wallet. + Aquesta operació requereix la seva contrasenya del moneder per a desbloquejar-lo. + + + + Unlock wallet + Desbloqueja el moneder + + + + This operation needs your wallet passphrase to decrypt the wallet. + Aquesta operació requereix la seva contrasenya del moneder per a desencriptar-lo. + + + + Decrypt wallet + Desencripta el moneder + + + + Change passphrase + Canviar la contrasenya + + + + Enter the old and new passphrase to the wallet. + Introdueixi tant l'antiga com la nova contrasenya de moneder. + + + + Confirm wallet encryption + Confirmar l'encriptació del moneder + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Advertència: Si encripteu el vostre moneder i perdeu la constrasenya, <b>PERDREU TOTS ELS VOSTRES CryptoLottoTokenS</b>! + + + + Are you sure you wish to encrypt your wallet? + Esteu segur que voleu encriptar el vostre moneder? + + + + 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. + IMPORTANT: Tota copia de seguretat que hagis realitzat hauria de ser reemplaçada pel, recentment generat, arxiu encriptat del moneder. + + + + + Warning: The Caps Lock key is on! + Advertència: Les lletres majúscules estàn activades! + + + + + Wallet encrypted + Moneder encriptat + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken es tancarà ara per acabar el procés d'encriptació. Recorda que encriptar el teu moneder no protegeix completament els teus CryptoLottoTokens de ser robades per programari maliciós instal·lat al teu ordinador. + + + + + + + Wallet encryption failed + L'encriptació del moneder ha fallat + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + L'encriptació del moneder ha fallat per un error intern. El seu moneder no ha estat encriptat. + + + + + The supplied passphrases do not match. + La contrasenya introduïda no coincideix. + + + + Wallet unlock failed + El desbloqueig del moneder ha fallat + + + + + + The passphrase entered for the wallet decryption was incorrect. + La contrasenya introduïda per a desencriptar el moneder és incorrecte. + + + + Wallet decryption failed + La desencriptació del moneder ha fallat + + + + Wallet passphrase was successfully changed. + La contrasenya del moneder ha estat modificada correctament. + + + + BitcoinGUI + + + Sign &message... + Signar &missatge... + + + + Synchronizing with network... + Sincronitzant amb la xarxa ... + + + + &Overview + &Panorama general + + + + Show general overview of wallet + Mostra panorama general del moneder + + + + &Transactions + &Transaccions + + + + Browse transaction history + Cerca a l'historial de transaccions + + + + Edit the list of stored addresses and labels for sending + Edita la llista d'adreces emmagatzemada i etiquetes + + + + Show the list of addresses for receiving payments + Mostra el llistat d'adreces per rebre pagaments + + + + E&xit + S&ortir + + + + Quit application + Sortir de l'aplicació + + + + Show information about CryptoLottoToken + Mostra informació sobre CryptoLottoToken + + + + About &Qt + Sobre &Qt + + + + Show information about Qt + Mostra informació sobre Qt + + + + &Options... + &Opcions... + + + + &Encrypt Wallet... + &Xifrar moneder + + + + &Backup Wallet... + &Realitzant copia de seguretat del moneder... + + + + &Change Passphrase... + &Canviar contrasenya... + + + + Importing blocks from disk... + Important blocs del disc.. + + + + Reindexing blocks on disk... + Re-indexant blocs al disc... + + + + Send coins to a CryptoLottoToken address + Enviar monedes a una adreça CryptoLottoToken + + + + Modify configuration options for CryptoLottoToken + Modificar les opcions de configuració per CryptoLottoToken + + + + Backup wallet to another location + Realitzar còpia de seguretat del moneder a un altre directori + + + + Change the passphrase used for wallet encryption + Canviar la constrasenya d'encriptació del moneder + + + + &Debug window + &Finestra de debug + + + + Open debugging and diagnostic console + Obrir la consola de diagnòstic i debugging + + + + &Verify message... + &Verifica el missatge.. + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Moneder + + + + &Send + &Enviar + + + + &Receive + &Rebre + + + + &Addresses + &Adreces + + + + &About CryptoLottoToken + &Sobre CryptoLottoToken + + + + &Show / Hide + &Mostrar / Amagar + + + + Show or hide the main Window + Mostrar o amagar la finestra principal + + + + Encrypt the private keys that belong to your wallet + Xifrar les claus privades pertanyents al seu moneder + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Signa el missatges amb la seva adreça de CryptoLottoToken per provar que les poseeixes + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Verificar els missatges per assegurar-te que han estat signades amb una adreça CryptoLottoToken específica. + + + + &File + &Arxiu + + + + &Settings + &Configuració + + + + &Help + &Ajuda + + + + Tabs toolbar + Barra d'eines de seccions + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + Client CryptoLottoToken + + + + %n active connection(s) to CryptoLottoToken network + %n connexió activa a la xarxa CryptoLottoToken%n connexions actives a la xarxa CryptoLottoToken + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + Processat el %1 de %2 (estimat) dels blocs del històric de transaccions. + + + + Processed %1 blocks of transaction history. + Proccessats %1 blocs del històric de transaccions. + + + + %n hour(s) + %n hora%n hores + + + + %n day(s) + %n dia%n dies + + + + %n week(s) + %n setmana%n setmanes + + + + %1 behind + %1 radera + + + + Last received block was generated %1 ago. + Lúltim bloc rebut ha estat generat fa %1. + + + + Transactions after this will not yet be visible. + Les transaccions a partir d'això no seràn visibles. + + + + Error + Error + + + + Warning + Avís + + + + Information + Informació + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + Aquesta transacció supera el límit de tamany. Tot i així pots enviar-la amb una comissió de %1, que es destinen als nodes que processen la seva transacció i ajuda a donar suport a la xarxa. Vols pagar la comissió? + + + + Up to date + Al dia + + + + Catching up... + Posar-se al dia ... + + + + Confirm transaction fee + Confirmar comisió de transacció + + + + Sent transaction + Transacció enviada + + + + Incoming transaction + Transacció entrant + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Data: %1\nQuantitat %2\n Tipus: %3\n Adreça: %4\n + + + + + URI handling + Manejant URI + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + la URI no pot ser processada! Això es pot ser causat per una adreça CryptoLottoToken invalida o paràmetres URI malformats. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + El moneder està <b>encriptat</b> i actualment <b>desbloquejat</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + El moneder està <b>encriptat</b> i actualment <b>bloquejat</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + Ha tingut lloc un error fatal. CryptoLottoToken no pot continuar executant-se de manera segura i es tancará. + + + + ClientModel + + + Network Alert + Alerta de xarxa + + + + EditAddressDialog + + + Edit Address + Editar Adreça + + + + &Label + &Etiqueta + + + + The label associated with this address book entry + Etiqueta associada amb aquesta entrada de la llibreta d'adreces + + + + &Address + &Direcció + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Adreça associada amb aquesta entrada de la llibreta d'adreces. Només pot ser modificat per a enviar adreces. + + + + New receiving address + Nova adreça de recepció. + + + + New sending address + Nova adreça d'enviament + + + + Edit receiving address + Editar adreces de recepció + + + + Edit sending address + Editar adreces d'enviament + + + + The entered address "%1" is already in the address book. + L'adreça introduïda "%1" ja és present a la llibreta d'adreces. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + L'adreça introduida "%1" no és una adreça CryptoLottoToken valida. + + + + Could not unlock wallet. + No s'ha pogut desbloquejar el moneder. + + + + New key generation failed. + Ha fallat la generació d'una nova clau. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + versió + + + + Usage: + Ús: + + + + command-line options + Opcions de la línia d'ordres + + + + UI options + Opcions de IU + + + + Set language, for example "de_DE" (default: system locale) + Definir llenguatge, per exemple "de_DE" (per defecte: Preferències locals de sistema) + + + + Start minimized + Iniciar minimitzat + + + + Show splash screen on startup (default: 1) + Mostrar finestra de benvinguda a l'inici (per defecte: 1) + + + + OptionsDialog + + + Options + Opcions + + + + &Main + &Principal + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + Pagar &comisió de transacció + + + + Automatically start CryptoLottoToken after logging in to the system. + Iniciar automàticament CryptoLottoToken després de l'inici de sessió del sistema. + + + + &Start CryptoLottoToken on system login + &Iniciar CryptoLottoToken al inici de sessió del sistema. + + + + Reset all client options to default. + Reestablir totes les opcions del client. + + + + &Reset Options + &Reestablir Opcions + + + + &Network + &Xarxa + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Obrir el port del client de CryptoLottoToken al router de forma automàtica. Això només funciona quan el teu router implementa UPnP i l'opció està activada. + + + + Map port using &UPnP + Port obert amb &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Connectar a la xarxa CryptoLottoToken a través de un SOCKS proxy (per exemple connectant a través de Tor). + + + + &Connect through SOCKS proxy: + &Connecta a través de un proxy SOCKS: + + + + Proxy &IP: + &IP del proxy: + + + + IP address of the proxy (e.g. 127.0.0.1) + Adreça IP del proxy (per exemple 127.0.0.1) + + + + &Port: + &Port: + + + + Port of the proxy (e.g. 9050) + Port del proxy (per exemple 9050) + + + + SOCKS &Version: + &Versió de SOCKS: + + + + SOCKS version of the proxy (e.g. 5) + Versió SOCKS del proxy (per exemple 5) + + + + &Window + &Finestra + + + + Show only a tray icon after minimizing the window. + Mostrar només l'icona de la barra al minimitzar l'aplicació. + + + + &Minimize to the tray instead of the taskbar + &Minimitzar a la barra d'aplicacions + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Minimitza en comptes de sortir de la aplicació al tancar la finestra. Quan aquesta opció està activa, la aplicació només es tancarà al seleccionar Sortir al menú. + + + + M&inimize on close + M&inimitzar al tancar + + + + &Display + &Pantalla + + + + User Interface &language: + Llenguatge de la Interfície d'Usuari: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + Aquí pots definir el llenguatge de l'aplicatiu. Aquesta configuració tindrà efecte un cop es reiniciï CryptoLottoToken. + + + + &Unit to show amounts in: + &Unitats per mostrar les quantitats en: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Sel·lecciona la unitat de subdivisió per defecte per mostrar en la interficie quan s'envien monedes. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Mostrar adreces CryptoLottoToken als llistats de transaccions o no. + + + + &Display addresses in transaction list + &Mostrar adreces al llistat de transaccions + + + + &OK + &OK + + + + &Cancel + &Cancel·la + + + + &Apply + &Aplicar + + + + default + Per defecte + + + + Confirm options reset + Confirmi el reestabliment de les opcions + + + + Some settings may require a client restart to take effect. + Algunes configuracions poden requerir reiniciar el client per a que tinguin efecte. + + + + Do you want to proceed? + Vols procedir? + + + + + Warning + Avís + + + + + This setting will take effect after restarting CryptoLottoToken. + Aquesta configuració tindrà efecte un cop es reiniciï CryptoLottoToken. + + + + The supplied proxy address is invalid. + L'adreça proxy introduïda és invalida. + + + + OverviewPage + + + Form + Formulari + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + La informació mostrada pot no estar al día. El teu moneder es sincronitza automàticament amb la xarxa CryptoLottoToken un cop s'ha establert connexió, però aquest proces no s'ha completat encara. + + + + Balance: + Balanç: + + + + Unconfirmed: + Sense confirmar: + + + + Wallet + Moneder + + + + Immature: + Immatur: + + + + Mined balance that has not yet matured + Balanç minat que encara no ha madurat + + + + <b>Recent transactions</b> + <b>Transaccions recents</b> + + + + Your current balance + El seu balanç actual + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Total de transaccions encara sense confirmar, que encara no es content en el balanç actual + + + + + out of sync + Fora de sincronia + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + No es pot iniciar CryptoLottoToken: manejador clicla-per-pagar + + + + QRCodeDialog + + + QR Code Dialog + Dialeg del codi QR + + + + Request Payment + Reclamar pagament + + + + Amount: + Quantitat: + + + + Label: + Etiqueta: + + + + Message: + Missatge: + + + + &Save As... + &Desar com... + + + + Error encoding URI into QR Code. + Error codificant la URI en un codi QR. + + + + The entered amount is invalid, please check. + La quantitat introduïda és invalida, si us plau comprovi-la. + + + + Resulting URI too long, try to reduce the text for label / message. + URI resultant massa llarga, intenta reduir el text per a la etiqueta / missatge + + + + Save QR Code + Desar codi QR + + + + PNG Images (*.png) + Imatges PNG (*.png) + + + + RPCConsole + + + Client name + Nom del client + + + + + + + + + + + + + N/A + N/A + + + + Client version + Versió del client + + + + &Information + &Informació + + + + Using OpenSSL version + Utilitzant OpenSSL versió + + + + Startup time + &Temps d'inici + + + + Network + Xarxa + + + + Number of connections + Nombre de connexions + + + + On testnet + A testnet + + + + Block chain + Bloquejar cadena + + + + Current number of blocks + Nombre de blocs actuals + + + + Estimated total blocks + Total estimat de blocs + + + + Last block time + Últim temps de bloc + + + + &Open + &Obrir + + + + Command-line options + Opcions de línia d'ordres + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Mostrar el missatge d'ajuda de CryptoLottoToken-Qt per a obtenir un llistat de possibles ordres per a la línia d'ordres de CryptoLottoToken. + + + + &Show + &Mostrar + + + + &Console + &Consola + + + + Build date + Data de compilació + + + + CryptoLottoToken - Debug window + CryptoLottoToken -Finestra de debug + + + + CryptoLottoToken Core + Nucli de CryptoLottoToken + + + + Debug log file + Dietàri de debug + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + Obrir el dietari de debug de CryptoLottoToken del directori de dades actual. Aixó pot trigar uns quants segons per a dietàris grossos. + + + + Clear console + Netejar consola + + + + Welcome to the CryptoLottoToken RPC console. + Benvingut a la consola RPC de CryptoLottoToken + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Utilitza les fletxes d'amunt i avall per navegar per l'històric, i <b>Ctrl-L<\b> per netejar la pantalla. + + + + Type <b>help</b> for an overview of available commands. + Escriu <b>help<\b> per a obtenir una llistat de les ordres disponibles. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Enviar monedes + + + + Send to multiple recipients at once + Enviar a multiples destinataris al mateix temps + + + + Add &Recipient + Affegir &Destinatari + + + + Remove all transaction fields + Netejar tots els camps de la transacció + + + + Clear &All + Esborrar &Tot + + + + Balance: + Balanç: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Confirmi l'acció d'enviament + + + + S&end + E&nviar + + + + <b>%1</b> to %2 (%3) + <b>%1</b> to %2 (%3) + + + + Confirm send coins + Confirmar l'enviament de monedes + + + + Are you sure you want to send %1? + Estas segur que vols enviar %1? + + + + and + i + + + + The recipient address is not valid, please recheck. + L'adreça remetent no és vàlida, si us plau comprovi-la. + + + + The amount to pay must be larger than 0. + La quantitat a pagar ha de ser major que 0. + + + + The amount exceeds your balance. + Import superi el saldo de la seva compte. + + + + The total exceeds your balance when the %1 transaction fee is included. + El total excedeix el teu balanç quan s'afegeix la comisió a la transacció %1. + + + + Duplicate address found, can only send to each address once per send operation. + S'ha trobat una adreça duplicada, tan sols es pot enviar a cada adreça un cop per ordre de enviament. + + + + Error: Transaction creation failed! + Error: La ceació de la transacció ha fallat! + + + + 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. + Error: La transacció ha estat rebutjada. Això pot passar si alguna de les monedes del teu moneder ja s'han gastat, com si haguesis usat una copia de l'arxiu wallet.dat i s'haguessin gastat monedes de la copia però sense marcar com gastades en aquest. + + + + SendCoinsEntry + + + Form + Formulari + + + + A&mount: + Q&uantitat: + + + + Pay &To: + Pagar &A: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + La adreça a on envia el pagament (per exemple: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + Introdueixi una etiquera per a aquesta adreça per afegir-la a la llibreta d'adreces + + + + &Label: + &Etiqueta: + + + + Choose address from address book + Escollir adreça del llibre d'adreces + + + + Alt+A + Alta+A + + + + Paste address from clipboard + Enganxar adreça del porta-retalls + + + + Alt+P + Alt+P + + + + Remove this recipient + Eliminar aquest destinatari + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Introdueixi una adreça de CryptoLottoToken (per exemple Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Signatures .Signar/Verificar un Missatge + + + + &Sign Message + &Signar Missatge + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Pots signar missatges amb la teva adreça per provar que són teus. Sigues cautelòs al signar qualsevol cosa, ja que els atacs phising poden intentar confondre't per a que els hi signis amb la teva identitat. Tan sols signa als documents completament detallats amb els que hi estàs d'acord. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + La adreça amb la que signat els missatges (per exemple Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Escollir una adreça de la llibreta de direccions + + + + + Alt+A + Alta+A + + + + Paste address from clipboard + Enganxar adreça del porta-retalls + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Introdueix aqui el missatge que vols signar + + + + Signature + Signatura + + + + Copy the current signature to the system clipboard + Copiar la signatura actual al porta-retalls del sistema + + + + Sign the message to prove you own this CryptoLottoToken address + Signa el missatge per provar que ets propietari d'aquesta adreça CryptoLottoToken + + + + Sign &Message + Signar &Missatge + + + + Reset all sign message fields + Neteja tots els camps de clau + + + + + Clear &All + Esborrar &Tot + + + + &Verify Message + &Verificar el missatge + + + + Enter the signing 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. + Introdueixi l'adreça signant, missatge (assegura't que copies salts de línia, espais, tabuladors, etc excactament tot el text) i la signatura a sota per verificar el missatge. Per evitar ser enganyat per un atac home-entre-mig, vés amb compte de no llegir més en la signatura del que hi ha al missatge signat mateix. + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + La adreça amb el que el missatge va ser signat (per exemple Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Verificar el missatge per assegurar-se que ha estat signat amb una adreça CryptoLottoToken específica + + + + Verify &Message + Verificar &Missatge + + + + Reset all verify message fields + Neteja tots els camps de verificació de missatge + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Introdueixi una adreça de CryptoLottoToken (per exemple Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Clica "Signar Missatge" per a generar una signatura + + + + Enter CryptoLottoToken signature + Introduïr una clau CryptoLottoToken + + + + + The entered address is invalid. + L'adreça intoduïda és invàlida. + + + + + + + Please check the address and try again. + Siu us plau, comprovi l'adreça i provi de nou. + + + + + The entered address does not refer to a key. + L'adreça introduïda no referencia a cap clau. + + + + Wallet unlock was cancelled. + El desbloqueig del moneder ha estat cancelat. + + + + Private key for the entered address is not available. + La clau privada per a la adreça introduïda no està disponible. + + + + Message signing failed. + El signat del missatge ha fallat. + + + + Message signed. + Missatge signat. + + + + The signature could not be decoded. + La signatura no s'ha pogut decodificar . + + + + + Please check the signature and try again. + Su us plau, comprovi la signatura i provi de nou. + + + + The signature did not match the message digest. + La signatura no coincideix amb el resum del missatge. + + + + Message verification failed. + Ha fallat la verificació del missatge. + + + + Message verified. + Missatge verificat. + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + + + + + TransactionDesc + + + Open until %1 + Obert fins %1 + + + + %1/offline + %1/offline + + + + %1/unconfirmed + %1/sense confirmar + + + + %1 confirmations + %1 confrimacions + + + + Status + Estat + + + + , broadcast through %n node(s) + , difusió a través de %n node, difusió a través de %n nodes + + + + Date + Data + + + + Source + Font + + + + Generated + Generat + + + + + From + Des de + + + + + + To + A + + + + + own address + Adreça pròpia + + + + label + etiqueta + + + + + + + + Credit + Crèdit + + + + matures in %n more block(s) + disponible en %n bloc mésdisponibles en %n blocs més + + + + not accepted + no acceptat + + + + + + + Debit + Dèbit + + + + Transaction fee + Comissió de transacció + + + + Net amount + Quantitat neta + + + + Message + Missatge + + + + Comment + Comentar + + + + Transaction ID + ID de transacció + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Les monedes generades han de madurar 120 blocs abans de poder ser gastades. Quan has generat aquest bloc, aquest ha estat transmés a la xarxa per a ser afegit a la cadena de blocs. Si no arriba a ser acceptat a la cadena, el seu estat passará a "no acceptat" i no podrá ser gastat. Això pot ocòrrer ocasionalment si un altre node genera un bloc a pocs segons del teu. + + + + Debug information + Informació de debug + + + + Transaction + Transacció + + + + Inputs + Entrades + + + + Amount + Quantitat + + + + true + cert + + + + false + fals + + + + , has not been successfully broadcast yet + , encara no ha estat emès correctement + + + + Open for %n more block(s) + Obre per %n bloc mésObre per %n blocs més + + + + unknown + desconegut + + + + TransactionDescDialog + + + Transaction details + Detall de la transacció + + + + This pane shows a detailed description of the transaction + Aquest panell mostra una descripció detallada de la transacció + + + + TransactionTableModel + + + Date + Data + + + + Type + Tipus + + + + Address + Direcció + + + + Amount + Quantitat + + + + Open for %n more block(s) + Obre per %n bloc mésObre per %n blocs més + + + + Open until %1 + Obert fins %1 + + + + Offline (%1 confirmations) + Sense connexió (%1 confirmacions) + + + + Unconfirmed (%1 of %2 confirmations) + Sense confirmar (%1 de %2 confirmacions) + + + + Confirmed (%1 confirmations) + Confirmat (%1 confirmacions) + + + + Mined balance will be available when it matures in %n more block(s) + El saldo recent minat estarà disponible quan venci el termini en %n bloc mésEl saldo recent minat estarà disponible quan venci el termini en %n blocs més + + + + This block was not received by any other nodes and will probably not be accepted! + Aquest bloc no ha estat rebut per cap altre node i probablement no serà acceptat! + + + + Generated but not accepted + Generat però no acceptat + + + + Received with + Rebut amb + + + + Received from + Rebut de + + + + Sent to + Enviat a + + + + Payment to yourself + Pagament a un mateix + + + + Mined + Minat + + + + (n/a) + (n/a) + + + + Transaction status. Hover over this field to show number of confirmations. + Estat de la transacció. Desplaça't per aquí sobre per mostrar el nombre de confirmacions. + + + + Date and time that the transaction was received. + Data i hora en que la transacció va ser rebuda. + + + + Type of transaction. + Tipus de transacció. + + + + Destination address of transaction. + Adreça del destinatari de la transacció. + + + + Amount removed from or added to balance. + Quantitat extreta o afegida del balanç. + + + + TransactionView + + + + All + Tot + + + + Today + Avui + + + + This week + Aquesta setmana + + + + This month + Aquest mes + + + + Last month + El mes passat + + + + This year + Enguany + + + + Range... + Rang... + + + + Received with + Rebut amb + + + + Sent to + Enviat a + + + + To yourself + A tu mateix + + + + Mined + Minat + + + + Other + Altres + + + + Enter address or label to search + Introdueix una adreça o una etiqueta per cercar + + + + Min amount + Quantitat mínima + + + + Copy address + Copiar adreça + + + + Copy label + Copiar etiqueta + + + + Copy amount + Copiar quantitat + + + + Copy transaction ID + Copiar ID de transacció + + + + Edit label + Editar etiqueta + + + + Show transaction details + Mostra detalls de la transacció + + + + Export Transaction Data + Exportar detalls de la transacció + + + + Comma separated file (*.csv) + Arxiu de separació per comes (*.csv) + + + + Confirmed + Confirmat + + + + Date + Data + + + + Type + Tipus + + + + Label + Etiqueta + + + + Address + Direcció + + + + Amount + Quantitat + + + + ID + ID + + + + Error exporting + Error en l'exportació + + + + Could not write to file %1. + No s'ha pogut escriure a l'arxiu %1. + + + + Range: + Rang: + + + + to + a + + + + WalletModel + + + Send Coins + Enviar monedes + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + + + + + Backup Wallet + Realitzar còpia de seguretat del moneder + + + + Wallet Data (*.dat) + Dades del moneder (*.dat) + + + + Backup Failed + Còpia de seguretat faillida + + + + There was an error trying to save the wallet data to the new location. + Hi ha hagut un error intentant desar les dades del moneder al nou directori + + + + Backup Successful + Copia de seguretat realitzada correctament + + + + The wallet data was successfully saved to the new location. + Les dades del moneder han estat desades cirrectament al nou emplaçament. + + + + bitcoin-core + + + CryptoLottoToken version + Versió de CryptoLottoToken + + + + Usage: + Ús: + + + + Send command to -server or CryptoLottoTokend + Enviar comanda a -servidor o CryptoLottoTokend + + + + List commands + Llista d'ordres + + + + Get help for a command + Obtenir ajuda per a un ordre. + + + + Options: + Opcions: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Especificat arxiu de configuració (per defecte: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + Especificar arxiu pid (per defecte: CryptoLottoTokend.pid) + + + + Specify data directory + Especificar directori de dades + + + + Set database cache size in megabytes (default: 25) + Establir tamany de la memoria cau en megabytes (per defecte: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Escoltar connexions a <port> (per defecte: 22556 o testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Mantenir com a molt <n> connexions a peers (per defecte: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Connectar al node per obtenir les adreces de les connexions, i desconectar + + + + Specify your own public address + Especificar la teva adreça pública + + + + Threshold for disconnecting misbehaving peers (default: 100) + Límit per a desconectar connexions errònies (per defecte: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Nombre de segons abans de reconectar amb connexions errònies (per defecte: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + Ha sorgit un error al configurar el port RPC %u escoltant a IPv4: %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Escoltar connexions JSON-RPC al port <port> (per defecte: 22555 o testnet:44555) + + + + Accept command line and JSON-RPC commands + Acceptar línia d'ordres i ordres JSON-RPC + + + + Run in the background as a daemon and accept commands + Executar en segon pla com a programa dimoni i acceptar ordres + + + + Use the test network + Usar la xarxa de prova + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Aceptar connexions d'afora (per defecte: 1 si no -proxy o -connect) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + %s has de establir una contrasenya RPC a l'arxiu de configuració:\n%s\nEs recomana que useu la següent constrasenya aleatòria:\nrpcuser=CryptoLottoTokenrpc\nrpcpassword=%s\n(no necesiteu recordar aquesta contrsenya)\nEl nom d'usuari i contrasenya NO HAN de ser els mateixos.\nSi l'arxiu no existeix, crea'l amb els permisos d'arxiu de només lectura per al propietari.\nTambé es recomana establir la notificació d'alertes i així seràs notificat de les incidències;\nper exemple: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + Ha sorgit un error al configurar el port RPC %u escoltant a IPv6, retrocedint a IPv4: %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + Vincular a una adreça específica i sempre escoltar-hi. Utilitza la notació [host]:port per IPv6 + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + No es pot bloquejar el directori de dades %s. Probablement CryptoLottoToken ja estigui en execució. + + + + 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. + Error: La transacció ha estat rebutjada. Això pot passar si alguna de les monedes del teu moneder ja s'han gastat, com si haguesis usat una copia de l'arxiu wallet.dat i s'haguessin gastat monedes de la copia però sense marcar com gastades en aquest. + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + Error: Aquesta transacció requereix una comissió d'almenys %s degut al seu import, complexitat o per l'ús de fons recentment rebuts! + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + Executar ordre al rebre una alerta rellevant (%s al cmd es reemplaça per message) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Executar una ordre quan una transacció del moneder canviï (%s in cmd es canvia per TxID) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Establir una mida màxima de transaccions d'alta prioritat/baixa comisió en bytes (per defecte: 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + Aquesta és una versió de pre-llançament - utilitza-la sota la teva responsabilitat - No usar per a minería o aplicacions de compra-venda + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Advertència: el -paytxfee és molt elevat! Aquesta és la comissió de transacció que pagaràs quan enviis una transacció. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + Advertència: Les transaccions mostrades poden no ser correctes! Pot esser que necessitis actualitzar, o bé que altres nodes ho necessitin. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Advertència: Si us plau comprovi que la data i hora del seu computador siguin correctes! Si el seu rellotge està mal configurat, CryptoLottoToken no funcionará de manera apropiada. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Advertència: Error llegint l'arxiu wallet.dat!! Totes les claus es llegeixen correctament, però hi ha dades de transaccions o entrades del llibre d'adreces absents o bé son incorrectes. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Advertència: L'arxiu wallet.dat és corrupte, dades rescatades! L'arxiu wallet.dat original ha estat desat com wallet.{estampa_temporal}.bak al directori %s; si el teu balanç o transaccions son incorrectes hauries de restaurar-lo de un backup. + + + + Attempt to recover private keys from a corrupt wallet.dat + Intentar recuperar les claus privades d'un arxiu wallet.dat corrupte + + + + Block creation options: + Opcions de la creació de blocs: + + + + Connect only to the specified node(s) + Connectar només al(s) node(s) especificats + + + + Corrupted block database detected + S'ha detectat una base de dades de blocs corrupta + + + + Discover own IP address (default: 1 when listening and no -externalip) + Descobrir la pròpia adreça IP (per defecte: 1 quan escoltant i no -externalip) + + + + Do you want to rebuild the block database now? + Vols reconstruir la base de dades de blocs ara? + + + + Error initializing block database + Error carregant la base de dades de blocs + + + + Error initializing wallet database environment %s! + Error inicialitzant l'entorn de la base de dades del moneder %s! + + + + Error loading block database + Error carregant la base de dades del bloc + + + + Error opening block database + Error obrint la base de dades de blocs + + + + Error: Disk space is low! + Error: Espai al disc baix! + + + + Error: Wallet locked, unable to create transaction! + Error: El moneder està blocat, no és possible crear la transacció! + + + + Error: system error: + Error: error de sistema: + + + + Failed to listen on any port. Use -listen=0 if you want this. + Error al escoltar a qualsevol port. Utilitza -listen=0 si vols això. + + + + Failed to read block info + Ha fallat la lectura de la informació del bloc + + + + Failed to read block + Ha fallat la lectura del bloc + + + + Failed to sync block index + Ha fallat la sincronització de l'índex de bloc + + + + Failed to write block index + Ha fallat la escriptura de l'índex de blocs + + + + Failed to write block info + Ha fallat la escriptura de la informació de bloc + + + + Failed to write block + Ha fallat l'escriptura del bloc + + + + Failed to write file info + Ha fallat l'escriptura de l'arxiu info + + + + Failed to write to coin database + Ha fallat l'escriptura de la basse de dades de monedes + + + + Failed to write transaction index + Ha fallat l'escriptura de l'índex de transaccions + + + + Failed to write undo data + Ha fallat el desfer de dades + + + + Find peers using DNS lookup (default: 1 unless -connect) + Cerca punts de connexió usant rastreig de DNS (per defecte: 1 tret d'usar -connect) + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + Quants blocs s'han de confirmar a l'inici (per defecte: 288, 0 = tots) + + + + How thorough the block verification is (0-4, default: 3) + Com verificar el bloc (0-4, per defecte 3) + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + Reconstruir l'índex de la cadena de blocs dels arxius actuals blk000??.dat + + + + Set the number of threads to service RPC calls (default: 4) + Estableix el nombre de fils per atendre trucades RPC (per defecte: 4) + + + + Verifying blocks... + Verificant blocs... + + + + Verifying wallet... + Verificant moneder... + + + + Imports blocks from external blk000??.dat file + Importa blocs de un fitxer blk000??.dat extern + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + &Informació + + + + Invalid -tor address: '%s' + Adreça -tor invàlida: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + Mantenir tot l'índex de transaccions (per defecte: 0) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Mida màxima del buffer de recepció per a cada connexió, <n>*1000 bytes (default: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Mida màxima del buffer d'enviament per a cada connexió, <n>*1000 bytes (default: 5000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + Tan sols acceptar cadenes de blocs que coincideixin amb els punts de prova (per defecte: 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Només connectar als nodes de la xarxa <net> (IPv4, IPv6 o Tor) + + + + Output extra debugging information. Implies all other -debug* options + Sortida de la informació extra de debugging. Implica totes les demés opcions -debug* + + + + Output extra network debugging information + Sortida de la informació extra de debugging de xarxa. + + + + Prepend debug output with timestamp (default: 1) + Anteposar estampa temporal a les dades de debug + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + Opcions SSL: (veure la Wiki de CryptoLottoToken per a instruccions de configuració SSL) + + + + Select the version of socks proxy to use (4-5, default: 5) + Selecciona la versió de socks proxy a utilitzar (4-5, per defecte: 5) + + + + Send trace/debug info to console instead of debug.log file + Enviar informació de traça/debug a la consola en comptes del arxiu debug.log + + + + Send trace/debug info to debugger + Enviar informació de traça/debug a un debugger + + + + Set maximum block size in bytes (default: 250000) + Establir una mida màxima de bloc en bytes (per defecte: 250000) + + + + Set minimum block size in bytes (default: 0) + Establir una mida mínima de bloc en bytes (per defecte: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Reduir l'arxiu debug.log al iniciar el client (per defecte 1 quan no -debug) + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + Especificar el temps limit per a un intent de connexió en milisegons (per defecte: 5000) + + + + System error: + Error de sistema: + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + Utilitza UPnP per a mapejar els ports d'escolta (per defecte: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Utilitza UPnP per a mapejar els ports d'escolta (per defecte: 1 quan s'escolta) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Utilitzar proxy per arribar als serveis tor amagats (per defecte: el mateix que -proxy) + + + + Username for JSON-RPC connections + Nom d'usuari per a connexions JSON-RPC + + + + Warning + Avís + + + + Warning: This version is obsolete, upgrade required! + Advertència: Aquetsa versió està obsoleta, és necessari actualitzar! + + + + You need to rebuild the databases using -reindex to change -txindex + Necessiteu reconstruir les bases de dades usant -reindex per canviar -txindex + + + + wallet.dat corrupt, salvage failed + L'arxiu wallet.data és corrupte, el rescat de les dades ha fallat + + + + Password for JSON-RPC connections + Contrasenya per a connexions JSON-RPC + + + + Allow JSON-RPC connections from specified IP address + Permetre connexions JSON-RPC d'adreces IP específiques + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Enviar ordre al node en execució a <ip> (per defecte: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Executar orde quan el millor bloc canviï (%s al cmd es reemplaça per un bloc de hash) + + + + Upgrade wallet to latest format + Actualitzar moneder a l'últim format + + + + Set key pool size to <n> (default: 100) + Establir límit de nombre de claus a <n> (per defecte: 100) + + + + Rescan the block chain for missing wallet transactions + Re-escanejar cadena de blocs en cerca de transaccions de moneder perdudes + + + + Use OpenSSL (https) for JSON-RPC connections + Utilitzar OpenSSL (https) per a connexions JSON-RPC + + + + Server certificate file (default: server.cert) + Arxiu del certificat de servidor (per defecte: server.cert) + + + + Server private key (default: server.pem) + Clau privada del servidor (per defecte: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Xifrats acceptats (per defecte: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Aquest misatge d'ajuda + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Impossible d'unir %s a aquest ordinador (s'ha retornat l'error %d, %s) + + + + Connect through socks proxy + Connectar a través de socks proxy + + + + Allow DNS lookups for -addnode, -seednode and -connect + Permetre consultes DNS per a -addnode, -seednode i -connect + + + + Loading addresses... + Carregant adreces... + + + + Error loading wallet.dat: Wallet corrupted + Error carregant wallet.dat: Moneder corrupte + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Error carregant wallet.dat: El moneder requereix una versió de CryptoLottoToken més moderna + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + El moneder necesita ser re-escrit: re-inicia CryptoLottoToken per a completar la tasca + + + + Error loading wallet.dat + Error carregant wallet.dat + + + + Invalid -proxy address: '%s' + Adreça -proxy invalida: '%s' + + + + Unknown network specified in -onlynet: '%s' + Xarxa desconeguda especificada a -onlynet: '%s' + + + + Unknown -socks proxy version requested: %i + S'ha demanat una versió desconeguda de -socks proxy: %i + + + + Cannot resolve -bind address: '%s' + No es pot resoldre l'adreça -bind: '%s' + + + + Cannot resolve -externalip address: '%s' + No es pot resoldre l'adreça -externalip: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + Quantitat invalida per a -paytxfee=<amount>: '%s' + + + + Invalid amount + Quanitat invalida + + + + Insufficient funds + Balanç insuficient + + + + Loading block index... + Carregant índex de blocs... + + + + Add a node to connect to and attempt to keep the connection open + Afegir un node per a connectar's-hi i intentar mantenir la connexió oberta + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Impossible d'unir %s en aquest ordinador. Probablement CryptoLottoToken ja estigui en execució. + + + + Fee per KB to add to transactions you send + Comisió a afegir per cada KB de transaccions que enviïs + + + + Loading wallet... + Carregant moneder... + + + + Cannot downgrade wallet + No es pot reduir la versió del moneder + + + + Cannot write default address + No es pot escriure l'adreça per defecte + + + + Rescanning... + Re-escanejant... + + + + Done loading + Càrrega acabada + + + + To use the %s option + Utilitza la opció %s + + + + Error + Error + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + Has de configurar el rpcpassword=<password> a l'arxiu de configuració:\n %s\n Si l'arxiu no existeix, crea'l amb els permís owner-readable-only. + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_cs.qm b/src/qt/locale/bitcoin_cs.qm new file mode 100644 index 0000000..61ae46a Binary files /dev/null and b/src/qt/locale/bitcoin_cs.qm differ diff --git a/src/qt/locale/bitcoin_cs.ts b/src/qt/locale/bitcoin_cs.ts new file mode 100644 index 0000000..2a10f88 --- /dev/null +++ b/src/qt/locale/bitcoin_cs.ts @@ -0,0 +1,2938 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + O CryptoLottoTokenu + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> verze + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +Tohle je experimentální program. + +Šířen pod licencí MIT/X11, viz přiložený soubor COPYING nebo http://www.opensource.org/licenses/mit-license.php. + +Tento produkt zahrnuje programy vyvinuté OpenSSL Projektem pro použití v OpenSSL Toolkitu (http://www.openssl.org/) a kryptografický program od Erika Younga (eay@cryptsoft.com) a program UPnP od Thomase Bernarda. + + + + Copyright + Copyright + + + + The CryptoLottoToken developers + Vývojáři CryptoLottoTokenu + + + + AddressBookPage + + + Address Book + Adresář + + + + Double-click to edit address or label + Dvojklikem myši začneš upravovat označení adresy + + + + Create a new address + Vytvoř novou adresu + + + + Copy the currently selected address to the system clipboard + Zkopíruj aktuálně vybranou adresu do systémové schránky + + + + &New Address + Nová &adresa + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Tohle jsou tvé CryptoLottoTokenové adresy pro příjem plateb. Můžeš dát pokaždé každému plátci novou adresu, abys věděl, kdo ti kdy kolik platil. + + + + &Copy Address + &Kopíruj adresu + + + + Show &QR Code + Zobraz &QR kód + + + + Sign a message to prove you own a CryptoLottoToken address + Podepiš zprávu, čímž prokážeš, že jsi vlastníkem CryptoLottoTokenové adresy + + + + Sign &Message + Po&depiš zprávu + + + + Delete the currently selected address from the list + Smaž zvolenou adresu ze seznamu + + + + Export the data in the current tab to a file + Exportuj data z tohoto panelu do souboru + + + + &Export + &Export + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Ověř zprávu, aby ses ujistil, že byla podepsána danou CryptoLottoTokenovou adresou + + + + &Verify Message + &Ověř zprávu + + + + &Delete + S&maž + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + Tohle jsou tvé CryptoLottoTokenové adresy pro posílání plateb. Před odesláním mincí si vždy zkontroluj částku a cílovou adresu. + + + + Copy &Label + Kopíruj &označení + + + + &Edit + &Uprav + + + + Send &Coins + Pošli min&ce + + + + Export Address Book Data + Exportuj data adresáře + + + + Comma separated file (*.csv) + CSV formát (*.csv) + + + + Error exporting + Chyba při exportu + + + + Could not write to file %1. + Nemohu zapisovat do souboru %1. + + + + AddressTableModel + + + Label + Označení + + + + Address + Adresa + + + + (no label) + (bez označení) + + + + AskPassphraseDialog + + + Passphrase Dialog + Změna hesla + + + + Enter passphrase + Zadej platné heslo + + + + New passphrase + Zadej nové heslo + + + + Repeat new passphrase + Totéž heslo ještě jednou + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Zadej nové heslo k peněžence.<br/>Použij <b>alespoň 10 náhodných znaků</b> nebo <b>alespoň osm slov</b>. + + + + Encrypt wallet + Zašifruj peněženku + + + + This operation needs your wallet passphrase to unlock the wallet. + K provedení této operace musíš zadat heslo k peněžence, aby se mohla odemknout. + + + + Unlock wallet + Odemkni peněženku + + + + This operation needs your wallet passphrase to decrypt the wallet. + K provedení této operace musíš zadat heslo k peněžence, aby se mohla dešifrovat. + + + + Decrypt wallet + Dešifruj peněženku + + + + Change passphrase + Změň heslo + + + + Enter the old and new passphrase to the wallet. + Zadej staré a nové heslo k peněžence. + + + + Confirm wallet encryption + Potvrď zašifrování peněženky + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Varování: Pokud si zašifruješ peněženku a ztratíš či zapomeneš heslo, <b>PŘIJDEŠ O VŠECHNY CryptoLottoTokenY</b>! + + + + Are you sure you wish to encrypt your wallet? + Jsi si jistý, že chceš peněženku zašifrovat? + + + + 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. + DŮLEŽITÉ: Všechny předchozí zálohy peněženky by měly být nahrazeny nově vygenerovanou, zašifrovanou peněženkou. Z bezpečnostních důvodů budou předchozí zálohy nešifrované peněženky nepoužitelné, jakmile začneš používat novou zašifrovanou peněženku. + + + + + Warning: The Caps Lock key is on! + Upozornění: Caps Lock je zapnutý! + + + + + Wallet encrypted + Peněženka je zašifrována + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken se teď ukončí, aby dokončil zašifrování. Pamatuj však, že pouhé zašifrování peněženky úplně nezabraňuje krádeži tvých CryptoLottoTokenů malwarem, kterým se může počítač nakazit. + + + + + + + Wallet encryption failed + Zašifrování peněženky selhalo + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Zašifrování peněženky selhalo kvůli vnitřní chybě. Tvá peněženka tedy nebyla zašifrována. + + + + + The supplied passphrases do not match. + Zadaná hesla nejsou shodná. + + + + Wallet unlock failed + Odemčení peněženky selhalo + + + + + + The passphrase entered for the wallet decryption was incorrect. + Nezadal jsi správné heslo pro dešifrování peněženky. + + + + Wallet decryption failed + Dešifrování peněženky selhalo + + + + Wallet passphrase was successfully changed. + Heslo k peněžence bylo v pořádku změněno. + + + + BitcoinGUI + + + Sign &message... + Po&depiš zprávu... + + + + Synchronizing with network... + Synchronizuji se se sítí... + + + + &Overview + &Přehled + + + + Show general overview of wallet + Zobraz celkový přehled peněženky + + + + &Transactions + &Transakce + + + + Browse transaction history + Procházej historii transakcí + + + + Edit the list of stored addresses and labels for sending + Uprav seznam uložených adres a jejich označení + + + + Show the list of addresses for receiving payments + Zobraz seznam adres pro příjem plateb + + + + E&xit + &Konec + + + + Quit application + Ukonči aplikaci + + + + Show information about CryptoLottoToken + Zobraz informace o CryptoLottoTokenu + + + + About &Qt + O &Qt + + + + Show information about Qt + Zobraz informace o Qt + + + + &Options... + &Možnosti... + + + + &Encrypt Wallet... + Zaši&fruj peněženku... + + + + &Backup Wallet... + &Zazálohuj peněženku... + + + + &Change Passphrase... + Změň &heslo... + + + + Importing blocks from disk... + Importuji bloky z disku... + + + + Reindexing blocks on disk... + Vytvářím nový index bloků na disku... + + + + Send coins to a CryptoLottoToken address + Pošli mince na CryptoLottoTokenovou adresu + + + + Modify configuration options for CryptoLottoToken + Uprav nastavení CryptoLottoTokenu + + + + Backup wallet to another location + Zazálohuj peněženku na jiné místo + + + + Change the passphrase used for wallet encryption + Změň heslo k šifrování peněženky + + + + &Debug window + &Ladicí okno + + + + Open debugging and diagnostic console + Otevři ladicí a diagnostickou konzoli + + + + &Verify message... + &Ověř zprávu... + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Peněženka + + + + &Send + &Pošli + + + + &Receive + Při&jmi + + + + &Addresses + &Adresy + + + + &About CryptoLottoToken + O &CryptoLottoTokenu + + + + &Show / Hide + &Zobraz/Skryj + + + + Show or hide the main Window + Zobraz nebo skryj hlavní okno + + + + Encrypt the private keys that belong to your wallet + Zašifruj soukromé klíče ve své peněžence + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Podepiš zprávy svými CryptoLottoTokenovými adresami, čímž prokážeš, že jsi jejich vlastníkem + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Ověř zprávy, aby ses ujistil, že byly podepsány danými CryptoLottoTokenovými adresami + + + + &File + &Soubor + + + + &Settings + &Nastavení + + + + &Help + Ná&pověda + + + + Tabs toolbar + Panel s listy + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + CryptoLottoToken klient + + + + %n active connection(s) to CryptoLottoToken network + %n aktivní spojení do CryptoLottoTokenové sítě%n aktivní spojení do CryptoLottoTokenové sítě%n aktivních spojení do CryptoLottoTokenové sítě + + + + No block source available... + Není dostupný žádný zdroj bloků... + + + + Processed %1 of %2 (estimated) blocks of transaction history. + Zpracováno %1 z přibližně %2 bloků transakční historie. + + + + Processed %1 blocks of transaction history. + Zpracováno %1 bloků transakční historie. + + + + %n hour(s) + hodinu%n hodiny%n hodin + + + + %n day(s) + den%n dny%n dnů + + + + %n week(s) + týden%n týdny%n týdnů + + + + %1 behind + Stahuji ještě bloky transakcí za poslední %1 + + + + Last received block was generated %1 ago. + Poslední stažený blok byl vygenerován %1 zpátky. + + + + Transactions after this will not yet be visible. + Následné transakce ještě nebudou vidět. + + + + Error + Chyba + + + + Warning + Upozornění + + + + Information + Informace + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + Tahle transakce přesahuje velikostní limit. I tak ji ale můžeš poslat, pokud za ni zaplatíš poplatek %1, který půjde uzlům, které tvou transakci zpracují, a navíc tak podpoříš síť. Chceš zaplatit poplatek? + + + + Up to date + Aktuální + + + + Catching up... + Stahuji... + + + + Confirm transaction fee + Potvrď transakční poplatek + + + + Sent transaction + Odeslané transakce + + + + Incoming transaction + Příchozí transakce + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Datum: %1 +Částka: %2 +Typ: %3 +Adresa: %4 + + + + + + URI handling + Zpracování URI + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + Nepodařilo se analyzovat URI! Důvodem může být neplatná CryptoLottoTokenová adresa nebo poškozené parametry URI. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Peněženka je <b>zašifrovaná</b> a momentálně <b>odemčená</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Peněženka je <b>zašifrovaná</b> a momentálně <b>zamčená</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + Stala se fatální chyba. CryptoLottoToken nemůže bezpečně pokračovat v činnosti, a proto skončí. + + + + ClientModel + + + Network Alert + Upozornění sítě + + + + EditAddressDialog + + + Edit Address + Uprav adresu + + + + &Label + &Označení + + + + The label associated with this address book entry + Označení spojené s tímto záznamem v adresáři + + + + &Address + &Adresa + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Adresa spojená s tímto záznamem v adresáři. Lze upravovat jen pro odesílací adresy. + + + + New receiving address + Nová přijímací adresa + + + + New sending address + Nová odesílací adresa + + + + Edit receiving address + Uprav přijímací adresu + + + + Edit sending address + Uprav odesílací adresu + + + + The entered address "%1" is already in the address book. + Zadaná adresa "%1" už v adresáři je. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + Zadaná adresa "%1" není platná CryptoLottoTokenová adresa. + + + + Could not unlock wallet. + Nemohu odemknout peněženku. + + + + New key generation failed. + Nepodařilo se mi vygenerovat nový klíč. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + verze + + + + Usage: + Užití: + + + + command-line options + možnosti příkazové řádky + + + + UI options + Možnosti UI + + + + Set language, for example "de_DE" (default: system locale) + Nastavit jazyk, například "de_DE" (výchozí: systémové nastavení) + + + + Start minimized + Nastartovat minimalizovaně + + + + Show splash screen on startup (default: 1) + Zobrazit startovací obrazovku (výchozí: 1) + + + + OptionsDialog + + + Options + Možnosti + + + + &Main + &Hlavní + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + Dobrovolný transakční poplatek za každý započatý kB dopomáhá k rychlému zpracování tvých transakcí. Většina transakcí má do 1 kB. + + + + Pay transaction &fee + Platit &transakční poplatek + + + + Automatically start CryptoLottoToken after logging in to the system. + Automaticky spustí CryptoLottoToken po přihlášení do systému. + + + + &Start CryptoLottoToken on system login + S&pustit CryptoLottoToken po přihlášení do systému + + + + Reset all client options to default. + Vrátí všechny volby na výchozí hodnoty. + + + + &Reset Options + &Obnovit nastavení + + + + &Network + &Síť + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Automaticky otevře potřebný port na routeru. Tohle funguje jen za předpokladu, že tvůj router podporuje UPnP a že je UPnP povolené. + + + + Map port using &UPnP + Namapovat port přes &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Připojí se do CryptoLottoTokenové sítě přes SOCKS proxy (např. když se připojuje přes Tor). + + + + &Connect through SOCKS proxy: + &Připojit přes SOCKS proxy: + + + + Proxy &IP: + &IP adresa proxy: + + + + IP address of the proxy (e.g. 127.0.0.1) + IP adresa proxy (např. 127.0.0.1) + + + + &Port: + Por&t: + + + + Port of the proxy (e.g. 9050) + Port proxy (např. 9050) + + + + SOCKS &Version: + &Verze SOCKS: + + + + SOCKS version of the proxy (e.g. 5) + Verze SOCKS proxy (např. 5) + + + + &Window + O&kno + + + + Show only a tray icon after minimizing the window. + Po minimalizaci okna zobrazí pouze ikonu v panelu. + + + + &Minimize to the tray instead of the taskbar + &Minimalizovávat do ikony v panelu + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Zavřením se aplikace minimalizuje. Pokud je tato volba zaškrtnuta, tak se aplikace ukončí pouze zvolením Konec v menu. + + + + M&inimize on close + Za&vřením minimalizovat + + + + &Display + Zobr&azení + + + + User Interface &language: + &Jazyk uživatelského rozhraní: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + Tady lze nastavit jazyk uživatelského rozhraní. Nastavení se projeví až po restartování CryptoLottoTokenu. + + + + &Unit to show amounts in: + J&ednotka pro částky: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Zvol výchozí podjednotku, která se bude zobrazovat v programu a při posílání mincí. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Zda ukazovat CryptoLottoTokenové adresy ve výpisu transakcí nebo ne. + + + + &Display addresses in transaction list + Ukazo&vat adresy ve výpisu transakcí + + + + &OK + &Budiž + + + + &Cancel + &Zrušit + + + + &Apply + &Uložit + + + + default + výchozí + + + + Confirm options reset + Potvrzení obnovení nastavení + + + + Some settings may require a client restart to take effect. + Některá nastavení mohou vyžadovat restart klienta, aby se mohly projevit. + + + + Do you want to proceed? + Chceš pokračovat? + + + + + Warning + Upozornění + + + + + This setting will take effect after restarting CryptoLottoToken. + Nastavení se projeví až po restartování CryptoLottoTokenu. + + + + The supplied proxy address is invalid. + Zadaná adresa proxy je neplatná. + + + + OverviewPage + + + Form + Formulář + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + Zobrazené informace nemusí být aktuální. Tvá peněženka se automaticky sesynchronizuje s CryptoLottoTokenovou sítí, jakmile se s ní spojí. Zatím ale ještě není synchronizace dokončena. + + + + Balance: + Stav účtu: + + + + Unconfirmed: + Nepotvrzeno: + + + + Wallet + Peněženka + + + + Immature: + Nedozráno: + + + + Mined balance that has not yet matured + Vytěžené mince, které ještě nejsou zralé + + + + <b>Recent transactions</b> + <b>Poslední transakce</b> + + + + Your current balance + Aktuální stav tvého účtu + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Celkem z transakcí, které ještě nejsou potvrzené a které se ještě nezapočítávají do celkového stavu účtu + + + + + out of sync + nesynchronizováno + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + Nemůžu spustit CryptoLottoToken: obsluha click-to-pay + + + + QRCodeDialog + + + QR Code Dialog + QR kód + + + + Request Payment + Požadovat platbu + + + + Amount: + Částka: + + + + Label: + Označení: + + + + Message: + Zpráva: + + + + &Save As... + &Ulož jako... + + + + Error encoding URI into QR Code. + Chyba při kódování URI do QR kódu. + + + + The entered amount is invalid, please check. + Zadaná částka je neplatná, překontroluj ji prosím. + + + + Resulting URI too long, try to reduce the text for label / message. + Výsledná URI je příliš dlouhá, zkus zkrátit text označení / zprávy. + + + + Save QR Code + Ulož QR kód + + + + PNG Images (*.png) + PNG obrázky (*.png) + + + + RPCConsole + + + Client name + Název klienta + + + + + + + + + + + + + N/A + N/A + + + + Client version + Verze klienta + + + + &Information + &Informace + + + + Using OpenSSL version + Používaná verze OpenSSL + + + + Startup time + Čas spuštění + + + + Network + Síť + + + + Number of connections + Počet spojení + + + + On testnet + V testnetu + + + + Block chain + Řetězec bloků + + + + Current number of blocks + Aktuální počet bloků + + + + Estimated total blocks + Odhad celkového počtu bloků + + + + Last block time + Čas posledního bloku + + + + &Open + &Otevřít + + + + Command-line options + Argumenty z příkazové řádky + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Seznam parametrů CryptoLottoTokenu pro příkazovou řádku získáš v nápovědě CryptoLottoTokenu Qt. + + + + &Show + &Zobrazit + + + + &Console + &Konzole + + + + Build date + Datum kompilace + + + + CryptoLottoToken - Debug window + CryptoLottoToken - ladicí okno + + + + CryptoLottoToken Core + Jádro CryptoLottoTokenu + + + + Debug log file + Soubor s ladicími záznamy + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + Otevři soubor s ladicími záznamy CryptoLottoTokenu z aktuálního datového adresáře. U velkých logů to může pár vteřin zabrat. + + + + Clear console + Vyčistit konzoli + + + + Welcome to the CryptoLottoToken RPC console. + Vítej v CryptoLottoTokenové RPC konzoli. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + V historii se pohybuješ šipkami nahoru a dolů a pomocí <b>Ctrl-L</b> čistíš obrazovku. + + + + Type <b>help</b> for an overview of available commands. + Napsáním <b>help</b> si vypíšeš přehled dostupných příkazů. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Pošli mince + + + + Send to multiple recipients at once + Pošli více příjemcům naráz + + + + Add &Recipient + Při&dej příjemce + + + + Remove all transaction fields + Smaž všechny transakční formuláře + + + + Clear &All + Všechno s&maž + + + + Balance: + Stav účtu: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Potvrď odeslání + + + + S&end + P&ošli + + + + <b>%1</b> to %2 (%3) + <b>%1</b> pro %2 (%3) + + + + Confirm send coins + Potvrď odeslání mincí + + + + Are you sure you want to send %1? + Jsi si jistý, že chceš poslat %1? + + + + and + a + + + + The recipient address is not valid, please recheck. + Adresa příjemce je neplatná, překontroluj ji prosím. + + + + The amount to pay must be larger than 0. + Odesílaná částka musí být větší než 0. + + + + The amount exceeds your balance. + Částka překračuje stav účtu. + + + + The total exceeds your balance when the %1 transaction fee is included. + Celková částka při připočítání poplatku %1 překročí stav účtu. + + + + Duplicate address found, can only send to each address once per send operation. + Zaznamenána duplikovaná adresa; každá adresa může být v odesílané platbě pouze jednou. + + + + Error: Transaction creation failed! + Chyba: Vytvoření transakce selhalo! + + + + 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. + Chyba: Transakce byla odmítnuta. Tohle může nastat, pokud nějaké mince z tvé peněženky už jednou byly utraceny, například pokud používáš kopii souboru wallet.dat a mince byly utraceny v druhé kopii, ale nebyly označeny jako utracené v této. + + + + SendCoinsEntry + + + Form + Formulář + + + + A&mount: + Čás&tka: + + + + Pay &To: + &Komu: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Adresa příjemce (např. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + Zadej označení této adresy; obojí se ti pak uloží do adresáře + + + + &Label: + O&značení: + + + + Choose address from address book + Vyber adresu z adresáře + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Vlož adresu ze schránky + + + + Alt+P + Alt+P + + + + Remove this recipient + Smaž tohoto příjemce + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Zadej CryptoLottoTokenovou adresu (např. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Podpisy - podepsat/ověřit zprávu + + + + &Sign Message + &Podepiš zprávu + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Podepsáním zprávy svými adresami můžeš prokázat, že je skutečně vlastníš. Buď opatrný a nepodepisuj nic vágního; například při phishingových útocích můžeš být lákán, abys něco takového podepsal. Podepisuj pouze zcela úplná a detailní prohlášení, se kterými souhlasíš. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Adresa, kterou se zpráva podepíše (např. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Vyber adresu z adresáře + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Vlož adresu ze schránky + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Sem vepiš zprávu, kterou chceš podepsat + + + + Signature + Podpis + + + + Copy the current signature to the system clipboard + Zkopíruj aktuálně vybraný podpis do systémové schránky + + + + Sign the message to prove you own this CryptoLottoToken address + Podepiš zprávu, čímž prokážeš, že jsi vlastníkem této CryptoLottoTokenové adresy + + + + Sign &Message + Po&depiš zprávu + + + + Reset all sign message fields + Vymaž všechna pole formuláře pro podepsání zrávy + + + + + Clear &All + Všechno &smaž + + + + &Verify Message + &Ověř zprávu + + + + Enter the signing 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. + K ověření podpisu zprávy zadej podepisující adresu, zprávu (ověř si, že správně kopíruješ zalomení řádků, mezery, tabulátory apod.) a podpis. Dávej pozor na to, abys nezkopíroval do podpisu víc, než co je v samotné podepsané zprávě, abys nebyl napálen man-in-the-middle útokem. + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Adresa, kterou je zpráva podepsána (např. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Ověř zprávu, aby ses ujistil, že byla podepsána danou CryptoLottoTokenovou adresou + + + + Verify &Message + O&věř zprávu + + + + Reset all verify message fields + Vymaž všechna pole formuláře pro ověření zrávy + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Zadej CryptoLottoTokenovou adresu (např. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Kliknutím na "Podepiš zprávu" vygeneruješ podpis + + + + Enter CryptoLottoToken signature + Vlož CryptoLottoTokenový podpis + + + + + The entered address is invalid. + Zadaná adresa je neplatná. + + + + + + + Please check the address and try again. + Zkontroluj ji prosím a zkus to pak znovu. + + + + + The entered address does not refer to a key. + Zadaná adresa nepasuje ke klíči. + + + + Wallet unlock was cancelled. + Odemčení peněženky bylo zrušeno. + + + + Private key for the entered address is not available. + Soukromý klíč pro zadanou adresu není dostupný. + + + + Message signing failed. + Podepisování zprávy selhalo. + + + + Message signed. + Zpráv podepsána. + + + + The signature could not be decoded. + Podpis nejde dekódovat. + + + + + Please check the signature and try again. + Zkontroluj ho prosím a zkus to pak znovu. + + + + The signature did not match the message digest. + Podpis se neshoduje s hašem zprávy. + + + + Message verification failed. + Ověřování zprávy selhalo. + + + + Message verified. + Zpráva ověřena. + + + + SplashScreen + + + The CryptoLottoToken developers + Vývojáři CryptoLottoTokenu + + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + Otřevřeno dokud %1 + + + + %1/offline + %1/offline + + + + %1/unconfirmed + %1/nepotvrzeno + + + + %1 confirmations + %1 potvrzení + + + + Status + Stav + + + + , broadcast through %n node(s) + , rozesláno přes 1 uzel, rozesláno přes %n uzly, rozesláno přes %n uzlů + + + + Date + Datum + + + + Source + Zdroj + + + + Generated + Vygenerováno + + + + + From + Od + + + + + + To + Pro + + + + + own address + vlastní adresa + + + + label + označení + + + + + + + + Credit + Příjem + + + + matures in %n more block(s) + dozraje po jednom blokudozraje po %n blocíchdozraje po %n blocích + + + + not accepted + neakceptováno + + + + + + + Debit + Výdaj + + + + Transaction fee + Transakční poplatek + + + + Net amount + Čistá částka + + + + Message + Zpráva + + + + Comment + Komentář + + + + Transaction ID + ID transakce + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Vygenerované mince musí čekat 120 bloků, než mohou být utraceny. Když jsi vygeneroval tenhle blok, tak byl rozposlán do sítě, aby byl přidán do řetězce bloků. Pokud se mu nepodaří dostat se do řetězce, změní se na "neakceptovaný" a nepůjde utratit. To se občas může stát, pokud jiný uzel vygeneruje blok zhruba ve stejném okamžiku jako ty. + + + + Debug information + Ladicí informace + + + + Transaction + Transakce + + + + Inputs + Vstupy + + + + Amount + Částka + + + + true + true + + + + false + false + + + + , has not been successfully broadcast yet + , ještě nebylo rozesláno + + + + Open for %n more block(s) + Otevřeno pro 1 další blokOtevřeno pro %n další blokyOtevřeno pro %n dalších bloků + + + + unknown + neznámo + + + + TransactionDescDialog + + + Transaction details + Detaily transakce + + + + This pane shows a detailed description of the transaction + Toto okno zobrazuje detailní popis transakce + + + + TransactionTableModel + + + Date + Datum + + + + Type + Typ + + + + Address + Adresa + + + + Amount + Částka + + + + Open for %n more block(s) + Otevřeno pro 1 další blokOtevřeno pro %n další blokyOtevřeno pro %n dalších bloků + + + + Open until %1 + Otřevřeno dokud %1 + + + + Offline (%1 confirmations) + Offline (%1 potvrzení) + + + + Unconfirmed (%1 of %2 confirmations) + Nepotvrzeno (%1 z %2 potvrzení) + + + + Confirmed (%1 confirmations) + Potvrzeno (%1 potvrzení) + + + + Mined balance will be available when it matures in %n more block(s) + Vytěžené mince budou použitelné po dozrání, tj. po jednom blokuVytěžené mince budou použitelné po dozrání, tj. po %n blocíchVytěžené mince budou použitelné po dozrání, tj. po %n blocích + + + + This block was not received by any other nodes and will probably not be accepted! + Tento blok nedostal žádný jiný uzel a pravděpodobně nebude akceptován! + + + + Generated but not accepted + Vygenerováno, ale neakceptováno + + + + Received with + Přijato do + + + + Received from + Přijato od + + + + Sent to + Posláno na + + + + Payment to yourself + Platba sama sobě + + + + Mined + Vytěženo + + + + (n/a) + (n/a) + + + + Transaction status. Hover over this field to show number of confirmations. + Stav transakce. Najetím myši na toto políčko si zobrazíš počet potvrzení. + + + + Date and time that the transaction was received. + Datum a čas přijetí transakce. + + + + Type of transaction. + Druh transakce. + + + + Destination address of transaction. + Cílová adresa transakce. + + + + Amount removed from or added to balance. + Částka odečtená z nebo přičtená k účtu. + + + + TransactionView + + + + All + Vše + + + + Today + Dnes + + + + This week + Tento týden + + + + This month + Tento měsíc + + + + Last month + Minulý měsíc + + + + This year + Letos + + + + Range... + Rozsah... + + + + Received with + Přijato + + + + Sent to + Posláno + + + + To yourself + Sám sobě + + + + Mined + Vytěženo + + + + Other + Ostatní + + + + Enter address or label to search + Zadej adresu nebo označení pro její vyhledání + + + + Min amount + Minimální částka + + + + Copy address + Kopíruj adresu + + + + Copy label + Kopíruj její označení + + + + Copy amount + Kopíruj částku + + + + Copy transaction ID + Kopíruj ID transakce + + + + Edit label + Uprav označení + + + + Show transaction details + Zobraz detaily transakce + + + + Export Transaction Data + Exportuj transakční data + + + + Comma separated file (*.csv) + CSV formát (*.csv) + + + + Confirmed + Potvrzeno + + + + Date + Datum + + + + Type + Typ + + + + Label + Označení + + + + Address + Adresa + + + + Amount + Částka + + + + ID + ID + + + + Error exporting + Chyba při exportu + + + + Could not write to file %1. + Nemohu zapisovat do souboru %1. + + + + Range: + Rozsah: + + + + to + + + + + WalletModel + + + Send Coins + Pošli mince + + + + WalletView + + + &Export + &Export + + + + Export the data in the current tab to a file + Exportuj data z tohoto panelu do souboru + + + + Backup Wallet + Záloha peněženky + + + + Wallet Data (*.dat) + Data peněženky (*.dat) + + + + Backup Failed + Zálohování selhalo + + + + There was an error trying to save the wallet data to the new location. + Při ukládání peněženky na nové místo se přihodila nějaká chyba. + + + + Backup Successful + Úspěšně zazálohováno + + + + The wallet data was successfully saved to the new location. + Data z peněženky byla v pořádku uložena na nové místo. + + + + bitcoin-core + + + CryptoLottoToken version + Verze CryptoLottoTokenu + + + + Usage: + Užití: + + + + Send command to -server or CryptoLottoTokend + Poslat příkaz pro -server nebo CryptoLottoTokend + + + + List commands + Výpis příkazů + + + + Get help for a command + Získat nápovědu pro příkaz + + + + Options: + Možnosti: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Konfigurační soubor (výchozí: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + PID soubor (výchozí: CryptoLottoTokend.pid) + + + + Specify data directory + Adresář pro data + + + + Set database cache size in megabytes (default: 25) + Nastavit velikost databázové vyrovnávací paměti v megabajtech (výchozí: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Čekat na spojení na <portu> (výchozí: 22556 nebo testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Povolit nejvýše <n> připojení k uzlům (výchozí: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Připojit se k uzlu, získat adresy jeho protějšků a odpojit se + + + + Specify your own public address + Specifikuj svou veřejnou adresu + + + + Threshold for disconnecting misbehaving peers (default: 100) + Práh pro odpojování zlobivých uzlů (výchozí: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Doba ve vteřinách, po kterou se nebudou moci zlobivé uzly znovu připojit (výchozí: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + Při nastavování naslouchacího RPC portu %i pro IPv4 nastala chyba: %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Čekat na JSON RPC spojení na <portu> (výchozí: 22555 nebo testnet: 44555) + + + + Accept command line and JSON-RPC commands + Akceptovat příkazy z příkazové řádky a přes JSON-RPC + + + + Run in the background as a daemon and accept commands + Běžet na pozadí jako démon a akceptovat příkazy + + + + Use the test network + Použít testovací síť (testnet) + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Přijímat spojení zvenčí (výchozí: 1, pokud není zadáno -proxy nebo -connect) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + %s, musíš nastavit rpcpassword v konfiguračním souboru: +%s +Je vhodné použít následující náhodné heslo: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(není potřeba si ho pamatovat) +rpcuser a rpcpassword NESMÍ být stejné. +Pokud konfigurační soubor ještě neexistuje, vytvoř ho tak, aby ho mohl číst pouze vlastník. +Je také doporučeno si nastavit alertnotify, abys byl upozorněn na případné problémy; +například: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + Při nastavování naslouchacího RPC portu %u pro IPv6 nastala chyba, vracím se k IPv4: %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + Poslouchat na zadané adrese. Pro zápis IPv6 adresy použij notaci [adresa]:port + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + Nedaří se mi získat zámek na datový adresář %s. CryptoLottoToken pravděpodobně už jednou běží. + + + + 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. + Chyba: Transakce byla odmítnuta! Tohle může nastat, pokud nějaké mince z tvé peněženky už jednou byly utraceny, například pokud používáš kopii souboru wallet.dat a mince byly utraceny v druhé kopii, ale nebyly označeny jako utracené v této. + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + Chyba: Tahle transakce vyžaduje transakční poplatek nejméně %s kvůli velikosti zasílané částky, komplexnosti nebo použití nedávno přijatých mincí! + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + Spustit příkaz po přijetí relevantního hlášení (%s se v příkazu nahradí za zprávu) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Spustit příkaz, když se objeví transakce týkající se peněženky (%s se v příkazu nahradí za TxID) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Nastavit maximální velikost prioritních/nízkopoplatkových transakcí v bajtech (výchozí: 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + Tohle je testovací verze – používej ji jen na vlastní riziko, ale rozhodně ji nepoužívej k těžbě nebo pro obchodní aplikace + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Upozornění: -paytxfee je nastaveno velmi vysoko! Toto je transakční poplatek, který zaplatíš za každou poslanou transakci. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + Upozornění: Zobrazené transakce nemusí být správné! Možná potřebuješ aktualizovat nebo ostatní uzly potřebují aktualizovat. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Upozornění: Zkontroluj, že máš v počítači správně nastavený datum a čas! Pokud jsou nastaveny špatně, CryptoLottoToken nebude fungovat správně. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Upozornění: nastala chyba při čtení souboru wallet.dat! Všechny klíče se přečetly správně, ale data o transakcích nebo záznamy v adresáři mohou chybět či být nesprávné. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Upozornění: soubor wallet.dat je poškozený, data jsou však zachráněna! Původní soubor wallet.dat je uložený jako wallet.{timestamp}.bak v %s. Pokud je stav tvého účtu nebo transakce nesprávné, zřejmě bys měl obnovit zálohu. + + + + Attempt to recover private keys from a corrupt wallet.dat + Pokusit se zachránit soukromé klíče z poškozeného souboru wallet.dat + + + + Block creation options: + Možnosti vytvoření bloku: + + + + Connect only to the specified node(s) + Připojit se pouze k zadanému uzlu (příp. zadaným uzlům) + + + + Corrupted block database detected + Bylo zjištěno poškození databáze bloků + + + + Discover own IP address (default: 1 when listening and no -externalip) + Zjistit vlastní IP adresu (výchozí: 1, pokud naslouchá a není zadáno -externalip) + + + + Do you want to rebuild the block database now? + Chceš přestavět databázi bloků hned teď? + + + + Error initializing block database + Chyba při zakládání databáze bloků + + + + Error initializing wallet database environment %s! + Chyba při vytváření databázového prostředí %s pro peněženku! + + + + Error loading block database + Chyba při načítání databáze bloků + + + + Error opening block database + Chyba při otevírání databáze bloků + + + + Error: Disk space is low! + Problém: Na disku je málo místa! + + + + Error: Wallet locked, unable to create transaction! + Chyba: Peněženka je zamčená, nemohu vytvořit transakci! + + + + Error: system error: + Chyba: systémová chyba: + + + + Failed to listen on any port. Use -listen=0 if you want this. + Nepodařilo se naslouchat na žádném portu. Použij -listen=0, pokud to byl tvůj záměr. + + + + Failed to read block info + Nepodařilo se přečíst informace o bloku + + + + Failed to read block + Nepodařilo se přečíst blok + + + + Failed to sync block index + Nepodařilo se sesynchronizovat index bloků + + + + Failed to write block index + Nepodařilo se zapsat index bloků + + + + Failed to write block info + Nepodařilo se zapsat informace o bloku + + + + Failed to write block + Nepodařilo se zapsat blok + + + + Failed to write file info + Nepodařilo se zapsat informace o souboru + + + + Failed to write to coin database + Selhal zápis do databáze mincí + + + + Failed to write transaction index + Nepodařilo se zapsat index transakcí + + + + Failed to write undo data + Nepodařilo se zapsat data o vracení změn + + + + Find peers using DNS lookup (default: 1 unless -connect) + Hledat uzly přes DNS (výchozí: 1, pokud není zadáno -connect) + + + + Generate coins (default: 0) + Generovat mince (výchozí: 0) + + + + How many blocks to check at startup (default: 288, 0 = all) + Kolik bloků při startu zkontrolovat (výchozí: 288, 0 = všechny) + + + + How thorough the block verification is (0-4, default: 3) + Jak moc důkladná má být verifikace bloků (0-4, výchozí: 3) + + + + Not enough file descriptors available. + Je nedostatek deskriptorů souborů. + + + + Rebuild block chain index from current blk000??.dat files + Znovu vytvořit index řetězce bloků z aktuálních blk000??.dat souborů + + + + Set the number of threads to service RPC calls (default: 4) + Nastavení počtu vláken pro servisní RPC volání (výchozí: 4) + + + + Verifying blocks... + Ověřuji bloky... + + + + Verifying wallet... + Kontroluji peněženku... + + + + Imports blocks from external blk000??.dat file + Importovat bloky z externího souboru blk000??.dat + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + Nastavení počtu vláken pro verifikaci skriptů (max. 16, 0 = automaticky, <0 = nechat daný počet jader volný, výchozí: 0) + + + + Information + Informace + + + + Invalid -tor address: '%s' + Neplatná -tor adresa: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + Neplatná částka pro -minrelaytxfee=<částka>: '%s' + + + + Invalid amount for -mintxfee=<amount>: '%s' + Neplatná částka pro -mintxfee=<částka>: '%s' + + + + Maintain a full transaction index (default: 0) + Spravovat úplný index transakcí (výchozí: 0) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Maximální velikost přijímacího bufferu pro každé spojení, <n>*1000 bajtů (výchozí: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Maximální velikost odesílacího bufferu pro každé spojení, <n>*1000 bajtů (výchozí: 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + Uznávat pouze řetěz bloků, který odpovídá vnitřním kontrolním bodům (výchozí: 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Připojit se pouze k uzlům v <net> síti (IPv4, IPv6 nebo Tor) + + + + Output extra debugging information. Implies all other -debug* options + Tisknout speciální ladicí informace. Implikuje použití všech -debug* voleb + + + + Output extra network debugging information + Tisknout speciální ladicí informace o síti + + + + Prepend debug output with timestamp (default: 1) + Připojit před ladicí výstup časové razítko + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + Možnosti SSL: (viz instrukce nastavení SSL v CryptoLottoToken Wiki) + + + + Select the version of socks proxy to use (4-5, default: 5) + Zvol verzi socks proxy (4-5, výchozí: 5) + + + + Send trace/debug info to console instead of debug.log file + Posílat stopovací/ladicí informace do konzole místo do souboru debug.log + + + + Send trace/debug info to debugger + Posílat stopovací/ladicí informace do debuggeru + + + + Set maximum block size in bytes (default: 250000) + Nastavit maximální velikost bloku v bajtech (výchozí: 250000) + + + + Set minimum block size in bytes (default: 0) + Nastavit minimální velikost bloku v bajtech (výchozí: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Při spuštění klienta zmenšit soubor debug.log (výchozí: 1, pokud není zadáno -debug) + + + + Signing transaction failed + Podepisování transakce selhalo + + + + Specify connection timeout in milliseconds (default: 5000) + Zadej časový limit spojení v milisekundách (výchozí: 5000) + + + + System error: + Systémová chyba: + + + + Transaction amount too small + Částka v transakci je příliš malá + + + + Transaction amounts must be positive + Částky v transakci musí být kladné + + + + Transaction too large + Transace je příliš velká + + + + Use UPnP to map the listening port (default: 0) + Použít UPnP k namapování naslouchacího portu (výchozí: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Použít UPnP k namapování naslouchacího portu (výchozí: 1, pokud naslouchá) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Použít proxy k připojení ke skryté služby (výchozí: stejné jako -proxy) + + + + Username for JSON-RPC connections + Uživatelské jméno pro JSON-RPC spojení + + + + Warning + Upozornění + + + + Warning: This version is obsolete, upgrade required! + Upozornění: tahle verze je zastaralá, měl bys ji aktualizovat! + + + + You need to rebuild the databases using -reindex to change -txindex + Je třeba přestavět databázi použitím -reindex, aby bylo možné změnit -txindex + + + + wallet.dat corrupt, salvage failed + Soubor wallet.dat je poškozen, jeho záchrana se nezdařila + + + + Password for JSON-RPC connections + Heslo pro JSON-RPC spojení + + + + Allow JSON-RPC connections from specified IP address + Povolit JSON-RPC spojení ze specifikované IP adresy + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Posílat příkazy uzlu běžícím na <ip> (výchozí: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Spustit příkaz, když se změní nejlepší blok (%s se v příkazu nahradí hashem bloku) + + + + Upgrade wallet to latest format + Převést peněženku na nejnovější formát + + + + Set key pool size to <n> (default: 100) + Nastavit zásobník klíčů na velikost <n> (výchozí: 100) + + + + Rescan the block chain for missing wallet transactions + Přeskenovat řetězec bloků na chybějící transakce tvé pěněženky + + + + Use OpenSSL (https) for JSON-RPC connections + Použít OpenSSL (https) pro JSON-RPC spojení + + + + Server certificate file (default: server.cert) + Soubor se serverovým certifikátem (výchozí: server.cert) + + + + Server private key (default: server.pem) + Soubor se serverovým soukromým klíčem (výchozí: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Akceptovatelné šifry (výchozí: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Tato nápověda + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Nedaří se mi připojit na %s na tomhle počítači (operace bind vrátila chybu %d, %s) + + + + Connect through socks proxy + Připojit se přes socks proxy + + + + Allow DNS lookups for -addnode, -seednode and -connect + Povolit DNS dotazy pro -addnode (přidání uzlu), -seednode a -connect (připojení) + + + + Loading addresses... + Načítám adresy... + + + + Error loading wallet.dat: Wallet corrupted + Chyba při načítání wallet.dat: peněženka je poškozená + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Chyba při načítání wallet.dat: peněženka vyžaduje novější verzi CryptoLottoTokenu + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Soubor s peněženkou potřeboval přepsat: restartuj CryptoLottoToken, aby se operace dokončila + + + + Error loading wallet.dat + Chyba při načítání wallet.dat + + + + Invalid -proxy address: '%s' + Neplatná -proxy adresa: '%s' + + + + Unknown network specified in -onlynet: '%s' + V -onlynet byla uvedena neznámá síť: '%s' + + + + Unknown -socks proxy version requested: %i + V -socks byla požadována neznámá verze proxy: %i + + + + Cannot resolve -bind address: '%s' + Nemohu přeložit -bind adresu: '%s' + + + + Cannot resolve -externalip address: '%s' + Nemohu přeložit -externalip adresu: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + Neplatná částka pro -paytxfee=<částka>: '%s' + + + + Invalid amount + Neplatná částka + + + + Insufficient funds + Nedostatek prostředků + + + + Loading block index... + Načítám index bloků... + + + + Add a node to connect to and attempt to keep the connection open + Přidat uzel, ke kterému se připojit a snažit se spojení udržet + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Nedaří se mi připojit na %s na tomhle počítači. CryptoLottoToken už pravděpodobně jednou běží. + + + + Fee per KB to add to transactions you send + Poplatek za kB, který se přidá ke každé odeslané transakci + + + + Loading wallet... + Načítám peněženku... + + + + Cannot downgrade wallet + Nemohu převést peněženku do staršího formátu + + + + Cannot write default address + Nemohu napsat výchozí adresu + + + + Rescanning... + Přeskenovávám... + + + + Done loading + Načítání dokončeno + + + + To use the %s option + K použití volby %s + + + + Error + Chyba + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + Musíš nastavit rpcpassword=<heslo> v konfiguračním souboru: +%s +Pokud konfigurační soubor ještě neexistuje, vytvoř ho tak, aby ho mohl číst pouze vlastník. + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_cy.ts b/src/qt/locale/bitcoin_cy.ts new file mode 100644 index 0000000..9433e64 --- /dev/null +++ b/src/qt/locale/bitcoin_cy.ts @@ -0,0 +1,2917 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + + + + + <b>CryptoLottoToken</b> version + Fersiwn <b>CryptoLottoToken</b> + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + Llyfr Cyfeiriadau + + + + Double-click to edit address or label + Clicio dwywaith i olygu cyfeiriad neu label + + + + Create a new address + Creu cyfeiriad newydd + + + + Copy the currently selected address to the system clipboard + Copio'r cyfeiriad sydd wedi'i ddewis i'r clipfwrdd system + + + + &New Address + + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + + + + + &Copy Address + + + + + Show &QR Code + + + + + Sign a message to prove you own a CryptoLottoToken address + + + + + Sign &Message + + + + + Delete the currently selected address from the list + + + + + Export the data in the current tab to a file + + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + + + + + &Verify Message + + + + + &Delete + &Dileu + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + + + + + &Edit + + + + + Send &Coins + + + + + Export Address Book Data + Allforio Data Llyfr Cyfeiriad + + + + Comma separated file (*.csv) + + + + + Error exporting + Gwall allforio + + + + Could not write to file %1. + Ni ellir ysgrifennu i ffeil %1. + + + + AddressTableModel + + + Label + Label + + + + Address + Cyfeiriad + + + + (no label) + (heb label) + + + + AskPassphraseDialog + + + Passphrase Dialog + + + + + Enter passphrase + Teipiwch gyfrinymadrodd + + + + New passphrase + Cyfrinymadrodd newydd + + + + Repeat new passphrase + Ailadroddwch gyfrinymadrodd newydd + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Dewiswch gyfrinymadrodd newydd ar gyfer y waled. <br/> Defnyddiwch cyfrinymadrodd o <b>10 neu fwy o lythyrennau hapgyrch</b>, neu <b> wyth neu fwy o eiriau. + + + + Encrypt wallet + Amgryptio'r waled + + + + This operation needs your wallet passphrase to unlock the wallet. + Mae angen i'r gweithred hon ddefnyddio'ch cyfrinymadrodd er mwyn datgloi'r waled. + + + + Unlock wallet + Datgloi'r waled + + + + This operation needs your wallet passphrase to decrypt the wallet. + Mae angen i'r gweithred hon ddefnyddio'ch cyfrinymadrodd er mwyn dadgryptio'r waled. + + + + Decrypt wallet + Dadgryptio'r waled + + + + Change passphrase + Newid cyfrinymadrodd + + + + Enter the old and new passphrase to the wallet. + Teipiwch yr hen cyfrinymadrodd a chyfrinymadrodd newydd i mewn i'r waled. + + + + Confirm wallet encryption + Cadarnau amgryptiad y waled + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + + + + + Are you sure you wish to encrypt your wallet? + + + + + 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. + + + + + + Warning: The Caps Lock key is on! + + + + + + Wallet encrypted + Waled wedi'i amgryptio + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + + + + + + + + Wallet encryption failed + Amgryptiad waled wedi methu + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Methodd amgryptiad y waled oherwydd gwall mewnol. Ni amgryptwyd eich waled. + + + + + The supplied passphrases do not match. + Dydy'r cyfrinymadroddion a ddarparwyd ddim yn cyd-fynd â'u gilydd. + + + + Wallet unlock failed + Methodd ddatgloi'r waled + + + + + + The passphrase entered for the wallet decryption was incorrect. + + + + + Wallet decryption failed + Methodd dadgryptiad y waled + + + + Wallet passphrase was successfully changed. + + + + + BitcoinGUI + + + Sign &message... + + + + + Synchronizing with network... + Cysoni â'r rhwydwaith... + + + + &Overview + &Trosolwg + + + + Show general overview of wallet + Dangos trosolwg cyffredinol y waled + + + + &Transactions + &Trafodion + + + + Browse transaction history + Pori hanes trafodion + + + + Edit the list of stored addresses and labels for sending + Golygu'r rhestr o cyfeiriadau a labeli ar gadw + + + + Show the list of addresses for receiving payments + Dangos rhestr o gyfeiriadau ar gyfer derbyn taliadau + + + + E&xit + + + + + Quit application + Gadael rhaglen + + + + Show information about CryptoLottoToken + Dangos gwybodaeth am CryptoLottoToken + + + + About &Qt + + + + + Show information about Qt + + + + + &Options... + &Opsiynau + + + + &Encrypt Wallet... + + + + + &Backup Wallet... + + + + + &Change Passphrase... + + + + + Importing blocks from disk... + + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + + + + + Modify configuration options for CryptoLottoToken + + + + + Backup wallet to another location + + + + + Change the passphrase used for wallet encryption + Newid y cyfrinymadrodd a ddefnyddiwyd ar gyfer amgryptio'r waled + + + + &Debug window + + + + + Open debugging and diagnostic console + + + + + &Verify message... + + + + + + CryptoLottoToken + + + + + Wallet + + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + + + + + &Show / Hide + + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + &Ffeil + + + + &Settings + &Gosodiadau + + + + &Help + &Cymorth + + + + Tabs toolbar + Bar offer tabiau + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + + + + + %n active connection(s) to CryptoLottoToken network + + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + + + + + Warning + + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + Cyfamserol + + + + Catching up... + Dal i fyny + + + + Confirm transaction fee + + + + + Sent transaction + Trafodiad a anfonwyd + + + + Incoming transaction + Trafodiad sy'n cyrraedd + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + + + + + + URI handling + + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Mae'r waled <b>wedi'i amgryptio</b> ac <b>heb ei gloi</b> ar hyn o bryd + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Mae'r waled <b>wedi'i amgryptio</b> ac <b>ar glo</b> ar hyn o bryd + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + + + + + EditAddressDialog + + + Edit Address + Golygu'r cyfeiriad + + + + &Label + &Label + + + + The label associated with this address book entry + Mae'r label hon yn cysylltiedig gyda'r cofnod llyfr cyfeiriad hon + + + + &Address + &Cyfeiriad + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Mae'r cyfeiriad hon yn cysylltiedig gyda'r cofnod llyfr cyfeiriad hon. Gall hyn gael ei olygu dim ond ar gyfer y pwrpas o anfon cyfeiriadau. + + + + New receiving address + Cyfeiriad derbyn newydd + + + + New sending address + Cyfeiriad anfon newydd + + + + Edit receiving address + Golygu'r cyfeiriad derbyn + + + + Edit sending address + Golygu'r cyfeiriad anfon + + + + The entered address "%1" is already in the address book. + Mae'r cyfeiriad "%1" sydd newydd gael ei geisio gennych yn y llyfr cyfeiriad yn barod. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + + + + + Could not unlock wallet. + Methodd ddatgloi'r waled. + + + + New key generation failed. + Methodd gynhyrchu allwedd newydd. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + + + + + version + + + + + Usage: + + + + + command-line options + + + + + UI options + + + + + Set language, for example "de_DE" (default: system locale) + + + + + Start minimized + + + + + Show splash screen on startup (default: 1) + + + + + OptionsDialog + + + Options + Opsiynau + + + + &Main + + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + + + + + Automatically start CryptoLottoToken after logging in to the system. + + + + + &Start CryptoLottoToken on system login + + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + + + + + Map port using &UPnP + + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + + + + + &Connect through SOCKS proxy: + + + + + Proxy &IP: + + + + + IP address of the proxy (e.g. 127.0.0.1) + + + + + &Port: + + + + + Port of the proxy (e.g. 9050) + + + + + SOCKS &Version: + + + + + SOCKS version of the proxy (e.g. 5) + + + + + &Window + + + + + Show only a tray icon after minimizing the window. + + + + + &Minimize to the tray instead of the taskbar + + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + + + + + M&inimize on close + + + + + &Display + + + + + User Interface &language: + + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + + + + + &Unit to show amounts in: + + + + + Choose the default subdivision unit to show in the interface and when sending coins. + + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + + + &Display addresses in transaction list + + + + + &OK + + + + + &Cancel + + + + + &Apply + + + + + default + + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + + + + + + This setting will take effect after restarting CryptoLottoToken. + + + + + The supplied proxy address is invalid. + + + + + OverviewPage + + + Form + Ffurflen + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + + + + + Balance: + Gweddill: + + + + Unconfirmed: + Nas cadarnheir: + + + + Wallet + + + + + Immature: + + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + <b>Trafodion diweddar</b> + + + + Your current balance + Eich gweddill presennol + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Cyfanswm o drafodion sydd heb eu cadarnhau a heb eu cyfri tuag at y gweddill presennol + + + + + out of sync + + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + + + + + Request Payment + + + + + Amount: + + + + + Label: + + + + + Message: + + + + + &Save As... + + + + + Error encoding URI into QR Code. + + + + + The entered amount is invalid, please check. + + + + + Resulting URI too long, try to reduce the text for label / message. + + + + + Save QR Code + + + + + PNG Images (*.png) + + + + + RPCConsole + + + Client name + + + + + + + + + + + + + + N/A + + + + + Client version + + + + + &Information + + + + + Using OpenSSL version + + + + + Startup time + + + + + Network + + + + + Number of connections + + + + + On testnet + + + + + Block chain + + + + + Current number of blocks + + + + + Estimated total blocks + + + + + Last block time + + + + + &Open + + + + + Command-line options + + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + + &Show + + + + + &Console + + + + + Build date + + + + + CryptoLottoToken - Debug window + + + + + CryptoLottoToken Core + + + + + Debug log file + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + + + + + Welcome to the CryptoLottoToken RPC console. + + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + + + + + Type <b>help</b> for an overview of available commands. + + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Anfon arian + + + + Send to multiple recipients at once + Anfon at pobl lluosog ar yr un pryd + + + + Add &Recipient + + + + + Remove all transaction fields + + + + + Clear &All + + + + + Balance: + Gweddill: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Cadarnhau'r gweithrediad anfon + + + + S&end + + + + + <b>%1</b> to %2 (%3) + <b>%1</b> to %2 (%3) + + + + Confirm send coins + + + + + Are you sure you want to send %1? + Ydych chi'n siwr eich bod chi eisiau anfon %1? + + + + and + a + + + + The recipient address is not valid, please recheck. + + + + + The amount to pay must be larger than 0. + + + + + The amount exceeds your balance. + + + + + The total exceeds your balance when the %1 transaction fee is included. + + + + + Duplicate address found, can only send to each address once per send operation. + + + + + Error: Transaction creation failed! + + + + + 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. + + + + + SendCoinsEntry + + + Form + Ffurflen + + + + A&mount: + &Maint + + + + Pay &To: + + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + + + + + &Label: + &Label: + + + + Choose address from address book + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Gludo cyfeiriad o'r glipfwrdd + + + + Alt+P + Alt+P + + + + Remove this recipient + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + + + + + &Sign Message + + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Choose an address from the address book + + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Gludo cyfeiriad o'r glipfwrdd + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + + + + + Signature + + + + + Copy the current signature to the system clipboard + + + + + Sign the message to prove you own this CryptoLottoToken address + + + + + Sign &Message + + + + + Reset all sign message fields + + + + + + Clear &All + + + + + &Verify Message + + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + + + + + Verify &Message + + + + + Reset all verify message fields + + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Click "Sign Message" to generate signature + + + + + Enter CryptoLottoToken signature + + + + + + The entered address is invalid. + + + + + + + + Please check the address and try again. + + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + + + + + Private key for the entered address is not available. + + + + + Message signing failed. + + + + + Message signed. + + + + + The signature could not be decoded. + + + + + + Please check the signature and try again. + + + + + The signature did not match the message digest. + + + + + Message verification failed. + + + + + Message verified. + + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + + + + + TransactionDesc + + + Open until %1 + Agor tan %1 + + + + %1/offline + + + + + %1/unconfirmed + + + + + %1 confirmations + + + + + Status + + + + + , broadcast through %n node(s) + + + + + Date + + + + + Source + + + + + Generated + + + + + + From + + + + + + + To + + + + + + own address + + + + + label + + + + + + + + + Credit + + + + + matures in %n more block(s) + + + + + not accepted + + + + + + + + Debit + + + + + Transaction fee + + + + + Net amount + + + + + Message + + + + + Comment + + + + + Transaction ID + + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + + + + + Debug information + + + + + Transaction + + + + + Inputs + + + + + Amount + + + + + true + + + + + false + + + + + , has not been successfully broadcast yet + + + + + Open for %n more block(s) + + + + + unknown + + + + + TransactionDescDialog + + + Transaction details + + + + + This pane shows a detailed description of the transaction + + + + + TransactionTableModel + + + Date + + + + + Type + + + + + Address + Cyfeiriad + + + + Amount + + + + + Open for %n more block(s) + + + + + Open until %1 + Agor tan %1 + + + + Offline (%1 confirmations) + + + + + Unconfirmed (%1 of %2 confirmations) + + + + + Confirmed (%1 confirmations) + + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + + + + + Generated but not accepted + + + + + Received with + + + + + Received from + + + + + Sent to + + + + + Payment to yourself + + + + + Mined + + + + + (n/a) + + + + + Transaction status. Hover over this field to show number of confirmations. + + + + + Date and time that the transaction was received. + + + + + Type of transaction. + + + + + Destination address of transaction. + + + + + Amount removed from or added to balance. + + + + + TransactionView + + + + All + + + + + Today + + + + + This week + + + + + This month + + + + + Last month + + + + + This year + + + + + Range... + + + + + Received with + + + + + Sent to + + + + + To yourself + + + + + Mined + + + + + Other + + + + + Enter address or label to search + + + + + Min amount + + + + + Copy address + + + + + Copy label + + + + + Copy amount + + + + + Copy transaction ID + + + + + Edit label + + + + + Show transaction details + + + + + Export Transaction Data + + + + + Comma separated file (*.csv) + + + + + Confirmed + + + + + Date + + + + + Type + + + + + Label + Label + + + + Address + Cyfeiriad + + + + Amount + + + + + ID + + + + + Error exporting + Gwall allforio + + + + Could not write to file %1. + Ni ellir ysgrifennu i ffeil %1. + + + + Range: + + + + + to + + + + + WalletModel + + + Send Coins + + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + + + + + Usage: + + + + + Send command to -server or CryptoLottoTokend + + + + + List commands + + + + + Get help for a command + + + + + Options: + + + + + Specify configuration file (default: CryptoLottoToken.conf) + + + + + Specify pid file (default: CryptoLottoTokend.pid) + + + + + Specify data directory + + + + + Set database cache size in megabytes (default: 25) + + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + + + + + Maintain at most <n> connections to peers (default: 125) + + + + + Connect to a node to retrieve peer addresses, and disconnect + + + + + Specify your own public address + + + + + Threshold for disconnecting misbehaving peers (default: 100) + + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + + + + + Accept command line and JSON-RPC commands + + + + + Run in the background as a daemon and accept commands + + + + + Use the test network + + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + + + + + Connect only to the specified node(s) + + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + + + + + Select the version of socks proxy to use (4-5, default: 5) + + + + + Send trace/debug info to console instead of debug.log file + + + + + Send trace/debug info to debugger + + + + + Set maximum block size in bytes (default: 250000) + + + + + Set minimum block size in bytes (default: 0) + + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + + + + + Use UPnP to map the listening port (default: 1 when listening) + + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + + + + + Allow JSON-RPC connections from specified IP address + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + + + + + Upgrade wallet to latest format + + + + + Set key pool size to <n> (default: 100) + + + + + Rescan the block chain for missing wallet transactions + + + + + Use OpenSSL (https) for JSON-RPC connections + + + + + Server certificate file (default: server.cert) + + + + + Server private key (default: server.pem) + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + + This help message + + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + + + + + Connect through socks proxy + + + + + Allow DNS lookups for -addnode, -seednode and -connect + + + + + Loading addresses... + + + + + Error loading wallet.dat: Wallet corrupted + + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + + + + + Error loading wallet.dat + + + + + Invalid -proxy address: '%s' + + + + + Unknown network specified in -onlynet: '%s' + + + + + Unknown -socks proxy version requested: %i + + + + + Cannot resolve -bind address: '%s' + + + + + Cannot resolve -externalip address: '%s' + + + + + Invalid amount for -paytxfee=<amount>: '%s' + + + + + Invalid amount + + + + + Insufficient funds + + + + + Loading block index... + + + + + Add a node to connect to and attempt to keep the connection open + + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + + + + + Fee per KB to add to transactions you send + + + + + Loading wallet... + + + + + Cannot downgrade wallet + + + + + Cannot write default address + + + + + Rescanning... + + + + + Done loading + + + + + To use the %s option + + + + + Error + + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_da.qm b/src/qt/locale/bitcoin_da.qm new file mode 100644 index 0000000..1ba6cd4 Binary files /dev/null and b/src/qt/locale/bitcoin_da.qm differ diff --git a/src/qt/locale/bitcoin_da.ts b/src/qt/locale/bitcoin_da.ts new file mode 100644 index 0000000..d7882ae --- /dev/null +++ b/src/qt/locale/bitcoin_da.ts @@ -0,0 +1,2938 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + Om CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> version + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +Dette program er ekperimentielt. + +Det er gjort tilgængeligt under MIT/X11-softwarelicensen. Se den tilhørende fil "COPYING" eller http://www.opensource.org/licenses/mit-license.php. + +Produktet indeholder software som er udviklet af OpenSSL Project til brug i OpenSSL Toolkit (http://www.openssl.org/), kryptografisk software skrevet af Eric Young (eay@cryptsoft.com) og UPnP-software skrevet af Thomas Bernard. + + + + Copyright + Copyright + + + + The CryptoLottoToken developers + CryptoLottoToken-udviklerne + + + + AddressBookPage + + + Address Book + Adressebog + + + + Double-click to edit address or label + Dobbeltklik for at redigere adresse eller mærkat + + + + Create a new address + Opret en ny adresse + + + + Copy the currently selected address to the system clipboard + Kopier den valgte adresse til systemets udklipsholder + + + + &New Address + Ny adresse + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Dette er dine CryptoLottoToken-adresser til at modtage betalinger med. Du kan give en forskellig adresse til hver afsender, så du kan holde styr på, hvem der betaler dig. + + + + &Copy Address + Kopier adresse + + + + Show &QR Code + Vis QR-kode + + + + Sign a message to prove you own a CryptoLottoToken address + Underskriv en besked for at bevise, at en CryptoLottoToken-adresse tilhører dig + + + + Sign &Message + Underskriv besked + + + + Delete the currently selected address from the list + Slet den markerede adresse fra listen + + + + Export the data in the current tab to a file + Eksportér den aktuelle visning til en fil + + + + &Export + Eksporter + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Efterprøv en besked for at sikre, at den er underskrevet med den angivne CryptoLottoToken-adresse + + + + &Verify Message + Efterprøv besked + + + + &Delete + Slet + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + Disse er dine CryptoLottoToken-adresser for at sende betalinger. Tjek altid beløb og modtageradresse, inden du sender CryptoLottoTokens. + + + + Copy &Label + Kopier mærkat + + + + &Edit + Rediger + + + + Send &Coins + Send CryptoLottoTokens + + + + Export Address Book Data + Eksporter adressebogsdata + + + + Comma separated file (*.csv) + Kommasepareret fil (*.csv) + + + + Error exporting + Fejl under eksport + + + + Could not write to file %1. + Kunne ikke skrive til filen %1. + + + + AddressTableModel + + + Label + Mærkat + + + + Address + Adresse + + + + (no label) + (ingen mærkat) + + + + AskPassphraseDialog + + + Passphrase Dialog + Adgangskodedialog + + + + Enter passphrase + Indtast adgangskode + + + + New passphrase + Ny adgangskode + + + + Repeat new passphrase + Gentag ny adgangskode + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Indtast den nye adgangskode til tegnebogen.<br/>Brug venligst en adgangskode på <b>10 eller flere tilfældige tegn</b> eller <b>otte eller flere ord</b>. + + + + Encrypt wallet + Krypter tegnebog + + + + This operation needs your wallet passphrase to unlock the wallet. + Denne funktion har brug for din tegnebogs adgangskode for at låse tegnebogen op. + + + + Unlock wallet + Lås tegnebog op + + + + This operation needs your wallet passphrase to decrypt the wallet. + Denne funktion har brug for din tegnebogs adgangskode for at dekryptere tegnebogen. + + + + Decrypt wallet + Dekrypter tegnebog + + + + Change passphrase + Skift adgangskode + + + + Enter the old and new passphrase to the wallet. + Indtast den gamle og den nye adgangskode til tegnebogen. + + + + Confirm wallet encryption + Bekræft tegnebogskryptering + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Advarsel: Hvis du krypterer din tegnebog og mister din adgangskode, vil du <b>MISTE ALLE DINE CryptoLottoTokenS</b>! + + + + Are you sure you wish to encrypt your wallet? + Er du sikker på, at du ønsker at kryptere din tegnebog? + + + + 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. + VIGTIGT: Enhver tidligere sikkerhedskopi, som du har lavet af tegnebogsfilen, bør blive erstattet af den nyligt genererede, krypterede tegnebogsfil. Af sikkerhedsmæssige årsager vil tidligere sikkerhedskopier af den ikke-krypterede tegnebogsfil blive ubrugelig i det øjeblik, du starter med at anvende den nye, krypterede tegnebog. + + + + + Warning: The Caps Lock key is on! + Advarsel: Caps Lock-tasten er aktiveret! + + + + + Wallet encrypted + Tegnebog krypteret + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken vil nu lukke for at gennemføre krypteringsprocessen. Husk på, at kryptering af din tegnebog vil ikke beskytte dine CryptoLottoTokens fuldt ud mod at blive stjålet af malware på din computer. + + + + + + + Wallet encryption failed + Tegnebogskryptering mislykkedes + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Tegnebogskryptering mislykkedes på grund af en intern fejl. Din tegnebog blev ikke krypteret. + + + + + The supplied passphrases do not match. + De angivne adgangskoder stemmer ikke overens. + + + + Wallet unlock failed + Tegnebogsoplåsning mislykkedes + + + + + + The passphrase entered for the wallet decryption was incorrect. + Den angivne adgangskode for tegnebogsdekrypteringen er forkert. + + + + Wallet decryption failed + Tegnebogsdekryptering mislykkedes + + + + Wallet passphrase was successfully changed. + Tegnebogens adgangskode blev ændret. + + + + BitcoinGUI + + + Sign &message... + Underskriv besked... + + + + Synchronizing with network... + Synkroniserer med netværk... + + + + &Overview + Oversigt + + + + Show general overview of wallet + Vis generel oversigt over tegnebog + + + + &Transactions + Transaktioner + + + + Browse transaction history + Gennemse transaktionshistorik + + + + Edit the list of stored addresses and labels for sending + Rediger listen over gemte adresser og mærkater + + + + Show the list of addresses for receiving payments + Vis listen over adresser for at modtage betalinger + + + + E&xit + Luk + + + + Quit application + Afslut program + + + + Show information about CryptoLottoToken + Vis informationer om CryptoLottoToken + + + + About &Qt + Om Qt + + + + Show information about Qt + Vis informationer om Qt + + + + &Options... + Indstillinger... + + + + &Encrypt Wallet... + Krypter tegnebog... + + + + &Backup Wallet... + Sikkerhedskopier tegnebog... + + + + &Change Passphrase... + Skift adgangskode... + + + + Importing blocks from disk... + Importerer blokke fra disken... + + + + Reindexing blocks on disk... + Genindekserer blokke på disken... + + + + Send coins to a CryptoLottoToken address + Send CryptoLottoTokens til en CryptoLottoToken-adresse + + + + Modify configuration options for CryptoLottoToken + Rediger konfigurationsindstillinger af CryptoLottoToken + + + + Backup wallet to another location + Lav sikkerhedskopi af tegnebogen til et andet sted + + + + Change the passphrase used for wallet encryption + Skift adgangskode anvendt til tegnebogskryptering + + + + &Debug window + Fejlsøgningsvindue + + + + Open debugging and diagnostic console + Åbn fejlsøgnings- og diagnosticeringskonsollen + + + + &Verify message... + Efterprøv besked... + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Tegnebog + + + + &Send + Send + + + + &Receive + Modtag + + + + &Addresses + Adresser + + + + &About CryptoLottoToken + Om CryptoLottoToken + + + + &Show / Hide + Vis/skjul + + + + Show or hide the main Window + Vis eller skjul hovedvinduet + + + + Encrypt the private keys that belong to your wallet + Krypter de private nøgler, der hører til din tegnebog + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Underskriv beskeder med dine CryptoLottoToken-adresser for at bevise, at de tilhører dig + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Efterprøv beskeder for at sikre, at de er underskrevet med de(n) angivne CryptoLottoToken-adresse(r) + + + + &File + Fil + + + + &Settings + Indstillinger + + + + &Help + Hjælp + + + + Tabs toolbar + Faneværktøjslinje + + + + + [testnet] + [testnetværk] + + + + CryptoLottoToken client + CryptoLottoToken-klient + + + + %n active connection(s) to CryptoLottoToken network + %n aktiv(e) forbindelse(r) til CryptoLottoToken-netværket%n aktiv(e) forbindelse(r) til CryptoLottoToken-netværket + + + + No block source available... + Ingen blokkilde tilgængelig... + + + + Processed %1 of %2 (estimated) blocks of transaction history. + %1 ud af %2 (estimeret) blokke af transaktionshistorikken er blevet behandlet. + + + + Processed %1 blocks of transaction history. + %1 blokke af transaktionshistorikken er blevet behandlet. + + + + %n hour(s) + %n time(r)%n time(r) + + + + %n day(s) + %n dag(e)%n dag(e) + + + + %n week(s) + %n uge(r)%n uge(r) + + + + %1 behind + %1 bagefter + + + + Last received block was generated %1 ago. + Senest modtagne blok blev genereret for %1 siden. + + + + Transactions after this will not yet be visible. + Transaktioner herefter vil endnu ikke være synlige. + + + + Error + Fejl + + + + Warning + Advarsel + + + + Information + Information + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + Transaktionen overskrider størrelsesgrænsen. Du kan stadig sende den for et gebyr på %1, hvilket går til de knuder, der behandler din transaktion og hjælper med at understøtte netværket. Vil du betale gebyret? + + + + Up to date + Opdateret + + + + Catching up... + Indhenter... + + + + Confirm transaction fee + Bekræft transaktionsgebyr + + + + Sent transaction + Afsendt transaktion + + + + Incoming transaction + Indgående transaktion + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Dato: %1 +Beløb: %2 +Type: %3 +Adresse: %4 + + + + + + URI handling + URI-håndtering + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + URI kan ikke fortolkes! Dette kan skyldes en ugyldig CryptoLottoToken-adresse eller misdannede URI-parametre. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Tegnebog er <b>krypteret</b> og i øjeblikket <b>ulåst</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Tegnebog er <b>krypteret</b> og i øjeblikket <b>låst</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + Der opstod en fatal fejl. CryptoLottoToken kan ikke længere fortsætte sikkert og vil afslutte. + + + + ClientModel + + + Network Alert + Netværksadvarsel + + + + EditAddressDialog + + + Edit Address + Rediger adresse + + + + &Label + Mærkat + + + + The label associated with this address book entry + Mærkaten forbundet med denne post i adressebogen + + + + &Address + Adresse + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Adressen tilknyttet til denne post i adressebogen. Dette kan kun ændres for afsendelsesadresser. + + + + New receiving address + Ny modtagelsesadresse + + + + New sending address + Ny afsendelsesadresse + + + + Edit receiving address + Rediger modtagelsesadresse + + + + Edit sending address + Rediger afsendelsesadresse + + + + The entered address "%1" is already in the address book. + Den indtastede adresse "%1" er allerede i adressebogen. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + Den indtastede adresse "%1" er ikke en gyldig CryptoLottoToken-adresse. + + + + Could not unlock wallet. + Kunne ikke låse tegnebog op. + + + + New key generation failed. + Ny nøglegenerering mislykkedes. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + version + + + + Usage: + Anvendelse: + + + + command-line options + kommandolinjetilvalg + + + + UI options + Brugergrænsefladeindstillinger + + + + Set language, for example "de_DE" (default: system locale) + Angiv sprog, f.eks "de_DE" (standard: systemlokalitet) + + + + Start minimized + Start minimeret + + + + Show splash screen on startup (default: 1) + Vis opstartsbillede ved start (standard: 1) + + + + OptionsDialog + + + Options + Indstillinger + + + + &Main + Generelt + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + Valgfrit transaktionsgebyr pr. kB, der hjælper dine transaktioner med at blive behandlet hurtigt. De fleste transaktioner er på 1 kB. + + + + Pay transaction &fee + Betal transaktionsgebyr + + + + Automatically start CryptoLottoToken after logging in to the system. + Start CryptoLottoToken automatisk, når der logges ind på systemet + + + + &Start CryptoLottoToken on system login + Start CryptoLottoToken, når systemet startes + + + + Reset all client options to default. + Nulstil alle klientindstillinger til deres standard. + + + + &Reset Options + Nulstil indstillinger + + + + &Network + Netværk + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Åbn CryptoLottoToken-klientens port på routeren automatisk. Dette virker kun, når din router understøtter UPnP og UPnP er aktiveret. + + + + Map port using &UPnP + Konfigurer port vha. UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Opret forbindelse til CryptoLottoToken-netværket via en SOCKS-proxy (f.eks. ved tilslutning gennem Tor) + + + + &Connect through SOCKS proxy: + Forbind gennem SOCKS-proxy: + + + + Proxy &IP: + Proxy-IP: + + + + IP address of the proxy (e.g. 127.0.0.1) + IP-adressen på proxyen (f.eks. 127.0.0.1) + + + + &Port: + Port: + + + + Port of the proxy (e.g. 9050) + Porten på proxyen (f.eks. 9050) + + + + SOCKS &Version: + SOCKS-version + + + + SOCKS version of the proxy (e.g. 5) + SOCKS-version af proxyen (f.eks. 5) + + + + &Window + Vindue + + + + Show only a tray icon after minimizing the window. + Vis kun et statusikon efter minimering af vinduet. + + + + &Minimize to the tray instead of the taskbar + Minimer til statusfeltet i stedet for proceslinjen + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Minimer i stedet for at afslutte programmet, når vinduet lukkes. Når denne indstilling er valgt, vil programmet kun blive lukket, når du har valgt Afslut i menuen. + + + + M&inimize on close + Minimer ved lukning + + + + &Display + Visning + + + + User Interface &language: + Brugergrænsefladesprog: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + Brugergrænsefladesproget kan angives her. Denne indstilling træder først i kraft, når CryptoLottoToken genstartes. + + + + &Unit to show amounts in: + Enhed at vise beløb i: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Vælg den standard underopdelingsenhed, som skal vises i brugergrænsefladen og ved afsendelse af CryptoLottoTokens. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Afgør hvorvidt CryptoLottoToken-adresser skal vises i transaktionslisten eller ej. + + + + &Display addresses in transaction list + Vis adresser i transaktionsliste + + + + &OK + OK + + + + &Cancel + Annuller + + + + &Apply + Anvend + + + + default + standard + + + + Confirm options reset + Bekræft nulstilling af indstillinger + + + + Some settings may require a client restart to take effect. + Nogle indstillinger kan kræve, at klienten genstartes, før de træder i kraft. + + + + Do you want to proceed? + Ønsker du at fortsætte? + + + + + Warning + Advarsel + + + + + This setting will take effect after restarting CryptoLottoToken. + Denne indstilling træder i kraft, efter CryptoLottoToken genstartes. + + + + The supplied proxy address is invalid. + Ugyldig proxy-adresse + + + + OverviewPage + + + Form + Formular + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + Den viste information kan være forældet. Din tegnebog synkroniserer automatisk med CryptoLottoToken-netværket, når en forbindelse etableres, men denne proces er ikke gennemført endnu. + + + + Balance: + Saldo: + + + + Unconfirmed: + Ubekræftede: + + + + Wallet + Tegnebog + + + + Immature: + Umodne: + + + + Mined balance that has not yet matured + Udvunden saldo, som endnu ikke er modnet + + + + <b>Recent transactions</b> + <b>Nyeste transaktioner</b> + + + + Your current balance + Din nuværende saldo + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Summen af transaktioner, der endnu ikke er bekræftet og endnu ikke er inkluderet i den nuværende saldo + + + + + out of sync + ikke synkroniseret + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + Kan ikke starte CryptoLottoToken: click-to-pay-håndtering + + + + QRCodeDialog + + + QR Code Dialog + QR-kode-dialog + + + + Request Payment + Anmod om betaling + + + + Amount: + Beløb: + + + + Label: + Mærkat: + + + + Message: + Besked: + + + + &Save As... + Gem som... + + + + Error encoding URI into QR Code. + Fejl ved kodning fra URI til QR-kode + + + + The entered amount is invalid, please check. + Det indtastede beløb er ugyldig, tjek venligst. + + + + Resulting URI too long, try to reduce the text for label / message. + Resulterende URI var for lang; prøv at forkorte teksten til mærkaten/beskeden. + + + + Save QR Code + Gem QR-kode + + + + PNG Images (*.png) + PNG-billeder (*.png) + + + + RPCConsole + + + Client name + Klientnavn + + + + + + + + + + + + + N/A + N/A + + + + Client version + Klientversion + + + + &Information + Information + + + + Using OpenSSL version + Anvendt OpenSSL-version + + + + Startup time + Opstartstid + + + + Network + Netværk + + + + Number of connections + Antal forbindelser + + + + On testnet + Tilsluttet testnetværk + + + + Block chain + Blokkæde + + + + Current number of blocks + Nuværende antal blokke + + + + Estimated total blocks + Estimeret antal blokke + + + + Last block time + Tidsstempel for seneste blok + + + + &Open + Åbn + + + + Command-line options + Kommandolinjetilvalg + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Vis CryptoLottoToken-Qt-hjælpebeskeden for at få en liste over de tilgængelige CryptoLottoToken-kommandolinjeindstillinger. + + + + &Show + Vis + + + + &Console + Konsol + + + + Build date + Byggedato + + + + CryptoLottoToken - Debug window + CryptoLottoToken - Fejlsøgningsvindue + + + + CryptoLottoToken Core + CryptoLottoToken Core + + + + Debug log file + Fejlsøgningslogfil + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + Åbn CryptoLottoToken-fejlsøgningslogfilen fra det nuværende datakatalog. Dette kan tage nogle få sekunder for en store logfiler. + + + + Clear console + Ryd konsol + + + + Welcome to the CryptoLottoToken RPC console. + Velkommen til CryptoLottoToken RPC-konsollen + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Brug op og ned-piletasterne til at navigere historikken og <b>Ctrl-L</b> til at rydde skærmen. + + + + Type <b>help</b> for an overview of available commands. + Tast <b>help</b> for en oversigt over de tilgængelige kommandoer. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Send CryptoLottoTokens + + + + Send to multiple recipients at once + Send til flere modtagere på en gang + + + + Add &Recipient + Tilføj modtager + + + + Remove all transaction fields + Fjern alle transaktionsfelter + + + + Clear &All + Ryd alle + + + + Balance: + Saldo: + + + + 123.456 BTC + 123,456 BTC + + + + Confirm the send action + Bekræft afsendelsen + + + + S&end + Afsend + + + + <b>%1</b> to %2 (%3) + <b>%1</b> til %2 (%3) + + + + Confirm send coins + Bekræft afsendelse af CryptoLottoTokens + + + + Are you sure you want to send %1? + Er du sikker på, at du vil sende %1? + + + + and + og + + + + The recipient address is not valid, please recheck. + Modtagerens adresse er ikke gyldig. Tjek venligst adressen igen. + + + + The amount to pay must be larger than 0. + Beløbet til betaling skal være større end 0. + + + + The amount exceeds your balance. + Beløbet overstiger din saldo. + + + + The total exceeds your balance when the %1 transaction fee is included. + Totalen overstiger din saldo, når %1 transaktionsgebyr er inkluderet. + + + + Duplicate address found, can only send to each address once per send operation. + Duplikeret adresse fundet. Du kan kun sende til hver adresse en gang pr. afsendelse. + + + + Error: Transaction creation failed! + Fejl: Oprettelse af transaktionen mislykkedes! + + + + 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. + Fejl: Transaktionen blev afvist. Dette kan ske, hvis nogle af dine CryptoLottoTokens i din tegnebog allerede er brugt, som hvis du brugte en kopi af wallet.dat og dine CryptoLottoTokens er blevet brugt i kopien, men ikke er markeret som brugt her. + + + + SendCoinsEntry + + + Form + Formular + + + + A&mount: + Beløb: + + + + Pay &To: + Betal til: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + CryptoLottoToken-adressen som betalingen skal sendes til (f.eks. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + Indtast en mærkat for denne adresse for at føje den til din adressebog + + + + &Label: + Mærkat: + + + + Choose address from address book + Vælg adresse fra adressebog + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Indsæt adresse fra udklipsholderen + + + + Alt+P + Alt+P + + + + Remove this recipient + Fjern denne modtager + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Indtast en CryptoLottoToken-adresse (f.eks. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Underskrifter - Underskriv/efterprøv en besked + + + + &Sign Message + Underskriv besked + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Du kan underskrive beskeder med dine CryptoLottoToken-adresser for at bevise, at de tilhører dig. Pas på ikke at underskrive noget vagt, da phisingangreb kan narre dig til at overdrage din identitet. Underskriv kun fuldt detaljerede udsagn, du er enig i. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + CryptoLottoToken-adressen som beskeden skal underskrives med (f.eks. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Vælg adresse fra adressebog + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Indsæt adresse fra udklipsholderen + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Indtast beskeden, du ønsker at underskrive + + + + Signature + Underskrift + + + + Copy the current signature to the system clipboard + Kopier den nuværende underskrift til systemets udklipsholder + + + + Sign the message to prove you own this CryptoLottoToken address + Underskriv denne besked for at bevise, at CryptoLottoToken-adressen tilhører dig + + + + Sign &Message + Underskriv besked + + + + Reset all sign message fields + Nulstil alle underskriv besked-indtastningsfelter + + + + + Clear &All + Ryd alle + + + + &Verify Message + Efterprøv besked + + + + Enter the signing 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. + Indtast den underskrevne adresse, beskeden (inkluder linjeskift, mellemrum mv. nøjagtigt, som de fremgår) og underskriften for at efterprøve beskeden. Vær forsigtig med ikke at lægge mere i underskriften end besked selv, så du undgår at blive narret af et man-in-the-middle-angreb. + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + CryptoLottoToken-adressen som beskeden er underskrevet med (f.eks. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Efterprøv beskeden for at sikre, at den er underskrevet med den angivne CryptoLottoToken-adresse + + + + Verify &Message + Efterprøv besked + + + + Reset all verify message fields + Nulstil alle efterprøv besked-indtastningsfelter + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Indtast en CryptoLottoToken-adresse (f.eks. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Klik "Underskriv besked" for at generere underskriften + + + + Enter CryptoLottoToken signature + Indtast CryptoLottoToken-underskriften + + + + + The entered address is invalid. + Den indtastede adresse er ugyldig. + + + + + + + Please check the address and try again. + Tjek venligst adressen, og forsøg igen. + + + + + The entered address does not refer to a key. + Den indtastede adresse henviser ikke til en nøgle. + + + + Wallet unlock was cancelled. + Tegnebogsoplåsning annulleret. + + + + Private key for the entered address is not available. + Den private nøgle for den indtastede adresse er ikke tilgængelig. + + + + Message signing failed. + Underskrivning af besked mislykkedes. + + + + Message signed. + Besked underskrevet. + + + + The signature could not be decoded. + Underskriften kunne ikke afkodes. + + + + + Please check the signature and try again. + Tjek venligst underskriften, og forsøg igen. + + + + The signature did not match the message digest. + Underskriften matcher ikke beskedens indhold. + + + + Message verification failed. + Efterprøvelse af besked mislykkedes. + + + + Message verified. + Besked efterprøvet. + + + + SplashScreen + + + The CryptoLottoToken developers + CryptoLottoToken-udviklerne + + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + Åben indtil %1 + + + + %1/offline + %1/offline + + + + %1/unconfirmed + %1/ubekræftet + + + + %1 confirmations + %1 bekræftelser + + + + Status + Status + + + + , broadcast through %n node(s) + , transmitteret igennem %n knude(r), transmitteret igennem %n knude(r) + + + + Date + Dato + + + + Source + Kilde + + + + Generated + Genereret + + + + + From + Fra + + + + + + To + Til + + + + + own address + egen adresse + + + + label + mærkat + + + + + + + + Credit + Kredit + + + + matures in %n more block(s) + modner efter yderligere %n blok(ke)modner efter yderligere %n blok(ke) + + + + not accepted + ikke accepteret + + + + + + + Debit + Debet + + + + Transaction fee + Transaktionsgebyr + + + + Net amount + Nettobeløb + + + + Message + Besked + + + + Comment + Kommentar + + + + Transaction ID + Transaktionens ID + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Genererede CryptoLottoTokens skal vente 120 blokke, før de kan blive brugt. Da du genererede denne blok, blev den transmitteret til netværket for at blive føjet til blokkæden. Hvis det mislykkes at komme ind i kæden, vil den skifte til "ikke godkendt" og ikke blive kunne bruges. Dette kan lejlighedsvis ske, hvis en anden knude genererer en blok inden for få sekunder af din. + + + + Debug information + Fejlsøgningsinformation + + + + Transaction + Transaktion + + + + Inputs + Input + + + + Amount + Beløb + + + + true + sand + + + + false + falsk + + + + , has not been successfully broadcast yet + , er ikke blevet transmitteret endnu + + + + Open for %n more block(s) + Åben %n blok yderligereÅben %n blokke yderligere + + + + unknown + ukendt + + + + TransactionDescDialog + + + Transaction details + Transaktionsdetaljer + + + + This pane shows a detailed description of the transaction + Denne rude viser en detaljeret beskrivelse af transaktionen + + + + TransactionTableModel + + + Date + Dato + + + + Type + Type + + + + Address + Adresse + + + + Amount + Beløb + + + + Open for %n more block(s) + Åben %n blok(ke) yderligereÅben %n blok(ke) yderligere + + + + Open until %1 + Åben indtil %1 + + + + Offline (%1 confirmations) + Offline (%1 bekræftelser) + + + + Unconfirmed (%1 of %2 confirmations) + Ubekræftet (%1 af %2 bekræftelser) + + + + Confirmed (%1 confirmations) + Bekræftet (%1 bekræftelser) + + + + Mined balance will be available when it matures in %n more block(s) + Udvunden saldo, som vil være tilgængelig, når den modner efter yderligere %n blok(ke)Udvunden saldo, som vil være tilgængelig, når den modner efter yderligere %n blok(ke) + + + + This block was not received by any other nodes and will probably not be accepted! + Denne blok blev ikke modtaget af nogen andre knuder og vil formentlig ikke blive accepteret! + + + + Generated but not accepted + Genereret, men ikke accepteret + + + + Received with + Modtaget med + + + + Received from + Modtaget fra + + + + Sent to + Sendt til + + + + Payment to yourself + Betaling til dig selv + + + + Mined + Udvundne + + + + (n/a) + (n/a) + + + + Transaction status. Hover over this field to show number of confirmations. + Transaktionsstatus. Hold musen over dette felt for at vise antallet af bekræftelser. + + + + Date and time that the transaction was received. + Dato og klokkeslæt for modtagelse af transaktionen. + + + + Type of transaction. + Transaktionstype. + + + + Destination address of transaction. + Destinationsadresse for transaktion. + + + + Amount removed from or added to balance. + Beløb fjernet eller tilføjet balance. + + + + TransactionView + + + + All + Alle + + + + Today + I dag + + + + This week + Denne uge + + + + This month + Denne måned + + + + Last month + Sidste måned + + + + This year + Dette år + + + + Range... + Interval... + + + + Received with + Modtaget med + + + + Sent to + Sendt til + + + + To yourself + Til dig selv + + + + Mined + Udvundne + + + + Other + Andet + + + + Enter address or label to search + Indtast adresse eller mærkat for at søge + + + + Min amount + Minimumsbeløb + + + + Copy address + Kopier adresse + + + + Copy label + Kopier mærkat + + + + Copy amount + Kopier beløb + + + + Copy transaction ID + Kopier transaktionens ID + + + + Edit label + Rediger mærkat + + + + Show transaction details + Vis transaktionsdetaljer + + + + Export Transaction Data + Eksporter transaktionsdata + + + + Comma separated file (*.csv) + Kommasepareret fil (*.csv) + + + + Confirmed + Bekræftet + + + + Date + Dato + + + + Type + Type + + + + Label + Mærkat + + + + Address + Adresse + + + + Amount + Beløb + + + + ID + ID + + + + Error exporting + Fejl under eksport + + + + Could not write to file %1. + Kunne ikke skrive til filen %1. + + + + Range: + Interval: + + + + to + til + + + + WalletModel + + + Send Coins + Send CryptoLottoTokens + + + + WalletView + + + &Export + Eksporter + + + + Export the data in the current tab to a file + Eksportér den aktuelle visning til en fil + + + + Backup Wallet + Sikkerhedskopier tegnebog + + + + Wallet Data (*.dat) + Tegnebogsdata (*.dat) + + + + Backup Failed + Foretagelse af sikkerhedskopi fejlede + + + + There was an error trying to save the wallet data to the new location. + Der opstod en fejl i forbindelse med at gemme tegnebogsdata til det nye sted + + + + Backup Successful + Sikkerhedskopieret problemfri + + + + The wallet data was successfully saved to the new location. + Tegnebogsdata blev problemfrit gemt til det nye sted. + + + + bitcoin-core + + + CryptoLottoToken version + CryptoLottoToken-version + + + + Usage: + Anvendelse: + + + + Send command to -server or CryptoLottoTokend + Send kommando til -server eller CryptoLottoTokend + + + + List commands + Liste over kommandoer + + + + Get help for a command + Få hjælp til en kommando + + + + Options: + Indstillinger: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Angiv konfigurationsfil (standard: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + Angiv pid-fil (default: CryptoLottoTokend.pid) + + + + Specify data directory + Angiv datakatalog + + + + Set database cache size in megabytes (default: 25) + Angiv databasecachestørrelse i megabytes (standard: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Lyt til forbindelser på <port> (standard: 22556 eller testnetværk: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Oprethold højest <n> forbindelser til andre i netværket (standard: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Forbind til en knude for at modtage adresse, og afbryd + + + + Specify your own public address + Angiv din egen offentlige adresse + + + + Threshold for disconnecting misbehaving peers (default: 100) + Grænse for afbrydelse til dårlige forbindelser (standard: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Antal sekunder dårlige forbindelser skal vente før reetablering (standard: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + Der opstod en fejl ved angivelse af RPC-porten %u til at lytte på IPv4: %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Lyt til JSON-RPC-forbindelser på <port> (standard: 22555 eller testnetværk: 44555) + + + + Accept command line and JSON-RPC commands + Accepter kommandolinje- og JSON-RPC-kommandoer + + + + Run in the background as a daemon and accept commands + Kør i baggrunden som en service, og accepter kommandoer + + + + Use the test network + Brug testnetværket + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Accepter forbindelser udefra (standard: 1 hvis hverken -proxy eller -connect) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + %s, du skal angive en RPC-adgangskode i konfigurationsfilen: +%s +Det anbefales, at du bruger nedenstående, tilfældige adgangskode: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(du behøver ikke huske denne adgangskode) +Brugernavnet og adgangskode MÅ IKKE være det samme. +Hvis filen ikke eksisterer, opret den og giv ingen andre end ejeren læserettighed. +Det anbefales også at angive alertnotify, så du påmindes om problemer; +f.eks.: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + Der opstod en fejl ved angivelse af RPC-porten %u til at lytte på IPv6, falder tilbage til IPv4: %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + Tildel til den givne adresse og lyt altid på den. Brug [vært]:port-notation for IPv6 + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + Kan ikke opnå lås på datakatalog %s. CryptoLottoToken er sandsynligvis allerede startet. + + + + 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. + Fejl: Transaktionen blev afvist. Dette kan ske, hvis nogle af dine CryptoLottoTokens i din tegnebog allerede er brugt, som hvis du brugte en kopi af wallet.dat og dine CryptoLottoTokens er blevet brugt i kopien, men ikke er markeret som brugt her. + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + Fejl: Denne transaktion kræver et transaktionsgebyr på minimum %s pga. dens størrelse, kompleksitet eller anvendelse af nyligt modtagne CryptoLottoTokens! + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + Udfør kommando, når en relevant advarsel modtages (%s i kommandoen erstattes med beskeden) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Udfør kommando, når en transaktion i tegnebogen ændres (%s i kommandoen erstattes med TxID) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Angiv maksimumstørrelse for høj prioritet/lavt gebyr-transaktioner i bytes (standard: 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + Dette er en foreløbig testudgivelse - brug på eget ansvar - brug ikke til udvinding eller handelsprogrammer + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Advarsel: -paytxfee er sat meget højt! Dette er det gebyr du vil betale, hvis du sender en transaktion. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + Advarsel: Viste transaktioner kan være ukorrekte! Du eller andre knuder kan have behov for at opgradere. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Advarsel: Undersøg venligst, at din computers dato og klokkeslæt er korrekt indstillet! Hvis der er fejl i disse, vil CryptoLottoToken ikke fungere korrekt. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Advarsel: fejl under læsning af wallet.dat! Alle nøgler blev læst korrekt, men transaktionsdata eller adressebogsposter kan mangle eller være forkerte. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Advarsel: wallet.dat ødelagt, data reddet! Oprindelig wallet.net gemt som wallet.{timestamp}.bak i %s; hvis din saldo eller dine transaktioner er forkert, bør du genskabe fra en sikkerhedskopi. + + + + Attempt to recover private keys from a corrupt wallet.dat + Forsøg at genskabe private nøgler fra ødelagt wallet.dat + + + + Block creation options: + Blokoprettelsestilvalg: + + + + Connect only to the specified node(s) + Tilslut kun til de(n) angivne knude(r) + + + + Corrupted block database detected + Ødelagt blokdatabase opdaget + + + + Discover own IP address (default: 1 when listening and no -externalip) + Find egen IP-adresse (standard: 1 når lytter og ingen -externalip) + + + + Do you want to rebuild the block database now? + Ønsker du at genbygge blokdatabasen nu? + + + + Error initializing block database + Klargøring af blokdatabase mislykkedes + + + + Error initializing wallet database environment %s! + Klargøring af tegnebogsdatabasemiljøet %s mislykkedes! + + + + Error loading block database + Indlæsning af blokdatabase mislykkedes + + + + Error opening block database + Åbning af blokdatabase mislykkedes + + + + Error: Disk space is low! + Fejl: Mangel på ledig diskplads! + + + + Error: Wallet locked, unable to create transaction! + Fejl: Tegnebog låst, kan ikke oprette transaktion! + + + + Error: system error: + Fejl: systemfejl: + + + + Failed to listen on any port. Use -listen=0 if you want this. + Lytning på enhver port mislykkedes. Brug -listen=0, hvis du ønsker dette. + + + + Failed to read block info + Læsning af blokinformation mislykkedes + + + + Failed to read block + Læsning af blok mislykkedes + + + + Failed to sync block index + Synkronisering af blokindeks mislykkedes + + + + Failed to write block index + Skrivning af blokindeks mislykkedes + + + + Failed to write block info + Skrivning af blokinformation mislykkedes + + + + Failed to write block + Skrivning af blok mislykkedes + + + + Failed to write file info + Skriving af filinformation mislykkedes + + + + Failed to write to coin database + Skrivning af CryptoLottoToken-database mislykkedes + + + + Failed to write transaction index + Skrivning af transaktionsindeks mislykkedes + + + + Failed to write undo data + Skrivning af genskabelsesdata mislykkedes + + + + Find peers using DNS lookup (default: 1 unless -connect) + Find ligeværdige ved DNS-opslag (standard: 1 hvis ikke -connect) + + + + Generate coins (default: 0) + Generer CryptoLottoTokens (standard: 0) + + + + How many blocks to check at startup (default: 288, 0 = all) + Antal blokke som tjekkes ved opstart (0=alle, standard: 288) + + + + How thorough the block verification is (0-4, default: 3) + Grundighed af efterprøvning af blokke (0-4, standard: 3) + + + + Not enough file descriptors available. + For få tilgængelige fildeskriptorer. + + + + Rebuild block chain index from current blk000??.dat files + Genbyg blokkædeindeks fra nuværende blk000??.dat filer + + + + Set the number of threads to service RPC calls (default: 4) + Angiv antallet af tråde til at håndtere RPC-kald (standard: 4) + + + + Verifying blocks... + Efterprøver blokke... + + + + Verifying wallet... + Efterprøver tegnebog... + + + + Imports blocks from external blk000??.dat file + Importerer blokke fra ekstern blk000??.dat fil + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + Angiv nummeret af skriptefterprøvningstråde (op til 16, 0 = automatisk, <0 = efterlad det antal kerner tilgængelige, standard: 0) + + + + Information + Information + + + + Invalid -tor address: '%s' + Ugyldig -tor adresse: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + Ugyldigt beløb til -minrelaytxfee=<beløb>:'%s' + + + + Invalid amount for -mintxfee=<amount>: '%s' + Ugyldigt beløb til -mintxfee=<beløb>:'%s' + + + + Maintain a full transaction index (default: 0) + Vedligehold et komplet transaktionsindeks (standard: 0) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Maksimum for modtagelsesbuffer pr. forbindelse, <n>*1000 bytes (standard: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Maksimum for afsendelsesbuffer pr. forbindelse, <n>*1000 bytes (standard: 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + Accepter kun blokkæde, som matcher indbyggede kontrolposter (standard: 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Tilslut kun til knuder i netværk <net> (IPv4, IPv6 eller Tor) + + + + Output extra debugging information. Implies all other -debug* options + Skriv ekstra fejlsøgningsinformation. Indebærer alle andre -debug* tilvalg + + + + Output extra network debugging information + Skriv ekstra netværksfejlsøgningsinformation + + + + Prepend debug output with timestamp (default: 1) + Tilføj fejlsøgningsoutput med tidsstempel + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + SSL-indstillinger: (se CryptoLottoToken Wiki for SSL-opsætningsinstruktioner) + + + + Select the version of socks proxy to use (4-5, default: 5) + Angiv version af SOCKS-proxyen (4-5, standard: 5) + + + + Send trace/debug info to console instead of debug.log file + Send sporings-/fejlsøgningsinformation til konsollen i stedet for debug.log filen + + + + Send trace/debug info to debugger + Send sporings-/fejlsøgningsinformation til fejlsøgningprogrammet + + + + Set maximum block size in bytes (default: 250000) + Angiv maksimumblokstørrelse i bytes (standard: 250000) + + + + Set minimum block size in bytes (default: 0) + Angiv minimumsblokstørrelse i bytes (standard: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Formindsk debug.log filen ved klientopstart (standard: 1 hvis ikke -debug) + + + + Signing transaction failed + Underskrift af transaktion mislykkedes + + + + Specify connection timeout in milliseconds (default: 5000) + Angiv tilslutningstimeout i millisekunder (standard: 5000) + + + + System error: + Systemfejl: + + + + Transaction amount too small + Transaktionsbeløb er for lavt + + + + Transaction amounts must be positive + Transaktionsbeløb skal være positive + + + + Transaction too large + Transaktionen er for stor + + + + Use UPnP to map the listening port (default: 0) + Forsøg at bruge UPnP til at konfigurere den lyttende port (standard: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Forsøg at bruge UPnP til at konfigurere den lyttende port (standard: 1 når lytter) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Brug proxy til at tilgå Tor Hidden Services (standard: som -proxy) + + + + Username for JSON-RPC connections + Brugernavn til JSON-RPC-forbindelser + + + + Warning + Advarsel + + + + Warning: This version is obsolete, upgrade required! + Advarsel: Denne version er forældet, opgradering påkrævet! + + + + You need to rebuild the databases using -reindex to change -txindex + Du skal genbygge databaserne med -reindex for at ændre -txindex + + + + wallet.dat corrupt, salvage failed + wallet.dat ødelagt, redning af data mislykkedes + + + + Password for JSON-RPC connections + Adgangskode til JSON-RPC-forbindelser + + + + Allow JSON-RPC connections from specified IP address + Tillad JSON-RPC-forbindelser fra bestemt IP-adresse + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Send kommandoer til knude, der kører på <ip> (standard: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Udfør kommando, når den bedste blok ændres (%s i kommandoen erstattes med blokhash) + + + + Upgrade wallet to latest format + Opgrader tegnebog til seneste format + + + + Set key pool size to <n> (default: 100) + Angiv nøglepoolstørrelse til <n> (standard: 100) + + + + Rescan the block chain for missing wallet transactions + Gennemsøg blokkæden for manglende tegnebogstransaktioner + + + + Use OpenSSL (https) for JSON-RPC connections + Brug OpenSSL (https) for JSON-RPC-forbindelser + + + + Server certificate file (default: server.cert) + Servercertifikat-fil (standard: server.cert) + + + + Server private key (default: server.pem) + Serverens private nøgle (standard: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Acceptable ciphers (standard: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Denne hjælpebesked + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Kunne ikke tildele %s på denne computer (bind returnerede fejl %d, %s) + + + + Connect through socks proxy + Tilslut via SOCKS-proxy + + + + Allow DNS lookups for -addnode, -seednode and -connect + Tillad DNS-opslag for -addnode, -seednode og -connect + + + + Loading addresses... + Indlæser adresser... + + + + Error loading wallet.dat: Wallet corrupted + Fejl ved indlæsning af wallet.dat: Tegnebog ødelagt + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Fejl ved indlæsning af wallet.dat: Tegnebog kræver en nyere version af CryptoLottoToken + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Det var nødvendigt at genskrive tegnebogen: genstart CryptoLottoToken for at gennemføre + + + + Error loading wallet.dat + Fejl ved indlæsning af wallet.dat + + + + Invalid -proxy address: '%s' + Ugyldig -proxy adresse: '%s' + + + + Unknown network specified in -onlynet: '%s' + Ukendt netværk anført i -onlynet: '%s' + + + + Unknown -socks proxy version requested: %i + Ukendt -socks proxy-version: %i + + + + Cannot resolve -bind address: '%s' + Kan ikke finde -bind adressen: '%s' + + + + Cannot resolve -externalip address: '%s' + Kan ikke finde -externalip adressen: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + Ugyldigt beløb for -paytxfee=<amount>: '%s' + + + + Invalid amount + Ugyldigt beløb + + + + Insufficient funds + Manglende dækning + + + + Loading block index... + Indlæser blokindeks... + + + + Add a node to connect to and attempt to keep the connection open + Tilføj en knude til at forbinde til og forsøg at holde forbindelsen åben + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Kunne ikke tildele %s på denne computer. CryptoLottoToken kører sikkert allerede. + + + + Fee per KB to add to transactions you send + Gebyr pr. kB, som skal tilføjes til transaktioner, du sender + + + + Loading wallet... + Indlæser tegnebog... + + + + Cannot downgrade wallet + Kan ikke nedgradere tegnebog + + + + Cannot write default address + Kan ikke skrive standardadresse + + + + Rescanning... + Genindlæser... + + + + Done loading + Indlæsning gennemført + + + + To use the %s option + For at bruge %s mulighed + + + + Error + Fejl + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + Du skal angive rpcpassword=<password> i konfigurationsfilen: +%s +Hvis filen ikke eksisterer, opret den og giv ingen andre end ejeren læserettighed. + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_de.qm b/src/qt/locale/bitcoin_de.qm new file mode 100644 index 0000000..a60985e Binary files /dev/null and b/src/qt/locale/bitcoin_de.qm differ diff --git a/src/qt/locale/bitcoin_de.ts b/src/qt/locale/bitcoin_de.ts new file mode 100644 index 0000000..4377097 --- /dev/null +++ b/src/qt/locale/bitcoin_de.ts @@ -0,0 +1,2937 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + Über CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b>-Version + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +Dies ist experimentelle Software. + +Veröffentlicht unter der MIT/X11-Softwarelizenz, siehe beiligende Datei COPYING oder http://www.opensource.org/licenses/mit-license.php. + +Dieses Produkt enthält Software, die vom OpenSSL-Projekt zur Verwendung im OpenSSL-Toolkit (http://www.openssl.org/) entwickelt wurde, sowie kryptographische Software geschrieben von Eric Young (eay@cryptsoft.com) und UPnP-Software geschrieben von Thomas Bernard. + + + + Copyright + Copyright + + + + The CryptoLottoToken developers + Die CryptoLottoTokenentwickler + + + + AddressBookPage + + + Address Book + Adressbuch + + + + Double-click to edit address or label + Doppelklicken, um die Adresse oder die Bezeichnung zu bearbeiten + + + + Create a new address + Eine neue Adresse erstellen + + + + Copy the currently selected address to the system clipboard + Ausgewählte Adresse in die Zwischenablage kopieren + + + + &New Address + &Neue Adresse + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Dies sind Ihre CryptoLottoToken-Adressen zum Empfangen von Zahlungen. Es steht Ihnen frei, jedem Absender eine Andere mitzuteilen, um einen besseren Überblick über eingehende Zahlungen zu erhalten. + + + + &Copy Address + Adresse &kopieren + + + + Show &QR Code + &QR-Code anzeigen + + + + Sign a message to prove you own a CryptoLottoToken address + Eine Nachricht signieren, um den Besitz einer CryptoLottoToken-Adresse zu beweisen + + + + Sign &Message + Nachricht &signieren + + + + Delete the currently selected address from the list + Die ausgewählte Adresse aus der Liste entfernen. + + + + Export the data in the current tab to a file + Daten der aktuellen Ansicht in eine Datei exportieren + + + + &Export + E&xportieren + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Eine Nachricht verifizieren, um sicherzustellen, dass diese mit einer angegebenen CryptoLottoToken-Adresse signiert wurde + + + + &Verify Message + Nachricht &verifizieren + + + + &Delete + &Löschen + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + Dies sind Ihre CryptoLottoToken-Adressen zum Tätigen von Überweisungen. Bitte prüfen Sie den Betrag und die Empfangsadresse, bevor Sie CryptoLottoTokens überweisen. + + + + Copy &Label + &Bezeichnung kopieren + + + + &Edit + &Editieren + + + + Send &Coins + CryptoLottoTokens &überweisen + + + + Export Address Book Data + Adressbuch exportieren + + + + Comma separated file (*.csv) + Kommagetrennte-Datei (*.csv) + + + + Error exporting + Fehler beim Exportieren + + + + Could not write to file %1. + Konnte nicht in Datei %1 schreiben. + + + + AddressTableModel + + + Label + Bezeichnung + + + + Address + Adresse + + + + (no label) + (keine Bezeichnung) + + + + AskPassphraseDialog + + + Passphrase Dialog + Passphrasendialog + + + + Enter passphrase + Passphrase eingeben + + + + New passphrase + Neue Passphrase + + + + Repeat new passphrase + Neue Passphrase wiederholen + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Geben Sie die neue Passphrase für die Brieftasche ein.<br>Bitte benutzen Sie eine Passphrase bestehend aus <b>10 oder mehr zufälligen Zeichen</b> oder <b>8 oder mehr Wörtern</b>. + + + + Encrypt wallet + Brieftasche verschlüsseln + + + + This operation needs your wallet passphrase to unlock the wallet. + Dieser Vorgang benötigt Ihre Passphrase, um die Brieftasche zu entsperren. + + + + Unlock wallet + Brieftasche entsperren + + + + This operation needs your wallet passphrase to decrypt the wallet. + Dieser Vorgang benötigt Ihre Passphrase, um die Brieftasche zu entschlüsseln. + + + + Decrypt wallet + Brieftasche entschlüsseln + + + + Change passphrase + Passphrase ändern + + + + Enter the old and new passphrase to the wallet. + Geben Sie die alte und neue Passphrase der Brieftasche ein. + + + + Confirm wallet encryption + Verschlüsselung der Brieftasche bestätigen + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Warnung: Wenn Sie Ihre Brieftasche verschlüsseln und Ihre Passphrase verlieren, werden Sie <b>alle Ihre CryptoLottoTokens verlieren</b>! + + + + Are you sure you wish to encrypt your wallet? + Sind Sie sich sicher, dass Sie Ihre Brieftasche verschlüsseln möchten? + + + + 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. + WICHTIG: Alle vorherigen Sicherungen Ihrer Brieftasche sollten durch die neu erzeugte, verschlüsselte Brieftasche ersetzt werden. Aus Sicherheitsgründen werden vorherige Sicherungen der unverschlüsselten Brieftasche nutzlos, sobald Sie die neue, verschlüsselte Brieftasche verwenden. + + + + + Warning: The Caps Lock key is on! + Warnung: Die Feststelltaste ist aktiviert! + + + + + Wallet encrypted + Brieftasche verschlüsselt + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken wird jetzt beendet, um den Verschlüsselungsprozess abzuschließen. Bitte beachten Sie, dass die Verschlüsselung Ihrer Brieftasche nicht vollständig vor Diebstahl Ihrer CryptoLottoTokens durch Schadsoftware schützt, die Ihren Computer befällt. + + + + + + + Wallet encryption failed + Verschlüsselung der Brieftasche fehlgeschlagen + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Die Verschlüsselung der Brieftasche ist aufgrund eines internen Fehlers fehlgeschlagen. Ihre Brieftasche wurde nicht verschlüsselt. + + + + + The supplied passphrases do not match. + Die eingegebenen Passphrasen stimmen nicht überein. + + + + Wallet unlock failed + Entsperrung der Brieftasche fehlgeschlagen + + + + + + The passphrase entered for the wallet decryption was incorrect. + Die eingegebene Passphrase zum Entschlüsseln der Brieftasche war nicht korrekt. + + + + Wallet decryption failed + Entschlüsselung der Brieftasche fehlgeschlagen + + + + Wallet passphrase was successfully changed. + Die Passphrase der Brieftasche wurde erfolgreich geändert. + + + + BitcoinGUI + + + Sign &message... + Nachricht s&ignieren... + + + + Synchronizing with network... + Synchronisiere mit Netzwerk... + + + + &Overview + &Übersicht + + + + Show general overview of wallet + Allgemeine Übersicht der Brieftasche anzeigen + + + + &Transactions + &Transaktionen + + + + Browse transaction history + Transaktionsverlauf durchsehen + + + + Edit the list of stored addresses and labels for sending + Liste der gespeicherten Zahlungsadressen und Bezeichnungen bearbeiten + + + + Show the list of addresses for receiving payments + Liste der Empfangsadressen anzeigen + + + + E&xit + &Beenden + + + + Quit application + Anwendung beenden + + + + Show information about CryptoLottoToken + Informationen über CryptoLottoToken anzeigen + + + + About &Qt + Über &Qt + + + + Show information about Qt + Informationen über Qt anzeigen + + + + &Options... + &Konfiguration... + + + + &Encrypt Wallet... + Brieftasche &verschlüsseln... + + + + &Backup Wallet... + Brieftasche &sichern... + + + + &Change Passphrase... + Passphrase &ändern... + + + + Importing blocks from disk... + Importiere Blöcke von Laufwerk... + + + + Reindexing blocks on disk... + Reindiziere Blöcke auf Laufwerk... + + + + Send coins to a CryptoLottoToken address + CryptoLottoTokens an eine CryptoLottoToken-Adresse überweisen + + + + Modify configuration options for CryptoLottoToken + Die Konfiguration des Clients bearbeiten + + + + Backup wallet to another location + Eine Sicherungskopie der Brieftasche erstellen und abspeichern + + + + Change the passphrase used for wallet encryption + Ändert die Passphrase, die für die Verschlüsselung der Brieftasche benutzt wird + + + + &Debug window + &Debugfenster + + + + Open debugging and diagnostic console + Debugging- und Diagnosekonsole öffnen + + + + &Verify message... + Nachricht &verifizieren... + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Brieftasche + + + + &Send + Überweisen + + + + &Receive + &Empfangen + + + + &Addresses + &Adressen + + + + &About CryptoLottoToken + &Über CryptoLottoToken + + + + &Show / Hide + &Anzeigen / Verstecken + + + + Show or hide the main Window + Das Hauptfenster anzeigen oder verstecken + + + + Encrypt the private keys that belong to your wallet + Verschlüsselt die zu Ihrer Brieftasche gehörenden privaten Schlüssel + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Nachrichten signieren, um den Besitz Ihrer CryptoLottoToken-Adressen zu beweisen + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Nachrichten verifizieren, um sicherzustellen, dass diese mit den angegebenen CryptoLottoToken-Adressen signiert wurden + + + + &File + &Datei + + + + &Settings + &Einstellungen + + + + &Help + &Hilfe + + + + Tabs toolbar + Registerkartenleiste + + + + + [testnet] + [Testnetz] + + + + CryptoLottoToken client + CryptoLottoToken-Client + + + + %n active connection(s) to CryptoLottoToken network + %n aktive Verbindung zum CryptoLottoToken-Netzwerk%n aktive Verbindungen zum CryptoLottoToken-Netzwerk + + + + No block source available... + Keine Blockquelle verfügbar... + + + + Processed %1 of %2 (estimated) blocks of transaction history. + %1 von (geschätzten) %2 Blöcken des Transaktionsverlaufs verarbeitet. + + + + Processed %1 blocks of transaction history. + %1 Blöcke des Transaktionsverlaufs verarbeitet. + + + + %n hour(s) + %n Stunde%n Stunden + + + + %n day(s) + %n Tag%n Tage + + + + %n week(s) + %n Woche%n Wochen + + + + %1 behind + %1 im Rückstand + + + + Last received block was generated %1 ago. + Der letzte empfangene Block ist %1 alt. + + + + Transactions after this will not yet be visible. + Transaktionen hiernach werden noch nicht angezeigt. + + + + Error + Fehler + + + + Warning + Warnung + + + + Information + Hinweis + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + Die Transaktion übersteigt das Größenlimit. Sie können sie trotzdem senden, wenn Sie eine zusätzliche Transaktionsgebühr in Höhe von %1 zahlen. Diese wird an die Knoten verteilt, die Ihre Transaktion bearbeiten und unterstützt damit das CryptoLottoToken-Netzwerk. Möchten Sie die Gebühr bezahlen? + + + + Up to date + Auf aktuellem Stand + + + + Catching up... + Hole auf... + + + + Confirm transaction fee + Transaktionsgebühr bestätigen + + + + Sent transaction + Gesendete Transaktion + + + + Incoming transaction + Eingehende Transaktion + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Datum: %1 +Betrag: %2 +Typ: %3 +Adresse: %4 + + + + + URI handling + URI Verarbeitung + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + URI kann nicht analysiert werden! Dies kann durch eine ungültige CryptoLottoToken-Adresse oder fehlerhafte URI-Parameter verursacht werden. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Brieftasche ist <b>verschlüsselt</b> und aktuell <b>entsperrt</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Brieftasche ist <b>verschlüsselt</b> und aktuell <b>gesperrt</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + Ein schwerer Fehler ist aufgetreten. CryptoLottoToken kann nicht stabil weiter ausgeführt werden und wird beendet. + + + + ClientModel + + + Network Alert + Netzwerkalarm + + + + EditAddressDialog + + + Edit Address + Adresse bearbeiten + + + + &Label + &Bezeichnung + + + + The label associated with this address book entry + Die Bezeichnung dieses Adressbuchseintrags + + + + &Address + &Adresse + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Die Adresse des Adressbucheintrags. Diese kann nur für Zahlungsadressen bearbeitet werden. + + + + New receiving address + Neue Empfangsadresse + + + + New sending address + Neue Zahlungsadresse + + + + Edit receiving address + Empfangsadresse bearbeiten + + + + Edit sending address + Zahlungsadresse bearbeiten + + + + The entered address "%1" is already in the address book. + Die eingegebene Adresse "%1" befindet sich bereits im Adressbuch. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + Die eingegebene Adresse "%1" ist keine gültige CryptoLottoToken-Adresse. + + + + Could not unlock wallet. + Die Brieftasche konnte nicht entsperrt werden. + + + + New key generation failed. + Generierung eines neuen Schlüssels fehlgeschlagen. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + Version + + + + Usage: + Benutzung: + + + + command-line options + Kommandozeilenoptionen + + + + UI options + UI-Optionen + + + + Set language, for example "de_DE" (default: system locale) + Sprache festlegen, z.B. "de_DE" (Standard: System Locale) + + + + Start minimized + Minimiert starten + + + + Show splash screen on startup (default: 1) + Startbildschirm beim Starten anzeigen (Standard: 1) + + + + OptionsDialog + + + Options + Erweiterte Einstellungen + + + + &Main + &Allgemein + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + Optionale Transaktionsgebühr pro KB, die sicherstellt, dass Ihre Transaktionen schnell bearbeitet werden. Die meisten Transaktionen sind 1 kB groß. + + + + Pay transaction &fee + Transaktions&gebühr bezahlen + + + + Automatically start CryptoLottoToken after logging in to the system. + CryptoLottoToken nach der Anmeldung am System automatisch ausführen. + + + + &Start CryptoLottoToken on system login + &Starte CryptoLottoToken nach Systemanmeldung + + + + Reset all client options to default. + Setzt die Clientkonfiguration auf Standardwerte zurück. + + + + &Reset Options + Konfiguration &zurücksetzen + + + + &Network + &Netzwerk + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Automatisch den CryptoLottoToken-Clientport auf dem Router öffnen. Dies funktioniert nur, wenn Ihr Router UPnP unterstützt und dies aktiviert ist. + + + + Map port using &UPnP + Portweiterleitung via &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Über einen SOCKS-Proxy mit dem CryptoLottoToken-Netzwerk verbinden (z.B. beim Verbinden über Tor). + + + + &Connect through SOCKS proxy: + Über einen SOCKS-Proxy &verbinden: + + + + Proxy &IP: + Proxy-&IP: + + + + IP address of the proxy (e.g. 127.0.0.1) + IP-Adresse des Proxies (z.B. 127.0.0.1) + + + + &Port: + &Port: + + + + Port of the proxy (e.g. 9050) + Port des Proxies (z.B. 9050) + + + + SOCKS &Version: + SOCKS-&Version: + + + + SOCKS version of the proxy (e.g. 5) + SOCKS-Version des Proxies (z.B. 5) + + + + &Window + &Programmfenster + + + + Show only a tray icon after minimizing the window. + Nur ein Symbol im Infobereich anzeigen, nachdem das Programmfenster minimiert wurde. + + + + &Minimize to the tray instead of the taskbar + In den Infobereich anstatt in die Taskleiste &minimieren + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Minimiert die Anwendung anstatt sie zu beenden wenn das Fenster geschlossen wird. Wenn dies aktiviert ist, müssen Sie das Programm über "Beenden" im Menü schließen. + + + + M&inimize on close + Beim Schließen m&inimieren + + + + &Display + &Anzeige + + + + User Interface &language: + &Sprache der Benutzeroberfläche: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + Legt die Sprache der Benutzeroberfläche fest. Diese Einstellung wird erst nach einem Neustart von CryptoLottoToken aktiv. + + + + &Unit to show amounts in: + &Einheit der Beträge: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Wählen Sie die Standarduntereinheit, die in der Benutzeroberfläche und beim Überweisen von CryptoLottoTokens angezeigt werden soll. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Legt fest, ob CryptoLottoToken-Adressen in der Transaktionsliste angezeigt werden. + + + + &Display addresses in transaction list + Adressen in der Transaktionsliste &anzeigen + + + + &OK + &OK + + + + &Cancel + &Abbrechen + + + + &Apply + &Übernehmen + + + + default + Standard + + + + Confirm options reset + Zurücksetzen der Konfiguration bestätigen + + + + Some settings may require a client restart to take effect. + Einige Einstellungen benötigen möglicherweise einen Clientneustart, um aktiv zu werden. + + + + Do you want to proceed? + Wollen Sie fortfahren? + + + + + Warning + Warnung + + + + + This setting will take effect after restarting CryptoLottoToken. + Diese Einstellung wird erst nach einem Neustart von CryptoLottoToken aktiv. + + + + The supplied proxy address is invalid. + Die eingegebene Proxyadresse ist ungültig. + + + + OverviewPage + + + Form + Formular + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + Die angezeigten Informationen sind möglicherweise nicht mehr aktuell. Ihre Brieftasche wird automatisch synchronisiert, nachdem eine Verbindung zum CryptoLottoToken-Netzwerk hergestellt wurde. Dieser Prozess ist jedoch derzeit noch nicht abgeschlossen. + + + + Balance: + Kontostand: + + + + Unconfirmed: + Unbestätigt: + + + + Wallet + Brieftasche + + + + Immature: + Unreif: + + + + Mined balance that has not yet matured + Erarbeiteter Betrag der noch nicht gereift ist + + + + <b>Recent transactions</b> + <b>Letzte Transaktionen</b> + + + + Your current balance + Ihr aktueller Kontostand + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Betrag aus unbestätigten Transaktionen, der noch nicht im aktuellen Kontostand enthalten ist + + + + + out of sync + nicht synchron + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + "CryptoLottoToken: Klicken-zum-Bezahlen"-Handler konnte nicht gestartet werden + + + + QRCodeDialog + + + QR Code Dialog + QR-Code-Dialog + + + + Request Payment + Zahlung anfordern + + + + Amount: + Betrag: + + + + Label: + Bezeichnung: + + + + Message: + Nachricht: + + + + &Save As... + &Speichern unter... + + + + Error encoding URI into QR Code. + Fehler beim Kodieren der URI in den QR-Code. + + + + The entered amount is invalid, please check. + Der eingegebene Betrag ist ungültig, bitte überprüfen. + + + + Resulting URI too long, try to reduce the text for label / message. + Resultierende URI zu lang, bitte den Text für Bezeichnung / Nachricht kürzen. + + + + Save QR Code + QR-Code abspeichern + + + + PNG Images (*.png) + PNG-Bild (*.png) + + + + RPCConsole + + + Client name + Clientname + + + + + + + + + + + + + N/A + n.v. + + + + Client version + Clientversion + + + + &Information + &Information + + + + Using OpenSSL version + Verwendete OpenSSL-Version + + + + Startup time + Startzeit + + + + Network + Netzwerk + + + + Number of connections + Anzahl Verbindungen + + + + On testnet + Im Testnetz + + + + Block chain + Blockkette + + + + Current number of blocks + Aktuelle Anzahl Blöcke + + + + Estimated total blocks + Geschätzte Gesamtzahl Blöcke + + + + Last block time + Letzte Blockzeit + + + + &Open + &Öffnen + + + + Command-line options + Kommandozeilenoptionen + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Zeige die CryptoLottoToken-Qt-Hilfsnachricht, um eine Liste mit möglichen Kommandozeilenoptionen zu erhalten. + + + + &Show + &Anzeigen + + + + &Console + &Konsole + + + + Build date + Erstellungsdatum + + + + CryptoLottoToken - Debug window + CryptoLottoToken - Debugfenster + + + + CryptoLottoToken Core + CryptoLottoToken-Kern + + + + Debug log file + Debugprotokolldatei + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + Öffnet die CryptoLottoToken-Debugprotokolldatei aus dem aktuellen Datenverzeichnis. Dies kann bei großen Protokolldateien einige Sekunden dauern. + + + + Clear console + Konsole zurücksetzen + + + + Welcome to the CryptoLottoToken RPC console. + Willkommen in der CryptoLottoToken-RPC-Konsole. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Pfeiltaste hoch und runter, um die Historie durchzublättern und <b>Strg-L</b>, um die Konsole zurückzusetzen. + + + + Type <b>help</b> for an overview of available commands. + Bitte <b>help</b> eingeben, um eine Übersicht verfügbarer Befehle zu erhalten. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + CryptoLottoTokens überweisen + + + + Send to multiple recipients at once + In einer Transaktion an mehrere Empfänger auf einmal überweisen + + + + Add &Recipient + Empfänger &hinzufügen + + + + Remove all transaction fields + Alle Überweisungsfelder zurücksetzen + + + + Clear &All + &Zurücksetzen + + + + Balance: + Kontostand: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Überweisung bestätigen + + + + S&end + &Überweisen + + + + <b>%1</b> to %2 (%3) + <b>%1</b> an %2 (%3) + + + + Confirm send coins + Überweisung bestätigen + + + + Are you sure you want to send %1? + Sind Sie sich sicher, dass Sie die folgende Überweisung ausführen möchten?<br>%1 + + + + and + und + + + + The recipient address is not valid, please recheck. + Die Zahlungsadresse ist ungültig, bitte nochmals überprüfen. + + + + The amount to pay must be larger than 0. + Der zu zahlende Betrag muss größer als 0 sein. + + + + The amount exceeds your balance. + Der angegebene Betrag übersteigt Ihren Kontostand. + + + + The total exceeds your balance when the %1 transaction fee is included. + Der angegebene Betrag übersteigt aufgrund der Transaktionsgebühr in Höhe von %1 Ihren Kontostand. + + + + Duplicate address found, can only send to each address once per send operation. + Doppelte Adresse gefunden, pro Überweisung kann an jede Adresse nur einmalig etwas überwiesen werden. + + + + Error: Transaction creation failed! + Fehler: Transaktionserstellung fehlgeschlagen! + + + + 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. + Fehler: Die Transaktion wurde abgelehnt. Dies kann passieren, wenn einige CryptoLottoTokens aus Ihrer Brieftasche bereits ausgegeben wurden. Beispielsweise weil Sie eine Kopie Ihrer wallet.dat genutzt, die CryptoLottoTokens dort ausgegeben haben und dies daher in der derzeit aktiven Brieftasche nicht vermerkt ist. + + + + SendCoinsEntry + + + Form + Formular + + + + A&mount: + &Betrag: + + + + Pay &To: + &Empfänger: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Die Zahlungsadresse der Überweisung (z.B. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + Adressbezeichnung eingeben (diese wird zusammen mit der Adresse dem Adressbuch hinzugefügt) + + + + &Label: + &Bezeichnung: + + + + Choose address from address book + Adresse aus Adressbuch wählen + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Adresse aus der Zwischenablage einfügen + + + + Alt+P + Alt+P + + + + Remove this recipient + Diesen Empfänger entfernen + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + CryptoLottoToken-Adresse eingeben (z.B. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Signaturen - eine Nachricht signieren / verifizieren + + + + &Sign Message + Nachricht &signieren + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Sie können Nachrichten mit Ihren Adressen signieren, um den Besitz dieser Adressen zu beweisen. Bitte nutzen Sie diese Funktion mit Vorsicht und nehmen Sie sich vor Phishingangriffen in Acht. Signieren Sie nur Nachrichten, mit denen Sie vollständig einverstanden sind. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Die Adresse mit der die Nachricht signiert wird (z.B. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Eine Adresse aus dem Adressbuch wählen + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Adresse aus der Zwischenablage einfügen + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Zu signierende Nachricht hier eingeben + + + + Signature + Signatur + + + + Copy the current signature to the system clipboard + Aktuelle Signatur in die Zwischenablage kopieren + + + + Sign the message to prove you own this CryptoLottoToken address + Die Nachricht signieren, um den Besitz dieser CryptoLottoToken-Adresse zu beweisen + + + + Sign &Message + &Nachricht signieren + + + + Reset all sign message fields + Alle "Nachricht signieren"-Felder zurücksetzen + + + + + Clear &All + &Zurücksetzen + + + + &Verify Message + Nachricht &verifizieren + + + + Enter the signing 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. + Geben Sie die signierende Adresse, 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, als in der signierten Nachricht selber enthalten ist, um nicht von einerm Man-in-the-middle-Angriff hinters Licht geführt zu werden. + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Die Adresse mit der die Nachricht signiert wurde (z.B. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Die Nachricht verifizieren, um sicherzustellen, dass diese mit der angegebenen CryptoLottoToken-Adresse signiert wurde + + + + Verify &Message + &Nachricht verifizieren + + + + Reset all verify message fields + Alle "Nachricht verifizieren"-Felder zurücksetzen + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + CryptoLottoToken-Adresse eingeben (z.B. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Auf "Nachricht signieren" klicken, um die Signatur zu erzeugen + + + + Enter CryptoLottoToken signature + CryptoLottoToken-Signatur eingeben + + + + + The entered address is invalid. + Die eingegebene Adresse ist ungültig. + + + + + + + Please check the address and try again. + Bitte überprüfen Sie die Adresse und versuchen Sie es erneut. + + + + + The entered address does not refer to a key. + Die eingegebene Adresse verweist nicht auf einen Schlüssel. + + + + Wallet unlock was cancelled. + Entsperrung der Brieftasche wurde abgebrochen. + + + + Private key for the entered address is not available. + Privater Schlüssel zur eingegebenen Adresse ist nicht verfügbar. + + + + Message signing failed. + Signierung der Nachricht fehlgeschlagen. + + + + Message signed. + Nachricht signiert. + + + + The signature could not be decoded. + Die Signatur konnte nicht dekodiert werden. + + + + + Please check the signature and try again. + Bitte überprüfen Sie die Signatur und versuchen Sie es erneut. + + + + The signature did not match the message digest. + Die Signatur entspricht nicht dem Message Digest. + + + + Message verification failed. + Verifikation der Nachricht fehlgeschlagen. + + + + Message verified. + Nachricht verifiziert. + + + + SplashScreen + + + The CryptoLottoToken developers + Die CryptoLottoTokenentwickler + + + + [testnet] + [Testnetz] + + + + TransactionDesc + + + Open until %1 + Offen bis %1 + + + + %1/offline + %1/offline + + + + %1/unconfirmed + %1/unbestätigt + + + + %1 confirmations + %1 Bestätigungen + + + + Status + Status + + + + , broadcast through %n node(s) + , über %n Knoten übertragen, über %n Knoten übertragen + + + + Date + Datum + + + + Source + Quelle + + + + Generated + Generiert + + + + + From + Von + + + + + + To + An + + + + + own address + eigene Adresse + + + + label + Bezeichnung + + + + + + + + Credit + Gutschrift + + + + matures in %n more block(s) + reift noch %n weiteren Blockreift noch %n weitere Blöcke + + + + not accepted + nicht angenommen + + + + + + + Debit + Belastung + + + + Transaction fee + Transaktionsgebühr + + + + Net amount + Nettobetrag + + + + Message + Nachricht signieren + + + + Comment + Kommentar + + + + Transaction ID + Transaktions-ID + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Generierte CryptoLottoTokens müssen 120 Blöcke lang reifen, bevor sie ausgegeben werden können. Als Sie diesen Block generierten, wurde er an das Netzwerk übertragen, um ihn der Blockkette hinzuzufügen. Falls dies fehlschlägt wird der Status in "nicht angenommen" geändert und der Betrag wird nicht verfügbar werden. Das kann gelegentlich passieren, wenn ein anderer Knoten einen Block fast zeitgleich generiert. + + + + Debug information + Debuginformationen + + + + Transaction + Transaktion + + + + Inputs + Eingaben + + + + Amount + Betrag + + + + true + wahr + + + + false + falsch + + + + , has not been successfully broadcast yet + , wurde noch nicht erfolgreich übertragen + + + + Open for %n more block(s) + Offen für %n weiteren BlockOffen für %n weitere Blöcke + + + + unknown + unbekannt + + + + TransactionDescDialog + + + Transaction details + Transaktionsdetails + + + + This pane shows a detailed description of the transaction + Dieser Bereich zeigt eine detaillierte Beschreibung der Transaktion an + + + + TransactionTableModel + + + Date + Datum + + + + Type + Typ + + + + Address + Adresse + + + + Amount + Betrag + + + + Open for %n more block(s) + Offen für %n weiteren BlockOffen für %n weitere Blöcke + + + + Open until %1 + Offen bis %1 + + + + Offline (%1 confirmations) + Offline (%1 Bestätigungen) + + + + Unconfirmed (%1 of %2 confirmations) + Unbestätigt (%1 von %2 Bestätigungen) + + + + Confirmed (%1 confirmations) + Bestätigt (%1 Bestätigungen) + + + + Mined balance will be available when it matures in %n more block(s) + Erarbeiteter Betrag wird verfügbar sein, nachdem er noch %n weiteren Block reiftErarbeiteter Betrag wird verfügbar sein, nachdem er noch %n weitere Blöcke reift + + + + This block was not received by any other nodes and will probably not be accepted! + Dieser Block wurde von keinem anderen Knoten empfangen und wird wahrscheinlich nicht angenommen werden! + + + + Generated but not accepted + Generiert, jedoch nicht angenommen + + + + Received with + Empfangen über + + + + Received from + Empfangen von + + + + Sent to + Überwiesen an + + + + Payment to yourself + Eigenüberweisung + + + + Mined + Erarbeitet + + + + (n/a) + (k.A.) + + + + Transaction status. Hover over this field to show number of confirmations. + Transaktionsstatus. Fahren Sie mit der Maus über dieses Feld, um die Anzahl der Bestätigungen zu sehen. + + + + Date and time that the transaction was received. + Datum und Uhrzeit als die Transaktion empfangen wurde. + + + + Type of transaction. + Art der Transaktion + + + + Destination address of transaction. + Zieladresse der Transaktion + + + + Amount removed from or added to balance. + Der Betrag, der dem Kontostand abgezogen oder hinzugefügt wurde. + + + + TransactionView + + + + All + Alle + + + + Today + Heute + + + + This week + Diese Woche + + + + This month + Diesen Monat + + + + Last month + Letzten Monat + + + + This year + Dieses Jahr + + + + Range... + Zeitraum... + + + + Received with + Empfangen über + + + + Sent to + Überwiesen an + + + + To yourself + Eigenüberweisung + + + + Mined + Erarbeitet + + + + Other + Andere + + + + Enter address or label to search + Zu suchende Adresse oder Bezeichnung eingeben + + + + Min amount + Minimaler Betrag + + + + Copy address + Adresse kopieren + + + + Copy label + Bezeichnung kopieren + + + + Copy amount + Betrag kopieren + + + + Copy transaction ID + Transaktions-ID kopieren + + + + Edit label + Bezeichnung bearbeiten + + + + Show transaction details + Transaktionsdetails anzeigen + + + + Export Transaction Data + Transaktionen exportieren + + + + Comma separated file (*.csv) + Kommagetrennte-Datei (*.csv) + + + + Confirmed + Bestätigt + + + + Date + Datum + + + + Type + Typ + + + + Label + Bezeichnung + + + + Address + Adresse + + + + Amount + Betrag + + + + ID + ID + + + + Error exporting + Fehler beim Exportieren + + + + Could not write to file %1. + Konnte nicht in Datei %1 schreiben. + + + + Range: + Zeitraum: + + + + to + bis + + + + WalletModel + + + Send Coins + CryptoLottoTokens überweisen + + + + WalletView + + + &Export + E&xportieren + + + + Export the data in the current tab to a file + Daten der aktuellen Ansicht in eine Datei exportieren + + + + Backup Wallet + Brieftasche sichern + + + + Wallet Data (*.dat) + Brieftaschendaten (*.dat) + + + + Backup Failed + Sicherung fehlgeschlagen + + + + There was an error trying to save the wallet data to the new location. + Beim Speichern der Brieftaschendaten an die neue Position ist ein Fehler aufgetreten. + + + + Backup Successful + Sicherung erfolgreich + + + + The wallet data was successfully saved to the new location. + Speichern der Brieftaschendaten an die neue Position war erfolgreich. + + + + bitcoin-core + + + CryptoLottoToken version + CryptoLottoToken-Version + + + + Usage: + Benutzung: + + + + Send command to -server or CryptoLottoTokend + Befehl an -server oder CryptoLottoTokend senden + + + + List commands + Befehle auflisten + + + + Get help for a command + Hilfe zu einem Befehl erhalten + + + + Options: + Optionen: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Konfigurationsdatei festlegen (Standard: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + PID-Datei festlegen (Standard: CryptoLottoTokend.pid) + + + + Specify data directory + Datenverzeichnis festlegen + + + + Set database cache size in megabytes (default: 25) + Größe des Datenbankcaches in MB festlegen (Standard: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + <port> nach Verbindungen abhören (Standard: 22556 oder Testnetz: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Maximal <n> Verbindungen zu Gegenstellen aufrechterhalten (Standard: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Mit dem Knoten verbinden um Adressen von Gegenstellen abzufragen, danach trennen + + + + Specify your own public address + Die eigene öffentliche Adresse angeben + + + + Threshold for disconnecting misbehaving peers (default: 100) + Schwellenwert, um Verbindungen zu sich nicht konform verhaltenden Gegenstellen zu beenden (Standard: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Anzahl Sekunden, während denen sich nicht konform verhaltenden Gegenstellen die Wiederverbindung verweigert wird (Standard: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + Beim Einrichten des abzuhörenden RPC-Ports %u für IPv4 ist ein Fehler aufgetreten: %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + <port> nach JSON-RPC-Verbindungen abhören (Standard: 22555 oder Testnetz: 44555) + + + + Accept command line and JSON-RPC commands + Kommandozeilenbefehle und JSON-RPC-Befehle annehmen + + + + Run in the background as a daemon and accept commands + Als Hintergrunddienst starten und Befehle annehmen + + + + Use the test network + Das Testnetz verwenden + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Eingehende Verbindungen annehmen (Standard: 1, wenn nicht -proxy oder -connect) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + %s, Sie müssen den Wert rpcpasswort in dieser Konfigurationsdatei angeben: +%s +Es wird empfohlen das folgende Zufallspasswort zu verwenden: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(Sie müssen sich dieses Passwort nicht merken!) +Der Benutzername und das Passwort dürfen NICHT identisch sein. +Falls die Konfigurationsdatei nicht existiert, erzeugen Sie diese bitte mit Leserechten nur für den Dateibesitzer. +Es wird ebenfalls empfohlen alertnotify anzugeben, um im Problemfall benachrichtig zu werden; +zum Beispiel: alertnotify=echo %%s | mail -s \"CryptoLottoToken Alert\" admin@foo.com + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + Beim Einrichten des abzuhörenden RPC-Ports %u für IPv6 ist ein Fehler aufgetreten, es wird auf IPv4 zurückgegriffen: %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + An die angegebene Adresse binden und immer abhören. Für IPv6 [Host]:Port-Schreibweise verwenden + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + Datenverzeichnis %s kann nicht gesperrt werden. Evtl. wurde CryptoLottoToken bereits gestartet. + + + + 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. + Fehler: Die Transaktion wurde abgelehnt! Dies kann passieren, wenn einige CryptoLottoTokens aus Ihrer Brieftasche bereits ausgegeben wurden. Beispielsweise weil Sie eine Kopie Ihrer wallet.dat genutzt, die CryptoLottoTokens dort ausgegeben haben und dies daher in der derzeit aktiven Brieftasche nicht vermerkt ist. + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + Fehler: Diese Transaktion benötigt aufgrund ihres Betrags, ihrer Komplexität oder der Nutzung kürzlich erhaltener Zahlungen eine Transaktionsgebühr in Höhe von mindestens %s! + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + Kommando ausführen wenn ein relevanter Alarm empfangen wird (%s im Kommando wird durch die Nachricht ersetzt) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Kommando ausführen wenn sich eine Transaktion der Briefrasche verändert (%s im Kommando wird durch die TxID ersetzt) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Maximale Größe von "high-priority/low-fee"-Transaktionen in Byte festlegen (Standard: 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + Dies ist eine Vorab-Testversion - Verwendung auf eigene Gefahr - nicht für Mining- oder Handelsanwendungen nutzen! + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Warnung: -paytxfee ist auf einen sehr hohen Wert festgelegt! Dies ist die Gebühr die beim Senden einer Transaktion fällig wird. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + Warnung: Angezeigte Transaktionen sind evtl. nicht korrekt! Sie oder die anderen Knoten müssen unter Umständen (den Client) aktualisieren. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Warnung: Bitte korrigieren Sie die Datums- und Uhrzeiteinstellungen Ihres Computers, da CryptoLottoToken ansonsten nicht ordnungsgemäß funktionieren wird! + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Warnung: Lesen von wallet.dat fehlgeschlagen! Alle Schlüssel wurden korrekt gelesen, Transaktionsdaten bzw. Adressbucheinträge fehlen aber möglicherweise oder sind inkorrekt. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Warnung: wallet.dat beschädigt, Rettung erfolgreich! Original wallet.dat wurde als wallet.{Zeitstempel}.dat in %s gespeichert. Falls Ihr Kontostand oder Transaktionen nicht korrekt sind, sollten Sie von einer Datensicherung wiederherstellen. + + + + Attempt to recover private keys from a corrupt wallet.dat + Versucht private Schlüssel aus einer beschädigten wallet.dat wiederherzustellen + + + + Block creation options: + Blockerzeugungsoptionen: + + + + Connect only to the specified node(s) + Nur mit dem/den angegebenen Knoten verbinden + + + + Corrupted block database detected + Beschädigte Blockdatenbank erkannt + + + + Discover own IP address (default: 1 when listening and no -externalip) + Eigene IP-Adresse erkennen (Standard: 1, wenn abgehört wird und nicht -externalip) + + + + Do you want to rebuild the block database now? + Möchten Sie die Blockdatenbank nun neu aufbauen? + + + + Error initializing block database + Fehler beim Initialisieren der Blockdatenbank + + + + Error initializing wallet database environment %s! + Fehler beim Initialisieren der Brieftaschen-Datenbankumgebung %s! + + + + Error loading block database + Fehler beim Laden der Blockdatenbank + + + + Error opening block database + Fehler beim Öffnen der Blockdatenbank + + + + Error: Disk space is low! + Fehler: Zu wenig freier Laufwerksspeicherplatz! + + + + Error: Wallet locked, unable to create transaction! + Fehler: Brieftasche gesperrt, Transaktion kann nicht erstellt werden! + + + + Error: system error: + Fehler: Systemfehler: + + + + Failed to listen on any port. Use -listen=0 if you want this. + Fehler, es konnte kein Port abgehört werden. Wenn dies so gewünscht wird -listen=0 verwenden. + + + + Failed to read block info + Lesen der Blockinformationen fehlgeschlagen + + + + Failed to read block + Lesen des Blocks fehlgeschlagen + + + + Failed to sync block index + Synchronisation des Blockindex fehlgeschlagen + + + + Failed to write block index + Schreiben des Blockindex fehlgeschlagen + + + + Failed to write block info + Schreiben der Blockinformationen fehlgeschlagen + + + + Failed to write block + Schreiben des Blocks fehlgeschlagen + + + + Failed to write file info + Schreiben der Dateiinformationen fehlgeschlagen + + + + Failed to write to coin database + Schreiben in die Münzendatenbank fehlgeschlagen + + + + Failed to write transaction index + Schreiben des Transaktionsindex fehlgeschlagen + + + + Failed to write undo data + Schreiben der Rücksetzdaten fehlgeschlagen + + + + Find peers using DNS lookup (default: 1 unless -connect) + Gegenstellen via DNS-Namensauflösung finden (Standard: 1, außer bei -connect) + + + + Generate coins (default: 0) + CryptoLottoTokens generieren (Standard: 0) + + + + How many blocks to check at startup (default: 288, 0 = all) + Wieviele Blöcke sollen beim Starten geprüft werden (Standard: 288, 0 = alle) + + + + How thorough the block verification is (0-4, default: 3) + Wie gründlich soll die Blockprüfung sein (0-4, Standard: 3) + + + + Not enough file descriptors available. + Nicht genügend File-Deskriptoren verfügbar. + + + + Rebuild block chain index from current blk000??.dat files + Blockkettenindex aus aktuellen Dateien blk000??.dat wiederaufbauen + + + + Set the number of threads to service RPC calls (default: 4) + Maximale Anzahl an Threads zur Verarbeitung von RPC-Anfragen festlegen (Standard: 4) + + + + Verifying blocks... + Verifiziere Blöcke... + + + + Verifying wallet... + Verifiziere Brieftasche... + + + + Imports blocks from external blk000??.dat file + Blöcke aus externer Datei blk000??.dat importieren + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + Maximale Anzahl an Skript-Verifizierungs-Threads festlegen (bis zu 16, 0 = automatisch, <0 = soviele Kerne frei lassen, Standard: 0) + + + + Information + Hinweis + + + + Invalid -tor address: '%s' + Ungültige Adresse in -tor: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + Ungültiger Betrag für -minrelaytxfee=<amount>: '%s' + + + + Invalid amount for -mintxfee=<amount>: '%s' + Ungültiger Betrag für -mintxfee=<amount>: '%s' + + + + Maintain a full transaction index (default: 0) + Einen vollständigen Transaktionsindex pflegen (Standard: 0) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Maximale Größe, <n> * 1000 Byte, des Empfangspuffers pro Verbindung (Standard: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Maximale Größe, <n> * 1000 Byte, des Sendepuffers pro Verbindung (Standard: 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + Blockkette nur akzeptieren, wenn sie mit den integrierten Prüfpunkten übereinstimmt (Standard: 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Verbinde nur zu Knoten des Netztyps <net> (IPv4, IPv6 oder Tor) + + + + Output extra debugging information. Implies all other -debug* options + Ausgabe zusätzlicher Debugginginformationen. Beinhaltet alle anderen "-debug*"-Parameter + + + + Output extra network debugging information + Ausgabe zusätzlicher Netzwerk-Debugginginformationen + + + + Prepend debug output with timestamp (default: 1) + Der Debugausgabe einen Zeitstempel voranstellen + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + SSL-Optionen: (siehe CryptoLottoToken-Wiki für SSL-Installationsanweisungen) + + + + Select the version of socks proxy to use (4-5, default: 5) + SOCKS-Version des Proxies festlegen (4-5, Standard: 5) + + + + Send trace/debug info to console instead of debug.log file + Rückverfolgungs- und Debuginformationen an die Konsole senden anstatt sie in die Datei debug.log zu schreiben + + + + Send trace/debug info to debugger + Rückverfolgungs- und Debuginformationen an den Debugger senden + + + + Set maximum block size in bytes (default: 250000) + Maximale Blockgröße in Byte festlegen (Standard: 250000) + + + + Set minimum block size in bytes (default: 0) + Minimale Blockgröße in Byte festlegen (Standard: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Verkleinere Datei debug.log beim Start des Clients (Standard: 1, wenn kein -debug) + + + + Signing transaction failed + Signierung der Transaktion fehlgeschlagen + + + + Specify connection timeout in milliseconds (default: 5000) + Verbindungstimeout in Millisekunden festlegen (Standard: 5000) + + + + System error: + Systemfehler: + + + + Transaction amount too small + Transaktionsbetrag zu gering + + + + Transaction amounts must be positive + Transaktionsbeträge müssen positiv sein + + + + Transaction too large + Transaktion zu groß + + + + Use UPnP to map the listening port (default: 0) + UPnP verwenden, um die Portweiterleitung einzurichten (Standard: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + UPnP verwenden, um die Portweiterleitung einzurichten (Standard: 1, wenn abgehört wird) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Proxy verwenden, um versteckte Tor-Dienste zu erreichen (Standard: identisch mit -proxy) + + + + Username for JSON-RPC connections + Benutzername für JSON-RPC-Verbindungen + + + + Warning + Warnung + + + + Warning: This version is obsolete, upgrade required! + Warnung: Diese Version is veraltet, Aktualisierung erforderlich! + + + + You need to rebuild the databases using -reindex to change -txindex + Sie müssen die Datenbank mit Hilfe von -reindex neu aufbauen, um -txindex zu verändern. + + + + wallet.dat corrupt, salvage failed + wallet.dat beschädigt, Rettung fehlgeschlagen + + + + Password for JSON-RPC connections + Passwort für JSON-RPC-Verbindungen + + + + Allow JSON-RPC connections from specified IP address + JSON-RPC-Verbindungen von der angegebenen IP-Adresse erlauben + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Sende Befehle an Knoten <ip> (Standard: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Kommando ausführen wenn der beste Block wechselt (%s im Kommando wird durch den Hash des Blocks ersetzt) + + + + Upgrade wallet to latest format + Brieftasche auf das neueste Format aktualisieren + + + + Set key pool size to <n> (default: 100) + Größe des Schlüsselpools festlegen auf <n> (Standard: 100) + + + + Rescan the block chain for missing wallet transactions + Blockkette erneut nach fehlenden Transaktionen der Brieftasche durchsuchen + + + + Use OpenSSL (https) for JSON-RPC connections + OpenSSL (https) für JSON-RPC-Verbindungen verwenden + + + + Server certificate file (default: server.cert) + Serverzertifikat (Standard: server.cert) + + + + Server private key (default: server.pem) + Privater Serverschlüssel (Standard: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Akzeptierte Chiffren (Standard: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Dieser Hilfetext + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Kann auf diesem Computer nicht an %s binden (von bind zurückgegebener Fehler %d, %s) + + + + Connect through socks proxy + Verbindung über SOCKS-Proxy herstellen + + + + Allow DNS lookups for -addnode, -seednode and -connect + Erlaube DNS-Namensauflösung für -addnode, -seednode und -connect + + + + Loading addresses... + Lade Adressen... + + + + Error loading wallet.dat: Wallet corrupted + Fehler beim Laden von wallet.dat: Brieftasche beschädigt + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Fehler beim Laden von wallet.dat: Brieftasche benötigt neuere Version von CryptoLottoToken + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Brieftasche musste neu geschrieben werden: Starten Sie CryptoLottoToken zur Fertigstellung neu + + + + Error loading wallet.dat + Fehler beim Laden von wallet.dat (Brieftasche) + + + + Invalid -proxy address: '%s' + Ungültige Adresse in -proxy: '%s' + + + + Unknown network specified in -onlynet: '%s' + Unbekannter Netztyp in -onlynet angegeben: '%s' + + + + Unknown -socks proxy version requested: %i + Unbekannte Proxyversion in -socks angefordert: %i + + + + Cannot resolve -bind address: '%s' + Kann Adresse in -bind nicht auflösen: '%s' + + + + Cannot resolve -externalip address: '%s' + Kann Adresse in -externalip nicht auflösen: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + Ungültiger Betrag für -paytxfee=<amount>: '%s' + + + + Invalid amount + Ungültiger Betrag + + + + Insufficient funds + Unzureichender Kontostand + + + + Loading block index... + Lade Blockindex... + + + + Add a node to connect to and attempt to keep the connection open + Mit dem Knoten verbinden und versuchen die Verbindung aufrecht zu halten + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Kann auf diesem Computer nicht an %s binden. Evtl. wurde CryptoLottoToken bereits gestartet. + + + + Fee per KB to add to transactions you send + Gebühr pro KB, die gesendeten Transaktionen hinzugefügt wird + + + + Loading wallet... + Lade Brieftasche... + + + + Cannot downgrade wallet + Brieftasche kann nicht auf eine ältere Version herabgestuft werden + + + + Cannot write default address + Standardadresse kann nicht geschrieben werden + + + + Rescanning... + Durchsuche erneut... + + + + Done loading + Laden abgeschlossen + + + + To use the %s option + Zur Nutzung der %s Option + + + + Error + Fehler + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + Sie müssen den Wert rpcpassword=<passwort> in der Konfigurationsdatei angeben: +%s +Falls die Konfigurationsdatei nicht existiert, erzeugen Sie diese bitte mit Leserechten nur für den Dateibesitzer. + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_el_GR.qm b/src/qt/locale/bitcoin_el_GR.qm new file mode 100644 index 0000000..62860c4 Binary files /dev/null and b/src/qt/locale/bitcoin_el_GR.qm differ diff --git a/src/qt/locale/bitcoin_el_GR.ts b/src/qt/locale/bitcoin_el_GR.ts new file mode 100644 index 0000000..eb1466f --- /dev/null +++ b/src/qt/locale/bitcoin_el_GR.ts @@ -0,0 +1,2941 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + Σχετικά με το CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + Έκδοση CryptoLottoToken + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + Copyright + Πνευματική ιδιοκτησία + + + + The CryptoLottoToken developers + Οι CryptoLottoToken προγραμματιστές + + + + AddressBookPage + + + Address Book + Βιβλίο Διευθύνσεων + + + + Double-click to edit address or label + Διπλό-κλικ για επεξεργασία της διεύθυνσης ή της ετικέτας + + + + Create a new address + Δημιούργησε νέα διεύθυνση + + + + Copy the currently selected address to the system clipboard + Αντέγραψε την επιλεγμένη διεύθυνση στο πρόχειρο του συστήματος + + + + &New Address + &Νέα διεύθυνση + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Αυτές είναι οι CryptoLottoToken διευθύνσεις σας για να λαμβάνετε πληρωμές. Δίνοντας μία ξεχωριστή διεύθυνση σε κάθε αποστολέα, θα μπορείτε να ελέγχετε ποιος σας πληρώνει. + + + + &Copy Address + &Αντιγραφή διεύθυνσης + + + + Show &QR Code + Δείξε &QR κωδικα + + + + Sign a message to prove you own a CryptoLottoToken address + Υπογράψτε ένα μήνυμα για ν' αποδείξετε πως σας ανήκει μια συγκεκριμένη διεύθυνση CryptoLottoToken + + + + Sign &Message + &Υπέγραψε το μήνυμα + + + + Delete the currently selected address from the list + Αντιγραφη της επιλεγμενης διεύθυνσης στο πρόχειρο του συστηματος + + + + Export the data in the current tab to a file + Εξαγωγή δεδομένων καρτέλας σε αρχείο + + + + &Export + &Εξαγωγή + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Υπογράψτε ένα μήνυμα για ν' αποδείξετε πως ανήκει μια συγκεκριμένη διεύθυνση CryptoLottoToken + + + + &Verify Message + &Επιβεβαίωση μηνύματος + + + + &Delete + &Διαγραφή + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + Αυτές είναι οι CryptoLottoToken διευθύνσεις σας για να λαμβάνετε πληρωμές. Δίνοντας μία ξεχωριστή διεύθυνση σε κάθε αποστολέα, θα μπορείτε να ελέγχετε ποιος σας πληρώνει. + + + + Copy &Label + Αντιγραφή &επιγραφής + + + + &Edit + &Επεξεργασία + + + + Send &Coins + Αποστολή νομισμάτων + + + + Export Address Book Data + Εξαγωγή Δεδομενων Βιβλίου Διευθύνσεων + + + + Comma separated file (*.csv) + Αρχείο οριοθετημένο με κόμματα (*.csv) + + + + Error exporting + Εξαγωγή λαθών + + + + Could not write to file %1. + Αδυναμία εγγραφής στο αρχείο %1. + + + + AddressTableModel + + + Label + Ετικέτα + + + + Address + Διεύθυνση + + + + (no label) + (χωρίς ετικέτα) + + + + AskPassphraseDialog + + + Passphrase Dialog + Φράση πρόσβασης + + + + Enter passphrase + Βάλτε κωδικό πρόσβασης + + + + New passphrase + Νέος κωδικός πρόσβασης + + + + Repeat new passphrase + Επανέλαβε τον νέο κωδικό πρόσβασης + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Εισάγετε τον νέο κωδικό πρόσβασης στον πορτοφόλι <br/> Παρακαλώ χρησιμοποιείστε ένα κωδικό με <b> 10 ή περισσότερους τυχαίους χαρακτήρες</b> ή <b> οχτώ ή παραπάνω λέξεις</b>. + + + + Encrypt wallet + Κρυπτογράφησε το πορτοφόλι + + + + This operation needs your wallet passphrase to unlock the wallet. + Αυτη η ενεργεία χρειάζεται τον κωδικό του πορτοφολιού για να ξεκλειδώσει το πορτοφόλι. + + + + Unlock wallet + Ξεκλειδωσε το πορτοφολι + + + + This operation needs your wallet passphrase to decrypt the wallet. + Αυτη η ενεργεια χρειάζεται τον κωδικο του πορτοφολιου για να αποκρυπτογραφησειι το πορτοφολι. + + + + Decrypt wallet + Αποκρυπτογράφησε το πορτοφολι + + + + Change passphrase + Άλλαξε κωδικο πρόσβασης + + + + Enter the old and new passphrase to the wallet. + Εισάγετε τον παλιό και τον νεο κωδικο στο πορτοφολι. + + + + Confirm wallet encryption + Επιβεβαίωσε την κρυπτογραφηση του πορτοφολιού + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Προσοχη: Εαν κρυπτογραφησεις το πορτοφολι σου και χάσεις τον κωδικο σου θα χάσεις <b> ΟΛΑ ΣΟΥ ΤΑ CryptoLottoTokenS</b>! +Είσαι σίγουρος ότι θέλεις να κρυπτογραφησεις το πορτοφολι; + + + + Are you sure you wish to encrypt your wallet? + Είστε σίγουροι ότι θέλετε να κρυπτογραφήσετε το πορτοφόλι σας; + + + + 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. + ΣΗΜΑΝΤΙΚΟ: Τα προηγούμενα αντίγραφα ασφαλείας που έχετε κάνει από το αρχείο του πορτοφόλιου σας θα πρέπει να αντικατασταθουν με το νέο που δημιουργείται, κρυπτογραφημένο αρχείο πορτοφόλιου. Για λόγους ασφαλείας, τα προηγούμενα αντίγραφα ασφαλείας του μη κρυπτογραφημένου αρχείου πορτοφόλιου θα καταστουν άχρηστα μόλις αρχίσετε να χρησιμοποιείτε το νέο κρυπτογραφημένο πορτοφόλι. + + + + + Warning: The Caps Lock key is on! + Προσοχη: το πλήκτρο Caps Lock είναι ενεργο. + + + + + Wallet encrypted + Κρυπτογραφημενο πορτοφολι + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + Το CryptoLottoToken θα κλεισει τώρα για να τελειώσει την διαδικασία κρυπτογραφησης. Θυμησου ότι κρυπτογραφώντας το πορτοφολι σου δεν μπορείς να προστατέψεις πλήρως τα CryptoLottoTokens σου από κλοπή στην περίπτωση όπου μολυνθεί ο υπολογιστής σου με κακόβουλο λογισμικο. + + + + + + + Wallet encryption failed + Η κρυπτογραφηση του πορτοφολιού απέτυχε + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Η κρυπτογράφηση του πορτοφολιού απέτυχε λογω εσωτερικού σφάλματος. Το πορτοφολι δεν κρυπτογραφηθηκε. + + + + + The supplied passphrases do not match. + Οι εισαχθέντες κωδικοί δεν ταιριάζουν. + + + + Wallet unlock failed + το ξεκλείδωμα του πορτοφολιού απέτυχε + + + + + + The passphrase entered for the wallet decryption was incorrect. + Ο κωδικος που εισήχθη για την αποκρυπτογραφηση του πορτοφολιού ήταν λαθος. + + + + Wallet decryption failed + Η αποκρυπτογραφηση του πορτοφολιού απέτυχε + + + + Wallet passphrase was successfully changed. + Ο κωδικος του πορτοφολιού άλλαξε με επιτυχία. + + + + BitcoinGUI + + + Sign &message... + Υπογραφή &Μηνύματος... + + + + Synchronizing with network... + Συγχρονισμός με το δίκτυο... + + + + &Overview + &Επισκόπηση + + + + Show general overview of wallet + Εμφάνισε γενική εικονα του πορτοφολιού + + + + &Transactions + &Συναλλαγές + + + + Browse transaction history + Περιήγηση στο ιστορικο συνναλαγων + + + + Edit the list of stored addresses and labels for sending + Εξεργασια της λιστας των αποθηκευμενων διευθύνσεων και ετικετων + + + + Show the list of addresses for receiving payments + Εμφάνισε την λίστα των διευθύνσεων για την παραλαβή πληρωμων + + + + E&xit + Έ&ξοδος + + + + Quit application + Εξοδος από την εφαρμογή + + + + Show information about CryptoLottoToken + Εμφάνισε πληροφορίες σχετικά με το CryptoLottoToken + + + + About &Qt + Σχετικά με &Qt + + + + Show information about Qt + Εμφάνισε πληροφορίες σχετικά με Qt + + + + &Options... + &Επιλογές... + + + + &Encrypt Wallet... + &Κρυπτογράφησε το πορτοφόλι + + + + &Backup Wallet... + &Αντίγραφο ασφαλείας του πορτοφολιού + + + + &Change Passphrase... + &Άλλαξε κωδικο πρόσβασης + + + + Importing blocks from disk... + Εισαγωγή μπλοκ από τον σκληρο δίσκο ... + + + + Reindexing blocks on disk... + Φόρτωση ευρετηρίου μπλοκ στον σκληρο δισκο... + + + + Send coins to a CryptoLottoToken address + Στείλε νομισματα σε μια διεύθυνση CryptoLottoToken + + + + Modify configuration options for CryptoLottoToken + Επεργασία ρυθμισεων επιλογών για το CryptoLottoToken + + + + Backup wallet to another location + Δημιουργία αντιγράφου ασφαλείας πορτοφολιού σε άλλη τοποθεσία + + + + Change the passphrase used for wallet encryption + Αλλαγή του κωδικού κρυπτογράφησης του πορτοφολιού + + + + &Debug window + &Παράθυρο αποσφαλμάτωσης + + + + Open debugging and diagnostic console + Άνοιγμα κονσόλας αποσφαλμάτωσης και διαγνωστικών + + + + &Verify message... + &Επιβεβαίωση μηνύματος + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Πορτοφόλι + + + + &Send + &Αποστολή + + + + &Receive + &Παραλαβή + + + + &Addresses + &Διεύθυνσεις + + + + &About CryptoLottoToken + &Σχετικα:CryptoLottoToken + + + + &Show / Hide + &Εμφάνισε/Κρύψε + + + + Show or hide the main Window + Εμφάνιση ή αποκρύψη του κεντρικου παράθυρου + + + + Encrypt the private keys that belong to your wallet + Κρυπτογραφήστε τα ιδιωτικά κλειδιά που ανήκουν στο πορτοφόλι σας + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Υπογράψτε ένα μήνυμα για να βεβαιώσετε πως είστε ο κάτοχος αυτής της διεύθυνσης + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Υπογράψτε ένα μήνυμα για ν' αποδείξετε πως ανήκει μια συγκεκριμένη διεύθυνση CryptoLottoToken + + + + &File + &Αρχείο + + + + &Settings + &Ρυθμίσεις + + + + &Help + &Βοήθεια + + + + Tabs toolbar + Εργαλειοθήκη καρτελών + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + Πελάτης CryptoLottoToken + + + + %n active connection(s) to CryptoLottoToken network + %n ενεργή σύνδεση στο δίκτυο CryptoLottoToken%n ενεργές συνδέσεις στο δίκτυο Βitcoin + + + + No block source available... + Η πηγή του μπλοκ δεν ειναι διαθέσιμη... + + + + Processed %1 of %2 (estimated) blocks of transaction history. + Μεταποιημένα %1 απο % 2 (κατ 'εκτίμηση) μπλοκ της ιστορίας της συναλλαγής. + + + + Processed %1 blocks of transaction history. + Έγινε λήψη %1 μπλοκ ιστορικού συναλλαγών + + + + %n hour(s) + %n ώρες %n ώρες + + + + %n day(s) + %n ημέρες %n ημέρες + + + + %n week(s) + %n εβδομαδες%n εβδομαδες + + + + %1 behind + %1 πίσω + + + + Last received block was generated %1 ago. + Το τελευταίο μπλοκ που ελήφθη δημιουργήθηκε %1 πριν. + + + + Transactions after this will not yet be visible. + Οι συναλλαγές μετά από αυτό δεν θα είναι ακόμη ορατες. + + + + Error + Σφάλμα + + + + Warning + Προειδοποίηση + + + + Information + Πληροφορία + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + Η συναλλαγή ξεπερνάει το όριο. +Μπορεί να ολοκληρωθεί με μια αμοιβή των %1, η οποία αποδίδεται στους κόμβους που επεξεργάζονται τις συναλλαγές και βοηθούν στην υποστήριξη του δικτύου. +Θέλετε να συνεχίσετε; + + + + Up to date + Ενημερωμένο + + + + Catching up... + Ενημέρωση... + + + + Confirm transaction fee + Επιβεβαίωση αμοιβής συναλλαγής + + + + Sent transaction + Η συναλλαγή απεστάλη + + + + Incoming transaction + Εισερχόμενη συναλλαγή + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Ημερομηνία: %1 +Ποσό: %2 +Τύπος: %3 +Διεύθυνση: %4 + + + + + + URI handling + Χειρισμός URI + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + Το URI δεν μπορεί να αναλυθεί! Αυτό μπορεί να προκληθεί από μια μη έγκυρη διεύθυνση CryptoLottoToken ή ακατάλληλη παραμέτρο URI. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Το πορτοφόλι είναι <b>κρυπτογραφημένο</b> και <b>ξεκλείδωτο</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Το πορτοφόλι είναι <b>κρυπτογραφημένο</b> και <b>κλειδωμένο</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + Παρουσιάστηκε ανεπανόρθωτο σφάλμα. Το CryptoLottoToken δεν μπορεί πλέον να συνεχίσει με ασφάλεια και θα τερματισθει. + + + + ClientModel + + + Network Alert + Ειδοποίηση Δικτύου + + + + EditAddressDialog + + + Edit Address + Επεξεργασία Διεύθυνσης + + + + &Label + &Επιγραφή + + + + The label associated with this address book entry + Η επιγραφή που σχετίζεται με αυτή την καταχώρηση του βιβλίου διευθύνσεων + + + + &Address + &Διεύθυνση + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Η διεύθυνση που σχετίζεται με αυτή την καταχώρηση του βιβλίου διευθύνσεων. Μπορεί να τροποποιηθεί μόνο για τις διευθύνσεις αποστολής. + + + + New receiving address + Νέα διεύθυνση λήψης + + + + New sending address + Νέα διεύθυνση αποστολής + + + + Edit receiving address + Επεξεργασία διεύθυνσης λήψης + + + + Edit sending address + Επεξεργασία διεύθυνσης αποστολής + + + + The entered address "%1" is already in the address book. + Η διεύθυνση "%1" βρίσκεται ήδη στο βιβλίο διευθύνσεων. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + Η διεύθυνση "%1" δεν είναι έγκυρη CryptoLottoToken διεύθυνση. + + + + Could not unlock wallet. + Δεν είναι δυνατό το ξεκλείδωμα του πορτοφολιού. + + + + New key generation failed. + Η δημιουργία νέου κλειδιού απέτυχε. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-qt + + + + version + έκδοση + + + + Usage: + Χρήση: + + + + command-line options + επιλογής γραμμής εντολών + + + + UI options + επιλογές UI + + + + Set language, for example "de_DE" (default: system locale) + Όρισε γλώσσα, για παράδειγμα "de_DE"(προεπιλογή:τοπικές ρυθμίσεις) + + + + Start minimized + Έναρξη ελαχιστοποιημένο + + + + Show splash screen on startup (default: 1) + Εμφάνισε την οθόνη εκκίνησης κατά την εκκίνηση(προεπιλογή:1) + + + + OptionsDialog + + + Options + Ρυθμίσεις + + + + &Main + &Κύριο + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + Η προαιρετική αμοιβή για κάθε kB επισπεύδει την επεξεργασία των συναλλαγών σας. Οι περισσότερες συναλλαγές είναι 1 kB. + + + + Pay transaction &fee + Αμοιβή &συναλλαγής + + + + Automatically start CryptoLottoToken after logging in to the system. + Αυτόματη εκκίνηση του CryptoLottoToken μετά την εισαγωγή στο σύστημα + + + + &Start CryptoLottoToken on system login + &Έναρξη του Βιtcoin κατά την εκκίνηση του συστήματος + + + + Reset all client options to default. + Επαναφορα όλων των επιλογων του πελάτη σε default. + + + + &Reset Options + Επαναφορα ρυθμίσεων + + + + &Network + &Δίκτυο + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Αυτόματο άνοιγμα των θυρών CryptoLottoToken στον δρομολογητή. Λειτουργεί μόνο αν ο δρομολογητής σας υποστηρίζει τη λειτουργία UPnP. + + + + Map port using &UPnP + Απόδοση θυρών με χρήστη &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Σύνδεση στο CryptoLottoToken δίκτυο μέσω διαμεσολαβητή SOCKS4 (π.χ. για σύνδεση μέσω Tor) + + + + &Connect through SOCKS proxy: + &Σύνδεση μέσω διαμεσολαβητή SOCKS + + + + Proxy &IP: + &IP διαμεσολαβητή: + + + + IP address of the proxy (e.g. 127.0.0.1) + Διεύθυνση IP του διαμεσολαβητή (π.χ. 127.0.0.1) + + + + &Port: + &Θύρα: + + + + Port of the proxy (e.g. 9050) + Θύρα διαμεσολαβητή + + + + SOCKS &Version: + SOCKS &Έκδοση: + + + + SOCKS version of the proxy (e.g. 5) + SOCKS εκδοση του διαμεσολαβητη (e.g. 5) + + + + &Window + &Παράθυρο + + + + Show only a tray icon after minimizing the window. + Εμφάνιση μόνο εικονιδίου στην περιοχή ειδοποιήσεων κατά την ελαχιστοποίηση + + + + &Minimize to the tray instead of the taskbar + &Ελαχιστοποίηση στην περιοχή ειδοποιήσεων αντί της γραμμής εργασιών + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Ελαχιστοποίηση αντί για έξοδο κατά το κλείσιμο του παραθύρου + + + + M&inimize on close + Ε&λαχιστοποίηση κατά το κλείσιμο + + + + &Display + %Απεικόνιση + + + + User Interface &language: + Γλώσσα περιβάλλοντος εργασίας: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + Εδώ μπορεί να ρυθμιστεί η γλώσσα διεπαφής χρήστη. Αυτή η ρύθμιση θα ισχύσει μετά την επανεκκίνηση του CryptoLottoToken. + + + + &Unit to show amounts in: + &Μονάδα μέτρησης: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Διαλέξτε την προεπιλεγμένη υποδιαίρεση που θα εμφανίζεται όταν στέλνετε νομίσματα. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Επιλέξτε αν θέλετε να εμφανίζονται οι διευθύνσεις CryptoLottoToken στη λίστα συναλλαγών. + + + + &Display addresses in transaction list + Εμφάνιση διευθύνσεων στη λίστα συναλλαγών + + + + &OK + &ΟΚ + + + + &Cancel + &Ακύρωση + + + + &Apply + &Εφαρμογή + + + + default + προεπιλογή + + + + Confirm options reset + Επιβεβαιώση των επιλογων επαναφοράς + + + + Some settings may require a client restart to take effect. + Για ορισμένες ρυθμίσεις πρεπει η επανεκκίνηση να τεθεί σε ισχύ. + + + + Do you want to proceed? + Θέλετε να προχωρήσετε; + + + + + Warning + Προειδοποίηση + + + + + This setting will take effect after restarting CryptoLottoToken. + Αυτή η ρύθμιση θα ισχύσει μετά την επανεκκίνηση του CryptoLottoToken. + + + + The supplied proxy address is invalid. + Δεν είναι έγκυρη η διεύθυνση διαμεσολαβητή + + + + OverviewPage + + + Form + Φόρμα + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + Οι πληροφορίες που εμφανίζονται μπορεί να είναι ξεπερασμένες. Το πορτοφόλι σας συγχρονίζεται αυτόματα με το δίκτυο CryptoLottoToken μετά από μια σύνδεση, αλλά αυτή η διαδικασία δεν έχει ακόμη ολοκληρωθεί. + + + + Balance: + Υπόλοιπο + + + + Unconfirmed: + Ανεπιβεβαίωτες + + + + Wallet + Πορτοφόλι + + + + Immature: + Ανώριμος + + + + Mined balance that has not yet matured + Εξορυγμενο υπόλοιπο που δεν έχει ακόμα ωριμάσει + + + + <b>Recent transactions</b> + <b>Πρόσφατες συναλλαγές</b> + + + + Your current balance + Το τρέχον υπόλοιπο + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Το άθροισμα των συναλλαγών που δεν έχουν ακόμα επιβεβαιωθεί και δεν προσμετρώνται στο τρέχον υπόλοιπό σας + + + + + out of sync + εκτός συγχρονισμού + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + Δεν είναι δυνατή η εκκίνηση του CryptoLottoToken: click-to-pay handler + + + + QRCodeDialog + + + QR Code Dialog + Κώδικας QR + + + + Request Payment + Αίτηση πληρωμής + + + + Amount: + Ποσό: + + + + Label: + Επιγραφή: + + + + Message: + Μήνυμα: + + + + &Save As... + &Αποθήκευση ως... + + + + Error encoding URI into QR Code. + Σφάλμα κατά την κωδικοποίηση του URI σε κώδικα QR + + + + The entered amount is invalid, please check. + Το αναγραφόμενο ποσό δεν είναι έγκυρο, παρακαλούμε να το ελέγξετε. + + + + Resulting URI too long, try to reduce the text for label / message. + Το αποτέλεσμα της διεύθυνσης είναι πολύ μεγάλο. Μειώστε το μέγεθος για το κείμενο της ετικέτας/ μηνύματος. + + + + Save QR Code + Αποθήκευση κώδικα QR + + + + PNG Images (*.png) + Εικόνες PNG (*.png) + + + + RPCConsole + + + Client name + Όνομα Πελάτη + + + + + + + + + + + + + N/A + Μη διαθέσιμο + + + + Client version + Έκδοση Πελάτη + + + + &Information + &Πληροφορία + + + + Using OpenSSL version + Χρησιμοποιηση της OpenSSL εκδοσης + + + + Startup time + Χρόνος εκκίνησης + + + + Network + Δίκτυο + + + + Number of connections + Αριθμός συνδέσεων + + + + On testnet + Στο testnet + + + + Block chain + αλυσίδα εμποδισμού + + + + Current number of blocks + Τρέχον αριθμός μπλοκ + + + + Estimated total blocks + Κατ' εκτίμηση συνολικά μπλοκς + + + + Last block time + Χρόνος τελευταίου μπλοκ + + + + &Open + &Άνοιγμα + + + + Command-line options + επιλογής γραμμής εντολών + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Εμφανιση του CryptoLottoToken-Qt μήνυματος βοήθειας για να πάρετε μια λίστα με τις πιθανές επιλογές CryptoLottoToken γραμμής εντολών. + + + + &Show + &Εμφάνιση + + + + &Console + &Κονσόλα + + + + Build date + Ημερομηνία κατασκευής + + + + CryptoLottoToken - Debug window + CryptoLottoToken - Παράθυρο αποσφαλμάτωσης + + + + CryptoLottoToken Core + CryptoLottoToken Core + + + + Debug log file + Αρχείο καταγραφής εντοπισμού σφαλμάτων + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + Ανοίξτε το αρχείο καταγραφής εντοπισμού σφαλμάτων από τον τρέχοντα κατάλογο δεδομένων. Αυτό μπορεί να πάρει μερικά δευτερόλεπτα για τα μεγάλα αρχεία καταγραφής. + + + + Clear console + Καθαρισμός κονσόλας + + + + Welcome to the CryptoLottoToken RPC console. + Καλώς ήρθατε στην CryptoLottoToken RPC κονσόλα. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Χρησιμοποιήστε το πάνω και κάτω βέλος για να περιηγηθείτε στο ιστορικο, και <b>Ctrl-L</b> για εκκαθαριση οθονης. + + + + Type <b>help</b> for an overview of available commands. + Γράψτε <b>βοήθεια</b> για μια επισκόπηση των διαθέσιμων εντολών + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Αποστολή νομισμάτων + + + + Send to multiple recipients at once + Αποστολή σε πολλούς αποδέκτες ταυτόχρονα + + + + Add &Recipient + &Προσθήκη αποδέκτη + + + + Remove all transaction fields + Διαγραφή όλων των πεδίων συναλλαγής + + + + Clear &All + Καθαρισμός &Όλων + + + + Balance: + Υπόλοιπο: + + + + 123.456 BTC + 123,456 BTC + + + + Confirm the send action + Επιβεβαίωση αποστολής + + + + S&end + Αποστολη + + + + <b>%1</b> to %2 (%3) + <b>%1</b> σε %2 (%3) + + + + Confirm send coins + Επιβεβαίωση αποστολής νομισμάτων + + + + Are you sure you want to send %1? + Είστε βέβαιοι για την αποστολή %1; + + + + and + και + + + + The recipient address is not valid, please recheck. + Η διεύθυνση του αποδέκτη δεν είναι σωστή. Παρακαλώ ελέγξτε ξανά. + + + + The amount to pay must be larger than 0. + Το ποσό πληρωμής πρέπει να είναι μεγαλύτερο από 0. + + + + The amount exceeds your balance. + Το ποσό ξεπερνάει το διαθέσιμο υπόλοιπο + + + + The total exceeds your balance when the %1 transaction fee is included. + Το σύνολο υπερβαίνει το υπόλοιπό σας όταν συμπεριληφθεί και η αμοιβή %1 + + + + Duplicate address found, can only send to each address once per send operation. + Βρέθηκε η ίδια διεύθυνση δύο φορές. Επιτρέπεται μία μόνο εγγραφή για κάθε διεύθυνση, σε κάθε διαδικασία αποστολής. + + + + Error: Transaction creation failed! + Σφάλμα: Η δημιουργία της συναλλαγής απέτυχε + + + + 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. + Σφάλμα: Η συναλλαγή απερρίφθη. Αυτό ενδέχεται να συμβαίνει αν κάποια από τα νομίσματα έχουν ήδη ξοδευθεί, όπως αν χρησιμοποιήσατε αντίγραφο του wallet.dat και τα νομίσματα ξοδεύθηκαν εκεί. + + + + SendCoinsEntry + + + Form + Φόρμα + + + + A&mount: + &Ποσό: + + + + Pay &To: + Πληρωμή &σε: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Διεύθυνση αποστολής της πληρωμής (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + Εισάγετε μια επιγραφή για αυτή τη διεύθυνση ώστε να καταχωρηθεί στο βιβλίο διευθύνσεων + + + + &Label: + &Επιγραφή + + + + Choose address from address book + Επιλογή διεύθυνσης από το βιβλίο διευθύνσεων + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Επικόλληση διεύθυνσης από το πρόχειρο + + + + Alt+P + Alt+P + + + + Remove this recipient + Αφαίρεση αποδέκτη + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Εισάγετε μια διεύθυνση CryptoLottoToken (π.χ. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Υπογραφές - Είσοδος / Επαλήθευση μήνυματος + + + + &Sign Message + &Υπογραφή Μηνύματος + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Μπορείτε να υπογράφετε μηνύματα με τις διευθύνσεις σας, ώστε ν' αποδεικνύετε πως αυτές σας ανήκουν. Αποφεύγετε να υπογράφετε κάτι αόριστο καθώς ενδέχεται να εξαπατηθείτε. Υπογράφετε μόνο πλήρης δηλώσεις με τις οποίες συμφωνείτε. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Εισάγετε μια διεύθυνση CryptoLottoToken (π.χ. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Επιλογή διεύθυνσης από το βιβλίο διευθύνσεων + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Επικόλληση διεύθυνσης από το βιβλίο διευθύνσεων + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Εισάγετε εδώ το μήνυμα που θέλετε να υπογράψετε + + + + Signature + Υπογραφή + + + + Copy the current signature to the system clipboard + Αντέγραφη της επιλεγμενης διεύθυνσης στο πρόχειρο του συστηματος + + + + Sign the message to prove you own this CryptoLottoToken address + Υπογράψτε ένα μήνυμα για ν' αποδείξετε πως σας ανήκει μια συγκεκριμένη διεύθυνση CryptoLottoToken + + + + Sign &Message + Υπογραφη μήνυματος + + + + Reset all sign message fields + Επαναφορά όλων των πεδίων μήνυματος + + + + + Clear &All + Καθαρισμός &Όλων + + + + &Verify Message + &Επιβεβαίωση μηνύματος + + + + Enter the signing 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. + Πληκτρολογήστε την υπογραφή διεύθυνσης, μήνυμα (βεβαιωθείτε ότι έχετε αντιγράψει τις αλλαγές γραμμής, κενά, tabs, κ.λπ. ακριβώς) και την υπογραφή παρακάτω, για να ελέγξει το μήνυμα. Να είστε προσεκτικοί για να μην διαβάσετε περισσότερα στην υπογραφή ό, τι είναι στην υπογραφή ίδιο το μήνυμα , για να μην εξαπατηθούν από έναν άνθρωπο -in - the-middle επίθεση. + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Εισάγετε μια διεύθυνση CryptoLottoToken (π.χ. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Υπογράψτε ένα μήνυμα για ν' αποδείξετε πως υπογραφθηκε απο μια συγκεκριμένη διεύθυνση CryptoLottoToken + + + + Verify &Message + Επιβεβαίωση μηνύματος + + + + Reset all verify message fields + Επαναφορά όλων επαλήθευμενων πεδίων μήνυματος + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Εισάγετε μια διεύθυνση CryptoLottoToken (π.χ. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Κάντε κλικ στο "Υπογραφή Μηνύματος" για να λάβετε την υπογραφή + + + + Enter CryptoLottoToken signature + Εισαγωγή υπογραφής CryptoLottoToken + + + + + The entered address is invalid. + Η διεύθυνση που εισήχθη είναι λάθος. + + + + + + + Please check the address and try again. + Παρακαλούμε ελέγξτε την διεύθυνση και δοκιμάστε ξανά. + + + + + The entered address does not refer to a key. + Η διεύθυνση που έχει εισαχθεί δεν αναφέρεται σε ένα πλήκτρο. + + + + Wallet unlock was cancelled. + το ξεκλείδωμα του πορτοφολιού απέτυχε + + + + Private key for the entered address is not available. + Το προσωπικό κλειδί εισαγμενης διευθυνσης δεν είναι διαθέσιμο. + + + + Message signing failed. + Η υπογραφή του μηνύματος απέτυχε. + + + + Message signed. + Μήνυμα υπεγράφη. + + + + The signature could not be decoded. + Η υπογραφή δεν μπόρεσε να αποκρυπτογραφηθεί. + + + + + Please check the signature and try again. + Παρακαλούμε ελέγξτε την υπογραφή και δοκιμάστε ξανά. + + + + The signature did not match the message digest. + Η υπογραφή δεν ταιριάζει με το μήνυμα. + + + + Message verification failed. + Η επιβεβαίωση του μηνύματος απέτυχε + + + + Message verified. + Μήνυμα επιβεβαιώθηκε. + + + + SplashScreen + + + The CryptoLottoToken developers + Οι CryptoLottoToken προγραμματιστές + + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + Ανοιχτό μέχρι %1 + + + + %1/offline + %1/χωρίς σύνδεση; + + + + %1/unconfirmed + %1/χωρίς επιβεβαίωση + + + + %1 confirmations + %1 επιβεβαιώσεις + + + + Status + Κατάσταση + + + + , broadcast through %n node(s) + , έχει μεταδοθεί μέσω %n κόμβων, έχει μεταδοθεί μέσω %n κόμβων + + + + Date + Ημερομηνία + + + + Source + Πηγή + + + + Generated + Δημιουργία + + + + + From + Από + + + + + + To + Προς + + + + + own address + δική σας διεύθυνση + + + + label + eπιγραφή + + + + + + + + Credit + Πίστωση + + + + matures in %n more block(s) + ωρίμανση σε %n επιπλέον μπλοκωρίμανση σε %n επιπλέον μπλοκ + + + + not accepted + μη αποδεκτό + + + + + + + Debit + Debit + + + + Transaction fee + Τέλος συναλλαγής + + + + Net amount + Καθαρό ποσό + + + + Message + Μήνυμα + + + + Comment + Σχόλιο: + + + + Transaction ID + ID Συναλλαγής: + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Πρέπει να περιμένετε 120 μπλοκ πριν μπορέσετε να χρησιμοποιήσετε τα νομίσματα που έχετε δημιουργήσει. Το μπλοκ που δημιουργήσατε μεταδόθηκε στο δίκτυο για να συμπεριληφθεί στην αλυσίδα των μπλοκ. Αν δεν μπει σε αυτή θα μετατραπεί σε "μη αποδεκτό" και δε θα μπορεί να καταναλωθεί. Αυτό συμβαίνει σπάνια όταν κάποιος άλλος κόμβος δημιουργήσει ένα μπλοκ λίγα δευτερόλεπτα πριν από εσάς. + + + + Debug information + Πληροφορίες αποσφαλμάτωσης + + + + Transaction + Συναλλαγή + + + + Inputs + εισροές + + + + Amount + Ποσό + + + + true + αληθής + + + + false + αναληθής + + + + , has not been successfully broadcast yet + , δεν έχει ακόμα μεταδοθεί μ' επιτυχία + + + + Open for %n more block(s) + Ανοιχτό για %n μπλοκΑνοιχτό για %n μπλοκ + + + + unknown + άγνωστο + + + + TransactionDescDialog + + + Transaction details + Λεπτομέρειες συναλλαγής + + + + This pane shows a detailed description of the transaction + Αυτό το παράθυρο δείχνει μια λεπτομερή περιγραφή της συναλλαγής + + + + TransactionTableModel + + + Date + Ημερομηνία + + + + Type + Τύπος + + + + Address + Διεύθυνση + + + + Amount + Ποσό + + + + Open for %n more block(s) + Ανοιχτό για %n μπλοκΑνοιχτό για %n μπλοκ + + + + Open until %1 + Ανοιχτό μέχρι %1 + + + + Offline (%1 confirmations) + Χωρίς σύνδεση (%1 επικυρώσεις) + + + + Unconfirmed (%1 of %2 confirmations) + Χωρίς επιβεβαίωση (%1 από %2 επικυρώσεις) + + + + Confirmed (%1 confirmations) + Επικυρωμένη (%1 επικυρώσεις) + + + + Mined balance will be available when it matures in %n more block(s) + Το υπόλοιπο από την εξόρυξη θα είναι διαθέσιμο μετά από %n μπλοκΤο υπόλοιπο από την εξόρυξη θα είναι διαθέσιμο μετά από %n μπλοκ + + + + This block was not received by any other nodes and will probably not be accepted! + Αυτό το μπλοκ δεν έχει παραληφθεί από κανέναν άλλο κόμβο και κατά πάσα πιθανότητα θα απορριφθεί! + + + + Generated but not accepted + Δημιουργήθηκε αλλά απορρίφθηκε + + + + Received with + Παραλαβή με + + + + Received from + Ελήφθη από + + + + Sent to + Αποστολή προς + + + + Payment to yourself + Πληρωμή προς εσάς + + + + Mined + Εξόρυξη + + + + (n/a) + (δ/α) + + + + Transaction status. Hover over this field to show number of confirmations. + Κατάσταση συναλλαγής. Πηγαίνετε το ποντίκι πάνω από αυτό το πεδίο για να δείτε τον αριθμό των επικυρώσεων + + + + Date and time that the transaction was received. + Ημερομηνία κι ώρα λήψης της συναλλαγής. + + + + Type of transaction. + Είδος συναλλαγής. + + + + Destination address of transaction. + Διεύθυνση αποστολής της συναλλαγής. + + + + Amount removed from or added to balance. + Ποσό που αφαιρέθηκε ή προστέθηκε στο υπόλοιπο. + + + + TransactionView + + + + All + Όλα + + + + Today + Σήμερα + + + + This week + Αυτή την εβδομάδα + + + + This month + Αυτόν τον μήνα + + + + Last month + Τον προηγούμενο μήνα + + + + This year + Αυτό το έτος + + + + Range... + Έκταση... + + + + Received with + Ελήφθη με + + + + Sent to + Απεστάλη προς + + + + To yourself + Προς εσάς + + + + Mined + Εξόρυξη + + + + Other + Άλλο + + + + Enter address or label to search + Αναζήτηση με βάση τη διεύθυνση ή την επιγραφή + + + + Min amount + Ελάχιστο ποσό + + + + Copy address + Αντιγραφή διεύθυνσης + + + + Copy label + Αντιγραφή επιγραφής + + + + Copy amount + Αντιγραφή ποσού + + + + Copy transaction ID + Αντιγραφη του ID Συναλλαγής + + + + Edit label + Επεξεργασία επιγραφής + + + + Show transaction details + Εμφάνιση λεπτομερειών συναλλαγής + + + + Export Transaction Data + Εξαγωγή Στοιχείων Συναλλαγών + + + + Comma separated file (*.csv) + Αρχείο οριοθετημένο με κόμματα (*.csv) + + + + Confirmed + Επικυρωμένες + + + + Date + Ημερομηνία + + + + Type + Τύπος + + + + Label + Επιγραφή + + + + Address + Διεύθυνση + + + + Amount + Ποσό + + + + ID + ID + + + + Error exporting + Σφάλμα εξαγωγής + + + + Could not write to file %1. + Αδυναμία εγγραφής στο αρχείο %1. + + + + Range: + Έκταση: + + + + to + έως + + + + WalletModel + + + Send Coins + Αποστολή νομισμάτων + + + + WalletView + + + &Export + &Εξαγωγή + + + + Export the data in the current tab to a file + Εξαγωγή δεδομένων καρτέλας σε αρχείο + + + + Backup Wallet + Αντίγραφο ασφαλείας του πορτοφολιού + + + + Wallet Data (*.dat) + Αρχεία δεδομένων πορτοφολιού (*.dat) + + + + Backup Failed + Αποτυχία κατά τη δημιουργία αντιγράφου + + + + There was an error trying to save the wallet data to the new location. + Παρουσιάστηκε σφάλμα κατά την αποθήκευση των δεδομένων πορτοφολιού στη νέα τοποθεσία. + + + + Backup Successful + Η δημιουργια αντιγραφου ασφαλειας πετυχε + + + + The wallet data was successfully saved to the new location. + Τα δεδομένα πορτοφόλιου αποθηκεύτηκαν με επιτυχία στη νέα θέση. + + + + bitcoin-core + + + CryptoLottoToken version + Έκδοση CryptoLottoToken + + + + Usage: + Χρήση: + + + + Send command to -server or CryptoLottoTokend + Αποστολή εντολής στον εξυπηρετητή ή στο CryptoLottoTokend + + + + List commands + Λίστα εντολών + + + + Get help for a command + Επεξήγηση εντολής + + + + Options: + Επιλογές: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Ορίστε αρχείο ρυθμίσεων (προεπιλογή: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + Ορίστε αρχείο pid (προεπιλογή: CryptoLottoTokend.pid) + + + + Specify data directory + Ορισμός φακέλου δεδομένων + + + + Set database cache size in megabytes (default: 25) + Όρισε το μέγεθος της βάσης προσωρινής αποθήκευσης σε megabytes(προεπιλογή:25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Εισερχόμενες συνδέσεις στη θύρα <port> (προεπιλογή: 22556 ή στο testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Μέγιστες αριθμός συνδέσεων με τους peers <n> (προεπιλογή: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Σύνδεση σε έναν κόμβο για την ανάκτηση διευθύνσεων από ομοτίμους, και αποσυνδέσh + + + + Specify your own public address + Διευκρινίστε τη δικιά σας δημόσια διεύθυνση. + + + + Threshold for disconnecting misbehaving peers (default: 100) + Όριο αποσύνδεσης προβληματικών peers (προεπιλογή: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Δευτερόλεπτα πριν επιτραπεί ξανά η σύνδεση των προβληματικών peers (προεπιλογή: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + Ένα σφάλμα συνέβη καθώς προετοιμαζόταν η πόρτα RPC %u για αναμονή IPv4: %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Εισερχόμενες συνδέσεις JSON-RPC στη θύρα <port> (προεπιλογή: 22555 or testnet: 44555) + + + + Accept command line and JSON-RPC commands + Αποδοχή εντολών κονσόλας και JSON-RPC + + + + Run in the background as a daemon and accept commands + Εκτέλεση στο παρασκήνιο κι αποδοχή εντολών + + + + Use the test network + Χρήση του δοκιμαστικού δικτύου + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Να δέχεσαι συνδέσεις από έξω(προεπιλογή:1) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + Ένα σφάλμα συνέβη καθώς προετοιμαζόταν η υποδοχη RPC %u για αναμονη του IPv6, επεσε πισω στο IPv4:%s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + Αποθηκευση σε συγκεκριμένη διεύθυνση. Χρησιμοποιήστε τα πλήκτρα [Host] : συμβολισμός θύρα για IPv6 + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + Αδυναμία κλειδώματος του φακέλου δεδομένων %s. Πιθανώς το CryptoLottoToken να είναι ήδη ενεργό. + + + + 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. + Σφάλμα: Η συναλλαγή απορρίφθηκε. +Αυτό ίσως οφείλεται στο ότι τα νομίσματά σας έχουν ήδη ξοδευτεί, π.χ. με την αντιγραφή του wallet.dat σε άλλο σύστημα και την χρήση τους εκεί, χωρίς η συναλλαγή να έχει καταγραφεί στο παρόν σύστημα. + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + Σφάλμα: Αυτή η συναλλαγή απαιτεί αμοιβή συναλλαγής τουλάχιστον %s λόγω του μεγέθους, πολυπλοκότητας ή της χρήσης πρόσφατης παραλαβής κεφαλαίου + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + Εκτέλεση της εντολής όταν το καλύτερο μπλοκ αλλάξει(%s στην εντολή αντικαθίσταται από το hash του μπλοκ) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Εκτέλεσε την εντολή όταν το καλύτερο μπλοκ αλλάξει(%s στην εντολή αντικαθίσταται από το hash του μπλοκ) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Ορίστε το μέγιστο μέγεθος των high-priority/low-fee συναλλαγων σε bytes (προεπιλογή: 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + Αυτό είναι ένα προ-τεστ κυκλοφορίας - χρησιμοποιήστε το με δική σας ευθύνη - δεν χρησιμοποιείτε για εξόρυξη ή για αλλες εφαρμογές + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Προειδοποίηση: Η παράμετρος -paytxfee είναι πολύ υψηλή. Πρόκειται για την αμοιβή που θα πληρώνετε για κάθε συναλλαγή που θα στέλνετε. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + Προειδοποίηση: Εμφανίσεις συναλλαγων δεν μπορεί να είναι σωστες! Μπορεί να χρειαστεί να αναβαθμίσετε, ή άλλοι κόμβοι μπορεί να χρειαστεί να αναβαθμίστουν. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Προειδοποίηση: Παρακαλώ βεβαιωθείτε πως η ημερομηνία κι ώρα του συστήματός σας είναι σωστές. Αν το ρολόι του υπολογιστή σας πάει λάθος, ενδέχεται να μη λειτουργεί σωστά το CryptoLottoToken. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Προειδοποίηση : Σφάλμα wallet.dat κατα την ανάγνωση ! Όλα τα κλειδιά αναγνωρισθηκαν σωστά, αλλά τα δεδομένα των συναλλαγών ή καταχωρήσεις στο βιβλίο διευθύνσεων μπορεί να είναι ελλιπείς ή λανθασμένα. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Προειδοποίηση : το αρχειο wallet.dat ειναι διεφθαρμένο, τα δεδομένα σώζονται ! Original wallet.dat αποθηκεύονται ως πορτοφόλι { timestamp } bak στο % s ? . . Αν το υπόλοιπο του ή τις συναλλαγές σας, είναι λάθος θα πρέπει να επαναφέρετε από ένα αντίγραφο ασφαλείας + + + + Attempt to recover private keys from a corrupt wallet.dat + Προσπάθεια για ανακτησει ιδιωτικων κλειδιων από ενα διεφθαρμένο αρχειο wallet.dat + + + + Block creation options: + Αποκλεισμός επιλογων δημιουργίας: + + + + Connect only to the specified node(s) + Σύνδεση μόνο με ορισμένους κόμβους + + + + Corrupted block database detected + Εντοπισθηκε διεφθαρμενη βαση δεδομενων των μπλοκ + + + + Discover own IP address (default: 1 when listening and no -externalip) + Ανακαλύψτε την δικη σας IP διεύθυνση (προεπιλογή: 1 όταν ακούει και δεν - externalip) + + + + Do you want to rebuild the block database now? + Θελετε να δημιουργηθει τωρα η βαση δεδομενων του μπλοκ? + + + + Error initializing block database + Σφάλμα κατά την ενεργοποίηση της βάσης δεδομένων μπλοκ + + + + Error initializing wallet database environment %s! + Σφάλμα κατά την ενεργοποίηση της βάσης δεδομένων πορτοφόλιου %s! + + + + Error loading block database + Σφάλμα φορτωσης της βασης δεδομενων των μπλοκ + + + + Error opening block database + Σφάλμα φορτωσης της βασης δεδομενων των μπλοκ + + + + Error: Disk space is low! + Προειδοποίηση: Χαμηλός χώρος στο δίσκο + + + + Error: Wallet locked, unable to create transaction! + Σφάλμα: το πορτοφόλι είναι κλειδωμένο, δεν μπορεί να δημιουργηθεί συναλλαγή + + + + Error: system error: + Λάθος: λάθος συστήματος: + + + + Failed to listen on any port. Use -listen=0 if you want this. + ταλαιπωρηθειτε για να ακούσετε σε οποιαδήποτε θύρα. Χρήση - ακούστε = 0 , αν θέλετε αυτό. + + + + Failed to read block info + Αποτυχία αναγνωσης των block πληροφοριων + + + + Failed to read block + Η αναγνωση του μπλοκ απετυχε + + + + Failed to sync block index + Ο συγχρονισμος του μπλοκ ευρετηριου απετυχε + + + + Failed to write block index + Η δημιουργια του μπλοκ ευρετηριου απετυχε + + + + Failed to write block info + Η δημιουργια των μπλοκ πληροφοριων απετυχε + + + + Failed to write block + Η δημιουργια του μπλοκ απετυχε + + + + Failed to write file info + Αδυναμία εγγραφής πληροφοριων αρχειου + + + + Failed to write to coin database + Αποτυχία εγγραφής στη βάση δεδομένων νομίσματος + + + + Failed to write transaction index + Αποτυχία εγγραφής δείκτη συναλλαγών + + + + Failed to write undo data + Αποτυχία εγγραφής αναίρεσης δεδομένων + + + + Find peers using DNS lookup (default: 1 unless -connect) + Βρες ομότιμους υπολογιστές χρησιμοποιώντας αναζήτηση DNS(προεπιλογή:1) + + + + Generate coins (default: 0) + Δημιουργία νομισμάτων (προκαθορισμος: 0) + + + + How many blocks to check at startup (default: 288, 0 = all) + Πόσα μπλοκ να ελέγχθουν κατά την εκκίνηση (προεπιλογή:288,0=όλα) + + + + How thorough the block verification is (0-4, default: 3) + Πόσο εξονυχιστική να είναι η επιβεβαίωση του μπλοκ(0-4, προεπιλογή:3) + + + + Not enough file descriptors available. + Δεν ειναι αρκετες περιγραφες αρχείων διαθέσιμες. + + + + Rebuild block chain index from current blk000??.dat files + Εισαγωγή μπλοκ από εξωτερικό αρχείο blk000?.dat + + + + Set the number of threads to service RPC calls (default: 4) + Ορίσμος του αριθμόυ θεματων στην υπηρεσία κλήσεων RPC (προεπιλογή: 4) + + + + Verifying blocks... + Επαλήθευση των μπλοκ... + + + + Verifying wallet... + Επαλήθευση πορτοφολιου... + + + + Imports blocks from external blk000??.dat file + Εισαγωγή μπλοκ από εξωτερικό αρχείο blk000?.dat + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + Ορίσμος του αριθμό των νημάτων ελέγχου σεναρίου (μέχρι 16, 0 = auto, <0 = αφήνουν τους πολλους πυρήνες δωρεάν, default: 0) + + + + Information + Πληροφορία + + + + Invalid -tor address: '%s' + Δεν είναι έγκυρη η διεύθυνση διαμεσολαβητή: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + Μη έγκυρο ποσό για την παράμετρο -paytxfee=<amount>: '%s' + + + + Invalid amount for -mintxfee=<amount>: '%s' + Μη έγκυρο ποσό για την παράμετρο -paytxfee=<amount>: '%s' + + + + Maintain a full transaction index (default: 0) + Διατηρήση ένος πλήρες ευρετήριου συναλλαγών (προεπιλογή: 0) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Μέγιστος buffer λήψης ανά σύνδεση, <n>*1000 bytes (προεπιλογή: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Μέγιστος buffer αποστολής ανά σύνδεση, <n>*1000 bytes (προεπιλογή: 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + Μονο αποδοχη αλυσίδας μπλοκ που ταιριάζει με τα ενσωματωμένα σημεία ελέγχου (προεπιλογή: 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Συνδέση μόνο σε κόμβους του δικτύου <net> (IPv4, IPv6 ή Tor) + + + + Output extra debugging information. Implies all other -debug* options + Έξοδος επιπλέον πληροφοριών εντοπισμού σφαλμάτων + + + + Output extra network debugging information + Έξοδος επιπλέον πληροφοριών εντοπισμού σφαλμάτων + + + + Prepend debug output with timestamp (default: 1) + Χρονοσφραγίδα πληροφοριών εντοπισμού σφαλμάτων + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + Ρυθμίσεις SSL: (ανατρέξτε στο CryptoLottoToken Wiki για οδηγίες ρυθμίσεων SSL) + + + + Select the version of socks proxy to use (4-5, default: 5) + Επιλέξτε την έκδοση του διαμεσολαβητη για να χρησιμοποιήσετε (4-5 , προεπιλογή: 5) + + + + Send trace/debug info to console instead of debug.log file + Αποστολή πληροφοριών εντοπισμού σφαλμάτων στην κονσόλα αντί του αρχείου debug.log + + + + Send trace/debug info to debugger + Αποστολή πληροφοριών εντοπισμού σφαλμάτων στον debugger + + + + Set maximum block size in bytes (default: 250000) + Ορίσμος του μέγιστου μέγεθος block σε bytes (προεπιλογή: 250000) + + + + Set minimum block size in bytes (default: 0) + Ορίστε το μέγιστο μέγεθος block σε bytes (προεπιλογή: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Συρρίκνωση του αρχείο debug.log κατα την εκκίνηση του πελάτη (προεπιλογή: 1 όταν δεν-debug) + + + + Signing transaction failed + Η υπογραφή συναλλαγής απέτυχε + + + + Specify connection timeout in milliseconds (default: 5000) + Ορισμός λήξης χρονικού ορίου σε χιλιοστά του δευτερολέπτου(προεπιλογή:5000) + + + + System error: + Λάθος Συστήματος: + + + + Transaction amount too small + Το ποσό της συναλλαγής είναι πολύ μικρο + + + + Transaction amounts must be positive + Τα ποσά των συναλλαγών πρέπει να είναι θετικα + + + + Transaction too large + Η συναλλαγή ειναι πολύ μεγάλη + + + + Use UPnP to map the listening port (default: 0) + Χρησιμοποίηση του UPnP για την χρήση της πόρτας αναμονής (προεπιλογή:0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Χρησιμοποίηση του UPnP για την χρήση της πόρτας αναμονής (προεπιλογή:1) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Χρήση διακομιστή μεσολάβησης για την επίτευξη των Tor κρυμμένων υπηρεσιων (προεπιλογή: ίδιο με το-proxy) + + + + Username for JSON-RPC connections + Όνομα χρήστη για τις συνδέσεις JSON-RPC + + + + Warning + Προειδοποίηση + + + + Warning: This version is obsolete, upgrade required! + Προειδοποίηση: Αυτή η έκδοση είναι ξεπερασμένη, απαιτείται αναβάθμιση + + + + You need to rebuild the databases using -reindex to change -txindex + Θα πρέπει να ξαναχτίστουν οι βάσεις δεδομένων που χρησιμοποιούντε-Αναδημιουργία αλλάγων-txindex + + + + wallet.dat corrupt, salvage failed + Το αρχειο wallet.dat ειναι διεφθαρμένο, η διάσωση απέτυχε + + + + Password for JSON-RPC connections + Κωδικός για τις συνδέσεις JSON-RPC + + + + Allow JSON-RPC connections from specified IP address + Αποδοχή συνδέσεων JSON-RPC από συγκεκριμένη διεύθυνση IP + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Αποστολή εντολών στον κόμβο <ip> (προεπιλογή: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Εκτέλεσε την εντολή όταν το καλύτερο μπλοκ αλλάξει(%s στην εντολή αντικαθίσταται από το hash του μπλοκ) + + + + Upgrade wallet to latest format + Αναβάθμισε το πορτοφόλι στην τελευταία έκδοση + + + + Set key pool size to <n> (default: 100) + Όριο πλήθους κλειδιών pool <n> (προεπιλογή: 100) + + + + Rescan the block chain for missing wallet transactions + Επανέλεγχος της αλυσίδας μπλοκ για απούσες συναλλαγές + + + + Use OpenSSL (https) for JSON-RPC connections + Χρήση του OpenSSL (https) για συνδέσεις JSON-RPC + + + + Server certificate file (default: server.cert) + Αρχείο πιστοποιητικού του διακομιστή (προεπιλογή: server.cert) + + + + Server private key (default: server.pem) + Προσωπικό κλειδί του διακομιστή (προεπιλογή: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Αποδεκτά κρυπτογραφήματα (προεπιλογή: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Αυτό το κείμενο βοήθειας + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Αδύνατη η σύνδεση με τη θύρα %s αυτού του υπολογιστή (bind returned error %d, %s) + + + + Connect through socks proxy + Σύνδεση μέσω διαμεσολαβητή socks + + + + Allow DNS lookups for -addnode, -seednode and -connect + Να επιτρέπονται οι έλεγχοι DNS για προσθήκη και σύνδεση κόμβων + + + + Loading addresses... + Φόρτωση διευθύνσεων... + + + + Error loading wallet.dat: Wallet corrupted + Σφάλμα φόρτωσης wallet.dat: Κατεστραμμένο Πορτοφόλι + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Σφάλμα φόρτωσης wallet.dat: Το Πορτοφόλι απαιτεί μια νεότερη έκδοση του CryptoLottoToken + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Απαιτείται η επανεγγραφή του Πορτοφολιού, η οποία θα ολοκληρωθεί στην επανεκκίνηση του CryptoLottoToken + + + + Error loading wallet.dat + Σφάλμα φόρτωσης αρχείου wallet.dat + + + + Invalid -proxy address: '%s' + Δεν είναι έγκυρη η διεύθυνση διαμεσολαβητή: '%s' + + + + Unknown network specified in -onlynet: '%s' + Άγνωστo δίκτυο ορίζεται σε onlynet: '%s' + + + + Unknown -socks proxy version requested: %i + Άγνωστo δίκτυο ορίζεται: %i + + + + Cannot resolve -bind address: '%s' + Δεν μπορώ να γράψω την προεπιλεγμένη διεύθυνση: '%s' + + + + Cannot resolve -externalip address: '%s' + Δεν μπορώ να γράψω την προεπιλεγμένη διεύθυνση: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + Μη έγκυρο ποσό για την παράμετρο -paytxfee=<amount>: '%s' + + + + Invalid amount + Λάθος ποσότητα + + + + Insufficient funds + Ανεπαρκές κεφάλαιο + + + + Loading block index... + Φόρτωση ευρετηρίου μπλοκ... + + + + Add a node to connect to and attempt to keep the connection open + Προσέθεσε ένα κόμβο για σύνδεση και προσπάθησε να κρατήσεις την σύνδεση ανοιχτή + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Αδύνατη η σύνδεση με τη θύρα %s αυτού του υπολογιστή. Το CryptoLottoToken είναι πιθανώς ήδη ενεργό. + + + + Fee per KB to add to transactions you send + Αμοιβή ανά KB που θα προστίθεται στις συναλλαγές που στέλνεις + + + + Loading wallet... + Φόρτωση πορτοφολιού... + + + + Cannot downgrade wallet + Δεν μπορώ να υποβαθμίσω το πορτοφόλι + + + + Cannot write default address + Δεν μπορώ να γράψω την προεπιλεγμένη διεύθυνση + + + + Rescanning... + Ανίχνευση... + + + + Done loading + Η φόρτωση ολοκληρώθηκε + + + + To use the %s option + Χρήση της %s επιλογής + + + + Error + Σφάλμα + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + Πρέπει να βάλεις ένα κωδικό στο αρχείο παραμέτρων: %s +Εάν το αρχείο δεν υπάρχει, δημιούργησε το με δικαιώματα μόνο για ανάγνωση από τον δημιουργό + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_en.qm b/src/qt/locale/bitcoin_en.qm new file mode 100644 index 0000000..06fc04b Binary files /dev/null and b/src/qt/locale/bitcoin_en.qm differ diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts new file mode 100644 index 0000000..5fdc070 --- /dev/null +++ b/src/qt/locale/bitcoin_en.ts @@ -0,0 +1,2967 @@ + + + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + About CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> version + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + Copyright + Copyright + + + + The CryptoLottoToken developers + The CryptoLottoToken developers + + + + AddressBookPage + + + Address Book + Address Book + + + + Double-click to edit address or label + Double-click to edit address or label + + + + Create a new address + Create a new address + + + + Copy the currently selected address to the system clipboard + Copy the currently selected address to the system clipboard + + + + &New Address + &New Address + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + + + + &Copy Address + &Copy Address + + + + Show &QR Code + Show &QR Code + + + + Sign a message to prove you own a CryptoLottoToken address + Sign a message to prove you own a CryptoLottoToken address + + + + Sign &Message + Sign &Message + + + + Delete the currently selected address from the list + Delete the currently selected address from the list + + + + Export the data in the current tab to a file + Export the data in the current tab to a file + + + + &Export + &Export + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Verify a message to ensure it was signed with a specified CryptoLottoToken address + + + + &Verify Message + &Verify Message + + + + &Delete + &Delete + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + Copy &Label + Copy &Label + + + + &Edit + &Edit + + + + Send &Coins + Send &Coins + + + + Export Address Book Data + Export Address Book Data + + + + Comma separated file (*.csv) + Comma separated file (*.csv) + + + + Error exporting + Error exporting + + + + Could not write to file %1. + Could not write to file %1. + + + + AddressTableModel + + + Label + Label + + + + Address + Address + + + + (no label) + (no label) + + + + AskPassphraseDialog + + + Passphrase Dialog + Passphrase Dialog + + + + Enter passphrase + Enter passphrase + + + + New passphrase + New passphrase + + + + Repeat new passphrase + Repeat new passphrase + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + + + + Encrypt wallet + Encrypt wallet + + + + This operation needs your wallet passphrase to unlock the wallet. + This operation needs your wallet passphrase to unlock the wallet. + + + + Unlock wallet + Unlock wallet + + + + This operation needs your wallet passphrase to decrypt the wallet. + This operation needs your wallet passphrase to decrypt the wallet. + + + + Decrypt wallet + Decrypt wallet + + + + Change passphrase + Change passphrase + + + + Enter the old and new passphrase to the wallet. + Enter the old and new passphrase to the wallet. + + + + Confirm wallet encryption + Confirm wallet encryption + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + + + + Are you sure you wish to encrypt your wallet? + Are you sure you wish to encrypt your wallet? + + + + 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. + 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. + + + + + Warning: The Caps Lock key is on! + Warning: The Caps Lock key is on! + + + + + Wallet encrypted + Wallet encrypted + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + + + + + + + Wallet encryption failed + Wallet encryption failed + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + + + + + The supplied passphrases do not match. + The supplied passphrases do not match. + + + + Wallet unlock failed + Wallet unlock failed + + + + + + The passphrase entered for the wallet decryption was incorrect. + The passphrase entered for the wallet decryption was incorrect. + + + + Wallet decryption failed + Wallet decryption failed + + + + Wallet passphrase was successfully changed. + Wallet passphrase was successfully changed. + + + + BitcoinGUI + + + Sign &message... + Sign &message... + + + + Synchronizing with network... + Synchronizing with network... + + + + &Overview + &Overview + + + + Show general overview of wallet + Show general overview of wallet + + + + &Transactions + &Transactions + + + + Browse transaction history + Browse transaction history + + + + Edit the list of stored addresses and labels for sending + Edit the list of stored addresses and labels for sending + + + + Show the list of addresses for receiving payments + Show the list of addresses for receiving payments + + + + E&xit + E&xit + + + + Quit application + Quit application + + + + Show information about CryptoLottoToken + Show information about CryptoLottoToken + + + + About &Qt + About &Qt + + + + Show information about Qt + Show information about Qt + + + + &Options... + &Options... + + + + &Encrypt Wallet... + &Encrypt Wallet... + + + + &Backup Wallet... + &Backup Wallet... + + + + &Change Passphrase... + &Change Passphrase... + + + + Importing blocks from disk... + Importing blocks from disk... + + + + Reindexing blocks on disk... + Reindexing blocks on disk... + + + + Send coins to a CryptoLottoToken address + Send coins to a CryptoLottoToken address + + + + Modify configuration options for CryptoLottoToken + Modify configuration options for CryptoLottoToken + + + + Backup wallet to another location + Backup wallet to another location + + + + Change the passphrase used for wallet encryption + Change the passphrase used for wallet encryption + + + + &Debug window + &Debug window + + + + Open debugging and diagnostic console + Open debugging and diagnostic console + + + + &Verify message... + &Verify message... + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Wallet + + + + &Send + &Send + + + + &Receive + &Receive + + + + &Addresses + &Addresses + + + + &About CryptoLottoToken + &About CryptoLottoToken + + + + &Show / Hide + &Show / Hide + + + + Show or hide the main Window + Show or hide the main Window + + + + Encrypt the private keys that belong to your wallet + Encrypt the private keys that belong to your wallet + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + &File + &File + + + + &Settings + &Settings + + + + &Help + &Help + + + + Tabs toolbar + Tabs toolbar + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + CryptoLottoToken client + + + + %n active connection(s) to CryptoLottoToken network + + %n active connection to CryptoLottoToken network + %n active connections to CryptoLottoToken network + + + + + No block source available... + No block source available... + + + + Processed %1 of %2 (estimated) blocks of transaction history. + Processed %1 of %2 (estimated) blocks of transaction history. + + + + Processed %1 blocks of transaction history. + Processed %1 blocks of transaction history. + + + + %n hour(s) + + %n hour + %n hours + + + + + %n day(s) + + %n day + %n days + + + + + %n week(s) + + %n week + %n weeks + + + + + %1 behind + %1 behind + + + + Last received block was generated %1 ago. + Last received block was generated %1 ago. + + + + Transactions after this will not yet be visible. + Transactions after this will not yet be visible. + + + + Error + Error + + + + Warning + Warning + + + + Information + Information + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + Up to date + Up to date + + + + Catching up... + Catching up... + + + + Confirm transaction fee + Confirm transaction fee + + + + Sent transaction + Sent transaction + + + + Incoming transaction + Incoming transaction + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + + + + + URI handling + URI handling + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Wallet is <b>encrypted</b> and currently <b>locked</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + ClientModel + + + Network Alert + Network Alert + + + + EditAddressDialog + + + Edit Address + Edit Address + + + + &Label + &Label + + + + The label associated with this address book entry + The label associated with this address book entry + + + + &Address + &Address + + + + The address associated with this address book entry. This can only be modified for sending addresses. + The address associated with this address book entry. This can only be modified for sending addresses. + + + + New receiving address + New receiving address + + + + New sending address + New sending address + + + + Edit receiving address + Edit receiving address + + + + Edit sending address + Edit sending address + + + + The entered address "%1" is already in the address book. + The entered address "%1" is already in the address book. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + The entered address "%1" is not a valid CryptoLottoToken address. + + + + Could not unlock wallet. + Could not unlock wallet. + + + + New key generation failed. + New key generation failed. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + version + + + + Usage: + Usage: + + + + command-line options + command-line options + + + + UI options + UI options + + + + Set language, for example "de_DE" (default: system locale) + Set language, for example "de_DE" (default: system locale) + + + + Start minimized + Start minimized + + + + Show splash screen on startup (default: 1) + Show splash screen on startup (default: 1) + + + + OptionsDialog + + + Options + Options + + + + &Main + &Main + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + Pay transaction &fee + Pay transaction &fee + + + + Automatically start CryptoLottoToken after logging in to the system. + Automatically start CryptoLottoToken after logging in to the system. + + + + &Start CryptoLottoToken on system login + &Start CryptoLottoToken on system login + + + + Reset all client options to default. + Reset all client options to default. + + + + &Reset Options + &Reset Options + + + + &Network + &Network + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + + + + Map port using &UPnP + Map port using &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + + + + &Connect through SOCKS proxy: + &Connect through SOCKS proxy: + + + + Proxy &IP: + Proxy &IP: + + + + IP address of the proxy (e.g. 127.0.0.1) + IP address of the proxy (e.g. 127.0.0.1) + + + + &Port: + &Port: + + + + Port of the proxy (e.g. 9050) + Port of the proxy (e.g. 9050) + + + + SOCKS &Version: + SOCKS &Version: + + + + SOCKS version of the proxy (e.g. 5) + SOCKS version of the proxy (e.g. 5) + + + + &Window + &Window + + + + Show only a tray icon after minimizing the window. + Show only a tray icon after minimizing the window. + + + + &Minimize to the tray instead of the taskbar + &Minimize to the tray instead of the taskbar + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + + + + M&inimize on close + M&inimize on close + + + + &Display + &Display + + + + User Interface &language: + User Interface &language: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + + + + &Unit to show amounts in: + &Unit to show amounts in: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Choose the default subdivision unit to show in the interface and when sending coins. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + + &Display addresses in transaction list + &Display addresses in transaction list + + + + &OK + &OK + + + + &Cancel + &Cancel + + + + &Apply + &Apply + + + + default + default + + + + Confirm options reset + Confirm options reset + + + + Some settings may require a client restart to take effect. + Some settings may require a client restart to take effect. + + + + Do you want to proceed? + Do you want to proceed? + + + + + Warning + Warning + + + + + This setting will take effect after restarting CryptoLottoToken. + This setting will take effect after restarting CryptoLottoToken. + + + + The supplied proxy address is invalid. + The supplied proxy address is invalid. + + + + OverviewPage + + + Form + Form + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + + + + Balance: + Balance: + + + + Unconfirmed: + Unconfirmed: + + + + Wallet + Wallet + + + + Immature: + Immature: + + + + Mined balance that has not yet matured + Mined balance that has not yet matured + + + + <b>Recent transactions</b> + <b>Recent transactions</b> + + + + Your current balance + Your current balance + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + + + + + out of sync + out of sync + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + Cannot start CryptoLottoToken: click-to-pay handler + + + + QRCodeDialog + + + QR Code Dialog + QR Code Dialog + + + + Request Payment + Request Payment + + + + Amount: + Amount: + + + + Label: + Label: + + + + Message: + Message: + + + + &Save As... + &Save As... + + + + Error encoding URI into QR Code. + Error encoding URI into QR Code. + + + + The entered amount is invalid, please check. + The entered amount is invalid, please check. + + + + Resulting URI too long, try to reduce the text for label / message. + Resulting URI too long, try to reduce the text for label / message. + + + + Save QR Code + Save QR Code + + + + PNG Images (*.png) + PNG Images (*.png) + + + + RPCConsole + + + Client name + Client name + + + + + + + + + + + + + N/A + N/A + + + + Client version + Client version + + + + &Information + &Information + + + + Using OpenSSL version + Using OpenSSL version + + + + Startup time + Startup time + + + + Network + Network + + + + Number of connections + Number of connections + + + + On testnet + On testnet + + + + Block chain + Block chain + + + + Current number of blocks + Current number of blocks + + + + Estimated total blocks + Estimated total blocks + + + + Last block time + Last block time + + + + &Open + &Open + + + + Command-line options + Command-line options + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + &Show + &Show + + + + &Console + &Console + + + + Build date + Build date + + + + CryptoLottoToken - Debug window + CryptoLottoToken - Debug window + + + + CryptoLottoToken Core + CryptoLottoToken Core + + + + Debug log file + Debug log file + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + Clear console + Clear console + + + + Welcome to the CryptoLottoToken RPC console. + Welcome to the CryptoLottoToken RPC console. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + + + + Type <b>help</b> for an overview of available commands. + Type <b>help</b> for an overview of available commands. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Send Coins + + + + Send to multiple recipients at once + Send to multiple recipients at once + + + + Add &Recipient + Add &Recipient + + + + Remove all transaction fields + Remove all transaction fields + + + + Clear &All + Clear &All + + + + Balance: + Balance: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Confirm the send action + + + + S&end + S&end + + + + <b>%1</b> to %2 (%3) + <b>%1</b> to %2 (%3) + + + + Confirm send coins + Confirm send coins + + + + Are you sure you want to send %1? + Are you sure you want to send %1? + + + + and + and + + + + The recipient address is not valid, please recheck. + The recipient address is not valid, please recheck. + + + + The amount to pay must be larger than 0. + The amount to pay must be larger than 0. + + + + The amount exceeds your balance. + The amount exceeds your balance. + + + + The total exceeds your balance when the %1 transaction fee is included. + The total exceeds your balance when the %1 transaction fee is included. + + + + Duplicate address found, can only send to each address once per send operation. + Duplicate address found, can only send to each address once per send operation. + + + + Error: Transaction creation failed! + Error: Transaction creation failed! + + + + 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. + 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. + + + + SendCoinsEntry + + + Form + Form + + + + A&mount: + A&mount: + + + + Pay &To: + Pay &To: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + Enter a label for this address to add it to your address book + + + + &Label: + &Label: + + + + Choose address from address book + Choose address from address book + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Paste address from clipboard + + + + Alt+P + Alt+P + + + + Remove this recipient + Remove this recipient + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Signatures - Sign / Verify a Message + + + + &Sign Message + &Sign Message + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Choose an address from the address book + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Paste address from clipboard + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Enter the message you want to sign here + + + + Signature + Signature + + + + Copy the current signature to the system clipboard + Copy the current signature to the system clipboard + + + + Sign the message to prove you own this CryptoLottoToken address + Sign the message to prove you own this CryptoLottoToken address + + + + Sign &Message + Sign &Message + + + + Reset all sign message fields + Reset all sign message fields + + + + + Clear &All + Clear &All + + + + &Verify Message + &Verify Message + + + + Enter the signing 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. + Enter the signing 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. + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Verify the message to ensure it was signed with the specified CryptoLottoToken address + + + + Verify &Message + Verify &Message + + + + Reset all verify message fields + Reset all verify message fields + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Click "Sign Message" to generate signature + + + + Enter CryptoLottoToken signature + Enter CryptoLottoToken signature + + + + + The entered address is invalid. + The entered address is invalid. + + + + + + + Please check the address and try again. + Please check the address and try again. + + + + + The entered address does not refer to a key. + The entered address does not refer to a key. + + + + Wallet unlock was cancelled. + Wallet unlock was cancelled. + + + + Private key for the entered address is not available. + Private key for the entered address is not available. + + + + Message signing failed. + Message signing failed. + + + + Message signed. + Message signed. + + + + The signature could not be decoded. + The signature could not be decoded. + + + + + Please check the signature and try again. + Please check the signature and try again. + + + + The signature did not match the message digest. + The signature did not match the message digest. + + + + Message verification failed. + Message verification failed. + + + + Message verified. + Message verified. + + + + SplashScreen + + + The CryptoLottoToken developers + The CryptoLottoToken developers + + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + Open until %1 + + + + %1/offline + %1/offline + + + + %1/unconfirmed + %1/unconfirmed + + + + %1 confirmations + %1 confirmations + + + + Status + Status + + + + , broadcast through %n node(s) + + , broadcast through %n node + , broadcast through %n nodes + + + + + Date + Date + + + + Source + Source + + + + Generated + Generated + + + + + From + From + + + + + + To + To + + + + + own address + own address + + + + label + label + + + + + + + + Credit + Credit + + + + matures in %n more block(s) + + matures in %n more block + matures in %n more blocks + + + + + not accepted + not accepted + + + + + + + Debit + Debit + + + + Transaction fee + Transaction fee + + + + Net amount + Net amount + + + + Message + Message + + + + Comment + Comment + + + + Transaction ID + Transaction ID + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + + + + Debug information + Debug information + + + + Transaction + Transaction + + + + Inputs + Inputs + + + + Amount + Amount + + + + true + true + + + + false + false + + + + , has not been successfully broadcast yet + , has not been successfully broadcast yet + + + + Open for %n more block(s) + + Open for %n more block + Open for %n more blocks + + + + + unknown + unknown + + + + TransactionDescDialog + + + Transaction details + Transaction details + + + + This pane shows a detailed description of the transaction + This pane shows a detailed description of the transaction + + + + TransactionTableModel + + + Date + Date + + + + Type + Type + + + + Address + Address + + + + Amount + Amount + + + + Open for %n more block(s) + + Open for %n more block + Open for %n more blocks + + + + + Open until %1 + Open until %1 + + + + Offline (%1 confirmations) + Offline (%1 confirmations) + + + + Unconfirmed (%1 of %2 confirmations) + Unconfirmed (%1 of %2 confirmations) + + + + Confirmed (%1 confirmations) + Confirmed (%1 confirmations) + + + + Mined balance will be available when it matures in %n more block(s) + + Mined balance will be available when it matures in %n more block + Mined balance will be available when it matures in %n more blocks + + + + + This block was not received by any other nodes and will probably not be accepted! + This block was not received by any other nodes and will probably not be accepted! + + + + Generated but not accepted + Generated but not accepted + + + + Received with + Received with + + + + Received from + Received from + + + + Sent to + Sent to + + + + Payment to yourself + Payment to yourself + + + + Mined + Mined + + + + (n/a) + (n/a) + + + + Transaction status. Hover over this field to show number of confirmations. + Transaction status. Hover over this field to show number of confirmations. + + + + Date and time that the transaction was received. + Date and time that the transaction was received. + + + + Type of transaction. + Type of transaction. + + + + Destination address of transaction. + Destination address of transaction. + + + + Amount removed from or added to balance. + Amount removed from or added to balance. + + + + TransactionView + + + + All + All + + + + Today + Today + + + + This week + This week + + + + This month + This month + + + + Last month + Last month + + + + This year + This year + + + + Range... + Range... + + + + Received with + Received with + + + + Sent to + Sent to + + + + To yourself + To yourself + + + + Mined + Mined + + + + Other + Other + + + + Enter address or label to search + Enter address or label to search + + + + Min amount + Min amount + + + + Copy address + Copy address + + + + Copy label + Copy label + + + + Copy amount + Copy amount + + + + Copy transaction ID + Copy transaction ID + + + + Edit label + Edit label + + + + Show transaction details + Show transaction details + + + + Export Transaction Data + Export Transaction Data + + + + Comma separated file (*.csv) + Comma separated file (*.csv) + + + + Confirmed + Confirmed + + + + Date + Date + + + + Type + Type + + + + Label + Label + + + + Address + Address + + + + Amount + Amount + + + + ID + ID + + + + Error exporting + Error exporting + + + + Could not write to file %1. + Could not write to file %1. + + + + Range: + Range: + + + + to + to + + + + WalletModel + + + Send Coins + Send Coins + + + + WalletView + + + &Export + &Export + + + + Export the data in the current tab to a file + Export the data in the current tab to a file + + + + Backup Wallet + Backup Wallet + + + + Wallet Data (*.dat) + Wallet Data (*.dat) + + + + Backup Failed + Backup Failed + + + + There was an error trying to save the wallet data to the new location. + There was an error trying to save the wallet data to the new location. + + + + Backup Successful + Backup Successful + + + + The wallet data was successfully saved to the new location. + The wallet data was successfully saved to the new location. + + + + bitcoin-core + + + CryptoLottoToken version + CryptoLottoToken version + + + + Usage: + Usage: + + + + Send command to -server or CryptoLottoTokend + Send command to -server or CryptoLottoTokend + + + + List commands + List commands + + + + Get help for a command + Get help for a command + + + + Options: + Options: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Specify configuration file (default: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + Specify pid file (default: CryptoLottoTokend.pid) + + + + Specify data directory + Specify data directory + + + + Set database cache size in megabytes (default: 25) + Set database cache size in megabytes (default: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Listen for connections on <port> (default: 22556 or testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Maintain at most <n> connections to peers (default: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Connect to a node to retrieve peer addresses, and disconnect + + + + Specify your own public address + Specify your own public address + + + + Threshold for disconnecting misbehaving peers (default: 100) + Threshold for disconnecting misbehaving peers (default: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + + + + Accept command line and JSON-RPC commands + Accept command line and JSON-RPC commands + + + + Run in the background as a daemon and accept commands + Run in the background as a daemon and accept commands + + + + Use the test network + Use the test network + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + 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. + 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. + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + Attempt to recover private keys from a corrupt wallet.dat + Attempt to recover private keys from a corrupt wallet.dat + + + + Block creation options: + Block creation options: + + + + Connect only to the specified node(s) + Connect only to the specified node(s) + + + + Corrupted block database detected + Corrupted block database detected + + + + Discover own IP address (default: 1 when listening and no -externalip) + Discover own IP address (default: 1 when listening and no -externalip) + + + + Do you want to rebuild the block database now? + Do you want to rebuild the block database now? + + + + Error initializing block database + Error initializing block database + + + + Error initializing wallet database environment %s! + Error initializing wallet database environment %s! + + + + Error loading block database + Error loading block database + + + + Error opening block database + Error opening block database + + + + Error: Disk space is low! + Error: Disk space is low! + + + + Error: Wallet locked, unable to create transaction! + Error: Wallet locked, unable to create transaction! + + + + Error: system error: + Error: system error: + + + + Failed to listen on any port. Use -listen=0 if you want this. + Failed to listen on any port. Use -listen=0 if you want this. + + + + Failed to read block info + Failed to read block info + + + + Failed to read block + Failed to read block + + + + Failed to sync block index + Failed to sync block index + + + + Failed to write block index + Failed to write block index + + + + Failed to write block info + Failed to write block info + + + + Failed to write block + Failed to write block + + + + Failed to write file info + Failed to write file info + + + + Failed to write to coin database + Failed to write to coin database + + + + Failed to write transaction index + Failed to write transaction index + + + + Failed to write undo data + Failed to write undo data + + + + Find peers using DNS lookup (default: 1 unless -connect) + Find peers using DNS lookup (default: 1 unless -connect) + + + + Generate coins (default: 0) + Generate coins (default: 0) + + + + How many blocks to check at startup (default: 288, 0 = all) + How many blocks to check at startup (default: 288, 0 = all) + + + + How thorough the block verification is (0-4, default: 3) + How thorough the block verification is (0-4, default: 3) + + + + Not enough file descriptors available. + Not enough file descriptors available. + + + + Rebuild block chain index from current blk000??.dat files + Rebuild block chain index from current blk000??.dat files + + + + Set the number of threads to service RPC calls (default: 4) + Set the number of threads to service RPC calls (default: 4) + + + + Verifying blocks... + Verifying blocks... + + + + Verifying wallet... + Verifying wallet... + + + + Imports blocks from external blk000??.dat file + Imports blocks from external blk000??.dat file + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + Information + Information + + + + Invalid -tor address: '%s' + Invalid -tor address: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + Invalid amount for -mintxfee=<amount>: '%s' + Invalid amount for -mintxfee=<amount>: '%s' + + + + Maintain a full transaction index (default: 0) + Maintain a full transaction index (default: 0) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + Only accept block chain matching built-in checkpoints (default: 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + Output extra debugging information. Implies all other -debug* options + Output extra debugging information. Implies all other -debug* options + + + + Output extra network debugging information + Output extra network debugging information + + + + Prepend debug output with timestamp (default: 1) + Prepend debug output with timestamp (default: 1) + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + + + + Select the version of socks proxy to use (4-5, default: 5) + Select the version of socks proxy to use (4-5, default: 5) + + + + Send trace/debug info to console instead of debug.log file + Send trace/debug info to console instead of debug.log file + + + + Send trace/debug info to debugger + Send trace/debug info to debugger + + + + Set maximum block size in bytes (default: 250000) + Set maximum block size in bytes (default: 250000) + + + + Set minimum block size in bytes (default: 0) + Set minimum block size in bytes (default: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + Signing transaction failed + Signing transaction failed + + + + Specify connection timeout in milliseconds (default: 5000) + Specify connection timeout in milliseconds (default: 5000) + + + + System error: + System error: + + + + Transaction amount too small + Transaction amount too small + + + + Transaction amounts must be positive + Transaction amounts must be positive + + + + Transaction too large + Transaction too large + + + + Use UPnP to map the listening port (default: 0) + Use UPnP to map the listening port (default: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Use UPnP to map the listening port (default: 1 when listening) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Use proxy to reach tor hidden services (default: same as -proxy) + + + + Username for JSON-RPC connections + Username for JSON-RPC connections + + + + Warning + Warning + + + + Warning: This version is obsolete, upgrade required! + Warning: This version is obsolete, upgrade required! + + + + You need to rebuild the databases using -reindex to change -txindex + You need to rebuild the databases using -reindex to change -txindex + + + + wallet.dat corrupt, salvage failed + wallet.dat corrupt, salvage failed + + + + Password for JSON-RPC connections + Password for JSON-RPC connections + + + + Allow JSON-RPC connections from specified IP address + Allow JSON-RPC connections from specified IP address + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Send commands to node running on <ip> (default: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Execute command when the best block changes (%s in cmd is replaced by block hash) + + + + Upgrade wallet to latest format + Upgrade wallet to latest format + + + + Set key pool size to <n> (default: 100) + Set key pool size to <n> (default: 100) + + + + Rescan the block chain for missing wallet transactions + Rescan the block chain for missing wallet transactions + + + + Use OpenSSL (https) for JSON-RPC connections + Use OpenSSL (https) for JSON-RPC connections + + + + Server certificate file (default: server.cert) + Server certificate file (default: server.cert) + + + + Server private key (default: server.pem) + Server private key (default: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + This help message + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Unable to bind to %s on this computer (bind returned error %d, %s) + + + + Connect through socks proxy + Connect through socks proxy + + + + Allow DNS lookups for -addnode, -seednode and -connect + Allow DNS lookups for -addnode, -seednode and -connect + + + + Loading addresses... + Loading addresses... + + + + Error loading wallet.dat: Wallet corrupted + Error loading wallet.dat: Wallet corrupted + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Wallet needed to be rewritten: restart CryptoLottoToken to complete + + + + Error loading wallet.dat + Error loading wallet.dat + + + + Invalid -proxy address: '%s' + Invalid -proxy address: '%s' + + + + Unknown network specified in -onlynet: '%s' + Unknown network specified in -onlynet: '%s' + + + + Unknown -socks proxy version requested: %i + Unknown -socks proxy version requested: %i + + + + Cannot resolve -bind address: '%s' + Cannot resolve -bind address: '%s' + + + + Cannot resolve -externalip address: '%s' + Cannot resolve -externalip address: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + Invalid amount for -paytxfee=<amount>: '%s' + + + + Invalid amount + Invalid amount + + + + Insufficient funds + Insufficient funds + + + + Loading block index... + Loading block index... + + + + Add a node to connect to and attempt to keep the connection open + Add a node to connect to and attempt to keep the connection open + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + + + + Fee per KB to add to transactions you send + Fee per KB to add to transactions you send + + + + Loading wallet... + Loading wallet... + + + + Cannot downgrade wallet + Cannot downgrade wallet + + + + Cannot write default address + Cannot write default address + + + + Rescanning... + Rescanning... + + + + Done loading + Done loading + + + + To use the %s option + To use the %s option + + + + Error + Error + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + + + diff --git a/src/qt/locale/bitcoin_eo.ts b/src/qt/locale/bitcoin_eo.ts new file mode 100644 index 0000000..9cd2121 --- /dev/null +++ b/src/qt/locale/bitcoin_eo.ts @@ -0,0 +1,2917 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + Pri CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b>-a versio + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + Adresaro + + + + Double-click to edit address or label + Duoble-klaku por redakti adreson aŭ etikedon + + + + Create a new address + Kreu novan adreson + + + + Copy the currently selected address to the system clipboard + Kopiu elektitan adreson al la tondejo + + + + &New Address + &Nova Adreso + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + + + + + &Copy Address + &Kopiu Adreson + + + + Show &QR Code + + + + + Sign a message to prove you own a CryptoLottoToken address + + + + + Sign &Message + + + + + Delete the currently selected address from the list + + + + + Export the data in the current tab to a file + + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + + + + + &Verify Message + + + + + &Delete + &Forviŝu + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + Kopiu &Etikedon + + + + &Edit + &Redaktu + + + + Send &Coins + + + + + Export Address Book Data + Eksportu Adresarajn Datumojn + + + + Comma separated file (*.csv) + Diskoma dosiero (*.csv) + + + + Error exporting + Eraro dum eksportado + + + + Could not write to file %1. + Ne eblis skribi al dosiero %1. + + + + AddressTableModel + + + Label + Etikedo + + + + Address + Adreso + + + + (no label) + (ne etikedo) + + + + AskPassphraseDialog + + + Passphrase Dialog + + + + + Enter passphrase + Enigu pasfrazon + + + + New passphrase + Nova pasfrazo + + + + Repeat new passphrase + Ripetu novan pasfrazon + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Enigu novan pasfrazon por la monujo.<br/>Bonvolu, uzu pasfrazon kun <b>10 aŭ pli hazardaj signoj</b>, aŭ <b>ok aŭ pli vortoj</b>. + + + + Encrypt wallet + Ĉifru monujon + + + + This operation needs your wallet passphrase to unlock the wallet. + Ĉi tiu operacio devas vian monujan pasfrazon, por malŝlosi la monujon. + + + + Unlock wallet + Malŝlosu monujon + + + + This operation needs your wallet passphrase to decrypt the wallet. + Ĉi tiu operacio devas vian monujan pasfrazon, por malĉifri la monujon. + + + + Decrypt wallet + Malĉifru monujon + + + + Change passphrase + Anstataŭigu pasfrazon + + + + Enter the old and new passphrase to the wallet. + Enigu la malnovan kaj novan monujan pasfrazon. + + + + Confirm wallet encryption + Konfirmu ĉifrado de monujo + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + + + + + Are you sure you wish to encrypt your wallet? + + + + + 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. + + + + + + Warning: The Caps Lock key is on! + + + + + + Wallet encrypted + Monujo ĉifrita + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + + + + + + + + Wallet encryption failed + Monujo ĉifrado fiaskis + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Ĉifrado de monujo fiaskis, kaŭze de interna eraro. Via monujo ne ĉifritas. + + + + + The supplied passphrases do not match. + La pasfrazoj enigitaj ne samas. + + + + Wallet unlock failed + Monujo malŝlosado fiaskis + + + + + + The passphrase entered for the wallet decryption was incorrect. + La pasfrazo enigita por ĉifrado de monujo ne konformas. + + + + Wallet decryption failed + Monujo malĉifrado fiaskis + + + + Wallet passphrase was successfully changed. + + + + + BitcoinGUI + + + Sign &message... + Subskribu &mesaĝon... + + + + Synchronizing with network... + Sinkronigante kun reto... + + + + &Overview + &Superrigardo + + + + Show general overview of wallet + + + + + &Transactions + &Transakcioj + + + + Browse transaction history + Esploru historion de transakcioj + + + + Edit the list of stored addresses and labels for sending + + + + + Show the list of addresses for receiving payments + + + + + E&xit + &Eliru + + + + Quit application + Eliru de aplikaĵo + + + + Show information about CryptoLottoToken + Vidigu informaĵon pri Bitmono + + + + About &Qt + Pri &QT + + + + Show information about Qt + Vidigu informaĵon pri Qt + + + + &Options... + &Opcioj... + + + + &Encrypt Wallet... + &Ĉifru Monujon... + + + + &Backup Wallet... + + + + + &Change Passphrase... + &Anstataŭigu pasfrazon... + + + + Importing blocks from disk... + + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + + + + + Modify configuration options for CryptoLottoToken + + + + + Backup wallet to another location + + + + + Change the passphrase used for wallet encryption + + + + + &Debug window + + + + + Open debugging and diagnostic console + + + + + &Verify message... + &Kontrolu mesaĝon... + + + + + CryptoLottoToken + + + + + Wallet + Monujo + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + + + + + &Show / Hide + + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + &Dosiero + + + + &Settings + &Agordoj + + + + &Help + &Helpo + + + + Tabs toolbar + + + + + + [testnet] + + + + + CryptoLottoToken client + + + + + %n active connection(s) to CryptoLottoToken network + + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + + + + + %n hour(s) + %n horo%n horoj + + + + %n day(s) + %n tago%n tagoj + + + + %n week(s) + %n semajno%n semajnoj + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + Eraro + + + + Warning + + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + Ĝisdata + + + + Catching up... + Ĝisdatigante... + + + + Confirm transaction fee + + + + + Sent transaction + Sendita transakcio + + + + Incoming transaction + Envenanta transakcio + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + + + + + + URI handling + + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Monujo estas <b>ĉifrita</b> kaj nun <b>malŝlosita</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Monujo estas <b>ĉifrita</b> kaj nun <b>ŝlosita</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + Reta Averto + + + + EditAddressDialog + + + Edit Address + Redaktu Adreson + + + + &Label + &Etikedo + + + + The label associated with this address book entry + La etikedo interrilatita kun ĉi tiun adreso + + + + &Address + &Adreso + + + + The address associated with this address book entry. This can only be modified for sending addresses. + + + + + New receiving address + + + + + New sending address + + + + + Edit receiving address + + + + + Edit sending address + + + + + The entered address "%1" is already in the address book. + La adreso enigita "%1" jam ekzistas en la adresaro. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + + + + + Could not unlock wallet. + Ne eblis malŝlosi monujon + + + + New key generation failed. + + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + versio + + + + Usage: + + + + + command-line options + + + + + UI options + + + + + Set language, for example "de_DE" (default: system locale) + + + + + Start minimized + + + + + Show splash screen on startup (default: 1) + + + + + OptionsDialog + + + Options + Opcioj + + + + &Main + + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + + + + + Automatically start CryptoLottoToken after logging in to the system. + + + + + &Start CryptoLottoToken on system login + + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + + + + + Map port using &UPnP + + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + + + + + &Connect through SOCKS proxy: + + + + + Proxy &IP: + + + + + IP address of the proxy (e.g. 127.0.0.1) + + + + + &Port: + + + + + Port of the proxy (e.g. 9050) + + + + + SOCKS &Version: + + + + + SOCKS version of the proxy (e.g. 5) + + + + + &Window + + + + + Show only a tray icon after minimizing the window. + + + + + &Minimize to the tray instead of the taskbar + + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + + + + + M&inimize on close + + + + + &Display + + + + + User Interface &language: + + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + + + + + &Unit to show amounts in: + + + + + Choose the default subdivision unit to show in the interface and when sending coins. + + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + + + &Display addresses in transaction list + + + + + &OK + &OK + + + + &Cancel + &Nuligu + + + + &Apply + &Apliku + + + + default + + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + + + + + + This setting will take effect after restarting CryptoLottoToken. + + + + + The supplied proxy address is invalid. + + + + + OverviewPage + + + Form + + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + + + + + Balance: + + + + + Unconfirmed: + Nekonfirmita: + + + + Wallet + Monujo + + + + Immature: + + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + <b>Lastaj transakcioj</b> + + + + Your current balance + + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + + + + + + out of sync + + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + + + + + Request Payment + + + + + Amount: + + + + + Label: + Etikedo: + + + + Message: + + + + + &Save As... + + + + + Error encoding URI into QR Code. + + + + + The entered amount is invalid, please check. + + + + + Resulting URI too long, try to reduce the text for label / message. + + + + + Save QR Code + + + + + PNG Images (*.png) + + + + + RPCConsole + + + Client name + + + + + + + + + + + + + + N/A + + + + + Client version + + + + + &Information + + + + + Using OpenSSL version + + + + + Startup time + + + + + Network + Reto + + + + Number of connections + + + + + On testnet + + + + + Block chain + + + + + Current number of blocks + + + + + Estimated total blocks + + + + + Last block time + + + + + &Open + &Malfermu + + + + Command-line options + + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + + &Show + + + + + &Console + + + + + Build date + + + + + CryptoLottoToken - Debug window + + + + + CryptoLottoToken Core + + + + + Debug log file + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + + + + + Welcome to the CryptoLottoToken RPC console. + + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + + + + + Type <b>help</b> for an overview of available commands. + + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Sendu Monojn + + + + Send to multiple recipients at once + Sendu samtempe al multaj ricevantoj + + + + Add &Recipient + + + + + Remove all transaction fields + + + + + Clear &All + + + + + Balance: + + + + + 123.456 BTC + 123,456 BTC + + + + Confirm the send action + + + + + S&end + + + + + <b>%1</b> to %2 (%3) + <b>%1</b> al %2 (%3) + + + + Confirm send coins + + + + + Are you sure you want to send %1? + Ĉu vi vere volas sendi %1? + + + + and + kaj + + + + The recipient address is not valid, please recheck. + + + + + The amount to pay must be larger than 0. + + + + + The amount exceeds your balance. + + + + + The total exceeds your balance when the %1 transaction fee is included. + + + + + Duplicate address found, can only send to each address once per send operation. + + + + + Error: Transaction creation failed! + + + + + 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. + + + + + SendCoinsEntry + + + Form + + + + + A&mount: + + + + + Pay &To: + + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + + + + + &Label: + &Etikedo: + + + + Choose address from address book + Elektu adreson el adresaro + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Algluu adreson de tondejo + + + + Alt+P + Alt+P + + + + Remove this recipient + Forigu ĉi tiun ricevanton + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + + + + + &Sign Message + + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Choose an address from the address book + + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Algluu adreson de tondejo + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + + + + + Signature + + + + + Copy the current signature to the system clipboard + + + + + Sign the message to prove you own this CryptoLottoToken address + + + + + Sign &Message + + + + + Reset all sign message fields + + + + + + Clear &All + + + + + &Verify Message + + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + + + + + Verify &Message + + + + + Reset all verify message fields + + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Click "Sign Message" to generate signature + + + + + Enter CryptoLottoToken signature + + + + + + The entered address is invalid. + + + + + + + + Please check the address and try again. + + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + + + + + Private key for the entered address is not available. + + + + + Message signing failed. + + + + + Message signed. + + + + + The signature could not be decoded. + + + + + + Please check the signature and try again. + + + + + The signature did not match the message digest. + + + + + Message verification failed. + + + + + Message verified. + + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + + + + + TransactionDesc + + + Open until %1 + + + + + %1/offline + + + + + %1/unconfirmed + %1/nekonfirmita + + + + %1 confirmations + %1 konfirmoj + + + + Status + + + + + , broadcast through %n node(s) + + + + + Date + Dato + + + + Source + + + + + Generated + + + + + + From + + + + + + + To + + + + + + own address + + + + + label + + + + + + + + + Credit + + + + + matures in %n more block(s) + + + + + not accepted + + + + + + + + Debit + + + + + Transaction fee + + + + + Net amount + + + + + Message + + + + + Comment + + + + + Transaction ID + + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + + + + + Debug information + + + + + Transaction + + + + + Inputs + + + + + Amount + Sumo + + + + true + + + + + false + + + + + , has not been successfully broadcast yet + , ankoraŭ ne elsendita sukcese + + + + Open for %n more block(s) + + + + + unknown + nekonata + + + + TransactionDescDialog + + + Transaction details + Transakciaj detaloj + + + + This pane shows a detailed description of the transaction + + + + + TransactionTableModel + + + Date + Dato + + + + Type + Tipo + + + + Address + Adreso + + + + Amount + Sumo + + + + Open for %n more block(s) + + + + + Open until %1 + + + + + Offline (%1 confirmations) + + + + + Unconfirmed (%1 of %2 confirmations) + + + + + Confirmed (%1 confirmations) + + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + + + + + Generated but not accepted + + + + + Received with + Ricevita kun + + + + Received from + Ricevita de + + + + Sent to + Sendita al + + + + Payment to yourself + Pago al vi mem + + + + Mined + Minita + + + + (n/a) + + + + + Transaction status. Hover over this field to show number of confirmations. + + + + + Date and time that the transaction was received. + + + + + Type of transaction. + Transakcia tipo. + + + + Destination address of transaction. + + + + + Amount removed from or added to balance. + + + + + TransactionView + + + + All + Ĉiuj + + + + Today + Hodiaŭ + + + + This week + + + + + This month + + + + + Last month + + + + + This year + + + + + Range... + + + + + Received with + Ricevita kun + + + + Sent to + Sendita al + + + + To yourself + Al vi mem + + + + Mined + Minita + + + + Other + + + + + Enter address or label to search + + + + + Min amount + + + + + Copy address + Kopiu adreson + + + + Copy label + + + + + Copy amount + + + + + Copy transaction ID + + + + + Edit label + Redaktu etikedon + + + + Show transaction details + + + + + Export Transaction Data + + + + + Comma separated file (*.csv) + Diskoma dosiero (*.csv) + + + + Confirmed + Konfirmita + + + + Date + Dato + + + + Type + Tipo + + + + Label + Etikedo + + + + Address + Adreso + + + + Amount + Sumo + + + + ID + + + + + Error exporting + Eraro dum eksportado + + + + Could not write to file %1. + Ne eblis skribi al dosiero %1. + + + + Range: + + + + + to + + + + + WalletModel + + + Send Coins + + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + CryptoLottoToken-a versio + + + + Usage: + + + + + Send command to -server or CryptoLottoTokend + + + + + List commands + Listigu instrukciojn + + + + Get help for a command + + + + + Options: + Opcioj: + + + + Specify configuration file (default: CryptoLottoToken.conf) + + + + + Specify pid file (default: CryptoLottoTokend.pid) + + + + + Specify data directory + + + + + Set database cache size in megabytes (default: 25) + + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + + + + + Maintain at most <n> connections to peers (default: 125) + + + + + Connect to a node to retrieve peer addresses, and disconnect + + + + + Specify your own public address + + + + + Threshold for disconnecting misbehaving peers (default: 100) + + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + + + + + Accept command line and JSON-RPC commands + + + + + Run in the background as a daemon and accept commands + + + + + Use the test network + + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + + + + + Connect only to the specified node(s) + + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + + + + + Select the version of socks proxy to use (4-5, default: 5) + + + + + Send trace/debug info to console instead of debug.log file + + + + + Send trace/debug info to debugger + + + + + Set maximum block size in bytes (default: 250000) + + + + + Set minimum block size in bytes (default: 0) + + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + + + + + Use UPnP to map the listening port (default: 1 when listening) + + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + + + + + Allow JSON-RPC connections from specified IP address + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + + + + + Upgrade wallet to latest format + + + + + Set key pool size to <n> (default: 100) + + + + + Rescan the block chain for missing wallet transactions + + + + + Use OpenSSL (https) for JSON-RPC connections + + + + + Server certificate file (default: server.cert) + + + + + Server private key (default: server.pem) + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + + This help message + + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + + + + + Connect through socks proxy + + + + + Allow DNS lookups for -addnode, -seednode and -connect + + + + + Loading addresses... + Ŝarĝante adresojn... + + + + Error loading wallet.dat: Wallet corrupted + + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + + + + + Error loading wallet.dat + + + + + Invalid -proxy address: '%s' + + + + + Unknown network specified in -onlynet: '%s' + + + + + Unknown -socks proxy version requested: %i + + + + + Cannot resolve -bind address: '%s' + + + + + Cannot resolve -externalip address: '%s' + + + + + Invalid amount for -paytxfee=<amount>: '%s' + + + + + Invalid amount + + + + + Insufficient funds + + + + + Loading block index... + Ŝarĝante blok-indekson... + + + + Add a node to connect to and attempt to keep the connection open + + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + + + + + Fee per KB to add to transactions you send + + + + + Loading wallet... + Ŝarĝante monujon... + + + + Cannot downgrade wallet + + + + + Cannot write default address + + + + + Rescanning... + + + + + Done loading + Ŝarĝado finitas + + + + To use the %s option + Por uzi la opcion %s + + + + Error + Eraro + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_es.qm b/src/qt/locale/bitcoin_es.qm new file mode 100644 index 0000000..b201931 Binary files /dev/null and b/src/qt/locale/bitcoin_es.qm differ diff --git a/src/qt/locale/bitcoin_es.ts b/src/qt/locale/bitcoin_es.ts new file mode 100644 index 0000000..2613ca9 --- /dev/null +++ b/src/qt/locale/bitcoin_es.ts @@ -0,0 +1,2959 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + Acerca de CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + Versión de <b>CryptoLottoToken</b> + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +Este es un software experimental. + +Distribuido bajo la licencia MIT/X11, vea el archivo adjunto +COPYING o http://www.opensource.org/licenses/mit-license.php. + +Este producto incluye software desarrollado por OpenSSL Project para su uso en +el OpenSSL Toolkit (http://www.openssl.org/) y software criptográfico escrito por +Eric Young (eay@cryptsoft.com) y el software UPnP escrito por Thomas Bernard. + + + + Copyright + Copyright + + + + The CryptoLottoToken developers + Los programadores CryptoLottoToken + + + + AddressBookPage + + + Address Book + Libreta de direcciones + + + + Double-click to edit address or label + Haga doble clic para editar una dirección o etiqueta + + + + Create a new address + Crear una nueva dirección + + + + Copy the currently selected address to the system clipboard + Copiar la dirección seleccionada al portapapeles del sistema + + + + &New Address + &Añadir dirección + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Estas son sus direcciones CryptoLottoToken para recibir pagos. Puede utilizar una diferente por cada persona emisora para saber quién le está pagando. + + + + &Copy Address + &Copiar dirección + + + + Show &QR Code + Mostrar código &QR + + + + Sign a message to prove you own a CryptoLottoToken address + Firmar un mensaje para demostrar que se posee una dirección CryptoLottoToken + + + + Sign &Message + &Firmar mensaje + + + + Delete the currently selected address from the list + Borrar de la lista la dirección seleccionada + + + + Export the data in the current tab to a file + Exportar a un archivo los datos de esta pestaña + + + + &Export + &Exportar + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Verificar un mensaje para comprobar que fue firmado con la dirección CryptoLottoToken indicada + + + + &Verify Message + &Verificar mensaje + + + + &Delete + &Eliminar + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + Estas son sus direcciones CryptoLottoToken para enviar pagos. Compruebe siempre la cantidad y la dirección receptora antes de transferir monedas. + + + + Copy &Label + Copiar &etiqueta + + + + &Edit + &Editar + + + + Send &Coins + Enviar &monedas + + + + Export Address Book Data + Exportar datos de la libreta de direcciones + + + + Comma separated file (*.csv) + Archivos de columnas separadas por coma (*.csv) + + + + Error exporting + Error al exportar + + + + Could not write to file %1. + No se pudo escribir en el archivo %1. + + + + AddressTableModel + + + Label + Etiqueta + + + + Address + Dirección + + + + (no label) + (sin etiqueta) + + + + AskPassphraseDialog + + + Passphrase Dialog + Diálogo de contraseña + + + + Enter passphrase + Introducir contraseña + + + + New passphrase + Nueva contraseña + + + + Repeat new passphrase + Repita la nueva contraseña + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Introduzca la nueva contraseña del monedero.<br/>Por favor elija una con <b>10 o más caracteres aleatorios</b> u <b>ocho o más palabras</b>. + + + + Encrypt wallet + Cifrar el monedero + + + + This operation needs your wallet passphrase to unlock the wallet. + Esta operación requiere su contraseña para desbloquear el monedero. + + + + Unlock wallet + Desbloquear monedero + + + + This operation needs your wallet passphrase to decrypt the wallet. + Esta operación requiere su contraseña para descifrar el monedero. + + + + Decrypt wallet + Descifrar el monedero + + + + Change passphrase + Cambiar contraseña + + + + Enter the old and new passphrase to the wallet. + Introduzca la contraseña anterior del monedero y la nueva. + + + + Confirm wallet encryption + Confirmar cifrado del monedero + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Atencion: ¡Si cifra su monedero y pierde la contraseña perderá <b>TODOS SUS CryptoLottoTokenS</b>!" + + + + Are you sure you wish to encrypt your wallet? + ¿Seguro que desea cifrar su monedero? + + + + 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. + IMPORTANTE: Cualquier copia de seguridad que haya realizado previamente de su archivo de monedero debe reemplazarse con el nuevo archivo de monedero cifrado. Por razones de seguridad, las copias de seguridad previas del archivo de monedero no cifradas serán inservibles en cuanto comience a usar el nuevo monedero cifrado. + + + + + Warning: The Caps Lock key is on! + Aviso: ¡La tecla de bloqueo de mayúsculas está activada! + + + + + Wallet encrypted + Monedero cifrado + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken se cerrará para finalizar el proceso de cifrado. Recuerde que el cifrado de su monedero no puede proteger totalmente sus CryptoLottoTokens de robo por malware que infecte su sistema. + + + + + + + Wallet encryption failed + Ha fallado el cifrado del monedero + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Ha fallado el cifrado del monedero debido a un error interno. El monedero no ha sido cifrado. + + + + + The supplied passphrases do not match. + Las contraseñas no coinciden. + + + + Wallet unlock failed + Ha fallado el desbloqueo del monedero + + + + + + The passphrase entered for the wallet decryption was incorrect. + La contraseña introducida para descifrar el monedero es incorrecta. + + + + Wallet decryption failed + Ha fallado el descifrado del monedero + + + + Wallet passphrase was successfully changed. + Se ha cambiado correctamente la contraseña del monedero. + + + + BitcoinGUI + + + Sign &message... + Firmar &mensaje... + + + + Synchronizing with network... + Sincronizando con la red… + + + + &Overview + &Vista general + + + + Show general overview of wallet + Mostrar vista general del monedero + + + + &Transactions + &Transacciones + + + + Browse transaction history + Examinar el historial de transacciones + + + + Edit the list of stored addresses and labels for sending + Editar la lista de las direcciones y etiquetas almacenadas + + + + Show the list of addresses for receiving payments + Mostrar la lista de direcciones utilizadas para recibir pagos + + + + E&xit + &Salir + + + + Quit application + Salir de la aplicación + + + + Show information about CryptoLottoToken + Mostrar información acerca de CryptoLottoToken + + + + About &Qt + Acerca de &Qt + + + + Show information about Qt + Mostrar información acerca de Qt + + + + &Options... + &Opciones... + + + + &Encrypt Wallet... + &Cifrar monedero… + + + + &Backup Wallet... + Copia de &respaldo del monedero... + + + + &Change Passphrase... + &Cambiar la contraseña… + + + + Importing blocks from disk... + Importando bloques de disco... + + + + Reindexing blocks on disk... + Reindexando bloques en disco... + + + + Send coins to a CryptoLottoToken address + Enviar monedas a una dirección CryptoLottoToken + + + + Modify configuration options for CryptoLottoToken + Modificar las opciones de configuración de CryptoLottoToken + + + + Backup wallet to another location + Copia de seguridad del monedero en otra ubicación + + + + Change the passphrase used for wallet encryption + Cambiar la contraseña utilizada para el cifrado del monedero + + + + &Debug window + Ventana de &depuración + + + + Open debugging and diagnostic console + Abrir la consola de depuración y diagnóstico + + + + &Verify message... + &Verificar mensaje... + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Monedero + + + + &Send + &Enviar + + + + &Receive + &Recibir + + + + &Addresses + &Direcciones + + + + &About CryptoLottoToken + &Acerca de CryptoLottoToken + + + + &Show / Hide + Mo&strar/ocultar + + + + Show or hide the main Window + Mostrar u ocultar la ventana principal + + + + Encrypt the private keys that belong to your wallet + Cifrar las claves privadas de su monedero + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Firmar mensajes con sus direcciones CryptoLottoToken para demostrar la propiedad + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Verificar mensajes comprobando que están firmados con direcciones CryptoLottoToken concretas + + + + &File + &Archivo + + + + &Settings + &Configuración + + + + &Help + A&yuda + + + + Tabs toolbar + Barra de pestañas + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + Cliente CryptoLottoToken + + + + %n active connection(s) to CryptoLottoToken network + %n conexión activa hacia la red CryptoLottoToken%n conexiones activas hacia la red CryptoLottoToken + + + + No block source available... + Ninguna fuente de bloques disponible ... + + + + Processed %1 of %2 (estimated) blocks of transaction history. + Se han procesado %1 de %2 bloques (estimados) del historial de transacciones. + + + + Processed %1 blocks of transaction history. + Procesados %1 bloques del historial de transacciones. + + + + %n hour(s) + %n hora%n horas + + + + %n day(s) + %n día%n días + + + + %n week(s) + %n semana%n semanas + + + + %1 behind + %1 atrás + + + + Last received block was generated %1 ago. + El último bloque recibido fue generado hace %1. + + + + Transactions after this will not yet be visible. + Las transacciones posteriores a esta aún no están visibles. + + + + Error + Error + + + + Warning + Aviso + + + + Information + Información + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + Esta transacción supera el límite de tamaño. Puede enviarla con una comisión de %1, destinada a los nodos que procesen su transacción para contribuir al mantenimiento de la red. ¿Desea pagar esta comisión? + + + + Up to date + Actualizado + + + + Catching up... + Actualizando... + + + + Confirm transaction fee + Confirme la tarifa de la transacción + + + + Sent transaction + Transacción enviada + + + + Incoming transaction + Transacción entrante + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Fecha: %1 +Cantidad: %2 +Tipo: %3 +Dirección: %4 + + + + + + URI handling + Gestión de URI + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + ¡No se puede interpretar la URI! Esto puede deberse a una dirección CryptoLottoToken inválida o a parámetros de URI mal formados. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + El monedero está <b>cifrado</b> y actualmente <b>desbloqueado</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + El monedero está <b>cifrado</b> y actualmente <b>bloqueado</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + Ha ocurrido un error crítico. CryptoLottoToken ya no puede continuar con seguridad y se cerrará. + + + + ClientModel + + + Network Alert + Alerta de red + + + + EditAddressDialog + + + Edit Address + Editar Dirección + + + + &Label + &Etiqueta + + + + The label associated with this address book entry + La etiqueta asociada con esta entrada en la libreta + + + + &Address + &Dirección + + + + The address associated with this address book entry. This can only be modified for sending addresses. + La dirección asociada con esta entrada en la guía. Solo puede ser modificada para direcciones de envío. + + + + New receiving address + Nueva dirección para recibir + + + + New sending address + Nueva dirección para enviar + + + + Edit receiving address + Editar dirección de recepción + + + + Edit sending address + Editar dirección de envío + + + + The entered address "%1" is already in the address book. + La dirección introducida "%1" ya está presente en la libreta de direcciones. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + La dirección introducida "%1" no es una dirección CryptoLottoToken válida. + + + + Could not unlock wallet. + No se pudo desbloquear el monedero. + + + + New key generation failed. + Ha fallado la generación de la nueva clave. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + versión + + + + Usage: + Uso: + + + + command-line options + opciones de la línea de órdenes + + + + UI options + Opciones GUI + + + + Set language, for example "de_DE" (default: system locale) + Establecer el idioma, por ejemplo, "es_ES" (predeterminado: configuración regional del sistema) + + + + Start minimized + Arrancar minimizado + + + + Show splash screen on startup (default: 1) + Mostrar pantalla de bienvenida en el inicio (predeterminado: 1) + + + + OptionsDialog + + + Options + Opciones + + + + &Main + &Principal + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + Tarifa de transacción opcional por kB que ayuda a asegurar que sus transacciones sean procesadas rápidamente. La mayoría de transacciones son de 1kB. + + + + Pay transaction &fee + Comisión de &transacciones + + + + Automatically start CryptoLottoToken after logging in to the system. + Iniciar CryptoLottoToken automáticamente al encender el sistema. + + + + &Start CryptoLottoToken on system login + &Iniciar CryptoLottoToken al iniciar el sistema + + + + Reset all client options to default. + Restablecer todas las opciones del cliente a las predeterminadas. + + + + &Reset Options + &Restablecer opciones + + + + &Network + &Red + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Abrir automáticamente el puerto del cliente CryptoLottoToken en el router. Esta opción solo funciona si el router admite UPnP y está activado. + + + + Map port using &UPnP + Mapear el puerto usando &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Conectar a la red CryptoLottoToken a través de un proxy SOCKS (ej. para conectar con la red Tor) + + + + &Connect through SOCKS proxy: + &Conectar a través de un proxy SOCKS: + + + + Proxy &IP: + Dirección &IP del proxy: + + + + IP address of the proxy (e.g. 127.0.0.1) + Dirección IP del proxy (ej. 127.0.0.1) + + + + &Port: + &Puerto: + + + + Port of the proxy (e.g. 9050) + Puerto del servidor proxy (ej. 9050) + + + + SOCKS &Version: + &Versión SOCKS: + + + + SOCKS version of the proxy (e.g. 5) + Versión del proxy SOCKS (ej. 5) + + + + &Window + &Ventana + + + + Show only a tray icon after minimizing the window. + Minimizar la ventana a la bandeja de iconos del sistema. + + + + &Minimize to the tray instead of the taskbar + &Minimizar a la bandeja en vez de a la barra de tareas + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Minimizar en lugar de salir de la aplicación al cerrar la ventana.Cuando esta opción está activa, la aplicación solo se puede cerrar seleccionando Salir desde el menú. + + + + M&inimize on close + M&inimizar al cerrar + + + + &Display + &Interfaz + + + + User Interface &language: + I&dioma de la interfaz de usuario + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + El idioma de la interfaz de usuario puede establecerse aquí. Este ajuste se aplicará cuando se reinicie CryptoLottoToken. + + + + &Unit to show amounts in: + Mostrar las cantidades en la &unidad: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Elegir la subdivisión predeterminada para mostrar cantidades en la interfaz y cuando se envían monedas. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Mostrar o no las direcciones CryptoLottoToken en la lista de transacciones. + + + + &Display addresses in transaction list + &Mostrar las direcciones en la lista de transacciones + + + + &OK + &Aceptar + + + + &Cancel + &Cancelar + + + + &Apply + &Aplicar + + + + default + predeterminado + + + + Confirm options reset + Confirme el restablecimiento de las opciones + + + + Some settings may require a client restart to take effect. + Algunas configuraciones pueden requerir un reinicio del cliente para que sean efectivas. + + + + Do you want to proceed? + ¿Quiere proceder? + + + + + Warning + Aviso + + + + + This setting will take effect after restarting CryptoLottoToken. + Esta configuración tendrá efecto tras reiniciar CryptoLottoToken. + + + + The supplied proxy address is invalid. + La dirección proxy indicada es inválida. + + + + OverviewPage + + + Form + Desde + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + La información mostrada puede estar desactualizada. Su monedero se sincroniza automáticamente con la red CryptoLottoToken después de que se haya establecido una conexión , pero este proceso aún no se ha completado. + + + + Balance: + Saldo: + + + + Unconfirmed: + No confirmado(s): + + + + Wallet + Monedero + + + + Immature: + No disponible: + + + + Mined balance that has not yet matured + Saldo recién minado que aún no está disponible. + + + + <b>Recent transactions</b> + <b>Movimientos recientes</b> + + + + Your current balance + Su saldo actual + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Total de las transacciones que faltan por confirmar y que no contribuyen al saldo actual + + + + + out of sync + desincronizado + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + No se pudo iniciar CryptoLottoToken: manejador de pago-al-clic + + + + QRCodeDialog + + + QR Code Dialog + Diálogo de códigos QR + + + + Request Payment + Solicitud de pago + + + + Amount: + Cuantía: + + + + Label: + Etiqueta: + + + + Message: + Mensaje: + + + + &Save As... + &Guardar como... + + + + Error encoding URI into QR Code. + Error al codificar la URI en el código QR. + + + + The entered amount is invalid, please check. + La cantidad introducida es inválida. Compruébela, por favor. + + + + Resulting URI too long, try to reduce the text for label / message. + URI esultante demasiado larga. Intente reducir el texto de la etiqueta / mensaje. + + + + Save QR Code + Guardar código QR + + + + PNG Images (*.png) + Imágenes PNG (*.png) + + + + RPCConsole + + + Client name + Nombre del cliente + + + + + + + + + + + + + N/A + N/D + + + + Client version + Versión del cliente + + + + &Information + &Información + + + + Using OpenSSL version + Utilizando la versión OpenSSL + + + + Startup time + Hora de inicio + + + + Network + Red + + + + Number of connections + Número de conexiones + + + + On testnet + En la red de pruebas + + + + Block chain + Cadena de bloques + + + + Current number of blocks + Número actual de bloques + + + + Estimated total blocks + Bloques totales estimados + + + + Last block time + Hora del último bloque + + + + &Open + &Abrir + + + + Command-line options + Opciones de la línea de órdenes + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Mostrar el mensaje de ayuda de CryptoLottoToken-Qt que enumera las opciones disponibles de línea de órdenes para CryptoLottoToken. + + + + &Show + &Mostrar + + + + &Console + &Consola + + + + Build date + Fecha de compilación + + + + CryptoLottoToken - Debug window + CryptoLottoToken - Ventana de depuración + + + + CryptoLottoToken Core + Núcleo de CryptoLottoToken + + + + Debug log file + Archivo de registro de depuración + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + Abrir el archivo de registro de depuración en el directorio actual de datos. Esto puede llevar varios segundos para archivos de registro grandes. + + + + Clear console + Borrar consola + + + + Welcome to the CryptoLottoToken RPC console. + Bienvenido a la consola RPC de CryptoLottoToken + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Use las flechas arriba y abajo para navegar por el historial y <b>Control+L</b> para limpiar la pantalla. + + + + Type <b>help</b> for an overview of available commands. + Escriba <b>help</b> para ver un resumen de los comandos disponibles. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Enviar monedas + + + + Send to multiple recipients at once + Enviar a multiples destinatarios de una vez + + + + Add &Recipient + Añadir &destinatario + + + + Remove all transaction fields + Eliminar todos los campos de las transacciones + + + + Clear &All + Limpiar &todo + + + + Balance: + Saldo: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Confirmar el envío + + + + S&end + &Enviar + + + + <b>%1</b> to %2 (%3) + <b>%1</b> a %2 (%3) + + + + Confirm send coins + Confirmar el envío de monedas + + + + Are you sure you want to send %1? + ¿Está seguro de que desea enviar %1? + + + + and + y + + + + The recipient address is not valid, please recheck. + La dirección de recepción no es válida, compruébela de nuevo. + + + + The amount to pay must be larger than 0. + La cantidad por pagar tiene que ser mayor de 0. + + + + The amount exceeds your balance. + La cantidad sobrepasa su saldo. + + + + The total exceeds your balance when the %1 transaction fee is included. + El total sobrepasa su saldo cuando se incluye la tasa de envío de %1 + + + + Duplicate address found, can only send to each address once per send operation. + Se ha encontrado una dirección duplicada. Solo se puede enviar a cada dirección una vez por operación de envío. + + + + Error: Transaction creation failed! + Error: ¡Ha fallado la creación de la transacción! + + + + 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. + Error: transacción rechazada. Puede haber ocurrido si alguna de las monedas ya estaba gastada o si ha usado una copia de wallet.dat y las monedas se gastaron en la copia pero no se han marcado así aquí. + + + + SendCoinsEntry + + + Form + Envío + + + + A&mount: + Ca&ntidad: + + + + Pay &To: + &Pagar a: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + La dirección a la que enviar el pago (p. ej. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + Etiquete esta dirección para añadirla a la libreta + + + + &Label: + &Etiqueta: + + + + Choose address from address book + Elija una dirección de la libreta de direcciones + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Pegar dirección desde portapapeles + + + + Alt+P + Alt+P + + + + Remove this recipient + Eliminar destinatario + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Introduzca una dirección CryptoLottoToken (ej. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Firmas - Firmar / verificar un mensaje + + + + &Sign Message + &Firmar mensaje + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Puede firmar mensajes con sus direcciones para demostrar que las posee. Tenga cuidado de no firmar cualquier cosa vaga, ya que los ataques de phishing pueden tratar de engañarle para suplantar su identidad. Firme solo declaraciones totalmente detalladas con las que usted esté de acuerdo. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + La dirección con la que firmar el mensaje (ej. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Elija una dirección de la libreta de direcciones + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Pegar dirección desde portapapeles + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Introduzca el mensaje que desea firmar aquí + + + + Signature + Firma + + + + Copy the current signature to the system clipboard + Copiar la firma actual al portapapeles del sistema + + + + Sign the message to prove you own this CryptoLottoToken address + Firmar el mensaje para demostrar que se posee esta dirección CryptoLottoToken + + + + Sign &Message + Firmar &mensaje + + + + Reset all sign message fields + Limpiar todos los campos de la firma de mensaje + + + + + Clear &All + Limpiar &todo + + + + &Verify Message + &Verificar mensaje + + + + Enter the signing 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. + Introduzca la dirección para la firma, el mensaje (asegurándose de copiar tal cual los saltos de línea, espacios, tabulaciones, etc.) y la firma a continuación para verificar el mensaje. Tenga cuidado de no asumir más información de lo que dice el propio mensaje firmado para evitar fraudes basados en ataques de tipo man-in-the-middle. + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + La dirección con la que se firmó el mensaje (ej. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Verificar el mensaje para comprobar que fue firmado con la dirección CryptoLottoToken indicada + + + + Verify &Message + Verificar &mensaje + + + + Reset all verify message fields + Limpiar todos los campos de la verificación de mensaje + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Introduzca una dirección CryptoLottoToken (ej. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Haga clic en "Firmar mensaje" para generar la firma + + + + Enter CryptoLottoToken signature + Introduzca una firma CryptoLottoToken + + + + + The entered address is invalid. + La dirección introducida es inválida. + + + + + + + Please check the address and try again. + Verifique la dirección e inténtelo de nuevo. + + + + + The entered address does not refer to a key. + La dirección introducida no corresponde a una clave. + + + + Wallet unlock was cancelled. + Se ha cancelado el desbloqueo del monedero. + + + + Private key for the entered address is not available. + No se dispone de la clave privada para la dirección introducida. + + + + Message signing failed. + Ha fallado la firma del mensaje. + + + + Message signed. + Mensaje firmado. + + + + The signature could not be decoded. + No se puede decodificar la firma. + + + + + Please check the signature and try again. + Compruebe la firma e inténtelo de nuevo. + + + + The signature did not match the message digest. + La firma no coincide con el resumen del mensaje. + + + + Message verification failed. + La verificación del mensaje ha fallado. + + + + Message verified. + Mensaje verificado. + + + + SplashScreen + + + The CryptoLottoToken developers + Los programadores CryptoLottoToken + + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + Abierto hasta %1 + + + + %1/offline + %1/fuera de línea + + + + %1/unconfirmed + %1/no confirmado + + + + %1 confirmations + %1 confirmaciones + + + + Status + Estado + + + + , broadcast through %n node(s) + , transmitir a través de %n nodo, transmitir a través de %n nodos + + + + Date + Fecha + + + + Source + Fuente + + + + Generated + Generado + + + + + From + De + + + + + + To + Para + + + + + own address + dirección propia + + + + label + etiqueta + + + + + + + + Credit + Crédito + + + + matures in %n more block(s) + disponible en %n bloque másdisponible en %n bloques más + + + + not accepted + no aceptada + + + + + + + Debit + Débito + + + + Transaction fee + Comisión de transacción + + + + Net amount + Cantidad neta + + + + Message + Mensaje + + + + Comment + Comentario + + + + Transaction ID + Identificador de transacción + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Las monedas generadas deben esperar 120 bloques antes de que se puedan gastar. Cuando se generó este bloque, se emitió a la red para ser agregado a la cadena de bloques. Si no consigue incorporarse a la cadena, su estado cambiará a "no aceptado" y las monedas no se podrán gastar. Esto puede ocurrir ocasionalmente si otro nodo genera un bloque casi al mismo tiempo que el suyo. + + + + Debug information + Información de depuración + + + + Transaction + Transacción + + + + Inputs + entradas + + + + Amount + Cantidad + + + + true + verdadero + + + + false + falso + + + + , has not been successfully broadcast yet + , todavía no se ha sido difundido satisfactoriamente + + + + Open for %n more block(s) + Abrir para %n bloque másAbrir para %n bloques más + + + + unknown + desconocido + + + + TransactionDescDialog + + + Transaction details + Detalles de transacción + + + + This pane shows a detailed description of the transaction + Esta ventana muestra información detallada sobre la transacción + + + + TransactionTableModel + + + Date + Fecha + + + + Type + Tipo + + + + Address + Dirección + + + + Amount + Cantidad + + + + Open for %n more block(s) + Abrir para %n bloque másAbrir para %n bloques más + + + + Open until %1 + Abierto hasta %1 + + + + Offline (%1 confirmations) + Fuera de línea (%1 confirmaciones) + + + + Unconfirmed (%1 of %2 confirmations) + No confirmado (%1 de %2 confirmaciones) + + + + Confirmed (%1 confirmations) + Confirmado (%1 confirmaciones) + + + + Mined balance will be available when it matures in %n more block(s) + El saldo recién minado estará disponible cuando venza el plazo en %n bloque másEl saldo recién minado estará disponible cuando venza el plazo en %n bloques más + + + + This block was not received by any other nodes and will probably not be accepted! + Este bloque no ha sido recibido por otros nodos y probablemente no sea aceptado! + + + + Generated but not accepted + Generado pero no aceptado + + + + Received with + Recibido con + + + + Received from + Recibidos de + + + + Sent to + Enviado a + + + + Payment to yourself + Pago propio + + + + Mined + Minado + + + + (n/a) + (nd) + + + + Transaction status. Hover over this field to show number of confirmations. + Estado de transacción. Pasa el ratón sobre este campo para ver el número de confirmaciones. + + + + Date and time that the transaction was received. + Fecha y hora en que se recibió la transacción. + + + + Type of transaction. + Tipo de transacción. + + + + Destination address of transaction. + Dirección de destino de la transacción. + + + + Amount removed from or added to balance. + Cantidad retirada o añadida al saldo. + + + + TransactionView + + + + All + Todo + + + + Today + Hoy + + + + This week + Esta semana + + + + This month + Este mes + + + + Last month + Mes pasado + + + + This year + Este año + + + + Range... + Rango... + + + + Received with + Recibido con + + + + Sent to + Enviado a + + + + To yourself + A usted mismo + + + + Mined + Minado + + + + Other + Otra + + + + Enter address or label to search + Introduzca una dirección o etiqueta que buscar + + + + Min amount + Cantidad mínima + + + + Copy address + Copiar dirección + + + + Copy label + Copiar etiqueta + + + + Copy amount + Copiar cuantía + + + + Copy transaction ID + Copiar identificador de transacción + + + + Edit label + Editar etiqueta + + + + Show transaction details + Mostrar detalles de la transacción + + + + Export Transaction Data + Exportar datos de la transacción + + + + Comma separated file (*.csv) + Archivos de columnas separadas por coma (*.csv) + + + + Confirmed + Confirmado + + + + Date + Fecha + + + + Type + Tipo + + + + Label + Etiqueta + + + + Address + Dirección + + + + Amount + Cantidad + + + + ID + ID + + + + Error exporting + Error exportando + + + + Could not write to file %1. + No se pudo escribir en el archivo %1. + + + + Range: + Rango: + + + + to + para + + + + WalletModel + + + Send Coins + Enviar monedas + + + + WalletView + + + &Export + &Exportar + + + + Export the data in the current tab to a file + Exportar a un archivo los datos de esta pestaña + + + + Backup Wallet + Respaldo de monedero + + + + Wallet Data (*.dat) + Datos de monedero (*.dat) + + + + Backup Failed + Ha fallado el respaldo + + + + There was an error trying to save the wallet data to the new location. + Se ha producido un error al intentar guardar los datos del monedero en la nueva ubicación. + + + + Backup Successful + Se ha completado con éxito la copia de respaldo + + + + The wallet data was successfully saved to the new location. + Los datos del monedero se han guardado con éxito en la nueva ubicación. + + + + bitcoin-core + + + CryptoLottoToken version + Versión de CryptoLottoToken + + + + Usage: + Uso: + + + + Send command to -server or CryptoLottoTokend + Envíar comando a -server o CryptoLottoTokend + + + + List commands + Muestra comandos + + + + + Get help for a command + Recibir ayuda para un comando + + + + + Options: + Opciones: + + + + + Specify configuration file (default: CryptoLottoToken.conf) + Especificar archivo de configuración (predeterminado: CryptoLottoToken.conf) + + + + + Specify pid file (default: CryptoLottoTokend.pid) + Especificar archivo pid (predeterminado: CryptoLottoToken.pid) + + + + + Specify data directory + Especificar directorio para los datos + + + + Set database cache size in megabytes (default: 25) + Establecer el tamaño de caché de la base de datos en megabytes (predeterminado: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Escuchar conexiones en <puerto> (predeterminado: 22556 o testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Mantener como máximo <n> conexiones a pares (predeterminado: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Conectar a un nodo para obtener direcciones de pares y desconectar + + + + Specify your own public address + Especifique su propia dirección pública + + + + Threshold for disconnecting misbehaving peers (default: 100) + Umbral para la desconexión de pares con mal comportamiento (predeterminado: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Número de segundos en que se evita la reconexión de pares con mal comportamiento (predeterminado: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + Ha ocurrido un error al configurar el puerto RPC %u para escucha en IPv4: %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Escuchar conexiones JSON-RPC en <puerto> (predeterminado: 22555 o testnet:44555) + + + + Accept command line and JSON-RPC commands + Aceptar comandos consola y JSON-RPC + + + + + Run in the background as a daemon and accept commands + Correr como demonio y aceptar comandos + + + + + Use the test network + Usar la red de pruebas + + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Aceptar conexiones desde el exterior (predeterminado: 1 si no -proxy o -connect) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + %s, debe establecer un valor rpcpassword en el archivo de configuración: +%s +Se recomienda utilizar la siguiente contraseña aleatoria: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(no es necesario recordar esta contraseña) +El nombre de usuario y la contraseña DEBEN NO ser iguales. +Si el archivo no existe, créelo con permisos de archivo de solo lectura. +Se recomienda también establecer alertnotify para recibir notificaciones de problemas. +Por ejemplo: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + Ha ocurrido un error al configurar el puerto RPC %u para escuchar mediante IPv6. Recurriendo a IPv4: %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + Vincular a la dirección dada y escuchar siempre en ella. Utilice la notación [host]:port para IPv6 + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + No se puede bloquear el directorio de datos %s. Probablemente CryptoLottoToken ya se está ejecutando. + + + + 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. + ¡Error: se ha rechazado la transacción! Esto puede ocurrir si ya se han gastado algunas de las monedas del monedero, como ocurriría si hubiera hecho una copia de wallet.dat y se hubieran gastado monedas a partir de la copia, con lo que no se habrían marcado aquí como gastadas. + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + ¡Error: Esta transacción requiere una comisión de al menos %s debido a su monto, complejidad, o al uso de fondos recién recibidos! + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + Ejecutar orden cuando se reciba un aviso relevante (%s en cmd se reemplazará por el mensaje) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Ejecutar comando cuando una transacción del monedero cambia (%s en cmd se remplazará por TxID) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Establecer el tamaño máximo de las transacciones de alta prioridad/comisión baja en bytes (predeterminado:27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + Esta es una versión de pre-prueba - utilícela bajo su propio riesgo. No la utilice para usos comerciales o de minería. + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Aviso: ¡-paytxfee tiene un valor muy alto! Esta es la comisión que pagará si envía una transacción. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + Aviso: ¡Las transacciones mostradas pueden no ser correctas! Puede necesitar una actualización o bien otros nodos necesitan actualizarse. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Precaución: Por favor, ¡revise que la fecha y hora de su ordenador son correctas! Si su reloj está mal, CryptoLottoToken no funcionará correctamente. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Aviso: ¡Error al leer wallet.dat! Todas las claves se han leído correctamente, pero podrían faltar o ser incorrectos los datos de transacciones o las entradas de la libreta de direcciones. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Aviso: ¡Recuperados datos de wallet.dat corrupto! El wallet.dat original se ha guardado como wallet.{timestamp}.bak en %s; si hubiera errores en su saldo o transacciones, deberá restaurar una copia de seguridad. + + + + Attempt to recover private keys from a corrupt wallet.dat + Intento de recuperar claves privadas de un wallet.dat corrupto + + + + Block creation options: + Opciones de creación de bloques: + + + + Connect only to the specified node(s) + Conectar sólo a los nodos (o nodo) especificados + + + + Corrupted block database detected + Corrupción de base de datos de bloques detectada. + + + + Discover own IP address (default: 1 when listening and no -externalip) + Descubrir dirección IP propia (predeterminado: 1 al escuchar sin -externalip) + + + + Do you want to rebuild the block database now? + ¿Quieres reconstruir la base de datos de bloques ahora? + + + + Error initializing block database + Error al inicializar la base de datos de bloques + + + + Error initializing wallet database environment %s! + Error al inicializar el entorno de la base de datos del monedero %s + + + + Error loading block database + Error cargando base de datos de bloques + + + + Error opening block database + Error al abrir base de datos de bloques. + + + + Error: Disk space is low! + Error: ¡Espacio en disco bajo! + + + + Error: Wallet locked, unable to create transaction! + Error: ¡El monedero está bloqueado; no se puede crear la transacción! + + + + Error: system error: + Error: error de sistema: + + + + Failed to listen on any port. Use -listen=0 if you want this. + Ha fallado la escucha en todos los puertos. Use -listen=0 si desea esto. + + + + Failed to read block info + No se ha podido leer la información de bloque + + + + Failed to read block + No se ha podido leer el bloque + + + + Failed to sync block index + No se ha podido sincronizar el índice de bloques + + + + Failed to write block index + No se ha podido escribir en el índice de bloques + + + + Failed to write block info + No se ha podido escribir la información de bloques + + + + Failed to write block + No se ha podido escribir el bloque + + + + Failed to write file info + No se ha podido escribir la información de archivo + + + + Failed to write to coin database + No se ha podido escribir en la base de datos de monedas + + + + Failed to write transaction index + No se ha podido escribir en el índice de transacciones + + + + Failed to write undo data + No se han podido escribir los datos de deshacer + + + + Find peers using DNS lookup (default: 1 unless -connect) + Encontrar pares mediante búsqueda de DNS (predeterminado: 1 salvo con -connect) + + + + Generate coins (default: 0) + Generar monedas (por defecto: 0) + + + + How many blocks to check at startup (default: 288, 0 = all) + Cuántos bloques comprobar al iniciar (predeterminado: 288, 0 = todos) + + + + How thorough the block verification is (0-4, default: 3) + Como es de exhaustiva la verificación de bloques (0-4, por defecto 3) + + + + Not enough file descriptors available. + No hay suficientes descriptores de archivo disponibles. + + + + Rebuild block chain index from current blk000??.dat files + Reconstruir el índice de la cadena de bloques a partir de los archivos blk000??.dat actuales + + + + Set the number of threads to service RPC calls (default: 4) + Establecer el número de hilos para atender las llamadas RPC (predeterminado: 4) + + + + Verifying blocks... + Verificando bloques... + + + + Verifying wallet... + Verificando monedero... + + + + Imports blocks from external blk000??.dat file + Importa los bloques desde un archivo blk000??.dat externo + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + Configura el número de hilos para el script de verificación (hasta 16, 0 = auto, <0 = leave that many cores free, por fecto: 0) + + + + Information + Información + + + + Invalid -tor address: '%s' + Dirección -tor inválida: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + Inválido por el monto -minrelaytxfee=<amount>: '%s' + + + + Invalid amount for -mintxfee=<amount>: '%s' + Inválido por el monto -mintxfee=<amount>: '%s' + + + + Maintain a full transaction index (default: 0) + Mantener índice de transacciones completo (predeterminado: 0) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Búfer de recepción máximo por conexión, <n>*1000 bytes (predeterminado: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Búfer de recepción máximo por conexión, , <n>*1000 bytes (predeterminado: 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + Aceptar solamente cadena de bloques que concuerde con los puntos de control internos (predeterminado: 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Conectarse solo a nodos de la red <net> (IPv4, IPv6 o Tor) + + + + Output extra debugging information. Implies all other -debug* options + Mostrar información de depuración adicional. Implica todos los demás opciones -debug* + + + + Output extra network debugging information + Mostrar información de depuración adicional + + + + Prepend debug output with timestamp (default: 1) + Anteponer marca temporal a la información de depuración + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + Opciones SSL: (ver la CryptoLottoToken Wiki para instrucciones de configuración SSL) + + + + Select the version of socks proxy to use (4-5, default: 5) + Elija la versión del proxy socks a usar (4-5, predeterminado: 5) + + + + Send trace/debug info to console instead of debug.log file + Enviar información de trazas/depuración a la consola en lugar de al archivo debug.log + + + + Send trace/debug info to debugger + Enviar información de trazas/depuración al depurador + + + + Set maximum block size in bytes (default: 250000) + Establecer tamaño máximo de bloque en bytes (predeterminado: 250000) + + + + Set minimum block size in bytes (default: 0) + Establecer tamaño mínimo de bloque en bytes (predeterminado: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Reducir el archivo debug.log al iniciar el cliente (predeterminado: 1 sin -debug) + + + + Signing transaction failed + Transacción falló + + + + Specify connection timeout in milliseconds (default: 5000) + Especificar el tiempo máximo de conexión en milisegundos (predeterminado: 5000) + + + + System error: + Error de sistema: + + + + Transaction amount too small + Monto de la transacción muy pequeño + + + + Transaction amounts must be positive + Montos de transacciones deben ser positivos + + + + Transaction too large + Transacción demasiado grande + + + + Use UPnP to map the listening port (default: 0) + Usar UPnP para asignar el puerto de escucha (predeterminado: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Usar UPnP para asignar el puerto de escucha (predeterminado: 1 al escuchar) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Utilizar proxy para conectar a Tor servicios ocultos (predeterminado: igual que -proxy) + + + + Username for JSON-RPC connections + Nombre de usuario para las conexiones JSON-RPC + + + + + Warning + Aviso + + + + Warning: This version is obsolete, upgrade required! + Aviso: Esta versión es obsoleta, actualización necesaria! + + + + You need to rebuild the databases using -reindex to change -txindex + Necesita reconstruir las bases de datos con la opción -reindex para modificar -txindex + + + + wallet.dat corrupt, salvage failed + wallet.dat corrupto. Ha fallado la recuperación. + + + + Password for JSON-RPC connections + Contraseña para las conexiones JSON-RPC + + + + + Allow JSON-RPC connections from specified IP address + Permitir conexiones JSON-RPC desde la dirección IP especificada + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Enviar comando al nodo situado en <ip> (predeterminado: 127.0.0.1) + + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Ejecutar un comando cuando cambia el mejor bloque (%s en cmd se sustituye por el hash de bloque) + + + + Upgrade wallet to latest format + Actualizar el monedero al último formato + + + + Set key pool size to <n> (default: 100) + Ajustar el número de claves en reserva <n> (predeterminado: 100) + + + + + Rescan the block chain for missing wallet transactions + Volver a examinar la cadena de bloques en busca de transacciones del monedero perdidas + + + + Use OpenSSL (https) for JSON-RPC connections + Usar OpenSSL (https) para las conexiones JSON-RPC + + + + + Server certificate file (default: server.cert) + Certificado del servidor (predeterminado: server.cert) + + + + + Server private key (default: server.pem) + Clave privada del servidor (predeterminado: server.pem) + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Cifrados aceptados (predeterminado: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + + This help message + Este mensaje de ayuda + + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + No es posible conectar con %s en este sistema (bind ha dado el error %d, %s) + + + + Connect through socks proxy + Conectar mediante proxy socks + + + + Allow DNS lookups for -addnode, -seednode and -connect + Permitir búsquedas DNS para -addnode, -seednode y -connect + + + + Loading addresses... + Cargando direcciones... + + + + Error loading wallet.dat: Wallet corrupted + Error al cargar wallet.dat: el monedero está dañado + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Error al cargar wallet.dat: El monedero requiere una versión más reciente de CryptoLottoToken + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + El monedero ha necesitado ser reescrito. Reinicie CryptoLottoToken para completar el proceso + + + + Error loading wallet.dat + Error al cargar wallet.dat + + + + Invalid -proxy address: '%s' + Dirección -proxy inválida: '%s' + + + + Unknown network specified in -onlynet: '%s' + La red especificada en -onlynet '%s' es desconocida + + + + Unknown -socks proxy version requested: %i + Solicitada versión de proxy -socks desconocida: %i + + + + Cannot resolve -bind address: '%s' + No se puede resolver la dirección de -bind: '%s' + + + + Cannot resolve -externalip address: '%s' + No se puede resolver la dirección de -externalip: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + Cantidad inválida para -paytxfee=<amount>: '%s' + + + + Invalid amount + Cuantía no válida + + + + Insufficient funds + Fondos insuficientes + + + + Loading block index... + Cargando el índice de bloques... + + + + Add a node to connect to and attempt to keep the connection open + Añadir un nodo al que conectarse y tratar de mantener la conexión abierta + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + No es posible conectar con %s en este sistema. Probablemente CryptoLottoToken ya está ejecutándose. + + + + Fee per KB to add to transactions you send + Tarifa por KB que añadir a las transacciones que envíe + + + + Loading wallet... + Cargando monedero... + + + + Cannot downgrade wallet + No se puede rebajar el monedero + + + + Cannot write default address + No se puede escribir la dirección predeterminada + + + + Rescanning... + Reexplorando... + + + + Done loading + Generado pero no aceptado + + + + To use the %s option + Para utilizar la opción %s + + + + Error + Error + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + Tiene que establecer rpcpassword=<contraseña> en el fichero de configuración: ⏎ +%s ⏎ +Si el archivo no existe, créelo con permiso de lectura solamente del propietario. + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_es_CL.qm b/src/qt/locale/bitcoin_es_CL.qm new file mode 100644 index 0000000..7383a2e Binary files /dev/null and b/src/qt/locale/bitcoin_es_CL.qm differ diff --git a/src/qt/locale/bitcoin_es_CL.ts b/src/qt/locale/bitcoin_es_CL.ts new file mode 100644 index 0000000..44cf39a --- /dev/null +++ b/src/qt/locale/bitcoin_es_CL.ts @@ -0,0 +1,2952 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + Sobre CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> - versión + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +Este es un software experimental. + +Distribuido bajo la licencia MIT/X11, vea el archivo adjunto +COPYING o http://www.opensource.org/licenses/mit-license.php. + +Este producto incluye software desarrollado por OpenSSL Project para su uso en +el OpenSSL Toolkit (http://www.openssl.org/), software criptográfico escrito por +Eric Young (eay@cryptsoft.com) y UPnP software escrito por Thomas Bernard. + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + Guia de direcciones + + + + Double-click to edit address or label + Haz doble clic para editar una dirección o etiqueta + + + + Create a new address + Crea una nueva dirección + + + + Copy the currently selected address to the system clipboard + Copia la dirección seleccionada al portapapeles + + + + &New Address + &Nueva dirección + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Estas son tus direcciones CryptoLottoToken para recibir pagos. Puedes utilizar una diferente por cada persona emisora para saber quien te está pagando. + + + + &Copy Address + &Copia dirección + + + + Show &QR Code + Mostrar Código &QR + + + + Sign a message to prove you own a CryptoLottoToken address + Firmar un mensaje para provar que usted es dueño de esta dirección + + + + Sign &Message + Firmar Mensaje + + + + Delete the currently selected address from the list + + + + + Export the data in the current tab to a file + Exportar los datos de la pestaña actual a un archivo + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + + + + + &Verify Message + + + + + &Delete + &Borrar + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + Copia &etiqueta + + + + &Edit + &Editar + + + + Send &Coins + + + + + Export Address Book Data + Exporta datos de la guia de direcciones + + + + Comma separated file (*.csv) + Archivos separados por coma (*.csv) + + + + Error exporting + Exportar errores + + + + Could not write to file %1. + No se pudo escribir al archivo %1. + + + + AddressTableModel + + + Label + Etiqueta + + + + Address + Dirección + + + + (no label) + (sin etiqueta) + + + + AskPassphraseDialog + + + Passphrase Dialog + + + + + Enter passphrase + Introduce contraseña actual + + + + New passphrase + Nueva contraseña + + + + Repeat new passphrase + Repite nueva contraseña: + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Introduce la nueva contraseña para la billetera.<br/>Por favor utiliza un contraseña <b>de 10 o mas caracteres aleatorios</b>, u <b>ocho o mas palabras</b>. + + + + Encrypt wallet + Codificar billetera + + + + This operation needs your wallet passphrase to unlock the wallet. + Esta operación necesita la contraseña para desbloquear la billetera. + + + + Unlock wallet + Desbloquea billetera + + + + This operation needs your wallet passphrase to decrypt the wallet. + Esta operación necesita la contraseña para decodificar la billetara. + + + + Decrypt wallet + Decodificar cartera + + + + Change passphrase + Cambia contraseña + + + + Enter the old and new passphrase to the wallet. + Introduce la contraseña anterior y la nueva de cartera + + + + Confirm wallet encryption + Confirma la codificación de cartera + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Atención: ¡Si codificas tu billetera y pierdes la contraseña perderás <b>TODOS TUS CryptoLottoTokenS</b>! + + + + Are you sure you wish to encrypt your wallet? + ¿Seguro que quieres seguir codificando la billetera? + + + + 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. + + + + + + Warning: The Caps Lock key is on! + Precaucion: Mayúsculas Activadas + + + + + Wallet encrypted + Billetera codificada + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken se cerrará para finalizar el proceso de encriptación. Recuerde que encriptar su billetera no protegera completatamente sus CryptoLottoTokens de ser robados por malware que infecte su computador + + + + + + + Wallet encryption failed + Falló la codificación de la billetera + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + La codificación de la billetera falló debido a un error interno. Tu billetera no ha sido codificada. + + + + + The supplied passphrases do not match. + Las contraseñas no coinciden. + + + + Wallet unlock failed + Ha fallado el desbloqueo de la billetera + + + + + + The passphrase entered for the wallet decryption was incorrect. + La contraseña introducida para decodificar la billetera es incorrecta. + + + + Wallet decryption failed + Ha fallado la decodificación de la billetera + + + + Wallet passphrase was successfully changed. + La contraseña de billetera ha sido cambiada con éxito. + + + + BitcoinGUI + + + Sign &message... + Firmar &Mensaje... + + + + Synchronizing with network... + Sincronizando con la red... + + + + &Overview + &Vista general + + + + Show general overview of wallet + Muestra una vista general de la billetera + + + + &Transactions + &Transacciónes + + + + Browse transaction history + Explora el historial de transacciónes + + + + Edit the list of stored addresses and labels for sending + Edita la lista de direcciones y etiquetas almacenadas + + + + Show the list of addresses for receiving payments + Muestra la lista de direcciónes utilizadas para recibir pagos + + + + E&xit + &Salir + + + + Quit application + Salir del programa + + + + Show information about CryptoLottoToken + Muestra información acerca de CryptoLottoToken + + + + About &Qt + Acerca de + + + + Show information about Qt + Mostrar Información sobre QT + + + + &Options... + &Opciones + + + + &Encrypt Wallet... + &Codificar la billetera... + + + + &Backup Wallet... + &Respaldar billetera... + + + + &Change Passphrase... + &Cambiar la contraseña... + + + + Importing blocks from disk... + + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + Enviar monedas a una dirección CryptoLottoToken + + + + Modify configuration options for CryptoLottoToken + Modifica las opciones de configuración de CryptoLottoToken + + + + Backup wallet to another location + Respaldar billetera en otra ubicación + + + + Change the passphrase used for wallet encryption + Cambiar la contraseña utilizada para la codificación de la billetera + + + + &Debug window + + + + + Open debugging and diagnostic console + + + + + &Verify message... + + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Cartera + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + &Sobre CryptoLottoToken + + + + &Show / Hide + &Mostrar/Ocultar + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + &Archivo + + + + &Settings + &Configuración + + + + &Help + &Ayuda + + + + Tabs toolbar + Barra de pestañas + + + + + [testnet] + [red-de-pruebas] + + + + CryptoLottoToken client + Cliente CryptoLottoToken + + + + %n active connection(s) to CryptoLottoToken network + %n conexión activa hacia la red CryptoLottoToken%n conexiones activas hacia la red CryptoLottoToken + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + + + + + Warning + + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + Actualizado + + + + Catching up... + Recuperando... + + + + Confirm transaction fee + + + + + Sent transaction + Transacción enviada + + + + Incoming transaction + Transacción entrante + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Fecha: %1 +Cantidad: %2 +Tipo: %3 +Dirección: %4 + + + + + URI handling + + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + La billetera esta <b>codificada</b> y actualmente <b>desbloqueda</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + La billetera esta <b>codificada</b> y actualmente <b>bloqueda</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + + + + + EditAddressDialog + + + Edit Address + Editar dirección + + + + &Label + &Etiqueta + + + + The label associated with this address book entry + La etiqueta asociada con esta entrada de la libreta de direcciones + + + + &Address + &Dirección + + + + The address associated with this address book entry. This can only be modified for sending addresses. + La dirección asociada con esta entrada en la libreta de direcciones. Solo puede ser modificada para direcciónes de envío. + + + + New receiving address + Nueva dirección para recibir + + + + New sending address + Nueva dirección para enviar + + + + Edit receiving address + Editar dirección de recepción + + + + Edit sending address + Editar dirección de envio + + + + The entered address "%1" is already in the address book. + La dirección introducida "%1" ya esta guardada en la libreta de direcciones. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + La dirección introducida "%1" no es una dirección CryptoLottoToken valida. + + + + Could not unlock wallet. + No se pudo desbloquear la billetera. + + + + New key generation failed. + La generación de nueva clave falló. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + + + + + version + versión + + + + Usage: + Uso: + + + + command-line options + + + + + UI options + UI opciones + + + + Set language, for example "de_DE" (default: system locale) + + + + + Start minimized + Arranca minimizado + + + + + Show splash screen on startup (default: 1) + + + + + OptionsDialog + + + Options + Opciones + + + + &Main + &Principal + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + Comisión de &transacciónes + + + + Automatically start CryptoLottoToken after logging in to the system. + Inicia CryptoLottoToken automáticamente despues de encender el computador + + + + &Start CryptoLottoToken on system login + &Inicia CryptoLottoToken al iniciar el sistema + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Abre automáticamente el puerto del cliente CryptoLottoToken en el router. Esto funciona solo cuando tu router es compatible con UPnP y está habilitado. + + + + Map port using &UPnP + Direcciona el puerto usando &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Conecta a la red CryptoLottoToken a través de un proxy SOCKS (ej. cuando te conectas por la red Tor) + + + + &Connect through SOCKS proxy: + &Conecta a traves de un proxy SOCKS: + + + + Proxy &IP: + &IP Proxy: + + + + IP address of the proxy (e.g. 127.0.0.1) + Dirección IP del servidor proxy (ej. 127.0.0.1) + + + + &Port: + &Puerto: + + + + Port of the proxy (e.g. 9050) + Puerto del servidor proxy (ej. 9050) + + + + SOCKS &Version: + + + + + SOCKS version of the proxy (e.g. 5) + + + + + &Window + + + + + Show only a tray icon after minimizing the window. + Muestra solo un ícono en la bandeja después de minimizar la ventana + + + + &Minimize to the tray instead of the taskbar + &Minimiza a la bandeja en vez de la barra de tareas + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Minimiza la ventana en lugar de salir del programa cuando la ventana se cierra. Cuando esta opción esta activa el programa solo se puede cerrar seleccionando Salir desde el menu. + + + + M&inimize on close + M&inimiza a la bandeja al cerrar + + + + &Display + &Mostrado + + + + User Interface &language: + + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + + + + + &Unit to show amounts in: + &Unidad en la que mostrar cantitades: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Elige la subdivisión por defecto para mostrar cantidaded en la interfaz cuando se envien monedas + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + + + &Display addresses in transaction list + &Muestra direcciones en el listado de transaccioines + + + + &OK + + + + + &Cancel + + + + + &Apply + + + + + default + + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + Atención + + + + + This setting will take effect after restarting CryptoLottoToken. + + + + + The supplied proxy address is invalid. + + + + + OverviewPage + + + Form + Formulario + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + + + + + Balance: + Saldo: + + + + Unconfirmed: + No confirmados: + + + + Wallet + Cartera + + + + Immature: + + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + <b>Transacciones recientes</b> + + + + Your current balance + Tu saldo actual + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Total de transacciones que no han sido confirmadas aun, y que no cuentan para el saldo actual. + + + + + out of sync + + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + + + + + Request Payment + Solicitar Pago + + + + Amount: + Cantidad: + + + + Label: + Etiqueta + + + + Message: + Mensaje: + + + + &Save As... + &Guardar Como... + + + + Error encoding URI into QR Code. + + + + + The entered amount is invalid, please check. + + + + + Resulting URI too long, try to reduce the text for label / message. + + + + + Save QR Code + + + + + PNG Images (*.png) + Imágenes PNG (*.png) + + + + RPCConsole + + + Client name + + + + + + + + + + + + + + N/A + + + + + Client version + + + + + &Information + + + + + Using OpenSSL version + + + + + Startup time + + + + + Network + + + + + Number of connections + + + + + On testnet + + + + + Block chain + + + + + Current number of blocks + + + + + Estimated total blocks + + + + + Last block time + + + + + &Open + + + + + Command-line options + + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + + &Show + + + + + &Console + + + + + Build date + + + + + CryptoLottoToken - Debug window + + + + + CryptoLottoToken Core + + + + + Debug log file + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + + + + + Welcome to the CryptoLottoToken RPC console. + + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + + + + + Type <b>help</b> for an overview of available commands. + + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Enviar monedas + + + + Send to multiple recipients at once + Enviar a múltiples destinatarios + + + + Add &Recipient + &Agrega destinatario + + + + Remove all transaction fields + Remover todos los campos de la transacción + + + + Clear &All + &Borra todos + + + + Balance: + Balance: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Confirma el envio + + + + S&end + &Envía + + + + <b>%1</b> to %2 (%3) + <b>%1</b> to %2 (%3) + + + + Confirm send coins + Confirmar el envio de monedas + + + + Are you sure you want to send %1? + Estas seguro que quieres enviar %1? + + + + and + y + + + + The recipient address is not valid, please recheck. + La dirección de destinatarion no es valida, comprueba otra vez. + + + + The amount to pay must be larger than 0. + La cantidad por pagar tiene que ser mayor 0. + + + + The amount exceeds your balance. + La cantidad sobrepasa tu saldo. + + + + The total exceeds your balance when the %1 transaction fee is included. + El total sobrepasa tu saldo cuando se incluyen %1 como tasa de envio. + + + + Duplicate address found, can only send to each address once per send operation. + Tienes una dirección duplicada, solo puedes enviar a direcciónes individuales de una sola vez. + + + + Error: Transaction creation failed! + + + + + 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. + Error: La transacción fue rechazada. Esto puede haber ocurrido si alguna de las monedas ya estaba gastada o si ha usado una copia de wallet.dat y las monedas se gastaron en la copia pero no se han marcado como gastadas aqui. + + + + SendCoinsEntry + + + Form + Envio + + + + A&mount: + Cantidad: + + + + Pay &To: + &Pagar a: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + Introduce una etiqueta a esta dirección para añadirla a tu guia + + + + &Label: + &Etiqueta: + + + + Choose address from address book + Elije dirección de la guia + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Pega dirección desde portapapeles + + + + Alt+P + Alt+P + + + + Remove this recipient + Elimina destinatario + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Introduce una dirección CryptoLottoToken (ej. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + + + + + &Sign Message + &Firmar Mensaje + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Introduce una dirección CryptoLottoToken (ej. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Elije dirección de la guia + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Pega dirección desde portapapeles + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Escriba el mensaje que desea firmar + + + + Signature + + + + + Copy the current signature to the system clipboard + + + + + Sign the message to prove you own this CryptoLottoToken address + Firmar un mensjage para probar que usted es dueño de esta dirección + + + + Sign &Message + + + + + Reset all sign message fields + + + + + + Clear &All + &Borra todos + + + + &Verify Message + + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Introduce una dirección CryptoLottoToken (ej. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + + + + + Verify &Message + + + + + Reset all verify message fields + + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Introduce una dirección CryptoLottoToken (ej. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Click en "Firmar Mensage" para conseguir firma + + + + Enter CryptoLottoToken signature + + + + + + The entered address is invalid. + + + + + + + + Please check the address and try again. + + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + + + + + Private key for the entered address is not available. + + + + + Message signing failed. + + + + + Message signed. + + + + + The signature could not be decoded. + + + + + + Please check the signature and try again. + + + + + The signature did not match the message digest. + + + + + Message verification failed. + + + + + Message verified. + + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + [red-de-pruebas] + + + + TransactionDesc + + + Open until %1 + Abierto hasta %1 + + + + %1/offline + %1/fuera de linea + + + + %1/unconfirmed + %1/no confirmado + + + + %1 confirmations + %1 confirmaciónes + + + + Status + Estado + + + + , broadcast through %n node(s) + + + + + Date + Fecha + + + + Source + + + + + Generated + Generado + + + + + From + De + + + + + + To + + + + + + own address + + + + + label + etiqueta + + + + + + + + Credit + Credito + + + + matures in %n more block(s) + + + + + not accepted + no aceptada + + + + + + + Debit + Debito + + + + Transaction fee + Comisión transacción + + + + Net amount + Cantidad total + + + + Message + Mensaje + + + + Comment + Comentario + + + + Transaction ID + ID de Transacción + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Las monedas generadas deben esperar 120 bloques antes de ser gastadas. Cuando has generado este bloque se emitió a la red para ser agregado en la cadena de bloques. Si falla al incluirse en la cadena, cambiará a "no aceptado" y las monedas no se podrán gastar. Esto puede ocurrir ocasionalmente si otro nodo genera un bloque casi al mismo tiempo que el tuyo. + + + + Debug information + + + + + Transaction + Transacción + + + + Inputs + + + + + Amount + Cantidad + + + + true + + + + + false + + + + + , has not been successfully broadcast yet + , no ha sido emitido satisfactoriamente todavía + + + + Open for %n more block(s) + + + + + unknown + desconocido + + + + TransactionDescDialog + + + Transaction details + Detalles de transacción + + + + This pane shows a detailed description of the transaction + Esta ventana muestra información detallada sobre la transacción + + + + TransactionTableModel + + + Date + Fecha + + + + Type + Tipo + + + + Address + Dirección + + + + Amount + Cantidad + + + + Open for %n more block(s) + + + + + Open until %1 + Abierto hasta %1 + + + + Offline (%1 confirmations) + Fuera de linea (%1 confirmaciónes) + + + + Unconfirmed (%1 of %2 confirmations) + No confirmado (%1 de %2 confirmaciónes) + + + + Confirmed (%1 confirmations) + Confirmado (%1 confirmaciones) + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + Este bloque no ha sido recibido por otros nodos y probablemente no sea aceptado ! + + + + Generated but not accepted + Generado pero no acceptado + + + + Received with + Recibido con + + + + Received from + Recibido de + + + + Sent to + Enviado a + + + + Payment to yourself + Pagar a usted mismo + + + + Mined + Minado + + + + (n/a) + (n/a) + + + + Transaction status. Hover over this field to show number of confirmations. + Estado de transacción. Pasa el raton sobre este campo para ver el numero de confirmaciónes. + + + + Date and time that the transaction was received. + Fecha y hora cuando se recibió la transaccion + + + + Type of transaction. + Tipo de transacción. + + + + Destination address of transaction. + Dirección de destino para la transacción + + + + Amount removed from or added to balance. + Cantidad restada o añadida al balance + + + + TransactionView + + + + All + Todo + + + + Today + Hoy + + + + This week + Esta semana + + + + This month + Esta mes + + + + Last month + Mes pasado + + + + This year + Este año + + + + Range... + Rango... + + + + Received with + Recibido con + + + + Sent to + Enviado a + + + + To yourself + A ti mismo + + + + Mined + Minado + + + + Other + Otra + + + + Enter address or label to search + Introduce una dirección o etiqueta para buscar + + + + Min amount + Cantidad minima + + + + Copy address + Copia dirección + + + + Copy label + Copia etiqueta + + + + Copy amount + Copiar Cantidad + + + + Copy transaction ID + + + + + Edit label + Edita etiqueta + + + + Show transaction details + + + + + Export Transaction Data + Exportar datos de transacción + + + + Comma separated file (*.csv) + Archivos separados por coma (*.csv) + + + + Confirmed + Confirmado + + + + Date + Fecha + + + + Type + Tipo + + + + Label + Etiqueta + + + + Address + Dirección + + + + Amount + Cantidad + + + + ID + ID + + + + Error exporting + Error exportando + + + + Could not write to file %1. + No se pudo escribir en el archivo %1. + + + + Range: + Rango: + + + + to + para + + + + WalletModel + + + Send Coins + Enviar monedas + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + Exportar los datos de la pestaña actual a un archivo + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + Versión CryptoLottoToken + + + + Usage: + Uso: + + + + Send command to -server or CryptoLottoTokend + Envia comando a CryptoLottoToken lanzado con -server u CryptoLottoTokend + + + + + List commands + Muestra comandos + + + + + Get help for a command + Recibir ayuda para un comando + + + + + Options: + Opciones: + + + + + Specify configuration file (default: CryptoLottoToken.conf) + Especifica archivo de configuración (predeterminado: CryptoLottoToken.conf) + + + + + Specify pid file (default: CryptoLottoTokend.pid) + Especifica archivo pid (predeterminado: CryptoLottoToken.pid) + + + + + Specify data directory + Especifica directorio para los datos + + + + + Set database cache size in megabytes (default: 25) + + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Escuchar por conecciones en <puerto> (Por defecto: 22556 o red de prueba: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Mantener al menos <n> conecciones por cliente (por defecto: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + + + + + Specify your own public address + + + + + Threshold for disconnecting misbehaving peers (default: 100) + Umbral de desconección de clientes con mal comportamiento (por defecto: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Escucha conexiones JSON-RPC en el puerto <port> (predeterminado: 22555 or testnet: 44555) + + + + Accept command line and JSON-RPC commands + Aceptar comandos consola y JSON-RPC + + + + + Run in the background as a daemon and accept commands + Correr como demonio y acepta comandos + + + + + Use the test network + Usa la red de pruebas + + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Precaución: -paytxfee es muy alta. Esta es la comisión que pagarás si envias una transacción. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Precaución: Por favor revise que la fecha y hora de tu ordenador son correctas. Si tu reloj está mal configurado CryptoLottoToken no funcionará correctamente. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + + + + + Connect only to the specified node(s) + Conecta solo al nodo especificado + + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + Dirección -tor invalida: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + Adjuntar informacion extra de depuracion. Implies all other -debug* options + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + Anteponer salida de depuracion con marca de tiempo + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + Opciones SSL: (ver la CryptoLottoToken Wiki para instrucciones de configuración SSL) + + + + Select the version of socks proxy to use (4-5, default: 5) + + + + + Send trace/debug info to console instead of debug.log file + Enviar informacion de seguimiento a la consola en vez del archivo debug.log + + + + Send trace/debug info to debugger + Enviar informacion de seguimiento al depurador + + + + Set maximum block size in bytes (default: 250000) + + + + + Set minimum block size in bytes (default: 0) + + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + Especifica tiempo de espera para conexion en milisegundos (predeterminado: 5000) + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + Intenta usar UPnP para mapear el puerto de escucha (default: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Intenta usar UPnP para mapear el puerto de escucha (default: 1 when listening) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + Usuario para las conexiones JSON-RPC + + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + Contraseña para las conexiones JSON-RPC + + + + + Allow JSON-RPC connections from specified IP address + Permite conexiones JSON-RPC desde la dirección IP especificada + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Envia comando al nodo situado en <ip> (predeterminado: 127.0.0.1) + + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + + + + + Upgrade wallet to latest format + Actualizar billetera al formato actual + + + + Set key pool size to <n> (default: 100) + Ajusta el numero de claves en reserva <n> (predeterminado: 100) + + + + + Rescan the block chain for missing wallet transactions + Rescanea la cadena de bloques para transacciones perdidas de la cartera + + + + + Use OpenSSL (https) for JSON-RPC connections + Usa OpenSSL (https) para las conexiones JSON-RPC + + + + + Server certificate file (default: server.cert) + Certificado del servidor (Predeterminado: server.cert) + + + + + Server private key (default: server.pem) + Clave privada del servidor (Predeterminado: server.pem) + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Cifrados aceptados (Predeterminado: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + + This help message + Este mensaje de ayuda + + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + No es posible escuchar en el %s en este ordenador (bind returned error %d, %s) + + + + Connect through socks proxy + Conecta mediante proxy socks + + + + Allow DNS lookups for -addnode, -seednode and -connect + Permite búsqueda DNS para addnode y connect + + + + + Loading addresses... + Cargando direcciónes... + + + + Error loading wallet.dat: Wallet corrupted + Error cargando wallet.dat: Billetera corrupta + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Error cargando wallet.dat: Billetera necesita una vercion reciente de CryptoLottoToken + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + La billetera necesita ser reescrita: reinicie CryptoLottoToken para completar + + + + Error loading wallet.dat + Error cargando wallet.dat + + + + Invalid -proxy address: '%s' + Dirección -proxy invalida: '%s' + + + + Unknown network specified in -onlynet: '%s' + + + + + Unknown -socks proxy version requested: %i + + + + + Cannot resolve -bind address: '%s' + + + + + Cannot resolve -externalip address: '%s' + + + + + Invalid amount for -paytxfee=<amount>: '%s' + Cantidad inválida para -paytxfee=<amount>: '%s' + + + + Invalid amount + Cantidad inválida + + + + Insufficient funds + Fondos insuficientes + + + + Loading block index... + Cargando el index de bloques... + + + + Add a node to connect to and attempt to keep the connection open + Agrega un nodo para conectarse and attempt to keep the connection open + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + No es posible escuchar en el %s en este ordenador. Probablemente CryptoLottoToken ya se está ejecutando. + + + + Fee per KB to add to transactions you send + Comisión por kB para adicionarla a las transacciones enviadas + + + + Loading wallet... + Cargando cartera... + + + + Cannot downgrade wallet + + + + + Cannot write default address + + + + + Rescanning... + Rescaneando... + + + + Done loading + Carga completa + + + + To use the %s option + + + + + Error + Error + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_et.qm b/src/qt/locale/bitcoin_et.qm new file mode 100644 index 0000000..1e2d0a8 Binary files /dev/null and b/src/qt/locale/bitcoin_et.qm differ diff --git a/src/qt/locale/bitcoin_et.ts b/src/qt/locale/bitcoin_et.ts new file mode 100644 index 0000000..fc21036 --- /dev/null +++ b/src/qt/locale/bitcoin_et.ts @@ -0,0 +1,2937 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + CryptoLottoTokenist + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoTokeni</b> versioon + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + ⏎ +See on eksperimentaalne tarkvara.⏎ +⏎ +Levitatud MIT/X11 tarkvara litsentsi all, vaata kaasasolevat faili COPYING või http://www.opensource.org/licenses/mit-license.php⏎ +⏎ +Toode sisaldab OpenSSL Projekti all toodetud tarkvara, mis on kasutamiseks OpenSSL Toolkitis (http://www.openssl.org/) ja Eric Young'i poolt loodud krüptograafilist tarkvara (eay@cryptsoft.com) ning Thomas Bernard'i loodud UPnP tarkvara. + + + + Copyright + Autoriõigus + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + Aadressiraamat + + + + Double-click to edit address or label + Topeltklõps aadressi või märgise muutmiseks + + + + Create a new address + Loo uus aadress + + + + Copy the currently selected address to the system clipboard + Kopeeri märgistatud aadress vahemällu + + + + &New Address + &Uus aadress + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Maksete saamiseks kasutatavad CryptoLottoTokeni aadressid. Maksjate paremaks jälgimiseks võib igaühele anda erineva. + + + + &Copy Address + &Aadressi kopeerimine + + + + Show &QR Code + Kuva %QR kood + + + + Sign a message to prove you own a CryptoLottoToken address + Allkirjasta sõnum, et tõestada Bitconi aadressi olemasolu. + + + + Sign &Message + Allkirjasta &Sõnum + + + + Delete the currently selected address from the list + Kustuta märgistatud aadress loetelust + + + + Export the data in the current tab to a file + + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Kinnita sõnum tõestamaks selle allkirjastatust määratud CryptoLottoTokeni aadressiga. + + + + &Verify Message + &Kinnita Sõnum + + + + &Delete + &Kustuta + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + Need on sinu CryptoLottoTokeni aadressid maksete saatmiseks. Müntide saatmisel kontrolli alati summat ning saaja aadressi. + + + + Copy &Label + &Märgise kopeerimine + + + + &Edit + &Muuda + + + + Send &Coins + Saada &Münte + + + + Export Address Book Data + Ekspordi Aadressiraamat + + + + Comma separated file (*.csv) + Komaeraldatud fail (*.csv) + + + + Error exporting + Viga eksportimisel + + + + Could not write to file %1. + Tõrge faili kirjutamisel %1. + + + + AddressTableModel + + + Label + Silt + + + + Address + Aadress + + + + (no label) + (silti pole) + + + + AskPassphraseDialog + + + Passphrase Dialog + Salafraasi dialoog + + + + Enter passphrase + Sisesta salafraas + + + + New passphrase + Uus salafraas + + + + Repeat new passphrase + Korda salafraasi + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Sisesta rahakotile uus salafraas.<br/>Palun kasuta salafraasina <b>vähemalt 10 tähte/numbrit/sümbolit</b>, või <b>vähemalt 8 sõna</b>. + + + + Encrypt wallet + Krüpteeri rahakott + + + + This operation needs your wallet passphrase to unlock the wallet. + See toiming nõuab sinu rahakoti salafraasi. + + + + Unlock wallet + Tee rahakott lukust lahti. + + + + This operation needs your wallet passphrase to decrypt the wallet. + See toiming nõuab sinu rahakoti salafraasi. + + + + Decrypt wallet + Dekrüpteeri rahakott. + + + + Change passphrase + Muuda salafraasi + + + + Enter the old and new passphrase to the wallet. + Sisesta rahakoti vana ning uus salafraas. + + + + Confirm wallet encryption + Kinnita rahakoti krüpteering + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Hoiatus: Kui sa kaotad oma, rahakoti krüpteerimisel kasutatud, salafraasi, siis <b>KAOTAD KA KÕIK OMA CryptoLottoTokenID</b>! + + + + Are you sure you wish to encrypt your wallet? + Kas soovid oma rahakoti krüpteerida? + + + + 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. + TÄHTIS: Kõik varasemad rahakoti varundfailid tuleks üle kirjutada äsja loodud krüpteeritud rahakoti failiga. Turvakaalutlustel tühistatakse krüpteerimata rahakoti failid alates uue, krüpteeritud rahakoti, kasutusele võtust. + + + + + Warning: The Caps Lock key is on! + Hoiatus: Caps Lock on sisse lülitatud! + + + + + Wallet encrypted + Rahakott krüpteeritud + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken sulgub krüpteeringu lõpetamiseks. Pea meeles, et rahakoti krüpteerimine ei välista CryptoLottoTokenide vargust, kui sinu arvuti on nakatunud pahavaraga. + + + + + + + Wallet encryption failed + Tõrge rahakoti krüpteerimisel + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Rahakoti krüpteering ebaõnnestus tõrke tõttu. Sinu rahakotti ei krüpteeritud. + + + + + The supplied passphrases do not match. + Salafraasid ei kattu. + + + + Wallet unlock failed + Rahakoti avamine ebaõnnestus + + + + + + The passphrase entered for the wallet decryption was incorrect. + Rahakoti salafraas ei ole õige. + + + + Wallet decryption failed + Rahakoti dekrüpteerimine ei õnnestunud + + + + Wallet passphrase was successfully changed. + Rahakoti salafraasi muutmine õnnestus. + + + + BitcoinGUI + + + Sign &message... + Signeeri &sõnum + + + + Synchronizing with network... + Võrgusünkimine... + + + + &Overview + &Ülevaade + + + + Show general overview of wallet + Kuva rahakoti üld-ülevaade + + + + &Transactions + &Tehingud + + + + Browse transaction history + Sirvi tehingute ajalugu + + + + Edit the list of stored addresses and labels for sending + Salvestatud aadresside ja märgiste loetelu muutmine + + + + Show the list of addresses for receiving payments + Kuva saadud maksete aadresside loetelu + + + + E&xit + V&älju + + + + Quit application + Väljumine + + + + Show information about CryptoLottoToken + Kuva info CryptoLottoTokeni kohta + + + + About &Qt + Teave &Qt kohta + + + + Show information about Qt + Kuva Qt kohta käiv info + + + + &Options... + &Valikud... + + + + &Encrypt Wallet... + &Krüpteeri Rahakott + + + + &Backup Wallet... + &Varunda Rahakott + + + + &Change Passphrase... + &Salafraasi muutmine + + + + Importing blocks from disk... + Impordi blokid kettalt... + + + + Reindexing blocks on disk... + Kettal olevate blokkide re-indekseerimine... + + + + Send coins to a CryptoLottoToken address + Saada münte CryptoLottoTokeni aadressile + + + + Modify configuration options for CryptoLottoToken + Muuda CryptoLottoTokeni seadeid + + + + Backup wallet to another location + Varunda rahakott teise asukohta + + + + Change the passphrase used for wallet encryption + Rahakoti krüpteerimise salafraasi muutmine + + + + &Debug window + &Debugimise aken + + + + Open debugging and diagnostic console + Ava debugimise ja diagnostika konsool + + + + &Verify message... + &Kontrolli sõnumit... + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Rahakott + + + + &Send + &Saada + + + + &Receive + &Saama + + + + &Addresses + &Aadressid + + + + &About CryptoLottoToken + %CryptoLottoTokenist + + + + &Show / Hide + &Näita / Peida + + + + Show or hide the main Window + Näita või peida peaaken + + + + Encrypt the private keys that belong to your wallet + Krüpteeri oma rahakoti privaatvõtmed + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Omandi tõestamiseks allkirjasta sõnumid oma CryptoLottoTokeni aadressiga + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Kinnita sõnumid kindlustamaks et need allkirjastati määratud CryptoLottoTokeni aadressiga + + + + &File + &Fail + + + + &Settings + &Seaded + + + + &Help + &Abi + + + + Tabs toolbar + Vahelehe tööriistariba + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + CryptoLottoTokeni klient + + + + %n active connection(s) to CryptoLottoToken network + %n aktiivne ühendus CryptoLottoTokeni võrku%n aktiivset ühendust CryptoLottoTokeni võrku + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + Protsessitud %1 (arvutuslikult) tehingu ajaloo blokki %2-st. + + + + Processed %1 blocks of transaction history. + Protsessitud %1 tehingute ajaloo blokki. + + + + %n hour(s) + %n tund%n tundi + + + + %n day(s) + %n päev%n päeva + + + + %n week(s) + %n nädal%n nädalat + + + + %1 behind + %1 maas + + + + Last received block was generated %1 ago. + Viimane saabunud blokk loodi %1 tagasi. + + + + Transactions after this will not yet be visible. + Peale seda ei ole tehingud veel nähtavad. + + + + Error + Tõrge + + + + Warning + Hoiatus + + + + Information + Informatsioon + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + See tehing ületab mahupiirangu. Saatmine on võimalik %1, node'idele ning võrgustiku toetuseks, makstava lisatasu eest. Kas nõustud lisatasuga? + + + + Up to date + Ajakohane + + + + Catching up... + Jõuan... + + + + Confirm transaction fee + Kinnita tehingu tasu + + + + Sent transaction + Saadetud tehing + + + + Incoming transaction + Sisenev tehing + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Kuupäev: %1⏎ +Summa: %2⏎ +Tüüp: %3⏎ +Aadress: %4⏎ + + + + + URI handling + URI käsitsemine + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + URI ei suudeta parsida. Põhjuseks võib olla kehtetu CryptoLottoTokeni aadress või vigased URI parameetrid. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Rahakott on <b>krüpteeritud</b> ning hetkel <b>avatud</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Rahakott on <b>krüpteeritud</b> ning hetkel <b>suletud</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + Ilmnes kriitiline tõrge. CryptoLottoToken suletakse turvakaalutluste tõttu. + + + + ClientModel + + + Network Alert + Võrgu Häire + + + + EditAddressDialog + + + Edit Address + Muuda aadressi + + + + &Label + &Märgis + + + + The label associated with this address book entry + Selle aadressiraamatu kirjega seotud märgis + + + + &Address + &Aadress + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Selle aadressiraamatu kirjega seotud aadress. Võimalik muuta ainult aadresside saatmiseks. + + + + New receiving address + Uus sissetulev aadress + + + + New sending address + Uus väljaminev aadress + + + + Edit receiving address + Sissetulevate aadresside muutmine + + + + Edit sending address + Väljaminevate aadresside muutmine + + + + The entered address "%1" is already in the address book. + Selline aadress on juba olemas: "%1" + + + + The entered address "%1" is not a valid CryptoLottoToken address. + Sisestatud aadress "%1" ei ole CryptoLottoTokenis kehtiv. + + + + Could not unlock wallet. + Rahakotti ei avatud + + + + New key generation failed. + Tõrge uue võtme loomisel. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoTokeni-Qt + + + + version + versioon + + + + Usage: + Kasutus: + + + + command-line options + käsurea valikud + + + + UI options + UI valikud + + + + Set language, for example "de_DE" (default: system locale) + Keele valik, nt "ee_ET" (vaikeväärtus: system locale) + + + + Start minimized + Käivitu tegumiribale + + + + Show splash screen on startup (default: 1) + Käivitamisel teabeakna kuvamine (vaikeväärtus: 1) + + + + OptionsDialog + + + Options + Valikud + + + + &Main + %Peamine + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + Tasu tehingu &fee + + + + Automatically start CryptoLottoToken after logging in to the system. + Käivita CryptoLottoToken süsteemi logimisel. + + + + &Start CryptoLottoToken on system login + &Start CryptoLottoToken sisselogimisel + + + + Reset all client options to default. + Taasta kõik klientprogrammi seadete vaikeväärtused. + + + + &Reset Options + &Lähtesta valikud + + + + &Network + &Võrk + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + CryptoLottoTokeni kliendi pordi automaatne avamine ruuteris. Toimib, kui sinu ruuter aktsepteerib UPnP ühendust. + + + + Map port using &UPnP + Suuna port &UPnP kaudu + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Kasuta CryptoLottoTokeni võrgustikku ühendumiseks SOCKS turva proxy't (nt Tor'i kasutamisel). + + + + &Connect through SOCKS proxy: + %Connect läbi turva proxi: + + + + Proxy &IP: + Proxi &IP: + + + + IP address of the proxy (e.g. 127.0.0.1) + Proxi IP (nt 127.0.0.1) + + + + &Port: + &Port: + + + + Port of the proxy (e.g. 9050) + Proxi port (nt 9050) + + + + SOCKS &Version: + Turva proxi SOCKS &Version: + + + + SOCKS version of the proxy (e.g. 5) + Turva proxi SOCKS versioon (nt 5) + + + + &Window + &Aken + + + + Show only a tray icon after minimizing the window. + Minimeeri systray alale. + + + + &Minimize to the tray instead of the taskbar + &Minimeeri systray alale + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Sulgemise asemel minimeeri aken. Selle valiku tegemisel suletakse programm Menüüst "Välju" käsuga. + + + + M&inimize on close + M&inimeeri sulgemisel + + + + &Display + &Kuva + + + + User Interface &language: + Kasutajaliidese &keel: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + Kasutajaliidese keele valimise koht. Valik rakendub CryptoLottoTokeni käivitamisel. + + + + &Unit to show amounts in: + Summade kuvamise &Unit: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Vali liideses ning müntide saatmisel kuvatav vaikimisi alajaotus. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Kuvada CryptoLottoTokeni aadress tehingute loetelus või mitte. + + + + &Display addresses in transaction list + Tehingute loetelu &Display aadress + + + + &OK + &OK + + + + &Cancel + &Katkesta + + + + &Apply + &Rakenda + + + + default + vaikeväärtus + + + + Confirm options reset + Kinnita valikute algseadistamine + + + + Some settings may require a client restart to take effect. + Mõned seadete muudatused rakenduvad programmi käivitumisel. + + + + Do you want to proceed? + Kas soovid jätkata? + + + + + Warning + Hoiatus + + + + + This setting will take effect after restarting CryptoLottoToken. + Tehtud valik rakendub CryptoLottoTokeni käivitamisel. + + + + The supplied proxy address is invalid. + Sisestatud kehtetu proxy aadress. + + + + OverviewPage + + + Form + Vorm + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + Kuvatav info ei pruugi olla ajakohane. Ühenduse loomisel süngitakse sinu rahakott automaatselt Bitconi võrgustikuga, kuid see toiming on hetkel lõpetamata. + + + + Balance: + Jääk: + + + + Unconfirmed: + Kinnitamata: + + + + Wallet + Rahakott + + + + Immature: + Ebaküps: + + + + Mined balance that has not yet matured + Mitte aegunud mine'itud jääk + + + + <b>Recent transactions</b> + <b>Uuesti saadetud tehingud</b> + + + + Your current balance + Sinu jääk hetkel + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Kinnitamata tehingud kokku. Ei kajastu hetke jäägis + + + + + out of sync + sünkimata + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + CryptoLottoToken ei käivitu: vajuta-maksa toiming + + + + QRCodeDialog + + + QR Code Dialog + QR koodi dialoog + + + + Request Payment + Makse taotlus + + + + Amount: + Summa: + + + + Label: + Märgis: + + + + Message: + Sõnum: + + + + &Save As... + &Salvesta nimega... + + + + Error encoding URI into QR Code. + Tõrge URI'st QR koodi loomisel + + + + The entered amount is invalid, please check. + Sisestatud summa on vale, palun kontrolli. + + + + Resulting URI too long, try to reduce the text for label / message. + Tulemuseks on liiga pikk URL, püüa lühendada märgise/teate teksti. + + + + Save QR Code + Salvesta QR kood + + + + PNG Images (*.png) + PNG pildifail (*.png) + + + + RPCConsole + + + Client name + Kliendi nimi + + + + + + + + + + + + + N/A + N/A + + + + Client version + Kliendi versioon + + + + &Information + &Informatsioon + + + + Using OpenSSL version + Kasutan OpenSSL versiooni + + + + Startup time + Käivitamise hetk + + + + Network + Võrgustik + + + + Number of connections + Ühenduste arv + + + + On testnet + Testnetis + + + + Block chain + Ploki jada + + + + Current number of blocks + Plokkide hetkearv + + + + Estimated total blocks + Ligikaudne plokkide kogus + + + + Last block time + Viimane ploki aeg + + + + &Open + &Ava + + + + Command-line options + Käsurea valikud + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Näita kehtivate käsurea valikute kuvamiseks CryptoLottoTokeni-Qt abiteksti + + + + &Show + &Kuva + + + + &Console + &Konsool + + + + Build date + Valmistusaeg + + + + CryptoLottoToken - Debug window + CryptoLottoToken - debugimise aken + + + + CryptoLottoToken Core + CryptoLottoTokeni tuumik + + + + Debug log file + Debugimise logifail + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + Ava CryptoLottoTokeni logifail praegusest andmekaustast. Toiminguks võib kuluda kuni mõni sekund. + + + + Clear console + Puhasta konsool + + + + Welcome to the CryptoLottoToken RPC console. + Teretulemast CryptoLottoTokeni RPC konsooli. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Ajaloo sirvimiseks kasuta üles ja alla nooli, ekraani puhastamiseks <b>Ctrl-L</b>. + + + + Type <b>help</b> for an overview of available commands. + Ülevaateks võimalikest käsklustest trüki <b>help</b>. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Müntide saatmine + + + + Send to multiple recipients at once + Saatmine mitmele korraga + + + + Add &Recipient + Lisa &Saaja + + + + Remove all transaction fields + Eemalda kõik tehingu väljad + + + + Clear &All + Puhasta &Kõik + + + + Balance: + Jääk: + + + + 123.456 BTC + 123,456 BTC + + + + Confirm the send action + Saatmise kinnitamine + + + + S&end + S&aada + + + + <b>%1</b> to %2 (%3) + <b>%1</b> kuni %2 (%3) + + + + Confirm send coins + Müntide saatmise kinnitamine + + + + Are you sure you want to send %1? + Soovid kindlasti saata %1? + + + + and + ja + + + + The recipient address is not valid, please recheck. + Saaja aadress ei ole kehtiv, palun kontrolli. + + + + The amount to pay must be larger than 0. + Makstav summa peab olema suurem kui 0. + + + + The amount exceeds your balance. + Summa ületab jäägi. + + + + The total exceeds your balance when the %1 transaction fee is included. + Summa koos tehingu tasuga %1 ületab sinu jääki. + + + + Duplicate address found, can only send to each address once per send operation. + Ühe saatmisega topelt-adressaati olla ei tohi. + + + + Error: Transaction creation failed! + Tõrge: Tehingu loomine ebaõnnestus! + + + + 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. + Viga: Tehingust keelduti. Nt rahakoti koopia kasutamisel võivad selle põhjustada juba kulutatud mündid. + + + + SendCoinsEntry + + + Form + Vorm + + + + A&mount: + S&umma: + + + + Pay &To: + Maksa &: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Tehingu saaja aadress (nt: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + Aadressiraamatusse sisestamiseks märgista aadress + + + + &Label: + &Märgis + + + + Choose address from address book + Vali saaja aadressiraamatust + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Kleebi aadress vahemälust + + + + Alt+P + Alt+P + + + + Remove this recipient + Saaja eemaldamine + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Sisesta CryptoLottoTokeni aadress (nt: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Signatuurid - Allkirjasta / Kinnita Sõnum + + + + &Sign Message + &Allkirjastamise teade + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Omandiõigsuse tõestamiseks saad sõnumeid allkirjastada oma aadressiga. Ettevaatust petturitega, kes üritavad saada sinu allkirja endale saada. Allkirjasta ainult korralikult täidetud avaldusi, millega nõustud. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Sõnumi signeerimise aadress (nt: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Vali aadress aadressiraamatust + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Kleebi aadress vahemälust + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Sisesta siia allkirjastamise sõnum + + + + Signature + Signatuur + + + + Copy the current signature to the system clipboard + Kopeeri praegune signatuur vahemällu + + + + Sign the message to prove you own this CryptoLottoToken address + Allkirjasta sõnum CryptoLottoTokeni aadressi sulle kuulumise tõestamiseks + + + + Sign &Message + Allkirjasta &Sõnum + + + + Reset all sign message fields + Tühjenda kõik sõnumi allkirjastamise väljad + + + + + Clear &All + Puhasta &Kõik + + + + &Verify Message + &Kinnita Sõnum + + + + Enter the signing 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. + Kinnitamiseks sisesta allkirjastamise aadress, sõnum (kindlasti kopeeri täpselt ka reavahetused, tühikud, tabulaatorid jms) ning allolev signatuur. + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Aadress, millega sõnum allkirjastati (nt: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Kinnita sõnum tõestamaks selle allkirjastatust määratud CryptoLottoTokeni aadressiga. + + + + Verify &Message + Kinnita &Sõnum + + + + Reset all verify message fields + Tühjenda kõik sõnumi kinnitamise väljad + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Sisesta CryptoLottoTokeni aadress (nt: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Signatuuri genereerimiseks vajuta "Allkirjasta Sõnum" + + + + Enter CryptoLottoToken signature + Sisesta CryptoLottoTokeni allkiri + + + + + The entered address is invalid. + Sisestatud aadress ei kehti. + + + + + + + Please check the address and try again. + Palun kontrolli aadressi ning proovi uuesti. + + + + + The entered address does not refer to a key. + Sisestatud aadress ei viita võtmele. + + + + Wallet unlock was cancelled. + Rahakoti avamine katkestati. + + + + Private key for the entered address is not available. + Sisestatud aadressi privaatvõti ei ole saadaval. + + + + Message signing failed. + Sõnumi signeerimine ebaõnnestus. + + + + Message signed. + Sõnum signeeritud. + + + + The signature could not be decoded. + Signatuuri ei õnnestunud dekodeerida. + + + + + Please check the signature and try again. + Palun kontrolli signatuuri ning proovi uuesti. + + + + The signature did not match the message digest. + Signatuur ei kattunud sõnumi kokkuvõttega. + + + + Message verification failed. + Sõnumi kontroll ebaõnnestus. + + + + Message verified. + Sõnum kontrollitud. + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + + + + + TransactionDesc + + + Open until %1 + Avatud kuni %1 + + + + %1/offline + %/1offline'is + + + + %1/unconfirmed + %1/kinnitamata + + + + %1 confirmations + %1 kinnitust + + + + Status + Staatus + + + + , broadcast through %n node(s) + , levita läbi %n node'i, levita läbi %n node'i + + + + Date + Kuupäev + + + + Source + Allikas + + + + Generated + Genereeritud + + + + + From + Saatja + + + + + + To + Saaja + + + + + own address + oma aadress + + + + label + märgis + + + + + + + + Credit + Krediit + + + + matures in %n more block(s) + aegub %n bloki pärastaegub %n bloki pärast + + + + not accepted + mitte aktsepteeritud + + + + + + + Debit + Deebet + + + + Transaction fee + Tehingu tasu + + + + Net amount + Neto summa + + + + Message + Sõnum + + + + Comment + Kommentaar + + + + Transaction ID + Tehingu ID + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Enne, kui loodud münte saab kulutama asuda, peavad need läbima 120 blokki. Kui sina selle bloki lõid, levitati see, bloki jadasse ühendamiseks, võrgustikku. Kui jadasse ühendamine ei õnnestu, muudetakse tema staatus "keeldutud" olekusse ning seda ei saa kulutada. Selline olukord võib juhtuda, kui mõni teine node loob bloki sinuga samal ajal. + + + + Debug information + Debug'imise info + + + + Transaction + Tehing + + + + Inputs + Sisendid + + + + Amount + Kogus + + + + true + õige + + + + false + vale + + + + , has not been successfully broadcast yet + , veel esitlemata + + + + Open for %n more block(s) + Avaneb %n bloki pärastAvaneb %n bloki pärast + + + + unknown + tundmatu + + + + TransactionDescDialog + + + Transaction details + Tehingu üksikasjad + + + + This pane shows a detailed description of the transaction + Paan kuvab tehingu detailid + + + + TransactionTableModel + + + Date + Kuupäev + + + + Type + Tüüp + + + + Address + Aadress + + + + Amount + Kogus + + + + Open for %n more block(s) + Avaneb %n bloki pärastAvaneb %n bloki pärast + + + + Open until %1 + Avatud kuni %1 + + + + Offline (%1 confirmations) + Ühenduseta (%1 kinnitust) + + + + Unconfirmed (%1 of %2 confirmations) + Kinnitamata (%1/%2 kinnitust) + + + + Confirmed (%1 confirmations) + Kinnitatud (%1 kinnitust) + + + + Mined balance will be available when it matures in %n more block(s) + Mine'itud jääk muutub kättesaadavaks %n bloki läbimiselMine'itud jääk muutub kättesaadavaks %n bloki läbimisel + + + + This block was not received by any other nodes and will probably not be accepted! + Antud klotsi pole saanud ükski osapool ning tõenäoliselt seda ei aktsepteerita! + + + + Generated but not accepted + Loodud, kuid aktsepteerimata + + + + Received with + Saadud koos + + + + Received from + Kellelt saadud + + + + Sent to + Saadetud + + + + Payment to yourself + Makse iseendale + + + + Mined + Mine'itud + + + + (n/a) + (n/a) + + + + Transaction status. Hover over this field to show number of confirmations. + Tehingu staatus. Kinnituste arvu kuvamiseks liigu hiire noolega selle peale. + + + + Date and time that the transaction was received. + Tehingu saamise kuupäev ning kellaaeg. + + + + Type of transaction. + Tehingu tüüp. + + + + Destination address of transaction. + Tehingu saaja aadress. + + + + Amount removed from or added to balance. + Jäägile lisatud või eemaldatud summa. + + + + TransactionView + + + + All + Kõik + + + + Today + Täna + + + + This week + Jooksev nädal + + + + This month + Jooksev kuu + + + + Last month + Eelmine kuu + + + + This year + Jooksev aasta + + + + Range... + Ulatus... + + + + Received with + Saadud koos + + + + Sent to + Saadetud + + + + To yourself + Iseendale + + + + Mined + Mine'itud + + + + Other + Muu + + + + Enter address or label to search + Otsimiseks sisesta märgis või aadress + + + + Min amount + Vähim summa + + + + Copy address + Aadressi kopeerimine + + + + Copy label + Märgise kopeerimine + + + + Copy amount + Kopeeri summa + + + + Copy transaction ID + Kopeeri tehingu ID + + + + Edit label + Märgise muutmine + + + + Show transaction details + Kuva tehingu detailid + + + + Export Transaction Data + Tehinguandmete eksport + + + + Comma separated file (*.csv) + Komaeraldatud fail (*.csv) + + + + Confirmed + Kinnitatud + + + + Date + Kuupäev + + + + Type + Tüüp + + + + Label + Silt + + + + Address + Aadress + + + + Amount + Kogus + + + + ID + ID + + + + Error exporting + Viga eksportimisel + + + + Could not write to file %1. + Tõrge faili kirjutamisel %1. + + + + Range: + Ulatus: + + + + to + saaja + + + + WalletModel + + + Send Coins + + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + + + + + Backup Wallet + Varundatud Rahakott + + + + Wallet Data (*.dat) + Rahakoti andmed (*.dat) + + + + Backup Failed + Varundamine nurjus + + + + There was an error trying to save the wallet data to the new location. + Rahakoti andmete uude kohta salvestamine nurjus. + + + + Backup Successful + Varundamine õnnestus + + + + The wallet data was successfully saved to the new location. + Rahakoti andmete uude kohta salvestamine õnnestus. + + + + bitcoin-core + + + CryptoLottoToken version + CryptoLottoTokeni versioon + + + + Usage: + Kasutus: + + + + Send command to -server or CryptoLottoTokend + Saada käsklus -serverile või CryptoLottoTokendile + + + + List commands + Käskluste loetelu + + + + Get help for a command + Käskluste abiinfo + + + + Options: + Valikud: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Täpsusta sätete fail (vaikimisi: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + Täpsusta PID fail (vaikimisi: CryptoLottoToken.pid) + + + + Specify data directory + Täpsusta andmekataloog + + + + Set database cache size in megabytes (default: 25) + Sea andmebaasi vahemälu suurus MB (vaikeväärtus: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Kuula ühendusi pordil <port> (vaikeväärtus: 22556 või testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Säilita vähemalt <n> ühendust peeridega (vaikeväärtus: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Peeri aadressi saamiseks ühendu korraks node'iga + + + + Specify your own public address + Täpsusta enda avalik aadress + + + + Threshold for disconnecting misbehaving peers (default: 100) + Ulakate peeride valulävi (vaikeväärtus: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Mitme sekundi pärast ulakad peerid tagasi võivad tulla (vaikeväärtus: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + RPC pordi %u kuulamiseks seadistamisel ilmnes viga IPv4'l: %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Kuula JSON-RPC ühendusel seda porti <port> (vaikeväärtus: 22555 või testnet: 44555) + + + + Accept command line and JSON-RPC commands + Luba käsurea ning JSON-RPC käsklusi + + + + Run in the background as a daemon and accept commands + Tööta taustal ning aktsepteeri käsklusi + + + + Use the test network + Testvõrgu kasutamine + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Luba välisühendusi (vaikeväärtus: 1 kui puudub -proxy või -connect) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + %s, sul tuleb rpcpassword määrata seadete failis: +%s +Soovitatav on kasutada järgmist juhuslikku parooli: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(seda parooli ei pea meeles pidama) +Kasutajanimi ning parool EI TOHI kattuda. +Kui faili ei leita, loo see ainult-omaniku-loetavas failiõigustes . +Soovitatav on seadistada tõrgete puhul teavitus; +nt: alertnotify=echo %%s | email -s "CryptoLottoToken Alert" admin@foo.com + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + RPC pordi %u kuulamiseks seadistamisel ilmnes viga IPv6'l, lülitumine tagasi IPv4'le : %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + Määratud aadressiga sidumine ning sellelt kuulamine. IPv6 jaoks kasuta vormingut [host]:port + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + Ei suuda määrata ainuõigust andmekaustale %s. Tõenäolisel on CryptoLottoToken juba avatud. + + + + 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. + Tõrge: Tehingust keelduti! Põhjuseks võib olla juba kulutatud mündid, nt kui wallet.dat fail koopias kulutatid mündid, kuid ei märgitud neid siin vastavalt. + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + Tõrge: Selle tehingu jaoks on nõutav lisatasu vähemalt %s. Põhjuseks võib olla summa suurus, keerukus või hiljuti saadud summade kasutamine! + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + Käivita käsklus, kui saabub tähtis hoiatus (%s cmd's asendatakse sõnumiga) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Käivita käsklus, kui rahakoti tehing muutub (%s cmd's muudetakse TxID'ks) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Sea "kõrge tähtsusega"/"madala tehingu lisatasuga" tehingute maksimumsuurus baitides (vaikeväärtus: 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + See on test-versioon - kasutamine omal riisikol - ära kasuta mining'uks ega kaupmeeste programmides + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Hoiatus: -paytxfee on seatud väga kõrgeks! See on sinu poolt makstav tehingu lisatasu. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + Hoiatus: Kuvatavad tehingud ei pruugi olla korrektsed! Sina või node'id peate tegema uuenduse. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Hoiatus: Palun kontrolli oma arvuti kuupäeva/kellaaega! Kui arvuti kell on vale, siis CryptoLottoToken ei tööta korralikult + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Hoiatus: ilmnes tõrge wallet.dat faili lugemisel! Võtmed on terved, kuid tehingu andmed või aadressiraamatu kirjed võivad olla kadunud või vigased. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Hoiatus: toimus wallet.dat faili andmete päästmine! Originaal wallet.dat nimetati kaustas %s ümber wallet.{ajatempel}.bak'iks, jäägi või tehingute ebakõlade puhul tuleks teha backup'ist taastamine. + + + + Attempt to recover private keys from a corrupt wallet.dat + Püüa vigasest wallet.dat failist taastada turvavõtmed + + + + Block creation options: + Blokeeri loomise valikud: + + + + Connect only to the specified node(s) + Ühendu ainult määratud node'i(de)ga + + + + Corrupted block database detected + Tuvastati vigane bloki andmebaas + + + + Discover own IP address (default: 1 when listening and no -externalip) + Leia oma IP aadress (vaikeväärtus: 1, kui kuulatakse ning puudub -externalip) + + + + Do you want to rebuild the block database now? + Kas soovid bloki andmebaasi taastada? + + + + Error initializing block database + Tõrge bloki andmebaasi käivitamisel + + + + Error initializing wallet database environment %s! + Tõrge rahakoti keskkonna %s käivitamisel! + + + + Error loading block database + Tõrge bloki baasi lugemisel + + + + Error opening block database + Tõrge bloki andmebaasi avamisel + + + + Error: Disk space is low! + Tõrge: liiga vähe kettaruumi! + + + + Error: Wallet locked, unable to create transaction! + Tõrge: Rahakott on lukus, tehingu loomine ei ole võimalik! + + + + Error: system error: + Tõrge: süsteemi tõrge: + + + + Failed to listen on any port. Use -listen=0 if you want this. + Pordi kuulamine nurjus. Soovikorral kasuta -listen=0. + + + + Failed to read block info + Tõrge bloki sisu lugemisel + + + + Failed to read block + Bloki lugemine ebaõnnestus + + + + Failed to sync block index + Bloki indeksi sünkimine ebaõnnestus + + + + Failed to write block index + Bloki indeksi kirjutamine ebaõnnestus + + + + Failed to write block info + Bloki sisu kirjutamine ebaõnnestus + + + + Failed to write block + Tõrge bloki sisu kirjutamisel + + + + Failed to write file info + Tõrge faili info kirjutamisel + + + + Failed to write to coin database + Tõrge mündi andmebaasi kirjutamisel + + + + Failed to write transaction index + Tehingu indeksi kirjutamine ebaõnnestus + + + + Failed to write undo data + Tagasivõtmise andmete kirjutamine ebaõnnestus + + + + Find peers using DNS lookup (default: 1 unless -connect) + Otsi DNS'i lookup'i kastavaid peere (vaikeväärtus: 1, kui mitte -connect) + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + Käivitamisel kontrollitavate blokkide arv (vaikeväärtus: 288, 0=kõik) + + + + How thorough the block verification is (0-4, default: 3) + Blokkide kontrollimise põhjalikkus (0-4, vaikeväärtus: 3) + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + Taasta bloki jada indeks blk000??.dat failist + + + + Set the number of threads to service RPC calls (default: 4) + Määra RPC kõnede haldurite arv (vaikeväärtus: 4) + + + + Verifying blocks... + Kontrollin blokke... + + + + Verifying wallet... + Kontrollin rahakotti... + + + + Imports blocks from external blk000??.dat file + Impordi blokid välisest blk000??.dat failist + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + Informatsioon + + + + Invalid -tor address: '%s' + Vigane -tor aadress: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + Säilita kogu tehingu indeks (vaikeväärtus: 0) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Maksimaalne saamise puhver -connection kohta , <n>*1000 baiti (vaikeväärtus: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Maksimaalne saatmise puhver -connection kohta , <n>*1000 baiti (vaikeväärtus: 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + Tunnusta ainult sisseehitatud turvapunktidele vastavaid bloki jadu (vaikeväärtus: 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Ühenda ainult node'idega <net> võrgus (IPv4, IPv6 või Tor) + + + + Output extra debugging information. Implies all other -debug* options + Väljund lisa debug'imise infoks. Tuleneb kõikidest teistest -debug* valikutest + + + + Output extra network debugging information + Lisa võrgu debug'imise info väljund + + + + Prepend debug output with timestamp (default: 1) + Varusta debugi väljund ajatempliga + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + SSL valikud: (vaata CryptoLottoTokeni Wikist või SSL sätete juhendist) + + + + Select the version of socks proxy to use (4-5, default: 5) + Vali turva proxi SOCKS versioon (4-5, vaikeväärtus: 5) + + + + Send trace/debug info to console instead of debug.log file + Saada jälitus/debug, debug.log faili asemel, konsooli + + + + Send trace/debug info to debugger + Saada jälitus/debug info debuggerile + + + + Set maximum block size in bytes (default: 250000) + Sea maksimaalne bloki suurus baitides (vaikeväärtus: 250000) + + + + Set minimum block size in bytes (default: 0) + Sea minimaalne bloki suurus baitides (vaikeväärtus: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Kahanda programmi käivitamisel debug.log faili (vaikeväärtus: 1, kui ei ole -debug) + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + Sea ühenduse timeout millisekundites (vaikeväärtus: 5000) + + + + System error: + Süsteemi tõrge: + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + Kasuta kuulatava pordi määramiseks UPnP ühendust (vaikeväärtus: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Kasuta kuulatava pordi määramiseks UPnP ühendust (vaikeväärtus: 1, kui kuulatakse) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Kasuta varjatud teenustele ligipääsuks proxy't (vaikeväärtus: sama, mis -proxy) + + + + Username for JSON-RPC connections + JSON-RPC ühenduste kasutajatunnus + + + + Warning + Hoiatus + + + + Warning: This version is obsolete, upgrade required! + Hoiatus: versioon on aegunud, uuendus on nõutav! + + + + You need to rebuild the databases using -reindex to change -txindex + Andmebaas tuleb taastada kasutades -reindex, et muuta -txindex + + + + wallet.dat corrupt, salvage failed + wallet.dat fail on katki, päästmine ebaõnnestus + + + + Password for JSON-RPC connections + JSON-RPC ühenduste salasõna + + + + Allow JSON-RPC connections from specified IP address + JSON-RPC ühenduste lubamine kindla IP pealt + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Saada käsklusi node'ile IP'ga <ip> (vaikeväärtus: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Käivita käsklus, kui parim plokk muutub (käskluse %s asendatakse ploki hash'iga) + + + + Upgrade wallet to latest format + Uuenda rahakott uusimasse vormingusse + + + + Set key pool size to <n> (default: 100) + Sea võtmete hulgaks <n> (vaikeväärtus: 100) + + + + Rescan the block chain for missing wallet transactions + Otsi ploki jadast rahakoti kadunud tehinguid + + + + Use OpenSSL (https) for JSON-RPC connections + Kasuta JSON-RPC ühenduste jaoks OpenSSL'i (https) + + + + Server certificate file (default: server.cert) + Serveri sertifikaadifail (vaikeväärtus: server.cert) + + + + Server private key (default: server.pem) + Serveri privaatvõti (vaikeväärtus: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Lubatud šiffrid (vaikeväärtus: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Käesolev abitekst + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Selle arvutiga ei ole võimalik siduda %s külge (katse nurjus %d, %s tõttu) + + + + Connect through socks proxy + Ühendu läbi turva proxi + + + + Allow DNS lookups for -addnode, -seednode and -connect + -addnode, -seednode ja -connect tohivad kasutada DNS lookup'i + + + + Loading addresses... + Aadresside laadimine... + + + + Error loading wallet.dat: Wallet corrupted + Viga wallet.dat käivitamisel. Vigane rahakkott + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Viga wallet.dat käivitamisel: Rahakott nõuab CryptoLottoTokeni uusimat versiooni + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Rahakott tuli ümberkirjutada: toimingu lõpetamiseks taaskäivita CryptoLottoToken + + + + Error loading wallet.dat + Viga wallet.dat käivitamisel + + + + Invalid -proxy address: '%s' + Vigane -proxi aadress: '%s' + + + + Unknown network specified in -onlynet: '%s' + Kirjeldatud tundmatu võrgustik -onlynet'is: '%s' + + + + Unknown -socks proxy version requested: %i + Küsitud tundmatu -socks proxi versioon: %i + + + + Cannot resolve -bind address: '%s' + Tundmatu -bind aadress: '%s' + + + + Cannot resolve -externalip address: '%s' + Tundmatu -externalip aadress: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + -paytxfee=<amount> jaoks vigane kogus: '%s' + + + + Invalid amount + Kehtetu summa + + + + Insufficient funds + Liiga suur summa + + + + Loading block index... + Klotside indeksi laadimine... + + + + Add a node to connect to and attempt to keep the connection open + Lisa node ning hoia ühendus avatud + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + %s'ga ei ole võimalik sellest arvutist siduda. CryptoLottoToken juba töötab. + + + + Fee per KB to add to transactions you send + Minu saadetavate tehingute lisatasu KB kohta + + + + Loading wallet... + Rahakoti laadimine... + + + + Cannot downgrade wallet + Rahakoti vanandamine ebaõnnestus + + + + Cannot write default address + Tõrge vaikimisi aadressi kirjutamisel + + + + Rescanning... + Üleskaneerimine... + + + + Done loading + Laetud + + + + To use the %s option + %s valiku kasutamine + + + + Error + Tõrge + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + rpcpassword=<password> peab sätete failis olema seadistatud:⏎ +%s⏎ +Kui seda faili ei ole, loo see ainult-omanikule-lugemiseks faili õigustes. + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_eu_ES.qm b/src/qt/locale/bitcoin_eu_ES.qm new file mode 100644 index 0000000..46dc2b2 Binary files /dev/null and b/src/qt/locale/bitcoin_eu_ES.qm differ diff --git a/src/qt/locale/bitcoin_eu_ES.ts b/src/qt/locale/bitcoin_eu_ES.ts new file mode 100644 index 0000000..51604e0 --- /dev/null +++ b/src/qt/locale/bitcoin_eu_ES.ts @@ -0,0 +1,2917 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + CryptoLottoToken-i buruz + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> bertsioa + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + Helbide-liburua + + + + Double-click to edit address or label + Klik bikoitza helbidea edo etiketa editatzeko + + + + Create a new address + Sortu helbide berria + + + + Copy the currently selected address to the system clipboard + Kopiatu hautatutako helbidea sistemaren arbelera + + + + &New Address + + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + + + + + &Copy Address + + + + + Show &QR Code + Erakutsi &QR kodea + + + + Sign a message to prove you own a CryptoLottoToken address + + + + + Sign &Message + + + + + Delete the currently selected address from the list + + + + + Export the data in the current tab to a file + + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + + + + + &Verify Message + + + + + &Delete + &Ezabatu + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + + + + + &Edit + + + + + Send &Coins + + + + + Export Address Book Data + Esportatu Helbide-liburuaren datuak + + + + Comma separated file (*.csv) + Komaz bereizitako artxiboa (*.csv) + + + + Error exporting + Errorea esportatzean + + + + Could not write to file %1. + Ezin idatzi %1 artxiboan. + + + + AddressTableModel + + + Label + Etiketa + + + + Address + Helbidea + + + + (no label) + (etiketarik ez) + + + + AskPassphraseDialog + + + Passphrase Dialog + + + + + Enter passphrase + Sartu pasahitza + + + + New passphrase + Pasahitz berria + + + + Repeat new passphrase + Errepikatu pasahitz berria + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Sartu zorrorako pasahitz berria.<br/> Mesedez erabili <b>gutxienez ausazko 10 karaktere</b>, edo <b>gutxienez zortzi hitz</b> pasahitza osatzeko. + + + + Encrypt wallet + Enkriptatu zorroa + + + + This operation needs your wallet passphrase to unlock the wallet. + Eragiketa honek zorroaren pasahitza behar du zorroa desblokeatzeko. + + + + Unlock wallet + Desblokeatu zorroa + + + + This operation needs your wallet passphrase to decrypt the wallet. + Eragiketa honek zure zorroaren pasahitza behar du, zorroa desenkriptatzeko. + + + + Decrypt wallet + Desenkriptatu zorroa + + + + Change passphrase + Aldatu pasahitza + + + + Enter the old and new passphrase to the wallet. + Sartu zorroaren pasahitz zaharra eta berria. + + + + Confirm wallet encryption + Berretsi zorroaren enkriptazioa + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + + + + + Are you sure you wish to encrypt your wallet? + + + + + 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. + + + + + + Warning: The Caps Lock key is on! + + + + + + Wallet encrypted + Zorroa enkriptatuta + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + + + + + + + + Wallet encryption failed + Zorroaren enkriptazioak huts egin du + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Zorroaren enkriptazioak huts egin du barne-errore baten ondorioz. Zure zorroa ez da enkriptatu. + + + + + The supplied passphrases do not match. + Eman dituzun pasahitzak ez datoz bat. + + + + Wallet unlock failed + Zorroaren desblokeoak huts egin du + + + + + + The passphrase entered for the wallet decryption was incorrect. + Zorroa desenkriptatzeko sartutako pasahitza okerra da. + + + + Wallet decryption failed + Zorroaren desenkriptazioak huts egin du + + + + Wallet passphrase was successfully changed. + + + + + BitcoinGUI + + + Sign &message... + + + + + Synchronizing with network... + Sarearekin sinkronizatzen... + + + + &Overview + &Gainbegiratu + + + + Show general overview of wallet + Ikusi zorroaren begirada orokorra + + + + &Transactions + &Transakzioak + + + + Browse transaction history + Ikusi transakzioen historia + + + + Edit the list of stored addresses and labels for sending + Editatu gordetako helbide eta etiketen zerrenda + + + + Show the list of addresses for receiving payments + Erakutsi ordainketak jasotzeko helbideen zerrenda + + + + E&xit + Irten + + + + Quit application + Irten aplikaziotik + + + + Show information about CryptoLottoToken + Erakutsi CryptoLottoToken-i buruzko informazioa + + + + About &Qt + &Qt-ari buruz + + + + Show information about Qt + Erakutsi CryptoLottoToken-i buruzko informazioa + + + + &Options... + &Aukerak... + + + + &Encrypt Wallet... + + + + + &Backup Wallet... + + + + + &Change Passphrase... + + + + + Importing blocks from disk... + + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + + + + + Modify configuration options for CryptoLottoToken + + + + + Backup wallet to another location + + + + + Change the passphrase used for wallet encryption + Aldatu zorroa enkriptatzeko erabilitako pasahitza + + + + &Debug window + + + + + Open debugging and diagnostic console + + + + + &Verify message... + + + + + + CryptoLottoToken + + + + + Wallet + + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + + + + + &Show / Hide + + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + &Artxiboa + + + + &Settings + &Ezarpenak + + + + &Help + &Laguntza + + + + Tabs toolbar + Fitxen tresna-barra + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + + + + + %n active connection(s) to CryptoLottoToken network + Konexio aktibo %n CryptoLottoToken-en sarera%n konexio aktibo CryptoLottoToken-en sarera + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + + + + + Warning + + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + Egunean + + + + Catching up... + Eguneratzen... + + + + Confirm transaction fee + + + + + Sent transaction + Bidalitako transakzioa + + + + Incoming transaction + Sarrerako transakzioa + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + + + + + + URI handling + + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Zorroa <b>enkriptatuta</b> eta <b>desblokeatuta</b> dago une honetan + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Zorroa <b>enkriptatuta</b> eta <b>blokeatuta</b> dago une honetan + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + + + + + EditAddressDialog + + + Edit Address + Editatu helbidea + + + + &Label + &Etiketa + + + + The label associated with this address book entry + Helbide-liburuko sarrera honekin lotutako etiketa + + + + &Address + &Helbidea + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Helbide-liburuko sarrera honekin lotutako helbidea. Bidaltzeko helbideeta soilik alda daiteke. + + + + New receiving address + Jasotzeko helbide berria + + + + New sending address + Bidaltzeko helbide berria + + + + Edit receiving address + Editatu jasotzeko helbidea + + + + Edit sending address + Editatu bidaltzeko helbidea + + + + The entered address "%1" is already in the address book. + Sartu berri den helbidea, "%1", helbide-liburuan dago jadanik. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + + + + + Could not unlock wallet. + Ezin desblokeatu zorroa. + + + + New key generation failed. + Gako berriaren sorrerak huts egin du. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + + + + + version + + + + + Usage: + + + + + command-line options + + + + + UI options + + + + + Set language, for example "de_DE" (default: system locale) + + + + + Start minimized + + + + + Show splash screen on startup (default: 1) + + + + + OptionsDialog + + + Options + Aukerak + + + + &Main + + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + + + + + Automatically start CryptoLottoToken after logging in to the system. + + + + + &Start CryptoLottoToken on system login + + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + + + + + Map port using &UPnP + + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + + + + + &Connect through SOCKS proxy: + + + + + Proxy &IP: + + + + + IP address of the proxy (e.g. 127.0.0.1) + + + + + &Port: + + + + + Port of the proxy (e.g. 9050) + + + + + SOCKS &Version: + + + + + SOCKS version of the proxy (e.g. 5) + + + + + &Window + + + + + Show only a tray icon after minimizing the window. + + + + + &Minimize to the tray instead of the taskbar + + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + + + + + M&inimize on close + + + + + &Display + + + + + User Interface &language: + + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + + + + + &Unit to show amounts in: + + + + + Choose the default subdivision unit to show in the interface and when sending coins. + + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + + + &Display addresses in transaction list + + + + + &OK + + + + + &Cancel + + + + + &Apply + + + + + default + + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + + + + + + This setting will take effect after restarting CryptoLottoToken. + + + + + The supplied proxy address is invalid. + + + + + OverviewPage + + + Form + Inprimakia + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + + + + + Balance: + Saldoa: + + + + Unconfirmed: + Konfirmatu gabe: + + + + Wallet + + + + + Immature: + + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + <b>Azken transakzioak</b> + + + + Your current balance + Zure uneko saldoa + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Oraindik konfirmatu gabe daudenez, uneko saldoab kontatu gabe dagoen transakzio kopurua + + + + + out of sync + + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + + + + + Request Payment + + + + + Amount: + Kopurua + + + + Label: + &Etiketa: + + + + Message: + Mezua + + + + &Save As... + Gorde honela... + + + + Error encoding URI into QR Code. + + + + + The entered amount is invalid, please check. + + + + + Resulting URI too long, try to reduce the text for label / message. + + + + + Save QR Code + + + + + PNG Images (*.png) + + + + + RPCConsole + + + Client name + + + + + + + + + + + + + + N/A + + + + + Client version + + + + + &Information + + + + + Using OpenSSL version + + + + + Startup time + + + + + Network + + + + + Number of connections + + + + + On testnet + + + + + Block chain + + + + + Current number of blocks + + + + + Estimated total blocks + + + + + Last block time + + + + + &Open + + + + + Command-line options + + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + + &Show + + + + + &Console + + + + + Build date + + + + + CryptoLottoToken - Debug window + + + + + CryptoLottoToken Core + + + + + Debug log file + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + + + + + Welcome to the CryptoLottoToken RPC console. + + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + + + + + Type <b>help</b> for an overview of available commands. + + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Bidali txanponak + + + + Send to multiple recipients at once + Bidali hainbat jasotzaileri batera + + + + Add &Recipient + + + + + Remove all transaction fields + + + + + Clear &All + + + + + Balance: + Saldoa: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Berretsi bidaltzeko ekintza + + + + S&end + + + + + <b>%1</b> to %2 (%3) + <b>%1</b> honi: %2 (%3) + + + + Confirm send coins + Berretsi txanponak bidaltzea + + + + Are you sure you want to send %1? + Ziur zaude %1 bidali nahi duzula? + + + + and + eta + + + + The recipient address is not valid, please recheck. + + + + + The amount to pay must be larger than 0. + Ordaintzeko kopurua 0 baino handiagoa izan behar du. + + + + The amount exceeds your balance. + + + + + The total exceeds your balance when the %1 transaction fee is included. + + + + + Duplicate address found, can only send to each address once per send operation. + + + + + Error: Transaction creation failed! + + + + + 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. + + + + + SendCoinsEntry + + + Form + Inprimakia + + + + A&mount: + K&opurua: + + + + Pay &To: + Ordaindu &honi: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + Sartu etiketa bat helbide honetarako, eta gehitu zure helbide-liburuan + + + + &Label: + &Etiketa: + + + + Choose address from address book + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Itsatsi helbidea arbeletik + + + + Alt+P + Alt+P + + + + Remove this recipient + Ezabatu jasotzaile hau + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Sartu Bitocin helbide bat (adb.: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + + + + + &Sign Message + + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Choose an address from the address book + + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Itsatsi helbidea arbeletik + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + + + + + Signature + + + + + Copy the current signature to the system clipboard + + + + + Sign the message to prove you own this CryptoLottoToken address + + + + + Sign &Message + + + + + Reset all sign message fields + + + + + + Clear &All + + + + + &Verify Message + + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + + + + + Verify &Message + + + + + Reset all verify message fields + + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Sartu Bitocin helbide bat (adb.: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + + + + + Enter CryptoLottoToken signature + + + + + + The entered address is invalid. + + + + + + + + Please check the address and try again. + + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + + + + + Private key for the entered address is not available. + + + + + Message signing failed. + + + + + Message signed. + + + + + The signature could not be decoded. + + + + + + Please check the signature and try again. + + + + + The signature did not match the message digest. + + + + + Message verification failed. + + + + + Message verified. + + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + + + + + TransactionDesc + + + Open until %1 + Zabalik %1 arte + + + + %1/offline + + + + + %1/unconfirmed + %1/konfirmatu gabe + + + + %1 confirmations + %1 konfirmazioak + + + + Status + + + + + , broadcast through %n node(s) + + + + + Date + Data + + + + Source + + + + + Generated + + + + + + From + + + + + + + To + + + + + + own address + + + + + label + + + + + + + + + Credit + + + + + matures in %n more block(s) + + + + + not accepted + + + + + + + + Debit + + + + + Transaction fee + + + + + Net amount + + + + + Message + + + + + Comment + + + + + Transaction ID + + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + + + + + Debug information + + + + + Transaction + + + + + Inputs + + + + + Amount + Kopurua + + + + true + + + + + false + + + + + , has not been successfully broadcast yet + , ez da arrakastaz emititu oraindik + + + + Open for %n more block(s) + + + + + unknown + ezezaguna + + + + TransactionDescDialog + + + Transaction details + Transakzioaren xehetasunak + + + + This pane shows a detailed description of the transaction + Panel honek transakzioaren deskribapen xehea erakusten du + + + + TransactionTableModel + + + Date + Data + + + + Type + Mota + + + + Address + Helbidea + + + + Amount + Kopurua + + + + Open for %n more block(s) + + + + + Open until %1 + Zabalik %1 arte + + + + Offline (%1 confirmations) + Offline (%1 konfirmazio) + + + + Unconfirmed (%1 of %2 confirmations) + + + + + Confirmed (%1 confirmations) + Konfirmatuta (%1 konfirmazio) + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + Bloke hau ez du beste inongo nodorik jaso, eta seguruenik ez da onartuko! + + + + Generated but not accepted + Sortua, baina ez onartua + + + + Received with + Jasoa honekin: + + + + Received from + + + + + Sent to + Honi bidalia: + + + + Payment to yourself + Ordainketa zeure buruari + + + + Mined + Bildua + + + + (n/a) + (n/a) + + + + Transaction status. Hover over this field to show number of confirmations. + Transakzioaren egoera. Pasatu sagua gainetik konfirmazio kopurua ikusteko. + + + + Date and time that the transaction was received. + Transakzioa jasotako data eta ordua. + + + + Type of transaction. + Transakzio mota. + + + + Destination address of transaction. + Transakzioaren xede-helbidea. + + + + Amount removed from or added to balance. + Saldoan kendu edo gehitutako kopurua. + + + + TransactionView + + + + All + Denak + + + + Today + Gaur + + + + This week + Aste honetan + + + + This month + Hil honetan + + + + Last month + Azken hilean + + + + This year + Aurten + + + + Range... + Muga... + + + + Received with + Jasota honekin: + + + + Sent to + Hona bidalia: + + + + To yourself + Zeure buruari + + + + Mined + Bildua + + + + Other + Beste + + + + Enter address or label to search + Sartu bilatzeko helbide edo etiketa + + + + Min amount + Kopuru minimoa + + + + Copy address + Kopiatu helbidea + + + + Copy label + Kopiatu etiketa + + + + Copy amount + + + + + Copy transaction ID + + + + + Edit label + + + + + Show transaction details + + + + + Export Transaction Data + Transakzioaren xehetasunak + + + + Comma separated file (*.csv) + Komaz bereizitako artxiboa (*.csv) + + + + Confirmed + + + + + Date + Data + + + + Type + Mota + + + + Label + Etiketa + + + + Address + Helbidea + + + + Amount + Kopurua + + + + ID + + + + + Error exporting + Errorea esportatzean + + + + Could not write to file %1. + Ezin idatzi %1 artxiboan. + + + + Range: + + + + + to + + + + + WalletModel + + + Send Coins + + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + Botcoin bertsioa + + + + Usage: + + + + + Send command to -server or CryptoLottoTokend + + + + + List commands + Komandoen lista + + + + Get help for a command + Laguntza komando batean + + + + Options: + Aukerak + + + + Specify configuration file (default: CryptoLottoToken.conf) + Ezarpen fitxategia aukeratu (berezkoa: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + pid fitxategia aukeratu (berezkoa: CryptoLottoTokend.pid) + + + + Specify data directory + + + + + Set database cache size in megabytes (default: 25) + + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + + + + + Maintain at most <n> connections to peers (default: 125) + + + + + Connect to a node to retrieve peer addresses, and disconnect + + + + + Specify your own public address + + + + + Threshold for disconnecting misbehaving peers (default: 100) + + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + + + + + Accept command line and JSON-RPC commands + + + + + Run in the background as a daemon and accept commands + + + + + Use the test network + + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + + + + + Connect only to the specified node(s) + + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + + + + + Select the version of socks proxy to use (4-5, default: 5) + + + + + Send trace/debug info to console instead of debug.log file + + + + + Send trace/debug info to debugger + + + + + Set maximum block size in bytes (default: 250000) + + + + + Set minimum block size in bytes (default: 0) + + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + + + + + Use UPnP to map the listening port (default: 1 when listening) + + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + + + + + Allow JSON-RPC connections from specified IP address + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + + + + + Upgrade wallet to latest format + + + + + Set key pool size to <n> (default: 100) + + + + + Rescan the block chain for missing wallet transactions + + + + + Use OpenSSL (https) for JSON-RPC connections + + + + + Server certificate file (default: server.cert) + + + + + Server private key (default: server.pem) + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + + This help message + Laguntza mezu hau + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + + + + + Connect through socks proxy + + + + + Allow DNS lookups for -addnode, -seednode and -connect + + + + + Loading addresses... + + + + + Error loading wallet.dat: Wallet corrupted + + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + + + + + Error loading wallet.dat + + + + + Invalid -proxy address: '%s' + + + + + Unknown network specified in -onlynet: '%s' + + + + + Unknown -socks proxy version requested: %i + + + + + Cannot resolve -bind address: '%s' + + + + + Cannot resolve -externalip address: '%s' + + + + + Invalid amount for -paytxfee=<amount>: '%s' + + + + + Invalid amount + + + + + Insufficient funds + + + + + Loading block index... + + + + + Add a node to connect to and attempt to keep the connection open + + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + + + + + Fee per KB to add to transactions you send + + + + + Loading wallet... + + + + + Cannot downgrade wallet + + + + + Cannot write default address + + + + + Rescanning... + Birbilatzen... + + + + Done loading + Zamaketa amaitua + + + + To use the %s option + + + + + Error + + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_fa.qm b/src/qt/locale/bitcoin_fa.qm new file mode 100644 index 0000000..7cee590 Binary files /dev/null and b/src/qt/locale/bitcoin_fa.qm differ diff --git a/src/qt/locale/bitcoin_fa.ts b/src/qt/locale/bitcoin_fa.ts new file mode 100644 index 0000000..1c600f1 --- /dev/null +++ b/src/qt/locale/bitcoin_fa.ts @@ -0,0 +1,2925 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + در مورد CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + نسخه CryptoLottoToken + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + ⏎ ⏎ این نسخه نرم افزار آزمایشی است⏎ ⏎ نرم افزار تحت لیسانس MIT/X11 منتشر شده است. به فایل coping یا آدرس http://www.opensource.org/licenses/mit-license.php. مراجعه شود⏎ ⏎ این محصول شامل نرم افزاری است که با OpenSSL برای استفاده از OpenSSL Toolkit (http://www.openssl.org/) و نرم افزار نوشته شده توسط اریک یانگ (eay@cryptsoft.com ) و UPnP توسط توماس برنارد طراحی شده است. + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + فهرست آدرس + + + + Double-click to edit address or label + برای ویرایش آدرس یا بر چسب دو بار کلیک کنید + + + + Create a new address + آدرس جدید ایجاد کنید + + + + Copy the currently selected address to the system clipboard + آدرس انتخاب شده در سیستم تخته رسم گیره دار کپی کنید + + + + &New Address + آدرس جدید + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + این آدرسها، آدرسهای CryptoLottoToken شما برای دریافت وجوه هستند. شما ممکن است آدرسهای متفاوت را به هر گیرنده اختصاص دهید که بتوانید مواردی که پرداخت می کنید را پیگیری نمایید + + + + &Copy Address + کپی آدرس + + + + Show &QR Code + نمایش &کد QR + + + + Sign a message to prove you own a CryptoLottoToken address + پیام را برای اثبات آدرس CryptoLottoToken خود امضا کنید + + + + Sign &Message + امضا و پیام + + + + Delete the currently selected address from the list + آدرس انتخاب شده در سیستم تخته رسم گیره دا حذف + + + + Export the data in the current tab to a file + داده ها نوارِ جاری را به فایل انتقال دهید + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + یک پیام را برای حصول اطمینان از ورود به سیستم با آدرس CryptoLottoToken مشخص، شناسایی کنید + + + + &Verify Message + شناسایی پیام + + + + &Delete + حذف + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + کپی و برچسب گذاری + + + + &Edit + ویرایش + + + + Send &Coins + + + + + Export Address Book Data + آدرس انتخاب شده در سیستم تخته رسم گیره دار کپی کنید + + + + Comma separated file (*.csv) + Comma separated file (*.csv) + + + + Error exporting + خطای صدور + + + + Could not write to file %1. + تا فایل %1 نمی شود نوشت + + + + AddressTableModel + + + Label + بر چسب + + + + Address + آدرس + + + + (no label) + بدون برچسب + + + + AskPassphraseDialog + + + Passphrase Dialog + دیالوگ Passphrase + + + + Enter passphrase + وارد عبارت عبور + + + + New passphrase + عبارت عبور نو + + + + Repeat new passphrase + تکرار عبارت عبور نو + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + وارد کنید..&lt;br/&gt عبارت عبور نو در پنجره + 10 یا بیشتر کاراکتورهای تصادفی استفاده کنید &lt;b&gt لطفا عبارت عبور + + + + Encrypt wallet + رمز بندی پنجره + + + + This operation needs your wallet passphrase to unlock the wallet. + این عملیت نیاز عبارت عبور پنجره شما دارد برای رمز گشایی آن + + + + Unlock wallet + تکرار عبارت عبور نو + + + + This operation needs your wallet passphrase to decrypt the wallet. + این عملیت نیاز عبارت عبور شما دارد برای رمز بندی آن + + + + Decrypt wallet + رمز بندی پنجره + + + + Change passphrase + تغییر عبارت عبور + + + + Enter the old and new passphrase to the wallet. + عبارت عبور نو و قدیم در پنجره وارد کنید + + + + Confirm wallet encryption + تایید رمز گذاری + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + هشدار: اگر wallet رمزگذاری شود و شما passphrase را گم کنید شما همه اطلاعات CryptoLottoToken را از دست خواهید داد. + + + + Are you sure you wish to encrypt your wallet? + آیا اطمینان دارید که می خواهید wallet رمزگذاری شود؟ + + + + 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. + + + + + + Warning: The Caps Lock key is on! + هشدار: Caps lock key روشن است + + + + + Wallet encrypted + تغییر عبارت عبور + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + Biticon هم اکنون بسته می‌شود تا فرایند رمزگذاری را تمام کند. به خاطر داشته باشید که رمزگذاری کیف پولتان نمی‌تواند به طور کامل بیتیکون‌های شما را در برابر دزدیده شدن توسط بدافزارهایی که رایانه شما را آلوده می‌کنند، محافظت نماید. + + + + + + + Wallet encryption failed + عبارت عبور نو و قدیم در پنجره وارد کنید + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + تنا موفق رمز بندی پنجره ناشی از خطای داخل شد. پنجره شما مرز بندی نشده است + + + + + The supplied passphrases do not match. + عبارت عبور عرضه تطابق نشد + + + + Wallet unlock failed + نجره رمز گذار شد + + + + + + The passphrase entered for the wallet decryption was incorrect. + اموفق رمز بندی پنجر + + + + Wallet decryption failed + ناموفق رمز بندی پنجره + + + + Wallet passphrase was successfully changed. + wallet passphrase با موفقیت تغییر یافت + + + + BitcoinGUI + + + Sign &message... + امضا و پیام + + + + Synchronizing with network... + همگام سازی با شبکه ... + + + + &Overview + بررسی اجمالی + + + + Show general overview of wallet + نمای کلی پنجره نشان بده + + + + &Transactions + &معاملات + + + + Browse transaction history + نمایش تاریخ معاملات + + + + Edit the list of stored addresses and labels for sending + ویرایش لیست آدرسها و بر چسب های ذخیره ای + + + + Show the list of addresses for receiving payments + نمایش لیست آدرس ها برای در یافت پر داخت ها + + + + E&xit + خروج + + + + Quit application + خروج از برنامه + + + + Show information about CryptoLottoToken + نمایش اطلاعات در مورد بیتکویین + + + + About &Qt + درباره &Qt + + + + Show information about Qt + نمایش اطلاعات درباره Qt + + + + &Options... + تنظیمات... + + + + &Encrypt Wallet... + رمزگذاری wallet + + + + &Backup Wallet... + پشتیبان گیری از wallet + + + + &Change Passphrase... + تغییر Passphrase + + + + Importing blocks from disk... + + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + سکه ها را به آدرس bitocin ارسال کن + + + + Modify configuration options for CryptoLottoToken + انتخابهای پیکربندی را برای CryptoLottoToken اصلاح کن + + + + Backup wallet to another location + نسخه پیشتیبان wallet را به محل دیگر انتقال دهید + + + + Change the passphrase used for wallet encryption + عبارت عبور رمز گشایی پنجره تغییر کنید + + + + &Debug window + اشکال زدایی از صفحه + + + + Open debugging and diagnostic console + کنسول اشکال زدایی و تشخیص را باز کنید + + + + &Verify message... + بازبینی پیام + + + + + CryptoLottoToken + یت کویین + + + + Wallet + wallet + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + در مورد CryptoLottoToken + + + + &Show / Hide + &نمایش/ عدم نمایش + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + فایل + + + + &Settings + تنظیمات + + + + &Help + کمک + + + + Tabs toolbar + نوار ابزار زبانه ها + + + + + [testnet] + آزمایش شبکه + + + + CryptoLottoToken client + مشتری CryptoLottoToken + + + + %n active connection(s) to CryptoLottoToken network + در صد ارتباطات فعال بیتکویین با شبکه %n + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + + + + + Warning + + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + تا تاریخ + + + + Catching up... + ابتلا به بالا + + + + Confirm transaction fee + هزینه تراکنش را تایید کنید + + + + Sent transaction + معامله ارسال شده + + + + Incoming transaction + معامله در یافت شده + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + تاریخ %1 +مبلغ%2 +نوع %3 +آدرس %4 + + + + + URI handling + مدیریت URI + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + URI قابل تحلیل نیست. این خطا ممکن است به دلیل ادرس CryptoLottoToken اشتباه یا پارامترهای اشتباه URI رخ داده باشد + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + زمایش شبکهه + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + زمایش شبکه + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + خطا روی داده است. CryptoLottoToken نمی تواند بدون مشکل ادامه دهد و باید بسته شود + + + + ClientModel + + + Network Alert + پیام شبکه + + + + EditAddressDialog + + + Edit Address + اصلاح آدرس + + + + &Label + بر چسب + + + + The label associated with this address book entry + بر چسب با دفتر آدرس ورود مرتبط است + + + + &Address + آدرس + + + + The address associated with this address book entry. This can only be modified for sending addresses. + آدرس با دفتر آدرس ورودی مرتبط است. این فقط در مورد آدرسهای ارسال شده است + + + + New receiving address + آدرس در یافت نو + + + + New sending address + آدرس ارسال نو + + + + Edit receiving address + اصلاح آدرس در یافت + + + + Edit sending address + اصلاح آدرس ارسال + + + + The entered address "%1" is already in the address book. + %1آدرس وارد شده دیگر در دفتر آدرس است + + + + The entered address "%1" is not a valid CryptoLottoToken address. + آدرس وارد شده %1 یک ادرس صحیح CryptoLottoToken نیست + + + + Could not unlock wallet. + رمز گشایی پنجره امکان پذیر نیست + + + + New key generation failed. + کلید نسل جدید ناموفق است + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + نسخه + + + + Usage: + ستفاده : + + + + command-line options + انتخابها برای خطوط دستور command line + + + + UI options + انتخابهای UI + + + + Set language, for example "de_DE" (default: system locale) + زبان را تنظیم کنید برای مثال "de_DE" (پیش فرض: system locale) + + + + Start minimized + شروع حد اقل + + + + Show splash screen on startup (default: 1) + نمایش صفحه splash در STARTUP (پیش فرض:1) + + + + OptionsDialog + + + Options + اصلی + + + + &Main + اصلی + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + دستمزد&پر داخت معامله + + + + Automatically start CryptoLottoToken after logging in to the system. + در زمان ورود به سیستم به صورت خودکار CryptoLottoToken را اجرا کن + + + + &Start CryptoLottoToken on system login + اجرای CryptoLottoToken در زمان ورود به سیستم + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + شبکه + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + اتوماتیک باز کردن بندر بیتکویین در روتر . این فقط در مواردی می باشد که روتر با کمک یو پ ن پ کار می کند + + + + Map port using &UPnP + درگاه با استفاده از + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + اتصال به شبکه CryptoLottoToken از طریق پراکسی ساکس (برای مثال وقتی از طریق نرم افزار TOR متصل می شوید) + + + + &Connect through SOCKS proxy: + اتصال با پراکسی SOCKS + + + + Proxy &IP: + پراکسی و آی.پی. + + + + IP address of the proxy (e.g. 127.0.0.1) + درس پروکسی + + + + &Port: + درگاه + + + + Port of the proxy (e.g. 9050) + درگاه پراکسی (مثال 9050) + + + + SOCKS &Version: + SOCKS و نسخه + + + + SOCKS version of the proxy (e.g. 5) + نسخه SOCKS از پراکسی (مثال 5) + + + + &Window + صفحه + + + + Show only a tray icon after minimizing the window. + tray icon را تنها بعد از کوچک کردن صفحه نمایش بده + + + + &Minimize to the tray instead of the taskbar + حد اقل رساندن در جای نوار ابزار ها + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + حد اقل رساندن در جای خروج بر نامه وقتیکه پنجره بسته است.وقتیکه این فعال است برنامه خاموش می شود بعد از انتخاب دستور خاموش در منیو + + + + M&inimize on close + کوچک کردن صفحه در زمان بستن + + + + &Display + نمایش + + + + User Interface &language: + میانجی کاربر و زبان + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + زبان میانجی کاربر می تواند در اینجا تنظیم شود. این تنظیمات بعد از شروع دوباره RESTART در CryptoLottoToken اجرایی خواهند بود. + + + + &Unit to show amounts in: + واحد برای نمایش میزان وجوه در: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + بخش فرعی پیش فرض را برای نمایش میانجی و زمان ارسال سکه ها مشخص و انتخاب نمایید + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + تا آدرسهای bITCOIN در فهرست تراکنش نمایش داده شوند یا نشوند. + + + + &Display addresses in transaction list + نمایش آدرسها در فهرست تراکنش + + + + &OK + تایید + + + + &Cancel + رد + + + + &Apply + انجام + + + + default + پیش فرض + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + هشدار + + + + + This setting will take effect after restarting CryptoLottoToken. + این تنظیمات پس از اجرای دوباره CryptoLottoToken اعمال می شوند + + + + The supplied proxy address is invalid. + آدرس پراکسی داده شده صحیح نیست + + + + OverviewPage + + + Form + تراز + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + اطلاعات نمایش داده شده روزآمد نیستند.wallet شما به صورت خودکار با شبکه CryptoLottoToken بعد از برقراری اتصال روزآمد می شود اما این فرایند هنوز کامل نشده است. + + + + Balance: + راز: + + + + Unconfirmed: + تایید نشده + + + + Wallet + wallet + + + + Immature: + نابالغ + + + + Mined balance that has not yet matured + بالانس/تتمه حساب استخراج شده، نابالغ است /تکمیل نشده است + + + + <b>Recent transactions</b> + اخرین معاملات&lt + + + + Your current balance + تزار جاری شما + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + تعداد معاملات که تایید شده ولی هنوز در تزار جاری شما بر شمار نرفته است + + + + + out of sync + روزآمد نشده + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + دیالوگ QR CODE + + + + Request Payment + درخواست پرداخت + + + + Amount: + مقدار: + + + + Label: + برچسب: + + + + Message: + پیام + + + + &Save As... + &ذخیره به عنوان... + + + + Error encoding URI into QR Code. + خطا در زمان رمزدار کردن URI در کد QR + + + + The entered amount is invalid, please check. + میزان وجه وارد شده صحیح نیست، لطفا بررسی نمایید + + + + Resulting URI too long, try to reduce the text for label / message. + URI ذکر شده بسیار طولانی است، متن برچسب/پیام را کوتاه کنید + + + + Save QR Code + ذخیره کد QR + + + + PNG Images (*.png) + تصاویر با فرمت PNG (*.png) + + + + RPCConsole + + + Client name + نام مشتری + + + + + + + + + + + + + N/A + - + + + + Client version + نسخه مشتری + + + + &Information + اطلاعات + + + + Using OpenSSL version + استفاده از نسخه OPENSSL + + + + Startup time + زمان آغاز STARTUP + + + + Network + شبکه + + + + Number of connections + تعداد اتصالات + + + + On testnet + در testnetکها + + + + Block chain + زنجیره بلاک + + + + Current number of blocks + تعداد کنونی بلاکها + + + + Estimated total blocks + تعداد تخمینی بلاکها + + + + Last block time + زمان آخرین بلاک + + + + &Open + باز کردن + + + + Command-line options + گزینه های command-line + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + پیام راهنمای CryptoLottoToken-Qt را برای گرفتن فهرست گزینه های command-line نشان بده + + + + &Show + نمایش + + + + &Console + کنسول + + + + Build date + ساخت تاریخ + + + + CryptoLottoToken - Debug window + صفحه اشکال زدایی CryptoLottoToken + + + + CryptoLottoToken Core + هسته CryptoLottoToken + + + + Debug log file + فایلِ لاگِ اشکال زدایی + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + فایلِ لاگِ اشکال زدایی CryptoLottoToken را از دایرکتوری جاری داده ها باز کنید. این عملیات ممکن است برای فایلهای لاگِ حجیم طولانی شود. + + + + Clear console + پاکسازی کنسول + + + + Welcome to the CryptoLottoToken RPC console. + به کنسول CryptoLottoToken RPC خوش آمدید + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + دکمه های بالا و پایین برای مرور تاریخچه و Ctrl-L برای پاکسازی صفحه + + + + Type <b>help</b> for an overview of available commands. + با تایپ عبارت HELP دستورهای در دسترس را مرور خواهید کرد + + + + SendCoinsDialog + + + + + + + + + + Send Coins + ارسال سکه ها + + + + Send to multiple recipients at once + ارسال چندین در یافت ها فورا + + + + Add &Recipient + اضافه کردن دریافت کننده + + + + Remove all transaction fields + پاک کردن تمام ستون‌های تراکنش + + + + Clear &All + پاکسازی همه + + + + Balance: + تزار : + + + + 123.456 BTC + 123.456 بتس + + + + Confirm the send action + عملیت دوم تایید کنید + + + + S&end + &;ارسال + + + + <b>%1</b> to %2 (%3) + (%3) تا <b>%1</b> درصد%2 + + + + Confirm send coins + ارسال سکه ها تایید کنید + + + + Are you sure you want to send %1? + %1شما متماینید که می خواهید 1% ارسال کنید ؟ + + + + and + و + + + + The recipient address is not valid, please recheck. + آدرس گیرنده نادرست است، لطفا دوباره بررسی کنید. + + + + The amount to pay must be larger than 0. + مبلغ پر داخت باید از 0 بیشتر باشد + + + + The amount exceeds your balance. + میزان وجه از بالانس/تتمه حساب شما بیشتر است + + + + The total exceeds your balance when the %1 transaction fee is included. + کل میزان وجه از بالانس/تتمه حساب شما بیشتر می شود وقتی %1 هزینه تراکنش نیز به ین میزان افزوده می شود + + + + Duplicate address found, can only send to each address once per send operation. + آدرس تکراری یافت شده است، در زمان انجام عملیات به هر آدرس تنها یکبار می توانید اطلاعات ارسال کنید + + + + Error: Transaction creation failed! + + + + + 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. + خطا: تراکنش تایید نشد. این پیام زمانی روی می دهد که مقداری از سکه های WALLET شما استفاده شده اند برای مثال اگر شما از WALLET.DAT استفاده کرده اید، ممکن است سکه ها استفاده شده باشند اما در اینجا نمایش داده نشوند + + + + SendCoinsEntry + + + Form + تراز + + + + A&mount: + A&مبلغ : + + + + Pay &To: + به&پر داخت : + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + برای آدرس بر پسب وارد کنید که در دفتر آدرس اضافه شود + + + + &Label: + &بر چسب + + + + Choose address from address book + اآدرسن ازدفتر آدرس انتخاب کنید + + + + Alt+A + Alt+A + + + + Paste address from clipboard + آدرس از تخته رسم گیره دار پست کنید + + + + Alt+P + Alt+P + + + + Remove this recipient + بر داشتن این در یافت کننده + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + آدرس بیتکویین وارد کنید (bijvoorbeeld: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + امضا - امضا کردن /شناسایی یک پیام + + + + &Sign Message + &امضای پیام + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + شما می توانید پیامها را با آدرس خودتان امضا نمایید تا ثابت شود متعلق به شما هستند. مواظب باشید تا چیزی که بدان مطمئن نیستنید را امضا نکنید زیرا حملات فیشینگ در زمان ورود شما به سیستم فریبنده هستند. تنها مواردی را که حاوی اطلاعات دقیق و قابل قبول برای شما هستند را امضا کنید + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + آدرس برای امضا کردن پیام با (برای مثال Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + یک آدرس را از فهرست آدرسها انتخاب کنید + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + آدرس از تخته رسم گیره دار پست کنید + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + پیامی را که می‌خواهید امضا کنید در اینجا وارد کنید + + + + Signature + + + + + Copy the current signature to the system clipboard + این امضا را در system clipboard کپی کن + + + + Sign the message to prove you own this CryptoLottoToken address + پیام را برای اثبات آدرس CryptoLottoToken خود امضا کنید + + + + Sign &Message + + + + + Reset all sign message fields + تنظیم دوباره تمامی فیلدهای پیام + + + + + Clear &All + پاکسازی همه + + + + &Verify Message + تایید پیام + + + + Enter the signing 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. + آدرس/پیام خود را وارد کنید (مطمئن شوید که فاصله بین خطوط، فاصله ها، تب ها و ... را دقیقا کپی می کنید) و سپس امضا کنید تا پیام تایید شود. مراقب باشید که پیام را بیشتر از مطالب درون امضا مطالعه نمایید تا فریب شخص سوم/دزدان اینترنتی را نخورید. + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + آدرس برای امضا کردن پیام با (برای مثال Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + پیام را برای اطمنان از ورود به سیستم با آدرس CryptoLottoToken مشخص خود،تایید کنید + + + + Verify &Message + + + + + Reset all verify message fields + تنظیم دوباره تمامی فیلدهای پیام تایید شده + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + آدرس بیتکویین وارد کنید (bijvoorbeeld: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + با کلیک بر "امضای پیام" شما یک امضای جدید درست می کنید + + + + Enter CryptoLottoToken signature + امضای BITOCOIN خود را وارد کنید + + + + + The entered address is invalid. + آدرس وارد شده صحیح نیست + + + + + + + Please check the address and try again. + اطفا آدرس را بررسی کرده و دوباره امتحان کنید + + + + + The entered address does not refer to a key. + آدرس وارد شده با کلید وارد شده مرتبط نیست + + + + Wallet unlock was cancelled. + قفل کردن wallet انجام نشد + + + + Private key for the entered address is not available. + کلید شخصی برای آدرس وارد شده در دسترس نیست + + + + Message signing failed. + پیام امضا کردن انجام نشد + + + + Message signed. + پیام امضا شد + + + + The signature could not be decoded. + امضا نمی تواند رمزگشایی شود + + + + + Please check the signature and try again. + لطفا امضا را بررسی و دوباره تلاش نمایید + + + + The signature did not match the message digest. + امضا با تحلیلِ پیام مطابقت ندارد + + + + Message verification failed. + عملیات شناسایی پیام انجام نشد + + + + Message verified. + پیام شناسایی شد + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + آزمایش شبکه + + + + TransactionDesc + + + Open until %1 + باز کردن تا%1 + + + + %1/offline + %1 آفلاین + + + + %1/unconfirmed + %1 تایید نشده + + + + %1 confirmations + ایید %1 + + + + Status + وضعیت + + + + , broadcast through %n node(s) + انتشار از طریق n% گره +انتشار از طریق %n گره + + + + Date + تاریخ + + + + Source + منبع + + + + Generated + تولید شده + + + + + From + فرستنده + + + + + + To + گیرنده + + + + + own address + آدرس شما + + + + label + برچسب + + + + + + + + Credit + بدهی + + + + matures in %n more block(s) + بلوغ در n% از بیشتر بلاکها +بلوغ در %n از بیشتر بلاکها + + + + not accepted + غیرقابل قبول + + + + + + + Debit + اعتبار + + + + Transaction fee + هزینه تراکنش + + + + Net amount + هزینه خالص + + + + Message + پیام + + + + Comment + نظر + + + + Transaction ID + شناسه کاربری برای تراکنش + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + سکه های ایجاد شده باید 120 بلاک را قبل از استفاده بالغ کنند. در هنگام ایجاد بلاک، آن بلاک در شبکه منتشر می شود تا به زنجیره بلاکها بپیوندد. اگر در زنجیره قرار نگیرد، پیام وضعیت به غیرقابل قبول تغییر می بپیابد و قابل استفاده نیست. این مورد معمولا زمانی پیش می آید که گره دیگری به طور همزمان بلاکی را با فاصل چند ثانیه ای از شما ایجاد کند. + + + + Debug information + اشکال زدایی طلاعات + + + + Transaction + تراکنش + + + + Inputs + درونداد + + + + Amount + مبلغ + + + + true + صحیح + + + + false + نادرست + + + + , has not been successfully broadcast yet + هنوز با مو فقیت ارسال نشده + + + + Open for %n more block(s) + + + + + unknown + مشخص نیست + + + + TransactionDescDialog + + + Transaction details + جزییات معاملات + + + + This pane shows a detailed description of the transaction + در این قاب شیشه توصیف دقیق معامله نشان می شود + + + + TransactionTableModel + + + Date + تاریخ + + + + Type + نوع + + + + Address + ایل جدا + + + + Amount + مبلغ + + + + Open for %n more block(s) + + + + + Open until %1 + از شده تا 1%1 + + + + Offline (%1 confirmations) + افلایین (%1) + + + + Unconfirmed (%1 of %2 confirmations) + تایید نشده (%1/%2) + + + + Confirmed (%1 confirmations) + تایید شده (%1) + + + + Mined balance will be available when it matures in %n more block(s) + بالانس/تتمه حساب استخراج شده زمانی که %n از بیشتر بلاکها بالغ شدند در دسترس خواهد بود +بالانس/تتمه حساب استخراج شده زمانی که n% از بیشتر بلاکها بالغ شدند در دسترس خواهد بود + + + + This block was not received by any other nodes and will probably not be accepted! + این بلوک از دیگر گره ها در یافت نشده بدین دلیل شاید قابل قابول نیست + + + + Generated but not accepted + تولید شده ولی قبول نشده + + + + Received with + در یافت با : + + + + Received from + دریافتی از + + + + Sent to + ارسال به : + + + + Payment to yourself + پر داخت به خودتان + + + + Mined + استخراج + + + + (n/a) + (کاربرد ندارد) + + + + Transaction status. Hover over this field to show number of confirmations. + وضعیت معالمه . عرصه که تعداد تایید نشان می دهد + + + + Date and time that the transaction was received. + تاریخ و ساعت در یافت معامله + + + + Type of transaction. + نوع معاملات + + + + Destination address of transaction. + آدرس مقصود معاملات + + + + Amount removed from or added to balance. + مبلغ از تزار شما خارج یا وارد شده + + + + TransactionView + + + + All + همه + + + + Today + امروز + + + + This week + این هفته + + + + This month + این ماه + + + + Last month + ماه گذشته + + + + This year + امسال + + + + Range... + محدوده + + + + Received with + در یافت با + + + + Sent to + ارسال به + + + + To yourself + به خودتان + + + + Mined + استخراج + + + + Other + یگر + + + + Enter address or label to search + برای جست‌‌وجو نشانی یا برچسب را وارد کنید + + + + Min amount + حد اقل مبلغ + + + + Copy address + کپی آدرس + + + + Copy label + کپی بر چسب + + + + Copy amount + روگرفت مقدار + + + + Copy transaction ID + + + + + Edit label + اصلاح بر چسب + + + + Show transaction details + جزئیات تراکنش را نمایش بده + + + + Export Transaction Data + صادرات تاریخ معامله + + + + Comma separated file (*.csv) + Comma فایل جدا + + + + Confirmed + تایید شده + + + + Date + تاریخ + + + + Type + نوع + + + + Label + ر چسب + + + + Address + ایل جدا + + + + Amount + مبلغ + + + + ID + آی دی + + + + Error exporting + خطای صادرت + + + + Could not write to file %1. + تا فایل %1 نمی شود نوشت + + + + Range: + >محدوده + + + + to + به + + + + WalletModel + + + Send Coins + ارسال سکه ها + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + داده ها نوارِ جاری را به فایل انتقال دهید + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + سخه بیتکویین + + + + Usage: + ستفاده : + + + + Send command to -server or CryptoLottoTokend + ارسال فرمان به سرور یا باتکویین + + + + List commands + لیست فومان ها + + + + Get help for a command + کمک برای فرمان + + + + Options: + تنظیمات + + + + Specify configuration file (default: CryptoLottoToken.conf) + (: CryptoLottoToken.confپیش فرض: )فایل تنظیمی خاص + + + + Specify pid file (default: CryptoLottoTokend.pid) + (CryptoLottoTokend.pidپیش فرض : ) فایل پید خاص + + + + Specify data directory + دایرکتور اطلاعاتی خاص + + + + Set database cache size in megabytes (default: 25) + سایز کَش بانک داده را بر حسب مگابایت تنظیم کنید (پیش فرض:25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + برای اتصالات به <port> (پیش‌فرض: 22556 یا تست‌نت: 44556) گوش کنید + + + + Maintain at most <n> connections to peers (default: 125) + حداکثر <n> اتصال با همکاران برقرار داشته باشید (پیش‌فرض: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + اتصال به گره برای دریافت آدرسهای قرینه و قطع اتصال + + + + Specify your own public address + آدرس عمومی خود را ذکر کنید + + + + Threshold for disconnecting misbehaving peers (default: 100) + آستانه برای قطع ارتباط با همکاران بدرفتار (پیش‌فرض: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + مدت زمان به ثانیه برای جلوگیری از همکاران بدرفتار برای اتصال دوباره (پیش‌فرض: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + در زمان تنظیم درگاه RPX %u در فهرست کردن %s اشکالی رخ داده است + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + ( 22555پیش فرض :) &lt;poort&gt; JSON-RPC شنوایی برای ارتباطات + + + + Accept command line and JSON-RPC commands + JSON-RPC قابل فرمانها و + + + + Run in the background as a daemon and accept commands + اجرای در پس زمینه به عنوان شبح و قبول فرمان ها + + + + Use the test network + استفاده شبکه آزمایش + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + پذیرش اتصالات از بیرون (پیش فرض:1 بدون پراکسی یا اتصال) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + حجم حداکثر تراکنشهای با/کم اهمیت را به بایت تنظیم کنید (پیش فرض:27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + هشدار:paytxfee بسیار بالا تعریف شده است! این هزینه تراکنش است که باید در زمان ارسال تراکنش بپردازید + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + هشدار: تراکنش نمایش داده شده ممکن است صحیح نباشد! شما/یا یکی از گره ها به روزآمد سازی نیاز دارید + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + هشدار: لطفا زمان و تاریخ رایانه خود را تصحیح نمایید! اگر ساعت رایانه شما اشتباه باشد CryptoLottoToken ممکن است صحیح کار نکند + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + بستن گزینه ایجاد + + + + Connect only to the specified node(s) + تنها در گره (های) مشخص شده متصل شوید + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + آدرس آی.پی. خود را شناسایی کنید (پیش فرض:1 در زمان when listening وno -externalip) + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + شنیدن هر گونه درگاه انجام پذیر نیست. ازlisten=0 برای اینکار استفاده کیند. + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + قرینه ها را برای جستجوی DNS بیاب (پیش فرض: 1 مگر در زمان اتصال) + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + آدرس نرم افزار تور غلط است %s + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + حداکثر بافر دریافت شده بر اساس اتصال <n>* 1000 بایت (پیش فرض:5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + حداکثر بافر دریافت شده بر اساس اتصال <n>* 1000 بایت (پیش فرض:1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + تنها =به گره ها در شبکه متصا شوید <net> (IPv4, IPv6 or Tor) + + + + Output extra debugging information. Implies all other -debug* options + برونداد اطلاعات اشکال زدایی اضافی. گزینه های اشکال زدایی دیگر رفع شدند + + + + Output extra network debugging information + برونداد اطلاعات اشکال زدایی اضافی برای شبکه + + + + Prepend debug output with timestamp (default: 1) + به خروجی اشکال‌زدایی برچسب زمان بزنید + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + گزینه ssl (به ویکیCryptoLottoToken برای راهنمای راه اندازی ssl مراجعه شود) + + + + Select the version of socks proxy to use (4-5, default: 5) + نسخه ای از پراکسی ساکس را برای استفاده انتخاب کنید (4-5 پیش فرض:5) + + + + Send trace/debug info to console instead of debug.log file + اطلاعات ردگیری/اشکال‌زدایی را به جای فایل لاگ اشکال‌زدایی به کنسول بفرستید + + + + Send trace/debug info to debugger + اطلاعات ردگیری/اشکال‌زدایی را به اشکال‌زدا بفرستید + + + + Set maximum block size in bytes (default: 250000) + حداکثر سایز بلاک بر اساس بایت تنظیم شود (پیش فرض: 250000) + + + + Set minimum block size in bytes (default: 0) + حداقل سایز بلاک بر اساس بایت تنظیم شود (پیش فرض: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + فایل debug.log را در startup مشتری کوچک کن (پیش فرض:1 اگر اشکال زدایی روی نداد) + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + (میلی ثانیه )فاصله ارتباط خاص + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + از UPnP برای شناسایی درگاه شنیداری استفاده کنید (پیش فرض:0) + + + + Use UPnP to map the listening port (default: 1 when listening) + از UPnP برای شناسایی درگاه شنیداری استفاده کنید (پیش فرض:1 در زمان شنیدن) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + برای دستیابی به سرویس مخفیانه نرم افزار تور از پراکسی استفاده کنید (پیش فرض:same as -proxy) + + + + Username for JSON-RPC connections + JSON-RPC شناسه برای ارتباطات + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + هشدار: این نسخه قدیمی است، روزآمدسازی مورد نیاز است + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + JSON-RPC عبارت عبور برای ارتباطات + + + + Allow JSON-RPC connections from specified IP address + از آدرس آی پی خاص JSON-RPC قبول ارتباطات + + + + Send commands to node running on <ip> (default: 127.0.0.1) + (127.0.0.1پیش فرض: ) &lt;ip&gt; دادن فرمانها برای استفاده گره ها روی + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + زمانی که بهترین بلاک تغییر کرد، دستور را اجرا کن (%s در cmd با block hash جایگزین شده است) + + + + Upgrade wallet to latest format + wallet را به جدیدترین فرمت روزآمد کنید + + + + Set key pool size to <n> (default: 100) + (100پیش فرض:)&lt;n&gt; گذاشتن اندازه کلید روی + + + + Rescan the block chain for missing wallet transactions + اسکان مجدد زنجیر بلوکها برای گم والت معامله + + + + Use OpenSSL (https) for JSON-RPC connections + JSON-RPCبرای ارتباطات استفاده کنید OpenSSL (https) + + + + Server certificate file (default: server.cert) + (server.certپیش فرض: )گواهی نامه سرور + + + + Server private key (default: server.pem) + (server.pemپیش فرض: ) کلید خصوصی سرور + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + رمز های قابل قبول( TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + پیام کمکی + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + امکان اتصال به %s از این رایانه وجود ندارد ( bind returned error %d, %s) + + + + Connect through socks proxy + اتصال از طریق پراکسی ساکس + + + + Allow DNS lookups for -addnode, -seednode and -connect + به DNS اجازه بده تا برای addnode ، seednode و اتصال جستجو کند + + + + Loading addresses... + بار گیری آدرس ها + + + + Error loading wallet.dat: Wallet corrupted + خطا در بارگیری wallet.dat: کیف پول خراب شده است + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + خطا در بارگیری wallet.dat: کیف پول به ویرایش جدیدتری از Biticon نیاز دارد + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + سلام + + + + Error loading wallet.dat + خطا در بارگیری wallet.dat + + + + Invalid -proxy address: '%s' + آدرس پراکسی اشتباه %s + + + + Unknown network specified in -onlynet: '%s' + شبکه مشخص شده غیرقابل شناسایی در onlynet: '%s' + + + + Unknown -socks proxy version requested: %i + نسخه پراکسی ساکس غیرقابل شناسایی درخواست شده است: %i + + + + Cannot resolve -bind address: '%s' + آدرس قابل اتصال- شناسایی نیست %s + + + + Cannot resolve -externalip address: '%s' + آدرس خارجی قابل اتصال- شناسایی نیست %s + + + + Invalid amount for -paytxfee=<amount>: '%s' + میزان وجه اشتباه برای paytxfee=<میزان وجه>: %s + + + + Invalid amount + میزان وجه اشتباه + + + + Insufficient funds + بود جه نا کافی + + + + Loading block index... + بار گیری شاخص بلوک + + + + Add a node to connect to and attempt to keep the connection open + به اتصال یک گره اضافه کنید و اتصال را باز نگاه دارید + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + اتصال به %s از این رایانه امکان پذیر نیست. CryptoLottoToken احتمالا در حال اجراست. + + + + Fee per KB to add to transactions you send + پر داجت برای هر کیلو بیت برای اضافه به معامله ارسال + + + + Loading wallet... + بار گیری والت + + + + Cannot downgrade wallet + امکان تنزل نسخه در wallet وجود ندارد + + + + Cannot write default address + آدرس پیش فرض قابل ذخیره نیست + + + + Rescanning... + اسکان مجدد + + + + Done loading + بار گیری انجام شده است + + + + To use the %s option + برای استفاده از %s از انتخابات + + + + Error + خطا + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + %s، شما باید یک rpcpassword را در فایل پیکربندی تنظیم کنید :⏎%s⏎ اگر فایل ایجاد نشد، یک فایل فقط متنی ایجاد کنید. + + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_fa_IR.qm b/src/qt/locale/bitcoin_fa_IR.qm new file mode 100644 index 0000000..cc2f766 Binary files /dev/null and b/src/qt/locale/bitcoin_fa_IR.qm differ diff --git a/src/qt/locale/bitcoin_fa_IR.ts b/src/qt/locale/bitcoin_fa_IR.ts new file mode 100644 index 0000000..eeedf98 --- /dev/null +++ b/src/qt/locale/bitcoin_fa_IR.ts @@ -0,0 +1,2921 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + در مورد بیتکویین + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> version + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + دفترچه آدرس + + + + Double-click to edit address or label + برای ویرایش آدرس/برچسب دوبار کلیک نمایید + + + + Create a new address + یک آدرس جدید بسازید + + + + Copy the currently selected address to the system clipboard + آدرس انتخاب شده را در کلیپ بوردِ سیستم کپی کنید + + + + &New Address + و آدرس جدید + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + + + + + &Copy Address + و کپی آدرس + + + + Show &QR Code + نشان و کد QR + + + + Sign a message to prove you own a CryptoLottoToken address + + + + + Sign &Message + + + + + Delete the currently selected address from the list + + + + + Export the data in the current tab to a file + صدور داده نوار جاری به یک فایل + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + + + + + &Verify Message + + + + + &Delete + و حذف + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + کپی و برچسب + + + + &Edit + و ویرایش + + + + Send &Coins + + + + + Export Address Book Data + انتقال اطلاعات دفترچه آدرس + + + + Comma separated file (*.csv) + سی.اس.وی. (فایل جداگانه دستوری) + + + + Error exporting + صدور پیام خطا + + + + Could not write to file %1. + قابل کپی در فایل نیست %1 + + + + AddressTableModel + + + Label + برچسب + + + + Address + آدرس + + + + (no label) + (برچسب ندارد) + + + + AskPassphraseDialog + + + Passphrase Dialog + + + + + Enter passphrase + رمز/پَس فرِیز را وارد کنید + + + + New passphrase + رمز/پَس فرِیز جدید را وارد کنید + + + + Repeat new passphrase + رمز/پَس فرِیز را دوباره وارد کنید + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + رمز/پَس فرِیز جدید را در wallet وارد کنید. برای انتخاب رمز/پَس فرِیز از 10 کاراکتر تصادفی یا بیشتر و یا هشت کلمه یا بیشتر استفاده کنید. + + + + Encrypt wallet + wallet را رمزگذاری کنید + + + + This operation needs your wallet passphrase to unlock the wallet. + برای انجام این عملکرد به رمز/پَس فرِیزِwallet نیاز است تا آن را از حالت قفل درآورد. + + + + Unlock wallet + باز کردن قفل wallet + + + + This operation needs your wallet passphrase to decrypt the wallet. + برای کشف رمز wallet، به رمز/پَس فرِیزِwallet نیاز است. + + + + Decrypt wallet + کشف رمز wallet + + + + Change passphrase + تغییر رمز/پَس فرِیز + + + + Enter the old and new passphrase to the wallet. + رمز/پَس فرِیزِ قدیم و جدید را در wallet وارد کنید + + + + Confirm wallet encryption + رمزگذاری wallet را تایید کنید + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + + + + + Are you sure you wish to encrypt your wallet? + + + + + 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. + + + + + + Warning: The Caps Lock key is on! + + + + + + Wallet encrypted + تایید رمزگذاری + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken برای اتمام فرایند رمزگذاری بسته خواهد شد. به خاطر داشته باشید که رمزگذاری WALLET شما، کامپیوتر شما را از آلودگی به بدافزارها مصون نمی دارد. + + + + + + + Wallet encryption failed + رمزگذاری تایید نشد + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + رمزگذاری به علت خطای داخلی تایید نشد. wallet شما رمزگذاری نشد + + + + + The supplied passphrases do not match. + رمزهای/پَس فرِیزهایِ وارد شده با هم تطابق ندارند + + + + Wallet unlock failed + قفل wallet باز نشد + + + + + + The passphrase entered for the wallet decryption was incorrect. + رمزهای/پَس فرِیزهایِ وارد شده wallet برای کشف رمز اشتباه است. + + + + Wallet decryption failed + کشف رمز wallet انجام نشد + + + + Wallet passphrase was successfully changed. + + + + + BitcoinGUI + + + Sign &message... + امضا و پیام + + + + Synchronizing with network... + به روز رسانی با شبکه... + + + + &Overview + و بازبینی + + + + Show general overview of wallet + نمای کلی از wallet را نشان بده + + + + &Transactions + و تراکنش + + + + Browse transaction history + تاریخچه تراکنش را باز کن + + + + Edit the list of stored addresses and labels for sending + فهرست آدرسها و برچسبهای ذخیره شده را ویرایش کن + + + + Show the list of addresses for receiving payments + فهرست آدرسها را برای دریافت وجه نشان بده + + + + E&xit + خروج + + + + Quit application + از "درخواست نامه"/ application خارج شو + + + + Show information about CryptoLottoToken + اطلاعات در مورد CryptoLottoToken را نشان بده + + + + About &Qt + درباره و QT + + + + Show information about Qt + نمایش اطلاعات درباره QT + + + + &Options... + و انتخابها + + + + &Encrypt Wallet... + و رمزگذاری wallet + + + + &Backup Wallet... + و گرفتن نسخه پیشتیبان از wallet + + + + &Change Passphrase... + تغییر رمز/پَس فرِیز + + + + Importing blocks from disk... + + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + + + + + Modify configuration options for CryptoLottoToken + اصلاح انتخابها برای پیکربندی CryptoLottoToken + + + + Backup wallet to another location + گرفتن نسخه پیشتیبان در آدرسی دیگر + + + + Change the passphrase used for wallet encryption + رمز مربوط به رمزگذاریِ wallet را تغییر دهید + + + + &Debug window + + + + + Open debugging and diagnostic console + + + + + &Verify message... + + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + کیف پول + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + &در مورد بیتکویین + + + + &Show / Hide + &نمایش/ عدم نمایش و + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + و فایل + + + + &Settings + و تنظیمات + + + + &Help + و راهنما + + + + Tabs toolbar + نوار ابزار + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + مشتری CryptoLottoToken + + + + %n active connection(s) to CryptoLottoToken network + %n ارتباط فعال به شبکه CryptoLottoToken +%n ارتباط فعال به شبکه CryptoLottoToken + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + + + + + Warning + + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + روزآمد + + + + Catching up... + در حال روزآمد سازی.. + + + + Confirm transaction fee + + + + + Sent transaction + ارسال تراکنش + + + + Incoming transaction + تراکنش دریافتی + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + تاریخ: %1⏎ میزان وجه : %2⏎ نوع: %3⏎ آدرس: %4⏎ + + + + + + URI handling + + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + wallet رمزگذاری شد و در حال حاضر از حالت قفل در آمده است + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + wallet رمزگذاری شد و در حال حاضر قفل است + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + هشدار شبکه + + + + EditAddressDialog + + + Edit Address + ویرایش آدرسها + + + + &Label + و برچسب + + + + The label associated with this address book entry + برچسب مربوط به این دفترچه آدرس + + + + &Address + و آدرس + + + + The address associated with this address book entry. This can only be modified for sending addresses. + برچسب مربوط به این دفترچه آدرس و تنها ب + + + + New receiving address + آدرسِ دریافت کننده جدید + + + + New sending address + آدرس ارسال کننده جدید + + + + Edit receiving address + ویرایش آدرسِ دریافت کننده + + + + Edit sending address + ویرایش آدرسِ ارسال کننده + + + + The entered address "%1" is already in the address book. + آدرس وارد شده %1 قبلا به فهرست آدرسها اضافه شده بوده است. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + آدرس وارد شده "%1" یک آدرس صحیح برای CryptoLottoToken نسشت + + + + Could not unlock wallet. + عدم توانیی برای قفل گشایی wallet + + + + New key generation failed. + عدم توانیی در ایجاد کلید جدید + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + + + + + version + نسخه + + + + Usage: + میزان استفاده: + + + + command-line options + + + + + UI options + + + + + Set language, for example "de_DE" (default: system locale) + + + + + Start minimized + + + + + Show splash screen on startup (default: 1) + + + + + OptionsDialog + + + Options + انتخاب/آپشن + + + + &Main + + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + + + + + Automatically start CryptoLottoToken after logging in to the system. + + + + + &Start CryptoLottoToken on system login + + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + + + + + Map port using &UPnP + + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + + + + + &Connect through SOCKS proxy: + + + + + Proxy &IP: + + + + + IP address of the proxy (e.g. 127.0.0.1) + + + + + &Port: + + + + + Port of the proxy (e.g. 9050) + + + + + SOCKS &Version: + + + + + SOCKS version of the proxy (e.g. 5) + + + + + &Window + + + + + Show only a tray icon after minimizing the window. + + + + + &Minimize to the tray instead of the taskbar + + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + + + + + M&inimize on close + + + + + &Display + + + + + User Interface &language: + + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + + + + + &Unit to show amounts in: + + + + + Choose the default subdivision unit to show in the interface and when sending coins. + + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + + + &Display addresses in transaction list + و نمایش آدرسها در فهرست تراکنش + + + + &OK + و تایید + + + + &Cancel + و رد + + + + &Apply + و به کار گرفتن + + + + default + پیش فرض + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + + + + + + This setting will take effect after restarting CryptoLottoToken. + + + + + The supplied proxy address is invalid. + + + + + OverviewPage + + + Form + فرم + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + اطلاعات نمایش داده شده ممکن است روزآمد نباشد. wallet شما به صورت خودکار بعد از برقراری اتصال با شبکه CryptoLottoToken به روز می شود اما این فرایند هنوز تکمیل نشده است. + + + + Balance: + مانده حساب: + + + + Unconfirmed: + تایید نشده + + + + Wallet + کیف پول + + + + Immature: + + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + تراکنشهای اخیر + + + + Your current balance + مانده حساب جاری + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + تعداد تراکنشهایی که نیاز به تایید دارند و هنوز در مانده حساب جاری شما به حساب نیامده اند + + + + + out of sync + خارج از روزآمد سازی + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + + + + + Request Payment + درخواست وجه + + + + Amount: + میزان وجه: + + + + Label: + برچسب: + + + + Message: + پیام: + + + + &Save As... + و ذخیره با عنوانِ... + + + + Error encoding URI into QR Code. + + + + + The entered amount is invalid, please check. + + + + + Resulting URI too long, try to reduce the text for label / message. + متن وارد شده طولانی است، متنِ برچسب/پیام را کوتاه کنید + + + + Save QR Code + + + + + PNG Images (*.png) + تصاویر با فرمت PNG +(*.png) + + + + RPCConsole + + + Client name + + + + + + + + + + + + + + N/A + + + + + Client version + + + + + &Information + + + + + Using OpenSSL version + + + + + Startup time + + + + + Network + + + + + Number of connections + + + + + On testnet + + + + + Block chain + + + + + Current number of blocks + + + + + Estimated total blocks + + + + + Last block time + + + + + &Open + + + + + Command-line options + + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + + &Show + + + + + &Console + + + + + Build date + + + + + CryptoLottoToken - Debug window + + + + + CryptoLottoToken Core + + + + + Debug log file + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + + + + + Welcome to the CryptoLottoToken RPC console. + به کنسول آر.پی.سی. CryptoLottoToken خوش آمدید + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + + + + + Type <b>help</b> for an overview of available commands. + + + + + SendCoinsDialog + + + + + + + + + + Send Coins + سکه های ارسالی + + + + Send to multiple recipients at once + ارسال همزمان به گیرنده های متعدد + + + + Add &Recipient + + + + + Remove all transaction fields + تمامی فیلدهای تراکنش حذف شوند + + + + Clear &All + + + + + Balance: + مانده حساب: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + تایید عملیات ارسال + + + + S&end + و ارسال + + + + <b>%1</b> to %2 (%3) + %1 به %2 (%3) + + + + Confirm send coins + تایید ارسال سکه ها + + + + Are you sure you want to send %1? + شما مطمئن هستید که می خواهید %1 را ارسال کنید؟ + + + + and + و + + + + The recipient address is not valid, please recheck. + + + + + The amount to pay must be larger than 0. + میزان پرداخت باید بیشتر از 0 باشد + + + + The amount exceeds your balance. + + + + + The total exceeds your balance when the %1 transaction fee is included. + + + + + Duplicate address found, can only send to each address once per send operation. + + + + + Error: Transaction creation failed! + + + + + 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. + خطا: تراکنش تایید نشد. این خطا ممکن است به این دلیل اتفاق بیافتد که سکه های wallet شما خرج شده باشند مثلا اگر wallet.dat را مپی کرده باشید و سکه های شما در آن کپی استفاده شده باشند اما در اینجا نمایش داده نشده اند. + + + + SendCoinsEntry + + + Form + فرم + + + + A&mount: + و میزان وجه + + + + Pay &To: + پرداخت و به چه کسی + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + یک برچسب برای این آدرس بنویسید تا به دفترچه آدرسهای شما اضافه شود + + + + &Label: + و برچسب + + + + Choose address from address book + آدرس از فهرست آدرس انتخاب کنید + + + + Alt+A + Alt و A + + + + Paste address from clipboard + آدرس را بر کلیپ بورد کپی کنید + + + + Alt+P + Alt و P + + + + Remove this recipient + این گیرنده را حذف کن + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + یک آدرس CryptoLottoToken وارد کنید (مثال Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + + + + + &Sign Message + و امضای پیام + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + یک آدرس CryptoLottoToken وارد کنید (مثال Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + آدرس از فهرست آدرس انتخاب کنید + + + + + Alt+A + Alt و A + + + + Paste address from clipboard + آدرس را بر کلیپ بورد کپی کنید + + + + Alt+P + Alt و P + + + + Enter the message you want to sign here + + + + + Signature + + + + + Copy the current signature to the system clipboard + + + + + Sign the message to prove you own this CryptoLottoToken address + + + + + Sign &Message + + + + + Reset all sign message fields + + + + + + Clear &All + + + + + &Verify Message + + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + یک آدرس CryptoLottoToken وارد کنید (مثال Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + + + + + Verify &Message + + + + + Reset all verify message fields + + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + یک آدرس CryptoLottoToken وارد کنید (مثال Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + + + + + Enter CryptoLottoToken signature + + + + + + The entered address is invalid. + + + + + + + + Please check the address and try again. + + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + + + + + Private key for the entered address is not available. + + + + + Message signing failed. + + + + + Message signed. + + + + + The signature could not be decoded. + + + + + + Please check the signature and try again. + + + + + The signature did not match the message digest. + + + + + Message verification failed. + + + + + Message verified. + + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + باز کن تا %1 + + + + %1/offline + + + + + %1/unconfirmed + %1 غیرقابل تایید + + + + %1 confirmations + %1 تاییدها + + + + Status + + + + + , broadcast through %n node(s) + + + + + Date + تاریخ + + + + Source + + + + + Generated + + + + + + From + + + + + + + To + + + + + + own address + + + + + label + برچسب + + + + + + + + Credit + + + + + matures in %n more block(s) + + + + + not accepted + + + + + + + + Debit + + + + + Transaction fee + + + + + Net amount + + + + + Message + پیام + + + + Comment + + + + + Transaction ID + + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + + + + + Debug information + + + + + Transaction + + + + + Inputs + + + + + Amount + میزان + + + + true + + + + + false + + + + + , has not been successfully broadcast yet + تا به حال با موفقیت انتشار نیافته است + + + + Open for %n more block(s) + + + + + unknown + ناشناس + + + + TransactionDescDialog + + + Transaction details + جزئیات تراکنش + + + + This pane shows a detailed description of the transaction + این بخش جزئیات تراکنش را نشان می دهد + + + + TransactionTableModel + + + Date + تاریخ + + + + Type + نوع + + + + Address + آدرس + + + + Amount + میزان وجه + + + + Open for %n more block(s) + + + + + Open until %1 + باز کن تا %1 + + + + Offline (%1 confirmations) + برون خطی (%1 تاییدها) + + + + Unconfirmed (%1 of %2 confirmations) + تایید نشده (%1 از %2 تاییدها) + + + + Confirmed (%1 confirmations) + تایید شده (%1 تاییدها) + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + این block توسط گره های دیگری دریافت نشده است و ممکن است قبول نشود + + + + Generated but not accepted + تولید شده اما قبول نشده است + + + + Received with + قبول با + + + + Received from + دریافت شده از + + + + Sent to + ارسال به + + + + Payment to yourself + وجه برای شما + + + + Mined + استخراج شده + + + + (n/a) + خالی + + + + Transaction status. Hover over this field to show number of confirmations. + وضعیت تراکنش. با اشاره به این بخش تعداد تاییدها نمایش داده می شود + + + + Date and time that the transaction was received. + زمان و تاریخی که تراکنش دریافت شده است + + + + Type of transaction. + نوع تراکنش + + + + Destination address of transaction. + آدرس مقصد در تراکنش + + + + Amount removed from or added to balance. + میزان وجه کم شده یا اضافه شده به حساب + + + + TransactionView + + + + All + همه + + + + Today + امروز + + + + This week + این هفته + + + + This month + این ماه + + + + Last month + ماه گذشته + + + + This year + این سال + + + + Range... + حدود.. + + + + Received with + دریافت با + + + + Sent to + ارسال به + + + + To yourself + به شما + + + + Mined + استخراج شده + + + + Other + دیگر + + + + Enter address or label to search + آدرس یا برچسب را برای جستجو وارد کنید + + + + Min amount + حداقل میزان وجه + + + + Copy address + آدرس را کپی کنید + + + + Copy label + برچسب را کپی کنید + + + + Copy amount + میزان وجه کپی شود + + + + Copy transaction ID + + + + + Edit label + برچسب را ویرایش کنید + + + + Show transaction details + + + + + Export Transaction Data + داده های تراکنش را صادر کنید + + + + Comma separated file (*.csv) + Comma separated file (*.csv) فایل جداگانه دستوری + + + + Confirmed + تایید شده + + + + Date + تاریخ + + + + Type + نوع + + + + Label + برچسب + + + + Address + آدرس + + + + Amount + میزان + + + + ID + شناسه کاربری + + + + Error exporting + خطا در ارسال + + + + Could not write to file %1. + قابل کپی به فایل نیست %1. + + + + Range: + دامنه: + + + + to + به + + + + WalletModel + + + Send Coins + سکه های ارسالی + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + صدور داده نوار جاری به یک فایل + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + نسخه CryptoLottoToken + + + + Usage: + میزان استفاده: + + + + Send command to -server or CryptoLottoTokend + ارسال دستور به سرور یا CryptoLottoTokened + + + + List commands + فهرست دستورها + + + + Get help for a command + درخواست کمک برای یک دستور + + + + Options: + انتخابها: + + + + Specify configuration file (default: CryptoLottoToken.conf) + فایل پیکربندیِ را مشخص کنید (پیش فرض: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + فایل pid را مشخص کنید (پیش فرض: CryptoLottoTokend.pid) + + + + Specify data directory + دایرکتوری داده را مشخص کن + + + + Set database cache size in megabytes (default: 25) + حافظه بانک داده را به مگابایت تنظیم کنید (پیش فرض: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + ارتباطات را در <PORT> بشنوید (پیش فرض: 22556 or testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + نگهداری <N> ارتباطات برای قرینه سازی (پیش فرض:125) + + + + Connect to a node to retrieve peer addresses, and disconnect + + + + + Specify your own public address + + + + + Threshold for disconnecting misbehaving peers (default: 100) + آستانه قطع برای قرینه سازی اشتباه (پیش فرض:100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + تعداد ثانیه ها برای اتصال دوباره قرینه های اشتباه (پیش فرض:86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + ارتباطاتِ JSON-RPC را در <port> گوش کنید (پیش فرض:22555) + + + + Accept command line and JSON-RPC commands + command line و JSON-RPC commands را قبول کنید + + + + Run in the background as a daemon and accept commands + به عنوان daemon بک گراند را اجرا کنید و دستورات را قبول نمایید + + + + Use the test network + از تستِ شبکه استفاده نمایید + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + + + + + Connect only to the specified node(s) + + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + برونداد اشکال زدایی با timestamp + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + + + + + Select the version of socks proxy to use (4-5, default: 5) + + + + + Send trace/debug info to console instead of debug.log file + ارسال اطلاعات پیگیری/خطایابی به کنسول به جای ارسال به فایل debug.log + + + + Send trace/debug info to debugger + ارسال اطاعات خطایابی/پیگیری به سیستم خطایاب + + + + Set maximum block size in bytes (default: 250000) + + + + + Set minimum block size in bytes (default: 0) + + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + تعیین مدت زمان وقفه (time out) به هزارم ثانیه + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + + + + + Use UPnP to map the listening port (default: 1 when listening) + + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + شناسه کاربری برای ارتباطاتِ JSON-RPC + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + رمز برای ارتباطاتِ JSON-RPC + + + + Allow JSON-RPC connections from specified IP address + ارتباطاتِ JSON-RPC را از آدرس آی.پی. مشخصی برقرار کنید. + + + + Send commands to node running on <ip> (default: 127.0.0.1) + دستورات را به گره اجرا شده در<ip> ارسال کنید (پیش فرض:127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + دستور را وقتی بهترین بلاک تغییر کرد اجرا کن (%s در دستور توسط block hash جایگزین شده است) + + + + Upgrade wallet to latest format + wallet را به جدیدترین نسخه روزآمد کنید + + + + Set key pool size to <n> (default: 100) + حجم key pool را به اندازه <n> تنظیم کنید (پیش فرض:100) + + + + Rescan the block chain for missing wallet transactions + زنجیره بلاک را برای تراکنش جا افتاده در WALLET دوباره اسکن کنید + + + + Use OpenSSL (https) for JSON-RPC connections + برای ارتباطاتِ JSON-RPC از OpenSSL (https) استفاده کنید + + + + Server certificate file (default: server.cert) + فایل certificate سرور (پیش فرض server.cert) + + + + Server private key (default: server.pem) + رمز اختصاصی سرور (پیش فرض: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + ciphers قابل قبول (پیش فرض: default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + این پیام راهنما + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + + + + + Connect through socks proxy + + + + + Allow DNS lookups for -addnode, -seednode and -connect + + + + + Loading addresses... + لود شدن آدرسها.. + + + + Error loading wallet.dat: Wallet corrupted + خطا در هنگام لود شدن wallet.dat: Wallet corrupted + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + خطا در هنگام لود شدن wallet.dat. به نسخه جدید Bitocin برای wallet نیاز است. + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + wallet نیاز به بازنویسی دارد. CryptoLottoToken را برای تکمیل عملیات دوباره اجرا کنید. + + + + Error loading wallet.dat + خطا در هنگام لود شدن wallet.dat + + + + Invalid -proxy address: '%s' + + + + + Unknown network specified in -onlynet: '%s' + + + + + Unknown -socks proxy version requested: %i + + + + + Cannot resolve -bind address: '%s' + + + + + Cannot resolve -externalip address: '%s' + + + + + Invalid amount for -paytxfee=<amount>: '%s' + میزان اشتباه است for -paytxfee=<amount>: '%s' + + + + Invalid amount + میزان اشتباه است + + + + Insufficient funds + وجوه ناکافی + + + + Loading block index... + لود شدن نمایه بلاکها.. + + + + Add a node to connect to and attempt to keep the connection open + یک گره برای اتصال اضافه کنید و تلاش کنید تا اتصال را باز نگاه دارید + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + + + + + Fee per KB to add to transactions you send + هزینه بر اساس کیلو بایت برای اضافه شدن به تراکنشی که ارسال کرده اید + + + + Loading wallet... + wallet در حال لود شدن است... + + + + Cannot downgrade wallet + قابلیت برگشت به نسخه قبلی برای wallet امکان پذیر نیست + + + + Cannot write default address + آدرس پیش فرض قابل ذخیره نیست + + + + Rescanning... + اسکنِ دوباره... + + + + Done loading + اتمام لود شدن + + + + To use the %s option + برای استفاده از %s از اختیارات + + + + Error + خطا + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + شما باید یک رمز rpcpassword=<password> را در فایل تنظیمات ایجاد کنید⏎ %s ⏎ اگر فایل ایجاد نشده است، آن را با یک فایل "فقط متنی" ایجاد کنید. + + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_fi.qm b/src/qt/locale/bitcoin_fi.qm new file mode 100644 index 0000000..973597e Binary files /dev/null and b/src/qt/locale/bitcoin_fi.qm differ diff --git a/src/qt/locale/bitcoin_fi.ts b/src/qt/locale/bitcoin_fi.ts new file mode 100644 index 0000000..8f9524f --- /dev/null +++ b/src/qt/locale/bitcoin_fi.ts @@ -0,0 +1,2929 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + Tietoa CryptoLottoTokenista + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> versio + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +Tämä on kokeellinen ohjelmisto. + +Levitetään MIT/X11 ohjelmistolisenssin alaisuudessa. Tarkemmat tiedot löytyvät tiedostosta COPYING tai osoitteesta http://www.opensource.org/licenses/mit-license.php. + +Tämä ohjelma sisältää OpenSSL projektin OpenSSL työkalupakin (http://www.openssl.org/), Eric Youngin (eay@cryptsoft.com) kehittämän salausohjelmiston sekä Thomas Bernardin UPnP ohjelmiston. + + + + + Copyright + Tekijänoikeus + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + Osoitekirja + + + + Double-click to edit address or label + Kaksoisnapauta muokataksesi osoitetta tai nimeä + + + + Create a new address + Luo uusi osoite + + + + Copy the currently selected address to the system clipboard + Kopioi valittu osoite leikepöydälle + + + + &New Address + &Uusi Osoite + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Nämä ovat CryptoLottoToken-osoitteesi joihin voit vastaanottaa maksuja. Voit haluta antaa jokaiselle maksajalle omansa, että pystyt seuraamaan keneltä maksut tulevat. + + + + &Copy Address + &Kopioi Osoite + + + + Show &QR Code + Näytä &QR-koodi + + + + Sign a message to prove you own a CryptoLottoToken address + Allekirjoita viesti todistaaksesi, että omistat CryptoLottoToken-osoitteen + + + + Sign &Message + Allekirjoita &viesti + + + + Delete the currently selected address from the list + Poista valittu osoite listalta + + + + Export the data in the current tab to a file + Vie auki olevan välilehden tiedot tiedostoon + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Tarkista viestin allekirjoitus varmistaaksesi, että se allekirjoitettiin tietyllä CryptoLottoToken-osoitteella + + + + &Verify Message + &Varmista viesti... + + + + &Delete + &Poista + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + Kopioi &Nimi + + + + &Edit + &Muokkaa + + + + Send &Coins + Lähetä &Rahaa + + + + Export Address Book Data + Vie osoitekirja + + + + Comma separated file (*.csv) + Comma separated file (*.csv) + + + + Error exporting + Virhe viedessä osoitekirjaa + + + + Could not write to file %1. + Ei voida kirjoittaa tiedostoon %1. + + + + AddressTableModel + + + Label + Nimi + + + + Address + Osoite + + + + (no label) + (ei nimeä) + + + + AskPassphraseDialog + + + Passphrase Dialog + Tunnuslauseen Dialogi + + + + Enter passphrase + Kirjoita tunnuslause + + + + New passphrase + Uusi tunnuslause + + + + Repeat new passphrase + Kiroita uusi tunnuslause uudelleen + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Anna lompakolle uusi tunnuslause.<br/>Käytä tunnuslausetta, jossa on ainakin <b>10 satunnaista mekkiä</b> tai <b>kahdeksan sanaa</b>. + + + + Encrypt wallet + Salaa lompakko + + + + This operation needs your wallet passphrase to unlock the wallet. + Tätä toimintoa varten sinun täytyy antaa lompakon tunnuslause sen avaamiseksi. + + + + Unlock wallet + Avaa lompakko + + + + This operation needs your wallet passphrase to decrypt the wallet. + Tätä toimintoa varten sinun täytyy antaa lompakon tunnuslause salauksen purkuun. + + + + Decrypt wallet + Pura lompakon salaus + + + + Change passphrase + Vaihda tunnuslause + + + + Enter the old and new passphrase to the wallet. + Anna vanha ja uusi tunnuslause. + + + + Confirm wallet encryption + Vahvista lompakon salaus + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Varoitus: Jos salaat lompakkosi ja menetät tunnuslauseesi, <b>MENETÄT KAIKKI CryptoLottoTokenISI</b>! + + + + Are you sure you wish to encrypt your wallet? + Haluatko varmasti salata lompakkosi? + + + + 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. + TÄRKEÄÄ: Kaikki vanhat lompakon varmuuskopiot pitäisi korvata uusilla suojatuilla varmuuskopioilla. Turvallisuussyistä edelliset varmuuskopiot muuttuvat turhiksi, kun aloitat suojatun lompakon käytön. + + + + + Warning: The Caps Lock key is on! + Varoitus: Caps Lock on käytössä! + + + + + Wallet encrypted + Lompakko salattu + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken sulkeutuu lopettaakseen salausprosessin. Muista, että salattukaan lompakko ei täysin suojaa sitä haittaohjelmien aiheuttamilta varkauksilta. + + + + + + + Wallet encryption failed + Lompakon salaus epäonnistui + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Lompakon salaaminen epäonnistui sisäisen virheen vuoksi. Lompakkoasi ei salattu. + + + + + The supplied passphrases do not match. + Annetut tunnuslauseet eivät täsmää. + + + + Wallet unlock failed + Lompakon avaaminen epäonnistui. + + + + + + The passphrase entered for the wallet decryption was incorrect. + Annettu tunnuslause oli väärä. + + + + Wallet decryption failed + Lompakon salauksen purku epäonnistui. + + + + Wallet passphrase was successfully changed. + Lompakon tunnuslause vaihdettiin onnistuneesti. + + + + BitcoinGUI + + + Sign &message... + &Allekirjoita viesti... + + + + Synchronizing with network... + Synkronoidaan verkon kanssa... + + + + &Overview + &Yleisnäkymä + + + + Show general overview of wallet + Lompakon tilanteen yleiskatsaus + + + + &Transactions + &Rahansiirrot + + + + Browse transaction history + Selaa rahansiirtohistoriaa + + + + Edit the list of stored addresses and labels for sending + Muokkaa tallennettujen nimien ja osoitteiden listaa + + + + Show the list of addresses for receiving payments + Näytä CryptoLottoTokenien vastaanottamiseen käytetyt osoitteet + + + + E&xit + L&opeta + + + + Quit application + Sulje ohjelma + + + + Show information about CryptoLottoToken + Näytä tietoa CryptoLottoToken-projektista + + + + About &Qt + Tietoja &Qt + + + + Show information about Qt + Näytä tietoja QT:ta + + + + &Options... + &Asetukset... + + + + &Encrypt Wallet... + &Salaa lompakko... + + + + &Backup Wallet... + &Varmuuskopioi Lompakko... + + + + &Change Passphrase... + &Vaihda Tunnuslause... + + + + Importing blocks from disk... + Tuodaan lohkoja levyltä + + + + Reindexing blocks on disk... + Ladataan lohkoindeksiä... + + + + Send coins to a CryptoLottoToken address + Lähetä kolikoita CryptoLottoToken-osoitteeseen + + + + Modify configuration options for CryptoLottoToken + Muuta CryptoLottoTokenin konfiguraatioasetuksia + + + + Backup wallet to another location + Varmuuskopioi lompakko toiseen sijaintiin + + + + Change the passphrase used for wallet encryption + Vaihda lompakon salaukseen käytettävä tunnuslause + + + + &Debug window + &Testausikkuna + + + + Open debugging and diagnostic console + Avaa debuggaus- ja diagnostiikkakonsoli + + + + &Verify message... + Varmista &viesti... + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Lompakko + + + + &Send + &Lähetä + + + + &Receive + &Vastaanota + + + + &Addresses + &Osoitteet + + + + &About CryptoLottoToken + &Tietoa CryptoLottoTokenista + + + + &Show / Hide + &Näytä / Piilota + + + + Show or hide the main Window + Näytä tai piilota CryptoLottoToken-ikkuna + + + + Encrypt the private keys that belong to your wallet + Suojaa yksityiset avaimet, jotka kuuluvat lompakkoosi + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Allekirjoita viestisi omalla CryptoLottoToken -osoitteellasi todistaaksesi, että omistat ne + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Varmista, että viestisi on allekirjoitettu määritetyllä CryptoLottoToken -osoitteella + + + + &File + &Tiedosto + + + + &Settings + &Asetukset + + + + &Help + &Apua + + + + Tabs toolbar + Välilehtipalkki + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + CryptoLottoToken-asiakas + + + + %n active connection(s) to CryptoLottoToken network + %n aktiivinen yhteys CryptoLottoToken-verkkoon%n aktiivista yhteyttä CryptoLottoToken-verkkoon + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + Käsitelty %1 lohkoa rahansiirtohistoriasta + + + + %n hour(s) + %n tunti%n tuntia + + + + %n day(s) + + + + + %n week(s) + %n viikko%n viikkoa + + + + %1 behind + + + + + Last received block was generated %1 ago. + Viimeisin vastaanotettu lohko tuotettu %1. + + + + Transactions after this will not yet be visible. + + + + + Error + Virhe + + + + Warning + Varoitus + + + + Information + Tietoa + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + Rahansiirtohistoria on ajan tasalla + + + + Catching up... + Saavutetaan verkkoa... + + + + Confirm transaction fee + Vahvista maksukulu + + + + Sent transaction + Lähetetyt rahansiirrot + + + + Incoming transaction + Saapuva rahansiirto + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Päivä: %1 +Määrä: %2 +Tyyppi: %3 +Osoite: %4 + + + + + URI handling + URI käsittely + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + URIa ei voitu jäsentää! Tämä voi johtua kelvottomasta CryptoLottoToken-osoitteesta tai virheellisistä URI parametreista. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Lompakko on <b>salattu</b> ja tällä hetkellä <b>avoinna</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Lompakko on <b>salattu</b> ja tällä hetkellä <b>lukittuna</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + Peruuttamaton virhe on tapahtunut. CryptoLottoToken ei voi enää jatkaa turvallisesti ja sammutetaan. + + + + ClientModel + + + Network Alert + Verkkohälytys + + + + EditAddressDialog + + + Edit Address + Muokkaa osoitetta + + + + &Label + &Nimi + + + + The label associated with this address book entry + Tähän osoitteeseen liitetty nimi + + + + &Address + &Osoite + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Osoite, joka liittyy tämän osoitekirjan merkintään. Tätä voidaan muuttaa vain lähtevissä osoitteissa. + + + + New receiving address + Uusi vastaanottava osoite + + + + New sending address + Uusi lähettävä osoite + + + + Edit receiving address + Muokkaa vastaanottajan osoitetta + + + + Edit sending address + Muokkaa lähtevää osoitetta + + + + The entered address "%1" is already in the address book. + Osoite "%1" on jo osoitekirjassa. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + Antamasi osoite "%1" ei ole validi CryptoLottoToken-osoite. + + + + Could not unlock wallet. + Lompakkoa ei voitu avata. + + + + New key generation failed. + Uuden avaimen luonti epäonnistui. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + versio + + + + Usage: + Käyttö: + + + + command-line options + komentorivi parametrit + + + + UI options + Käyttöliittymäasetukset + + + + Set language, for example "de_DE" (default: system locale) + Set language, for example "de_DE" (default: system locale) + + + + Start minimized + Käynnistä pienennettynä + + + + Show splash screen on startup (default: 1) + Näytä aloitusruutu käynnistettäessä (oletus: 1) + + + + OptionsDialog + + + Options + Asetukset + + + + &Main + &Yleiset + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + Maksa rahansiirtopalkkio + + + + Automatically start CryptoLottoToken after logging in to the system. + Käynnistä CryptoLottoToken kirjautumisen yhteydessä. + + + + &Start CryptoLottoToken on system login + &Käynnistä CryptoLottoToken kirjautumisen yhteydessä + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + &Verkko + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Avaa CryptoLottoToken-asiakasohjelman portti reitittimellä automaattisesti. Tämä toimii vain, jos reitittimesi tukee UPnP:tä ja se on käytössä. + + + + Map port using &UPnP + Portin uudelleenohjaus &UPnP:llä + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Ota yhteys CryptoLottoToken-verkkoon SOCKS-proxyn läpi (esimerkiksi kun haluat käyttää Tor-verkkoa). + + + + &Connect through SOCKS proxy: + &Ota yhteys SOCKS-proxyn kautta: + + + + Proxy &IP: + Proxyn &IP: + + + + IP address of the proxy (e.g. 127.0.0.1) + Välityspalvelimen IP-osoite (esim. 127.0.0.1) + + + + &Port: + &Portti + + + + Port of the proxy (e.g. 9050) + Proxyn Portti (esim. 9050) + + + + SOCKS &Version: + SOCKS &Versio: + + + + SOCKS version of the proxy (e.g. 5) + Proxyn SOCKS-versio (esim. 5) + + + + &Window + &Ikkuna + + + + Show only a tray icon after minimizing the window. + Näytä ainoastaan ilmaisinalueella ikkunan pienentämisen jälkeen. + + + + &Minimize to the tray instead of the taskbar + &Pienennä ilmaisinalueelle työkalurivin sijasta + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Ikkunaa suljettaessa vain pienentää CryptoLottoToken-ohjelman ikkunan lopettamatta itse ohjelmaa. Kun tämä asetus on valittuna, ohjelman voi sulkea vain valitsemalla Lopeta ohjelman valikosta. + + + + M&inimize on close + P&ienennä suljettaessa + + + + &Display + &Käyttöliittymä + + + + User Interface &language: + &Käyttöliittymän kieli + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + Tässä voit määritellä käyttöliittymän kielen. Muutokset astuvat voimaan seuraavan kerran, kun CryptoLottoToken käynnistetään. + + + + &Unit to show amounts in: + Yksikkö jona CryptoLottoToken-määrät näytetään + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Valitse mitä yksikköä käytetään ensisijaisesti CryptoLottoToken-määrien näyttämiseen. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Näytetäänkö CryptoLottoToken-osoitteet rahansiirrot listassa vai ei. + + + + &Display addresses in transaction list + &Näytä osoitteet rahansiirrot listassa + + + + &OK + &OK + + + + &Cancel + &Peruuta + + + + &Apply + &Hyväksy + + + + default + oletus + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + Varoitus + + + + + This setting will take effect after restarting CryptoLottoToken. + Tämä asetus astuu voimaan seuraavalla kerralla, kun CryptoLottoToken käynnistetään. + + + + The supplied proxy address is invalid. + Antamasi proxy-osoite on virheellinen. + + + + OverviewPage + + + Form + Lomake + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + Näytetyt tiedot eivät välttämättä ole ajantasalla. Lompakkosi synkronoituu CryptoLottoToken-verkon kanssa automaattisesti yhteyden muodostamisen jälkeen, mutta synkronointi on vielä meneillään. + + + + Balance: + Saldo: + + + + Unconfirmed: + Vahvistamatta: + + + + Wallet + Lompakko + + + + Immature: + Epäkypsää: + + + + Mined balance that has not yet matured + Louhittu saldo, joka ei ole vielä kypsynyt + + + + <b>Recent transactions</b> + <b>Viimeisimmät rahansiirrot</b> + + + + Your current balance + Tililläsi tällä hetkellä olevien CryptoLottoTokenien määrä + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Niiden saapuvien rahansiirtojen määrä, joita CryptoLottoToken-verkko ei vielä ole ehtinyt vahvistaa ja siten eivät vielä näy saldossa. + + + + + out of sync + Ei ajan tasalla + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + QR-koodi Dialogi + + + + Request Payment + Vastaanota maksu + + + + Amount: + Määrä: + + + + Label: + Tunniste: + + + + Message: + Viesti: + + + + &Save As... + &Tallenna nimellä... + + + + Error encoding URI into QR Code. + Virhe käännettäessä URI:a QR-koodiksi. + + + + The entered amount is invalid, please check. + Syötetty määrä on virheellinen. Tarkista kirjoitusasu. + + + + Resulting URI too long, try to reduce the text for label / message. + Tuloksen URI liian pitkä, yritä lyhentää otsikon tekstiä / viestiä. + + + + Save QR Code + Tallenna QR-koodi + + + + PNG Images (*.png) + PNG kuvat (*png) + + + + RPCConsole + + + Client name + Pääteohjelman nimi + + + + + + + + + + + + + N/A + Ei saatavilla + + + + Client version + Pääteohjelman versio + + + + &Information + T&ietoa + + + + Using OpenSSL version + Käytössä oleva OpenSSL-versio + + + + Startup time + Käynnistysaika + + + + Network + Verkko + + + + Number of connections + Yhteyksien lukumäärä + + + + On testnet + Käyttää testiverkkoa + + + + Block chain + Lohkoketju + + + + Current number of blocks + Nykyinen Lohkojen määrä + + + + Estimated total blocks + Arvioitu lohkojen kokonaismäärä + + + + Last block time + Viimeisimmän lohkon aika + + + + &Open + &Avaa + + + + Command-line options + Komentorivi parametrit + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Näytä CryptoLottoToken-Qt komentoriviparametrien ohjesivu, jossa on listattuna mahdolliset komentoriviparametrit. + + + + &Show + &Näytä + + + + &Console + &Konsoli + + + + Build date + Kääntöpäiväys + + + + CryptoLottoToken - Debug window + CryptoLottoToken - Debug ikkuna + + + + CryptoLottoToken Core + CryptoLottoToken-ydin + + + + Debug log file + Debug lokitiedosto + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + Avaa lokitiedosto nykyisestä data-kansiosta. Tämä voi viedä useamman sekunnin, jos lokitiedosto on iso. + + + + Clear console + Tyhjennä konsoli + + + + Welcome to the CryptoLottoToken RPC console. + Tervetuloa CryptoLottoToken RPC konsoliin. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Ylös- ja alas-nuolet selaavat historiaa ja <b>Ctrl-L</b> tyhjentää ruudun. + + + + Type <b>help</b> for an overview of available commands. + Kirjoita <b>help</b> nähdäksesi yleiskatsauksen käytettävissä olevista komennoista. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Lähetä CryptoLottoTokeneja + + + + Send to multiple recipients at once + Lähetä monelle vastaanottajalle + + + + Add &Recipient + Lisää &Vastaanottaja + + + + Remove all transaction fields + Poista kaikki rahansiirtokentät + + + + Clear &All + &Tyhjennnä Kaikki + + + + Balance: + Saldo: + + + + 123.456 BTC + 123,456 BTC + + + + Confirm the send action + Vahvista lähetys + + + + S&end + &Lähetä + + + + <b>%1</b> to %2 (%3) + <b>%1</b> to %2 (%3) + + + + Confirm send coins + Hyväksy CryptoLottoTokenien lähettäminen + + + + Are you sure you want to send %1? + Haluatko varmasti lähettää %1? + + + + and + ja + + + + The recipient address is not valid, please recheck. + Vastaanottajan osoite on virheellinen. Tarkista osoite. + + + + The amount to pay must be larger than 0. + Maksettavan summan tulee olla suurempi kuin 0 CryptoLottoTokenia. + + + + The amount exceeds your balance. + Määrä ylittää käytettävissä olevan saldon. + + + + The total exceeds your balance when the %1 transaction fee is included. + Kokonaismäärä ylittää saldosi kun %1 maksukulu lisätään summaan. + + + + Duplicate address found, can only send to each address once per send operation. + Sama osoite toistuu useamman kerran. Samaan osoitteeseen voi lähettää vain kerran per maksu. + + + + Error: Transaction creation failed! + + + + + 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. + Virhe: Rahansiirto hylättiin. Tämä voi tapahtua jos jotkin CryptoLottoTokeneistasi on jo käytetty, esimerkiksi jos olet käyttänyt kopiota wallet.dat-lompakkotiedostosta ja CryptoLottoTokenit on merkitty käytetyksi vain kopiossa. + + + + SendCoinsEntry + + + Form + Lomake + + + + A&mount: + M&äärä: + + + + Pay &To: + Maksun saaja: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + Anna nimi tälle osoitteelle, jos haluat lisätä sen osoitekirjaan + + + + &Label: + &Nimi: + + + + Choose address from address book + Valitse osoite osoitekirjasta + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Liitä osoite leikepöydältä + + + + Alt+P + Alt+P + + + + Remove this recipient + Poista + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Anna CryptoLottoToken-osoite (esim. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Allekirjoitukset - Allekirjoita / Varmista viesti + + + + &Sign Message + &Allekirjoita viesti + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Voit allekirjoittaa viestit omalla osoitteellasi todistaaksesi että omistat ne. Ole huolellinen, että et allekirjoita mitään epämääräistä, phishing-hyökkääjät voivat huijata sinua allekirjoittamaan luovuttamalla henkilöllisyytesi. Allekirjoita selvitys täysin yksityiskohtaisesti mihin olet sitoutunut. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Osoite, jolla viesti allekirjoitetaan (esimerkiksi Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Valitse osoite osoitekirjasta + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Liitä osoite leikepöydältä + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Kirjoita tähän viesti minkä haluat allekirjoittaa + + + + Signature + Allekirjoitus + + + + Copy the current signature to the system clipboard + Kopioi tämänhetkinen allekirjoitus leikepöydälle + + + + Sign the message to prove you own this CryptoLottoToken address + Allekirjoita viesti todistaaksesi, että omistat tämän CryptoLottoToken-osoitteen + + + + Sign &Message + Allekirjoita &viesti + + + + Reset all sign message fields + Tyhjennä kaikki allekirjoita-viesti-kentät + + + + + Clear &All + &Tyhjennä Kaikki + + + + &Verify Message + &Varmista viesti + + + + Enter the signing 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. + Syötä allekirjoittava osoite, viesti ja allekirjoitus alla oleviin kenttiin varmistaaksesi allekirjoituksen aitouden. Varmista että kopioit kaikki kentät täsmälleen oikein, myös rivinvaihdot, välilyönnit, tabulaattorit, jne. + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Osoite, jolla viesti allekirjoitettiin (esimerkiksi Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Tarkista viestin allekirjoitus varmistaaksesi, että se allekirjoitettiin tietyllä CryptoLottoToken-osoitteella + + + + Verify &Message + + + + + Reset all verify message fields + Tyhjennä kaikki varmista-viesti-kentät + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Anna CryptoLottoToken-osoite (esim. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Klikkaa "Allekirjoita Viesti luodaksesi allekirjoituksen + + + + Enter CryptoLottoToken signature + Syötä CryptoLottoToken-allekirjoitus + + + + + The entered address is invalid. + Syötetty osoite on virheellinen. + + + + + + + Please check the address and try again. + Tarkista osoite ja yritä uudelleen. + + + + + The entered address does not refer to a key. + Syötetyn osoitteen avainta ei löydy. + + + + Wallet unlock was cancelled. + Lompakon avaaminen peruttiin. + + + + Private key for the entered address is not available. + Yksityistä avainta syötetylle osoitteelle ei ole saatavilla. + + + + Message signing failed. + Viestin allekirjoitus epäonnistui. + + + + Message signed. + Viesti allekirjoitettu. + + + + The signature could not be decoded. + Allekirjoitusta ei pystytty tulkitsemaan. + + + + + Please check the signature and try again. + Tarkista allekirjoitus ja yritä uudelleen. + + + + The signature did not match the message digest. + Allekirjoitus ei täsmää viestin tiivisteeseen. + + + + Message verification failed. + Viestin varmistus epäonnistui. + + + + Message verified. + Viesti varmistettu. + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + Avoinna %1 asti + + + + %1/offline + %1/offline + + + + %1/unconfirmed + %1/vahvistamaton + + + + %1 confirmations + %1 vahvistusta + + + + Status + Tila + + + + , broadcast through %n node(s) + lähetetty %n noodin läpilähetetty %n noodin läpi + + + + Date + Päivämäärä + + + + Source + Lähde + + + + Generated + Generoitu + + + + + From + Lähettäjä + + + + + + To + Saaja + + + + + own address + oma osoite + + + + label + nimi + + + + + + + + Credit + Credit + + + + matures in %n more block(s) + kypsyy %n lohkon kuluttuakypsyy %n lohkon kuluttua + + + + not accepted + ei hyväksytty + + + + + + + Debit + Debit + + + + Transaction fee + Maksukulu + + + + Net amount + Netto määrä + + + + Message + Viesti + + + + Comment + Viesti + + + + Transaction ID + Siirtotunnus + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Generoitujen kolikoiden täytyy kypsyä 120 lohkon ajan ennen kuin ne voidaan lähettää. Kun loit tämän lohkon, se lähetettiin verkkoon lisättäväksi lohkoketjuun. Jos se ei päädy osaksi lohkoketjua, sen tila vaihtuu "ei hyväksytty" ja sitä ei voida lähettää. Näin voi joskus käydä, jos toinen noodi löytää lohkon muutamaa sekuntia ennen tai jälkeen sinun lohkosi löytymisen. + + + + Debug information + Debug tiedot + + + + Transaction + Rahansiirto + + + + Inputs + Sisääntulot + + + + Amount + Määrä + + + + true + tosi + + + + false + epätosi + + + + , has not been successfully broadcast yet + , ei ole vielä onnistuneesti lähetetty + + + + Open for %n more block(s) + + + + + unknown + tuntematon + + + + TransactionDescDialog + + + Transaction details + Rahansiirron yksityiskohdat + + + + This pane shows a detailed description of the transaction + Tämä ruutu näyttää yksityiskohtaisen tiedon rahansiirrosta + + + + TransactionTableModel + + + Date + Päivämäärä + + + + Type + Laatu + + + + Address + Osoite + + + + Amount + Määrä + + + + Open for %n more block(s) + + + + + Open until %1 + Avoinna %1 asti + + + + Offline (%1 confirmations) + Ei yhteyttä verkkoon (%1 vahvistusta) + + + + Unconfirmed (%1 of %2 confirmations) + Vahvistamatta (%1/%2 vahvistusta) + + + + Confirmed (%1 confirmations) + Vahvistettu (%1 vahvistusta) + + + + Mined balance will be available when it matures in %n more block(s) + Louhittu saldo on käytettävissä kun se kypsyy %n lohkon päästäLouhittu saldo on käytettävissä kun se kypsyy %n lohkon päästä + + + + This block was not received by any other nodes and will probably not be accepted! + Tätä lohkoa ei vastaanotettu mistään muusta solmusta ja sitä ei mahdollisesti hyväksytä! + + + + Generated but not accepted + Generoitu mutta ei hyväksytty + + + + Received with + Vastaanotettu osoitteella + + + + Received from + Vastaanotettu + + + + Sent to + Saaja + + + + Payment to yourself + Maksu itsellesi + + + + Mined + Louhittu + + + + (n/a) + (ei saatavilla) + + + + Transaction status. Hover over this field to show number of confirmations. + Rahansiirron tila. Siirrä osoitin kentän päälle nähdäksesi vahvistusten lukumäärä. + + + + Date and time that the transaction was received. + Rahansiirron vastaanottamisen päivämäärä ja aika. + + + + Type of transaction. + Rahansiirron laatu. + + + + Destination address of transaction. + Rahansiirron kohteen CryptoLottoToken-osoite + + + + Amount removed from or added to balance. + Saldoon lisätty tai siitä vähennetty määrä. + + + + TransactionView + + + + All + Kaikki + + + + Today + Tänään + + + + This week + Tällä viikolla + + + + This month + Tässä kuussa + + + + Last month + Viime kuussa + + + + This year + Tänä vuonna + + + + Range... + Alue... + + + + Received with + Vastaanotettu osoitteella + + + + Sent to + Saaja + + + + To yourself + Itsellesi + + + + Mined + Louhittu + + + + Other + Muu + + + + Enter address or label to search + Anna etsittävä osoite tai tunniste + + + + Min amount + Minimimäärä + + + + Copy address + Kopioi osoite + + + + Copy label + Kopioi nimi + + + + Copy amount + Kopioi määrä + + + + Copy transaction ID + + + + + Edit label + Muokkaa nimeä + + + + Show transaction details + Näytä rahansiirron yksityiskohdat + + + + Export Transaction Data + Vie rahansiirron tiedot + + + + Comma separated file (*.csv) + Comma separated file (*.csv) + + + + Confirmed + Vahvistettu + + + + Date + Aika + + + + Type + Laatu + + + + Label + Nimi + + + + Address + Osoite + + + + Amount + Määrä + + + + ID + ID + + + + Error exporting + Virhe tietojen viennissä + + + + Could not write to file %1. + Ei voida kirjoittaa tiedostoon %1. + + + + Range: + Alue: + + + + to + kenelle + + + + WalletModel + + + Send Coins + Lähetä CryptoLottoTokeneja + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + Vie auki olevan välilehden tiedot tiedostoon + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + Varmuuskopio Onnistui + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + CryptoLottoTokenin versio + + + + Usage: + Käyttö: + + + + Send command to -server or CryptoLottoTokend + Lähetä käsky palvelimelle tai CryptoLottoTokend:lle + + + + List commands + Lista komennoista + + + + Get help for a command + Hanki apua käskyyn + + + + Options: + Asetukset: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Määritä asetustiedosto (oletus: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + Määritä pid-tiedosto (oletus: CryptoLottoToken.pid) + + + + Specify data directory + Määritä data-hakemisto + + + + Set database cache size in megabytes (default: 25) + Aseta tietokannan välimuistin koko megatavuina (oletus: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Kuuntele yhteyksiä portista <port> (oletus: 22556 tai testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Pidä enintään <n> yhteyttä verkkoihin (oletus: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Yhdistä noodiin hakeaksesi naapurien osoitteet ja katkaise yhteys + + + + Specify your own public address + Määritä julkinen osoitteesi + + + + Threshold for disconnecting misbehaving peers (default: 100) + Kynnysarvo aikakatkaisulle heikosti toimiville verkoille (oletus: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Sekuntien määrä, kuinka kauan uudelleenkytkeydytään verkkoihin (oletus: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + Virhe valmisteltaessa RPC-portin %u avaamista kuunneltavaksi: %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Kuuntele JSON-RPC -yhteyksiä portista <port> (oletus: 22555 or testnet: 44555) + + + + Accept command line and JSON-RPC commands + Hyväksy merkkipohjaiset- ja JSON-RPC-käskyt + + + + Run in the background as a daemon and accept commands + Aja taustalla daemonina ja hyväksy komennot + + + + Use the test network + Käytä test -verkkoa + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Hyväksy yhteyksiä ulkopuolelta (vakioasetus: 1 jos -proxy tai -connect ei määritelty) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + Virhe ilmennyt asetettaessa RPC-porttia %u IPv6:n kuuntelemiseksi, palataan takaisin IPv4:ään %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Aseta suurin korkean prioriteetin / matalan palkkion siirron koko tavuissa (vakioasetus: 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Varoitus: -paytxfee on asetettu erittäin korkeaksi! Tämä on maksukulu jonka tulet maksamaan kun lähetät siirron. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + Varoitus: Näytetyt siirrot eivät välttämättä pidä paikkaansa! Sinun tai toisten noodien voi olla tarpeen asentaa päivitys. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Varoitus: Tarkista että tietokoneesi kellonaika ja päivämäärä ovat paikkansapitäviä! CryptoLottoToken ei toimi oikein väärällä päivämäärällä ja/tai kellonajalla. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + Lohkon luonnin asetukset: + + + + Connect only to the specified node(s) + Yhidstä ainoastaan määrättyihin noodeihin + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + Hae oma IP osoite (vakioasetus: 1 kun kuuntelemassa ja ei -externalip) + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + Virhe avattaessa lohkoindeksiä + + + + Error: Disk space is low! + Varoitus: Levytila on vähissä! + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + Virhe: Järjestelmävirhe + + + + Failed to listen on any port. Use -listen=0 if you want this. + Ei onnistuttu kuuntelemaan missään portissa. Käytä -listen=0 jos haluat tätä. + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + Lohkon kirjoitus epäonnistui + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + Hae naapureita DNS hauilla (vakioasetus: 1 paitsi jos -connect) + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + Tuodaan lohkoja ulkoisesta blk000??.dat tiedostosta + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + Tietoa + + + + Invalid -tor address: '%s' + Virheellinen -tor osoite '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Suurin vastaanottopuskuri yksittäiselle yhteydelle, <n>*1000 tavua (vakioasetus: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Suurin lähetyspuskuri yksittäiselle yhteydelle, <n>*1000 tavua (vakioasetus: 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Yhdistä vain noodeihin verkossa <net> (IPv4, IPv6 tai Tor) + + + + Output extra debugging information. Implies all other -debug* options + Tulosta enemmän debug tietoa. Aktivoi kaikki -debug* asetukset + + + + Output extra network debugging information + Tulosta lisää verkkoyhteys debug tietoa + + + + Prepend debug output with timestamp (default: 1) + Lisää debuggaustiedon tulostukseen aikaleima + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + SSL asetukset (katso CryptoLottoToken Wikistä tarkemmat SSL ohjeet) + + + + Select the version of socks proxy to use (4-5, default: 5) + Valitse käytettävän SOCKS-proxyn versio (4-5, vakioasetus: 5) + + + + Send trace/debug info to console instead of debug.log file + Lähetä jäljitys/debug-tieto konsoliin, debug.log-tiedoston sijaan + + + + Send trace/debug info to debugger + Lähetä jäljitys/debug-tieto debuggeriin + + + + Set maximum block size in bytes (default: 250000) + Aseta suurin lohkon koko tavuissa (vakioasetus: 250000) + + + + Set minimum block size in bytes (default: 0) + Asetan pienin lohkon koko tavuissa (vakioasetus: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Pienennä debug.log tiedosto käynnistyksen yhteydessä (vakioasetus: 1 kun ei -debug) + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + Määritä yhteyden aikakataisu millisekunneissa (vakioasetus: 5000) + + + + System error: + Järjestelmävirhe: + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + Käytä UPnP:tä kuunneltavan portin avaamiseen (vakioasetus: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Käytä UPnP:tä kuunneltavan portin avaamiseen (vakioasetus: 1 kun kuuntelemassa) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Käytä proxyä tor yhteyksien avaamiseen (vakioasetus: sama kuin -proxy) + + + + Username for JSON-RPC connections + Käyttäjätunnus JSON-RPC-yhteyksille + + + + Warning + Varoitus + + + + Warning: This version is obsolete, upgrade required! + Varoitus: Tämä versio on vanhentunut, päivitys tarpeen! + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + Salasana JSON-RPC-yhteyksille + + + + Allow JSON-RPC connections from specified IP address + Salli JSON-RPC yhteydet tietystä ip-osoitteesta + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Lähetä käskyjä solmuun osoitteessa <ip> (oletus: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Suorita käsky kun paras lohko muuttuu (%s cmd on vaihdettu block hashin kanssa) + + + + Upgrade wallet to latest format + Päivitä lompakko uusimpaan formaattiin + + + + Set key pool size to <n> (default: 100) + Aseta avainpoolin koko arvoon <n> (oletus: 100) + + + + Rescan the block chain for missing wallet transactions + Skannaa uudelleen lohkoketju lompakon puuttuvien rahasiirtojen vuoksi + + + + Use OpenSSL (https) for JSON-RPC connections + Käytä OpenSSL:ää (https) JSON-RPC-yhteyksille + + + + Server certificate file (default: server.cert) + Palvelimen sertifikaatti-tiedosto (oletus: server.cert) + + + + Server private key (default: server.pem) + Palvelimen yksityisavain (oletus: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Hyväksyttävä salaus (oletus: +TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Tämä ohjeviesti + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Kytkeytyminen %s tällä tietokonella ei onnistu (kytkeytyminen palautti virheen %d, %s) + + + + Connect through socks proxy + Yhdistä socks proxyn läpi + + + + Allow DNS lookups for -addnode, -seednode and -connect + Salli DNS kyselyt -addnode, -seednode ja -connect yhteydessä + + + + Loading addresses... + Ladataan osoitteita... + + + + Error loading wallet.dat: Wallet corrupted + Virhe ladattaessa wallet.dat-tiedostoa: Lompakko vioittunut + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Virhe ladattaessa wallet.dat-tiedostoa: Tarvitset uudemman version CryptoLottoTokenista + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Lompakko tarvitsee uudelleenkirjoittaa: käynnistä CryptoLottoToken uudelleen + + + + Error loading wallet.dat + Virhe ladattaessa wallet.dat-tiedostoa + + + + Invalid -proxy address: '%s' + Virheellinen proxy-osoite '%s' + + + + Unknown network specified in -onlynet: '%s' + Tuntematon verkko -onlynet parametrina: '%s' + + + + Unknown -socks proxy version requested: %i + Tuntematon -socks proxy versio pyydetty: %i + + + + Cannot resolve -bind address: '%s' + -bind osoitteen '%s' selvittäminen epäonnistui + + + + Cannot resolve -externalip address: '%s' + -externalip osoitteen '%s' selvittäminen epäonnistui + + + + Invalid amount for -paytxfee=<amount>: '%s' + -paytxfee=<amount>: '%s' on virheellinen + + + + Invalid amount + Virheellinen määrä + + + + Insufficient funds + Lompakon saldo ei riitä + + + + Loading block index... + Ladataan lohkoindeksiä... + + + + Add a node to connect to and attempt to keep the connection open + Linää solmu mihin liittyä pitääksesi yhteyden auki + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Kytkeytyminen %s ei onnistu tällä tietokoneella. CryptoLottoToken on todennäköisesti jo ajamassa. + + + + Fee per KB to add to transactions you send + Rahansiirtopalkkio per KB lisätään lähettämääsi rahansiirtoon + + + + Loading wallet... + Ladataan lompakkoa... + + + + Cannot downgrade wallet + Et voi päivittää lompakkoasi vanhempaan versioon + + + + Cannot write default address + Oletusosoitetta ei voi kirjoittaa + + + + Rescanning... + Skannataan uudelleen... + + + + Done loading + Lataus on valmis + + + + To use the %s option + Käytä %s optiota + + + + Error + Virhe + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + Sinun täytyy asettaa rpcpassword=<password> asetustiedostoon: +%s +Jos tiedostoa ei ole, niin luo se ainoastaan omistajan kirjoitusoikeuksin. + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_fr.qm b/src/qt/locale/bitcoin_fr.qm new file mode 100644 index 0000000..026f4e1 Binary files /dev/null and b/src/qt/locale/bitcoin_fr.qm differ diff --git a/src/qt/locale/bitcoin_fr.ts b/src/qt/locale/bitcoin_fr.ts new file mode 100644 index 0000000..056f799 --- /dev/null +++ b/src/qt/locale/bitcoin_fr.ts @@ -0,0 +1,2938 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + À propos de CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> version + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + Ce logiciel est en phase expérimentale. + + Distribué sous licence MIT/X11, voir le fichier COPYING ou http://www.opensource.org/licenses/mit-license.php. + + Ce produit comprend des fonctionnalités développées par le projet OpenSSL pour être utilisés dans la boîte à outils OpenSSL (http://www.openssl.org/), un logiciel cryptographique écrit par Eric Young (eay@cryptsoft.com), et des fonctionnalités développées pour le logiciel UPnP écrit par Thomas Bernard. + + + + Copyright + Droit d'auteur + + + + The CryptoLottoToken developers + Les développeurs CryptoLottoToken + + + + AddressBookPage + + + Address Book + Carnet d'adresses + + + + Double-click to edit address or label + Double cliquez afin de modifier l'adresse ou l'étiquette + + + + Create a new address + Créer une nouvelle adresse + + + + Copy the currently selected address to the system clipboard + Copier l'adresse sélectionnée dans le presse-papiers + + + + &New Address + &Nouvelle adresse + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Voici vos adresses CryptoLottoToken qui vous permettent de recevoir des paiements. Vous pouvez donner une adresse différente à chaque expéditeur afin de savoir qui vous paye. + + + + &Copy Address + &Copier l'adresse + + + + Show &QR Code + Afficher le &QR Code + + + + Sign a message to prove you own a CryptoLottoToken address + Signer un message pour prouver que vous détenez une adresse CryptoLottoToken + + + + Sign &Message + Signer un &message + + + + Delete the currently selected address from the list + Effacer l'adresse actuellement sélectionnée de la liste + + + + Export the data in the current tab to a file + Exporter les données de l'onglet courant vers un fichier + + + + &Export + &Exporter + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Vérifier un message pour vous assurer qu'il a bien été signé avec l'adresse CryptoLottoToken spécifiée + + + + &Verify Message + &Vérifier un message + + + + &Delete + &Supprimer + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + Ce sont vos adresses CryptoLottoToken pour émettre des paiements. Vérifiez toujours le montant et l'adresse du destinataire avant d'envoyer des pièces. + + + + Copy &Label + Copier l'é&tiquette + + + + &Edit + &Éditer + + + + Send &Coins + Envoyer des Bit&coins + + + + Export Address Book Data + Exporter les données du carnet d'adresses + + + + Comma separated file (*.csv) + Valeurs séparées par des virgules (*.csv) + + + + Error exporting + Erreur lors de l'exportation + + + + Could not write to file %1. + Impossible d'écrire dans le fichier %1. + + + + AddressTableModel + + + Label + Étiquette + + + + Address + Adresse + + + + (no label) + (aucune étiquette) + + + + AskPassphraseDialog + + + Passphrase Dialog + Dialogue de phrase de passe + + + + Enter passphrase + Entrez la phrase de passe + + + + New passphrase + Nouvelle phrase de passe + + + + Repeat new passphrase + Répétez la phrase de passe + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Entrez une nouvelle phrase de passe pour le porte-monnaie.<br/>Veuillez utiliser une phrase composée de <b>10 caractères aléatoires ou plus</b>, ou bien de <b>huit mots ou plus</b>. + + + + Encrypt wallet + Chiffrer le porte-monnaie + + + + This operation needs your wallet passphrase to unlock the wallet. + Cette opération nécessite votre phrase de passe pour déverrouiller le porte-monnaie. + + + + Unlock wallet + Déverrouiller le porte-monnaie + + + + This operation needs your wallet passphrase to decrypt the wallet. + Cette opération nécessite votre phrase de passe pour décrypter le porte-monnaie. + + + + Decrypt wallet + Déchiffrer le porte-monnaie + + + + Change passphrase + Changer la phrase de passe + + + + Enter the old and new passphrase to the wallet. + Entrez l’ancienne phrase de passe pour le porte-monnaie ainsi que la nouvelle. + + + + Confirm wallet encryption + Confirmer le chiffrement du porte-monnaie + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Attention : Si vous chiffrez votre porte-monnaie et perdez votre phrase de passe, vous <b>PERDREZ ACCÈS À TOUS VOS CryptoLottoTokenS</b> ! + + + + Are you sure you wish to encrypt your wallet? + Êtes-vous sûr de vouloir chiffrer votre porte-monnaie ? + + + + 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. + IMPORTANT : Les sauvegardes précédentes de votre fichier de porte-monnaie devraient être remplacées par le nouveau fichier crypté de porte-monnaie. Pour des raisons de sécurité, les précédentes sauvegardes de votre fichier de porte-monnaie non chiffré deviendront inutilisables dès que vous commencerez à utiliser le nouveau porte-monnaie chiffré. + + + + + Warning: The Caps Lock key is on! + Attention : la touche Verr. Maj. est activée ! + + + + + Wallet encrypted + Porte-monnaie chiffré + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken va à présent se fermer pour terminer la procédure de cryptage. N'oubliez pas que le chiffrement de votre porte-monnaie ne peut pas fournir une protection totale contre le vol par des logiciels malveillants qui infecteraient votre ordinateur. + + + + + + + Wallet encryption failed + Le chiffrement du porte-monnaie a échoué + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Le chiffrement du porte-monnaie a échoué en raison d'une erreur interne. Votre porte-monnaie n'a pas été chiffré. + + + + + The supplied passphrases do not match. + Les phrases de passe entrées ne correspondent pas. + + + + Wallet unlock failed + Le déverrouillage du porte-monnaie a échoué + + + + + + The passphrase entered for the wallet decryption was incorrect. + La phrase de passe entrée pour décrypter le porte-monnaie était incorrecte. + + + + Wallet decryption failed + Le déchiffrage du porte-monnaie a échoué + + + + Wallet passphrase was successfully changed. + La phrase de passe du porte-monnaie a été modifiée avec succès. + + + + BitcoinGUI + + + Sign &message... + Signer un &message... + + + + Synchronizing with network... + Synchronisation avec le réseau… + + + + &Overview + &Vue d'ensemble + + + + Show general overview of wallet + Afficher une vue d’ensemble du porte-monnaie + + + + &Transactions + &Transactions + + + + Browse transaction history + Parcourir l'historique des transactions + + + + Edit the list of stored addresses and labels for sending + Éditer la liste des adresses et des étiquettes stockées + + + + Show the list of addresses for receiving payments + Afficher la liste des adresses pour recevoir des paiements + + + + E&xit + Q&uitter + + + + Quit application + Quitter l’application + + + + Show information about CryptoLottoToken + Afficher des informations à propos de CryptoLottoToken + + + + About &Qt + À propos de &Qt + + + + Show information about Qt + Afficher des informations sur Qt + + + + &Options... + &Options… + + + + &Encrypt Wallet... + &Chiffrer le porte-monnaie... + + + + &Backup Wallet... + &Sauvegarder le porte-monnaie... + + + + &Change Passphrase... + &Modifier la phrase de passe... + + + + Importing blocks from disk... + Importation des blocs depuis le disque... + + + + Reindexing blocks on disk... + Réindexation des blocs sur le disque... + + + + Send coins to a CryptoLottoToken address + Envoyer des pièces à une adresse CryptoLottoToken + + + + Modify configuration options for CryptoLottoToken + Modifier les options de configuration de CryptoLottoToken + + + + Backup wallet to another location + Sauvegarder le porte-monnaie à un autre emplacement + + + + Change the passphrase used for wallet encryption + Modifier la phrase de passe utilisée pour le chiffrement du porte-monnaie + + + + &Debug window + Fenêtre de &débogage + + + + Open debugging and diagnostic console + Ouvrir une console de débogage et de diagnostic + + + + &Verify message... + &Vérifier un message... + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Porte-monnaie + + + + &Send + &Envoyer + + + + &Receive + &Recevoir + + + + &Addresses + &Adresses + + + + &About CryptoLottoToken + À &propos de CryptoLottoToken + + + + &Show / Hide + &Afficher / Cacher + + + + Show or hide the main Window + Afficher ou masquer la fenêtre principale + + + + Encrypt the private keys that belong to your wallet + Crypter les clefs privées de votre porte-monnaie + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Signer les messages avec vos adresses CryptoLottoToken pour prouver que vous les détenez + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Vérifier les messages pour vous assurer qu'ils ont bien été signés avec les adresses CryptoLottoToken spécifiées + + + + &File + &Fichier + + + + &Settings + &Réglages + + + + &Help + &Aide + + + + Tabs toolbar + Barre d'outils des onglets + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + Client CryptoLottoToken + + + + %n active connection(s) to CryptoLottoToken network + %n connexion active avec le réseau CryptoLottoToken%n connexions actives avec le réseau CryptoLottoToken + + + + No block source available... + Aucune source de bloc disponible... + + + + Processed %1 of %2 (estimated) blocks of transaction history. + %1 blocs sur %2 (estimés) de l'historique des transactions traités. + + + + Processed %1 blocks of transaction history. + %1 blocs de l'historique des transactions traités. + + + + %n hour(s) + %n heure%n heures + + + + %n day(s) + %n jour%n jours + + + + %n week(s) + %n semaine%n semaines + + + + %1 behind + %1 en arrière + + + + Last received block was generated %1 ago. + Le dernier bloc reçu avait été généré il y a %1. + + + + Transactions after this will not yet be visible. + Les transactions après cela ne seront pas encore visibles. + + + + Error + Erreur + + + + Warning + Avertissement + + + + Information + Information + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + Cette transaction dépasse la limite de taille. Vous pouvez quand même l'envoyer en vous acquittant de frais d'un montant de %1 qui iront aux nœuds qui traiteront la transaction et aideront à soutenir le réseau. Voulez-vous payer les frais ? + + + + Up to date + À jour + + + + Catching up... + Rattrapage… + + + + Confirm transaction fee + Confirmer les frais de transaction + + + + Sent transaction + Transaction envoyée + + + + Incoming transaction + Transaction entrante + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Date : %1 +Montant : %2 +Type : %3 +Adresse : %4 + + + + + + URI handling + Gestion des URI + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + L'URI ne peut être analysé ! Cela peut être causé par une adresse CryptoLottoToken invalide ou par des paramètres d'URI malformés. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Le porte-monnaie est <b>chiffré</b> et est actuellement <b>déverrouillé</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Le porte-monnaie est <b>chiffré</b> et est actuellement <b>verrouillé</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + Une erreur fatale est survenue. CryptoLottoToken ne peut plus continuer à fonctionner de façon sûre et va s'arrêter. + + + + ClientModel + + + Network Alert + Alerte réseau + + + + EditAddressDialog + + + Edit Address + Éditer l'adresse + + + + &Label + &Étiquette + + + + The label associated with this address book entry + L’étiquette associée à cette entrée du carnet d'adresses + + + + &Address + &Adresse + + + + The address associated with this address book entry. This can only be modified for sending addresses. + L’adresse associée avec cette entrée du carnet d'adresses. Ne peut être modifiées que les adresses d’envoi. + + + + New receiving address + Nouvelle adresse de réception + + + + New sending address + Nouvelle adresse d’envoi + + + + Edit receiving address + Éditer l’adresse de réception + + + + Edit sending address + Éditer l’adresse d'envoi + + + + The entered address "%1" is already in the address book. + L’adresse fournie « %1 » est déjà présente dans le carnet d'adresses. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + L'adresse fournie « %1 » n'est pas une adresse CryptoLottoToken valide. + + + + Could not unlock wallet. + Impossible de déverrouiller le porte-monnaie. + + + + New key generation failed. + Échec de la génération de la nouvelle clef. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + version + + + + Usage: + Utilisation : + + + + command-line options + options de ligne de commande + + + + UI options + Options Interface Utilisateur + + + + Set language, for example "de_DE" (default: system locale) + Définir la langue, par exemple « de_DE » (par défaut : la langue du système) + + + + Start minimized + Démarrer sous forme minimisée + + + + Show splash screen on startup (default: 1) + Afficher l'écran d'accueil au démarrage (par défaut : 1) + + + + OptionsDialog + + + Options + Options + + + + &Main + Réglages &principaux + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + Frais de transaction optionnel par ko qui aident à garantir un traitement rapide des transactions. La plupart des transactions utilisent 1 ko. + + + + Pay transaction &fee + Payer des &frais de transaction + + + + Automatically start CryptoLottoToken after logging in to the system. + Démarrer CryptoLottoToken automatiquement lors de l'ouverture une session sur l'ordinateur. + + + + &Start CryptoLottoToken on system login + &Démarrer CryptoLottoToken lors de l'ouverture d'une session + + + + Reset all client options to default. + Remettre toutes les options du client aux valeurs par défaut. + + + + &Reset Options + &Remise à zéro des options + + + + &Network + &Réseau + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Ouvrir le port du client CryptoLottoToken automatiquement sur le routeur. Cela ne fonctionne que si votre routeur supporte l'UPnP et si la fonctionnalité est activée. + + + + Map port using &UPnP + Ouvrir le port avec l'&UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Connexion au réseau CryptoLottoToken à travers un proxy SOCKS (par ex. lors d'une connexion via Tor). + + + + &Connect through SOCKS proxy: + &Connexion à travers un proxy SOCKS : + + + + Proxy &IP: + &IP du proxy : + + + + IP address of the proxy (e.g. 127.0.0.1) + Adresse IP du proxy (par ex. 127.0.0.1) + + + + &Port: + &Port : + + + + Port of the proxy (e.g. 9050) + Port du proxy (par ex. 9050) + + + + SOCKS &Version: + &Version SOCKS : + + + + SOCKS version of the proxy (e.g. 5) + Version SOCKS du serveur mandataire (par ex. 5) + + + + &Window + &Fenêtre + + + + Show only a tray icon after minimizing the window. + Afficher uniquement une icône système après minimisation. + + + + &Minimize to the tray instead of the taskbar + &Minimiser dans la barre système au lieu de la barre des tâches + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Minimiser au lieu quitter l'application lorsque la fenêtre est fermée. Lorsque cette option est activée, l'application ne pourra être fermée qu'en sélectionnant Quitter dans le menu déroulant. + + + + M&inimize on close + M&inimiser lors de la fermeture + + + + &Display + &Affichage + + + + User Interface &language: + &Langue de l'interface utilisateur : + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + La langue de l'interface utilisateur peut être définie ici. Ce réglage sera pris en compte après redémarrage de CryptoLottoToken. + + + + &Unit to show amounts in: + &Unité d'affichage des montants : + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Choisissez la sous-unité par défaut pour l'affichage dans l'interface et lors de l'envoi de pièces. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Détermine si les adresses CryptoLottoToken seront affichées sur la liste des transactions. + + + + &Display addresses in transaction list + &Afficher les adresses sur la liste des transactions + + + + &OK + &Valider + + + + &Cancel + A&nnuler + + + + &Apply + &Appliquer + + + + default + par défaut + + + + Confirm options reset + Confirmer la remise à zéro des options + + + + Some settings may require a client restart to take effect. + La prise en compte de certains réglages peut nécessiter un redémarrage du client. + + + + Do you want to proceed? + Voulez-vous continuer ? + + + + + Warning + Avertissement + + + + + This setting will take effect after restarting CryptoLottoToken. + Ce réglage sera pris en compte après un redémarrage de CryptoLottoToken. + + + + The supplied proxy address is invalid. + L'adresse de proxy fournie est invalide. + + + + OverviewPage + + + Form + Formulaire + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + Les informations affichées peuvent être obsolètes. Votre porte-monnaie est automatiquement synchronisé avec le réseau CryptoLottoToken lorsque la connexion s'établit, or ce processus n'est pas encore terminé. + + + + Balance: + Solde : + + + + Unconfirmed: + Non confirmé : + + + + Wallet + Porte-monnaie + + + + Immature: + Immature : + + + + Mined balance that has not yet matured + Le solde généré n'est pas encore mûr + + + + <b>Recent transactions</b> + <b>Transactions récentes</b> + + + + Your current balance + Votre solde actuel + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Total des transactions qui doivent encore être confirmées et qui ne sont pas prises en compte dans le solde actuel + + + + + out of sync + désynchronisé + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + Impossible de démarrer CryptoLottoToken : gestionnaire de cliquer-pour-payer + + + + QRCodeDialog + + + QR Code Dialog + Dialogue de QR Code + + + + Request Payment + Demande de paiement + + + + Amount: + Montant : + + + + Label: + Étiquette : + + + + Message: + Message : + + + + &Save As... + &Enregistrer sous... + + + + Error encoding URI into QR Code. + Erreur de l'encodage de l'URI dans le QR Code. + + + + The entered amount is invalid, please check. + Le montant entré est invalide, veuillez le vérifier. + + + + Resulting URI too long, try to reduce the text for label / message. + L'URI résultant est trop long, essayez avec un texte d'étiquette ou de message plus court. + + + + Save QR Code + Sauvegarder le QR Code + + + + PNG Images (*.png) + Images PNG (*.png) + + + + RPCConsole + + + Client name + Nom du client + + + + + + + + + + + + + N/A + Indisponible + + + + Client version + Version du client + + + + &Information + &Informations + + + + Using OpenSSL version + Version d'OpenSSL utilisée + + + + Startup time + Date de démarrage + + + + Network + Réseau + + + + Number of connections + Nombre de connexions + + + + On testnet + Sur testnet + + + + Block chain + Chaîne de blocs + + + + Current number of blocks + Nombre actuel de blocs + + + + Estimated total blocks + Nombre total estimé de blocs + + + + Last block time + Horodatage du dernier bloc + + + + &Open + &Ouvrir + + + + Command-line options + Options de ligne de commande + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Afficher le message d'aide de CryptoLottoToken-Qt pour obtenir la liste des options de ligne de commande disponibles pour CryptoLottoToken. + + + + &Show + &Afficher + + + + &Console + &Console + + + + Build date + Date de compilation + + + + CryptoLottoToken - Debug window + CryptoLottoToken - Fenêtre de débogage + + + + CryptoLottoToken Core + Noyau CryptoLottoToken + + + + Debug log file + Journal de débogage + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + Ouvrir le journal de débogage de CryptoLottoToken depuis le répertoire de données actuel. Cela peut prendre quelques secondes pour les journaux de grande taille. + + + + Clear console + Nettoyer la console + + + + Welcome to the CryptoLottoToken RPC console. + Bienvenue sur la console RPC de CryptoLottoToken. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Utilisez les touches de curseur pour naviguer dans l'historique et <b>Ctrl-L</b> pour effacer l'écran. + + + + Type <b>help</b> for an overview of available commands. + Tapez <b>help</b> pour afficher une vue générale des commandes disponibles. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Envoyer des pièces + + + + Send to multiple recipients at once + Envoyer des pièces à plusieurs destinataires à la fois + + + + Add &Recipient + Ajouter un &destinataire + + + + Remove all transaction fields + Enlever tous les champs de transaction + + + + Clear &All + &Tout nettoyer + + + + Balance: + Solde : + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Confirmer l’action d'envoi + + + + S&end + E&nvoyer + + + + <b>%1</b> to %2 (%3) + <b>%1</b> à %2 (%3) + + + + Confirm send coins + Confirmer l’envoi des pièces + + + + Are you sure you want to send %1? + Êtes-vous sûr de vouloir envoyer %1 ? + + + + and + et + + + + The recipient address is not valid, please recheck. + Cette adresse de destinataire n’est pas valide, veuillez la vérifier. + + + + The amount to pay must be larger than 0. + Le montant à payer doit être supérieur à 0. + + + + The amount exceeds your balance. + Le montant dépasse votre solde. + + + + The total exceeds your balance when the %1 transaction fee is included. + Le montant dépasse votre solde lorsque les frais de transaction de %1 sont inclus. + + + + Duplicate address found, can only send to each address once per send operation. + Adresse dupliquée trouvée, il n'est possible d'envoyer qu'une fois à chaque adresse par opération d'envoi. + + + + Error: Transaction creation failed! + Erreur : Échec de la création de la transaction ! + + + + 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. + Erreur : la transaction a été rejetée. Cela peut arriver si certaines pièces de votre porte-monnaie ont déjà été dépensées, par exemple si vous avez utilisé une copie de wallet.dat avec laquelle les pièces ont été dépensées mais pas marquées comme telles ici. + + + + SendCoinsEntry + + + Form + Formulaire + + + + A&mount: + &Montant : + + + + Pay &To: + Payer &à : + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + L'adresse à laquelle le paiement sera envoyé (par ex. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + Entrez une étiquette pour cette adresse afin de l’ajouter à votre carnet d’adresses + + + + &Label: + &Étiquette : + + + + Choose address from address book + Choisir une adresse dans le carnet d'adresses + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Coller une adresse depuis le presse-papiers + + + + Alt+P + Alt+P + + + + Remove this recipient + Enlever ce destinataire + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Entrez une adresse CryptoLottoToken (par ex. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Signatures - Signer / Vérifier un message + + + + &Sign Message + &Signer un message + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Vous pouvez signer des messages avec vos adresses pour prouver que les détenez. Faites attention à ne pas signer quoi que ce soit de vague car des attaques d'hameçonnage peuvent essayer d'usurper votre identité par votre signature. Ne signez que des déclarations entièrement détaillées et avec lesquelles vous serez d'accord. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + L'adresse avec laquelle le message sera signé (par ex. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Choisir une adresse depuis le carnet d'adresses + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Coller une adresse depuis le presse-papiers + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Entrez ici le message que vous désirez signer + + + + Signature + Signature + + + + Copy the current signature to the system clipboard + Copier la signature actuelle dans le presse-papiers + + + + Sign the message to prove you own this CryptoLottoToken address + Signer le message pour prouver que vous détenez cette adresse CryptoLottoToken + + + + Sign &Message + Signer le &message + + + + Reset all sign message fields + Remettre à zéro tous les champs de signature de message + + + + + Clear &All + &Tout nettoyer + + + + &Verify Message + &Vérifier un message + + + + Enter the signing 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. + Entrez ci-dessous l'adresse ayant servi à signer, le message (assurez-vous d'avoir copié exactement les retours à la ligne, les espacements, tabulations etc.) et la signature pour vérifier le message. Faites attention à ne pas déduire davantage de la signature que ce qui est contenu dans le message signé lui-même pour éviter d'être trompé par une attaque d'homme du milieu. + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + L'adresse avec laquelle le message a été signé (par ex. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Vérifier le message pour vous assurer qu'il a bien été signé par l'adresse CryptoLottoToken spécifiée + + + + Verify &Message + Vérifier un &message + + + + Reset all verify message fields + Remettre à zéro tous les champs de vérification de message + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Entrez une adresse CryptoLottoToken (par ex. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Cliquez sur « Signer le message » pour générer la signature + + + + Enter CryptoLottoToken signature + Entrer une signature CryptoLottoToken + + + + + The entered address is invalid. + L'adresse entrée est invalide. + + + + + + + Please check the address and try again. + Veuillez vérifier l'adresse et réessayez. + + + + + The entered address does not refer to a key. + L'adresse entrée ne fait pas référence à une clef. + + + + Wallet unlock was cancelled. + Le déverrouillage du porte-monnaie a été annulé. + + + + Private key for the entered address is not available. + La clef privée pour l'adresse indiquée n'est pas disponible. + + + + Message signing failed. + La signature du message a échoué. + + + + Message signed. + Le message a été signé. + + + + The signature could not be decoded. + La signature n'a pu être décodée. + + + + + Please check the signature and try again. + Veuillez vérifier la signature et réessayez. + + + + The signature did not match the message digest. + La signature ne correspond pas au hachage du message. + + + + Message verification failed. + Échec de la vérification du message. + + + + Message verified. + Message vérifié. + + + + SplashScreen + + + The CryptoLottoToken developers + Les développeurs CryptoLottoToken + + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + Ouvert jusqu'à %1 + + + + %1/offline + %1/hors ligne + + + + %1/unconfirmed + %1/non confirmée + + + + %1 confirmations + %1 confirmations + + + + Status + État + + + + , broadcast through %n node(s) + , diffusée à travers %n nœud, diffusée à travers %n nœuds + + + + Date + Date + + + + Source + Source + + + + Generated + Génération + + + + + From + De + + + + + + To + À + + + + + own address + votre propre adresse + + + + label + étiquette + + + + + + + + Credit + Crédit + + + + matures in %n more block(s) + arrive à maturité dans %n blocarrive à maturité dans %n blocs de plus + + + + not accepted + non accepté + + + + + + + Debit + Débit + + + + Transaction fee + Frais de transaction + + + + Net amount + Montant net + + + + Message + Message + + + + Comment + Commentaire + + + + Transaction ID + ID de la transaction + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Les pièces générées doivent mûrir pendant 120 blocs avant de pouvoir être dépensées. Lorsque vous avez généré ce bloc, il a été diffusé sur le réseau pour être ajouté à la chaîne de blocs. S’il échoue a intégrer la chaîne, son état sera modifié en « non accepté » et il ne sera pas possible de le dépenser. Cela peut arriver occasionnellement si un autre nœud génère un bloc quelques secondes avant ou après vous. + + + + Debug information + Informations de débogage + + + + Transaction + Transaction + + + + Inputs + Entrées + + + + Amount + Montant + + + + true + vrai + + + + false + faux + + + + , has not been successfully broadcast yet + , n’a pas encore été diffusée avec succès + + + + Open for %n more block(s) + Ouvert pour %n bloc de plusOuvert pour %n blocs de plus + + + + unknown + inconnu + + + + TransactionDescDialog + + + Transaction details + Détails de la transaction + + + + This pane shows a detailed description of the transaction + Ce panneau affiche une description détaillée de la transaction + + + + TransactionTableModel + + + Date + Date + + + + Type + Type + + + + Address + Adresse + + + + Amount + Montant + + + + Open for %n more block(s) + Ouvert pour %n bloc de plusOuvert pour %n blocs de plus + + + + Open until %1 + Ouvert jusqu'à %1 + + + + Offline (%1 confirmations) + Hors ligne (%1 confirmations) + + + + Unconfirmed (%1 of %2 confirmations) + Non confirmée (%1 confirmations sur un total de %2) + + + + Confirmed (%1 confirmations) + Confirmée (%1 confirmations) + + + + Mined balance will be available when it matures in %n more block(s) + Le solde généré (mined) sera disponible quand il aura mûri dans %n blocLe solde généré (mined) sera disponible quand il aura mûri dans %n blocs + + + + This block was not received by any other nodes and will probably not be accepted! + Ce bloc n’a été reçu par aucun autre nœud et ne sera probablement pas accepté ! + + + + Generated but not accepted + Généré mais pas accepté + + + + Received with + Reçue avec + + + + Received from + Reçue de + + + + Sent to + Envoyée à + + + + Payment to yourself + Paiement à vous-même + + + + Mined + Extraction + + + + (n/a) + (indisponible) + + + + Transaction status. Hover over this field to show number of confirmations. + État de la transaction. Laissez le pointeur de la souris sur ce champ pour voir le nombre de confirmations. + + + + Date and time that the transaction was received. + Date et heure de réception de la transaction. + + + + Type of transaction. + Type de transaction. + + + + Destination address of transaction. + L’adresse de destination de la transaction. + + + + Amount removed from or added to balance. + Montant ajouté au, ou enlevé du, solde. + + + + TransactionView + + + + All + Toutes + + + + Today + Aujourd’hui + + + + This week + Cette semaine + + + + This month + Ce mois-ci + + + + Last month + Mois dernier + + + + This year + Cette année + + + + Range... + Intervalle… + + + + Received with + Reçues avec + + + + Sent to + Envoyées à + + + + To yourself + À vous-même + + + + Mined + Extraction + + + + Other + Autres + + + + Enter address or label to search + Entrez une adresse ou une étiquette à rechercher + + + + Min amount + Montant min + + + + Copy address + Copier l’adresse + + + + Copy label + Copier l’étiquette + + + + Copy amount + Copier le montant + + + + Copy transaction ID + Copier l'ID de la transaction + + + + Edit label + Éditer l’étiquette + + + + Show transaction details + Afficher les détails de la transaction + + + + Export Transaction Data + Exporter les données des transactions + + + + Comma separated file (*.csv) + Valeurs séparées par des virgules (*.csv) + + + + Confirmed + Confirmée + + + + Date + Date + + + + Type + Type + + + + Label + Étiquette + + + + Address + Adresse + + + + Amount + Montant + + + + ID + ID + + + + Error exporting + Erreur lors de l’exportation + + + + Could not write to file %1. + Impossible d'écrire dans le fichier %1. + + + + Range: + Intervalle : + + + + to + à + + + + WalletModel + + + Send Coins + Envoyer des pièces + + + + WalletView + + + &Export + &Exporter + + + + Export the data in the current tab to a file + Exporter les données de l'onglet courant vers un fichier + + + + Backup Wallet + Sauvegarder le porte-monnaie + + + + Wallet Data (*.dat) + Données de porte-monnaie (*.dat) + + + + Backup Failed + Échec de la sauvegarde + + + + There was an error trying to save the wallet data to the new location. + Une erreur est survenue lors de l'enregistrement des données de porte-monnaie à un nouvel endroit + + + + Backup Successful + Sauvegarde réussie + + + + The wallet data was successfully saved to the new location. + Les données de porte-monnaie ont été enregistrées avec succès sur le nouvel emplacement. + + + + bitcoin-core + + + CryptoLottoToken version + Version de CryptoLottoToken + + + + Usage: + Utilisation : + + + + Send command to -server or CryptoLottoTokend + Envoyer une commande à -server ou à CryptoLottoTokend + + + + List commands + Lister les commandes + + + + Get help for a command + Obtenir de l’aide pour une commande + + + + Options: + Options : + + + + Specify configuration file (default: CryptoLottoToken.conf) + Spécifier le fichier de configuration (par défaut : CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + Spécifier le fichier PID (par défaut : CryptoLottoTokend.pid) + + + + Specify data directory + Spécifier le répertoire de données + + + + Set database cache size in megabytes (default: 25) + Définir la taille du tampon en mégaoctets (par défaut : 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Écouter les connexions sur le <port> (par défaut : 22556 ou testnet : 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Garder au plus <n> connexions avec les pairs (par défaut : 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Se connecter à un nœud pour obtenir des adresses de pairs puis se déconnecter + + + + Specify your own public address + Spécifier votre propre adresse publique + + + + Threshold for disconnecting misbehaving peers (default: 100) + Seuil de déconnexion des pairs de mauvaise qualité (par défaut : 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Délai en secondes de refus de reconnexion aux pairs de mauvaise qualité (par défaut : 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + Une erreur est survenue lors de la mise en place du port RPC %u pour écouter sur IPv4 : %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Écouter les connexions JSON-RPC sur le <port> (par défaut : 22555 ou tesnet : 44555) + + + + Accept command line and JSON-RPC commands + Accepter les commandes de JSON-RPC et de la ligne de commande + + + + Run in the background as a daemon and accept commands + Fonctionner en arrière-plan en tant que démon et accepter les commandes + + + + Use the test network + Utiliser le réseau de test + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Accepter les connexions entrantes (par défaut : 1 si -proxy ou -connect ne sont pas présents) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + %s, vous devez définir un mot de passe rpc dans le fichier de configuration : +%s +Il vous est conseillé d'utiliser le mot de passe aléatoire suivant : +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(vous n'avez pas besoin de retenir ce mot de passe) +Le nom d'utilisateur et le mot de passe NE DOIVENT PAS être identiques. +Si le fichier n'existe pas, créez-le avec les droits de lecture accordés au propriétaire. +Il est aussi conseillé de régler alertnotify pour être prévenu des problèmes ; +par exemple : alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + Une erreur est survenue lors de la mise en place du port RPC %u pour écouter sur IPv6, retour à IPv4 : %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + Se lier à l'adresse donnée et toujours l'écouter. Utilisez la notation [host]:port pour l'IPv6 + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + Impossible d’obtenir un verrou sur le répertoire de données %s. CryptoLottoToken fonctionne probablement déjà. + + + + 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. + Erreur : la transaction a été rejetée ! Cela peut arriver si certaines pièces de votre porte-monnaie étaient déjà dépensées, par exemple si vous avez utilisé une copie de wallet.dat et les pièces ont été dépensées avec cette copie sans être marquées comme telles ici. + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + Erreur : cette transaction nécessite des frais de transaction d'au moins %s en raison de son montant, de sa complexité ou parce que des fonds reçus récemment sont utilisés ! + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + Exécuter une commande lorsqu'une alerte correspondante est reçue (%s dans la commande sera remplacé par le message) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Exécuter la commande lorsqu'une transaction de porte-monnaie change (%s dans la commande est remplacée par TxID) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Définir la taille maximale en octets des transactions prioritaires/à frais modiques (par défaut : 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + Ceci est une pré-version de test - utilisez à vos risques et périls - ne l'utilisez pas pour miner ou pour des applications marchandes + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Attention : -paytxfee est réglée sur un montant très élevé ! Il s'agit des frais de transaction que vous payerez si vous émettez une transaction. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + Avertissement : les transactions affichées pourraient être incorrectes ! Vous ou d'autres nœuds du réseau pourriez avoir besoin d'effectuer une mise à jour. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Attention : veuillez vérifier que l'heure et la date de votre ordinateur sont correctes ! Si votre horloge n'est pas à l'heure, CryptoLottoToken ne fonctionnera pas correctement. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Avertissement : une erreur est survenue lors de la lecture de wallet.dat ! Toutes les clefs ont été lues correctement mais les données de transaction ou les entrées du carnet d'adresses pourraient être incorrectes ou manquantes. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Avertissement : wallet.dat corrompu, données récupérées ! Le fichier wallet.dat original a été enregistré en tant que wallet.{horodatage}.bak dans %s ; si votre solde ou transactions sont incorrects vous devriez effectuer une restauration depuis une sauvegarde. + + + + Attempt to recover private keys from a corrupt wallet.dat + Tenter de récupérer les clefs privées d'un wallet.dat corrompu + + + + Block creation options: + Options de création des blocs : + + + + Connect only to the specified node(s) + Ne se connecter qu'au(x) nœud(s) spécifié(s) + + + + Corrupted block database detected + Base de données des blocs corrompue détectée + + + + Discover own IP address (default: 1 when listening and no -externalip) + Découvrir sa propre adresse IP (par défaut : 1 lors de l'écoute et si -externalip n'est pas présent) + + + + Do you want to rebuild the block database now? + Voulez-vous reconstruire la base de données des blocs maintenant ? + + + + Error initializing block database + Erreur lors de l'initialisation de la base de données des blocs + + + + Error initializing wallet database environment %s! + Erreur lors de l'initialisation de l'environnement de la base de données du porte-monnaie %s ! + + + + Error loading block database + Erreur du chargement de la base de données des blocs + + + + Error opening block database + Erreur lors de l'ouverture de la base de données + + + + Error: Disk space is low! + Erreur : l'espace disque est faible ! + + + + Error: Wallet locked, unable to create transaction! + Erreur : Porte-monnaie verrouillé, impossible de créer la transaction ! + + + + Error: system error: + Erreur : erreur système : + + + + Failed to listen on any port. Use -listen=0 if you want this. + Échec de l'écoute sur un port quelconque. Utilisez -listen=0 si vous voulez cela. + + + + Failed to read block info + La lecture des informations de bloc a échoué + + + + Failed to read block + La lecture du bloc a échoué + + + + Failed to sync block index + La synchronisation de l'index des blocs a échoué + + + + Failed to write block index + L''écriture de l'index des blocs a échoué + + + + Failed to write block info + L'écriture des informations du bloc a échoué + + + + Failed to write block + L'écriture du bloc a échoué + + + + Failed to write file info + L'écriture des informations de fichier a échoué + + + + Failed to write to coin database + L'écriture dans la base de données des pièces a échoué + + + + Failed to write transaction index + L'écriture de l'index des transactions a échoué + + + + Failed to write undo data + L'écriture des données d'annulation a échoué + + + + Find peers using DNS lookup (default: 1 unless -connect) + Trouver des pairs en utilisant la recherche DNS (par défaut : 1 sauf si -connect est utilisé) + + + + Generate coins (default: 0) + Générer des pièces (défaut: 0) + + + + How many blocks to check at startup (default: 288, 0 = all) + Nombre de blocs à vérifier au démarrage (par défaut : 288, 0 = tout) + + + + How thorough the block verification is (0-4, default: 3) + Niveau d'approfondissement de la vérification des blocs (0-4, par défaut : 3) + + + + Not enough file descriptors available. + Pas assez de descripteurs de fichiers disponibles. + + + + Rebuild block chain index from current blk000??.dat files + Reconstruire l'index de la chaîne des blocs à partir des fichiers blk000??.dat actuels + + + + Set the number of threads to service RPC calls (default: 4) + Définir le nombre d'exétrons pour desservir les appels RPC (par défaut : 4) + + + + Verifying blocks... + Vérification des blocs... + + + + Verifying wallet... + Vérification du porte-monnaie... + + + + Imports blocks from external blk000??.dat file + Importe des blocs depuis un fichier blk000??.dat externe + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + Définir le nombre de fils d’exécution pour la vérification des scripts (maximum 16, 0 = auto, < 0 = laisser ce nombre de cœurs libres, par défaut : 0) + + + + Information + Informations + + + + Invalid -tor address: '%s' + Adresse -tor invalide : « %s » + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + Montant invalide pour -minrelayfee=<montant> : « %s » + + + + Invalid amount for -mintxfee=<amount>: '%s' + Montant invalide pour -mintxfee=<montant> : « %s » + + + + Maintain a full transaction index (default: 0) + Maintenir un index complet des transactions (par défaut : 0) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Tampon maximal de réception par -connection, <n>*1000 octets (par défaut : 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Tampon maximal d'envoi par connexion, <n>*1000 octets (par défaut : 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + N'accepter que la chaîne de blocs correspondant aux points de vérification internes (par défaut : 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Se connecter uniquement aux nœuds du réseau <net> (IPv4, IPv6 ou Tor) + + + + Output extra debugging information. Implies all other -debug* options + Afficher des information de débogage supplémentaires. Cela signifie toutes les autres options -debug* + + + + Output extra network debugging information + Afficher des informations de débogage réseau supplémentaires + + + + Prepend debug output with timestamp (default: 1) + Faire précéder les données de débogage par un horodatage + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + Options SSL : (cf. le wiki de CryptoLottoToken pour les instructions de configuration du SSL) + + + + Select the version of socks proxy to use (4-5, default: 5) + Sélectionner la version du proxy socks à utiliser (4-5, 5 étant la valeur par défaut) + + + + Send trace/debug info to console instead of debug.log file + Envoyer les informations de débogage/trace à la console au lieu du fichier debug.log + + + + Send trace/debug info to debugger + Envoyer les informations de débogage/trace au débogueur + + + + Set maximum block size in bytes (default: 250000) + Définir la taille maximale des blocs en octets (par défaut : 250000) + + + + Set minimum block size in bytes (default: 0) + Définir la taille minimale des blocs en octets (par défaut : 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Réduire le fichier debug.log lors du démarrage du client (par défaut : 1 lorsque -debug n'est pas présent) + + + + Signing transaction failed + La signature de la transaction a échoué + + + + Specify connection timeout in milliseconds (default: 5000) + Spécifier le délai d'expiration de la connexion en millisecondes (par défaut : 5000) + + + + System error: + Erreur système : + + + + Transaction amount too small + Montant de la transaction trop bas + + + + Transaction amounts must be positive + Les montants de la transaction doivent être positifs + + + + Transaction too large + Transaction trop volumineuse + + + + Use UPnP to map the listening port (default: 0) + Utiliser l'UPnP pour rediriger le port d'écoute (par défaut : 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Utiliser l'UPnP pour rediriger le port d'écoute (par défaut : 1 lors de l'écoute) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Utiliser un proxy pour atteindre les services cachés de Tor (par défaut : même valeur que -proxy) + + + + Username for JSON-RPC connections + Nom d'utilisateur pour les connexions JSON-RPC + + + + Warning + Avertissement + + + + Warning: This version is obsolete, upgrade required! + Avertissement : cette version est obsolète, une mise à jour est nécessaire ! + + + + You need to rebuild the databases using -reindex to change -txindex + Vous devez reconstruire les bases de données avec -reindex pour modifier -txindex + + + + wallet.dat corrupt, salvage failed + wallet.dat corrompu, la récupération a échoué + + + + Password for JSON-RPC connections + Mot de passe pour les connexions JSON-RPC + + + + Allow JSON-RPC connections from specified IP address + Autoriser les connexions JSON-RPC depuis l'adresse IP spécifiée + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Envoyer des commandes au nœud fonctionnant à <ip> (par défaut : 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Exécuter la commande lorsque le meilleur bloc change (%s est remplacé par le hachage du bloc dans cmd) + + + + Upgrade wallet to latest format + Mettre à jour le format du porte-monnaie + + + + Set key pool size to <n> (default: 100) + Régler la taille de la plage de clefs sur <n> (par défaut : 100) + + + + Rescan the block chain for missing wallet transactions + Réanalyser la chaîne de blocs pour les transactions de porte-monnaie manquantes + + + + Use OpenSSL (https) for JSON-RPC connections + Utiliser OpenSSL (https) pour les connexions JSON-RPC + + + + Server certificate file (default: server.cert) + Fichier de certificat serveur (par défaut : server.cert) + + + + Server private key (default: server.pem) + Clef privée du serveur (par défaut : server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Clefs de chiffrement acceptables (par défaut : TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Ce message d'aide + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Impossible de se lier à %s sur cet ordinateur (bind a retourné l'erreur %d, %s) + + + + Connect through socks proxy + Connexion via un proxy socks + + + + Allow DNS lookups for -addnode, -seednode and -connect + Autoriser les recherches DNS pour -addnode, -seednode et -connect + + + + Loading addresses... + Chargement des adresses… + + + + Error loading wallet.dat: Wallet corrupted + Erreur lors du chargement de wallet.dat : porte-monnaie corrompu + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Erreur lors du chargement de wallet.dat : le porte-monnaie nécessite une version plus récente de CryptoLottoToken + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Le porte-monnaie nécessitait une réécriture : veuillez redémarrer CryptoLottoToken pour terminer l'opération + + + + Error loading wallet.dat + Erreur lors du chargement de wallet.dat + + + + Invalid -proxy address: '%s' + Adresse -proxy invalide : « %s » + + + + Unknown network specified in -onlynet: '%s' + Réseau inconnu spécifié sur -onlynet : « %s » + + + + Unknown -socks proxy version requested: %i + Version inconnue de proxy -socks demandée : %i + + + + Cannot resolve -bind address: '%s' + Impossible de résoudre l'adresse -bind : « %s » + + + + Cannot resolve -externalip address: '%s' + Impossible de résoudre l'adresse -externalip : « %s » + + + + Invalid amount for -paytxfee=<amount>: '%s' + Montant invalide pour -paytxfee=<montant> : « %s » + + + + Invalid amount + Montant invalide + + + + Insufficient funds + Fonds insuffisants + + + + Loading block index... + Chargement de l’index des blocs… + + + + Add a node to connect to and attempt to keep the connection open + Ajouter un nœud auquel se connecter et tenter de garder la connexion ouverte + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Impossible de se lier à %s sur cet ordinateur. CryptoLottoToken fonctionne probablement déjà. + + + + Fee per KB to add to transactions you send + Frais par Ko à ajouter aux transactions que vous enverrez + + + + Loading wallet... + Chargement du porte-monnaie… + + + + Cannot downgrade wallet + Impossible de revenir à une version antérieure du porte-monnaie + + + + Cannot write default address + Impossible d'écrire l'adresse par défaut + + + + Rescanning... + Nouvelle analyse… + + + + Done loading + Chargement terminé + + + + To use the %s option + Pour utiliser l'option %s + + + + Error + Erreur + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + Vous devez ajouter la ligne rpcpassword=<mot-de-passe> au fichier de configuration : +%s +Si le fichier n'existe pas, créez-le avec les droits de lecture seule accordés au propriétaire. + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_fr_CA.qm b/src/qt/locale/bitcoin_fr_CA.qm new file mode 100644 index 0000000..a029261 Binary files /dev/null and b/src/qt/locale/bitcoin_fr_CA.qm differ diff --git a/src/qt/locale/bitcoin_fr_CA.ts b/src/qt/locale/bitcoin_fr_CA.ts new file mode 100644 index 0000000..cc17363 --- /dev/null +++ b/src/qt/locale/bitcoin_fr_CA.ts @@ -0,0 +1,2922 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + A propos de CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> version + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +Ce logiciel est en phase expérimentale. + +Distribué sous licence MIT/X11, voir le fichier COPYING ou http://www.opensource.org/licenses/mit-license.php. + +Ce produit comprend des logiciels développés par le projet OpenSSL pour être utilisés dans la boîte à outils OpenSSL (http://www.openssl.org/), un logiciel cryptographique écrit par Eric Young (eay@cryptsoft.com) et un logiciel UPnP écrit par Thomas Bernard. + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + Carnet d'adresses + + + + Double-click to edit address or label + Double-cliquez afin de modifier l'adress ou l'étiquette + + + + Create a new address + Créer une nouvelle adresse + + + + Copy the currently selected address to the system clipboard + Copier l'adresse surligné a votre presse-papier + + + + &New Address + + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Ceux-ci sont vos adresses CryptoLottoToken qui vous permettent de recevoir des paiements. Vous pouvez en donner une différente à chaque expédieur afin de savoir qui vous payent. + + + + &Copy Address + + + + + Show &QR Code + + + + + Sign a message to prove you own a CryptoLottoToken address + + + + + Sign &Message + + + + + Delete the currently selected address from the list + + + + + Export the data in the current tab to a file + + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + + + + + &Verify Message + + + + + &Delete + &Supprimer + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + + + + + &Edit + + + + + Send &Coins + + + + + Export Address Book Data + Exporter les données du carnet d'adresses + + + + Comma separated file (*.csv) + + + + + Error exporting + + + + + Could not write to file %1. + + + + + AddressTableModel + + + Label + + + + + Address + + + + + (no label) + + + + + AskPassphraseDialog + + + Passphrase Dialog + + + + + Enter passphrase + + + + + New passphrase + + + + + Repeat new passphrase + + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + + + + + Encrypt wallet + + + + + This operation needs your wallet passphrase to unlock the wallet. + + + + + Unlock wallet + + + + + This operation needs your wallet passphrase to decrypt the wallet. + + + + + Decrypt wallet + + + + + Change passphrase + + + + + Enter the old and new passphrase to the wallet. + + + + + Confirm wallet encryption + + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + + + + + Are you sure you wish to encrypt your wallet? + + + + + 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. + + + + + + Warning: The Caps Lock key is on! + + + + + + Wallet encrypted + + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + + + + + + + + Wallet encryption failed + + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + + + + + + The supplied passphrases do not match. + + + + + Wallet unlock failed + + + + + + + The passphrase entered for the wallet decryption was incorrect. + + + + + Wallet decryption failed + + + + + Wallet passphrase was successfully changed. + + + + + BitcoinGUI + + + Sign &message... + + + + + Synchronizing with network... + + + + + &Overview + + + + + Show general overview of wallet + + + + + &Transactions + + + + + Browse transaction history + + + + + Edit the list of stored addresses and labels for sending + + + + + Show the list of addresses for receiving payments + + + + + E&xit + + + + + Quit application + + + + + Show information about CryptoLottoToken + + + + + About &Qt + + + + + Show information about Qt + + + + + &Options... + + + + + &Encrypt Wallet... + + + + + &Backup Wallet... + + + + + &Change Passphrase... + + + + + Importing blocks from disk... + + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + + + + + Modify configuration options for CryptoLottoToken + + + + + Backup wallet to another location + + + + + Change the passphrase used for wallet encryption + + + + + &Debug window + + + + + Open debugging and diagnostic console + + + + + &Verify message... + + + + + + CryptoLottoToken + + + + + Wallet + + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + &A propos de CryptoLottoToken + + + + &Show / Hide + + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + + + + + &Settings + + + + + &Help + + + + + Tabs toolbar + + + + + + [testnet] + + + + + CryptoLottoToken client + + + + + %n active connection(s) to CryptoLottoToken network + + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + + + + + Warning + + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + + + + + Catching up... + + + + + Confirm transaction fee + + + + + Sent transaction + + + + + Incoming transaction + + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + + + + + + URI handling + + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + + + + + EditAddressDialog + + + Edit Address + + + + + &Label + + + + + The label associated with this address book entry + + + + + &Address + + + + + The address associated with this address book entry. This can only be modified for sending addresses. + + + + + New receiving address + + + + + New sending address + + + + + Edit receiving address + + + + + Edit sending address + + + + + The entered address "%1" is already in the address book. + + + + + The entered address "%1" is not a valid CryptoLottoToken address. + + + + + Could not unlock wallet. + + + + + New key generation failed. + + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + + + + + version + + + + + Usage: + + + + + command-line options + + + + + UI options + + + + + Set language, for example "de_DE" (default: system locale) + + + + + Start minimized + + + + + Show splash screen on startup (default: 1) + + + + + OptionsDialog + + + Options + + + + + &Main + + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + + + + + Automatically start CryptoLottoToken after logging in to the system. + + + + + &Start CryptoLottoToken on system login + + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + + + + + Map port using &UPnP + + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + + + + + &Connect through SOCKS proxy: + + + + + Proxy &IP: + + + + + IP address of the proxy (e.g. 127.0.0.1) + + + + + &Port: + + + + + Port of the proxy (e.g. 9050) + + + + + SOCKS &Version: + + + + + SOCKS version of the proxy (e.g. 5) + + + + + &Window + + + + + Show only a tray icon after minimizing the window. + + + + + &Minimize to the tray instead of the taskbar + + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + + + + + M&inimize on close + + + + + &Display + + + + + User Interface &language: + + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + + + + + &Unit to show amounts in: + + + + + Choose the default subdivision unit to show in the interface and when sending coins. + + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + + + &Display addresses in transaction list + + + + + &OK + + + + + &Cancel + + + + + &Apply + + + + + default + + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + + + + + + This setting will take effect after restarting CryptoLottoToken. + + + + + The supplied proxy address is invalid. + + + + + OverviewPage + + + Form + + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + + + + + Balance: + + + + + Unconfirmed: + + + + + Wallet + + + + + Immature: + + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + + + + + Your current balance + + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + + + + + + out of sync + + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + + + + + Request Payment + + + + + Amount: + + + + + Label: + + + + + Message: + + + + + &Save As... + + + + + Error encoding URI into QR Code. + + + + + The entered amount is invalid, please check. + + + + + Resulting URI too long, try to reduce the text for label / message. + + + + + Save QR Code + + + + + PNG Images (*.png) + + + + + RPCConsole + + + Client name + + + + + + + + + + + + + + N/A + + + + + Client version + + + + + &Information + + + + + Using OpenSSL version + + + + + Startup time + + + + + Network + + + + + Number of connections + + + + + On testnet + + + + + Block chain + + + + + Current number of blocks + + + + + Estimated total blocks + + + + + Last block time + + + + + &Open + + + + + Command-line options + + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + + &Show + + + + + &Console + + + + + Build date + + + + + CryptoLottoToken - Debug window + + + + + CryptoLottoToken Core + + + + + Debug log file + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + + + + + Welcome to the CryptoLottoToken RPC console. + + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + + + + + Type <b>help</b> for an overview of available commands. + + + + + SendCoinsDialog + + + + + + + + + + Send Coins + + + + + Send to multiple recipients at once + + + + + Add &Recipient + + + + + Remove all transaction fields + + + + + Clear &All + + + + + Balance: + + + + + 123.456 BTC + + + + + Confirm the send action + + + + + S&end + + + + + <b>%1</b> to %2 (%3) + + + + + Confirm send coins + + + + + Are you sure you want to send %1? + + + + + and + + + + + The recipient address is not valid, please recheck. + + + + + The amount to pay must be larger than 0. + + + + + The amount exceeds your balance. + + + + + The total exceeds your balance when the %1 transaction fee is included. + + + + + Duplicate address found, can only send to each address once per send operation. + + + + + Error: Transaction creation failed! + + + + + 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. + + + + + SendCoinsEntry + + + Form + + + + + A&mount: + + + + + Pay &To: + + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + + + + + &Label: + + + + + Choose address from address book + + + + + Alt+A + + + + + Paste address from clipboard + + + + + Alt+P + + + + + Remove this recipient + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + + + + + &Sign Message + + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Choose an address from the address book + + + + + + Alt+A + + + + + Paste address from clipboard + + + + + Alt+P + + + + + Enter the message you want to sign here + + + + + Signature + + + + + Copy the current signature to the system clipboard + + + + + Sign the message to prove you own this CryptoLottoToken address + + + + + Sign &Message + + + + + Reset all sign message fields + + + + + + Clear &All + + + + + &Verify Message + + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + + + + + Verify &Message + + + + + Reset all verify message fields + + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Click "Sign Message" to generate signature + + + + + Enter CryptoLottoToken signature + + + + + + The entered address is invalid. + + + + + + + + Please check the address and try again. + + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + + + + + Private key for the entered address is not available. + + + + + Message signing failed. + + + + + Message signed. + + + + + The signature could not be decoded. + + + + + + Please check the signature and try again. + + + + + The signature did not match the message digest. + + + + + Message verification failed. + + + + + Message verified. + + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + + + + + TransactionDesc + + + Open until %1 + + + + + %1/offline + + + + + %1/unconfirmed + + + + + %1 confirmations + + + + + Status + + + + + , broadcast through %n node(s) + + + + + Date + + + + + Source + + + + + Generated + + + + + + From + + + + + + + To + + + + + + own address + + + + + label + + + + + + + + + Credit + + + + + matures in %n more block(s) + + + + + not accepted + + + + + + + + Debit + + + + + Transaction fee + + + + + Net amount + + + + + Message + + + + + Comment + + + + + Transaction ID + + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + + + + + Debug information + + + + + Transaction + + + + + Inputs + + + + + Amount + + + + + true + + + + + false + + + + + , has not been successfully broadcast yet + + + + + Open for %n more block(s) + + + + + unknown + + + + + TransactionDescDialog + + + Transaction details + + + + + This pane shows a detailed description of the transaction + + + + + TransactionTableModel + + + Date + + + + + Type + + + + + Address + + + + + Amount + + + + + Open for %n more block(s) + + + + + Open until %1 + + + + + Offline (%1 confirmations) + + + + + Unconfirmed (%1 of %2 confirmations) + + + + + Confirmed (%1 confirmations) + + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + + + + + Generated but not accepted + + + + + Received with + + + + + Received from + + + + + Sent to + + + + + Payment to yourself + + + + + Mined + + + + + (n/a) + + + + + Transaction status. Hover over this field to show number of confirmations. + + + + + Date and time that the transaction was received. + + + + + Type of transaction. + + + + + Destination address of transaction. + + + + + Amount removed from or added to balance. + + + + + TransactionView + + + + All + + + + + Today + + + + + This week + + + + + This month + + + + + Last month + + + + + This year + + + + + Range... + + + + + Received with + + + + + Sent to + + + + + To yourself + + + + + Mined + + + + + Other + + + + + Enter address or label to search + + + + + Min amount + + + + + Copy address + + + + + Copy label + + + + + Copy amount + + + + + Copy transaction ID + + + + + Edit label + + + + + Show transaction details + + + + + Export Transaction Data + + + + + Comma separated file (*.csv) + + + + + Confirmed + + + + + Date + + + + + Type + + + + + Label + + + + + Address + + + + + Amount + + + + + ID + + + + + Error exporting + + + + + Could not write to file %1. + + + + + Range: + + + + + to + + + + + WalletModel + + + Send Coins + + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + + + + + Usage: + + + + + Send command to -server or CryptoLottoTokend + + + + + List commands + + + + + Get help for a command + + + + + Options: + + + + + Specify configuration file (default: CryptoLottoToken.conf) + + + + + Specify pid file (default: CryptoLottoTokend.pid) + + + + + Specify data directory + + + + + Set database cache size in megabytes (default: 25) + + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + + + + + Maintain at most <n> connections to peers (default: 125) + + + + + Connect to a node to retrieve peer addresses, and disconnect + + + + + Specify your own public address + + + + + Threshold for disconnecting misbehaving peers (default: 100) + + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + + + + + Accept command line and JSON-RPC commands + + + + + Run in the background as a daemon and accept commands + + + + + Use the test network + + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + + + + + Connect only to the specified node(s) + + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + + + + + Select the version of socks proxy to use (4-5, default: 5) + + + + + Send trace/debug info to console instead of debug.log file + + + + + Send trace/debug info to debugger + + + + + Set maximum block size in bytes (default: 250000) + + + + + Set minimum block size in bytes (default: 0) + + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + + + + + Use UPnP to map the listening port (default: 1 when listening) + + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + + + + + Allow JSON-RPC connections from specified IP address + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + + + + + Upgrade wallet to latest format + + + + + Set key pool size to <n> (default: 100) + + + + + Rescan the block chain for missing wallet transactions + + + + + Use OpenSSL (https) for JSON-RPC connections + + + + + Server certificate file (default: server.cert) + + + + + Server private key (default: server.pem) + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + + This help message + + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + + + + + Connect through socks proxy + + + + + Allow DNS lookups for -addnode, -seednode and -connect + + + + + Loading addresses... + + + + + Error loading wallet.dat: Wallet corrupted + + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + + + + + Error loading wallet.dat + + + + + Invalid -proxy address: '%s' + + + + + Unknown network specified in -onlynet: '%s' + + + + + Unknown -socks proxy version requested: %i + + + + + Cannot resolve -bind address: '%s' + + + + + Cannot resolve -externalip address: '%s' + + + + + Invalid amount for -paytxfee=<amount>: '%s' + + + + + Invalid amount + + + + + Insufficient funds + + + + + Loading block index... + + + + + Add a node to connect to and attempt to keep the connection open + + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + + + + + Fee per KB to add to transactions you send + + + + + Loading wallet... + + + + + Cannot downgrade wallet + + + + + Cannot write default address + + + + + Rescanning... + + + + + Done loading + + + + + To use the %s option + + + + + Error + + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_gu_IN.ts b/src/qt/locale/bitcoin_gu_IN.ts new file mode 100644 index 0000000..6f8e301 --- /dev/null +++ b/src/qt/locale/bitcoin_gu_IN.ts @@ -0,0 +1,2917 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + બીટકોઈન વિષે + + + + <b>CryptoLottoToken</b> version + + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + + + + + Double-click to edit address or label + + + + + Create a new address + + + + + Copy the currently selected address to the system clipboard + + + + + &New Address + + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + + + + + &Copy Address + + + + + Show &QR Code + + + + + Sign a message to prove you own a CryptoLottoToken address + + + + + Sign &Message + + + + + Delete the currently selected address from the list + + + + + Export the data in the current tab to a file + + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + + + + + &Verify Message + + + + + &Delete + + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + + + + + &Edit + + + + + Send &Coins + + + + + Export Address Book Data + + + + + Comma separated file (*.csv) + + + + + Error exporting + + + + + Could not write to file %1. + + + + + AddressTableModel + + + Label + + + + + Address + + + + + (no label) + + + + + AskPassphraseDialog + + + Passphrase Dialog + + + + + Enter passphrase + + + + + New passphrase + + + + + Repeat new passphrase + + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + + + + + Encrypt wallet + + + + + This operation needs your wallet passphrase to unlock the wallet. + + + + + Unlock wallet + + + + + This operation needs your wallet passphrase to decrypt the wallet. + + + + + Decrypt wallet + + + + + Change passphrase + + + + + Enter the old and new passphrase to the wallet. + + + + + Confirm wallet encryption + + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + + + + + Are you sure you wish to encrypt your wallet? + + + + + 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. + + + + + + Warning: The Caps Lock key is on! + + + + + + Wallet encrypted + + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + + + + + + + + Wallet encryption failed + + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + + + + + + The supplied passphrases do not match. + + + + + Wallet unlock failed + + + + + + + The passphrase entered for the wallet decryption was incorrect. + + + + + Wallet decryption failed + + + + + Wallet passphrase was successfully changed. + + + + + BitcoinGUI + + + Sign &message... + + + + + Synchronizing with network... + + + + + &Overview + + + + + Show general overview of wallet + + + + + &Transactions + + + + + Browse transaction history + + + + + Edit the list of stored addresses and labels for sending + + + + + Show the list of addresses for receiving payments + + + + + E&xit + + + + + Quit application + + + + + Show information about CryptoLottoToken + + + + + About &Qt + + + + + Show information about Qt + + + + + &Options... + + + + + &Encrypt Wallet... + + + + + &Backup Wallet... + + + + + &Change Passphrase... + + + + + Importing blocks from disk... + + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + + + + + Modify configuration options for CryptoLottoToken + + + + + Backup wallet to another location + + + + + Change the passphrase used for wallet encryption + + + + + &Debug window + + + + + Open debugging and diagnostic console + + + + + &Verify message... + + + + + + CryptoLottoToken + + + + + Wallet + + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + + + + + &Show / Hide + + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + + + + + &Settings + + + + + &Help + + + + + Tabs toolbar + + + + + + [testnet] + + + + + CryptoLottoToken client + + + + + %n active connection(s) to CryptoLottoToken network + + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + + + + + Warning + + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + + + + + Catching up... + + + + + Confirm transaction fee + + + + + Sent transaction + + + + + Incoming transaction + + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + + + + + + URI handling + + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + + + + + EditAddressDialog + + + Edit Address + + + + + &Label + + + + + The label associated with this address book entry + + + + + &Address + + + + + The address associated with this address book entry. This can only be modified for sending addresses. + + + + + New receiving address + + + + + New sending address + + + + + Edit receiving address + + + + + Edit sending address + + + + + The entered address "%1" is already in the address book. + + + + + The entered address "%1" is not a valid CryptoLottoToken address. + + + + + Could not unlock wallet. + + + + + New key generation failed. + + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + + + + + version + + + + + Usage: + + + + + command-line options + + + + + UI options + + + + + Set language, for example "de_DE" (default: system locale) + + + + + Start minimized + + + + + Show splash screen on startup (default: 1) + + + + + OptionsDialog + + + Options + + + + + &Main + + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + + + + + Automatically start CryptoLottoToken after logging in to the system. + + + + + &Start CryptoLottoToken on system login + + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + + + + + Map port using &UPnP + + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + + + + + &Connect through SOCKS proxy: + + + + + Proxy &IP: + + + + + IP address of the proxy (e.g. 127.0.0.1) + + + + + &Port: + + + + + Port of the proxy (e.g. 9050) + + + + + SOCKS &Version: + + + + + SOCKS version of the proxy (e.g. 5) + + + + + &Window + + + + + Show only a tray icon after minimizing the window. + + + + + &Minimize to the tray instead of the taskbar + + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + + + + + M&inimize on close + + + + + &Display + + + + + User Interface &language: + + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + + + + + &Unit to show amounts in: + + + + + Choose the default subdivision unit to show in the interface and when sending coins. + + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + + + &Display addresses in transaction list + + + + + &OK + + + + + &Cancel + + + + + &Apply + + + + + default + + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + + + + + + This setting will take effect after restarting CryptoLottoToken. + + + + + The supplied proxy address is invalid. + + + + + OverviewPage + + + Form + + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + + + + + Balance: + + + + + Unconfirmed: + + + + + Wallet + + + + + Immature: + + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + + + + + Your current balance + + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + + + + + + out of sync + + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + + + + + Request Payment + + + + + Amount: + + + + + Label: + + + + + Message: + + + + + &Save As... + + + + + Error encoding URI into QR Code. + + + + + The entered amount is invalid, please check. + + + + + Resulting URI too long, try to reduce the text for label / message. + + + + + Save QR Code + + + + + PNG Images (*.png) + + + + + RPCConsole + + + Client name + + + + + + + + + + + + + + N/A + + + + + Client version + + + + + &Information + + + + + Using OpenSSL version + + + + + Startup time + + + + + Network + + + + + Number of connections + + + + + On testnet + + + + + Block chain + + + + + Current number of blocks + + + + + Estimated total blocks + + + + + Last block time + + + + + &Open + + + + + Command-line options + + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + + &Show + + + + + &Console + + + + + Build date + + + + + CryptoLottoToken - Debug window + + + + + CryptoLottoToken Core + + + + + Debug log file + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + + + + + Welcome to the CryptoLottoToken RPC console. + + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + + + + + Type <b>help</b> for an overview of available commands. + + + + + SendCoinsDialog + + + + + + + + + + Send Coins + + + + + Send to multiple recipients at once + + + + + Add &Recipient + + + + + Remove all transaction fields + + + + + Clear &All + + + + + Balance: + + + + + 123.456 BTC + + + + + Confirm the send action + + + + + S&end + + + + + <b>%1</b> to %2 (%3) + + + + + Confirm send coins + + + + + Are you sure you want to send %1? + + + + + and + + + + + The recipient address is not valid, please recheck. + + + + + The amount to pay must be larger than 0. + + + + + The amount exceeds your balance. + + + + + The total exceeds your balance when the %1 transaction fee is included. + + + + + Duplicate address found, can only send to each address once per send operation. + + + + + Error: Transaction creation failed! + + + + + 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. + + + + + SendCoinsEntry + + + Form + + + + + A&mount: + + + + + Pay &To: + + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + + + + + &Label: + + + + + Choose address from address book + + + + + Alt+A + + + + + Paste address from clipboard + + + + + Alt+P + + + + + Remove this recipient + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + + + + + &Sign Message + + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Choose an address from the address book + + + + + + Alt+A + + + + + Paste address from clipboard + + + + + Alt+P + + + + + Enter the message you want to sign here + + + + + Signature + + + + + Copy the current signature to the system clipboard + + + + + Sign the message to prove you own this CryptoLottoToken address + + + + + Sign &Message + + + + + Reset all sign message fields + + + + + + Clear &All + + + + + &Verify Message + + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + + + + + Verify &Message + + + + + Reset all verify message fields + + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Click "Sign Message" to generate signature + + + + + Enter CryptoLottoToken signature + + + + + + The entered address is invalid. + + + + + + + + Please check the address and try again. + + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + + + + + Private key for the entered address is not available. + + + + + Message signing failed. + + + + + Message signed. + + + + + The signature could not be decoded. + + + + + + Please check the signature and try again. + + + + + The signature did not match the message digest. + + + + + Message verification failed. + + + + + Message verified. + + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + + + + + TransactionDesc + + + Open until %1 + + + + + %1/offline + + + + + %1/unconfirmed + + + + + %1 confirmations + + + + + Status + + + + + , broadcast through %n node(s) + + + + + Date + + + + + Source + + + + + Generated + + + + + + From + + + + + + + To + + + + + + own address + + + + + label + + + + + + + + + Credit + + + + + matures in %n more block(s) + + + + + not accepted + + + + + + + + Debit + + + + + Transaction fee + + + + + Net amount + + + + + Message + + + + + Comment + + + + + Transaction ID + + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + + + + + Debug information + + + + + Transaction + + + + + Inputs + + + + + Amount + + + + + true + + + + + false + + + + + , has not been successfully broadcast yet + + + + + Open for %n more block(s) + + + + + unknown + + + + + TransactionDescDialog + + + Transaction details + + + + + This pane shows a detailed description of the transaction + + + + + TransactionTableModel + + + Date + + + + + Type + + + + + Address + + + + + Amount + + + + + Open for %n more block(s) + + + + + Open until %1 + + + + + Offline (%1 confirmations) + + + + + Unconfirmed (%1 of %2 confirmations) + + + + + Confirmed (%1 confirmations) + + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + + + + + Generated but not accepted + + + + + Received with + + + + + Received from + + + + + Sent to + + + + + Payment to yourself + + + + + Mined + + + + + (n/a) + + + + + Transaction status. Hover over this field to show number of confirmations. + + + + + Date and time that the transaction was received. + + + + + Type of transaction. + + + + + Destination address of transaction. + + + + + Amount removed from or added to balance. + + + + + TransactionView + + + + All + + + + + Today + + + + + This week + + + + + This month + + + + + Last month + + + + + This year + + + + + Range... + + + + + Received with + + + + + Sent to + + + + + To yourself + + + + + Mined + + + + + Other + + + + + Enter address or label to search + + + + + Min amount + + + + + Copy address + + + + + Copy label + + + + + Copy amount + + + + + Copy transaction ID + + + + + Edit label + + + + + Show transaction details + + + + + Export Transaction Data + + + + + Comma separated file (*.csv) + + + + + Confirmed + + + + + Date + + + + + Type + + + + + Label + + + + + Address + + + + + Amount + + + + + ID + + + + + Error exporting + + + + + Could not write to file %1. + + + + + Range: + + + + + to + + + + + WalletModel + + + Send Coins + + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + + + + + Usage: + + + + + Send command to -server or CryptoLottoTokend + + + + + List commands + + + + + Get help for a command + + + + + Options: + + + + + Specify configuration file (default: CryptoLottoToken.conf) + + + + + Specify pid file (default: CryptoLottoTokend.pid) + + + + + Specify data directory + + + + + Set database cache size in megabytes (default: 25) + + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + + + + + Maintain at most <n> connections to peers (default: 125) + + + + + Connect to a node to retrieve peer addresses, and disconnect + + + + + Specify your own public address + + + + + Threshold for disconnecting misbehaving peers (default: 100) + + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + + + + + Accept command line and JSON-RPC commands + + + + + Run in the background as a daemon and accept commands + + + + + Use the test network + + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + + + + + Connect only to the specified node(s) + + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + + + + + Select the version of socks proxy to use (4-5, default: 5) + + + + + Send trace/debug info to console instead of debug.log file + + + + + Send trace/debug info to debugger + + + + + Set maximum block size in bytes (default: 250000) + + + + + Set minimum block size in bytes (default: 0) + + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + + + + + Use UPnP to map the listening port (default: 1 when listening) + + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + + + + + Allow JSON-RPC connections from specified IP address + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + + + + + Upgrade wallet to latest format + + + + + Set key pool size to <n> (default: 100) + + + + + Rescan the block chain for missing wallet transactions + + + + + Use OpenSSL (https) for JSON-RPC connections + + + + + Server certificate file (default: server.cert) + + + + + Server private key (default: server.pem) + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + + This help message + + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + + + + + Connect through socks proxy + + + + + Allow DNS lookups for -addnode, -seednode and -connect + + + + + Loading addresses... + + + + + Error loading wallet.dat: Wallet corrupted + + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + + + + + Error loading wallet.dat + + + + + Invalid -proxy address: '%s' + + + + + Unknown network specified in -onlynet: '%s' + + + + + Unknown -socks proxy version requested: %i + + + + + Cannot resolve -bind address: '%s' + + + + + Cannot resolve -externalip address: '%s' + + + + + Invalid amount for -paytxfee=<amount>: '%s' + + + + + Invalid amount + + + + + Insufficient funds + + + + + Loading block index... + + + + + Add a node to connect to and attempt to keep the connection open + + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + + + + + Fee per KB to add to transactions you send + + + + + Loading wallet... + + + + + Cannot downgrade wallet + + + + + Cannot write default address + + + + + Rescanning... + + + + + Done loading + + + + + To use the %s option + + + + + Error + + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_he.qm b/src/qt/locale/bitcoin_he.qm new file mode 100644 index 0000000..1f33949 Binary files /dev/null and b/src/qt/locale/bitcoin_he.qm differ diff --git a/src/qt/locale/bitcoin_he.ts b/src/qt/locale/bitcoin_he.ts new file mode 100644 index 0000000..f054807 --- /dev/null +++ b/src/qt/locale/bitcoin_he.ts @@ -0,0 +1,2937 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + אודות לייטקוין + + + + <b>CryptoLottoToken</b> version + גרסת <b>לייטקוין</b> + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +זוהי תוכנה ניסיונית. + +מופצת תחת רישיון התוכנה MIT/X11, ראה את הקובץ המצורף COPYING או http://www.opensource.org/licenses/mit-license.php. + +המוצר הזה כולל תוכנה שפותחה ע"י פרויקט OpenSSL לשימוש בתיבת הכלים OpenSSL (http://www.openssl.org/) ותוכנה קריפטוגרפית שנכתבה ע"י אריק יאנג (eay@cryptsoft.com) ותוכנת UPnP שנכתבה ע"י תומס ברנרד. + + + + Copyright + זכויות יוצרים + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + פנקס כתובות + + + + Double-click to edit address or label + לחץ לחיצה כפולה לערוך כתובת או תוית + + + + Create a new address + יצירת כתובת חדשה + + + + Copy the currently selected address to the system clipboard + העתק את הכתובת המסומנת ללוח העריכה + + + + &New Address + כתובת חדשה + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + אלה כתובת הלייטקוין שלך עבור קבלת תשלומים. ייתכן ותרצה לתת כתובת שונה לכל שולח כדי שתוכל לעקוב אחר מי משלם לך. + + + + &Copy Address + העתק כתובת + + + + Show &QR Code + הצג &קוד QR + + + + Sign a message to prove you own a CryptoLottoToken address + חתום על הודעה בכדי להוכיח כי אתה הבעלים של כתובת לייטקוין. + + + + Sign &Message + חתום על הודעה + + + + Delete the currently selected address from the list + מחק את הכתובת שנבחרה מהרשימה + + + + Export the data in the current tab to a file + יצוא הנתונים בטאב הנוכחי לקובץ + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + אמת הודעה בכדי להבטיח שהיא נחתמה עם כתובת לייטקוין מסוימת. + + + + &Verify Message + אמת הודעה + + + + &Delete + מחק + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + אלה כתובת הלייטקוין שלך עבור שליחת תשלומים. תמיד בדוק את מספר ואת כתובות מקבלי התשלומים לפני שליחת מטבעות. + + + + Copy &Label + העתק תוית + + + + &Edit + עריכה + + + + Send &Coins + שלח מטבעות + + + + Export Address Book Data + יצוא נתוני פנקס כתובות + + + + Comma separated file (*.csv) + קובץ מופרד בפסיקים (*.csv) + + + + Error exporting + שגיאה ביצוא + + + + Could not write to file %1. + לא מסוגל לכתוב לקובץ %1. + + + + AddressTableModel + + + Label + תוית + + + + Address + כתובת + + + + (no label) + (ללא תוית) + + + + AskPassphraseDialog + + + Passphrase Dialog + שיח סיסמא + + + + Enter passphrase + הכנס סיסמא + + + + New passphrase + סיסמה חדשה + + + + Repeat new passphrase + חזור על הסיסמה החדשה + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + הכנס את הסיסמה החדשה לארנק. <br/>אנא השתמש בסיסמה המכילה <b>10 תוים אקראיים או יותר</b>, או <b>שמונה מילים או יותר</b>. + + + + Encrypt wallet + הצפן ארנק + + + + This operation needs your wallet passphrase to unlock the wallet. + הפעולה הזו דורשת את סיסמת הארנק שלך בשביל לפתוח את הארנק. + + + + Unlock wallet + פתיחת ארנק + + + + This operation needs your wallet passphrase to decrypt the wallet. + הפעולה הזו דורשת את סיסמת הארנק שלך בשביל לפענח את הארנק. + + + + Decrypt wallet + פענוח ארנק + + + + Change passphrase + שינוי סיסמה + + + + Enter the old and new passphrase to the wallet. + הכנס את הסיסמות הישנה והחדשה לארנק. + + + + Confirm wallet encryption + אשר הצפנת ארנק + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + אזהרה: אם אתה מצפין את הארנק ומאבד את הסיסמא, אתה <b>תאבד את כל הלייטקוינים שלך</b>! + + + + Are you sure you wish to encrypt your wallet? + האם אתה בטוח שברצונך להצפין את הארנק? + + + + 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. + חשוב! כל גיבוי קודם שעשית לארנק שלך יש להחליף עם קובץ הארנק המוצפן שזה עתה נוצר. מסיבות אבטחה, גיבויים קודמים של קובץ הארנק הלא-מוצפן יהפכו לחסרי שימוש ברגע שתתחיל להשתמש בארנק החדש המוצפן. + + + + + Warning: The Caps Lock key is on! + זהירות: מקש Caps Lock מופעל! + + + + + Wallet encrypted + הארנק הוצפן + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + לייטקוין ייסגר עכשיו כדי להשלים את תהליך ההצפנה. זכור שהצפנת הארנק שלך אינו יכול להגן באופן מלא על הלייטקוינים שלך מתוכנות זדוניות המושתלות על המחשב. + + + + + + + Wallet encryption failed + הצפנת הארנק נכשלה + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + הצפנת הארנק נכשלה עקב שגיאה פנימית. הארנק שלך לא הוצפן. + + + + + The supplied passphrases do not match. + הסיסמות שניתנו אינן תואמות. + + + + Wallet unlock failed + פתיחת הארנק נכשלה + + + + + + The passphrase entered for the wallet decryption was incorrect. + הסיסמה שהוכנסה לפענוח הארנק שגויה. + + + + Wallet decryption failed + פענוח הארנק נכשל + + + + Wallet passphrase was successfully changed. + סיסמת הארנק שונתה בהצלחה. + + + + BitcoinGUI + + + Sign &message... + חתום על הודעה + + + + Synchronizing with network... + מסתנכרן עם הרשת... + + + + &Overview + &סקירה + + + + Show general overview of wallet + הצג סקירה כללית של הארנק + + + + &Transactions + &פעולות + + + + Browse transaction history + דפדף בהיסטוריית הפעולות + + + + Edit the list of stored addresses and labels for sending + ערוך את רשימת הכתובות והתויות + + + + Show the list of addresses for receiving payments + הצג את רשימת הכתובות לקבלת תשלומים + + + + E&xit + י&ציאה + + + + Quit application + סגור תוכנה + + + + Show information about CryptoLottoToken + הצג מידע על לייטקוין + + + + About &Qt + אודות Qt + + + + Show information about Qt + הצג מידע על Qt + + + + &Options... + &אפשרויות + + + + &Encrypt Wallet... + הצפן ארנק + + + + &Backup Wallet... + גיבוי ארנק + + + + &Change Passphrase... + שנה סיסמא + + + + Importing blocks from disk... + מייבא בלוקים מהדיסק... + + + + Reindexing blocks on disk... + מחדש את אינדקס הבלוקים בדיסק... + + + + Send coins to a CryptoLottoToken address + שלח מטבעות לכתובת לייטקוין + + + + Modify configuration options for CryptoLottoToken + שנה אפשרויות תצורה עבור לייטקוין + + + + Backup wallet to another location + גיבוי הארנק למקום אחר + + + + Change the passphrase used for wallet encryption + שנה את הסיסמה להצפנת הארנק + + + + &Debug window + חלון ניפוי + + + + Open debugging and diagnostic console + פתח את לוח הבקרה לאבחון וניפוי + + + + &Verify message... + אמת הודעה... + + + + + CryptoLottoToken + לייטקוין + + + + Wallet + ארנק + + + + &Send + ושלח + + + + &Receive + וקבל + + + + &Addresses + וכתובות + + + + &About CryptoLottoToken + אודות לייטקוין + + + + &Show / Hide + הצג / הסתר + + + + Show or hide the main Window + הצג או הסתר את החלון הראשי + + + + Encrypt the private keys that belong to your wallet + הצפן את המפתחות הפרטיים ששייכים לארנק שלך + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + חתום על הודעות עם כתובות הלייטקוין שלך כדי להוכיח שהן בבעלותך + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + אמת הודעות כדי להבטיח שהן נחתמו עם כתובת לייטקוין מסוימות + + + + &File + &קובץ + + + + &Settings + ה&גדרות + + + + &Help + &עזרה + + + + Tabs toolbar + סרגל כלים טאבים + + + + + [testnet] + [רשת-בדיקה] + + + + CryptoLottoToken client + תוכנת לייטקוין + + + + %n active connection(s) to CryptoLottoToken network + חיבור פעיל אחד לרשת הלייטקוין%n חיבורים פעילים לרשת הלייטקוין + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + 1% מתוך 2% (משוער) בלוקים של הסטוריית פעולת עובדו + + + + Processed %1 blocks of transaction history. + הושלם עיבוד של %1 בלוקים של היסטוריית פעולות. + + + + %n hour(s) + %n שעה%n שעות + + + + %n day(s) + %n יום%n ימים + + + + %n week(s) + %n שבוע%n שבועות + + + + %1 behind + 1% מאחור + + + + Last received block was generated %1 ago. + הבלוק האחרון שהתקבל נוצר לפני %1 + + + + Transactions after this will not yet be visible. + לאחר זאת פעולות נספות טרם יהיו גלויות + + + + Error + שגיאה + + + + Warning + אזהרה + + + + Information + מידע + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + פעולה זו חורגת מגבולות הגודל. עדיין באפשרותך לשלוח אותה תמורת עמלה של %1, המיועדת לצמתים שמעבדים את הפעולה שלך ועוזרת לתמוך ברשת. האם ברצונך לשלם את העמלה? + + + + Up to date + עדכני + + + + Catching up... + מתעדכן... + + + + Confirm transaction fee + אשר עמלת פעולה + + + + Sent transaction + פעולה שנשלחה + + + + Incoming transaction + פעולה שהתקבלה + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + תאריך: %1 +כמות: %2 +סוג: %3 +כתובת: %4 + + + + + URI handling + תפעול URI + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + לא ניתן לנתח URI! זה יכול להיגרם כתוצאה מכתובת לייטקוין לא תקינה או פרמטרי URI חסרי צורה תקינה. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + הארנק <b>מוצפן</b> וכרגע <b>פתוח</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + הארנק <b>מוצפן</b> וכרגע <b>נעול</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + שגיאה סופנית אירעה. לייטקוין אינו יכול להמשיך לפעול בבטחה ולכן ייסגר. + + + + ClientModel + + + Network Alert + אזעקת רשת + + + + EditAddressDialog + + + Edit Address + ערוך כתובת + + + + &Label + ת&וית + + + + The label associated with this address book entry + התוית המשויכת לרשומה הזו בפנקס הכתובות + + + + &Address + &כתובת + + + + The address associated with this address book entry. This can only be modified for sending addresses. + הכתובת המשויכת לרשומה זו בפנקס הכתובות. ניתן לשנות זאת רק עבור כתובות לשליחה. + + + + New receiving address + כתובת חדשה לקבלה + + + + New sending address + כתובת חדשה לשליחה + + + + Edit receiving address + ערוך כתובת לקבלה + + + + Edit sending address + ערוך כתובת לשליחה + + + + The entered address "%1" is already in the address book. + הכתובת שהכנסת "%1" כבר נמצאת בפנקס הכתובות. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + הכתובת שהוכנסה "%1" אינה כתובת לייטקוין תקינה. + + + + Could not unlock wallet. + פתיחת הארנק נכשלה. + + + + New key generation failed. + יצירת מפתח חדש נכשלה. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + גרסה + + + + Usage: + שימוש: + + + + command-line options + אפשרויות שורת פקודה + + + + UI options + אפשרויות ממשק + + + + Set language, for example "de_DE" (default: system locale) + קבע שפה, למשל "he_il" (ברירת מחדל: שפת המערכת) + + + + Start minimized + התחל ממוזער + + + + Show splash screen on startup (default: 1) + הצג מסך פתיחה בעת הפעלה (ברירת מחדל: 1) + + + + OptionsDialog + + + Options + אפשרויות + + + + &Main + ראשי + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + שלם &עמלת פעולה + + + + Automatically start CryptoLottoToken after logging in to the system. + הפעל את לייטקוין באופן עצמאי לאחר התחברות למערכת. + + + + &Start CryptoLottoToken on system login + התחל את לייטקוין בעת התחברות למערכת + + + + Reset all client options to default. + אפס כל אפשרויות התוכנה לברירת המחדל. + + + + &Reset Options + איפוס אפשרויות + + + + &Network + רשת + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + פתח את פורט לייטקוין בנתב באופן אוטומטי. עובד רק אם UPnP מאופשר ונתמך ע"י הנתב. + + + + Map port using &UPnP + מיפוי פורט באמצעות UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + התחבר לרשת הלייטקוין דרך פרוקסי SOCKS (למשל בעת התחברות דרך Tor). + + + + &Connect through SOCKS proxy: + התחבר דרך פרוקסי SOCKS + + + + Proxy &IP: + כתובת IP של פרוקסי: + + + + IP address of the proxy (e.g. 127.0.0.1) + כתובת האינטרנט של הפרוקסי (למשל 127.0.0.1) + + + + &Port: + פורט: + + + + Port of the proxy (e.g. 9050) + הפורט של הפרוקסי (למשל 9050) + + + + SOCKS &Version: + גרסת SOCKS: + + + + SOCKS version of the proxy (e.g. 5) + גרסת SOCKS של הפרוקסי (למשל 5) + + + + &Window + חלון + + + + Show only a tray icon after minimizing the window. + הצג סמל מגש בלבד לאחר מזעור החלון. + + + + &Minimize to the tray instead of the taskbar + מ&זער למגש במקום לשורת המשימות + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + מזער את התוכנה במקום לצאת ממנה כשהחלון נסגר. כשאפשרות זו פעילה, התוכנה תיסגר רק לאחר בחירת יציאה מהתפריט. + + + + M&inimize on close + מזער בעת סגירה + + + + &Display + תצוגה + + + + User Interface &language: + שפת ממשק המשתמש: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + ניתן לקבוע כאן את שפת ממשק המשתמש. הגדרה זו תחול לאחר הפעלה מחדש של לייטקוין. + + + + &Unit to show amounts in: + יחידת מדידה להצגת כמויות: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + בחר את ברירת המחדל ליחידת החלוקה אשר תוצג בממשק ובעת שליחת מטבעות. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + האם להציג כתובות לייטקוין ברשימת הפעולות או לא. + + + + &Display addresses in transaction list + הצג כתובות ברשימת הפעולות + + + + &OK + אישור + + + + &Cancel + ביטול + + + + &Apply + יישום + + + + default + ברירת מחדל + + + + Confirm options reset + אשר את איפוס האפשרויות + + + + Some settings may require a client restart to take effect. + כמה מההגדרות עשויות לדרוש אתחול התוכנה כדי להיכנס לפועל. + + + + Do you want to proceed? + האם ברצונך להמשיך? + + + + + Warning + אזהרה + + + + + This setting will take effect after restarting CryptoLottoToken. + הגדרה זו תחול לאחר הפעלה מחדש של לייטקוין. + + + + The supplied proxy address is invalid. + כתובת הפרוקסי שסופקה אינה תקינה. + + + + OverviewPage + + + Form + טופס + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + המידע המוצג עשוי להיות מיושן. הארנק שלך מסתנכרן באופן אוטומטי עם רשת הלייטקוין לאחר כינון חיבור, אך התהליך טרם הסתיים. + + + + Balance: + יתרה: + + + + Unconfirmed: + ממתין לאישור: + + + + Wallet + ארנק + + + + Immature: + לא בשל: + + + + Mined balance that has not yet matured + מאזן שנכרה וטרם הבשיל + + + + <b>Recent transactions</b> + <b>פעולות אחרונות</b> + + + + Your current balance + היתרה הנוכחית שלך + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + הסכום הכולל של פעולות שטרם אושרו, ועוד אינן נספרות בחישוב היתרה הנוכחית + + + + + out of sync + לא מסונכרן + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + לא ניתן להתחיל את לייטקוין: מפעיל לחץ-לתשלום + + + + QRCodeDialog + + + QR Code Dialog + שיח קוד QR + + + + Request Payment + בקש תשלום + + + + Amount: + כמות: + + + + Label: + תוית: + + + + Message: + הודעה: + + + + &Save As... + &שמור בשם... + + + + Error encoding URI into QR Code. + שגיאה בקידוד URI לקוד QR + + + + The entered amount is invalid, please check. + הכמות שהוכנסה אינה תקינה, אנא ודא. + + + + Resulting URI too long, try to reduce the text for label / message. + המזהה המתקבל ארוך מדי, נסה להפחית את הטקסט בתוית / הודעה. + + + + Save QR Code + שמור קוד QR + + + + PNG Images (*.png) + תמונות PNG (*.png) + + + + RPCConsole + + + Client name + שם ממשק + + + + + + + + + + + + + N/A + N/A + + + + Client version + גרסת ממשק + + + + &Information + מידע + + + + Using OpenSSL version + משתמש ב-OpenSSL גרסה + + + + Startup time + זמן אתחול + + + + Network + רשת + + + + Number of connections + מספר חיבורים + + + + On testnet + ברשת הבדיקה + + + + Block chain + שרשרת הבלוקים + + + + Current number of blocks + מספר הבלוקים הנוכחי + + + + Estimated total blocks + מספר כולל משוער של בלוקים + + + + Last block time + זמן הבלוק האחרון + + + + &Open + פתח + + + + Command-line options + אפשרויות שורת פקודה + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + הצג את הודעה העזרה של CryptoLottoToken-qt כדי לקבל רשימה של אפשרויות שורת פקודה של לייטקוין. + + + + &Show + הצג + + + + &Console + לוח בקרה + + + + Build date + תאריך בניה + + + + CryptoLottoToken - Debug window + לייטקוין - חלון ניפוי + + + + CryptoLottoToken Core + ליבת לייטקוין + + + + Debug log file + קובץ יומן ניפוי + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + פתח את קובץ יומן הניפוי מתיקיית הנתונים הנוכחית. זה עשוי לקחת מספר שניות עבור קובצי יומן גדולים. + + + + Clear console + נקה לוח בקרה + + + + Welcome to the CryptoLottoToken RPC console. + ברוכים הבאים ללוח בקרת RPC של לייטקוין + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + השתמש בחיצים למעלה ולמטה כדי לנווט בהיסטוריה, ו- <b>Ctrl-L</b> כדי לנקות את המסך. + + + + Type <b>help</b> for an overview of available commands. + הקלד <b>help</b> בשביל סקירה של הפקודות הזמינות. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + שלח מטבעות + + + + Send to multiple recipients at once + שלח למספר מקבלים בו-זמנית + + + + Add &Recipient + הוסף מקבל + + + + Remove all transaction fields + הסר את כל השדות בפעולה + + + + Clear &All + נקה הכל + + + + Balance: + יתרה: + + + + 123.456 BTC + 123.456 לייטקוין + + + + Confirm the send action + אשר את פעולת השליחה + + + + S&end + שלח + + + + <b>%1</b> to %2 (%3) + <b>%1</b> ל- %2 (%3) + + + + Confirm send coins + אשר שליחת מטבעות + + + + Are you sure you want to send %1? + האם אתה בטוח שברצונך לשלוח %1? + + + + and + ו- + + + + The recipient address is not valid, please recheck. + כתובת המקבל אינה תקינה, אנא בדוק שנית. + + + + The amount to pay must be larger than 0. + הכמות לשלם חייבת להיות גדולה מ-0. + + + + The amount exceeds your balance. + הכמות עולה על המאזן שלך. + + + + The total exceeds your balance when the %1 transaction fee is included. + הכמות הכוללת, ובכללה עמלת פעולה בסך %1, עולה על המאזן שלך. + + + + Duplicate address found, can only send to each address once per send operation. + כתובת כפולה נמצאה, ניתן לשלוח לכל כתובת רק פעם אחת בכל פעולת שליחה. + + + + Error: Transaction creation failed! + שגיאה: יצירת הפעולה נכשלה! + + + + 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. + שגיאה: הפעולה נדחתה. זה עשוי לקרות עם חלק מהמטבעות בארנק שלך כבר נוצלו, למשל אם השתמשת בעותק של wallet.dat ומטבעות נוצלו בעותק אך לא סומנו כמנוצלות כאן. + + + + SendCoinsEntry + + + Form + טופס + + + + A&mount: + כ&מות: + + + + Pay &To: + שלם &ל: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + הכתובת שאליה ישלח התשלום (למשל Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + הכנס תוית לכתובת הזאת כדי להכניס לפנקס הכתובות + + + + &Label: + ת&וית: + + + + Choose address from address book + בחר כתובת מפנקס הכתובות + + + + Alt+A + Alt+A + + + + Paste address from clipboard + הדבר כתובת מהלוח + + + + Alt+P + Alt+P + + + + Remove this recipient + הסר את המקבל הזה + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + הכנס כתובת לייטקוין (למשל Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + חתימות - חתום או אמת הודעה + + + + &Sign Message + חתום על הו&דעה + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + אתה יכול לחתום על הודעות עם הכתובות שלך כדי להוכיח שהן בבעלותך. היזהר לא לחתום על משהו מעורפל, שכן התקפות פישינג עשויות לגרום לך בעורמה למסור את זהותך. חתום רק על אמרות מפורטות לחלוטין שאתה מסכים עימן. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + הכתובת איתה לחתום על ההודעה (למשל Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + בחר כתובת מפנקס הכתובות + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + הדבק כתובת מהלוח + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + הכנס כאן את ההודעה שעליך ברצונך לחתום + + + + Signature + חתימה + + + + Copy the current signature to the system clipboard + העתק את החתימה הנוכחית ללוח המערכת + + + + Sign the message to prove you own this CryptoLottoToken address + חתום על ההודעה כדי להוכיח שכתובת הלייטקוין הזו בבעלותך. + + + + Sign &Message + חתום על הודעה + + + + Reset all sign message fields + אפס את כל שדות החתימה על הודעה + + + + + Clear &All + נקה הכל + + + + &Verify Message + אמת הודעה + + + + Enter the signing 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. + הכנס למטה את הכתובת החותמת, ההודעה (ודא שאתה מעתיק מעברי שורה, רווחים, טאבים וכו' באופן מדויק) והחתימה כדי לאמת את ההודעה. היזהר לא לפרש את החתימה כיותר ממה שמופיע בהודעה החתומה בעצמה, כדי להימנע מליפול קורבן למתקפת איש-באמצע. + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + הכתובת איתה ההודעה נחתמה (למשל Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + אמת את ההודעה כדי להבטיח שהיא נחתמה עם כתובת הלייטקוין הנתונה + + + + Verify &Message + אימות הודעה + + + + Reset all verify message fields + אפס את כל שדות אימות הודעה + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + הכנס כתובת לייטקוין (למשל Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + לחץ "חתום על ההודעה" כדי לחולל חתימה + + + + Enter CryptoLottoToken signature + הכנס חתימת לייטקוין + + + + + The entered address is invalid. + הכתובת שהוכנסה אינה תקינה. + + + + + + + Please check the address and try again. + אנא בדוק את הכתובת ונסה שנית. + + + + + The entered address does not refer to a key. + הכתובת שהוכנסה אינה מתייחסת למפתח. + + + + Wallet unlock was cancelled. + פתיחת הארנק בוטלה. + + + + Private key for the entered address is not available. + המפתח הפרטי עבור הכתובת שהוכנסה אינו זמין. + + + + Message signing failed. + החתימה על ההודעה נכשלה. + + + + Message signed. + ההודעה נחתמה. + + + + The signature could not be decoded. + לא ניתן לפענח את החתימה. + + + + + Please check the signature and try again. + אנא בדוק את החתימה ונסה שנית. + + + + The signature did not match the message digest. + החתימה לא תואמת את תקציר ההודעה. + + + + Message verification failed. + אימות ההודעה נכשל. + + + + Message verified. + ההודעה אומתה. + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + [רשת-בדיקה] + + + + TransactionDesc + + + Open until %1 + פתוח עד %1 + + + + %1/offline + %1/מנותק + + + + %1/unconfirmed + %1/ממתין לאישור + + + + %1 confirmations + %1 אישורים + + + + Status + מצב + + + + , broadcast through %n node(s) + , הופץ דרך צומת אחד, הופץ דרך %n צמתים + + + + Date + תאריך + + + + Source + מקור + + + + Generated + נוצר + + + + + From + מאת + + + + + + To + אל + + + + + own address + כתובת עצמית + + + + label + תוית + + + + + + + + Credit + זיכוי + + + + matures in %n more block(s) + מבשיל בעוד בלוק אחדמבשיל בעוד %n בלוקים + + + + not accepted + לא התקבל + + + + + + + Debit + חיוב + + + + Transaction fee + עמלת פעולה + + + + Net amount + כמות נקיה + + + + Message + הודעה + + + + Comment + הערה + + + + Transaction ID + זיהוי פעולה + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + מטבעות שנוצרים חייבים להבשיל למשך 120 בלוקים לפני שניתן לנצל אותם. כשיצרת את הבלוק הזה, הוא הופץ לרשת כדי להתווסף לשרשרת הבלוקים. אם הוא אינו מצליח לביע לשרשרת, המצב שלו ישתנה ל"לא התקבל" ולא ניתן יהיה לנצל אותו. זה עשוי לקרות מעת לעת אם צומת אחר יוצר בלוק בטווח של מספר שניות מהבלוק שלך. + + + + Debug information + מידע ניפוי + + + + Transaction + פעולה + + + + Inputs + קלטים + + + + Amount + כמות + + + + true + אמת + + + + false + שקר + + + + , has not been successfully broadcast yet + , טרם שודר בהצלחה + + + + Open for %n more block(s) + פתח למשך בלוק %n יותרפתח למשך %n בלוקים נוספים + + + + unknown + לא ידוע + + + + TransactionDescDialog + + + Transaction details + פרטי הפעולה + + + + This pane shows a detailed description of the transaction + חלונית זו מציגה תיאור מפורט של הפעולה + + + + TransactionTableModel + + + Date + תאריך + + + + Type + סוג + + + + Address + כתובת + + + + Amount + כמות + + + + Open for %n more block(s) + פתח למשך בלוק %n יותרפתח למשך %n בלוקים נוספים + + + + Open until %1 + פתוח עד %1 + + + + Offline (%1 confirmations) + לא מחובר (%1 אישורים) + + + + Unconfirmed (%1 of %2 confirmations) + ממתין לאישור (%1 מתוך %2 אישורים) + + + + Confirmed (%1 confirmations) + מאושר (%1 אישורים) + + + + Mined balance will be available when it matures in %n more block(s) + המאזן שנכרה יהיה זמין כשהוא מבשיל בעוד בלוק אחדהמאזן שנכרה יהיה זמין כשהוא מבשיל בעוד %n בלוקים + + + + This block was not received by any other nodes and will probably not be accepted! + הבלוק הזה לא נקלט על ידי אף צומת אחר, וכנראה לא יתקבל! + + + + Generated but not accepted + נוצר אך לא התקבל + + + + Received with + התקבל עם + + + + Received from + התקבל מאת + + + + Sent to + נשלח ל + + + + Payment to yourself + תשלום לעצמך + + + + Mined + נכרה + + + + (n/a) + (n/a) + + + + Transaction status. Hover over this field to show number of confirmations. + מצב הפעולה. השהה את הסמן מעל שדה זה כדי לראות את מספר האישורים. + + + + Date and time that the transaction was received. + התאריך והשעה בה הפעולה הזאת התקבלה. + + + + Type of transaction. + סוג הפעולה. + + + + Destination address of transaction. + כתובת היעד של הפעולה. + + + + Amount removed from or added to balance. + הכמות שהתווספה או הוסרה מהיתרה. + + + + TransactionView + + + + All + הכל + + + + Today + היום + + + + This week + השבוע + + + + This month + החודש + + + + Last month + החודש שעבר + + + + This year + השנה + + + + Range... + טווח... + + + + Received with + התקבל עם + + + + Sent to + נשלח ל + + + + To yourself + לעצמך + + + + Mined + נכרה + + + + Other + אחר + + + + Enter address or label to search + הכנס כתובת או תוית לחפש + + + + Min amount + כמות מזערית + + + + Copy address + העתק כתובת + + + + Copy label + העתק תוית + + + + Copy amount + העתק כמות + + + + Copy transaction ID + העתק מזהה פעולה + + + + Edit label + ערוך תוית + + + + Show transaction details + הצג פרטי פעולה + + + + Export Transaction Data + יצוא נתוני פעולות + + + + Comma separated file (*.csv) + קובץ מופרד בפסיקים (*.csv) + + + + Confirmed + מאושר + + + + Date + תאריך + + + + Type + סוג + + + + Label + תוית + + + + Address + כתובת + + + + Amount + כמות + + + + ID + מזהה + + + + Error exporting + שגיאה ביצוא + + + + Could not write to file %1. + לא מסוגל לכתוב לקובץ %1. + + + + Range: + טווח: + + + + to + אל + + + + WalletModel + + + Send Coins + שלח מטבעות + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + יצוא הנתונים בטאב הנוכחי לקובץ + + + + Backup Wallet + גבה ארנק + + + + Wallet Data (*.dat) + נתוני ארנק (*.dat) + + + + Backup Failed + גיבוי נכשל + + + + There was an error trying to save the wallet data to the new location. + הייתה שגיאה בנסיון לשמור את המידע הארנק למיקום החדש. + + + + Backup Successful + גיבוי הושלם בהצלחה + + + + The wallet data was successfully saved to the new location. + נתוני הארנק נשמרו בהצלחה במקום החדש. + + + + bitcoin-core + + + CryptoLottoToken version + גרסת לייטקוין + + + + Usage: + שימוש: + + + + Send command to -server or CryptoLottoTokend + שלח פקודה ל -server או CryptoLottoTokend + + + + List commands + רשימת פקודות + + + + Get help for a command + קבל עזרה עבור פקודה + + + + Options: + אפשרויות: + + + + Specify configuration file (default: CryptoLottoToken.conf) + ציין קובץ הגדרות (ברירת מחדל: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + ציין קובץ pid (ברירת מחדל: CryptoLottoTokend.pid) + + + + Specify data directory + ציין תיקיית נתונים + + + + Set database cache size in megabytes (default: 25) + קבע את גודל המטמון של מסד הנתונים במגהבייט (ברירת מחדל: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + האזן לחיבורים ב<פורט> (ברירת מחדל: 22556 או ברשת הבדיקה: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + החזק לכל היותר <n> חיבורים לעמיתים (ברירת מחדל: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + התחבר לצומת כדי לדלות כתובות עמיתים, ואז התנתק + + + + Specify your own public address + ציין את הכתובת הפומבית שלך + + + + Threshold for disconnecting misbehaving peers (default: 100) + סף להתנתקות מעמיתים הנוהגים שלא כהלכה (ברירת מחדל: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + מספר שניות למנוע מעמיתים הנוהגים שלא כהלכה מלהתחבר מחדש (ברירת מחדל: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + אירעה שגיאה בעת הגדרת פורט RPC %u להאזנה ב-IPv4: %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + האזן לחיבורי JSON-RPC ב- <port> (ברירת מחדל: 22555 או רשת בדיקה: 44555) + + + + Accept command line and JSON-RPC commands + קבל פקודות משורת הפקודה ו- JSON-RPC + + + + Run in the background as a daemon and accept commands + רוץ ברקע כדימון וקבל פקודות + + + + Use the test network + השתמש ברשת הבדיקה + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + קבל חיבורים מבחוץ (ברירת מחדל: 1 ללא -proxy או -connect) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + %s, עליך לקבוע סיסמת RPC בקובץ הקונפיגורציה: + %s +מומלץ להשתמש בסיסמא האקראית הבאה: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(אין צורך לזכור את הסיסמה) +אסור ששם המשתמש והסיסמא יהיו זהים. +אם הקובץ אינו קיים, צור אותו עם הרשאות קריאה לבעלים בלבד. +זה מומלץ לסמן alertnotify כדי לקבל דיווח על תקלות; +למשל: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + אירעה שגיאה בעת הגדרת פורט RPC %u להאזנה ב-IPv6, נסוג ל-IPv4: %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + קשור עם כתובת נתונה והאזן לה תמיד. השתמש בסימון [host]:port עבוד IPv6. + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + לא מסוגל להשיג נעילה על תיקיית הנתונים %s. כנראה שלייטקוין כבר רץ. + + + + 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. + שגיאה: הפעולה נדחתה! זה עלול לקרות אם כמה מהמטבעות בארנק שלך כבר נוצלו, למשל אם השתמשת בעותק של wallet.dat ומטבעות נשלחו בעותק אך לא סומנו כמנוצלות כאן. + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + שגיאה: הפעולה הזאת דורשת עמלת פעולה של לפחות %s עקב הכמות, המורכבות, או השימוש בכספים שהתקבלו לאחרונה! + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + בצע פעולה כאשר ההודעה הרלוונטית מתקבלת(%s בשורת הפקודה משתנה על-ידי ההודעה) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + בצע פקודה כאשר פעולת ארנק משתנה (%s ב cmd יוחלף ב TxID) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + קבע גודל מקסימלי עבור פעולות עדיפות גבוהה/עמלה נמוכה בבתים (ברירת מחדל: 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + זוהי בניית ניסיון טרום-שחרור - השימוש בה על אחריותך - אין להשתמש לצורך כריה או יישומי מסחר + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + אזהרה: -paytxfee נקבע לערך מאד גבוה! זוהי עמלת הפעולה שתשלם אם אתה שולח פעולה. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + אזהרה: הפעולות המוצגות עשויות לא להיות נכונות! ייתכן ואתה צריך לשדרג, או שצמתים אחרים צריכים לשדרג. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + אזהרה: אנא בדוק שהתאריך והשעה של המחשב שלך נכונים! אם השעון שלך אינו נכון לייטקוין לא יעבוד כראוי. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + אזהרה: שגיאה בקריאת wallet.dat! כל המתפחות נקראו באופן תקין, אך נתוני הפעולות או ספר הכתובות עלולים להיות חסרים או שגויים. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + אזהרה: קובץ wallet.dat מושחת, המידע חולץ! קובץ wallet.dat המקורח נשמר כ - wallet.{timestamp}.bak ב - %s; אם המאזן או הפעולות שגויים עליך לשחזר גיבוי. + + + + Attempt to recover private keys from a corrupt wallet.dat + נסה לשחזר מפתחות פרטיים מקובץ wallet.dat מושחת. + + + + Block creation options: + אפשרויות יצירת בלוק: + + + + Connect only to the specified node(s) + התחבר רק לצמתים המצוינים + + + + Corrupted block database detected + התגלה מסד נתוני בלוקים לא תקין + + + + Discover own IP address (default: 1 when listening and no -externalip) + גלה את כתובת ה-IP העצמית (ברירת מחדל: 1 כשמאזינים וללא -externalip) + + + + Do you want to rebuild the block database now? + האם תרצה כעט לבנות מחדש את מסד נתוני הבלוקים? + + + + Error initializing block database + שגיאה באתחול מסד נתוני הבלוקים + + + + Error initializing wallet database environment %s! + שגיאה באתחול סביבת מסד נתוני הארנקים %s! + + + + Error loading block database + שגיאה בטעינת מסד נתוני הבלוקים + + + + Error opening block database + שגיאה בטעינת מסד נתוני הבלוקים + + + + Error: Disk space is low! + שגיאה: מעט מקום פנוי בדיסק! + + + + Error: Wallet locked, unable to create transaction! + שגיאה: הארנק נעול, אין אפשרות ליצור פעולה! + + + + Error: system error: + שגיאה: שגיאת מערכת: + + + + Failed to listen on any port. Use -listen=0 if you want this. + האזנה נכשלה בכל פורט. השתמש ב- -listen=0 אם ברצונך בכך. + + + + Failed to read block info + קריאת מידע הבלוקים נכשלה + + + + Failed to read block + קריאת הבלוק נכשלה + + + + Failed to sync block index + סנכרון אינדקס הבלוקים נכשל + + + + Failed to write block index + כתיבת אינדקס הבלוקים נכשל + + + + Failed to write block info + כתיבת מידע הבלוקים נכשל + + + + Failed to write block + כתיבת הבלוק נכשלה + + + + Failed to write file info + כתיבת מידע הקבצים נכשלה + + + + Failed to write to coin database + כתיבת מסד נתוני המטבעות נכשלה + + + + Failed to write transaction index + כתיבת אינדקס הפעולות נכשלה + + + + Failed to write undo data + כתיבת נתוני ביטול נכשלה + + + + Find peers using DNS lookup (default: 1 unless -connect) + מצא עמיתים ע"י חיפוש DNS (ברירת מחדל: 1 ללא -connect) + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + מספר הבלוקים לבדוק בעת אתחול (ברירת מחדל: 288, 0 = כולם) + + + + How thorough the block verification is (0-4, default: 3) + מידת היסודיות של אימות הבלוקים (0-4, ברירת מחדל: 3) + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + בנה מחדש את אינדק שרשרת הבלוקים מקבצי ה-blk000??.dat הנוכחיים. + + + + Set the number of threads to service RPC calls (default: 4) + קבע את מספר תהליכוני לשירות קריאות RPC (ברירת מחדל: 4) + + + + Verifying blocks... + מאמת את שלמות מסד הנתונים... + + + + Verifying wallet... + מאמת את יושרת הארנק... + + + + Imports blocks from external blk000??.dat file + מייבא בלוקים מקובצי blk000??.dat חיצוניים + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + מידע + + + + Invalid -tor address: '%s' + כתובת לא תקינה ל -tor: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + תחזק אינדקס פעולות מלא (ברירת מחדל: 0) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + חוצץ קבלה מירבי לכל חיבור, <n>*1000 בתים (ברירת מחדל: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + חוצץ שליחה מירבי לכל חיבור, <n>*1000 בתים (ברירת מחדל: 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + קבל רק שרשרת בלוקים התואמת נקודות ביקורת מובנות (ברירת מחדל: 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + התחבר רק לצמתים ברשת <net> (IPv4, IPv6 או Tor) + + + + Output extra debugging information. Implies all other -debug* options + פלוט מידע ניפוי נוסף. נובע מכך כל אפשרויות -debug* האחרות. + + + + Output extra network debugging information + פלוט מידע נוסף לניפוי שגיאות ברשת. + + + + Prepend debug output with timestamp (default: 1) + הוסף חותמת זמן לפני פלט דיבאג + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + אפשרויות SSL: (ראה את הויקי של לייטקוין עבור הוראות הגדרת SSL) + + + + Select the version of socks proxy to use (4-5, default: 5) + בחר את גרסת פרוקסי SOCKS להשתמש בה (4-5, ברירת מחדל: 5) + + + + Send trace/debug info to console instead of debug.log file + שלח מידע דיבאג ועקבה לקונסולה במקום לקובץ debug.log + + + + Send trace/debug info to debugger + שלח מידע דיבאג ועקבה לכלי דיבאג + + + + Set maximum block size in bytes (default: 250000) + קבע את גדול הבלוק המירבי בבתים (ברירת מחדל: 250000) + + + + Set minimum block size in bytes (default: 0) + קבע את גודל הבלוק המינימלי בבתים (ברירת מחדל: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + כווץ את קובץ debug.log בהפעלת הקליינט (ברירת מחדל: 1 ללא -debug) + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + ציין הגבלת זמן לחיבור במילישניות (ברירת מחדל: 5000) + + + + System error: + שגיאת מערכת: + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + השתמש ב-UPnP כדי למפות את הפורט להאזנה (ברירת מחדל: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + השתמש ב-UPnP כדי למפות את הפורט להאזנה (ברירת מחדל: 1 בעת האזנה) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + השתמש בפרוקסי כדי להגיע לשירותים חבויים ב-tor (ברירת מחדל: כמו ב- -proxy) + + + + Username for JSON-RPC connections + שם משתמש לחיבורי JSON-RPC + + + + Warning + אזהרה + + + + Warning: This version is obsolete, upgrade required! + אזהרה: הגרסה הזאת מיושנת, יש צורך בשדרוג! + + + + You need to rebuild the databases using -reindex to change -txindex + עליך לבנות מחדש את מסדי הנתונים תוך שימוש ב- -reindex על מנת לשנות את -txindex + + + + wallet.dat corrupt, salvage failed + קובץ wallet.dat מושחת, החילוץ נכשל + + + + Password for JSON-RPC connections + סיסמה לחיבורי JSON-RPC + + + + Allow JSON-RPC connections from specified IP address + אפשר חיבורי JSON-RPC מכתובת האינטרנט המצוינת + + + + Send commands to node running on <ip> (default: 127.0.0.1) + שלח פקודות לצומת ב-<ip> (ברירת מחדל: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + בצע פקודה זו כשהבלוק הטוב ביותר משתנה (%s בפקודה יוחלף בגיבוב הבלוק) + + + + Upgrade wallet to latest format + שדרג את הארנק לפורמט העדכני + + + + Set key pool size to <n> (default: 100) + קבע את גודל המאגר ל -<n> (ברירת מחדל: 100) + + + + Rescan the block chain for missing wallet transactions + סרוק מחדש את שרשרת הבלוקים למציאת פעולות חסרות בארנק + + + + Use OpenSSL (https) for JSON-RPC connections + השתמש ב-OpenSSL (https( עבור חיבורי JSON-RPC + + + + Server certificate file (default: server.cert) + קובץ תעודת שרת (ברירת מחדל: server.cert) + + + + Server private key (default: server.pem) + מפתח פרטי של השרת (ברירת מחדל: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + צפנים קבילים (ברירת מחדל: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + הודעת העזרה הזו + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + לא מסוגל לקשור ל-%s במחשב זה (הקשירה החזירה שגיאה %d, %s) + + + + Connect through socks proxy + התחבר דרך פרוקסי SOCKS + + + + Allow DNS lookups for -addnode, -seednode and -connect + אפשר בדיקת DNS עבור -addnode, -seednode ו- -connect + + + + Loading addresses... + טוען כתובות... + + + + Error loading wallet.dat: Wallet corrupted + שגיאה בטעינת הקובץ wallet.dat: הארנק מושחת + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + שגיאה בטעינת הקובץ wallet.dat: הארנק דורש גרסה חדשה יותר של לייטקוין + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + יש לכתוב מחדש את הארנק: אתחל את לייטקוין לסיום + + + + Error loading wallet.dat + שגיאה בטעינת הקובץ wallet.dat + + + + Invalid -proxy address: '%s' + כתובת -proxy לא תקינה: '%s' + + + + Unknown network specified in -onlynet: '%s' + רשת לא ידועה צוינה ב- -onlynet: '%s' + + + + Unknown -socks proxy version requested: %i + התבקשה גרסת פרוקסי -socks לא ידועה: %i + + + + Cannot resolve -bind address: '%s' + לא מסוגל לפתור כתובת -bind: '%s' + + + + Cannot resolve -externalip address: '%s' + לא מסוגל לפתור כתובת -externalip: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + כמות לא תקינה עבור -paytxfee=<amount>: '%s' + + + + Invalid amount + כמות לא תקינה + + + + Insufficient funds + אין מספיק כספים + + + + Loading block index... + טוען את אינדקס הבלוקים... + + + + Add a node to connect to and attempt to keep the connection open + הוסף צומת להתחברות ונסה לשמור את החיבור פתוח + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + לא ניתן לקשור ל-%s במחשב זה. לייטקוין כנראה עדיין רץ. + + + + Fee per KB to add to transactions you send + עמלה להוסיף לפעולות שאתה שולח עבור כל KB + + + + Loading wallet... + טוען ארנק... + + + + Cannot downgrade wallet + לא יכול להוריד דרגת הארנק + + + + Cannot write default address + לא יכול לכתוב את כתובת ברירת המחדל + + + + Rescanning... + סורק מחדש... + + + + Done loading + טעינה הושלמה + + + + To use the %s option + להשתמש באפשרות %s + + + + Error + שגיאה + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + עליך לקבוע rpcpassword=yourpassword בקובץ ההגדרות: +%s +אם הקובץ אינו קיים, צור אותו עם הרשאות קריאה לבעלים בלבד. + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_hi_IN.ts b/src/qt/locale/bitcoin_hi_IN.ts new file mode 100644 index 0000000..e530894 --- /dev/null +++ b/src/qt/locale/bitcoin_hi_IN.ts @@ -0,0 +1,2922 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + बिटकोइन के संबंध में + + + + <b>CryptoLottoToken</b> version + बिटकोइन वर्सन + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + + Copyright + कापीराइट + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + पता पुस्तक + + + + Double-click to edit address or label + दो बार क्लिक करे पता या लेबल संपादन करने के लिए ! + + + + Create a new address + नया पता लिखिए ! + + + + Copy the currently selected address to the system clipboard + चुनिन्दा पते को सिस्टम क्लिपबोर्ड पर कापी करे ! + + + + &New Address + &नया पता + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + + + + + &Copy Address + &पता कॉपी करे + + + + Show &QR Code + + + + + Sign a message to prove you own a CryptoLottoToken address + + + + + Sign &Message + + + + + Delete the currently selected address from the list + + + + + Export the data in the current tab to a file + + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + + + + + &Verify Message + + + + + &Delete + &मिटाए !! + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + &लेबल कॉपी करे + + + + &Edit + &एडिट + + + + Send &Coins + + + + + Export Address Book Data + पता पुस्तक का डेटा एक्सपोर्ट (निर्यात) करे ! + + + + Comma separated file (*.csv) + Comma separated file (*.csv) + + + + Error exporting + ग़लतियाँ एक्सपोर्ट (निर्यात) करे! + + + + Could not write to file %1. + फाइल में लिख नही सके %1. + + + + AddressTableModel + + + Label + लेबल + + + + Address + पता + + + + (no label) + (कोई लेबल नही !) + + + + AskPassphraseDialog + + + Passphrase Dialog + + + + + Enter passphrase + पहचान शब्द/अक्षर डालिए ! + + + + New passphrase + नया पहचान शब्द/अक्षर डालिए ! + + + + Repeat new passphrase + दोबारा नया पहचान शब्द/अक्षर डालिए ! + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + नया पहचान शब्द/अक्षर वॉलेट मे डालिए ! <br/> कृपा करके पहचान शब्द में <br> 10 से ज़्यादा अक्षॉरों का इस्तेमाल करे </b>,या <b>आठ या उससे से ज़्यादा शब्दो का इस्तेमाल करे</b> ! + + + + Encrypt wallet + एनक्रिप्ट वॉलेट ! + + + + This operation needs your wallet passphrase to unlock the wallet. + वॉलेट खोलने के आपका वॉलेट पहचान शब्द्‌/अक्षर चाईए ! + + + + Unlock wallet + वॉलेट खोलिए + + + + This operation needs your wallet passphrase to decrypt the wallet. + वॉलेट डीक्रिप्ट( विकोड) करने के लिए आपका वॉलेट पहचान शब्द्‌/अक्षर चाईए ! + + + + Decrypt wallet + डीक्रिप्ट वॉलेट + + + + Change passphrase + पहचान शब्द/अक्षर बदलिये ! + + + + Enter the old and new passphrase to the wallet. + कृपा करके पुराना एवं नया पहचान शब्द/अक्षर वॉलेट में डालिए ! + + + + Confirm wallet encryption + वॉलेट एनक्रिपशन को प्रमाणित कीजिए ! + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + + + + + Are you sure you wish to encrypt your wallet? + + + + + 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. + + + + + + Warning: The Caps Lock key is on! + + + + + + Wallet encrypted + वॉलेट एनक्रिप्ट हो गया ! + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + + + + + + + + Wallet encryption failed + वॉलेट एनक्रिप्ट नही हुआ! + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + वॉलेट एनक्रिपशन नाकाम हो गया इंटर्नल एरर की वजह से! आपका वॉलेट एनक्रीपत नही हुआ है! + + + + + The supplied passphrases do not match. + आपके द्वारा डाले गये पहचान शब्द/अक्षर मिलते नही है ! + + + + Wallet unlock failed + वॉलेट का लॉक नही खुला ! + + + + + + The passphrase entered for the wallet decryption was incorrect. + वॉलेट डीक्रिप्ट करने के लिए जो पहचान शब्द/अक्षर डाले गये है वो सही नही है! + + + + Wallet decryption failed + वॉलेट का डीक्रिप्ट-ष्ण असफल ! + + + + Wallet passphrase was successfully changed. + + + + + BitcoinGUI + + + Sign &message... + + + + + Synchronizing with network... + नेटवर्क से समकालिक (मिल) रहा है ... + + + + &Overview + &विवरण + + + + Show general overview of wallet + वॉलेट का सामानया विवरण दिखाए ! + + + + &Transactions + & लेन-देन + + + + + Browse transaction history + देखिए पुराने लेन-देन के विवरण ! + + + + Edit the list of stored addresses and labels for sending + स्टोर किए हुए पते और लेबलओ को बदलिए ! + + + + Show the list of addresses for receiving payments + पते की सूची दिखाए जिन्हे भुगतान करना है ! + + + + E&xit + बाहर जायें + + + + Quit application + अप्लिकेशन से बाहर निकलना ! + + + + Show information about CryptoLottoToken + बीटकोइन के बारे में जानकारी ! + + + + About &Qt + + + + + Show information about Qt + + + + + &Options... + &विकल्प + + + + &Encrypt Wallet... + + + + + &Backup Wallet... + &बैकप वॉलेट + + + + &Change Passphrase... + + + + + Importing blocks from disk... + + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + + + + + Modify configuration options for CryptoLottoToken + + + + + Backup wallet to another location + + + + + Change the passphrase used for wallet encryption + पहचान शब्द/अक्षर जो वॉलेट एनक्रिपशन के लिए इस्तेमाल किया है उसे बदलिए! + + + + &Debug window + + + + + Open debugging and diagnostic console + + + + + &Verify message... + + + + + + CryptoLottoToken + बीटकोइन + + + + Wallet + वॉलेट + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + + + + + &Show / Hide + + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + &फाइल + + + + &Settings + &सेट्टिंग्स + + + + &Help + &मदद + + + + Tabs toolbar + टैबस टूलबार + + + + + [testnet] + [टेस्टनेट] + + + + CryptoLottoToken client + + + + + %n active connection(s) to CryptoLottoToken network + %n सक्रिया संपर्क बीटकोइन नेटवर्क से%n सक्रिया संपर्क बीटकोइन नेटवर्क से + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + + + + + %n hour(s) + %n घंटा%n घंटे + + + + %n day(s) + %n दिन%n दिनो + + + + %n week(s) + %n हफ़्ता%n हफ्ते + + + + %1 behind + %1 पीछे + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + भूल + + + + Warning + चेतावनी + + + + Information + जानकारी + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + नवीनतम + + + + Catching up... + + + + + Confirm transaction fee + + + + + Sent transaction + भेजी ट्रांजक्शन + + + + Incoming transaction + प्राप्त हुई ट्रांजक्शन + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + तारीख: %1\n +राशि: %2\n +टाइप: %3\n +पता:%4\n + + + + + URI handling + + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + वॉलेट एन्क्रिप्टेड है तथा अभी लॉक्ड नहीं है + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + वॉलेट एन्क्रिप्टेड है तथा अभी लॉक्ड है + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + + + + + EditAddressDialog + + + Edit Address + पता एडिट करना + + + + &Label + &लेबल + + + + The label associated with this address book entry + इस एड्रेस बुक से जुड़ा एड्रेस + + + + &Address + &पता + + + + The address associated with this address book entry. This can only be modified for sending addresses. + इस एड्रेस बुक से जुड़ी प्रविष्टि केवल भेजने वाले addresses के लिए बदली जा सकती है| + + + + New receiving address + नया स्वीकार्य पता + + + + New sending address + नया भेजने वाला पता + + + + Edit receiving address + एडिट स्वीकार्य पता + + + + Edit sending address + एडिट भेजने वाला पता + + + + The entered address "%1" is already in the address book. + डाला गया पता "%1" एड्रेस बुक में पहले से ही मोजूद है| + + + + The entered address "%1" is not a valid CryptoLottoToken address. + + + + + Could not unlock wallet. + वॉलेट को unlock नहीं किया जा सकता| + + + + New key generation failed. + नयी कुंजी का निर्माण असफल रहा| + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + बीटकोइन-Qt + + + + version + संस्करण + + + + Usage: + खपत : + + + + command-line options + + + + + UI options + + + + + Set language, for example "de_DE" (default: system locale) + + + + + Start minimized + + + + + Show splash screen on startup (default: 1) + + + + + OptionsDialog + + + Options + विकल्प + + + + &Main + + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + + + + + Automatically start CryptoLottoToken after logging in to the system. + + + + + &Start CryptoLottoToken on system login + + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + + + + + Map port using &UPnP + + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + + + + + &Connect through SOCKS proxy: + + + + + Proxy &IP: + + + + + IP address of the proxy (e.g. 127.0.0.1) + + + + + &Port: + + + + + Port of the proxy (e.g. 9050) + + + + + SOCKS &Version: + + + + + SOCKS version of the proxy (e.g. 5) + + + + + &Window + + + + + Show only a tray icon after minimizing the window. + + + + + &Minimize to the tray instead of the taskbar + + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + + + + + M&inimize on close + + + + + &Display + + + + + User Interface &language: + + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + + + + + &Unit to show amounts in: + + + + + Choose the default subdivision unit to show in the interface and when sending coins. + + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + + + &Display addresses in transaction list + + + + + &OK + &ओके + + + + &Cancel + &कैन्सल + + + + &Apply + + + + + default + + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + चेतावनी + + + + + This setting will take effect after restarting CryptoLottoToken. + + + + + The supplied proxy address is invalid. + + + + + OverviewPage + + + Form + फार्म + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + + + + + Balance: + बाकी रकम : + + + + Unconfirmed: + अपुष्ट : + + + + Wallet + वॉलेट + + + + Immature: + + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + <b>हाल का लेन-देन</b> + + + + Your current balance + आपका चालू बॅलेन्स + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + लेन देन की पुष्टि अभी नहीं हुई है, इसलिए इन्हें अभी मोजुदा बैलेंस में गिना नहीं गया है| + + + + + out of sync + + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + + + + + Request Payment + भुगतान का अनुरोध + + + + Amount: + राशि : + + + + Label: + लेबल : + + + + Message: + + + + + &Save As... + + + + + Error encoding URI into QR Code. + + + + + The entered amount is invalid, please check. + + + + + Resulting URI too long, try to reduce the text for label / message. + + + + + Save QR Code + + + + + PNG Images (*.png) + + + + + RPCConsole + + + Client name + + + + + + + + + + + + + + N/A + लागू नही + + + + + Client version + + + + + &Information + + + + + Using OpenSSL version + + + + + Startup time + + + + + Network + + + + + Number of connections + + + + + On testnet + + + + + Block chain + + + + + Current number of blocks + + + + + Estimated total blocks + + + + + Last block time + + + + + &Open + + + + + Command-line options + + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + + &Show + + + + + &Console + + + + + Build date + + + + + CryptoLottoToken - Debug window + + + + + CryptoLottoToken Core + + + + + Debug log file + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + + + + + Welcome to the CryptoLottoToken RPC console. + + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + + + + + Type <b>help</b> for an overview of available commands. + + + + + SendCoinsDialog + + + + + + + + + + Send Coins + सिक्के भेजें| + + + + Send to multiple recipients at once + एक साथ कई प्राप्तकर्ताओं को भेजें + + + + Add &Recipient + + + + + Remove all transaction fields + + + + + Clear &All + + + + + Balance: + बाकी रकम : + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + भेजने की पुष्टि करें + + + + S&end + + + + + <b>%1</b> to %2 (%3) + <b>%1</b> से %2 (%3) + + + + Confirm send coins + सिक्के भेजने की पुष्टि करें + + + + Are you sure you want to send %1? + क्या आप %1 भेजना चाहते हैं? + + + + and + और + + + + The recipient address is not valid, please recheck. + + + + + The amount to pay must be larger than 0. + भेजा गया अमाउंट शुन्य से अधिक होना चाहिए| + + + + The amount exceeds your balance. + + + + + The total exceeds your balance when the %1 transaction fee is included. + + + + + Duplicate address found, can only send to each address once per send operation. + + + + + Error: Transaction creation failed! + + + + + 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. + + + + + SendCoinsEntry + + + Form + फार्म + + + + A&mount: + अमाउंट: + + + + Pay &To: + प्राप्तकर्ता: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + आपकी एड्रेस बुक में इस एड्रेस के लिए एक लेबल लिखें + + + + &Label: + लेबल: + + + + Choose address from address book + + + + + Alt+A + Alt-A + + + + Paste address from clipboard + Clipboard से एड्रेस paste करें + + + + Alt+P + Alt-P + + + + Remove this recipient + प्राप्तकर्ता हटायें + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + CryptoLottoToken एड्रेस लिखें (उदाहरण: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + + + + + &Sign Message + + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Choose an address from the address book + + + + + + Alt+A + Alt-A + + + + Paste address from clipboard + Clipboard से एड्रेस paste करें + + + + Alt+P + Alt-P + + + + Enter the message you want to sign here + + + + + Signature + हस्ताक्षर + + + + Copy the current signature to the system clipboard + + + + + Sign the message to prove you own this CryptoLottoToken address + + + + + Sign &Message + + + + + Reset all sign message fields + + + + + + Clear &All + + + + + &Verify Message + + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + + + + + Verify &Message + + + + + Reset all verify message fields + + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + CryptoLottoToken एड्रेस लिखें (उदाहरण: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + + + + + Enter CryptoLottoToken signature + + + + + + The entered address is invalid. + + + + + + + + Please check the address and try again. + + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + + + + + Private key for the entered address is not available. + + + + + Message signing failed. + + + + + Message signed. + + + + + The signature could not be decoded. + + + + + + Please check the signature and try again. + + + + + The signature did not match the message digest. + + + + + Message verification failed. + + + + + Message verified. + + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + + + + + TransactionDesc + + + Open until %1 + खुला है जबतक %1 + + + + %1/offline + + + + + %1/unconfirmed + %1/अपुष्ट + + + + %1 confirmations + %1 पुष्टियाँ + + + + Status + + + + + , broadcast through %n node(s) + + + + + Date + taareek + + + + Source + + + + + Generated + + + + + + From + + + + + + + To + + + + + + own address + + + + + label + + + + + + + + + Credit + + + + + matures in %n more block(s) + + + + + not accepted + + + + + + + + Debit + + + + + Transaction fee + + + + + Net amount + + + + + Message + + + + + Comment + + + + + Transaction ID + + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + + + + + Debug information + + + + + Transaction + + + + + Inputs + + + + + Amount + राशि + + + + true + सही + + + + false + ग़लत + + + + , has not been successfully broadcast yet + , अभी तक सफलतापूर्वक प्रसारित नहीं किया गया है + + + + Open for %n more block(s) + + + + + unknown + अज्ञात + + + + TransactionDescDialog + + + Transaction details + लेन-देन का विवरण + + + + This pane shows a detailed description of the transaction + ये खिड़की आपको लेन-देन का विस्तृत विवरण देगी ! + + + + TransactionTableModel + + + Date + taareek + + + + Type + टाइप + + + + Address + पता + + + + Amount + राशि + + + + Open for %n more block(s) + + + + + Open until %1 + खुला है जबतक %1 + + + + Offline (%1 confirmations) + ऑफलाइन ( %1 पक्का करना) + + + + Unconfirmed (%1 of %2 confirmations) + अपुष्ट ( %1 मे %2 पक्के ) + + + + Confirmed (%1 confirmations) + पक्के ( %1 पक्का करना) + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + यह ब्लॉक किसी भी और नोड को मिला नही है ! शायद यह ब्लॉक कोई भी नोड स्वीकारे गा नही ! + + + + Generated but not accepted + जेनरेट किया गया किंतु स्वीकारा नही गया ! + + + + Received with + स्वीकारा गया + + + + Received from + स्वीकार्य ओर से + + + + Sent to + भेजा गया + + + + Payment to yourself + भेजा खुद को भुगतान + + + + Mined + माइंड + + + + (n/a) + (लागू नहीं) + + + + Transaction status. Hover over this field to show number of confirmations. + ट्रांसेक्शन स्तिथि| पुष्टियों की संख्या जानने के लिए इस जगह पर माउस लायें| + + + + Date and time that the transaction was received. + तारीख तथा समय जब ये ट्रांसेक्शन प्राप्त हुई थी| + + + + Type of transaction. + ट्रांसेक्शन का प्रकार| + + + + Destination address of transaction. + ट्रांसेक्शन की मंजिल का पता| + + + + Amount removed from or added to balance. + अमाउंट बैलेंस से निकला या जमा किया गया | + + + + TransactionView + + + + All + सभी + + + + Today + आज + + + + This week + इस हफ्ते + + + + This month + इस महीने + + + + Last month + पिछले महीने + + + + This year + इस साल + + + + Range... + विस्तार... + + + + Received with + स्वीकार करना + + + + Sent to + भेजा गया + + + + To yourself + अपनेआप को + + + + Mined + माइंड + + + + Other + अन्य + + + + Enter address or label to search + ढूँदने के लिए कृपा करके पता या लेबल टाइप करे ! + + + + Min amount + लघुत्तम राशि + + + + Copy address + पता कॉपी करे + + + + Copy label + लेबल कॉपी करे + + + + Copy amount + कॉपी राशि + + + + Copy transaction ID + + + + + Edit label + एडिट लेबल + + + + Show transaction details + + + + + Export Transaction Data + लेन-देन का डेटा निर्यात करे ! + + + + Comma separated file (*.csv) + Comma separated file (*.csv) + + + + Confirmed + पक्का + + + + Date + taareek + + + + Type + टाइप + + + + Label + लेबल + + + + Address + पता + + + + Amount + राशि + + + + ID + ID + + + + Error exporting + ग़लतियाँ एक्सपोर्ट (निर्यात) करे! + + + + Could not write to file %1. + फाइल में लिख नही सके %1. + + + + Range: + विस्तार: + + + + to + तक + + + + WalletModel + + + Send Coins + + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + + + + + Backup Wallet + बैकप वॉलेट + + + + Wallet Data (*.dat) + वॉलेट डेटा (*.dat) + + + + Backup Failed + बैकप असफल + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + बैकप सफल + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + बीटकोइन संस्करण + + + + Usage: + खपत : + + + + Send command to -server or CryptoLottoTokend + -server या CryptoLottoTokend को कमांड भेजें + + + + List commands + commands की लिस्ट बनाएं + + + + Get help for a command + किसी command के लिए मदद लें + + + + Options: + विकल्प: + + + + Specify configuration file (default: CryptoLottoToken.conf) + configuraion की फाइल का विवरण दें (default: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + pid फाइल का विवरण दें (default: CryptoLottoToken.pid) + + + + Specify data directory + + + + + Set database cache size in megabytes (default: 25) + + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + + + + + Maintain at most <n> connections to peers (default: 125) + + + + + Connect to a node to retrieve peer addresses, and disconnect + + + + + Specify your own public address + + + + + Threshold for disconnecting misbehaving peers (default: 100) + + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + + + + + Accept command line and JSON-RPC commands + + + + + Run in the background as a daemon and accept commands + + + + + Use the test network + + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + + + + + Connect only to the specified node(s) + + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + ब्लॉक्स जाँचे जा रहा है... + + + + Verifying wallet... + वॉलेट जाँचा जा रहा है... + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + जानकारी + + + + Invalid -tor address: '%s' + + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + + + + + Select the version of socks proxy to use (4-5, default: 5) + + + + + Send trace/debug info to console instead of debug.log file + + + + + Send trace/debug info to debugger + + + + + Set maximum block size in bytes (default: 250000) + + + + + Set minimum block size in bytes (default: 0) + + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + + + + + Use UPnP to map the listening port (default: 1 when listening) + + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + + + + + Warning + चेतावनी + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + + + + + Allow JSON-RPC connections from specified IP address + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + + + + + Upgrade wallet to latest format + + + + + Set key pool size to <n> (default: 100) + + + + + Rescan the block chain for missing wallet transactions + + + + + Use OpenSSL (https) for JSON-RPC connections + + + + + Server certificate file (default: server.cert) + + + + + Server private key (default: server.pem) + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + + This help message + + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + + + + + Connect through socks proxy + + + + + Allow DNS lookups for -addnode, -seednode and -connect + + + + + Loading addresses... + पता पुस्तक आ रही है... + + + + Error loading wallet.dat: Wallet corrupted + + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + + + + + Error loading wallet.dat + + + + + Invalid -proxy address: '%s' + + + + + Unknown network specified in -onlynet: '%s' + + + + + Unknown -socks proxy version requested: %i + + + + + Cannot resolve -bind address: '%s' + + + + + Cannot resolve -externalip address: '%s' + + + + + Invalid amount for -paytxfee=<amount>: '%s' + + + + + Invalid amount + राशि ग़लत है + + + + Insufficient funds + + + + + Loading block index... + ब्लॉक इंडेक्स आ रहा है... + + + + Add a node to connect to and attempt to keep the connection open + + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + + + + + Fee per KB to add to transactions you send + + + + + Loading wallet... + वॉलेट आ रहा है... + + + + Cannot downgrade wallet + + + + + Cannot write default address + + + + + Rescanning... + रि-स्केनी-इंग... + + + + Done loading + लोड हो गया| + + + + To use the %s option + + + + + Error + भूल + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_hr.qm b/src/qt/locale/bitcoin_hr.qm new file mode 100644 index 0000000..25934cd Binary files /dev/null and b/src/qt/locale/bitcoin_hr.qm differ diff --git a/src/qt/locale/bitcoin_hr.ts b/src/qt/locale/bitcoin_hr.ts new file mode 100644 index 0000000..71d23b2 --- /dev/null +++ b/src/qt/locale/bitcoin_hr.ts @@ -0,0 +1,2921 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + O CryptoLottoToken-u + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> verzija + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + Adresar + + + + Double-click to edit address or label + Dvostruki klik za uređivanje adrese ili oznake + + + + Create a new address + Dodajte novu adresu + + + + Copy the currently selected address to the system clipboard + Kopiraj trenutno odabranu adresu u međuspremnik + + + + &New Address + &Nova adresa + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Ovo su vaše CryptoLottoToken adrese za primanje isplate. Možda želite dati drukčiju adresu svakom primatelju tako da možete pratiti tko je platio. + + + + &Copy Address + &Kopirati adresu + + + + Show &QR Code + Prikaži &QR Kôd + + + + Sign a message to prove you own a CryptoLottoToken address + + + + + Sign &Message + + + + + Delete the currently selected address from the list + + + + + Export the data in the current tab to a file + Izvoz podataka iz trenutnog taba u datoteku + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + + + + + &Verify Message + + + + + &Delete + &Brisanje + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + Kopirati &oznaku + + + + &Edit + &Izmjeniti + + + + Send &Coins + + + + + Export Address Book Data + Izvoz podataka adresara + + + + Comma separated file (*.csv) + Datoteka vrijednosti odvojenih zarezom (*. csv) + + + + Error exporting + Pogreška kod izvoza + + + + Could not write to file %1. + Ne mogu pisati u datoteku %1. + + + + AddressTableModel + + + Label + Oznaka + + + + Address + Adresa + + + + (no label) + (bez oznake) + + + + AskPassphraseDialog + + + Passphrase Dialog + + + + + Enter passphrase + Unesite lozinku + + + + New passphrase + Nova lozinka + + + + Repeat new passphrase + Ponovite novu lozinku + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Unesite novi lozinku za novčanik. <br/> Molimo Vas da koristite zaporku od <b>10 ili više slučajnih znakova,</b> ili <b>osam ili više riječi.</b> + + + + Encrypt wallet + Šifriranje novčanika + + + + This operation needs your wallet passphrase to unlock the wallet. + Ova operacija treba lozinku vašeg novčanika kako bi se novčanik otključao. + + + + Unlock wallet + Otključaj novčanik + + + + This operation needs your wallet passphrase to decrypt the wallet. + Ova operacija treba lozinku vašeg novčanika kako bi se novčanik dešifrirao. + + + + Decrypt wallet + Dešifriranje novčanika. + + + + Change passphrase + Promjena lozinke + + + + Enter the old and new passphrase to the wallet. + Unesite staru i novu lozinku za novčanik. + + + + Confirm wallet encryption + Potvrdi šifriranje novčanika + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Upozorenje: Ako šifrirate vaš novčanik i izgubite lozinku, <b>IZGUBIT ĆETE SVE SVOJE CryptoLottoTokenSE!</b> + + + + Are you sure you wish to encrypt your wallet? + Jeste li sigurni da želite šifrirati svoj novčanik? + + + + 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. + + + + + + Warning: The Caps Lock key is on! + + + + + + Wallet encrypted + Novčanik šifriran + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken će se sada zatvoriti kako bi dovršio postupak šifriranja. Zapamtite da šifriranje vašeg novčanika ne može u potpunosti zaštititi vaše CryptoLottoTokene od krađe preko zloćudnog softvera koji bi bio na vašem računalu. + + + + + + + Wallet encryption failed + Šifriranje novčanika nije uspjelo + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Šifriranje novčanika nije uspjelo zbog interne pogreške. Vaš novčanik nije šifriran. + + + + + The supplied passphrases do not match. + Priložene lozinke se ne podudaraju. + + + + Wallet unlock failed + Otključavanje novčanika nije uspjelo + + + + + + The passphrase entered for the wallet decryption was incorrect. + Lozinka za dešifriranje novčanika nije točna. + + + + Wallet decryption failed + Dešifriranje novčanika nije uspjelo + + + + Wallet passphrase was successfully changed. + Lozinka novčanika je uspješno promijenjena. + + + + BitcoinGUI + + + Sign &message... + &Potpišite poruku... + + + + Synchronizing with network... + Usklađivanje s mrežom ... + + + + &Overview + &Pregled + + + + Show general overview of wallet + Prikaži opći pregled novčanika + + + + &Transactions + &Transakcije + + + + Browse transaction history + Pretraži povijest transakcija + + + + Edit the list of stored addresses and labels for sending + Uređivanje popisa pohranjenih adresa i oznaka + + + + Show the list of addresses for receiving payments + Prikaži popis adresa za primanje isplate + + + + E&xit + &Izlaz + + + + Quit application + Izlazak iz programa + + + + Show information about CryptoLottoToken + Prikaži informacije o CryptoLottoTokenu + + + + About &Qt + Više o &Qt + + + + Show information about Qt + Prikaži informacije o Qt + + + + &Options... + &Postavke + + + + &Encrypt Wallet... + &Šifriraj novčanik... + + + + &Backup Wallet... + &Backup novčanika... + + + + &Change Passphrase... + &Promijena lozinke... + + + + Importing blocks from disk... + Importiranje blokova sa diska... + + + + Reindexing blocks on disk... + Re-indeksiranje blokova na disku... + + + + Send coins to a CryptoLottoToken address + Slanje novca na CryptoLottoToken adresu + + + + Modify configuration options for CryptoLottoToken + Promijeni postavke konfiguracije za CryptoLottoToken + + + + Backup wallet to another location + Napravite sigurnosnu kopiju novčanika na drugoj lokaciji + + + + Change the passphrase used for wallet encryption + Promijenite lozinku za šifriranje novčanika + + + + &Debug window + + + + + Open debugging and diagnostic console + + + + + &Verify message... + + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Novčanik + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + &O CryptoLottoTokenu + + + + &Show / Hide + + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + &Datoteka + + + + &Settings + &Konfiguracija + + + + &Help + &Pomoć + + + + Tabs toolbar + Traka kartica + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + CryptoLottoToken klijent + + + + %n active connection(s) to CryptoLottoToken network + %n aktivna veza na CryptoLottoToken mrežu%n aktivne veze na CryptoLottoToken mrežu%n aktivnih veza na CryptoLottoToken mrežu + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + Obrađeno %1 blokova povijesti transakcije. + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + Greška + + + + Warning + + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + Ažurno + + + + Catching up... + Ažuriranje... + + + + Confirm transaction fee + + + + + Sent transaction + Poslana transakcija + + + + Incoming transaction + Dolazna transakcija + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Datum:%1 +Iznos:%2 +Tip:%3 +Adresa:%4 + + + + + + URI handling + + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Novčanik je <b>šifriran</b> i trenutno <b>otključan</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Novčanik je <b>šifriran</b> i trenutno <b>zaključan</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + + + + + EditAddressDialog + + + Edit Address + Izmjeni adresu + + + + &Label + &Oznaka + + + + The label associated with this address book entry + Oznaka ovog upisa u adresar + + + + &Address + &Adresa + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Adresa ovog upisa u adresar. Može se mjenjati samo kod adresa za slanje. + + + + New receiving address + Nova adresa za primanje + + + + New sending address + Nova adresa za slanje + + + + Edit receiving address + Uredi adresu za primanje + + + + Edit sending address + Uredi adresu za slanje + + + + The entered address "%1" is already in the address book. + Upisana adresa "%1" je već u adresaru. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + Upisana adresa "%1" nije valjana CryptoLottoToken adresa. + + + + Could not unlock wallet. + Ne mogu otključati novčanik. + + + + New key generation failed. + Stvaranje novog ključa nije uspjelo. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + + + + + version + verzija + + + + Usage: + Upotreba: + + + + command-line options + + + + + UI options + UI postavke + + + + Set language, for example "de_DE" (default: system locale) + + + + + Start minimized + Pokreni minimiziran + + + + Show splash screen on startup (default: 1) + + + + + OptionsDialog + + + Options + Postavke + + + + &Main + &Glavno + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + Plati &naknadu za transakciju + + + + Automatically start CryptoLottoToken after logging in to the system. + Automatski pokreni CryptoLottoToken kad se uključi računalo + + + + &Start CryptoLottoToken on system login + &Pokreni CryptoLottoToken kod pokretanja sustava + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Automatski otvori port CryptoLottoToken klijenta na ruteru. To radi samo ako ruter podržava UPnP i ako je omogućen. + + + + Map port using &UPnP + Mapiraj port koristeći &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Spojite se na Bitcon mrežu putem SOCKS proxy-a (npr. kod povezivanja kroz Tor) + + + + &Connect through SOCKS proxy: + &Povezivanje putem SOCKS proxy-a: + + + + Proxy &IP: + + + + + IP address of the proxy (e.g. 127.0.0.1) + IP adresa proxy-a (npr. 127.0.0.1) + + + + &Port: + + + + + Port of the proxy (e.g. 9050) + Port od proxy-a (npr. 9050) + + + + SOCKS &Version: + + + + + SOCKS version of the proxy (e.g. 5) + + + + + &Window + + + + + Show only a tray icon after minimizing the window. + Prikaži samo ikonu u sistemskoj traci nakon minimiziranja prozora + + + + &Minimize to the tray instead of the taskbar + &Minimiziraj u sistemsku traku umjesto u traku programa + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Minimizirati umjesto izaći iz aplikacije kada je prozor zatvoren. Kada je ova opcija omogućena, aplikacija će biti zatvorena tek nakon odabira Izlaz u izborniku. + + + + M&inimize on close + M&inimiziraj kod zatvaranja + + + + &Display + &Prikaz + + + + User Interface &language: + + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + + + + + &Unit to show amounts in: + &Jedinica za prikazivanje iznosa: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Izaberite željeni najmanji dio CryptoLottoTokena koji će biti prikazan u sučelju i koji će se koristiti za plaćanje. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + + + &Display addresses in transaction list + &Prikaži adrese u popisu transakcija + + + + &OK + + + + + &Cancel + + + + + &Apply + + + + + default + + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + Upozorenje + + + + + This setting will take effect after restarting CryptoLottoToken. + + + + + The supplied proxy address is invalid. + + + + + OverviewPage + + + Form + Oblik + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + + + + + Balance: + Stanje: + + + + Unconfirmed: + Nepotvrđene: + + + + Wallet + Novčanik + + + + Immature: + + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + <b>Nedavne transakcije</b> + + + + Your current balance + Vaše trenutno stanje računa + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Ukupni iznos transakcija koje tek trebaju biti potvrđene, i još uvijek nisu uračunate u trenutni saldo + + + + + out of sync + + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + QR Code Dijalog + + + + Request Payment + Zatraži plaćanje + + + + Amount: + Iznos: + + + + Label: + Oznaka + + + + Message: + Poruka: + + + + &Save As... + &Spremi kao... + + + + Error encoding URI into QR Code. + + + + + The entered amount is invalid, please check. + + + + + Resulting URI too long, try to reduce the text for label / message. + Rezultirajući URI je predug, probajte umanjiti tekst za naslov / poruku. + + + + Save QR Code + + + + + PNG Images (*.png) + PNG slike (*.png) + + + + RPCConsole + + + Client name + + + + + + + + + + + + + + N/A + + + + + Client version + + + + + &Information + + + + + Using OpenSSL version + + + + + Startup time + + + + + Network + + + + + Number of connections + + + + + On testnet + + + + + Block chain + Lanac blokova + + + + Current number of blocks + Trenutni broj blokova + + + + Estimated total blocks + Procjenjeni ukupni broj blokova + + + + Last block time + Posljednje vrijeme bloka + + + + &Open + + + + + Command-line options + + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + + &Show + + + + + &Console + + + + + Build date + + + + + CryptoLottoToken - Debug window + + + + + CryptoLottoToken Core + + + + + Debug log file + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + + + + + Welcome to the CryptoLottoToken RPC console. + + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + + + + + Type <b>help</b> for an overview of available commands. + + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Slanje novca + + + + Send to multiple recipients at once + Pošalji k nekoliko primatelja odjednom + + + + Add &Recipient + &Dodaj primatelja + + + + Remove all transaction fields + Obriši sva polja transakcija + + + + Clear &All + Obriši &sve + + + + Balance: + Stanje: + + + + 123.456 BTC + 123,456 BTC + + + + Confirm the send action + Potvrdi akciju slanja + + + + S&end + &Pošalji + + + + <b>%1</b> to %2 (%3) + <b>%1</b> do %2 (%3) + + + + Confirm send coins + Potvrdi slanje novca + + + + Are you sure you want to send %1? + Jeste li sigurni da želite poslati %1? + + + + and + i + + + + The recipient address is not valid, please recheck. + Adresa primatelja je nevaljala, molimo provjerite je ponovo. + + + + The amount to pay must be larger than 0. + Iznos mora biti veći od 0. + + + + The amount exceeds your balance. + Iznos je veći od stanja računa. + + + + The total exceeds your balance when the %1 transaction fee is included. + Iznos je veći od stanja računa kad se doda naknada za transakcije od %1. + + + + Duplicate address found, can only send to each address once per send operation. + Pronašli smo adresu koja se ponavlja. U svakom plaćanju program može svaku adresu koristiti samo jedanput. + + + + Error: Transaction creation failed! + + + + + 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. + Generirani novčići moraju pričekati nastanak 120 blokova prije nego što ih je moguće potrošiti. Kad ste generirali taj blok, on je bio emitiran u mrežu kako bi bio dodan postojećim lancima blokova. Ako ne uspije biti dodan, njegov status bit će promijenjen u "nije prihvatljiv" i on neće biti potrošiv. S vremena na vrijeme tako nešto se može desiti ako neki drugi nod približno istovremeno generira blok. + + + + SendCoinsEntry + + + Form + Oblik + + + + A&mount: + &Iznos: + + + + Pay &To: + &Primatelj plaćanja: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + Unesite oznaku za ovu adresu kako bi ju dodali u vaš adresar + + + + &Label: + &Oznaka: + + + + Choose address from address book + Odaberite adresu iz adresara + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Zalijepi adresu iz međuspremnika + + + + Alt+P + Alt+P + + + + Remove this recipient + Ukloni ovog primatelja + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Unesite CryptoLottoToken adresu (npr. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + + + + + &Sign Message + &Potpišite poruku + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Možete potpisati poruke sa svojom adresom kako bi dokazali da ih posjedujete. Budite oprezni da ne potpisujete ništa mutno, jer bi vas phishing napadi mogli na prevaru natjerati da prepišete svoj identitet njima. Potpisujte samo detaljno objašnjene izjave sa kojima se slažete. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Unesite CryptoLottoToken adresu (npr. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Odaberite adresu iz adresara + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Zalijepi adresu iz međuspremnika + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Upišite poruku koju želite potpisati ovdje + + + + Signature + + + + + Copy the current signature to the system clipboard + + + + + Sign the message to prove you own this CryptoLottoToken address + + + + + Sign &Message + + + + + Reset all sign message fields + + + + + + Clear &All + Obriši &sve + + + + &Verify Message + + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Unesite CryptoLottoToken adresu (npr. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + + + + + Verify &Message + + + + + Reset all verify message fields + + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Unesite CryptoLottoToken adresu (npr. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + + + + + Enter CryptoLottoToken signature + + + + + + The entered address is invalid. + + + + + + + + Please check the address and try again. + + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + + + + + Private key for the entered address is not available. + + + + + Message signing failed. + + + + + Message signed. + + + + + The signature could not be decoded. + + + + + + Please check the signature and try again. + + + + + The signature did not match the message digest. + + + + + Message verification failed. + + + + + Message verified. + + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + Otvoren do %1 + + + + %1/offline + %1 nije dostupan + + + + %1/unconfirmed + %1/nepotvrđeno + + + + %1 confirmations + %1 potvrda + + + + Status + Status + + + + , broadcast through %n node(s) + + + + + Date + Datum + + + + Source + + + + + Generated + Generiran + + + + + From + Od + + + + + + To + Za + + + + + own address + + + + + label + oznaka + + + + + + + + Credit + Uplaćeno + + + + matures in %n more block(s) + + + + + not accepted + Nije prihvaćeno + + + + + + + Debit + Zaduženje + + + + Transaction fee + Naknada za transakciju + + + + Net amount + Neto iznos + + + + Message + Poruka + + + + Comment + Komentar + + + + Transaction ID + + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Generirani novčići moraju pričekati nastanak 120 blokova prije nego što ih je moguće potrošiti. Kad ste generirali taj blok, on je bio emitiran u mrežu kako bi bio dodan postojećim lancima blokova. Ako ne uspije biti dodan, njegov status bit će promijenjen u "nije prihvaćen" i on neće biti potrošiv. S vremena na vrijeme tako nešto se može desiti ako neki drugi nod generira blok u približno isto vrijeme. + + + + Debug information + + + + + Transaction + + + + + Inputs + + + + + Amount + Iznos + + + + true + + + + + false + + + + + , has not been successfully broadcast yet + , još nije bio uspješno emitiran + + + + Open for %n more block(s) + + + + + unknown + nepoznato + + + + TransactionDescDialog + + + Transaction details + Detalji transakcije + + + + This pane shows a detailed description of the transaction + Ova panela prikazuje detaljni opis transakcije + + + + TransactionTableModel + + + Date + Datum + + + + Type + Tip + + + + Address + Adresa + + + + Amount + Iznos + + + + Open for %n more block(s) + + + + + Open until %1 + Otvoren do %1 + + + + Offline (%1 confirmations) + Nije na mreži (%1 potvrda) + + + + Unconfirmed (%1 of %2 confirmations) + Nepotvrđen (%1 od %2 potvrda) + + + + Confirmed (%1 confirmations) + Potvrđen (%1 potvrda) + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + Generirano - Upozorenje: ovaj blok nije bio primljen od strane bilo kojeg drugog noda i vjerojatno neće biti prihvaćen! + + + + Generated but not accepted + Generirano, ali nije prihvaćeno + + + + Received with + Primljeno s + + + + Received from + Primljeno od + + + + Sent to + Poslano za + + + + Payment to yourself + Plaćanje samom sebi + + + + Mined + Rudareno + + + + (n/a) + (n/d) + + + + Transaction status. Hover over this field to show number of confirmations. + Status transakcije + + + + Date and time that the transaction was received. + Datum i vrijeme kad je transakcija primljena + + + + Type of transaction. + Vrsta transakcije. + + + + Destination address of transaction. + Odredište transakcije + + + + Amount removed from or added to balance. + Iznos odbijen od ili dodan k saldu. + + + + TransactionView + + + + All + Sve + + + + Today + Danas + + + + This week + Ovaj tjedan + + + + This month + Ovaj mjesec + + + + Last month + Prošli mjesec + + + + This year + Ove godine + + + + Range... + Raspon... + + + + Received with + Primljeno s + + + + Sent to + Poslano za + + + + To yourself + Tebi + + + + Mined + Rudareno + + + + Other + Ostalo + + + + Enter address or label to search + Unesite adresu ili oznaku za pretraživanje + + + + Min amount + Min iznos + + + + Copy address + Kopirati adresu + + + + Copy label + Kopirati oznaku + + + + Copy amount + Kopiraj iznos + + + + Copy transaction ID + + + + + Edit label + Izmjeniti oznaku + + + + Show transaction details + + + + + Export Transaction Data + Izvoz podataka transakcija + + + + Comma separated file (*.csv) + Datoteka podataka odvojenih zarezima (*.csv) + + + + Confirmed + Potvrđeno + + + + Date + Datum + + + + Type + Tip + + + + Label + Oznaka + + + + Address + Adresa + + + + Amount + Iznos + + + + ID + ID + + + + Error exporting + Izvoz pogreške + + + + Could not write to file %1. + Ne mogu pisati u datoteku %1. + + + + Range: + Raspon: + + + + to + za + + + + WalletModel + + + Send Coins + Slanje novca + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + Izvoz podataka iz trenutnog taba u datoteku + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + CryptoLottoToken verzija + + + + Usage: + Upotreba: + + + + Send command to -server or CryptoLottoTokend + Pošalji komandu usluzi -server ili CryptoLottoTokend + + + + List commands + Prikaži komande + + + + Get help for a command + Potraži pomoć za komandu + + + + Options: + Postavke: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Odredi konfiguracijsku datoteku (ugrađeni izbor: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + Odredi proces ID datoteku (ugrađeni izbor: CryptoLottoToken.pid) + + + + Specify data directory + Odredi direktorij za datoteke + + + + Set database cache size in megabytes (default: 25) + Postavi cache za bazu podataka u MB (zadano:25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Slušaj na <port>u (default: 22556 ili testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Održavaj najviše <n> veza sa članovima (default: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + + + + + Specify your own public address + + + + + Threshold for disconnecting misbehaving peers (default: 100) + Prag za odspajanje članova koji se čudno ponašaju (default: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Broj sekundi koliko se članovima koji se čudno ponašaju neće dopustiti da se opet spoje (default: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Prihvaćaj JSON-RPC povezivanje na portu broj <port> (ugrađeni izbor: 22555 or testnet: 44555) + + + + Accept command line and JSON-RPC commands + Prihvati komande iz tekst moda i JSON-RPC + + + + Run in the background as a daemon and accept commands + Izvršavaj u pozadini kao uslužnik i prihvaćaj komande + + + + Use the test network + Koristi test mrežu + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Upozorenje: -paytxfee je podešen na preveliki iznos. To je iznos koji ćete platiti za obradu transakcije. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Upozorenje: Molimo provjerite jesu li datum i vrijeme na vašem računalu točni. Ako vaš sat ide krivo, CryptoLottoToken neće raditi ispravno. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + Opcije za kreiranje bloka: + + + + Connect only to the specified node(s) + Poveži se samo sa određenim nodom + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + Importiraj blokove sa vanjskog blk000??.dat fajla + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + Nevaljala -tor adresa: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + + Only accept block chain matching built-in checkpoints (default: 1) + Prihvati samo lance blokova koji se podudaraju sa ugrađenim checkpoint-ovima (default: 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + Dodaj izlaz debuga na početak sa vremenskom oznakom + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + SSL postavke: (za detalje o podešavanju SSL opcija vidi CryptoLottoToken Wiki) + + + + Select the version of socks proxy to use (4-5, default: 5) + + + + + Send trace/debug info to console instead of debug.log file + Šalji trace/debug informacije na konzolu umjesto u debug.log datoteku + + + + Send trace/debug info to debugger + Pošalji trace/debug informacije u debugger + + + + Set maximum block size in bytes (default: 250000) + Podesite maksimalnu veličinu bloka u bajtovima (default: 250000) + + + + Set minimum block size in bytes (default: 0) + Podesite minimalnu veličinu bloka u bajtovima (default: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + Odredi vremenski prozor za spajanje na mrežu u milisekundama (ugrađeni izbor: 5000) + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + Pokušaj koristiti UPnP da otvoriš port za uslugu (default: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Pokušaj koristiti UPnP da otvoriš port za uslugu (default: 1 when listening) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + Korisničko ime za JSON-RPC veze + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + Lozinka za JSON-RPC veze + + + + Allow JSON-RPC connections from specified IP address + Dozvoli JSON-RPC povezivanje s određene IP adrese + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Pošalji komande nodu na adresi <ip> (ugrađeni izbor: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Izvršite naredbu kada se najbolji blok promjeni (%s u cmd je zamjenjen sa block hash) + + + + Upgrade wallet to latest format + Nadogradite novčanik u posljednji format. + + + + Set key pool size to <n> (default: 100) + Podesi memorijski prostor za ključeve na <n> (ugrađeni izbor: 100) + + + + Rescan the block chain for missing wallet transactions + Ponovno pretraži lanac blokova za transakcije koje nedostaju + + + + Use OpenSSL (https) for JSON-RPC connections + Koristi OpenSSL (https) za JSON-RPC povezivanje + + + + Server certificate file (default: server.cert) + Uslužnikov SSL certifikat (ugrađeni izbor: server.cert) + + + + Server private key (default: server.pem) + Uslužnikov privatni ključ (ugrađeni izbor: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Prihvaljivi načini šifriranja (ugrađeni izbor: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Ova poruka za pomoć + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Program ne može koristiti %s na ovom računalu (bind returned error %d, %s) + + + + Connect through socks proxy + Poveži se kroz socks proxy + + + + Allow DNS lookups for -addnode, -seednode and -connect + Dozvoli DNS upite za dodavanje nodova i povezivanje + + + + Loading addresses... + Učitavanje adresa... + + + + Error loading wallet.dat: Wallet corrupted + Greška kod učitavanja wallet.dat: Novčanik pokvaren + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Greška kod učitavanja wallet.dat: Novčanik zahtjeva noviju verziju CryptoLottoTokena + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Novčanik je trebao prepravak: ponovo pokrenite CryptoLottoToken + + + + Error loading wallet.dat + Greška kod učitavanja wallet.dat + + + + Invalid -proxy address: '%s' + Nevaljala -proxy adresa: '%s' + + + + Unknown network specified in -onlynet: '%s' + + + + + Unknown -socks proxy version requested: %i + + + + + Cannot resolve -bind address: '%s' + + + + + Cannot resolve -externalip address: '%s' + + + + + Invalid amount for -paytxfee=<amount>: '%s' + Nevaljali iznos za opciju -paytxfee=<amount>: '%s' + + + + Invalid amount + Nevaljali iznos za opciju + + + + Insufficient funds + Nedovoljna sredstva + + + + Loading block index... + Učitavanje indeksa blokova... + + + + Add a node to connect to and attempt to keep the connection open + Unesite nod s kojim se želite spojiti and attempt to keep the connection open + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Program ne može koristiti %s na ovom računalu. CryptoLottoToken program je vjerojatno već pokrenut. + + + + Fee per KB to add to transactions you send + Naknada posredniku po KB-u koja će biti dodana svakoj transakciji koju pošalješ + + + + Loading wallet... + Učitavanje novčanika... + + + + Cannot downgrade wallet + Nije moguće novčanik vratiti na prijašnju verziju. + + + + Cannot write default address + Nije moguće upisati zadanu adresu. + + + + Rescanning... + Rescaniranje + + + + Done loading + Učitavanje gotovo + + + + To use the %s option + + + + + Error + Greška + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_hu.qm b/src/qt/locale/bitcoin_hu.qm new file mode 100644 index 0000000..06557fd Binary files /dev/null and b/src/qt/locale/bitcoin_hu.qm differ diff --git a/src/qt/locale/bitcoin_hu.ts b/src/qt/locale/bitcoin_hu.ts new file mode 100644 index 0000000..4829837 --- /dev/null +++ b/src/qt/locale/bitcoin_hu.ts @@ -0,0 +1,2950 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + A CryptoLottoTokenról + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> verzió + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +Ez egy kísérleti program. +MIT/X11 szoftverlicenc alatt kiadva, lásd a mellékelt fájlt COPYING vagy http://www.opensource.org/licenses/mit-license.php. + +Ez a termék az OpenSSL Project által lett kifejlesztve az OpenSSL Toolkit (http://www.openssl.org/) és kriptográfiai szoftvertben való felhasználásra, írta Eric Young (eay@cryptsoft.com) és UPnP szoftver, írta Thomas Bernard. + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + Címjegyzék + + + + Double-click to edit address or label + Dupla-kattintás a cím vagy a címke szerkesztéséhez + + + + Create a new address + Új cím létrehozása + + + + Copy the currently selected address to the system clipboard + A kiválasztott cím másolása a vágólapra + + + + &New Address + &Új cím + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Ezekkel a CryptoLottoToken-címekkel fogadhatod kifizetéseket. Érdemes lehet minden egyes kifizető számára külön címet létrehozni, hogy könnyebben nyomon követhesd, kitől kaptál már pénzt. + + + + &Copy Address + &Cím másolása + + + + Show &QR Code + &QR kód mutatása + + + + Sign a message to prove you own a CryptoLottoToken address + + + + + Sign &Message + + + + + Delete the currently selected address from the list + + + + + Export the data in the current tab to a file + Jelenlegi nézet exportálása fájlba + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Üzenet ellenőrzése, hogy valóban a megjelölt CryptoLottoToken címekkel van-e aláírva. + + + + &Verify Message + Üzenet ellenőrzése + + + + &Delete + &Törlés + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + Címke &másolása + + + + &Edit + Sz&erkesztés + + + + Send &Coins + + + + + Export Address Book Data + Címjegyzék adatainak exportálása + + + + Comma separated file (*.csv) + Vesszővel elválasztott fájl (*. csv) + + + + Error exporting + Hiba exportálás közben + + + + Could not write to file %1. + %1 nevű fájl nem írható. + + + + AddressTableModel + + + Label + Címke + + + + Address + Cím + + + + (no label) + (nincs címke) + + + + AskPassphraseDialog + + + Passphrase Dialog + Kulcsszó párbeszédablak + + + + Enter passphrase + Add meg a jelszót + + + + New passphrase + Új jelszó + + + + Repeat new passphrase + Új jelszó újra + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Írd be az új jelszót a tárcához.<br/>Használj legalább 10<br/>véletlenszerű karaktert</b> vagy <b>legalább nyolc szót</b>. + + + + Encrypt wallet + Tárca kódolása + + + + This operation needs your wallet passphrase to unlock the wallet. + A tárcád megnyitásához a műveletnek szüksége van a tárcád jelszavára. + + + + Unlock wallet + Tárca megnyitása + + + + This operation needs your wallet passphrase to decrypt the wallet. + A tárcád dekódolásához a műveletnek szüksége van a tárcád jelszavára. + + + + Decrypt wallet + Tárca dekódolása + + + + Change passphrase + Jelszó megváltoztatása + + + + Enter the old and new passphrase to the wallet. + Írd be a tárca régi és új jelszavát. + + + + Confirm wallet encryption + Biztosan kódolni akarod a tárcát? + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Figyelem: Ha kódolod a tárcát, és elveszíted a jelszavad, akkor <b>AZ ÖSSZES CryptoLottoTokenODAT IS EL FOGOD VESZÍTENI!</b> + + + + Are you sure you wish to encrypt your wallet? + Biztosan kódolni akarod a tárcát? + + + + 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. + FONTOS: A pénztárca-fájl korábbi mentéseit ezzel az új, titkosított pénztárca-fájllal kell helyettesíteni. Biztonsági okokból a pénztárca-fájl korábbi titkosítás nélküli mentései haszontalanná válnak amint elkezdi használni az új, titkosított pénztárcát. + + + + + Warning: The Caps Lock key is on! + + + + + + Wallet encrypted + Tárca kódolva + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken will close now to finish the encryption process. Ne feledd, hogy a tárca titkosítása sem nyújt teljes védelmet az adathalász programok fertőzésével szemben. + + + + + + + Wallet encryption failed + Tárca kódolása sikertelen. + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Tárca kódolása belső hiba miatt sikertelen. A tárcád nem lett kódolva. + + + + + The supplied passphrases do not match. + A megadott jelszavak nem egyeznek. + + + + Wallet unlock failed + Tárca megnyitása sikertelen + + + + + + The passphrase entered for the wallet decryption was incorrect. + Hibás jelszó. + + + + Wallet decryption failed + Dekódolás sikertelen. + + + + Wallet passphrase was successfully changed. + Jelszó megváltoztatva. + + + + BitcoinGUI + + + Sign &message... + Üzenet aláírása... + + + + Synchronizing with network... + Szinkronizálás a hálózattal... + + + + &Overview + &Áttekintés + + + + Show general overview of wallet + Tárca általános áttekintése + + + + &Transactions + &Tranzakciók + + + + Browse transaction history + Tranzakciótörténet megtekintése + + + + Edit the list of stored addresses and labels for sending + Tárolt címek és címkék listájának szerkesztése + + + + Show the list of addresses for receiving payments + Kiizetést fogadó címek listája + + + + E&xit + &Kilépés + + + + Quit application + Kilépés + + + + Show information about CryptoLottoToken + Információk a CryptoLottoTokenról + + + + About &Qt + A &Qt-ról + + + + Show information about Qt + Információk a Qt ról + + + + &Options... + &Opciók... + + + + &Encrypt Wallet... + Tárca &kódolása... + + + + &Backup Wallet... + &Bisztonsági másolat készítése a Tárcáról + + + + &Change Passphrase... + Jelszó &megváltoztatása... + + + + Importing blocks from disk... + A blokkok importálása lemezről... + + + + Reindexing blocks on disk... + A blokkok lemezen történő ujraindexelése... + + + + Send coins to a CryptoLottoToken address + Érmék küldése megadott címre + + + + Modify configuration options for CryptoLottoToken + CryptoLottoToken konfigurációs opciók + + + + Backup wallet to another location + Biztonsági másolat készítése a Tárcáról egy másik helyre + + + + Change the passphrase used for wallet encryption + Tárcakódoló jelszó megváltoztatása + + + + &Debug window + &Debug ablak + + + + Open debugging and diagnostic console + Hibakereső és diagnosztikai konzol megnyitása + + + + &Verify message... + Üzenet &valódiságának ellenőrzése + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Tárca + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + &A CryptoLottoTokenról + + + + &Show / Hide + + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + A pénztárcájához tartozó privát kulcsok titkosítása + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Üzenet aláírása a CryptoLottoToken címmel, amivel bizonyítja, hogy a cím az ön tulajdona. + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Annak ellenőrzése, hogy az üzenetek valóban a megjelölt CryptoLottoToken címekkel vannak-e alaírva + + + + &File + &Fájl + + + + &Settings + &Beállítások + + + + &Help + &Súgó + + + + Tabs toolbar + Fül eszköztár + + + + + [testnet] + [teszthálózat] + + + + CryptoLottoToken client + CryptoLottoToken kliens + + + + %n active connection(s) to CryptoLottoToken network + %n aktív kapcsolat a CryptoLottoToken-hálózattal%n aktív kapcsolat a CryptoLottoToken-hálózattal + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + A tranzakció-történet %1 blokkja feldolgozva. + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + + + + + Warning + + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + Naprakész + + + + Catching up... + Frissítés... + + + + Confirm transaction fee + Tranzakciós díj jóváhagyása + + + + Sent transaction + Tranzakció elküldve. + + + + Incoming transaction + Beérkező tranzakció + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Dátum: %1 +Összeg: %2 +Típus: %3 +Cím: %4 + + + + + + URI handling + + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Tárca <b>kódolva</b> és jelenleg <b>nyitva</b>. + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Tárca <b>kódolva</b> és jelenleg <b>zárva</b>. + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + + + + + EditAddressDialog + + + Edit Address + Cím szerkesztése + + + + &Label + Cím&ke + + + + The label associated with this address book entry + A címhez tartozó címke + + + + &Address + &Cím + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Az ehhez a címjegyzék-bejegyzéshez tartozó cím. Ez csak a küldő címeknél módosítható. + + + + New receiving address + Új fogadó cím + + + + New sending address + Új küldő cím + + + + Edit receiving address + Fogadó cím szerkesztése + + + + Edit sending address + Küldő cím szerkesztése + + + + The entered address "%1" is already in the address book. + A megadott "%1" cím már szerepel a címjegyzékben. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + A megadott "%1" cím nem egy érvényes CryptoLottoToken-cím. + + + + Could not unlock wallet. + Tárca feloldása sikertelen + + + + New key generation failed. + Új kulcs generálása sikertelen + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + + + + + version + verzió + + + + Usage: + Használat: + + + + command-line options + parancssoros opciók + + + + UI options + UI opciók + + + + Set language, for example "de_DE" (default: system locale) + + + + + Start minimized + Indítás lekicsinyítve + + + + + Show splash screen on startup (default: 1) + + + + + OptionsDialog + + + Options + Opciók + + + + &Main + &Fő + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + Tranzakciós &díj fizetése + + + + Automatically start CryptoLottoToken after logging in to the system. + Induljon el a CryptoLottoToken a számítógép bekapcsolásakor + + + + &Start CryptoLottoToken on system login + &Induljon el a számítógép bekapcsolásakor + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + A CryptoLottoToken-kliens portjának automatikus megnyitása a routeren. Ez csak akkor működik, ha a routered támogatja az UPnP-t és az engedélyezve is van rajta. + + + + Map port using &UPnP + &UPnP port-feltérképezés + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + SOCKS proxyn keresztüli csatlakozás a CryptoLottoToken hálózatához (pl. Tor-on keresztüli csatlakozás esetén) + + + + &Connect through SOCKS proxy: + &Csatlakozás SOCKS proxyn keresztül: + + + + Proxy &IP: + + + + + IP address of the proxy (e.g. 127.0.0.1) + Proxy IP címe (pl.: 127.0.0.1) + + + + &Port: + + + + + Port of the proxy (e.g. 9050) + Proxy portja (pl.: 9050) + + + + SOCKS &Version: + + + + + SOCKS version of the proxy (e.g. 5) + + + + + &Window + + + + + Show only a tray icon after minimizing the window. + Kicsinyítés után csak eszköztár-ikont mutass + + + + &Minimize to the tray instead of the taskbar + &Kicsinyítés a tálcára az eszköztár helyett + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Az alkalmazásból való kilépés helyett az eszköztárba kicsinyíti az alkalmazást az ablak bezárásakor. Ez esetben az alkalmazás csak a Kilépés menüponttal zárható be. + + + + M&inimize on close + K&icsinyítés záráskor + + + + &Display + &Megjelenítés + + + + User Interface &language: + + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + + + + + &Unit to show amounts in: + &Mértékegység: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Válaszd ki az interfészen és érmék küldésekor megjelenítendő alapértelmezett alegységet. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + + + &Display addresses in transaction list + &Címek megjelenítése a tranzakciólistában + + + + &OK + + + + + &Cancel + Megszakítás + + + + &Apply + Alkalmazás + + + + default + alapértelmezett + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + Figyelem + + + + + This setting will take effect after restarting CryptoLottoToken. + Ez a beállítás a CryptoLottoToken ujraindítása után lép érvénybe. + + + + The supplied proxy address is invalid. + + + + + OverviewPage + + + Form + Űrlap + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + A kijelzett információ lehet, hogy elavult. A pénztárcája automatikusan szinkronizálja magát a CryptoLottoToken hálózattal miután a kapcsolat létrejön, de ez e folyamat még nem fejeződött be. + + + + Balance: + Egyenleg: + + + + Unconfirmed: + Megerősítetlen: + + + + Wallet + Tárca + + + + Immature: + + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + <b>Legutóbbi tranzakciók</b> + + + + Your current balance + Aktuális egyenleged + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Még megerősítésre váró, a jelenlegi egyenlegbe be nem számított tranzakciók + + + + + out of sync + Nincs szinkronban. + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + QR kód párbeszédablak + + + + Request Payment + Fizetés kérése + + + + Amount: + Összeg: + + + + Label: + Címke: + + + + Message: + Üzenet: + + + + &Save As... + Mentés má&sként + + + + Error encoding URI into QR Code. + Hiba lépett fel az URI QR kóddá alakításakor + + + + The entered amount is invalid, please check. + A megadott összeg nem érvényes. Kérem ellenőrizze. + + + + Resulting URI too long, try to reduce the text for label / message. + A keletkezett URI túl hosszú, próbálja meg csökkenteni a cimkeszöveg / üzenet méretét. + + + + Save QR Code + QR kód mentése + + + + PNG Images (*.png) + PNG Képfájlok (*.png) + + + + RPCConsole + + + Client name + Kliens néve + + + + + + + + + + + + + N/A + Nem elérhető + + + + Client version + Kliens verzió + + + + &Information + &Információ + + + + Using OpenSSL version + + + + + Startup time + Bekapcsolás ideje + + + + Network + Hálózat + + + + Number of connections + Kapcsolatok száma + + + + On testnet + Teszthálózaton + + + + Block chain + Blokklánc + + + + Current number of blocks + Aktuális blokkok száma + + + + Estimated total blocks + Becsült összes blokk + + + + Last block time + Utolsó blokk ideje + + + + &Open + &Megnyitás + + + + Command-line options + + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + + &Show + + + + + &Console + &Konzol + + + + Build date + Fordítás dátuma + + + + CryptoLottoToken - Debug window + + + + + CryptoLottoToken Core + + + + + Debug log file + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + Konzol törlése + + + + Welcome to the CryptoLottoToken RPC console. + + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Navigálhat a fel és le nyilakkal, és <b>Ctrl-L</b> -vel törölheti a képernyőt. + + + + Type <b>help</b> for an overview of available commands. + + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Érmék küldése + + + + Send to multiple recipients at once + Küldés több címzettnek egyszerre + + + + Add &Recipient + &Címzett hozzáadása + + + + Remove all transaction fields + Az összes tranzakciós mező eltávolítása + + + + Clear &All + Mindent &töröl + + + + Balance: + Egyenleg: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Küldés megerősítése + + + + S&end + &Küldés + + + + <b>%1</b> to %2 (%3) + <b>%1</b> %2-re (%3) + + + + Confirm send coins + Küldés megerősítése + + + + Are you sure you want to send %1? + Valóban el akarsz küldeni %1-t? + + + + and + és + + + + The recipient address is not valid, please recheck. + A címzett címe érvénytelen, kérlek, ellenőrizd. + + + + The amount to pay must be larger than 0. + A fizetendő összegnek nagyobbnak kell lennie 0-nál. + + + + The amount exceeds your balance. + Nincs ennyi CryptoLottoToken az egyenlegeden. + + + + The total exceeds your balance when the %1 transaction fee is included. + A küldeni kívánt összeg és a %1 tranzakciós díj együtt meghaladja az egyenlegeden rendelkezésedre álló összeget. + + + + Duplicate address found, can only send to each address once per send operation. + Többször szerepel ugyanaz a cím. Egy küldési műveletben egy címre csak egyszer lehet küldeni. + + + + Error: Transaction creation failed! + + + + + 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. + Hiba: a tranzakciót elutasították. Ezt az okozhatja, ha már elköltöttél valamennyi érmét a tárcádból például ha a wallet.dat-od egy másolatát használtad, és így az elköltés csak abban lett jelölve, de itt nem. + + + + SendCoinsEntry + + + Form + Űrlap + + + + A&mount: + Összeg: + + + + Pay &To: + Címzett: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + Milyen címkével kerüljön be ez a cím a címtáradba? + + + + + &Label: + Címke: + + + + Choose address from address book + Válassz egy címet a címjegyzékből + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Cím beillesztése a vágólapról + + + + Alt+P + Alt+P + + + + Remove this recipient + Címzett eltávolítása + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Adj meg egy CryptoLottoToken-címet (pl.: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2 ) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + + + + + &Sign Message + Üzenet aláírása... + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Aláírhat a címeivel üzeneteket, amivel bizonyíthatja, hogy a címek az önéi. Vigyázzon, hogy ne írjon alá semmi félreérthetőt, mivel a phising támadásokkal megpróbálhatják becsapni, hogy az azonosságát átírja másokra. Csak olyan részletes állításokat írjon alá, amivel egyetért. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Adj meg egy CryptoLottoToken-címet (pl.: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2 ) + + + + + Choose an address from the address book + Válassz egy címet a címjegyzékből + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Cím beillesztése a vágólapról + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Ide írja az aláírandó üzenetet + + + + Signature + + + + + Copy the current signature to the system clipboard + A jelenleg kiválasztott aláírás másolása a rendszer-vágólapra + + + + Sign the message to prove you own this CryptoLottoToken address + + + + + Sign &Message + + + + + Reset all sign message fields + + + + + + Clear &All + Mindent &töröl + + + + &Verify Message + Üzenet ellenőrzése + + + + Enter the signing 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. + Írja be az aláírás címét, az üzenetet (ügyelve arra, hogy az új-sor, szóköz, tab, stb. karaktereket is pontosan) és az aláírást az üzenet ellenőrzéséhez. Ügyeljen arra, ne gondoljon többet az aláírásról, mint amennyi az aláírt szövegben ténylegesen áll, hogy elkerülje a köztes-ember (man-in-the-middle) támadást. + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Adj meg egy CryptoLottoToken-címet (pl.: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2 ) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + + + + + Verify &Message + + + + + Reset all verify message fields + + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Adj meg egy CryptoLottoToken-címet (pl.: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2 ) + + + + Click "Sign Message" to generate signature + + + + + Enter CryptoLottoToken signature + Adja meg a CryptoLottoToken aláírást + + + + + The entered address is invalid. + A megadott cím nem érvényes. + + + + + + + Please check the address and try again. + Ellenőrizze a címet és próbálja meg újra. + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + + + + + Private key for the entered address is not available. + + + + + Message signing failed. + + + + + Message signed. + + + + + The signature could not be decoded. + + + + + + Please check the signature and try again. + + + + + The signature did not match the message digest. + + + + + Message verification failed. + + + + + Message verified. + + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + [teszthálózat] + + + + TransactionDesc + + + Open until %1 + Megnyitva %1-ig + + + + %1/offline + + + + + %1/unconfirmed + %1/megerősítetlen + + + + %1 confirmations + %1 megerősítés + + + + Status + Állapot + + + + , broadcast through %n node(s) + + + + + Date + Dátum + + + + Source + + + + + Generated + Legenerálva + + + + + From + Űrlap + + + + + + To + Címzett + + + + + own address + + + + + label + címke + + + + + + + + Credit + Jóváírás + + + + matures in %n more block(s) + + + + + not accepted + elutasítva + + + + + + + Debit + Terhelés + + + + Transaction fee + Tranzakciós díj + + + + Net amount + Nettó összeg + + + + Message + Üzenet + + + + Comment + Megjegyzés + + + + Transaction ID + + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + A frissen generált érméket csak 120 blokkal később tudod elkölteni. Ez a blokk nyomban szétküldésre került a hálózatba, amint legeneráltad, hogy hozzáadhassák a blokklánchoz. Ha nem kerül be a láncba, úgy az állapota "elutasítva"-ra módosul, és nem költheted el az érméket. Ez akkor következhet be időnként, ha egy másik csomópont mindössze néhány másodperc különbséggel generált le egy blokkot a tiédhez képest. + + + + Debug information + + + + + Transaction + Tranzakció + + + + Inputs + + + + + Amount + Összeg + + + + true + + + + + false + + + + + , has not been successfully broadcast yet + , még nem sikerült elküldeni. + + + + Open for %n more block(s) + + + + + unknown + ismeretlen + + + + TransactionDescDialog + + + Transaction details + Tranzakció részletei + + + + This pane shows a detailed description of the transaction + Ez a mező a tranzakció részleteit mutatja + + + + TransactionTableModel + + + Date + Dátum + + + + Type + Típus + + + + Address + Cím + + + + Amount + Összeg + + + + Open for %n more block(s) + + + + + Open until %1 + %1-ig megnyitva + + + + Offline (%1 confirmations) + Offline (%1 megerősítés) + + + + Unconfirmed (%1 of %2 confirmations) + Megerősítetlen (%1 %2 megerősítésből) + + + + Confirmed (%1 confirmations) + Megerősítve (%1 megerősítés) + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + Ezt a blokkot egyetlen másik csomópont sem kapta meg, így valószínűleg nem lesz elfogadva! + + + + Generated but not accepted + Legenerálva, de még el nem fogadva. + + + + Received with + Erre a címre + + + + Received from + Erről az + + + + Sent to + Erre a címre + + + + Payment to yourself + Magadnak kifizetve + + + + Mined + Kibányászva + + + + (n/a) + (nincs) + + + + Transaction status. Hover over this field to show number of confirmations. + Tranzakció állapota. Húzd ide a kurzort, hogy lásd a megerősítések számát. + + + + Date and time that the transaction was received. + Tranzakció fogadásának dátuma és időpontja. + + + + Type of transaction. + Tranzakció típusa. + + + + Destination address of transaction. + A tranzakció címzettjének címe. + + + + Amount removed from or added to balance. + Az egyenleghez jóváírt vagy ráterhelt összeg. + + + + TransactionView + + + + All + Mind + + + + Today + Mai + + + + This week + Ezen a héten + + + + This month + Ebben a hónapban + + + + Last month + Múlt hónapban + + + + This year + Ebben az évben + + + + Range... + Tartomány ... + + + + Received with + Erre a címre + + + + Sent to + Erre a címre + + + + To yourself + Magadnak + + + + Mined + Kibányászva + + + + Other + Más + + + + Enter address or label to search + Írd be a keresendő címet vagy címkét + + + + Min amount + Minimális összeg + + + + Copy address + Cím másolása + + + + Copy label + Címke másolása + + + + Copy amount + Összeg másolása + + + + Copy transaction ID + + + + + Edit label + Címke szerkesztése + + + + Show transaction details + Tranzakciós részletek megjelenítése + + + + Export Transaction Data + Tranzakció adatainak exportálása + + + + Comma separated file (*.csv) + Vesszővel elválasztott fájl (*.csv) + + + + Confirmed + Megerősítve + + + + Date + Dátum + + + + Type + Típus + + + + Label + Címke + + + + Address + Cím + + + + Amount + Összeg + + + + ID + Azonosító + + + + Error exporting + Hiba lépett fel exportálás közben + + + + Could not write to file %1. + %1 fájlba való kiírás sikertelen. + + + + Range: + Tartomány: + + + + to + meddig + + + + WalletModel + + + Send Coins + Érmék küldése + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + Jelenlegi nézet exportálása fájlba + + + + Backup Wallet + Biztonsági másolat készítése a Tárcáról + + + + Wallet Data (*.dat) + Tárca fájl (*.dat) + + + + Backup Failed + Biztonsági másolat készítése sikertelen + + + + There was an error trying to save the wallet data to the new location. + Hiba lépett fel a Tárca másik helyre való mentése közben + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + CryptoLottoToken verzió + + + + Usage: + Használat: + + + + Send command to -server or CryptoLottoTokend + Parancs küldése a -serverhez vagy a CryptoLottoTokendhez + + + + + List commands + Parancsok kilistázása + + + + + Get help for a command + Segítség egy parancsról + + + + + Options: + Opciók + + + + + Specify configuration file (default: CryptoLottoToken.conf) + Konfigurációs fájl (alapértelmezett: CryptoLottoToken.conf) + + + + + Specify pid file (default: CryptoLottoTokend.pid) + pid-fájl (alapértelmezett: CryptoLottoTokend.pid) + + + + + Specify data directory + Adatkönyvtár + + + + + Set database cache size in megabytes (default: 25) + Az adatbázis gyorsítótár mérete megabájtban (alapértelmezés: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Csatlakozásokhoz figyelendő <port> (alapértelmezett: 22556 or testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Maximálisan <n> számú kapcsolat fenntartása a peerekkel (alapértelmezés: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Kapcsolódás egy csomóponthoz a peerek címeinek megszerzése miatt, majd szétkapcsolás + + + + Specify your own public address + Adja meg az Ön saját nyilvános címét + + + + Threshold for disconnecting misbehaving peers (default: 100) + Helytelenül viselkedő peerek leválasztási határértéke (alapértelmezés: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Helytelenül viselkedő peerek kizárási ideje másodpercben (alapértelmezés: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + JSON-RPC csatlakozásokhoz figyelendő <port> (alapértelmezett: 22555 or testnet: 44555) + + + + Accept command line and JSON-RPC commands + Parancssoros és JSON-RPC parancsok elfogadása + + + + + Run in the background as a daemon and accept commands + Háttérben futtatás daemonként és parancsok elfogadása + + + + + Use the test network + Teszthálózat használata + + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Figyelem: a -paytxfee nagyon magas. Ennyi tranzakciós díjat fogsz fizetni, ha elküldöd a tranzakciót. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Figyelem: Ellenőrizd, hogy helyesen van-e beállítva a gépeden a dátum és az idő. A CryptoLottoToken nem fog megfelelően működni, ha rosszul van beállítvaaz órád. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + + + + + Connect only to the specified node(s) + Csatlakozás csak a megadott csomóponthoz + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + Érvénytelen -tor cím: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + + Only accept block chain matching built-in checkpoints (default: 1) + Csak blokklánccal egyező beépített ellenőrző pontok elfogadása (alapértelmezés: 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + Időbélyeges hibakeresési kimenet hozzáadása az elejéhez + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + SSL-opciók: (lásd a CryptoLottoToken Wiki SSL-beállítási instrukcióit) + + + + Select the version of socks proxy to use (4-5, default: 5) + + + + + Send trace/debug info to console instead of debug.log file + trace/debug információ küldése a konzolra a debog.log fájl helyett + + + + Send trace/debug info to debugger + trace/debug információ küldése a debuggerre + + + + Set maximum block size in bytes (default: 250000) + + + + + Set minimum block size in bytes (default: 0) + + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + Csatlakozás időkerete milliszekundumban (alapértelmezett: 5000) + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + UPnP-használat engedélyezése a figyelő port feltérképezésénél (default: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + UPnP-használat engedélyezése a figyelő port feltérképezésénél (default: 1 when listening) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + Felhasználói név JSON-RPC csatlakozásokhoz + + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + Jelszó JSON-RPC csatlakozásokhoz + + + + + Allow JSON-RPC connections from specified IP address + JSON-RPC csatlakozások engedélyezése meghatározott IP-címről + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Parancsok küldése <ip> címen működő csomóponthoz (alapértelmezett: 127.0.0.1) + + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Parancs, amit akkor hajt végre, amikor a legjobb blokk megváltozik (%s a cmd-ban lecserélődik a blokk hash-re) + + + + Upgrade wallet to latest format + A Tárca frissítése a legfrissebb formátumra + + + + Set key pool size to <n> (default: 100) + Kulcskarika mérete <n> (alapértelmezett: 100) + + + + + Rescan the block chain for missing wallet transactions + Blokklánc újraszkennelése hiányzó tárca-tranzakciók után + + + + + Use OpenSSL (https) for JSON-RPC connections + OpenSSL (https) használata JSON-RPC csatalkozásokhoz + + + + + Server certificate file (default: server.cert) + Szervertanúsítvány-fájl (alapértelmezett: server.cert) + + + + + Server private key (default: server.pem) + Szerver titkos kulcsa (alapértelmezett: server.pem) + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Elfogadható rejtjelkulcsok (alapértelmezett: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH ) + + + + + This help message + Ez a súgó-üzenet + + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + A %s nem elérhető ezen a gépen (bind returned error %d, %s) + + + + Connect through socks proxy + Csatlakozás SOCKS proxyn keresztül + + + + Allow DNS lookups for -addnode, -seednode and -connect + DNS-kikeresés engedélyezése az addnode-nál és a connect-nél + + + + Loading addresses... + Címek betöltése... + + + + Error loading wallet.dat: Wallet corrupted + Hiba a wallet.dat betöltése közben: meghibásodott tárca + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Hiba a wallet.dat betöltése közben: ehhez a tárcához újabb verziójú CryptoLottoToken-kliens szükséges + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + A Tárca újraírása szükséges: Indítsa újra a teljesen a CryptoLottoToken-t + + + + Error loading wallet.dat + Hiba az wallet.dat betöltése közben + + + + Invalid -proxy address: '%s' + Érvénytelen -proxy cím: '%s' + + + + Unknown network specified in -onlynet: '%s' + Ismeretlen hálózat lett megadva -onlynet: '%s' + + + + Unknown -socks proxy version requested: %i + Ismeretlen -socks proxy kérése: %i + + + + Cannot resolve -bind address: '%s' + + + + + Cannot resolve -externalip address: '%s' + + + + + Invalid amount for -paytxfee=<amount>: '%s' + Étvénytelen -paytxfee=<összeg> összeg: '%s' + + + + Invalid amount + Étvénytelen összeg + + + + Insufficient funds + Nincs elég CryptoLottoTokenod. + + + + Loading block index... + Blokkindex betöltése... + + + + Add a node to connect to and attempt to keep the connection open + Elérendő csomópont megadása and attempt to keep the connection open + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + A %s nem elérhető ezen a gépen. A CryptoLottoToken valószínűleg fut már. + + + + Fee per KB to add to transactions you send + kB-onként felajánlandó díj az általad küldött tranzakciókhoz + + + + Loading wallet... + Tárca betöltése... + + + + Cannot downgrade wallet + Nem sikerült a Tárca visszaállítása a korábbi verzióra + + + + Cannot write default address + Nem sikerült az alapértelmezett címet írni. + + + + Rescanning... + Újraszkennelés... + + + + Done loading + Betöltés befejezve. + + + + To use the %s option + Használd a %s opciót + + + + Error + Hiba + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + Be kell állítani rpcpassword=<password> a konfigurációs fájlban +%s +Ha a fájl nem létezik, hozd létre 'csak a felhasználó által olvasható' fájl engedéllyel + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_it.qm b/src/qt/locale/bitcoin_it.qm new file mode 100644 index 0000000..c47379f Binary files /dev/null and b/src/qt/locale/bitcoin_it.qm differ diff --git a/src/qt/locale/bitcoin_it.ts b/src/qt/locale/bitcoin_it.ts new file mode 100644 index 0000000..6dd0ef7 --- /dev/null +++ b/src/qt/locale/bitcoin_it.ts @@ -0,0 +1,2950 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + Info su CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + Versione di <b>CryptoLottoToken</b> + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +Questo è un software sperimentale. + +Distribuito sotto la licenza software MIT/X11, vedi il file COPYING incluso oppure su http://www.opensource.org/licenses/mit-license.php. + +Questo prodotto include software sviluppato dal progetto OpenSSL per l'uso del Toolkit OpenSSL (http://www.openssl.org/), software crittografico scritto da Eric Young (eay@cryptsoft.com) e software UPnP scritto da Thomas Bernard. + + + + Copyright + Copyright + + + + The CryptoLottoToken developers + Sviluppatori di CryptoLottoToken + + + + AddressBookPage + + + Address Book + Rubrica + + + + Double-click to edit address or label + Fai doppio click per modificare o cancellare l'etichetta + + + + Create a new address + Crea un nuovo indirizzo + + + + Copy the currently selected address to the system clipboard + Copia l'indirizzo attualmente selezionato nella clipboard + + + + &New Address + &Nuovo indirizzo + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Questi sono i tuoi indirizzi CryptoLottoToken per ricevere pagamenti. Potrai darne uno diverso ad ognuno per tenere così traccia di chi ti sta pagando. + + + + &Copy Address + &Copia l'indirizzo + + + + Show &QR Code + Mostra il codice &QR + + + + Sign a message to prove you own a CryptoLottoToken address + Firma un messaggio per dimostrare di possedere questo indirizzo + + + + Sign &Message + Firma il &messaggio + + + + Delete the currently selected address from the list + Cancella l'indirizzo attualmente selezionato dalla lista + + + + Export the data in the current tab to a file + Esporta i dati nella tabella corrente su un file + + + + &Export + &Esporta... + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Verifica un messaggio per accertarsi che sia firmato con un indirizzo CryptoLottoToken specifico + + + + &Verify Message + &Verifica Messaggio + + + + &Delete + &Cancella + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + Copia &l'etichetta + + + + &Edit + &Modifica + + + + Send &Coins + Invia &CryptoLottoToken + + + + Export Address Book Data + Esporta gli indirizzi della rubrica + + + + Comma separated file (*.csv) + Testo CSV (*.csv) + + + + Error exporting + Errore nell'esportazione + + + + Could not write to file %1. + Impossibile scrivere sul file %1. + + + + AddressTableModel + + + Label + Etichetta + + + + Address + Indirizzo + + + + (no label) + (nessuna etichetta) + + + + AskPassphraseDialog + + + Passphrase Dialog + Finestra passphrase + + + + Enter passphrase + Inserisci la passphrase + + + + New passphrase + Nuova passphrase + + + + Repeat new passphrase + Ripeti la passphrase + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Inserisci la passphrase per il portamonete.<br/>Per piacere usare unapassphrase di <b>10 o più caratteri casuali</b>, o <b>otto o più parole</b>. + + + + Encrypt wallet + Cifra il portamonete + + + + This operation needs your wallet passphrase to unlock the wallet. + Quest'operazione necessita della passphrase per sbloccare il portamonete. + + + + Unlock wallet + Sblocca il portamonete + + + + This operation needs your wallet passphrase to decrypt the wallet. + Quest'operazione necessita della passphrase per decifrare il portamonete, + + + + Decrypt wallet + Decifra il portamonete + + + + Change passphrase + Cambia la passphrase + + + + Enter the old and new passphrase to the wallet. + Inserisci la vecchia e la nuova passphrase per il portamonete. + + + + Confirm wallet encryption + Conferma la cifratura del portamonete + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Attenzione: se si cifra il portamonete e si perde la frase d'ordine, <b>SI PERDERANNO TUTTI I PROPRI CryptoLottoToken</b>! + + + + Are you sure you wish to encrypt your wallet? + Si è sicuri di voler cifrare il portamonete? + + + + 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. + IMPORTANTE: qualsiasi backup del portafoglio effettuato precedentemente dovrebbe essere sostituito con il file del portafoglio criptato appena generato. Per ragioni di sicurezza, i backup precedenti del file del portafoglio non criptato diventeranno inservibili non appena si inizi ad usare il nuovo portafoglio criptato. + + + + + Warning: The Caps Lock key is on! + Attenzione: tasto Blocco maiuscole attivo. + + + + + Wallet encrypted + Portamonete cifrato + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken verrà ora chiuso per finire il processo di crittazione. Ricorda che criptare il tuo portamonete non può fornire una protezione totale contro furti causati da malware che dovessero infettare il tuo computer. + + + + + + + Wallet encryption failed + Cifratura del portamonete fallita + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Cifratura del portamonete fallita a causa di un errore interno. Il portamonete non è stato cifrato. + + + + + The supplied passphrases do not match. + Le passphrase inserite non corrispondono. + + + + Wallet unlock failed + Sblocco del portamonete fallito + + + + + + The passphrase entered for the wallet decryption was incorrect. + La passphrase inserita per la decifrazione del portamonete è errata. + + + + Wallet decryption failed + Decifrazione del portamonete fallita + + + + Wallet passphrase was successfully changed. + Passphrase del portamonete modificata con successo. + + + + BitcoinGUI + + + Sign &message... + Firma il &messaggio... + + + + Synchronizing with network... + Sto sincronizzando con la rete... + + + + &Overview + &Sintesi + + + + Show general overview of wallet + Mostra lo stato generale del portamonete + + + + &Transactions + &Transazioni + + + + Browse transaction history + Cerca nelle transazioni + + + + Edit the list of stored addresses and labels for sending + Modifica la lista degli indirizzi salvati e delle etichette + + + + Show the list of addresses for receiving payments + Mostra la lista di indirizzi su cui ricevere pagamenti + + + + E&xit + &Esci + + + + Quit application + Chiudi applicazione + + + + Show information about CryptoLottoToken + Mostra informazioni su CryptoLottoToken + + + + About &Qt + Informazioni su &Qt + + + + Show information about Qt + Mostra informazioni su Qt + + + + &Options... + &Opzioni... + + + + &Encrypt Wallet... + &Cifra il portamonete... + + + + &Backup Wallet... + &Backup Portamonete... + + + + &Change Passphrase... + &Cambia la passphrase... + + + + Importing blocks from disk... + Importa blocchi dal disco... + + + + Reindexing blocks on disk... + Re-indicizzazione blocchi su disco... + + + + Send coins to a CryptoLottoToken address + Invia monete ad un indirizzo CryptoLottoToken + + + + Modify configuration options for CryptoLottoToken + Modifica configurazione opzioni per CryptoLottoToken + + + + Backup wallet to another location + Backup portamonete in un'altra locazione + + + + Change the passphrase used for wallet encryption + Cambia la passphrase per la cifratura del portamonete + + + + &Debug window + Finestra &Debug + + + + Open debugging and diagnostic console + Apri la console di degugging e diagnostica + + + + &Verify message... + &Verifica messaggio... + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Portamonete + + + + &Send + &Spedisci + + + + &Receive + &Ricevi + + + + &Addresses + &Indirizzi + + + + &About CryptoLottoToken + &Info su CryptoLottoToken + + + + &Show / Hide + &Mostra/Nascondi + + + + Show or hide the main Window + Mostra o nascondi la Finestra principale + + + + Encrypt the private keys that belong to your wallet + Crittografa le chiavi private che appartengono al tuo portafoglio + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Firma i messaggi con il tuo indirizzo CryptoLottoToken per dimostrare di possederli + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Verifica i messaggi per accertarsi che siano stati firmati con gli indirizzi CryptoLottoToken specificati + + + + &File + &File + + + + &Settings + &Impostazioni + + + + &Help + &Aiuto + + + + Tabs toolbar + Barra degli strumenti "Tabs" + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + CryptoLottoToken client + + + + %n active connection(s) to CryptoLottoToken network + %n connessione attiva alla rete CryptoLottoToken%n connessioni attive alla rete CryptoLottoToken + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + Processati %1 di %2 (circa) blocchi della cronologia transazioni. + + + + Processed %1 blocks of transaction history. + Processati %1 blocchi della cronologia transazioni. + + + + %n hour(s) + %n ora%n ore + + + + %n day(s) + %n giorno%n giorni + + + + %n week(s) + %n settimana%n settimane + + + + %1 behind + + + + + Last received block was generated %1 ago. + L'ultimo blocco ricevuto è stato generato %1 fa. + + + + Transactions after this will not yet be visible. + + + + + Error + Errore + + + + Warning + Attenzione + + + + Information + Informazione + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + Questa transazione è superiore al limite di dimensione. È comunque possibile inviarla con una commissione di %1, che va ai nodi che processano la tua transazione e contribuisce a sostenere la rete. Vuoi pagare la commissione? + + + + Up to date + Aggiornato + + + + Catching up... + In aggiornamento... + + + + Confirm transaction fee + Conferma compenso transazione + + + + Sent transaction + Transazione inviata + + + + Incoming transaction + Transazione ricevuta + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Data: %1 +Quantità: %2 +Tipo: %3 +Indirizzo: %4 + + + + + + + URI handling + Gestione URI + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + Impossibile interpretare l'URI! Ciò può essere causato da un indirizzo CryptoLottoToken invalido o da parametri URI non corretti. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Il portamonete è <b>cifrato</b> e attualmente <b>sbloccato</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Il portamonete è <b>cifrato</b> e attualmente <b>bloccato</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + Riscontrato un errore irreversibile. CryptoLottoToken non può più continuare in sicurezza e verrà terminato. + + + + ClientModel + + + Network Alert + Avviso di rete + + + + EditAddressDialog + + + Edit Address + Modifica l'indirizzo + + + + &Label + &Etichetta + + + + The label associated with this address book entry + L'etichetta associata a questo indirizzo nella rubrica + + + + &Address + &Indirizzo + + + + The address associated with this address book entry. This can only be modified for sending addresses. + L'indirizzo associato a questa voce della rubrica. Si può modificare solo negli indirizzi di spedizione. + + + + New receiving address + Nuovo indirizzo di ricezione + + + + New sending address + Nuovo indirizzo d'invio + + + + Edit receiving address + Modifica indirizzo di ricezione + + + + Edit sending address + Modifica indirizzo d'invio + + + + The entered address "%1" is already in the address book. + L'indirizzo inserito "%1" è già in rubrica. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + L'indirizzo inserito "%1" non è un indirizzo CryptoLottoToken valido. + + + + Could not unlock wallet. + Impossibile sbloccare il portamonete. + + + + New key generation failed. + Generazione della nuova chiave non riuscita. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + versione + + + + Usage: + Utilizzo: + + + + command-line options + opzioni riga di comando + + + + UI options + UI opzioni + + + + Set language, for example "de_DE" (default: system locale) + Imposta lingua, ad esempio "it_IT" (predefinita: lingua di sistema) + + + + Start minimized + Parti in icona + + + + + Show splash screen on startup (default: 1) + Mostra finestra di presentazione all'avvio (default: 1) + + + + OptionsDialog + + + Options + Opzioni + + + + &Main + &Principale + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + Paga la &commissione + + + + Automatically start CryptoLottoToken after logging in to the system. + Avvia automaticamente CryptoLottoToken all'accensione del computer + + + + &Start CryptoLottoToken on system login + &Fai partire CryptoLottoToken all'avvio del sistema + + + + Reset all client options to default. + Ripristina tutte le opzioni del client alle predefinite. + + + + &Reset Options + &Ripristina Opzioni + + + + &Network + Rete + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Apri automaticamente la porta del client CryptoLottoToken sul router. Questo funziona solo se il router supporta UPnP ed è abilitato. + + + + Map port using &UPnP + Mappa le porte tramite l'&UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Connettiti alla rete Bitcon attraverso un proxy SOCKS (ad esempio quando ci si collega via Tor) + + + + &Connect through SOCKS proxy: + &Collegati tramite SOCKS proxy: + + + + Proxy &IP: + &IP del proxy: + + + + IP address of the proxy (e.g. 127.0.0.1) + Indirizzo IP del proxy (ad esempio 127.0.0.1) + + + + &Port: + &Porta: + + + + Port of the proxy (e.g. 9050) + Porta del proxy (es. 9050) + + + + SOCKS &Version: + SOCKS &Version: + + + + SOCKS version of the proxy (e.g. 5) + Versione SOCKS del proxy (es. 5) + + + + &Window + &Finestra + + + + Show only a tray icon after minimizing the window. + Mostra solo un'icona nel tray quando si minimizza la finestra + + + + &Minimize to the tray instead of the taskbar + &Minimizza sul tray invece che sulla barra delle applicazioni + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Riduci ad icona, invece di uscire dall'applicazione quando la finestra viene chiusa. Quando questa opzione è attivata, l'applicazione verrà chiusa solo dopo aver selezionato Esci nel menu. + + + + M&inimize on close + M&inimizza alla chiusura + + + + &Display + &Mostra + + + + User Interface &language: + &Lingua Interfaccia Utente: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + La lingua dell'interfaccia utente può essere impostata qui. L'impostazione avrà effetto dopo il riavvio di CryptoLottoToken. + + + + &Unit to show amounts in: + &Unità di misura degli importi in: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Scegli l'unità di suddivisione di default per l'interfaccia e per l'invio di monete + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Se mostrare l'indirizzo CryptoLottoToken nella transazione o meno. + + + + &Display addresses in transaction list + &Mostra gli indirizzi nella lista delle transazioni + + + + &OK + &OK + + + + &Cancel + &Cancella + + + + &Apply + &Applica + + + + default + default + + + + Confirm options reset + Conferma ripristino opzioni + + + + Some settings may require a client restart to take effect. + Alcune modifiche necessitano del riavvio del programma per essere salvate. + + + + Do you want to proceed? + Vuoi procedere? + + + + + Warning + Attenzione + + + + + This setting will take effect after restarting CryptoLottoToken. + L'impostazione avrà effetto dopo il riavvio di CryptoLottoToken. + + + + The supplied proxy address is invalid. + L'indirizzo proxy che hai fornito è invalido. + + + + OverviewPage + + + Form + Modulo + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + Le informazioni visualizzate sono datate. Il tuo partafogli verrà sincronizzato automaticamente con il network CryptoLottoToken dopo che la connessione è stabilita, ma questo processo non può essere completato ora. + + + + Balance: + Saldo + + + + Unconfirmed: + Non confermato: + + + + Wallet + Portamonete + + + + Immature: + Immaturo: + + + + Mined balance that has not yet matured + Importo scavato che non è ancora maturato + + + + <b>Recent transactions</b> + <b>Transazioni recenti</b> + + + + Your current balance + Saldo attuale + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Totale delle transazioni in corso di conferma, che non sono ancora incluse nel saldo attuale + + + + + out of sync + fuori sincrono + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + Codice QR di dialogo + + + + Request Payment + Richiedi pagamento + + + + Amount: + Importo: + + + + Label: + Etichetta: + + + + Message: + Messaggio: + + + + &Save As... + &Salva come... + + + + Error encoding URI into QR Code. + Errore nella codifica URI nel codice QR + + + + The entered amount is invalid, please check. + L'importo specificato non è valido, prego verificare. + + + + Resulting URI too long, try to reduce the text for label / message. + L'URI risulta troppo lungo, prova a ridurre il testo nell'etichetta / messaggio. + + + + Save QR Code + Salva codice QR + + + + PNG Images (*.png) + Immagini PNG (*.png) + + + + RPCConsole + + + Client name + Nome del client + + + + + + + + + + + + + N/A + N/D + + + + Client version + Versione client + + + + &Information + &Informazione + + + + Using OpenSSL version + Versione OpenSSL in uso + + + + Startup time + Tempo di avvio + + + + Network + Rete + + + + Number of connections + Numero connessioni + + + + On testnet + Nel testnet + + + + Block chain + Block chain + + + + Current number of blocks + Numero attuale di blocchi + + + + Estimated total blocks + Numero totale stimato di blocchi + + + + Last block time + Ora dell blocco piu recente + + + + &Open + &Apri + + + + Command-line options + opzioni riga di comando + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Mostra il messaggio di aiuto di CryptoLottoToken-QT per avere la lista di tutte le opzioni della riga di comando di CryptoLottoToken. + + + + &Show + &Mostra + + + + &Console + &Console + + + + Build date + Data di creazione + + + + CryptoLottoToken - Debug window + CryptoLottoToken - Finestra debug + + + + CryptoLottoToken Core + + + + + Debug log file + File log del Debug + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + Apri il file di log del debug di CryptoLottoToken dalla cartella attuale. Può richiedere alcuni secondi per file di log grandi. + + + + Clear console + Svuota console + + + + Welcome to the CryptoLottoToken RPC console. + Benvenuto nella console RPC di CryptoLottoToken + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Usa le frecce direzionali per navigare la cronologia, and <b>Ctrl-L</b> per cancellarla. + + + + Type <b>help</b> for an overview of available commands. + Scrivi <b>help</b> per un riassunto dei comandi disponibili + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Spedisci CryptoLottoToken + + + + Send to multiple recipients at once + Spedisci a diversi beneficiari in una volta sola + + + + Add &Recipient + &Aggiungi beneficiario + + + + Remove all transaction fields + Rimuovi tutti i campi della transazione + + + + Clear &All + Cancella &tutto + + + + Balance: + Saldo: + + + + 123.456 BTC + 123,456 BTC + + + + Confirm the send action + Conferma la spedizione + + + + S&end + &Spedisci + + + + <b>%1</b> to %2 (%3) + <b>%1</b> to %2 (%3) + + + + Confirm send coins + Conferma la spedizione di CryptoLottoToken + + + + Are you sure you want to send %1? + Si è sicuri di voler spedire %1? + + + + and + e + + + + The recipient address is not valid, please recheck. + L'indirizzo del beneficiario non è valido, per cortesia controlla. + + + + The amount to pay must be larger than 0. + L'importo da pagare dev'essere maggiore di 0. + + + + The amount exceeds your balance. + L'importo è superiore al saldo attuale + + + + The total exceeds your balance when the %1 transaction fee is included. + Il totale è superiore al saldo attuale includendo la commissione %1. + + + + Duplicate address found, can only send to each address once per send operation. + Trovato un indirizzo doppio, si può spedire solo una volta a ciascun indirizzo in una singola operazione. + + + + Error: Transaction creation failed! + Errore: Creazione transazione fallita! + + + + 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. + Errore: la transazione è stata rifiutata. Ciò accade se alcuni CryptoLottoToken nel portamonete sono stati già spesi, ad esempio se è stata usata una copia del file wallet.dat e i CryptoLottoToken sono stati spesi dalla copia ma non segnati come spesi qui. + + + + SendCoinsEntry + + + Form + Modulo + + + + A&mount: + &Importo: + + + + Pay &To: + Paga &a: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + L'indirizzo del beneficiario a cui inviare il pagamento (ad esempio Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + Inserisci un'etichetta per questo indirizzo, per aggiungerlo nella rubrica + + + + &Label: + &Etichetta + + + + Choose address from address book + Scegli l'indirizzo dalla rubrica + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Incollare l'indirizzo dagli appunti + + + + Alt+P + Alt+P + + + + Remove this recipient + Rimuovere questo beneficiario + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Inserisci un indirizzo CryptoLottoToken (ad esempio Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Firme - Firma / Verifica un messaggio + + + + &Sign Message + &Firma il messaggio + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Puoi firmare messeggi con i tuoi indirizzi per dimostrare che sono tuoi. Fai attenzione a non firmare niente di vago, visto che gli attacchi di phishing potrebbero cercare di spingerti a mettere la tua firma su di loro. Firma solo dichiarazioni completamente dettagliate con cui sei d'accordo. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Inserisci un indirizzo CryptoLottoToken (ad esempio Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Scegli l'indirizzo dalla rubrica + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Incollare l'indirizzo dagli appunti + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Inserisci qui il messaggio che vuoi firmare + + + + Signature + Firma + + + + Copy the current signature to the system clipboard + Copia la firma corrente nella clipboard + + + + Sign the message to prove you own this CryptoLottoToken address + Firma un messaggio per dimostrare di possedere questo indirizzo + + + + Sign &Message + Firma &messaggio + + + + Reset all sign message fields + Reimposta tutti i campi della firma + + + + + Clear &All + Cancella &tutto + + + + &Verify Message + &Verifica Messaggio + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Inserisci un indirizzo CryptoLottoToken (ad esempio Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Verifica il messaggio per assicurarsi che sia stato firmato con l'indirizzo CryptoLottoToken specificato + + + + Verify &Message + &Verifica Messaggio + + + + Reset all verify message fields + Reimposta tutti i campi della verifica messaggio + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Inserisci un indirizzo CryptoLottoToken (ad esempio Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Clicca "Firma il messaggio" per ottenere la firma + + + + Enter CryptoLottoToken signature + Inserisci firma CryptoLottoToken + + + + + The entered address is invalid. + L'indirizzo inserito non è valido. + + + + + + + Please check the address and try again. + Per favore controlla l'indirizzo e prova ancora + + + + + The entered address does not refer to a key. + L'indirizzo CryptoLottoToken inserito non è associato a nessuna chiave. + + + + Wallet unlock was cancelled. + Sblocco del portafoglio annullato. + + + + Private key for the entered address is not available. + La chiave privata per l'indirizzo inserito non è disponibile. + + + + Message signing failed. + Firma messaggio fallita. + + + + Message signed. + Messaggio firmato. + + + + The signature could not be decoded. + Non è stato possibile decodificare la firma. + + + + + Please check the signature and try again. + Per favore controlla la firma e prova ancora. + + + + The signature did not match the message digest. + La firma non corrisponde al sunto del messaggio. + + + + Message verification failed. + Verifica messaggio fallita. + + + + Message verified. + Messaggio verificato. + + + + SplashScreen + + + The CryptoLottoToken developers + Sviluppatori di CryptoLottoToken + + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + Aperto fino a %1 + + + + %1/offline + %1/offline + + + + %1/unconfirmed + %1/non confermato + + + + %1 confirmations + %1 conferme + + + + Status + Stato + + + + , broadcast through %n node(s) + , trasmesso attraverso %n nodo, trasmesso attraverso %n nodi + + + + Date + Data + + + + Source + Sorgente + + + + Generated + Generato + + + + + From + Da + + + + + + To + A + + + + + own address + proprio indirizzo + + + + label + etichetta + + + + + + + + Credit + Credito + + + + matures in %n more block(s) + matura in %n ulteriore bloccomatura in altri %n blocchi + + + + not accepted + non accettate + + + + + + + Debit + Debito + + + + Transaction fee + Tranzakciós díj + + + + Net amount + Importo netto + + + + Message + Messaggio + + + + Comment + Commento + + + + Transaction ID + ID della transazione + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Bisogna attendere 120 blocchi prima di spendere I CryptoLottoToken generati. Quando è stato generato questo blocco, è stato trasmesso alla rete per aggiungerlo alla catena di blocchi. Se non riesce a entrare nella catena, verrà modificato in "non accettato" e non sarà spendibile. Questo può accadere a volte, se un altro nodo genera un blocco entro pochi secondi del tuo. + + + + Debug information + Informazione di debug + + + + Transaction + Transazione + + + + Inputs + Input + + + + Amount + Importo + + + + true + vero + + + + false + falso + + + + , has not been successfully broadcast yet + , non è stato ancora trasmesso con successo + + + + Open for %n more block(s) + Aperto per %n altro bloccoAperto per altri %n blocchi + + + + unknown + sconosciuto + + + + TransactionDescDialog + + + Transaction details + Dettagli sulla transazione + + + + This pane shows a detailed description of the transaction + Questo pannello mostra una descrizione dettagliata della transazione + + + + TransactionTableModel + + + Date + Data + + + + Type + Tipo + + + + Address + Indirizzo + + + + Amount + Importo + + + + Open for %n more block(s) + Aperto per %n altro bloccoAperto per altri %n blocchi + + + + Open until %1 + Aperto fino a %1 + + + + Offline (%1 confirmations) + Offline (%1 conferme) + + + + Unconfirmed (%1 of %2 confirmations) + Non confermati (%1 su %2 conferme) + + + + Confirmed (%1 confirmations) + Confermato (%1 conferme) + + + + Mined balance will be available when it matures in %n more block(s) + Il saldo generato sarà disponibile quando maturerà in %n altro bloccoIl saldo generato sarà disponibile quando maturerà in altri %n blocchi + + + + This block was not received by any other nodes and will probably not be accepted! + Questo blocco non è stato ricevuto da altri nodi e probabilmente non sarà accettato! + + + + Generated but not accepted + Generati, ma non accettati + + + + Received with + Ricevuto tramite + + + + Received from + Ricevuto da + + + + Sent to + Spedito a + + + + Payment to yourself + Pagamento a te stesso + + + + Mined + Ottenuto dal mining + + + + (n/a) + (N / a) + + + + Transaction status. Hover over this field to show number of confirmations. + Stato della transazione. Passare con il mouse su questo campo per vedere il numero di conferme. + + + + Date and time that the transaction was received. + Data e ora in cui la transazione è stata ricevuta. + + + + Type of transaction. + Tipo di transazione. + + + + Destination address of transaction. + Indirizzo di destinazione della transazione. + + + + Amount removed from or added to balance. + Importo rimosso o aggiunto al saldo. + + + + TransactionView + + + + All + Tutti + + + + Today + Oggi + + + + This week + Questa settimana + + + + This month + Questo mese + + + + Last month + Il mese scorso + + + + This year + Quest'anno + + + + Range... + Intervallo... + + + + Received with + Ricevuto tramite + + + + Sent to + Spedito a + + + + To yourself + A te + + + + Mined + Ottenuto dal mining + + + + Other + Altro + + + + Enter address or label to search + Inserisci un indirizzo o un'etichetta da cercare + + + + Min amount + Importo minimo + + + + Copy address + Copia l'indirizzo + + + + Copy label + Copia l'etichetta + + + + Copy amount + Copia l'importo + + + + Copy transaction ID + + + + + Edit label + Modifica l'etichetta + + + + Show transaction details + Mostra i dettagli della transazione + + + + Export Transaction Data + Esporta i dati della transazione + + + + Comma separated file (*.csv) + Testo CSV (*.csv) + + + + Confirmed + Confermato + + + + Date + Data + + + + Type + Tipo + + + + Label + Etichetta + + + + Address + Indirizzo + + + + Amount + Importo + + + + ID + ID + + + + Error exporting + Errore nell'esportazione + + + + Could not write to file %1. + Impossibile scrivere sul file %1. + + + + Range: + Intervallo: + + + + to + a + + + + WalletModel + + + Send Coins + Spedisci CryptoLottoToken + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + Esporta i dati nella tabella corrente su un file + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + Backup fallito + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + Backup eseguito con successo + + + + The wallet data was successfully saved to the new location. + Il portafoglio è stato correttamente salvato nella nuova cartella. + + + + bitcoin-core + + + CryptoLottoToken version + Versione di CryptoLottoToken + + + + Usage: + Utilizzo: + + + + Send command to -server or CryptoLottoTokend + Manda il comando a -server o CryptoLottoTokend + + + + + List commands + Lista comandi + + + + + Get help for a command + Aiuto su un comando + + + + + Options: + Opzioni: + + + + + Specify configuration file (default: CryptoLottoToken.conf) + Specifica il file di configurazione (di default: CryptoLottoToken.conf) + + + + + Specify pid file (default: CryptoLottoTokend.pid) + Specifica il file pid (default: CryptoLottoTokend.pid) + + + + + Specify data directory + Specifica la cartella dati + + + + + Set database cache size in megabytes (default: 25) + Imposta la dimensione cache del database in megabyte (default: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Ascolta le connessioni JSON-RPC su <porta> (default: 22556 o testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Mantieni al massimo <n> connessioni ai peer (default: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Connessione ad un nodo per ricevere l'indirizzo del peer, e disconnessione + + + + Specify your own public address + Specifica il tuo indirizzo pubblico + + + + Threshold for disconnecting misbehaving peers (default: 100) + Soglia di disconnessione dei peer di cattiva qualità (default: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Numero di secondi di sospensione che i peer di cattiva qualità devono trascorrere prima di riconnettersi (default: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + Errore riscontrato durante l'impostazione della porta RPC %u per l'ascolto su IPv4: %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Attendi le connessioni JSON-RPC su <porta> (default: 22555 or testnet: 44555) + + + + Accept command line and JSON-RPC commands + Accetta da linea di comando e da comandi JSON-RPC + + + + + Run in the background as a daemon and accept commands + Esegui in background come demone e accetta i comandi + + + + + Use the test network + Utilizza la rete di prova + + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Accetta connessioni dall'esterno (default: 1 se no -proxy o -connect) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + Errore riscontrato durante l'impostazione della porta RPC %u per l'ascolto su IPv6, tornando su IPv4: %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + Collega all'indirizzo indicato e resta sempre in ascolto su questo. Usa la notazione [host]:porta per l'IPv6 + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + Non è possibile ottenere i dati sulla cartella %s. Probabilmente CryptoLottoToken è già in esecuzione. + + + + 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. + Errore: la transazione è stata rifiutata. Ciò accade se alcuni CryptoLottoToken nel portamonete sono stati già spesi, ad esempio se è stata usata una copia del file wallet.dat e i CryptoLottoToken sono stati spesi dalla copia ma non segnati come spesi qui. + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + Errore: questa transazione necessita di una commissione di almeno %s a causa del suo ammontare, della sua complessità, o dell'uso di fondi recentemente ricevuti! + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Esegui comando quando una transazione del portafoglio cambia (%s in cmd è sostituito da TxID) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Imposta dimensione massima delle transazioni ad alta priorità/bassa-tassa in bytes (predefinito: 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + Questa versione è una compilazione pre-rilascio - usala a tuo rischio - non utilizzarla per la generazione o per applicazioni di commercio + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Attenzione: -paytxfee è molto alta. Questa è la commissione che si paga quando si invia una transazione. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + Attenzione: le transazioni mostrate potrebbero essere sbagliate! Potresti aver bisogno di aggiornare, o altri nodi ne hanno bisogno. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Attenzione: si prega di controllare che la data del computer e l'ora siano corrette. Se il vostro orologio è sbagliato CryptoLottoToken non funziona correttamente. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Attenzione: errore di lettura di wallet.dat! Tutte le chiave lette correttamente, ma i dati delle transazioni o le voci in rubrica potrebbero mancare o non essere corretti. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Attenzione: wallet.dat corrotto, dati salvati! Il wallet.dat originale salvato come wallet.{timestamp}.bak in %s; se il tuo bilancio o le transazioni non sono corrette dovresti ripristinare da un backup. + + + + Attempt to recover private keys from a corrupt wallet.dat + Tenta di recuperare le chiavi private da un wallet.dat corrotto + + + + Block creation options: + Opzioni creazione blocco: + + + + Connect only to the specified node(s) + Connetti solo al nodo specificato + + + + Corrupted block database detected + Rilevato database blocchi corrotto + + + + Discover own IP address (default: 1 when listening and no -externalip) + Scopri proprio indirizzo IP (default: 1 se in ascolto e no -externalip) + + + + Do you want to rebuild the block database now? + Vuoi ricostruire ora il database dei blocchi? + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + Errore caricamento database blocchi + + + + Error opening block database + Errore caricamento database blocchi + + + + Error: Disk space is low! + Errore: la spazio libero sul disco è poco! + + + + Error: Wallet locked, unable to create transaction! + Errore: portafoglio bloccato, impossibile creare la transazione! + + + + Error: system error: + Errore: errore di sistema: + + + + Failed to listen on any port. Use -listen=0 if you want this. + Impossibile mettersi in ascolto su una porta. Usa -listen=0 se vuoi usare questa opzione. + + + + Failed to read block info + Lettura informazioni blocco fallita + + + + Failed to read block + Lettura blocco fallita + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + Scrittura informazioni blocco fallita + + + + Failed to write block + Scrittura blocco fallita + + + + Failed to write file info + Scrittura informazioni file fallita + + + + Failed to write to coin database + Scrittura nel database dei CryptoLottoToken fallita + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + Trova peer utilizzando la ricerca DNS (predefinito: 1 finché utilizzato -connect) + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + Quanti blocchi da controllare all'avvio (predefinito: 288, 0 = tutti) + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + Verifica blocchi... + + + + Verifying wallet... + Verifica portafoglio... + + + + Imports blocks from external blk000??.dat file + Importa blocchi da un file blk000??.dat esterno + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + Informazione + + + + Invalid -tor address: '%s' + Indirizzo -tor non valido: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Buffer di ricezione massimo per connessione, <n>*1000 byte (default: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Buffer di invio massimo per connessione, <n>*1000 byte (default: 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Connetti solo a nodi nella rete <net> (IPv4, IPv6 o Tor) + + + + Output extra debugging information. Implies all other -debug* options + Produci informazioni extra utili al debug. Implies all other -debug* options + + + + Output extra network debugging information + Genera informazioni extra utili al debug della rete + + + + Prepend debug output with timestamp (default: 1) + Anteponi all'output di debug una marca temporale + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + Opzioni SSL: (vedi il wiki di CryptoLottoToken per le istruzioni di configurazione SSL) + + + + Select the version of socks proxy to use (4-5, default: 5) + Selezionare la versione del proxy socks da usare (4-5, default: 5) + + + + Send trace/debug info to console instead of debug.log file + Invia le informazioni di trace/debug alla console invece che al file debug.log + + + + Send trace/debug info to debugger + Invia le informazioni di trace/debug al debugger + + + + Set maximum block size in bytes (default: 250000) + Imposta dimensione massima del blocco in bytes (predefinito: 250000) + + + + Set minimum block size in bytes (default: 0) + Imposta dimensione minima del blocco in bytes (predefinito: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Riduci il file debug.log all'avvio del client (predefinito: 1 se non impostato -debug) + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + Specifica il timeout di connessione in millisecondi (default: 5000) + + + + System error: + Errore di sistema: + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + UPnP-használat engedélyezése a figyelő port feltérképezésénél (default: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + UPnP-használat engedélyezése a figyelő port feltérképezésénél (default: 1 when listening) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Usa un proxy per raggiungere servizi nascosti di tor (predefinito: uguale a -proxy) + + + + Username for JSON-RPC connections + Nome utente per connessioni JSON-RPC + + + + + Warning + Attenzione + + + + Warning: This version is obsolete, upgrade required! + Attenzione: questa versione è obsoleta, aggiornamento necessario! + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + wallet.dat corrotto, salvataggio fallito + + + + Password for JSON-RPC connections + Password per connessioni JSON-RPC + + + + + Allow JSON-RPC connections from specified IP address + Consenti connessioni JSON-RPC dall'indirizzo IP specificato + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Inviare comandi al nodo in esecuzione su <ip> (default: 127.0.0.1) + + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Esegui il comando quando il miglior block cambia(%s nel cmd è sostituito dall'hash del blocco) + + + + Upgrade wallet to latest format + Aggiorna il wallet all'ultimo formato + + + + Set key pool size to <n> (default: 100) + Impostare la quantità di chiavi di riserva a <n> (default: 100) + + + + + Rescan the block chain for missing wallet transactions + Ripeti analisi della catena dei blocchi per cercare le transazioni mancanti dal portamonete + + + + + Use OpenSSL (https) for JSON-RPC connections + Utilizzare OpenSSL (https) per le connessioni JSON-RPC + + + + + Server certificate file (default: server.cert) + File certificato del server (default: server.cert) + + + + + Server private key (default: server.pem) + Chiave privata del server (default: server.pem) + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Cifrari accettabili (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + + This help message + Questo messaggio di aiuto + + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Impossibile collegarsi alla %s su questo computer (bind returned error %d, %s) + + + + Connect through socks proxy + Connessione tramite socks proxy + + + + Allow DNS lookups for -addnode, -seednode and -connect + Consenti ricerche DNS per aggiungere nodi e collegare + + + + + Loading addresses... + Caricamento indirizzi... + + + + Error loading wallet.dat: Wallet corrupted + Errore caricamento wallet.dat: Wallet corrotto + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Errore caricamento wallet.dat: il wallet richiede una versione nuova di CryptoLottoToken + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Il portamonete deve essere riscritto: riavviare CryptoLottoToken per completare + + + + Error loading wallet.dat + Errore caricamento wallet.dat + + + + Invalid -proxy address: '%s' + Indirizzo -proxy non valido: '%s' + + + + Unknown network specified in -onlynet: '%s' + Rete sconosciuta specificata in -onlynet: '%s' + + + + Unknown -socks proxy version requested: %i + Versione -socks proxy sconosciuta richiesta: %i + + + + Cannot resolve -bind address: '%s' + Impossibile risolvere -bind address: '%s' + + + + Cannot resolve -externalip address: '%s' + Impossibile risolvere indirizzo -externalip: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + Importo non valido per -paytxfee=<amount>: '%s' + + + + Invalid amount + Importo non valido + + + + Insufficient funds + Fondi insufficienti + + + + Loading block index... + Caricamento dell'indice del blocco... + + + + Add a node to connect to and attempt to keep the connection open + Elérendő csomópont megadása and attempt to keep the connection open + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Impossibile collegarsi alla %s su questo computer. Probabilmente CryptoLottoToken è già in esecuzione. + + + + Fee per KB to add to transactions you send + Commissione per KB da aggiungere alle transazioni in uscita + + + + Loading wallet... + Caricamento portamonete... + + + + Cannot downgrade wallet + Non è possibile retrocedere il wallet + + + + Cannot write default address + Non è possibile scrivere l'indirizzo di default + + + + Rescanning... + Ripetere la scansione... + + + + Done loading + Caricamento completato + + + + To use the %s option + Per usare la opzione %s + + + + Error + Errore + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + Devi settare rpcpassword=<password> nel file di configurazione: %s Se il file non esiste, crealo con i permessi di amministratore + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_ja.ts b/src/qt/locale/bitcoin_ja.ts new file mode 100644 index 0000000..2896ab2 --- /dev/null +++ b/src/qt/locale/bitcoin_ja.ts @@ -0,0 +1,2917 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + CryptoLottoTokenについて + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> バージョン + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + アドレス帳 + + + + Double-click to edit address or label + アドレスまたはラベルを編集するにはダブルクリック + + + + Create a new address + 新規アドレスの作成 + + + + Copy the currently selected address to the system clipboard + 現在選択されているアドレスをシステムのクリップボードにコピーする + + + + &New Address + + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + + + + + &Copy Address + + + + + Show &QR Code + + + + + Sign a message to prove you own a CryptoLottoToken address + + + + + Sign &Message + + + + + Delete the currently selected address from the list + + + + + Export the data in the current tab to a file + + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + + + + + &Verify Message + + + + + &Delete + 削除(&D) + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + + + + + &Edit + + + + + Send &Coins + + + + + Export Address Book Data + アドレス帳データをエクスポートする + + + + Comma separated file (*.csv) + CSVファイル (*.csv) + + + + Error exporting + エクスポートエラー + + + + Could not write to file %1. + %1のファイルに書き込めませんでした。 + + + + AddressTableModel + + + Label + ラベル + + + + Address + アドレス + + + + (no label) + (ラベル無し) + + + + AskPassphraseDialog + + + Passphrase Dialog + + + + + Enter passphrase + パスフレーズを入力 + + + + New passphrase + 新しいパスフレーズ + + + + Repeat new passphrase + 新しいパスフレーズをもう一度 + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + ウォレットの新しいパスフレーズを入力してください。<br/><b>8個以上の単語か10個以上のランダムな文字</b>を使ってください。 + + + + Encrypt wallet + ウォレットを暗号化する + + + + This operation needs your wallet passphrase to unlock the wallet. + この操作はウォレットをアンロックするためにパスフレーズが必要です。 + + + + Unlock wallet + ウォレットをアンロックする + + + + This operation needs your wallet passphrase to decrypt the wallet. + この操作はウォレットの暗号化解除のためにパスフレーズが必要です。 + + + + Decrypt wallet + ウォレットの暗号化を解除する + + + + Change passphrase + パスフレーズの変更 + + + + Enter the old and new passphrase to the wallet. + 新旧両方のパスフレーズを入力してください。 + + + + Confirm wallet encryption + ウォレットの暗号化を確認する + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + + + + + Are you sure you wish to encrypt your wallet? + + + + + 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. + + + + + + Warning: The Caps Lock key is on! + + + + + + Wallet encrypted + ウォレットは暗号化されました + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + + + + + + + + Wallet encryption failed + ウォレットの暗号化に失敗しました + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + 内部エラーによりウォレットの暗号化が失敗しました。ウォレットは暗号化されませんでした。 + + + + + The supplied passphrases do not match. + パスフレーズが同じではありません。 + + + + Wallet unlock failed + ウォレットのアンロックに失敗しました + + + + + + The passphrase entered for the wallet decryption was incorrect. + ウォレットの暗号化解除のパスフレーズが正しくありません。 + + + + Wallet decryption failed + ウォレットの暗号化解除に失敗しました + + + + Wallet passphrase was successfully changed. + + + + + BitcoinGUI + + + Sign &message... + + + + + Synchronizing with network... + ネットワークに同期中…… + + + + &Overview + 概要(&O) + + + + Show general overview of wallet + ウォレットの概要を見る + + + + &Transactions + 取引(&T) + + + + Browse transaction history + 取引履歴を閲覧 + + + + Edit the list of stored addresses and labels for sending + 保存されたアドレスとラベルのリストを編集 + + + + Show the list of addresses for receiving payments + 支払い受け取り用アドレスのリストを見る + + + + E&xit + + + + + Quit application + アプリケーションを終了 + + + + Show information about CryptoLottoToken + CryptoLottoTokenに関する情報を見る + + + + About &Qt + + + + + Show information about Qt + + + + + &Options... + オプション(&O) + + + + &Encrypt Wallet... + + + + + &Backup Wallet... + + + + + &Change Passphrase... + + + + + Importing blocks from disk... + + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + + + + + Modify configuration options for CryptoLottoToken + + + + + Backup wallet to another location + + + + + Change the passphrase used for wallet encryption + ウォレット暗号化用パスフレーズの変更 + + + + &Debug window + + + + + Open debugging and diagnostic console + + + + + &Verify message... + + + + + + CryptoLottoToken + + + + + Wallet + + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + + + + + &Show / Hide + + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + ファイル(&F) + + + + &Settings + 設定(&S) + + + + &Help + ヘルプ(&H) + + + + Tabs toolbar + タブツールバー + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + + + + + %n active connection(s) to CryptoLottoToken network + + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + + + + + Warning + + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + バージョンは最新です + + + + Catching up... + + + + + Confirm transaction fee + + + + + Sent transaction + 送金取引 + + + + Incoming transaction + 着金取引 + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + + + + + + URI handling + + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + ウォレットは<b>暗号化され、アンロックされています</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + ウォレットは<b>暗号化され、ロックされています</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + + + + + EditAddressDialog + + + Edit Address + アドレスの編集 + + + + &Label + ラベル(&L) + + + + The label associated with this address book entry + このアドレス帳の入った事と関係のレーベル + + + + &Address + &アドレス + + + + The address associated with this address book entry. This can only be modified for sending addresses. + アドレス帳の入った事の関係のアドレスです。これは遅れるのアドレスのためだけに編集出来ます。 + + + + New receiving address + 新しいの受け入れのアドレス + + + + New sending address + 新しいの送るのアドレス + + + + Edit receiving address + 受け入れのアドレスを編集する + + + + Edit sending address + 送るのアドレスを編集する + + + + The entered address "%1" is already in the address book. + 入ったのアドレス「%1」はもうアドレス帳にあります。 + + + + The entered address "%1" is not a valid CryptoLottoToken address. + + + + + Could not unlock wallet. + 財布をアンロックするのは出来ませんでした。 + + + + New key generation failed. + 新しいのキーの生成は失敗しました。 + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + + + + + version + + + + + Usage: + + + + + command-line options + + + + + UI options + + + + + Set language, for example "de_DE" (default: system locale) + + + + + Start minimized + + + + + Show splash screen on startup (default: 1) + + + + + OptionsDialog + + + Options + オプションズ + + + + &Main + + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + + + + + Automatically start CryptoLottoToken after logging in to the system. + + + + + &Start CryptoLottoToken on system login + + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + + + + + Map port using &UPnP + + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + + + + + &Connect through SOCKS proxy: + + + + + Proxy &IP: + + + + + IP address of the proxy (e.g. 127.0.0.1) + + + + + &Port: + + + + + Port of the proxy (e.g. 9050) + + + + + SOCKS &Version: + + + + + SOCKS version of the proxy (e.g. 5) + + + + + &Window + + + + + Show only a tray icon after minimizing the window. + + + + + &Minimize to the tray instead of the taskbar + + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + + + + + M&inimize on close + + + + + &Display + + + + + User Interface &language: + + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + + + + + &Unit to show amounts in: + + + + + Choose the default subdivision unit to show in the interface and when sending coins. + + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + + + &Display addresses in transaction list + + + + + &OK + + + + + &Cancel + + + + + &Apply + + + + + default + + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + + + + + + This setting will take effect after restarting CryptoLottoToken. + + + + + The supplied proxy address is invalid. + + + + + OverviewPage + + + Form + フォーム + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + + + + + Balance: + 残高: + + + + Unconfirmed: + 未確認: + + + + Wallet + + + + + Immature: + + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + <b>最近の取引</b> + + + + Your current balance + 今の残高 + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + + + + + + out of sync + + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + + + + + Request Payment + + + + + Amount: + + + + + Label: + + + + + Message: + + + + + &Save As... + + + + + Error encoding URI into QR Code. + + + + + The entered amount is invalid, please check. + + + + + Resulting URI too long, try to reduce the text for label / message. + + + + + Save QR Code + + + + + PNG Images (*.png) + + + + + RPCConsole + + + Client name + + + + + + + + + + + + + + N/A + + + + + Client version + + + + + &Information + + + + + Using OpenSSL version + + + + + Startup time + + + + + Network + + + + + Number of connections + + + + + On testnet + + + + + Block chain + + + + + Current number of blocks + + + + + Estimated total blocks + + + + + Last block time + + + + + &Open + + + + + Command-line options + + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + + &Show + + + + + &Console + + + + + Build date + + + + + CryptoLottoToken - Debug window + + + + + CryptoLottoToken Core + + + + + Debug log file + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + + + + + Welcome to the CryptoLottoToken RPC console. + + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + + + + + Type <b>help</b> for an overview of available commands. + + + + + SendCoinsDialog + + + + + + + + + + Send Coins + コインを送る + + + + Send to multiple recipients at once + + + + + Add &Recipient + + + + + Remove all transaction fields + + + + + Clear &All + + + + + Balance: + 残高: + + + + 123.456 BTC + + + + + Confirm the send action + + + + + S&end + + + + + <b>%1</b> to %2 (%3) + + + + + Confirm send coins + + + + + Are you sure you want to send %1? + + + + + and + + + + + The recipient address is not valid, please recheck. + + + + + The amount to pay must be larger than 0. + + + + + The amount exceeds your balance. + + + + + The total exceeds your balance when the %1 transaction fee is included. + + + + + Duplicate address found, can only send to each address once per send operation. + + + + + Error: Transaction creation failed! + + + + + 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. + + + + + SendCoinsEntry + + + Form + フォーム + + + + A&mount: + + + + + Pay &To: + + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + + + + + &Label: + + + + + Choose address from address book + + + + + Alt+A + + + + + Paste address from clipboard + + + + + Alt+P + + + + + Remove this recipient + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + + + + + &Sign Message + + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Choose an address from the address book + + + + + + Alt+A + + + + + Paste address from clipboard + + + + + Alt+P + + + + + Enter the message you want to sign here + + + + + Signature + + + + + Copy the current signature to the system clipboard + + + + + Sign the message to prove you own this CryptoLottoToken address + + + + + Sign &Message + + + + + Reset all sign message fields + + + + + + Clear &All + + + + + &Verify Message + + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + + + + + Verify &Message + + + + + Reset all verify message fields + + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Click "Sign Message" to generate signature + + + + + Enter CryptoLottoToken signature + + + + + + The entered address is invalid. + + + + + + + + Please check the address and try again. + + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + + + + + Private key for the entered address is not available. + + + + + Message signing failed. + + + + + Message signed. + + + + + The signature could not be decoded. + + + + + + Please check the signature and try again. + + + + + The signature did not match the message digest. + + + + + Message verification failed. + + + + + Message verified. + + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + + + + + %1/offline + + + + + %1/unconfirmed + + + + + %1 confirmations + + + + + Status + + + + + , broadcast through %n node(s) + + + + + Date + + + + + Source + + + + + Generated + + + + + + From + + + + + + + To + + + + + + own address + + + + + label + + + + + + + + + Credit + + + + + matures in %n more block(s) + + + + + not accepted + + + + + + + + Debit + + + + + Transaction fee + + + + + Net amount + + + + + Message + + + + + Comment + + + + + Transaction ID + + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + + + + + Debug information + + + + + Transaction + + + + + Inputs + + + + + Amount + + + + + true + + + + + false + + + + + , has not been successfully broadcast yet + + + + + Open for %n more block(s) + + + + + unknown + + + + + TransactionDescDialog + + + Transaction details + + + + + This pane shows a detailed description of the transaction + + + + + TransactionTableModel + + + Date + + + + + Type + + + + + Address + Helbidea + + + + Amount + + + + + Open for %n more block(s) + + + + + Open until %1 + + + + + Offline (%1 confirmations) + + + + + Unconfirmed (%1 of %2 confirmations) + + + + + Confirmed (%1 confirmations) + + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + + + + + Generated but not accepted + + + + + Received with + + + + + Received from + + + + + Sent to + + + + + Payment to yourself + + + + + Mined + + + + + (n/a) + + + + + Transaction status. Hover over this field to show number of confirmations. + + + + + Date and time that the transaction was received. + + + + + Type of transaction. + + + + + Destination address of transaction. + + + + + Amount removed from or added to balance. + + + + + TransactionView + + + + All + + + + + Today + + + + + This week + + + + + This month + + + + + Last month + + + + + This year + + + + + Range... + + + + + Received with + + + + + Sent to + + + + + To yourself + + + + + Mined + + + + + Other + + + + + Enter address or label to search + + + + + Min amount + + + + + Copy address + + + + + Copy label + + + + + Copy amount + + + + + Copy transaction ID + + + + + Edit label + + + + + Show transaction details + + + + + Export Transaction Data + + + + + Comma separated file (*.csv) + テキスト CSV (*.csv) + + + + Confirmed + + + + + Date + + + + + Type + + + + + Label + レーベル + + + + Address + Helbidea + + + + Amount + + + + + ID + + + + + Error exporting + エラー輸出 + + + + Could not write to file %1. + %1のファイルに書き込めませんでした。 + + + + Range: + + + + + to + + + + + WalletModel + + + Send Coins + + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + CryptoLottoToken Bertsio + + + + Usage: + + + + + Send command to -server or CryptoLottoTokend + + + + + List commands + + + + + Get help for a command + + + + + Options: + + + + + Specify configuration file (default: CryptoLottoToken.conf) + + + + + Specify pid file (default: CryptoLottoTokend.pid) + + + + + Specify data directory + + + + + Set database cache size in megabytes (default: 25) + + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + + + + + Maintain at most <n> connections to peers (default: 125) + + + + + Connect to a node to retrieve peer addresses, and disconnect + + + + + Specify your own public address + + + + + Threshold for disconnecting misbehaving peers (default: 100) + + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + + + + + Accept command line and JSON-RPC commands + + + + + Run in the background as a daemon and accept commands + + + + + Use the test network + + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + + + + + Connect only to the specified node(s) + + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + + + + + Select the version of socks proxy to use (4-5, default: 5) + + + + + Send trace/debug info to console instead of debug.log file + + + + + Send trace/debug info to debugger + + + + + Set maximum block size in bytes (default: 250000) + + + + + Set minimum block size in bytes (default: 0) + + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + + + + + Use UPnP to map the listening port (default: 1 when listening) + + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + + + + + Allow JSON-RPC connections from specified IP address + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + + + + + Upgrade wallet to latest format + + + + + Set key pool size to <n> (default: 100) + + + + + Rescan the block chain for missing wallet transactions + + + + + Use OpenSSL (https) for JSON-RPC connections + + + + + Server certificate file (default: server.cert) + + + + + Server private key (default: server.pem) + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + + This help message + + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + + + + + Connect through socks proxy + + + + + Allow DNS lookups for -addnode, -seednode and -connect + + + + + Loading addresses... + + + + + Error loading wallet.dat: Wallet corrupted + + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + + + + + Error loading wallet.dat + + + + + Invalid -proxy address: '%s' + + + + + Unknown network specified in -onlynet: '%s' + + + + + Unknown -socks proxy version requested: %i + + + + + Cannot resolve -bind address: '%s' + + + + + Cannot resolve -externalip address: '%s' + + + + + Invalid amount for -paytxfee=<amount>: '%s' + + + + + Invalid amount + + + + + Insufficient funds + + + + + Loading block index... + + + + + Add a node to connect to and attempt to keep the connection open + + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + + + + + Fee per KB to add to transactions you send + + + + + Loading wallet... + + + + + Cannot downgrade wallet + + + + + Cannot write default address + + + + + Rescanning... + + + + + Done loading + + + + + To use the %s option + + + + + Error + + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_la.ts b/src/qt/locale/bitcoin_la.ts new file mode 100644 index 0000000..f955d9b --- /dev/null +++ b/src/qt/locale/bitcoin_la.ts @@ -0,0 +1,2937 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + Informatio de CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> versio + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + Hoc est experimentale programma. + +Distributum sub MIT/X11 licentia programmatum, vide comitantem plicam COPYING vel http://www.opensource.org/licenses/mit-license.php. + +Hoc productum continet programmata composita ab OpenSSL Project pro utendo in OpenSSL Toolkit (http://www.openssl.org/) et programmata cifrarum scripta ab Eric Young (eay@cryptsoft.com) et UPnP programmata scripta ab Thomas Bernard. + + + + Copyright + Copyright + + + + The CryptoLottoToken developers + CryptoLottoToken curatores + + + + AddressBookPage + + + Address Book + Liber Inscriptionum + + + + Double-click to edit address or label + Dupliciter-clicca ut inscriptionem vel titulum mutes + + + + Create a new address + Crea novam inscriptionem + + + + Copy the currently selected address to the system clipboard + Copia inscriptionem iam selectam in latibulum systematis + + + + &New Address + &Nova Inscriptio + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Haec sunt inscriptiones CryptoLottoToken tuae pro accipendo pensitationes. Cupias variam ad quemque mittentem dare ut melius scias quem tibi pensare. + + + + &Copy Address + &Copia Inscriptionem + + + + Show &QR Code + Monstra codicem &QR + + + + Sign a message to prove you own a CryptoLottoToken address + Signa nuntium ut demonstres inscriptionem CryptoLottoToken a te possessam esse + + + + Sign &Message + Signa &Nuntium + + + + Delete the currently selected address from the list + Dele active selectam inscriptionem ex enumeratione + + + + Export the data in the current tab to a file + Exporta data in hac tabella in plicam + + + + &Export + &Exporta + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Verifica nuntium ut cures signatum esse cum specificata inscriptione CryptoLottoToken + + + + &Verify Message + &Verifica Nuntium + + + + &Delete + &Dele + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + Hae sunt inscriptiones mittendi pensitationes. Semper inspice quantitatem et inscriptionem accipiendi antequam nummos mittis. + + + + Copy &Label + Copia &Titulum + + + + &Edit + &Muta + + + + Send &Coins + Mitte &Nummos + + + + Export Address Book Data + Exporta Data Libri Inscriptionum + + + + Comma separated file (*.csv) + Comma Separata Plica (*.csv) + + + + Error exporting + Error exportandi + + + + Could not write to file %1. + Non potuisse scribere in plicam %1. + + + + AddressTableModel + + + Label + Titulus + + + + Address + Inscriptio + + + + (no label) + (nullus titulus) + + + + AskPassphraseDialog + + + Passphrase Dialog + Dialogus Tesserae + + + + Enter passphrase + Insere tesseram + + + + New passphrase + Nova tessera + + + + Repeat new passphrase + Itera novam tesseram + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Insero novam tesseram cassidili.<br/>Sodes tessera <b>10 pluriumve fortuitarum litterarum</b> utere aut <b>octo pluriumve verborum</b>. + + + + Encrypt wallet + Cifra cassidile + + + + This operation needs your wallet passphrase to unlock the wallet. + Huic operationi necesse est tessera cassidili tuo ut cassidile reseret. + + + + Unlock wallet + Resera cassidile + + + + This operation needs your wallet passphrase to decrypt the wallet. + Huic operationi necesse est tessera cassidili tuo ut cassidile decifret. + + + + Decrypt wallet + Decifra cassidile + + + + Change passphrase + Muta tesseram + + + + Enter the old and new passphrase to the wallet. + Insero veterem novamque tesseram cassidili. + + + + Confirm wallet encryption + Confirma cifrationem cassidilis + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Monitio: Si cassidile tuum cifras et tesseram amittis, tu <b>AMITTES OMNES TUOS NUMMOS BITOS</b>! + + + + Are you sure you wish to encrypt your wallet? + Certusne es te velle tuum cassidile cifrare? + + + + 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. + GRAVE: Oportet ulla prioria conservata quae fecisti de plica tui cassidilis reponi a nove generata cifrata plica cassidilis. Propter securitatem, prioria conservata de plica non cifrata cassidilis inutilia fiet simul atque incipis uti novo cifrato cassidili. + + + + + Warning: The Caps Lock key is on! + Monitio: Litterae ut capitales seratae sunt! + + + + + Wallet encrypted + Cassidile cifratum + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken iam desinet ut finiat actionem cifrandi. Memento cassidile cifrare non posse cuncte curare ne tui nummi clepantur ab malis programatibus in tuo computatro. + + + + + + + Wallet encryption failed + Cassidile cifrare abortum est + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Cassidile cifrare abortum est propter internum errorem. Tuum cassidile cifratum non est. + + + + + The supplied passphrases do not match. + Tesserae datae non eaedem sunt. + + + + Wallet unlock failed + Cassidile reserare abortum est. + + + + + + The passphrase entered for the wallet decryption was incorrect. + Tessera inserta pro cassidilis decifrando prava erat. + + + + Wallet decryption failed + Cassidile decifrare abortum est. + + + + Wallet passphrase was successfully changed. + Tessera cassidilis successa est in mutando. + + + + BitcoinGUI + + + Sign &message... + Signa &nuntium... + + + + Synchronizing with network... + Synchronizans cum rete... + + + + &Overview + &Summarium + + + + Show general overview of wallet + Monstra generale summarium cassidilis + + + + &Transactions + &Transactiones + + + + Browse transaction history + Inspicio historiam transactionum + + + + Edit the list of stored addresses and labels for sending + Muta indicem salvatarum inscriptionum titulorumque + + + + Show the list of addresses for receiving payments + Monstra indicem inscriptionum quibus pensitationes acceptandae + + + + E&xit + E&xi + + + + Quit application + Exi applicatione + + + + Show information about CryptoLottoToken + Monstra informationem de CryptoLottoToken + + + + About &Qt + Informatio de &Qt + + + + Show information about Qt + Monstra informationem de Qt + + + + &Options... + &Optiones + + + + &Encrypt Wallet... + &Cifra Cassidile... + + + + &Backup Wallet... + &Conserva Cassidile... + + + + &Change Passphrase... + &Muta tesseram... + + + + Importing blocks from disk... + Importans frusta ab disco... + + + + Reindexing blocks on disk... + Recreans indicem frustorum in disco... + + + + Send coins to a CryptoLottoToken address + Mitte nummos ad inscriptionem CryptoLottoToken + + + + Modify configuration options for CryptoLottoToken + Muta configurationis optiones pro CryptoLottoToken + + + + Backup wallet to another location + Conserva cassidile in locum alium + + + + Change the passphrase used for wallet encryption + Muta tesseram utam pro cassidilis cifrando + + + + &Debug window + Fenestra &Debug + + + + Open debugging and diagnostic console + Aperi terminalem debug et diagnosticalem + + + + &Verify message... + &Verifica nuntium... + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Cassidile + + + + &Send + &Mitte + + + + &Receive + &Accipe + + + + &Addresses + &Inscriptiones + + + + &About CryptoLottoToken + &Informatio de CryptoLottoToken + + + + &Show / Hide + &Monstra/Occulta + + + + Show or hide the main Window + Monstra vel occulta Fenestram principem + + + + Encrypt the private keys that belong to your wallet + Cifra claves privatas quae cassidili tui sunt + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Signa nuntios cum tuis inscriptionibus CryptoLottoToken ut demonstres te eas possidere + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Verifica nuntios ut certus sis eos signatos esse cum specificatis inscriptionibus CryptoLottoToken + + + + &File + &Plica + + + + &Settings + &Configuratio + + + + &Help + &Auxilium + + + + Tabs toolbar + Tabella instrumentorum "Tabs" + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + CryptoLottoToken cliens + + + + %n active connection(s) to CryptoLottoToken network + %n activa conexio ad rete CryptoLottoToken%n activae conexiones ad rete CryptoLottoToken + + + + No block source available... + Nulla fons frustorum absens... + + + + Processed %1 of %2 (estimated) blocks of transaction history. + Perfecta %1 de %2 (aestimato) frusta historiae transactionum. + + + + Processed %1 blocks of transaction history. + Processae %1 frusta historiae transactionum. + + + + %n hour(s) + %n hora%n horae + + + + %n day(s) + %n dies%n dies + + + + %n week(s) + %n hebdomas%n hebdomades + + + + %1 behind + %1 post + + + + Last received block was generated %1 ago. + Postremum acceptum frustum generatum est %1 abhinc. + + + + Transactions after this will not yet be visible. + Transactiones post hoc nondum visibiles erunt. + + + + Error + Error + + + + Warning + Monitio + + + + Information + Informatio + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + Haec transactio maior est quam limen magnitudinis. Adhuc potes id mittere mercede %1, quae it nodis qui procedunt tuam transactionem et adiuvat sustinere rete. Visne mercedem solvere? + + + + Up to date + Recentissimo + + + + Catching up... + Persequens... + + + + Confirm transaction fee + Confirma mercedem transactionis + + + + Sent transaction + Transactio missa + + + + Incoming transaction + Transactio incipiens + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Dies: %1 +Quantitas: %2 +Typus: %3 +Inscriptio: %4 + + + + + + URI handling + Tractatio URI + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + URI intellegi non posse! Huius causa possit inscriptionem CryptoLottoToken non validam aut URI parametra maleformata. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Cassidile <b>cifratum</b> est et iam nunc <b>reseratum</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Cassidile <b>cifratum</b> est et iam nunc <b>seratum</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + Error fatalis accidit. CryptoLottoToken nondum pergere tute potest, et exibit. + + + + ClientModel + + + Network Alert + Monitio Retis + + + + EditAddressDialog + + + Edit Address + Muta Inscriptionem + + + + &Label + &Titulus + + + + The label associated with this address book entry + Titulus associatus huic insertione libri inscriptionum + + + + &Address + &Inscriptio + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Titulus associatus huic insertione libri inscriptionum. Haec tantum mutari potest pro inscriptionibus mittendi + + + + New receiving address + Nova inscriptio accipiendi + + + + New sending address + Nova inscriptio mittendi + + + + Edit receiving address + Muta inscriptionem accipiendi + + + + Edit sending address + Muta inscriptionem mittendi + + + + The entered address "%1" is already in the address book. + Inserta inscriptio "%1" iam in libro inscriptionum est. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + Inscriptio inserta "%1" non valida inscriptio CryptoLottoToken est. + + + + Could not unlock wallet. + Non potuisse cassidile reserare + + + + New key generation failed. + Generare novam clavem abortum est. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + versio + + + + Usage: + Usus: + + + + command-line options + Optiones mandati intiantis + + + + UI options + UI optiones + + + + Set language, for example "de_DE" (default: system locale) + Constitue linguam, exempli gratia "de_DE" (praedefinitum: lingua systematis) + + + + Start minimized + Incipe minifactum ut icon + + + + Show splash screen on startup (default: 1) + Monstra principem imaginem ad initium (praedefinitum: 1) + + + + OptionsDialog + + + Options + Optiones + + + + &Main + &Princeps + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + Optionalis merces transactionum singulis kB quae adiuvat curare tuas transactiones processas esse celeriter. Plurimi transactiones 1kB sunt. + + + + Pay transaction &fee + Solve &mercedem transactionis + + + + Automatically start CryptoLottoToken after logging in to the system. + Pelle CryptoLottoToken per se postquam in systema inire. + + + + &Start CryptoLottoToken on system login + &Pelle CryptoLottoToken cum inire systema + + + + Reset all client options to default. + Reconstitue omnes optiones clientis ad praedefinita. + + + + &Reset Options + &Reconstitue Optiones + + + + &Network + &Rete + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Aperi per se portam clientis CryptoLottoToken in itineratore. Hoc tantum effectivum est si itineratrum tuum supportat UPnP et id activum est. + + + + Map port using &UPnP + Designa portam utendo &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Connecte ad rete CryptoLottoToken per SOCKS vicarium (e.g. quando conectens per Tor). + + + + &Connect through SOCKS proxy: + &Conecte per SOCKS vicarium: + + + + Proxy &IP: + &IP vicarii: + + + + IP address of the proxy (e.g. 127.0.0.1) + Inscriptio IP vicarii (e.g. 127.0.0.1) + + + + &Port: + &Porta: + + + + Port of the proxy (e.g. 9050) + Porta vicarii (e.g. 9050) + + + + SOCKS &Version: + SOCKS &Versio: + + + + SOCKS version of the proxy (e.g. 5) + SOCKS versio vicarii (e.g. 5) + + + + &Window + &Fenestra + + + + Show only a tray icon after minimizing the window. + Monstra tantum iconem in tabella systematis postquam fenestram minifactam est. + + + + &Minimize to the tray instead of the taskbar + &Minifac in tabellam systematis potius quam applicationum + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Minifac potius quam exire applicatione quando fenestra clausa sit. Si haec optio activa est, applicatio clausa erit tantum postquam selegeris Exi in menu. + + + + M&inimize on close + M&inifac ad claudendum + + + + &Display + &UI + + + + User Interface &language: + &Lingua monstranda utenti: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + Lingua monstranda utenti hic constitui potest. Haec configuratio effectiva erit postquam CryptoLottoToken iterum initiatum erit. + + + + &Unit to show amounts in: + &Unita qua quantitates monstrare: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Selige praedefinitam unitam subdivisionis monstrare in interfacie et quando nummos mittere + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Num monstrare inscriptiones CryptoLottoToken in enumeratione transactionum. + + + + &Display addresses in transaction list + &Monstra inscriptiones in enumeratione transactionum + + + + &OK + &OK + + + + &Cancel + &Cancella + + + + &Apply + &Applica + + + + default + praedefinitum + + + + Confirm options reset + Confirma optionum reconstituere + + + + Some settings may require a client restart to take effect. + Aliis configurationibus fortasse necesse est clientem iterum initiare ut effectivae sint. + + + + Do you want to proceed? + Vis procedere? + + + + + Warning + Monitio + + + + + This setting will take effect after restarting CryptoLottoToken. + Haec configuratio effectiva erit postquam CryptoLottoToken iterum initiatum erit. + + + + The supplied proxy address is invalid. + Inscriptio vicarii tradita non valida est. + + + + OverviewPage + + + Form + Schema + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + Monstrata informatio fortasse non recentissima est. Tuum cassidile per se synchronizat cum rete CryptoLottoToken postquam conexio constabilita est, sed hoc actio nondum perfecta est. + + + + Balance: + Pendendum: + + + + Unconfirmed: + Non confirmata: + + + + Wallet + Cassidile + + + + Immature: + Immatura: + + + + Mined balance that has not yet matured + Fossum pendendum quod nondum maturum est + + + + <b>Recent transactions</b> + <b>Recentes transactiones</b> + + + + Your current balance + Tuum pendendum iam nunc + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Totali nummi transactionum quae adhuc confirmandae sunt, et nondum afficiunt pendendum + + + + + out of sync + non synchronizato + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + CryptoLottoToken incipere non potest: cliccare-ad-pensandum handler + + + + QRCodeDialog + + + QR Code Dialog + Dialogus QR Codicis + + + + Request Payment + Posce Pensitationem + + + + Amount: + Quantitas: + + + + Label: + Titulus: + + + + Message: + Nuntius: + + + + &Save As... + &Salva ut... + + + + Error encoding URI into QR Code. + Error codificandi URI in codicem QR. + + + + The entered amount is invalid, please check. + Inserta quantitas non est valida, sodes proba. + + + + Resulting URI too long, try to reduce the text for label / message. + Resultato URI nimis longo, conare minuere verba pro titulo / nuntio. + + + + Save QR Code + Salva codicem QR + + + + PNG Images (*.png) + Imagines PNG (*.png) + + + + RPCConsole + + + Client name + Nomen clientis + + + + + + + + + + + + + N/A + N/A + + + + Client version + Versio clientis + + + + &Information + &Informatio + + + + Using OpenSSL version + Utens OpenSSL versione + + + + Startup time + Tempus initiandi + + + + Network + Rete + + + + Number of connections + Numerus conexionum + + + + On testnet + In testnet + + + + Block chain + Catena frustorum + + + + Current number of blocks + Numerus frustorum iam nunc + + + + Estimated total blocks + Aestimatus totalis numerus frustorum + + + + Last block time + Hora postremi frusti + + + + &Open + &Aperi + + + + Command-line options + Optiones mandati initiantis + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Monstra nuntium auxilii CryptoLottoToken-Qt ut videas enumerationem possibilium optionum CryptoLottoToken mandati initiantis. + + + + &Show + &Monstra + + + + &Console + &Terminale + + + + Build date + Dies aedificandi + + + + CryptoLottoToken - Debug window + CryptoLottoToken - Fenestra debug + + + + CryptoLottoToken Core + CryptoLottoToken Nucleus + + + + Debug log file + Debug catalogi plica + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + Aperi plicam catalogi de CryptoLottoToken debug ex activo indice datorum. Hoc possit pauca secunda pro plicis magnis catalogi. + + + + Clear console + Vacuefac terminale + + + + Welcome to the CryptoLottoToken RPC console. + Bene ventio in terminale RPC de CryptoLottoToken. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Utere sagittis sursum deorsumque ut per historiam naviges, et <b>Ctrl+L</b> ut scrinium vacuefacias. + + + + Type <b>help</b> for an overview of available commands. + Scribe <b>help</b> pro summario possibilium mandatorum. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Mitte Nummos + + + + Send to multiple recipients at once + Mitte pluribus accipientibus simul + + + + Add &Recipient + Adde &Accipientem + + + + Remove all transaction fields + Remove omnes campos transactionis + + + + Clear &All + Vacuefac &Omnia + + + + Balance: + Pendendum: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Confirma actionem mittendi + + + + S&end + &Mitte + + + + <b>%1</b> to %2 (%3) + <b>%1</b> ad %2 (%3) + + + + Confirm send coins + Confirma mittendum nummorum + + + + Are you sure you want to send %1? + Certus es te velle mittere %1? + + + + and + et + + + + The recipient address is not valid, please recheck. + Inscriptio accipientis non est valida, sodes reproba. + + + + The amount to pay must be larger than 0. + Oportet quantitatem ad pensandum maiorem quam 0 esse. + + + + The amount exceeds your balance. + Quantitas est ultra quod habes. + + + + The total exceeds your balance when the %1 transaction fee is included. + Quantitas est ultra quod habes cum merces transactionis %1 includitur. + + + + Duplicate address found, can only send to each address once per send operation. + Geminata inscriptio inventa, tantum posse mittere ad quamque inscriptionem semel singulare operatione. + + + + Error: Transaction creation failed! + Error: Creare transactionem abortum est! + + + + 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. + Error: transactio reiecta est. Hoc fiat si alii nummorum in tuo cassidili iam soluti sunt, ut si usus es exemplar de wallet.dat et nummi soluti sunt in exemplari sed non hic notati ut soluti. + + + + SendCoinsEntry + + + Form + Schema + + + + A&mount: + &Quantitas: + + + + Pay &To: + Pensa &Ad: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Inscriptio cui mittere pensitationem (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + Insero titulum huic inscriptioni ut eam in tuum librum inscriptionum addas. + + + + &Label: + &Titulus: + + + + Choose address from address book + Selige inscriptionem ex libro inscriptionum + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Conglutina inscriptionem ex latibulo + + + + Alt+P + Alt+P + + + + Remove this recipient + Remove hunc accipientem + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Insero inscriptionem CryptoLottoToken (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Signationes - Signa / Verifica nuntium + + + + &Sign Message + &Signa Nuntium + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Potes nuntios signare inscriptionibus tuis ut demonstres te eas possidere. Cautus es non amibiguum signare, quia impetus phiscatorum conentur te fallere ut signes identitatem tuam ad eos. Solas signa sententias cuncte descriptas quibus convenis. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Inscriptio qua signare nuntium (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Selige inscriptionem ex librum inscriptionum + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Glutina inscriptionem ex latibulo + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Insere hic nuntium quod vis signare + + + + Signature + Signatio + + + + Copy the current signature to the system clipboard + Copia signationem in latibulum systematis + + + + Sign the message to prove you own this CryptoLottoToken address + Signa nuntium ut demonstres hanc inscriptionem CryptoLottoToken a te possessa esse + + + + Sign &Message + Signa &Nuntium + + + + Reset all sign message fields + Reconstitue omnes campos signandi nuntii + + + + + Clear &All + Vacuefac &Omnia + + + + &Verify Message + &Verifica Nuntium + + + + Enter the signing 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. + Insere inscriptionem signantem, nuntium (cura ut copias intermissiones linearum, spatia, tabs, et cetera exacte) et signationem infra ut nuntium verifices. Cautus esto ne magis legas in signationem quam in nuntio signato ipso est, ut vites falli ab impetu homo-in-medio. + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Inscriptio qua nuntius signatus est (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Verifica nuntium ut cures signatum esse cum specifica inscriptione CryptoLottoToken + + + + Verify &Message + Verifica &Nuntium + + + + Reset all verify message fields + Reconstitue omnes campos verificandi nuntii + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Insere inscriptionem CryptoLottoToken (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Clicca "Signa Nuntium" ut signatio generetur + + + + Enter CryptoLottoToken signature + Insere signationem CryptoLottoToken + + + + + The entered address is invalid. + Inscriptio inserta non valida est. + + + + + + + Please check the address and try again. + Sodes inscriptionem proba et rursus conare. + + + + + The entered address does not refer to a key. + Inserta inscriptio clavem non refert. + + + + Wallet unlock was cancelled. + Cassidilis reserare cancellatum est. + + + + Private key for the entered address is not available. + Clavis privata absens est pro inserta inscriptione. + + + + Message signing failed. + Nuntium signare abortum est. + + + + Message signed. + Nuntius signatus. + + + + The signature could not be decoded. + Signatio decodificari non potuit. + + + + + Please check the signature and try again. + Sodes signationem proba et rursus conare. + + + + The signature did not match the message digest. + Signatio non convenit digesto nuntii + + + + Message verification failed. + Nuntium verificare abortum est. + + + + Message verified. + Nuntius verificatus. + + + + SplashScreen + + + The CryptoLottoToken developers + CryptoLottoToken curatores + + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + Apertum donec %1 + + + + %1/offline + %1/non conecto + + + + %1/unconfirmed + %1/non confirmata + + + + %1 confirmations + %1 confirmationes + + + + Status + Status + + + + , broadcast through %n node(s) + , disseminatum per %n nodo, disseminata per %n nodis + + + + Date + Dies + + + + Source + Fons + + + + Generated + Generatum + + + + + From + Ab + + + + + + To + Ad + + + + + own address + inscriptio propria + + + + label + titulus + + + + + + + + Credit + Creditum + + + + matures in %n more block(s) + maturum erit in %n plure frustomaturum erit in %n pluribus frustis + + + + not accepted + non acceptum + + + + + + + Debit + Debitum + + + + Transaction fee + Transactionis merces + + + + Net amount + Cuncta quantitas + + + + Message + Nuntius + + + + Comment + Annotatio + + + + Transaction ID + ID transactionis + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Nummis generatis necesse est maturitas 120 frustorum antequam illi transmitti possunt. Cum hoc frustum generavisti, disseminatum est ad rete ut addatur ad catenam frustorum. Si aboritur inire catenam, status eius mutabit in "non acceptum" et non transmittabile erit. Hoc interdum accidat si alter nodus frustum generat paucis secundis ante vel post tuum. + + + + Debug information + Informatio de debug + + + + Transaction + Transactio + + + + Inputs + Lectenda + + + + Amount + Quantitas + + + + true + verum + + + + false + falsum + + + + , has not been successfully broadcast yet + , nondum prospere disseminatum est + + + + Open for %n more block(s) + Aperi pro %n pluribus frustis + + + + unknown + ignotum + + + + TransactionDescDialog + + + Transaction details + Particularia transactionis + + + + This pane shows a detailed description of the transaction + Haec tabula monstrat descriptionem verbosam transactionis + + + + TransactionTableModel + + + Date + Dies + + + + Type + Typus + + + + Address + Inscriptio + + + + Amount + Quantitas + + + + Open for %n more block(s) + Aperi pro %n plure frustoAperi pro %n pluribus frustis + + + + Open until %1 + Apertum donec %1 + + + + Offline (%1 confirmations) + Non conectum (%1 confirmationes) + + + + Unconfirmed (%1 of %2 confirmations) + Non confirmatum (%1 de %2 confirmationibus) + + + + Confirmed (%1 confirmations) + Confirmatum (%1 confirmationes) + + + + Mined balance will be available when it matures in %n more block(s) + Fossum pendendum utibile erit quando id maturum est post %n plus frustumFossum pendendum utibile erit quando id maturum est post %n pluria frusta + + + + This block was not received by any other nodes and will probably not be accepted! + Hoc frustum non acceptum est ab ulla alia nodis et probabiliter non acceptum erit! + + + + Generated but not accepted + Generatum sed non acceptum + + + + Received with + Acceptum cum + + + + Received from + Acceptum ab + + + + Sent to + Missum ad + + + + Payment to yourself + Pensitatio ad te ipsum + + + + Mined + Fossa + + + + (n/a) + (n/a) + + + + Transaction status. Hover over this field to show number of confirmations. + Status transactionis. Supervola cum mure ut monstretur numerus confirmationum. + + + + Date and time that the transaction was received. + Dies et tempus quando transactio accepta est. + + + + Type of transaction. + Typus transactionis. + + + + Destination address of transaction. + Inscriptio destinationis transactionis. + + + + Amount removed from or added to balance. + Quantitas remota ex pendendo aut addita ei. + + + + TransactionView + + + + All + Omne + + + + Today + Hodie + + + + This week + Hac hebdomade + + + + This month + Hoc mense + + + + Last month + Postremo mense + + + + This year + Hoc anno + + + + Range... + Intervallum... + + + + Received with + Acceptum cum + + + + Sent to + Missum ad + + + + To yourself + Ad te ipsum + + + + Mined + Fossa + + + + Other + Alia + + + + Enter address or label to search + Insere inscriptionem vel titulum ut quaeras + + + + Min amount + Quantitas minima + + + + Copy address + Copia inscriptionem + + + + Copy label + Copia titulum + + + + Copy amount + Copia quantitatem + + + + Copy transaction ID + Copia transactionis ID + + + + Edit label + Muta titulum + + + + Show transaction details + Monstra particularia transactionis + + + + Export Transaction Data + Exporta Data Transactionum + + + + Comma separated file (*.csv) + Comma Separata Plica (*.csv) + + + + Confirmed + Confirmatum + + + + Date + Dies + + + + Type + Typus + + + + Label + Titulus + + + + Address + Inscriptio + + + + Amount + Quantitas + + + + ID + ID + + + + Error exporting + Error exportandi + + + + Could not write to file %1. + Non potuisse scribere ad plicam %1. + + + + Range: + Intervallum: + + + + to + ad + + + + WalletModel + + + Send Coins + Mitte Nummos + + + + WalletView + + + &Export + &Exporta + + + + Export the data in the current tab to a file + Exporta data in hac tabella in plicam + + + + Backup Wallet + Conserva cassidile + + + + Wallet Data (*.dat) + Data cassidilis (*.dat) + + + + Backup Failed + Conservare abortum est. + + + + There was an error trying to save the wallet data to the new location. + Error erat conante salvare data cassidilis ad novum locum. + + + + Backup Successful + Successum in conservando + + + + The wallet data was successfully saved to the new location. + Successum in salvando data cassidilis in novum locum. + + + + bitcoin-core + + + CryptoLottoToken version + Versio de CryptoLottoToken + + + + Usage: + Usus: + + + + Send command to -server or CryptoLottoTokend + Mitte mandatum ad -server vel CryptoLottoTokend + + + + List commands + Enumera mandata + + + + Get help for a command + Accipe auxilium pro mandato + + + + Options: + Optiones: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Specifica configurationis plicam (praedefinitum: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + Specifica pid plicam (praedefinitum: CryptoLottoToken.pid) + + + + Specify data directory + Specifica indicem datorum + + + + Set database cache size in megabytes (default: 25) + Constitue magnitudinem databasis cache in megabytes (praedefinitum: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Ausculta pro conexionibus in <porta> (praedefinitum: 22556 vel testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Manutene non plures quam <n> conexiones ad paria (praedefinitum: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Conecta ad nodum acceptare inscriptiones parium, et disconecte + + + + Specify your own public address + Specifica tuam propriam publicam inscriptionem + + + + Threshold for disconnecting misbehaving peers (default: 100) + Limen pro disconectendo paria improba (praedefinitum: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Numerum secundorum prohibere ne paria improba reconectant (praedefinitum: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + Error erat dum initians portam RPC %u pro auscultando in IPv4: %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Ausculta pro conexionibus JSON-RPC in <porta> (praedefinitum: 22555 vel testnet: 44555) + + + + Accept command line and JSON-RPC commands + Accipe terminalis et JSON-RPC mandata. + + + + Run in the background as a daemon and accept commands + Operare infere sicut daemon et mandata accipe + + + + Use the test network + Utere rete experimentale + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Accipe conexiones externas (praedefinitum: 1 nisi -proxy neque -connect) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + %s, necesse est te rpcpassword constituere in plica configurationis: +%s +Hortatur te hanc fortuitam tesseram uti: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(non est necesse te hanc tesseram meminisse) +Nomen usoris et tessera eadem esse NON POSSUNT. +Si plica non existit, eam crea cum permissionibus ut eius dominus tantum sinitur id legere. +Quoque hortatur alertnotify constituere ut tu notificetur de problematibus; +exempli gratia: alertnotify=echo %%s | mail -s "CryptoLottoToken Notificatio" admin@foo.com + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + Error erat dum initians portam RPC %u pro auscultando in IPv6, labens retrorsum ad IPv4: %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + Conglutina ad inscriptionem datam et semper in eam ausculta. Utere [moderatrum]:porta notationem pro IPv6 + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + Non posse serare datorum indicem %s. CryptoLottoToken probabiliter iam operatur. + + + + 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. + Error: Transactio eiecta est! Hoc possit accidere si alii nummorum in cassidili tuo iam soluti sint, ut si usus es exemplar de wallet.dat et nummi soluti sunt in exemplari sed non hic notati ut soluti. + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + Error: Huic transactioni necesse est merces saltem %s propter eius magnitudinem, complexitatem, vel usum recentum acceptorum nummorum! + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + Facere mandatum quotiescumque notificatio affinis accipitur (%s in mandato mutatur in nuntium) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Facere mandatum quotiescumque cassidilis transactio mutet (%s in mandato sbstituitur ab TxID) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Constitue magnitudinem maximam transactionum magnae-prioritatis/parvae-mercedis in octetis/bytes (praedefinitum: 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + Hoc est prae-dimittum experimentala aedes - utere eo periculo tuo proprio - nolite utere fodendo vel applicationibus mercatoriis + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Monitio: -paytxfee constitutum valde magnum! Hoc est merces transactionis solves si mittis transactionem. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + Monitio: Monstratae transactiones fortasse non recta sint! Forte oportet tibi progredere, an aliis nodis progredere. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Monitio: Sodes cura ut dies tempusque computatri tui recti sunt! Si horologium tuum pravum est, CryptoLottoToken non proprie fungetur. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Monitio: error legendo wallet.dat! Omnes claves recte lectae, sed data transactionum vel libri inscriptionum fortasse desint vel prava sint. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Monitio: wallet.data corrupta, data salvata! Originalis wallet.dat salvata ut wallet.{timestamp}.bak in %s; si pendendum tuum vel transactiones pravae sunt, oportet ab conservato restituere. + + + + Attempt to recover private keys from a corrupt wallet.dat + Conare recipere claves privatas de corrupto wallet.dat + + + + Block creation options: + Optiones creandi frustorum: + + + + Connect only to the specified node(s) + Conecte sole ad nodos specificatos (vel nodum specificatum) + + + + Corrupted block database detected + Corruptum databasum frustorum invenitur + + + + Discover own IP address (default: 1 when listening and no -externalip) + Discooperi propriam inscriptionem IP (praedefinitum: 1 quando auscultans et nullum -externalip) + + + + Do you want to rebuild the block database now? + Visne reficere databasum frustorum iam? + + + + Error initializing block database + Error initiando databasem frustorum + + + + Error initializing wallet database environment %s! + Error initiando systematem databasi cassidilis %s! + + + + Error loading block database + Error legendo frustorum databasem + + + + Error opening block database + Error aperiendo databasum frustorum + + + + Error: Disk space is low! + Error: Inopia spatii disci! + + + + Error: Wallet locked, unable to create transaction! + Error: Cassidile seratum, non posse transactionem creare! + + + + Error: system error: + Error: systematis error: + + + + Failed to listen on any port. Use -listen=0 if you want this. + Non potuisse auscultare in ulla porta. Utere -listen=0 si hoc vis. + + + + Failed to read block info + Non potuisse informationem frusti legere + + + + Failed to read block + Non potuisse frustum legere + + + + Failed to sync block index + Synchronizare indicem frustorum abortum est + + + + Failed to write block index + Scribere indicem frustorum abortum est + + + + Failed to write block info + Scribere informationem abortum est + + + + Failed to write block + Scribere frustum abortum est + + + + Failed to write file info + Scribere informationem plicae abortum est + + + + Failed to write to coin database + Scribere databasem nummorum abortum est + + + + Failed to write transaction index + Scribere indicem transactionum abortum est + + + + Failed to write undo data + Scribere data pro cancellando mutationes abortum est + + + + Find peers using DNS lookup (default: 1 unless -connect) + Inveni paria utendo DNS quaerendo (praedefinitum: 1 nisi -connect) + + + + Generate coins (default: 0) + Genera nummos (praedefinitum: 0) + + + + How many blocks to check at startup (default: 288, 0 = all) + Quot frusta proba ad initium (praedefinitum: 288, 0 = omnia) + + + + How thorough the block verification is (0-4, default: 3) + Quam perfecta frustorum verificatio est (0-4, praedefinitum: 3) + + + + Not enough file descriptors available. + Inopia descriptorum plicarum. + + + + Rebuild block chain index from current blk000??.dat files + Restituere indicem catenae frustorum ex activis plicis blk000??.dat + + + + Set the number of threads to service RPC calls (default: 4) + Constitue numerum filorum ad tractandum RPC postulationes (praedefinitum: 4) + + + + Verifying blocks... + Verificante frusta... + + + + Verifying wallet... + Verificante cassidilem... + + + + Imports blocks from external blk000??.dat file + Importat frusta ab externa plica blk000??.dat + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + Constitue numerum filorum verificationis scriptorum (Maximum 16, 0 = auto, <0 = tot corda libera erunt, praedefinitum: 0) + + + + Information + Informatio + + + + Invalid -tor address: '%s' + Inscriptio -tor non valida: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + Quantitas non valida pro -minrelaytxfee=<amount>: '%s' + + + + Invalid amount for -mintxfee=<amount>: '%s' + Quantitas non valida pro -mintxfee=<amount>: '%s' + + + + Maintain a full transaction index (default: 0) + Manutene completam indicem transactionum (praedefinitum: 0) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Maxima magnitudo memoriae pro datis accipendis singulis conexionibus, <n>*1000 octetis/bytes (praedefinitum: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Maxima magnitudo memoriae pro datis mittendis singulis conexionibus, <n>*1000 octetis/bytes (praedefinitum: 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + Tantum accipe catenam frustorum convenientem internis lapidibus (praedefinitum: 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Tantum conecte ad nodos in rete <net> (IPv4, IPv6 aut Tor) + + + + Output extra debugging information. Implies all other -debug* options + Exscribe additiciam informationem pro debug. Implicat omnes alias optiones -debug* + + + + Output extra network debugging information + Exscribe additiciam informationem pro retis debug. + + + + Prepend debug output with timestamp (default: 1) + Antepone pittacium temporis ante exscriptum de debug + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + Optiones SSL: (vide vici de CryptoLottoToken pro instructionibus SSL configurationis) + + + + Select the version of socks proxy to use (4-5, default: 5) + Selige versionem socks vicarii utendam (4-5, praedefinitum: 5) + + + + Send trace/debug info to console instead of debug.log file + Mitte informationem vestigii/debug ad terminale potius quam plicam debug.log + + + + Send trace/debug info to debugger + Mitte informationem vestigii/debug ad debugger + + + + Set maximum block size in bytes (default: 250000) + Constitue maximam magnitudinem frusti in octetis/bytes (praedefinitum: 250000) + + + + Set minimum block size in bytes (default: 0) + Constitue minimam magnitudinem frusti in octetis/bytes (praedefinitum: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Diminue plicam debug.log ad initium clientis (praedefinitum: 1 nisi -debug) + + + + Signing transaction failed + Signandum transactionis abortum est + + + + Specify connection timeout in milliseconds (default: 5000) + Specifica tempumfati conexionis in millisecundis (praedefinitum: 5000) + + + + System error: + Systematis error: + + + + Transaction amount too small + Magnitudo transactionis nimis parva + + + + Transaction amounts must be positive + Necesse est magnitudines transactionum positivas esse. + + + + Transaction too large + Transactio nimis magna + + + + Use UPnP to map the listening port (default: 0) + Utere UPnP designare portam auscultandi (praedefinitum: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Utere UPnP designare portam auscultandi (praedefinitum: 1 quando auscultans) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Utere vicarium ut extendas ad tor servitia occulta (praedefinitum: idem ut -proxy) + + + + Username for JSON-RPC connections + Nomen utentis pro conexionibus JSON-RPC + + + + Warning + Monitio + + + + Warning: This version is obsolete, upgrade required! + Monitio: Haec versio obsoleta est, progressio postulata! + + + + You need to rebuild the databases using -reindex to change -txindex + Oportet recreare databases utendo -reindex ut mutes -txindex + + + + wallet.dat corrupt, salvage failed + wallet.dat corrupta, salvare abortum est + + + + Password for JSON-RPC connections + Tessera pro conexionibus JSON-RPC + + + + Allow JSON-RPC connections from specified IP address + Permitte conexionibus JSON-RPC ex inscriptione specificata + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Mitte mandata nodo operanti in <ip> (praedefinitum: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Pelle mandatum quando optissimum frustum mutat (%s in mandato substituitur ab hash frusti) + + + + Upgrade wallet to latest format + Progredere cassidile ad formam recentissimam + + + + Set key pool size to <n> (default: 100) + Constitue magnitudinem stagni clavium ad <n> (praedefinitum: 100) + + + + Rescan the block chain for missing wallet transactions + Iterum perlege catenam frustorum propter absentes cassidilis transactiones + + + + Use OpenSSL (https) for JSON-RPC connections + Utere OpenSSL (https) pro conexionibus JSON-RPC + + + + Server certificate file (default: server.cert) + Plica certificationis daemonis moderantis (praedefinitum: server.cert) + + + + Server private key (default: server.pem) + Clavis privata daemonis moderans (praedefinitum: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Acceptabiles cifrae (praedefinitum: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Hic nuntius auxilii + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Non posse conglutinare ad %s in hoc computatro (conglutinare redidit errorem %d, %s) + + + + Connect through socks proxy + Conecte per socks vicarium + + + + Allow DNS lookups for -addnode, -seednode and -connect + Permitte quaerenda DNS pro -addnode, -seednode, et -connect + + + + Loading addresses... + Legens inscriptiones... + + + + Error loading wallet.dat: Wallet corrupted + Error legendi wallet.dat: Cassidile corruptum + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Error legendi wallet.dat: Cassidili necesse est recentior versio CryptoLottoToken + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Cassidili necesse erat rescribi: Repelle CryptoLottoToken ut compleas + + + + Error loading wallet.dat + Error legendi wallet.dat + + + + Invalid -proxy address: '%s' + Inscriptio -proxy non valida: '%s' + + + + Unknown network specified in -onlynet: '%s' + Ignotum rete specificatum in -onlynet: '%s' + + + + Unknown -socks proxy version requested: %i + Ignota -socks vicarii versio postulata: %i + + + + Cannot resolve -bind address: '%s' + Non posse resolvere -bind inscriptonem: '%s' + + + + Cannot resolve -externalip address: '%s' + Non posse resolvere -externalip inscriptionem: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + Quantitas non valida pro -paytxfee=<quantitas>: '%s' + + + + Invalid amount + Quantitas non valida + + + + Insufficient funds + Inopia nummorum + + + + Loading block index... + Legens indicem frustorum... + + + + Add a node to connect to and attempt to keep the connection open + Adice nodum cui conectere et conare sustinere conexionem apertam + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Non posse conglutinare ad %s in hoc cumputatro. CryptoLottoToken probabiliter iam operatur. + + + + Fee per KB to add to transactions you send + Merces per KB addere ad transactiones tu mittas + + + + Loading wallet... + Legens cassidile... + + + + Cannot downgrade wallet + Non posse cassidile regredi + + + + Cannot write default address + Non posse scribere praedefinitam inscriptionem + + + + Rescanning... + Iterum perlegens... + + + + Done loading + Completo lengendi + + + + To use the %s option + Ut utaris optione %s + + + + Error + Error + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + Necesse est te rpcpassword=<tesseram> constituere in plica configurationum: +%s +Si plica non existat, crea eam cum permissionibus ut solus eius dominus eam legere sinatur. + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_lt.qm b/src/qt/locale/bitcoin_lt.qm new file mode 100644 index 0000000..09e0fad Binary files /dev/null and b/src/qt/locale/bitcoin_lt.qm differ diff --git a/src/qt/locale/bitcoin_lt.ts b/src/qt/locale/bitcoin_lt.ts new file mode 100644 index 0000000..494b837 --- /dev/null +++ b/src/qt/locale/bitcoin_lt.ts @@ -0,0 +1,2924 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + Apie CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> versija + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + Tai eksperimentinė programa. + +Platinama pagal MIT/X11 licenciją, kurią rasite faile COPYING arba http://www.opensource.org/licenses/mit-license.php. + +Šiame produkte yra OpenSSL projekto kuriamas OpenSSL Toolkit (http://www.openssl.org/), Eric Young parašyta kriptografinė programinė įranga bei Thomas Bernard sukurta UPnP programinė įranga. + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + Adresų knygelė + + + + Double-click to edit address or label + Spragtelėkite, kad pakeistumėte adresą arba žymę + + + + Create a new address + Sukurti naują adresą + + + + Copy the currently selected address to the system clipboard + Kopijuoti esamą adresą į mainų atmintį + + + + &New Address + &Naujas adresas + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Tai yra jūsų CryptoLottoToken adresai mokėjimų gavimui. Galite duoti skirtingus adresus atskiriems siuntėjams, kad galėtumėte sekti, kas jums moka. + + + + &Copy Address + &Kopijuoti adresą + + + + Show &QR Code + Rodyti &QR kodą + + + + Sign a message to prove you own a CryptoLottoToken address + Pasirašykite žinutę, kad įrodytume, jog esate CryptoLottoToken adreso savininkas + + + + Sign &Message + Registruoti praneši&mą + + + + Delete the currently selected address from the list + + + + + Export the data in the current tab to a file + + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Patikrinkite žinutę, jog įsitikintumėte, kad ją pasirašė nurodytas CryptoLottoToken adresas + + + + &Verify Message + &Tikrinti žinutę + + + + &Delete + &Trinti + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + Kopijuoti ž&ymę + + + + &Edit + &Keisti + + + + Send &Coins + + + + + Export Address Book Data + Eksportuoti adresų knygelės duomenis + + + + Comma separated file (*.csv) + Kableliais išskirtas failas (*.csv) + + + + Error exporting + Eksportavimo klaida + + + + Could not write to file %1. + Nepavyko įrašyti į failą %1. + + + + AddressTableModel + + + Label + Žymė + + + + Address + Adresas + + + + (no label) + (nėra žymės) + + + + AskPassphraseDialog + + + Passphrase Dialog + Slaptafrazės dialogas + + + + Enter passphrase + Įvesti slaptafrazę + + + + New passphrase + Nauja slaptafrazė + + + + Repeat new passphrase + Pakartokite naują slaptafrazę + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Įveskite naują piniginės slaptafrazę.<br/>Prašome naudoti slaptafrazę iš <b> 10 ar daugiau atsitiktinių simbolių</b> arba <b>aštuonių ar daugiau žodžių</b>. + + + + Encrypt wallet + Užšifruoti piniginę + + + + This operation needs your wallet passphrase to unlock the wallet. + Ši operacija reikalauja jūsų piniginės slaptafrazės jai atrakinti. + + + + Unlock wallet + Atrakinti piniginę + + + + This operation needs your wallet passphrase to decrypt the wallet. + Ši operacija reikalauja jūsų piniginės slaptafrazės jai iššifruoti. + + + + Decrypt wallet + Iššifruoti piniginę + + + + Change passphrase + Pakeisti slaptafrazę + + + + Enter the old and new passphrase to the wallet. + Įveskite seną ir naują piniginės slaptafrazes. + + + + Confirm wallet encryption + Patvirtinkite piniginės užšifravimą + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Dėmesio: jei užšifruosite savo piniginę ir pamesite slaptafrazę, jūs<b>PRARASITE VISUS SAVO CryptoLottoTokenUS</b>! + + + + Are you sure you wish to encrypt your wallet? + Ar tikrai norite šifruoti savo piniginę? + + + + 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. + + + + + + Warning: The Caps Lock key is on! + Įspėjimas: įjungtas Caps Lock klavišas! + + + + + Wallet encrypted + Piniginė užšifruota + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken dabar užsidarys šifravimo proceso pabaigai. Atminkite, kad piniginės šifravimas negali pilnai apsaugoti CryptoLottoTokenų vagysčių kai tinkle esančios kenkėjiškos programos patenka į jūsų kompiuterį. + + + + + + + Wallet encryption failed + Nepavyko užšifruoti piniginę + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Dėl vidinės klaidos nepavyko užšifruoti piniginę.Piniginė neužšifruota. + + + + + The supplied passphrases do not match. + Įvestos slaptafrazės nesutampa. + + + + Wallet unlock failed + Nepavyko atrakinti piniginę + + + + + + The passphrase entered for the wallet decryption was incorrect. + Neteisingai įvestas slaptažodis piniginės iššifravimui. + + + + Wallet decryption failed + Nepavyko iššifruoti piniginės + + + + Wallet passphrase was successfully changed. + Piniginės slaptažodis sėkmingai pakeistas. + + + + BitcoinGUI + + + Sign &message... + Pasirašyti ži&nutę... + + + + Synchronizing with network... + Sinchronizavimas su tinklu ... + + + + &Overview + &Apžvalga + + + + Show general overview of wallet + Rodyti piniginės bendrą apžvalgą + + + + &Transactions + &Sandoriai + + + + Browse transaction history + Apžvelgti sandorių istoriją + + + + Edit the list of stored addresses and labels for sending + Redaguoti išsaugotus adresus bei žymes + + + + Show the list of addresses for receiving payments + Parodyti adresų sąraša mokėjimams gauti + + + + E&xit + &Išeiti + + + + Quit application + Išjungti programą + + + + Show information about CryptoLottoToken + Rodyti informaciją apie CryptoLottoToken + + + + About &Qt + Apie &Qt + + + + Show information about Qt + Rodyti informaciją apie Qt + + + + &Options... + &Parinktys... + + + + &Encrypt Wallet... + &Užšifruoti piniginę... + + + + &Backup Wallet... + &Backup piniginę... + + + + &Change Passphrase... + &Keisti slaptafrazę... + + + + Importing blocks from disk... + + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + Siųsti monetas CryptoLottoToken adresui + + + + Modify configuration options for CryptoLottoToken + Keisti CryptoLottoToken konfigūracijos galimybes + + + + Backup wallet to another location + Daryti piniginės atsarginę kopiją + + + + Change the passphrase used for wallet encryption + Pakeisti slaptafrazę naudojamą piniginės užšifravimui + + + + &Debug window + &Derinimo langas + + + + Open debugging and diagnostic console + Atverti derinimo ir diagnostikos konsolę + + + + &Verify message... + &Tikrinti žinutę... + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Piniginė + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + &Apie CryptoLottoToken + + + + &Show / Hide + &Rodyti / Slėpti + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + &Failas + + + + &Settings + &Nustatymai + + + + &Help + &Pagalba + + + + Tabs toolbar + Kortelių įrankinė + + + + + [testnet] + [testavimotinklas] + + + + CryptoLottoToken client + CryptoLottoToken klientas + + + + %n active connection(s) to CryptoLottoToken network + %n CryptoLottoToken tinklo aktyvus ryšys%n CryptoLottoToken tinklo aktyvūs ryšiai%n CryptoLottoToken tinklo aktyvūs ryšiai + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + + + + + Warning + + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + Atnaujinta + + + + Catching up... + Vejamasi... + + + + Confirm transaction fee + Patvirtinti sandorio mokestį + + + + Sent transaction + Sandoris nusiųstas + + + + Incoming transaction + Ateinantis sandoris + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Data: %1 +Suma: %2 +Tipas: %3 +Adresas: %4 + + + + + URI handling + URI apdorojimas + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Piniginė <b>užšifruota</b> ir šiuo metu <b>atrakinta</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Piniginė <b>užšifruota</b> ir šiuo metu <b>užrakinta</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + Tinklo įspėjimas + + + + EditAddressDialog + + + Edit Address + Keisti adresą + + + + &Label + Ž&ymė + + + + The label associated with this address book entry + Žymė yra susieta su šios adresų knygelęs turiniu + + + + &Address + &Adresas + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Adresas yra susietas su šios adresų knygelęs turiniu. Tai gali būti keičiama tik siuntimo adresams. + + + + New receiving address + Naujas gavimo adresas + + + + New sending address + Naujas siuntimo adresas + + + + Edit receiving address + Keisti gavimo adresą + + + + Edit sending address + Keisti siuntimo adresą + + + + The entered address "%1" is already in the address book. + Įvestas adresas „%1“ jau yra adresų knygelėje. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + Įvestas adresas „%1“ nėra galiojantis CryptoLottoToken adresas. + + + + Could not unlock wallet. + Nepavyko atrakinti piniginės. + + + + New key generation failed. + Naujo rakto generavimas nepavyko. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + versija + + + + Usage: + Naudojimas: + + + + command-line options + komandinės eilutės parametrai + + + + UI options + Naudotoji sąsajos parametrai + + + + Set language, for example "de_DE" (default: system locale) + Nustatyti kalbą, pavyzdžiui "lt_LT" (numatyta: sistemos kalba) + + + + Start minimized + Paleisti sumažintą + + + + Show splash screen on startup (default: 1) + + + + + OptionsDialog + + + Options + Parinktys + + + + &Main + &Pagrindinės + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + &Mokėti sandorio mokestį + + + + Automatically start CryptoLottoToken after logging in to the system. + Automatiškai paleisti Bitkoin programą įjungus sistemą. + + + + &Start CryptoLottoToken on system login + &Paleisti CryptoLottoToken programą su window sistemos paleidimu + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + &Tinklas + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Automatiškai atidaryti CryptoLottoToken kliento prievadą maršrutizatoriuje. Tai veikia tik tada, kai jūsų maršrutizatorius palaiko UPnP ir ji įjungta. + + + + Map port using &UPnP + Persiųsti prievadą naudojant &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Jungtis į Bitkoin tinklą per socks proxy (pvz. jungiantis per Tor) + + + + &Connect through SOCKS proxy: + &Jungtis per SOCKS tarpinį serverį: + + + + Proxy &IP: + Tarpinio serverio &IP: + + + + IP address of the proxy (e.g. 127.0.0.1) + Tarpinio serverio IP adresas (pvz. 127.0.0.1) + + + + &Port: + &Prievadas: + + + + Port of the proxy (e.g. 9050) + Tarpinio serverio preivadas (pvz, 9050) + + + + SOCKS &Version: + SOCKS &versija: + + + + SOCKS version of the proxy (e.g. 5) + Tarpinio serverio SOCKS versija (pvz., 5) + + + + &Window + &Langas + + + + Show only a tray icon after minimizing the window. + Po programos lango sumažinimo rodyti tik programos ikoną. + + + + &Minimize to the tray instead of the taskbar + &M sumažinti langą bet ne užduočių juostą + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Uždarant langą neuždaryti programos. Kai ši parinktis įjungta, programa bus uždaryta tik pasirinkus meniu komandą Baigti. + + + + M&inimize on close + &Sumažinti uždarant + + + + &Display + &Rodymas + + + + User Interface &language: + Naudotojo sąsajos &kalba: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + Čia gali būti nustatyta naudotojo sąsajos kalba. Šis nustatymas įsigalios iš naujo paleidus CryptoLottoToken. + + + + &Unit to show amounts in: + &Vienetai, kuriais rodyti sumas: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Rodomų ir siunčiamų monetų kiekio matavimo vienetai + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + + + &Display addresses in transaction list + &Rodyti adresus sandorių sąraše + + + + &OK + &Gerai + + + + &Cancel + &Atšaukti + + + + &Apply + &Pritaikyti + + + + default + numatyta + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + Įspėjimas + + + + + This setting will take effect after restarting CryptoLottoToken. + + + + + The supplied proxy address is invalid. + Nurodytas tarpinio serverio adresas negalioja. + + + + OverviewPage + + + Form + Forma + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + + + + + Balance: + Balansas: + + + + Unconfirmed: + Nepatvirtinti: + + + + Wallet + Piniginė + + + + Immature: + Nepribrendę: + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + <b>Naujausi sandoriai</b> + + + + Your current balance + Jūsų einamasis balansas + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Iš viso sandorių, įskaitant tuos kurie dar turi būti patvirtinti, ir jie dar nėra įskaičiuotii į einamosios sąskaitos balansą + + + + + out of sync + + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + QR kodo dialogas + + + + Request Payment + Prašau išmokėti + + + + Amount: + Suma: + + + + Label: + Žymė: + + + + Message: + Žinutė: + + + + &Save As... + Į&rašyti kaip... + + + + Error encoding URI into QR Code. + Klaida, koduojant URI į QR kodą. + + + + The entered amount is invalid, please check. + Įvesta suma neteisinga, prašom patikrinti. + + + + Resulting URI too long, try to reduce the text for label / message. + + + + + Save QR Code + Įrašyti QR kodą + + + + PNG Images (*.png) + PNG paveikslėliai (*.png) + + + + RPCConsole + + + Client name + Kliento pavadinimas + + + + + + + + + + + + + N/A + nėra + + + + Client version + Kliento versija + + + + &Information + &Informacija + + + + Using OpenSSL version + Naudojama OpenSSL versija + + + + Startup time + Paleidimo laikas + + + + Network + Tinklas + + + + Number of connections + Prisijungimų kiekis + + + + On testnet + Testnete + + + + Block chain + Blokų grandinė + + + + Current number of blocks + Dabartinis blokų skaičius + + + + Estimated total blocks + + + + + Last block time + Paskutinio bloko laikas + + + + &Open + &Atverti + + + + Command-line options + Komandinės eilutės parametrai + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + + &Show + &Rodyti + + + + &Console + &Konsolė + + + + Build date + Kompiliavimo data + + + + CryptoLottoToken - Debug window + CryptoLottoToken - Derinimo langas + + + + CryptoLottoToken Core + CryptoLottoToken branduolys + + + + Debug log file + Derinimo žurnalo failas + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + Išvalyti konsolę + + + + Welcome to the CryptoLottoToken RPC console. + + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + + + + + Type <b>help</b> for an overview of available commands. + + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Siųsti monetas + + + + Send to multiple recipients at once + Siųsti keliems gavėjams vienu metu + + + + Add &Recipient + &A Pridėti gavėją + + + + Remove all transaction fields + Pašalinti visus sandorio laukus + + + + Clear &All + Išvalyti &viską + + + + Balance: + Balansas: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Patvirtinti siuntimo veiksmą + + + + S&end + &Siųsti + + + + <b>%1</b> to %2 (%3) + <b>%1</b> to %2 (%3) + + + + Confirm send coins + Patvirtinti monetų siuntimą + + + + Are you sure you want to send %1? + Ar tikrai norite siųsti %1? + + + + and + ir + + + + The recipient address is not valid, please recheck. + Negaliojantis gavėjo adresas. Patikrinkite. + + + + The amount to pay must be larger than 0. + Apmokėjimo suma turi būti didesnė nei 0. + + + + The amount exceeds your balance. + Suma viršija jūsų balansą. + + + + The total exceeds your balance when the %1 transaction fee is included. + Jei pridedame sandorio mokestį %1 bendra suma viršija jūsų balansą. + + + + Duplicate address found, can only send to each address once per send operation. + Rastas adreso dublikatas. + + + + Error: Transaction creation failed! + + + + + 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. + Klaida: sandoris buvo atmestas.Tai gali įvykti, jei kai kurios monetos iš jūsų piniginėje jau buvo panaudotos, pvz. jei naudojote wallet.dat kopiją ir monetos buvo išleistos kopijoje, bet nepažymėtos kaip skirtos išleisti čia. + + + + SendCoinsEntry + + + Form + Forma + + + + A&mount: + Su&ma: + + + + Pay &To: + Mokėti &gavėjui: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + Įveskite žymę šiam adresui kad galėtumėte įtraukti ją į adresų knygelę + + + + &Label: + Ž&ymė: + + + + Choose address from address book + Pasirinkite adresą iš adresų knygelės + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Įvesti adresą iš mainų atminties + + + + Alt+P + Alt+P + + + + Remove this recipient + Pašalinti šį gavėją + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Įveskite bitkoinų adresą (pvz. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + + + + + &Sign Message + &Pasirašyti žinutę + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Įveskite bitkoinų adresą (pvz. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Pasirinkite adresą iš adresų knygelės + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Įvesti adresą iš mainų atminties + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Įveskite pranešimą, kurį norite pasirašyti čia + + + + Signature + + + + + Copy the current signature to the system clipboard + + + + + Sign the message to prove you own this CryptoLottoToken address + Registruotis žinute įrodymuii, kad turite šį adresą + + + + Sign &Message + + + + + Reset all sign message fields + + + + + + Clear &All + Išvalyti &viską + + + + &Verify Message + &Patikrinti žinutę + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Įveskite bitkoinų adresą (pvz. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Patikrinkite žinutę, jog įsitikintumėte, kad ją pasirašė nurodytas CryptoLottoToken adresas + + + + Verify &Message + + + + + Reset all verify message fields + + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Įveskite bitkoinų adresą (pvz. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Spragtelėkite "Registruotis žinutę" tam, kad gauti parašą + + + + Enter CryptoLottoToken signature + Įveskite CryptoLottoToken parašą + + + + + The entered address is invalid. + Įvestas adresas negalioja. + + + + + + + Please check the address and try again. + Prašom patikrinti adresą ir bandyti iš naujo. + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + Piniginės atrakinimas atšauktas. + + + + Private key for the entered address is not available. + + + + + Message signing failed. + Žinutės pasirašymas nepavyko. + + + + Message signed. + Žinutė pasirašyta. + + + + The signature could not be decoded. + Nepavyko iškoduoti parašo. + + + + + Please check the signature and try again. + Prašom patikrinti parašą ir bandyti iš naujo. + + + + The signature did not match the message digest. + Parašas neatitinka žinutės. + + + + Message verification failed. + Žinutės tikrinimas nepavyko. + + + + Message verified. + Žinutė patikrinta. + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + [testavimotinklas] + + + + TransactionDesc + + + Open until %1 + Atidaryta iki %1 + + + + %1/offline + %1/neprisijungęs + + + + %1/unconfirmed + %1/nepatvirtintas + + + + %1 confirmations + %1 patvirtinimų + + + + Status + Būsena + + + + , broadcast through %n node(s) + + + + + Date + Data + + + + Source + Šaltinis + + + + Generated + Sugeneruotas + + + + + From + Nuo + + + + + + To + Kam + + + + + own address + savo adresas + + + + label + žymė + + + + + + + + Credit + Kreditas + + + + matures in %n more block(s) + + + + + not accepted + nepriimta + + + + + + + Debit + Debitas + + + + Transaction fee + Sandorio mokestis + + + + Net amount + Neto suma + + + + Message + Žinutė + + + + Comment + Komentaras + + + + Transaction ID + Sandorio ID + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Išgautos monetos turi sulaukti 120 blokų, kol jos gali būti naudojamos. Kai sukūrėte šį bloką, jis buvo transliuojamas tinkle ir turėjo būti įtrauktas į blokų grandinę. Jei nepavyksta patekti į grandinę, bus pakeista į "nepriėmė", o ne "vartojamas". Tai kartais gali atsitikti, jei kitas mazgas per keletą sekundžių sukuria bloką po jūsų bloko. + + + + Debug information + Derinimo informacija + + + + Transaction + Sandoris + + + + Inputs + + + + + Amount + Suma + + + + true + tiesa + + + + false + netiesa + + + + , has not been successfully broadcast yet + , transliavimas dar nebuvo sėkmingas + + + + Open for %n more block(s) + + + + + unknown + nežinomas + + + + TransactionDescDialog + + + Transaction details + Sandorio detelės + + + + This pane shows a detailed description of the transaction + Šis langas sandorio detalų aprašymą + + + + TransactionTableModel + + + Date + Data + + + + Type + Tipas + + + + Address + Adresas + + + + Amount + Suma + + + + Open for %n more block(s) + + + + + Open until %1 + Atidaryta iki %1 + + + + Offline (%1 confirmations) + Atjungta (%1 patvirtinimai) + + + + Unconfirmed (%1 of %2 confirmations) + Nepatvirtintos (%1 iš %2 patvirtinimų) + + + + Confirmed (%1 confirmations) + Patvirtinta (%1 patvirtinimai) + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + Šis blokas negautas nė vienu iš mazgų ir matomai nepriimtas + + + + Generated but not accepted + Išgauta bet nepriimta + + + + Received with + Gauta su + + + + Received from + Gauta iš + + + + Sent to + Siųsta + + + + Payment to yourself + Mokėjimas sau + + + + Mined + Išgauta + + + + (n/a) + nepasiekiama + + + + Transaction status. Hover over this field to show number of confirmations. + Sandorio būklė. Užvedus pelės žymeklį ant šios srities matysite patvirtinimų skaičių. + + + + Date and time that the transaction was received. + Sandorio gavimo data ir laikas + + + + Type of transaction. + Sandorio tipas. + + + + Destination address of transaction. + Sandorio paskirties adresas + + + + Amount removed from or added to balance. + Suma pridėta ar išskaičiuota iš balanso + + + + TransactionView + + + + All + Visi + + + + Today + Šiandien + + + + This week + Šią savaitę + + + + This month + Šį mėnesį + + + + Last month + Paskutinį mėnesį + + + + This year + Šiais metais + + + + Range... + Intervalas... + + + + Received with + Gauta su + + + + Sent to + Išsiųsta + + + + To yourself + Skirta sau + + + + Mined + Išgauta + + + + Other + Kita + + + + Enter address or label to search + Įveskite adresą ar žymę į paiešką + + + + Min amount + Minimali suma + + + + Copy address + Kopijuoti adresą + + + + Copy label + Kopijuoti žymę + + + + Copy amount + Kopijuoti sumą + + + + Copy transaction ID + + + + + Edit label + Taisyti žymę + + + + Show transaction details + Rodyti sandėrio detales + + + + Export Transaction Data + Sandorio duomenų eksportavimas + + + + Comma separated file (*.csv) + Kableliais atskirtų duomenų failas (*.csv) + + + + Confirmed + Patvirtintas + + + + Date + Data + + + + Type + Tipas + + + + Label + Žymė + + + + Address + Adresas + + + + Amount + Suma + + + + ID + ID + + + + Error exporting + Eksportavimo klaida + + + + Could not write to file %1. + Neįmanoma įrašyti į failą %1. + + + + Range: + Grupė: + + + + to + skirta + + + + WalletModel + + + Send Coins + Siųsti monetas + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + CryptoLottoToken versija + + + + Usage: + Naudojimas: + + + + Send command to -server or CryptoLottoTokend + Siųsti komandą serveriui arba CryptoLottoTokend + + + + List commands + Komandų sąrašas + + + + Get help for a command + Suteikti pagalba komandai + + + + Options: + Parinktys: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Nurodyti konfigūracijos failą (pagal nutylėjimąt: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + Nurodyti pid failą (pagal nutylėjimą: CryptoLottoTokend.pid) + + + + Specify data directory + Nustatyti duomenų aplanką + + + + Set database cache size in megabytes (default: 25) + + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Sujungimo klausymas prijungčiai <port> (pagal nutylėjimą: 22556 arba testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Palaikyti ne daugiau <n> jungčių kolegoms (pagal nutylėjimą: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + + + + + Specify your own public address + + + + + Threshold for disconnecting misbehaving peers (default: 100) + Atjungimo dėl netinkamo kolegų elgesio riba (pagal nutylėjimą: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Sekundžių kiekis eikiamas palaikyti ryšį dėl lygiarangių nestabilumo (pagal nutylėjimą: 86.400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Klausymas JSON-RPC sujungimui prijungčiai <port> (pagal nutylėjimą: 22555 or testnet: 44555) + + + + Accept command line and JSON-RPC commands + Priimti komandinę eilutę ir JSON-RPC komandas + + + + Run in the background as a daemon and accept commands + Dirbti fone kaip šešėlyje ir priimti komandas + + + + Use the test network + Naudoti testavimo tinklą + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Įspėjimas: -paytxfee yra nustatytas per didelis. Tai sandorio mokestis, kurį turėsite mokėti, jei siųsite sandorį. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Įspėjimas: Patikrinkite, kad kompiuterio data ir laikas yra teisingi.Jei Jūsų laikrodis neteisingai nustatytas CryptoLottoToken, veiks netinkamai. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + + + + + Connect only to the specified node(s) + Prisijungti tik prie nurodyto mazgo + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + Neteisingas tor adresas: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Maksimalus buferis priėmimo sujungimui <n>*1000 bitų (pagal nutylėjimą: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Maksimalus buferis siuntimo sujungimui <n>*1000 bitų (pagal nutylėjimą: 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + Išvesti papildomą derinimo informaciją. Numanomi visi kiti -debug* parametrai + + + + Output extra network debugging information + Išvesti papildomą tinklo derinimo informaciją + + + + Prepend debug output with timestamp (default: 1) + Prideėti laiko žymę derinimo rezultatams + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + SSL opcijos (žr.e CryptoLottoToken Wiki for SSL setup instructions) + + + + Select the version of socks proxy to use (4-5, default: 5) + + + + + Send trace/debug info to console instead of debug.log file + Siųsti atsekimo/derinimo info į konsolę vietoj debug.log failo + + + + Send trace/debug info to debugger + Siųsti sekimo/derinimo info derintojui + + + + Set maximum block size in bytes (default: 250000) + + + + + Set minimum block size in bytes (default: 0) + + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + Nustatyti sujungimo trukmę milisekundėmis (pagal nutylėjimą: 5000) + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + Bandymas naudoti UPnP struktūra klausymosi prievadui (default: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Bandymas naudoti UPnP struktūra klausymosi prievadui (default: 1 when listening) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + Vartotojo vardas JSON-RPC jungimuisi + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + Slaptažodis JSON-RPC sujungimams + + + + Allow JSON-RPC connections from specified IP address + Leisti JSON-RPC tik iš nurodytų IP adresų + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Siųsti komandą mazgui dirbančiam <ip> (pagal nutylėjimą: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + + + + + Upgrade wallet to latest format + Atnaujinti piniginę į naujausią formatą + + + + Set key pool size to <n> (default: 100) + Nustatyti rakto apimties dydį <n> (pagal nutylėjimą: 100) + + + + Rescan the block chain for missing wallet transactions + Ieškoti prarastų piniginės sandorių blokų grandinėje + + + + Use OpenSSL (https) for JSON-RPC connections + Naudoti OpenSSL (https) jungimuisi JSON-RPC + + + + Server certificate file (default: server.cert) + Serverio sertifikato failas (pagal nutylėjimą: server.cert) + + + + Server private key (default: server.pem) + Serverio privatus raktas (pagal nutylėjimą: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Priimtini šifrai (pagal nutylėjimą: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Pagelbos žinutė + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Nepavyko susieti šiame kompiuteryje prievado %s (bind returned error %d, %s) + + + + Connect through socks proxy + Jungtis per socks tarpinį serverį + + + + Allow DNS lookups for -addnode, -seednode and -connect + Leisti DNS paiešką sujungimui ir mazgo pridėjimui + + + + Loading addresses... + Užkraunami adresai... + + + + Error loading wallet.dat: Wallet corrupted + wallet.dat pakrovimo klaida, wallet.dat sugadintas + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + wallet.dat pakrovimo klaida, wallet.dat reikalauja naujasnės CryptoLottoToken versijos + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Piniginė turi būti prrašyta: įvykdymui perkraukite CryptoLottoToken + + + + Error loading wallet.dat + wallet.dat pakrovimo klaida + + + + Invalid -proxy address: '%s' + Neteisingas proxy adresas: '%s' + + + + Unknown network specified in -onlynet: '%s' + + + + + Unknown -socks proxy version requested: %i + + + + + Cannot resolve -bind address: '%s' + + + + + Cannot resolve -externalip address: '%s' + + + + + Invalid amount for -paytxfee=<amount>: '%s' + Neteisinga suma -paytxfee=<amount>: '%s' + + + + Invalid amount + Neteisinga suma + + + + Insufficient funds + Nepakanka lėšų + + + + Loading block index... + Įkeliamas blokų indeksas... + + + + Add a node to connect to and attempt to keep the connection open + Pridėti mazgą prie sujungti su and attempt to keep the connection open + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Nepavyko susieti šiame kompiuteryje prievado %s. CryptoLottoToken tikriausiai jau veikia. + + + + Fee per KB to add to transactions you send + Įtraukti mokestį už kB siunčiamiems sandoriams + + + + Loading wallet... + Užkraunama piniginė... + + + + Cannot downgrade wallet + + + + + Cannot write default address + + + + + Rescanning... + Peržiūra + + + + Done loading + Įkėlimas baigtas + + + + To use the %s option + + + + + Error + Klaida + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_lv_LV.ts b/src/qt/locale/bitcoin_lv_LV.ts new file mode 100644 index 0000000..ce7fed3 --- /dev/null +++ b/src/qt/locale/bitcoin_lv_LV.ts @@ -0,0 +1,2923 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + Par CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> versija + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + Adrešu grāmata + + + + Double-click to edit address or label + Adresi vai nosaukumu rediģē ar dubultklikšķi + + + + Create a new address + Izveidot jaunu adresi + + + + Copy the currently selected address to the system clipboard + Kopēt iezīmēto adresi uz starpliktuvi + + + + &New Address + &Jauna adrese + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + + + + + &Copy Address + &Kopēt adresi + + + + Show &QR Code + Parādīt &QR kodu + + + + Sign a message to prove you own a CryptoLottoToken address + + + + + Sign &Message + + + + + Delete the currently selected address from the list + + + + + Export the data in the current tab to a file + + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + + + + + &Verify Message + + + + + &Delete + &Dzēst + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + Kopēt &Nosaukumu + + + + &Edit + &Rediģēt + + + + Send &Coins + + + + + Export Address Book Data + Eksportēt adreses + + + + Comma separated file (*.csv) + Fails ar komatu kā atdalītāju (*.csv) + + + + Error exporting + Kļūda eksportējot + + + + Could not write to file %1. + Nevar ierakstīt failā %1. + + + + AddressTableModel + + + Label + Nosaukums + + + + Address + Adrese + + + + (no label) + (bez nosaukuma) + + + + AskPassphraseDialog + + + Passphrase Dialog + Paroles dialogs + + + + Enter passphrase + Ierakstiet paroli + + + + New passphrase + Jauna parole + + + + Repeat new passphrase + Jaunā parole vēlreiz + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Ierakstiet maciņa jauno paroli.<br/>Lūdzu izmantojiet <b>10 vai vairāk nejauši izvēlētas zīmes</b>, vai <b>astoņus un vairāk vārdus</b>. + + + + Encrypt wallet + Šifrēt maciņu + + + + This operation needs your wallet passphrase to unlock the wallet. + Lai veikto šo darbību, maciņš jāatslēdz ar paroli. + + + + Unlock wallet + Atslēgt maciņu + + + + This operation needs your wallet passphrase to decrypt the wallet. + Šai darbībai maciņš jāatšifrē ar maciņa paroli. + + + + Decrypt wallet + Atšifrēt maciņu + + + + Change passphrase + Mainīt paroli + + + + Enter the old and new passphrase to the wallet. + Ierakstiet maciņa veco un jauno paroli. + + + + Confirm wallet encryption + Apstiprināt maciņa šifrēšanu + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + + + + + Are you sure you wish to encrypt your wallet? + + + + + 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. + + + + + + Warning: The Caps Lock key is on! + + + + + + Wallet encrypted + Maciņš nošifrēts + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken aizvērsies, lai pabeigtu šifrēšanu. Atcerieties, ka maciņa šifrēšana nevar pilnībā novērst bitkoinu zādzību, ko veic datorā ieviesušās kaitīgas programmas. + + + + + + + Wallet encryption failed + Maciņa šifrēšana neizdevās + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Maciņa šifrēšana neizdevās programmas kļūdas dēļ. Jūsu maciņš netika šifrēts. + + + + + The supplied passphrases do not match. + Ievadītās paroles nav vienādas. + + + + Wallet unlock failed + Maciņu atšifrēt neizdevās + + + + + + The passphrase entered for the wallet decryption was incorrect. + Maciņa atšifrēšanai ievadītā parole nav pareiza. + + + + Wallet decryption failed + Maciņu neizdevās atšifrēt + + + + Wallet passphrase was successfully changed. + + + + + BitcoinGUI + + + Sign &message... + Parakstīt &ziņojumu... + + + + Synchronizing with network... + Sinhronizācija ar tīklu... + + + + &Overview + &Pārskats + + + + Show general overview of wallet + Rādīt vispārēju maciņa pārskatu + + + + &Transactions + &Transakcijas + + + + Browse transaction history + Skatīt transakciju vēsturi + + + + Edit the list of stored addresses and labels for sending + Rediģēt saglabātās adreses un nosaukumus + + + + Show the list of addresses for receiving payments + Rādīt maksājumu saņemšanas adreses + + + + E&xit + &Iziet + + + + Quit application + Aizvērt programmu + + + + Show information about CryptoLottoToken + Parādīt informāciju par CryptoLottoToken + + + + About &Qt + Par &Qt + + + + Show information about Qt + Parādīt informāciju par Qt + + + + &Options... + &Iespējas + + + + &Encrypt Wallet... + Š&ifrēt maciņu... + + + + &Backup Wallet... + &Izveidot maciņa rezerves kopiju + + + + &Change Passphrase... + &Mainīt paroli + + + + Importing blocks from disk... + + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + Nosūtīt bitkoinus uz CryptoLottoToken adresi + + + + Modify configuration options for CryptoLottoToken + Mainīt CryptoLottoToken konfigurācijas uzstādījumus + + + + Backup wallet to another location + Izveidot maciņa rezerves kopiju citur + + + + Change the passphrase used for wallet encryption + Mainīt maciņa šifrēšanas paroli + + + + &Debug window + &Debug logs + + + + Open debugging and diagnostic console + Atvērt atkļūdošanas un diagnostikas konsoli + + + + &Verify message... + &Pārbaudīt ziņojumu... + + + + + CryptoLottoToken + + + + + Wallet + Maciņš + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + + + + + &Show / Hide + + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + &Fails + + + + &Settings + &Uzstādījumi + + + + &Help + &Palīdzība + + + + Tabs toolbar + Ciļņu rīkjosla + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + CryptoLottoToken klients + + + + %n active connection(s) to CryptoLottoToken network + %n aktīvu savienojumu ar CryptoLottoToken tīklu%n aktīvs savienojums ar CryptoLottoToken tīklu%n aktīvu savienojumu as CryptoLottoToken tīklu + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + Kļūda + + + + Warning + Brīdinājums + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + Sinhronizēts + + + + Catching up... + Sinhronizējos... + + + + Confirm transaction fee + Apstiprināt transakcijas maksu + + + + Sent transaction + Transakcija nosūtīta + + + + Incoming transaction + Ienākoša transakcija + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Datums: %1 +Daudzums: %2 +Tips: %3 +Adrese: %4 + + + + + + URI handling + + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Maciņš ir <b>šifrēts</b> un pašlaik <b>atslēgts</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Maciņš ir <b>šifrēts</b> un pašlaik <b>slēgts</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + Tīkla brīdinājums + + + + EditAddressDialog + + + Edit Address + Mainīt adrese + + + + &Label + &Nosaukums + + + + The label associated with this address book entry + Adrešu grāmatas ieraksta nosaukums + + + + &Address + &Adrese + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Adrese adrešu grāmatas ierakstā. To var mainīt tikai nosūtīšanas adresēm. + + + + New receiving address + Jauna saņemšanas adrese + + + + New sending address + Jauna nosūtīšanas adrese + + + + Edit receiving address + Mainīt saņemšanas adresi + + + + Edit sending address + Mainīt nosūtīšanas adresi + + + + The entered address "%1" is already in the address book. + Nupat ierakstītā adrese "%1" jau atrodas adrešu grāmatā. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + Ierakstītā adrese "%1" nav derīga CryptoLottoToken adrese. + + + + Could not unlock wallet. + Nav iespējams atslēgt maciņu. + + + + New key generation failed. + Neizdevās ģenerēt jaunu atslēgu. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + versija + + + + Usage: + Lietojums: + + + + command-line options + komandrindas izvēles + + + + UI options + Lietotāja interfeisa izvēlnes + + + + Set language, for example "de_DE" (default: system locale) + Uzstādiet valodu, piemēram "de_DE" (pēc noklusēšanas: sistēmas lokāle) + + + + Start minimized + Sākt minimizētu + + + + Show splash screen on startup (default: 1) + Uzsākot, parādīt programmas informācijas logu (pēc noklusēšanas: 1) + + + + OptionsDialog + + + Options + Iespējas + + + + &Main + &Galvenais + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + &Maksāt par transakciju + + + + Automatically start CryptoLottoToken after logging in to the system. + Automātiski sākt CryptoLottoToken pēc pieteikšanās sistēmā. + + + + &Start CryptoLottoToken on system login + &Sākt CryptoLottoToken reizē ar sistēmu + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + &Tīkls + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Uz rūtera automātiski atvērt CryptoLottoToken klienta portu. Tas strādā tikai tad, ja rūteris atbalsta UPnP un tas ir ieslēgts. + + + + Map port using &UPnP + Kartēt portu, izmantojot &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + + + + + &Connect through SOCKS proxy: + &Savienoties caur SOCKS proxy: + + + + Proxy &IP: + Proxy &IP: + + + + IP address of the proxy (e.g. 127.0.0.1) + proxy IP adrese (piem. 127.0.0.1) + + + + &Port: + &Ports: + + + + Port of the proxy (e.g. 9050) + Proxy ports (piem. 9050) + + + + SOCKS &Version: + SOCKS &Versija: + + + + SOCKS version of the proxy (e.g. 5) + proxy SOCKS versija (piem. 5) + + + + &Window + &Logs + + + + Show only a tray icon after minimizing the window. + Pēc loga minimizācijas rādīt tikai ikonu sistēmas teknē. + + + + &Minimize to the tray instead of the taskbar + &Minimizēt uz sistēmas tekni, nevis rīkjoslu + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Logu aizverot, minimizēt, nevis beigt darbu. Kad šī izvēlne iespējota, programma aizvērsies tikai pēc Beigt komandas izvēlnē. + + + + M&inimize on close + M&inimizēt aizverot + + + + &Display + &Izskats + + + + User Interface &language: + Lietotāja interfeiss un &valoda: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + Šeit var iestatīt lietotāja valodu. Iestatījums aktivizēsies pēc CryptoLottoToken pārstartēšanas. + + + + &Unit to show amounts in: + &Vienības, kurās attēlot daudzumus: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Izvēlēties dalījuma vienību pēc noklusēšanas, ko izmantot interfeisā un nosūtot bitkoinus. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Rādīt vai nē CryptoLottoToken adreses transakciju sarakstā. + + + + &Display addresses in transaction list + &Attēlot adreses transakciju sarakstā + + + + &OK + &OK + + + + &Cancel + &Atcelt + + + + &Apply + &Pielietot + + + + default + pēc noklusēšanas + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + Brīdinājums + + + + + This setting will take effect after restarting CryptoLottoToken. + Iestatījums aktivizēsies pēc Bitkoin pārstartēšanas. + + + + The supplied proxy address is invalid. + Norādītā proxy adrese nav derīga. + + + + OverviewPage + + + Form + Forma + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + Attēlotā informācija var būt novecojusi. Jūsu maciņš pēc savienojuma izveides automātiski sinhronizējas ar CryptoLottoToken tīklu, taču šis process vēl nav beidzies. + + + + Balance: + Bilance: + + + + Unconfirmed: + Neapstiprinātas: + + + + Wallet + Maciņš + + + + Immature: + + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + <b>Pēdējās transakcijas</b> + + + + Your current balance + Jūsu tekošā bilance + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Kopējā apstiprināmo transakciju vērtība, vēl nav ieskaitīta kopējā bilancē + + + + + out of sync + nav sinhronizēts + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + QR koda dialogs + + + + Request Payment + Pieprasīt maksājumu + + + + Amount: + Daudzums: + + + + Label: + Nosaukums: + + + + Message: + Ziņojums: + + + + &Save As... + &Saglabāt kā... + + + + Error encoding URI into QR Code. + Kļūda kodējot URI QR kodā. + + + + The entered amount is invalid, please check. + + + + + Resulting URI too long, try to reduce the text for label / message. + Rezultāta URI pārāk garš, mēģiniet saīsināt nosaukumu vai ziņojumu. + + + + Save QR Code + Saglabāt QR kodu + + + + PNG Images (*.png) + PNG attēli (*.png) + + + + RPCConsole + + + Client name + Klienta vārds + + + + + + + + + + + + + N/A + N/A + + + + Client version + Klienta versija + + + + &Information + &Informācija + + + + Using OpenSSL version + + + + + Startup time + Sākuma laiks + + + + Network + Tīkls + + + + Number of connections + Savienojumu skaits + + + + On testnet + Testa tīklā + + + + Block chain + Bloku virkne + + + + Current number of blocks + Pašreizējais bloku skaits + + + + Estimated total blocks + Bloku skaita novērtējums + + + + Last block time + Pēdējā bloka laiks + + + + &Open + &Atvērt + + + + Command-line options + + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + + &Show + + + + + &Console + &Konsole + + + + Build date + Kompilācijas datums + + + + CryptoLottoToken - Debug window + + + + + CryptoLottoToken Core + + + + + Debug log file + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + Notīrīt konsoli + + + + Welcome to the CryptoLottoToken RPC console. + Laipni lūgti CryptoLottoToken RPC konsolē. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Izmantojiet bultiņas uz augšu un leju, lai pārvietotos pa vēsturi, un <b>Ctrl-L</b> ekrāna notīrīšanai. + + + + Type <b>help</b> for an overview of available commands. + Ierakstiet <b>help</b> lai iegūtu pieejamo komandu sarakstu. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Sūtīt bitkoinus + + + + Send to multiple recipients at once + Sūtīt vairākiem saņēmējiem uzreiz + + + + Add &Recipient + + + + + Remove all transaction fields + Dzēst visus transakcijas laukus + + + + Clear &All + &Notīrīt visu + + + + Balance: + Bilance: + + + + 123.456 BTC + 123,456 BTC + + + + Confirm the send action + Apstiprināt nosūtīšanu + + + + S&end + + + + + <b>%1</b> to %2 (%3) + <b>%1</b> līdz %2 (%3) + + + + Confirm send coins + Apstiprināt bitkoinu sūtīšanu + + + + Are you sure you want to send %1? + Vai tiešām vēlaties nosūtīt %1? + + + + and + un + + + + The recipient address is not valid, please recheck. + + + + + The amount to pay must be larger than 0. + Nosūtāmajai summai jābūt lielākai par 0. + + + + The amount exceeds your balance. + Daudzums pārsniedz pieejamo. + + + + The total exceeds your balance when the %1 transaction fee is included. + Kopsumma pārsniedz pieejamo, ja pieskaitīta %1 transakcijas maksa. + + + + Duplicate address found, can only send to each address once per send operation. + Atrastas divas vienādas adreses, vienā nosūtīšanas reizē uz katru adresi var sūtīt tikai vienreiz. + + + + Error: Transaction creation failed! + + + + + 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. + Kļūda: transakcija tika atteikta. Tā var gadīties, ja kāds no maciņā esošiem bitkoiniem jau iztērēts, piemēram, izmantojot wallet.dat kopiju, kurā nav atzīmēti iztērētie bitkoini. + + + + SendCoinsEntry + + + Form + Forma + + + + A&mount: + Apjo&ms + + + + Pay &To: + &Saņēmējs: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + Lai pievienotu adresi adrešu grāmatai, tai jādod nosaukums + + + + &Label: + &Nosaukums: + + + + Choose address from address book + Izvēlēties adresi no adrešu grāmatas + + + + Alt+A + Alt+A + + + + Paste address from clipboard + ielīmēt adresi no starpliktuves + + + + Alt+P + Alt+P + + + + Remove this recipient + Dzēst šo saņēmēju + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Ierakstiet CryptoLottoToken adresi (piem. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + + + + + &Sign Message + + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Choose an address from the address book + + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + ielīmēt adresi no starpliktuves + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + + + + + Signature + + + + + Copy the current signature to the system clipboard + + + + + Sign the message to prove you own this CryptoLottoToken address + + + + + Sign &Message + + + + + Reset all sign message fields + + + + + + Clear &All + &Notīrīt visu + + + + &Verify Message + + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + + + + + Verify &Message + + + + + Reset all verify message fields + + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Ierakstiet CryptoLottoToken adresi (piem. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + + + + + Enter CryptoLottoToken signature + + + + + + The entered address is invalid. + + + + + + + + Please check the address and try again. + + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + + + + + Private key for the entered address is not available. + + + + + Message signing failed. + + + + + Message signed. + + + + + The signature could not be decoded. + + + + + + Please check the signature and try again. + + + + + The signature did not match the message digest. + + + + + Message verification failed. + + + + + Message verified. + + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + + + + + TransactionDesc + + + Open until %1 + Atvērts līdz %1 + + + + %1/offline + + + + + %1/unconfirmed + %1/neapstiprinātas + + + + %1 confirmations + %1 apstiprinājumu + + + + Status + + + + + , broadcast through %n node(s) + + + + + Date + Datums + + + + Source + + + + + Generated + + + + + + From + + + + + + + To + + + + + + own address + + + + + label + + + + + + + + + Credit + + + + + matures in %n more block(s) + + + + + not accepted + + + + + + + + Debit + + + + + Transaction fee + + + + + Net amount + + + + + Message + + + + + Comment + + + + + Transaction ID + + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + + + + + Debug information + + + + + Transaction + + + + + Inputs + + + + + Amount + Daudzums + + + + true + + + + + false + + + + + , has not been successfully broadcast yet + , vēl nav veiksmīgi izziņots + + + + Open for %n more block(s) + + + + + unknown + nav zināms + + + + TransactionDescDialog + + + Transaction details + Transakcijas detaļas + + + + This pane shows a detailed description of the transaction + Šis panelis parāda transakcijas detaļas + + + + TransactionTableModel + + + Date + Datums + + + + Type + Tips + + + + Address + Adrese + + + + Amount + Daudzums + + + + Open for %n more block(s) + + + + + Open until %1 + Atvērts līdz %1 + + + + Offline (%1 confirmations) + Nav pieslēgts (%1 apstiprinājumu) + + + + Unconfirmed (%1 of %2 confirmations) + Nav apstiprināts (%1 no %2 apstiprinājumu) + + + + Confirmed (%1 confirmations) + Apstiprināts (%1 apstiprinājumu) + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + Neviens cits mezgls šo bloku nav saņēmis un droši vien netiks akceptēts! + + + + Generated but not accepted + Ģenerēts, taču nav akceptēts + + + + Received with + Saņemts ar + + + + Received from + Saņemts no + + + + Sent to + Nosūtīts + + + + Payment to yourself + Maksājums sev + + + + Mined + Atrasts + + + + (n/a) + (nav pieejams) + + + + Transaction status. Hover over this field to show number of confirmations. + Transakcijas statuss. Turiet peli virs šī lauka, lai redzētu apstiprinājumu skaitu. + + + + Date and time that the transaction was received. + Transakcijas saņemšanas datums un laiks. + + + + Type of transaction. + Transakcijas tips. + + + + Destination address of transaction. + Transakcijas mērķa adrese. + + + + Amount removed from or added to balance. + Bilancei pievienotais vai atņemtais daudzums. + + + + TransactionView + + + + All + Visi + + + + Today + Šodien + + + + This week + Šonedēļ + + + + This month + Šomēnes + + + + Last month + Pēdējais mēnesis + + + + This year + Šogad + + + + Range... + Diapazons... + + + + Received with + Saņemts ar + + + + Sent to + Nosūtīts + + + + To yourself + Sev + + + + Mined + Atrasts + + + + Other + Cits + + + + Enter address or label to search + Ierakstiet meklējamo nosaukumu vai adresi + + + + Min amount + Minimālais daudzums + + + + Copy address + Kopēt adresi + + + + Copy label + Kopēt nosaukumu + + + + Copy amount + Kopēt daudzumu + + + + Copy transaction ID + + + + + Edit label + Mainīt nosaukumu + + + + Show transaction details + Rādīt transakcijas detaļas + + + + Export Transaction Data + Eksportēt transakcijas datus + + + + Comma separated file (*.csv) + Fails ar komatu kā atdalītāju (*.csv) + + + + Confirmed + Apstiprināts + + + + Date + Datums + + + + Type + Tips + + + + Label + Nosaukums + + + + Address + Adrese + + + + Amount + Daudzums + + + + ID + ID + + + + Error exporting + Eksportēšanas kļūda + + + + Could not write to file %1. + Nevar ierakstīt failā %1. + + + + Range: + Diapazons: + + + + to + + + + + WalletModel + + + Send Coins + + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + + + + + Backup Wallet + Izveidot maciņa rezerves kopiju + + + + Wallet Data (*.dat) + Maciņa dati (*.dat) + + + + Backup Failed + Rezerves kopēšana neizdevās + + + + There was an error trying to save the wallet data to the new location. + Kļūda, saglabājot maciņu jaunajā vietā. + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + CryptoLottoToken versija + + + + Usage: + Lietojums: + + + + Send command to -server or CryptoLottoTokend + Nosūtīt komantu uz -server vai CryptoLottoTokend + + + + List commands + Komandu saraksts + + + + Get help for a command + Palīdzība par komandu + + + + Options: + Iespējas: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Norādiet konfigurācijas failu (pēc noklusēšanas: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + Norādiet pid failu (pēc noklusēšanas: CryptoLottoTokend.pid) + + + + Specify data directory + Norādiet datu direktoriju + + + + Set database cache size in megabytes (default: 25) + Uzstādiet datu bāzes bufera izmēru megabaitos (pēc noklusēšanas: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Gaidīt savienojumus portā <port> (pēc noklusēšanas: 22556 vai testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Uzturēt līdz <n> savienojumiem ar citiem mezgliem(pēc noklusēšanas: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Pievienoties mezglam, lai iegūtu citu mezglu adreses, un atvienoties + + + + Specify your own public address + Norādiet savu publisko adresi + + + + Threshold for disconnecting misbehaving peers (default: 100) + Slieksnis pārkāpējmezglu atvienošanai (pēc noklusēšanas: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Sekundes, cik ilgi atturēt pārkāpējmezglus no atkārtotas pievienošanās (pēc noklusēšanas: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + + + + + Accept command line and JSON-RPC commands + Pieņemt komandrindas un JSON-RPC komandas + + + + Run in the background as a daemon and accept commands + Darbināt fonā kā servisu un pieņemt komandas + + + + Use the test network + Izmantot testa tīklu + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + + + + + Connect only to the specified node(s) + + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + Debug izvadei sākumā pievienot laika zīmogu + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + + + + + Select the version of socks proxy to use (4-5, default: 5) + + + + + Send trace/debug info to console instead of debug.log file + Debug/trace informāciju izvadīt konsolē, nevis debug.log failā + + + + Send trace/debug info to debugger + Debug/trace informāciju izvadīt debug programmai + + + + Set maximum block size in bytes (default: 250000) + + + + + Set minimum block size in bytes (default: 0) + + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + + + + + Use UPnP to map the listening port (default: 1 when listening) + + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + JSON-RPC savienojumu lietotājvārds + + + + Warning + Brīdinājums + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + JSON-RPC savienojumu parole + + + + Allow JSON-RPC connections from specified IP address + Atļaut JSON-RPC savienojumus no norādītās IP adreses + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Nosūtīt komandas mezglam, kas darbojas adresē <ip> (pēc noklusēšanas: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Izpildīt komandu, kad labāk atbilstošais bloks izmainās (%s cmd aizvieto ar bloka hešu) + + + + Upgrade wallet to latest format + Atjaunot maciņa formātu uz jaunāko + + + + Set key pool size to <n> (default: 100) + Uzstādīt atslēgu bufera izmēru uz <n> (pēc noklusēšanas: 100) + + + + Rescan the block chain for missing wallet transactions + Atkārtoti skanēt bloku virkni, meklējot trūkstošās maciņa transakcijas + + + + Use OpenSSL (https) for JSON-RPC connections + JSON-RPC savienojumiem izmantot OpenSSL (https) + + + + Server certificate file (default: server.cert) + Servera sertifikāta fails (pēc noklusēšanas: server.cert) + + + + Server private key (default: server.pem) + Servera privātā atslēga (pēc noklusēšanas: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Pieņemamie šifri (pēc noklusēšanas: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Šis palīdzības paziņojums + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Nevar pievienoties pie %s šajā datorā (pievienošanās atgrieza kļūdu %d, %s) + + + + Connect through socks proxy + Savienoties caurs socks proxy + + + + Allow DNS lookups for -addnode, -seednode and -connect + Atļaut DNS uzmeklēšanu priekš -addnode, -seednode un -connect + + + + Loading addresses... + Ielādē adreses... + + + + Error loading wallet.dat: Wallet corrupted + Nevar ielādēt wallet.dat: maciņš bojāts + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Nevar ielādēt wallet.dat: maciņa atvēršanai nepieciešama jaunāka CryptoLottoToken versija + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Bija nepieciešams pārstartēt maciņu: pabeigšanai pārstartējiet CryptoLottoToken + + + + Error loading wallet.dat + Kļūda ielādējot wallet.dat + + + + Invalid -proxy address: '%s' + Nederīga -proxy adrese: '%s' + + + + Unknown network specified in -onlynet: '%s' + -onlynet komandā norādīts nepazīstams tīkls: '%s' + + + + Unknown -socks proxy version requested: %i + Pieprasīta nezināma -socks proxy versija: %i + + + + Cannot resolve -bind address: '%s' + Nevar uzmeklēt -bind adresi: '%s' + + + + Cannot resolve -externalip address: '%s' + Nevar atrisināt -externalip adresi: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + Nederīgs daudzums priekš -paytxfree=<amount>: '%s' + + + + Invalid amount + Nederīgs daudzums + + + + Insufficient funds + Nepietiek bitkoinu + + + + Loading block index... + Ielādē bloku indeksu... + + + + Add a node to connect to and attempt to keep the connection open + Pievienot mezglu, kam pievienoties un turēt savienojumu atvērtu + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Nevar pievienoties %s uz šī datora. CryptoLottoToken droši vien jau darbojas. + + + + Fee per KB to add to transactions you send + Maksa par KB, ko pievienot nosūtāmajām transakcijām + + + + Loading wallet... + Ielādē maciņu... + + + + Cannot downgrade wallet + Nevar maciņa formātu padarīt vecāku + + + + Cannot write default address + Nevar ierakstīt adresi pēc noklusēšanas + + + + Rescanning... + Skanēju no jauna... + + + + Done loading + Ielāde pabeigta + + + + To use the %s option + Izmantot opciju %s + + + + Error + Kļūda + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + Konfigurācijas failā jāuzstāda rpcpassword=<password>: +%s +Ja fails neeksistē, izveidojiet to ar atļauju lasīšanai tikai īpašniekam. + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_nb.qm b/src/qt/locale/bitcoin_nb.qm new file mode 100644 index 0000000..cfb9887 Binary files /dev/null and b/src/qt/locale/bitcoin_nb.qm differ diff --git a/src/qt/locale/bitcoin_nb.ts b/src/qt/locale/bitcoin_nb.ts new file mode 100644 index 0000000..80eebf1 --- /dev/null +++ b/src/qt/locale/bitcoin_nb.ts @@ -0,0 +1,2938 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + Om CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> versjon + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +Dette er eksperimentell programvare. + +Distribuert under MIT/X11 programvarelisensen, se medfølgende fil COPYING eller http://www.opensource.org/licenses/mit-license.php. + +Dette produktet inneholder programvare utviklet av OpenSSL prosjektet for bruk i OpenSSL Toolkit (http://www.openssl.org/) og kryptografisk programvare skrevet av Eric Young (eay@cryptsoft.com) og UPnP programvare skrevet av Thomas Bernard. + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + Adressebok + + + + Double-click to edit address or label + Dobbeltklikk for å redigere adresse eller merkelapp + + + + Create a new address + Lag en ny adresse + + + + Copy the currently selected address to the system clipboard + Kopier den valgte adressen til systemets utklippstavle + + + + &New Address + &Ny Adresse + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Dette er dine CryptoLottoToken-adresser for mottak av betalinger. Du kan gi forskjellige adresser til alle som skal betale deg for å holde bedre oversikt. + + + + &Copy Address + &Kopier Adresse + + + + Show &QR Code + Vis &QR Kode + + + + Sign a message to prove you own a CryptoLottoToken address + Signer en melding for å bevise at du eier en CryptoLottoToken-adresse + + + + Sign &Message + Signér &Melding + + + + Delete the currently selected address from the list + Slett den valgte adressen fra listen. + + + + Export the data in the current tab to a file + Eksporter data fra nåværende fane til fil + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Verifiser en melding for å være sikker på at den ble signert av en angitt CryptoLottoToken-adresse + + + + &Verify Message + &Verifiser Melding + + + + &Delete + &Slett + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + Kopier &Merkelapp + + + + &Edit + &Rediger + + + + Send &Coins + Send &Coins + + + + Export Address Book Data + Eksporter adressebok + + + + Comma separated file (*.csv) + Kommaseparert fil (*.csv) + + + + Error exporting + Feil ved eksportering + + + + Could not write to file %1. + Kunne ikke skrive til filen %1. + + + + AddressTableModel + + + Label + Merkelapp + + + + Address + Adresse + + + + (no label) + (ingen merkelapp) + + + + AskPassphraseDialog + + + Passphrase Dialog + Dialog for Adgangsfrase + + + + Enter passphrase + Angi adgangsfrase + + + + New passphrase + Ny adgangsfrase + + + + Repeat new passphrase + Gjenta ny adgangsfrase + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Skriv inn den nye adgangsfrasen for lommeboken.<br/>Vennligst bruk en adgangsfrase med <b>10 eller flere tilfeldige tegn</b>, eller <b>åtte eller flere ord</b>. + + + + Encrypt wallet + Krypter lommebok + + + + This operation needs your wallet passphrase to unlock the wallet. + Denne operasjonen krever adgangsfrasen til lommeboken for å låse den opp. + + + + Unlock wallet + Lås opp lommebok + + + + This operation needs your wallet passphrase to decrypt the wallet. + Denne operasjonen krever adgangsfrasen til lommeboken for å dekryptere den. + + + + Decrypt wallet + Dekrypter lommebok + + + + Change passphrase + Endre adgangsfrase + + + + Enter the old and new passphrase to the wallet. + Skriv inn gammel og ny adgangsfrase for lommeboken. + + + + Confirm wallet encryption + Bekreft kryptering av lommebok + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Advarsel: Hvis du krypterer lommeboken og mister adgangsfrasen, så vil du <b>MISTE ALLE DINE CryptoLottoTokenS</b>! + + + + Are you sure you wish to encrypt your wallet? + Er du sikker på at du vil kryptere lommeboken? + + + + 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. + VIKTIG: Tidligere sikkerhetskopier av din lommebok-fil, bør erstattes med den nylig genererte, krypterte filen, da de blir ugyldiggjort av sikkerhetshensyn så snart du begynner å bruke den nye krypterte lommeboken. + + + + + Warning: The Caps Lock key is on! + Advarsel: Caps Lock er på ! + + + + + Wallet encrypted + Lommebok kryptert + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken vil nå lukkes for å fullføre krypteringsprosessen. Husk at kryptering av lommeboken ikke fullt ut kan beskytte dine CryptoLottoTokens fra å bli stjålet om skadevare infiserer datamaskinen. + + + + + + + Wallet encryption failed + Kryptering av lommebok feilet + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Kryptering av lommebok feilet på grunn av en intern feil. Din lommebok ble ikke kryptert. + + + + + The supplied passphrases do not match. + De angitte adgangsfrasene er ulike. + + + + Wallet unlock failed + Opplåsing av lommebok feilet + + + + + + The passphrase entered for the wallet decryption was incorrect. + Adgangsfrasen angitt for dekryptering av lommeboken var feil. + + + + Wallet decryption failed + Dekryptering av lommebok feilet + + + + Wallet passphrase was successfully changed. + Adgangsfrase for lommebok endret. + + + + BitcoinGUI + + + Sign &message... + Signer &melding... + + + + Synchronizing with network... + Synkroniserer med nettverk... + + + + &Overview + &Oversikt + + + + Show general overview of wallet + Vis generell oversikt over lommeboken + + + + &Transactions + &Transaksjoner + + + + Browse transaction history + Vis transaksjonshistorikk + + + + Edit the list of stored addresses and labels for sending + Rediger listen over adresser og deres merkelapper + + + + Show the list of addresses for receiving payments + Vis listen over adresser for mottak av betalinger + + + + E&xit + &Avslutt + + + + Quit application + Avslutt applikasjonen + + + + Show information about CryptoLottoToken + Vis informasjon om CryptoLottoToken + + + + About &Qt + Om &Qt + + + + Show information about Qt + Vis informasjon om Qt + + + + &Options... + &Innstillinger... + + + + &Encrypt Wallet... + &Krypter Lommebok... + + + + &Backup Wallet... + Lag &Sikkerhetskopi av Lommebok... + + + + &Change Passphrase... + &Endre Adgangsfrase... + + + + Importing blocks from disk... + Importere blokker... + + + + Reindexing blocks on disk... + Re-indekserer blokker på disk... + + + + Send coins to a CryptoLottoToken address + Send til en CryptoLottoToken-adresse + + + + Modify configuration options for CryptoLottoToken + Endre oppsett for CryptoLottoToken + + + + Backup wallet to another location + Sikkerhetskopiér lommebok til annet sted + + + + Change the passphrase used for wallet encryption + Endre adgangsfrasen brukt for kryptering av lommebok + + + + &Debug window + &Feilsøkingsvindu + + + + Open debugging and diagnostic console + Åpne konsoll for feilsøk og diagnostikk + + + + &Verify message... + &Verifiser melding... + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Lommebok + + + + &Send + &Send + + + + &Receive + &Motta + + + + &Addresses + &Adressebok + + + + &About CryptoLottoToken + &Om CryptoLottoToken + + + + &Show / Hide + &Gjem / vis + + + + Show or hide the main Window + Vis eller skjul hovedvinduet + + + + Encrypt the private keys that belong to your wallet + Krypter de private nøklene som tilhører lommeboken din + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Signér en melding for å bevise at du eier denne adressen + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Bekreft meldinger for å være sikker på at de ble signert av en angitt CryptoLottoToken-adresse + + + + &File + &Fil + + + + &Settings + &Innstillinger + + + + &Help + &Hjelp + + + + Tabs toolbar + Verktøylinje for faner + + + + + [testnet] + [testnett] + + + + CryptoLottoToken client + CryptoLottoTokenklient + + + + %n active connection(s) to CryptoLottoToken network + %n aktiv forbindelse til CryptoLottoToken-nettverket%n aktive forbindelser til CryptoLottoToken-nettverket + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + Lastet %1 blokker med transaksjonshistorikk. + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + Transaksjoner etter dette vil ikke være synlige enda. + + + + Error + + + + + Warning + + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + Denne transaksjonen overstiger størrelsesbegrensningen. Du kan likevel sende den med et gebyr på %1, som går til nodene som prosesserer transaksjonen din og støtter nettverket. Vil du betale gebyret? + + + + Up to date + Ajour + + + + Catching up... + Kommer ajour... + + + + Confirm transaction fee + Bekreft transaksjonsgebyr + + + + Sent transaction + Sendt transaksjon + + + + Incoming transaction + Innkommende transaksjon + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Dato: %1 +Beløp: %2 +Type: %3 +Adresse: %4 + + + + + + URI handling + URI håndtering + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + URI kunne ikke tolkes! Dette kan forårsakes av en ugyldig CryptoLottoToken-adresse eller feil i URI-parametere. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Lommeboken er <b>kryptert</b> og for tiden <b>ulåst</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Lommeboken er <b>kryptert</b> og for tiden <b>låst</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + En fatal feil har inntruffet. Det er ikke trygt å fortsette og CryptoLottoToken må derfor avslutte. + + + + ClientModel + + + Network Alert + Nettverksvarsel + + + + EditAddressDialog + + + Edit Address + Rediger adresse + + + + &Label + &Merkelapp + + + + The label associated with this address book entry + Merkelappen koblet til denne adressen i adresseboken + + + + &Address + &Adresse + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Adressen til denne oppføringen i adresseboken. Denne kan kun endres for utsendingsadresser. + + + + New receiving address + Ny mottaksadresse + + + + New sending address + Ny utsendingsadresse + + + + Edit receiving address + Rediger mottaksadresse + + + + Edit sending address + Rediger utsendingsadresse + + + + The entered address "%1" is already in the address book. + Den oppgitte adressen "%1" er allerede i adresseboken. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + Den angitte adressed "%1" er ikke en gyldig CryptoLottoToken-adresse. + + + + Could not unlock wallet. + Kunne ikke låse opp lommeboken. + + + + New key generation failed. + Generering av ny nøkkel feilet. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + versjon + + + + Usage: + Bruk: + + + + command-line options + kommandolinjevalg + + + + UI options + valg i brukergrensesnitt + + + + Set language, for example "de_DE" (default: system locale) + Sett språk, for eksempel "nb_NO" (standardverdi: fra operativsystem) + + + + Start minimized + Start minimert + + + + + Show splash screen on startup (default: 1) + Vis splashskjerm ved oppstart (standardverdi: 1) + + + + OptionsDialog + + + Options + Innstillinger + + + + &Main + &Hoved + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + Betal transaksjons&gebyr + + + + Automatically start CryptoLottoToken after logging in to the system. + Start CryptoLottoToken automatisk etter innlogging. + + + + &Start CryptoLottoToken on system login + &Start CryptoLottoToken ved systeminnlogging + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + &Nettverk + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Åpne automatisk CryptoLottoToken klientporten på ruteren. Dette virker kun om din ruter støtter UPnP og dette er påslått. + + + + Map port using &UPnP + Sett opp port vha. &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Koble til CryptoLottoToken-nettverket gjennom en SOCKS proxy (f.eks. ved tilkobling gjennom Tor). + + + + &Connect through SOCKS proxy: + &Koble til gjenom SOCKS proxy: + + + + Proxy &IP: + Proxy &IP: + + + + IP address of the proxy (e.g. 127.0.0.1) + IP-adresse for mellomtjener (f.eks. 127.0.0.1) + + + + &Port: + &Port: + + + + Port of the proxy (e.g. 9050) + Proxyens port (f.eks. 9050) + + + + SOCKS &Version: + SOCKS &Versjon: + + + + SOCKS version of the proxy (e.g. 5) + Proxyens SOCKS versjon (f.eks. 5) + + + + &Window + &Vindu + + + + Show only a tray icon after minimizing the window. + Vis kun ikon i systemkurv etter minimering av vinduet. + + + + &Minimize to the tray instead of the taskbar + &Minimer til systemkurv istedenfor oppgavelinjen + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Minimerer vinduet istedenfor å avslutte applikasjonen når vinduet lukkes. Når dette er slått på avsluttes applikasjonen kun ved å velge avslutt i menyen. + + + + M&inimize on close + M&inimer ved lukking + + + + &Display + &Visning + + + + User Interface &language: + &Språk for brukergrensesnitt + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + Språket for brukergrensesnittet kan settes her. Innstillingen trer i kraft ved omstart av CryptoLottoToken. + + + + &Unit to show amounts in: + &Enhet for visning av beløper: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Velg standard delt enhet for visning i grensesnittet og for sending av CryptoLottoTokens. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Om CryptoLottoToken-adresser skal vises i transaksjonslisten eller ikke. + + + + &Display addresses in transaction list + &Vis adresser i transaksjonslisten + + + + &OK + &OK + + + + &Cancel + &Avbryt + + + + &Apply + &Bruk + + + + default + standardverdi + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + Advarsel + + + + + This setting will take effect after restarting CryptoLottoToken. + Denne innstillingen trer i kraft etter omstart av CryptoLottoToken. + + + + The supplied proxy address is invalid. + Angitt proxyadresse er ugyldig. + + + + OverviewPage + + + Form + Skjema + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + Informasjonen som vises kan være foreldet. Din lommebok synkroniseres automatisk med CryptoLottoToken-nettverket etter at tilkobling er opprettet, men denne prosessen er ikke ferdig enda. + + + + Balance: + Saldo: + + + + Unconfirmed: + Ubekreftet + + + + Wallet + Lommebok + + + + Immature: + Umoden: + + + + Mined balance that has not yet matured + Minet saldo har ikke modnet enda + + + + <b>Recent transactions</b> + <b>Siste transaksjoner</b> + + + + Your current balance + Din nåværende saldo + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Totalt antall ubekreftede transaksjoner som ikke telles med i saldo enda + + + + + out of sync + ute av synk + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + Kan ikke starte CryptoLottoToken: klikk-og-betal håndterer + + + + QRCodeDialog + + + QR Code Dialog + Dialog for QR Kode + + + + Request Payment + Etterspør Betaling + + + + Amount: + Beløp: + + + + Label: + Merkelapp: + + + + Message: + Melding: + + + + &Save As... + &Lagre Som... + + + + Error encoding URI into QR Code. + Feil ved koding av URI i QR kode. + + + + The entered amount is invalid, please check. + Angitt beløp er ugyldig. + + + + Resulting URI too long, try to reduce the text for label / message. + Resulterende URI for lang, prøv å redusere teksten for merkelapp / melding. + + + + Save QR Code + Lagre QR Kode + + + + PNG Images (*.png) + PNG bilder (*.png) + + + + RPCConsole + + + Client name + Klientnavn + + + + + + + + + + + + + N/A + - + + + + Client version + Klientversjon + + + + &Information + &Informasjon + + + + Using OpenSSL version + Bruker OpenSSL versjon + + + + Startup time + Oppstartstidspunkt + + + + Network + Nettverk + + + + Number of connections + Antall tilkoblinger + + + + On testnet + På testnett + + + + Block chain + Blokkjeden + + + + Current number of blocks + Nåværende antall blokker + + + + Estimated total blocks + Estimert totalt antall blokker + + + + Last block time + Tidspunkt for siste blokk + + + + &Open + &Åpne + + + + Command-line options + Kommandolinjevalg + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Vis CryptoLottoToken-Qt hjelpemelding for å få en liste med mulige kommandolinjevalg. + + + + &Show + &Vis + + + + &Console + &Konsoll + + + + Build date + Byggedato + + + + CryptoLottoToken - Debug window + CryptoLottoToken - vindu for feilsøk + + + + CryptoLottoToken Core + CryptoLottoToken Kjerne + + + + Debug log file + Loggfil for feilsøk + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + Åpne CryptoLottoToken loggfil for feilsøk fra datamappen. Dette kan ta noen sekunder for store loggfiler. + + + + Clear console + Tøm konsoll + + + + Welcome to the CryptoLottoToken RPC console. + Velkommen til CryptoLottoToken RPC konsoll. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Bruk opp og ned pil for å navigere historikken, og <b>Ctrl-L</b> for å tømme skjermen. + + + + Type <b>help</b> for an overview of available commands. + Skriv <b>help</b> for en oversikt over kommandoer. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Send CryptoLottoTokens + + + + Send to multiple recipients at once + Send til flere enn én mottaker + + + + Add &Recipient + &Legg til Mottaker + + + + Remove all transaction fields + Fjern alle transaksjonsfelter + + + + Clear &All + Fjern &Alt + + + + Balance: + Saldo: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Bekreft sending + + + + S&end + S&end + + + + <b>%1</b> to %2 (%3) + <b>%1</b> til %2 (%3) + + + + Confirm send coins + Bekreft sending av CryptoLottoTokens + + + + Are you sure you want to send %1? + Er du sikker på at du vil sende %1? + + + + and + og + + + + The recipient address is not valid, please recheck. + Adresse for mottaker er ugyldig. + + + + The amount to pay must be larger than 0. + Beløpen som skal betales må være over 0. + + + + The amount exceeds your balance. + Beløpet overstiger saldo. + + + + The total exceeds your balance when the %1 transaction fee is included. + Totalbeløpet overstiger saldo etter at %1 transaksjonsgebyr er lagt til. + + + + Duplicate address found, can only send to each address once per send operation. + Duplikate adresser funnet. Kan bare sende én gang til hver adresse per operasjon. + + + + Error: Transaction creation failed! + Feil: Opprettelse av transaksjon feilet + + + + 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. + Feil: Transaksjonen ble avvist. Dette kan skje om noe av beløpet allerede var brukt, f.eks. hvis du kopierte wallet.dat og noen CryptoLottoTokens ble brukt i kopien men ikke ble markert som brukt her. + + + + SendCoinsEntry + + + Form + Skjema + + + + A&mount: + &Beløp: + + + + Pay &To: + Betal &Til: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Adressen betalingen skal sendes til (f.eks. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + Skriv inn en merkelapp for denne adressen for å legge den til i din adressebok + + + + &Label: + &Merkelapp: + + + + Choose address from address book + Velg adresse fra adresseboken + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Lim inn adresse fra utklippstavlen + + + + Alt+P + Alt+P + + + + Remove this recipient + Fjern denne mottakeren + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Skriv inn en CryptoLottoToken adresse (f.eks. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Signaturer - Signer / Verifiser en melding + + + + &Sign Message + &Signér Melding + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Du kan signere meldinger med dine adresser for å bevise at du eier dem. Ikke signér vage meldinger da phishing-angrep kan prøve å lure deg til å signere din identitet over til andre. Signér kun fullt detaljerte utsagn som du er enig i. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Adressen for signering av meldingen (f.eks. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Velg en adresse fra adresseboken + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Lim inn adresse fra utklippstavlen + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Skriv inn meldingen du vil signere her + + + + Signature + + + + + Copy the current signature to the system clipboard + Kopier valgt signatur til utklippstavle + + + + Sign the message to prove you own this CryptoLottoToken address + Signer meldingen for å bevise at du eier denne CryptoLottoToken-adressen + + + + Sign &Message + + + + + Reset all sign message fields + Tilbakestill alle felter for meldingssignering + + + + + Clear &All + Fjern &Alt + + + + &Verify Message + &Verifiser Melding + + + + Enter the signing 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. + Angi adresse for signering, melding (vær sikker på at du kopierer linjeskift, mellomrom, tab, etc. helt nøyaktig) og signatur under for å verifisere meldingen. Vær forsiktig med at du ikke gir signaturen mer betydning enn det som faktisk står i meldingen, for å unngå å bli lurt av såkalte "man-in-the-middle" angrep. + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Adressen meldingen var signert med (f.eks. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Verifiser meldingen for å være sikker på at den ble signert av den angitte CryptoLottoToken-adressen + + + + Verify &Message + + + + + Reset all verify message fields + Tilbakestill alle felter for meldingsverifikasjon + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Skriv inn en CryptoLottoToken adresse (f.eks. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Klikk "Signer Melding" for å generere signatur + + + + Enter CryptoLottoToken signature + Angi CryptoLottoToken signatur + + + + + The entered address is invalid. + Angitt adresse er ugyldig. + + + + + + + Please check the address and try again. + Vennligst sjekk adressen og prøv igjen. + + + + + The entered address does not refer to a key. + Angitt adresse refererer ikke til en nøkkel. + + + + Wallet unlock was cancelled. + Opplåsing av lommebok ble avbrutt. + + + + Private key for the entered address is not available. + Privat nøkkel for den angitte adressen er ikke tilgjengelig. + + + + Message signing failed. + Signering av melding feilet. + + + + Message signed. + Melding signert. + + + + The signature could not be decoded. + Signaturen kunne ikke dekodes. + + + + + Please check the signature and try again. + Vennligst sjekk signaturen og prøv igjen. + + + + The signature did not match the message digest. + Signaturen passer ikke til meldingen. + + + + Message verification failed. + Verifikasjon av melding feilet. + + + + Message verified. + Melding verifisert. + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + [testnett] + + + + TransactionDesc + + + Open until %1 + Åpen til %1 + + + + %1/offline + %1/frakoblet + + + + %1/unconfirmed + %1/ubekreftet + + + + %1 confirmations + %1 bekreftelser + + + + Status + Status + + + + , broadcast through %n node(s) + , kringkast gjennom %n node, kringkast gjennom %n noder + + + + Date + Dato + + + + Source + Kilde + + + + Generated + Generert + + + + + From + Fra + + + + + + To + Til + + + + + own address + egen adresse + + + + label + merkelapp + + + + + + + + Credit + Kredit + + + + matures in %n more block(s) + blir moden om %n blokkblir moden om %n blokker + + + + not accepted + ikke akseptert + + + + + + + Debit + Debet + + + + Transaction fee + Transaksjonsgebyr + + + + Net amount + Nettobeløp + + + + Message + Melding + + + + Comment + Kommentar + + + + Transaction ID + Transaksjons-ID + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Genererte CryptoLottoTokens må modnes 120 blokker før de kan brukes. Da du genererte denne blokken ble den kringkastet til nettverket for å legges til i blokkjeden. Hvis den ikke kommer inn i kjeden får den tilstanden "ikke akseptert" og vil ikke kunne brukes. Dette skjer noen ganger hvis en annen node genererer en blokk noen sekunder fra din. + + + + Debug information + Informasjon for feilsøk + + + + Transaction + Transaksjon + + + + Inputs + Inndata + + + + Amount + Beløp + + + + true + sann + + + + false + usann + + + + , has not been successfully broadcast yet + , har ikke blitt kringkastet uten problemer enda. + + + + Open for %n more block(s) + + + + + unknown + ukjent + + + + TransactionDescDialog + + + Transaction details + Transaksjonsdetaljer + + + + This pane shows a detailed description of the transaction + Her vises en detaljert beskrivelse av transaksjonen + + + + TransactionTableModel + + + Date + Dato + + + + Type + Type + + + + Address + Adresse + + + + Amount + Beløp + + + + Open for %n more block(s) + + + + + Open until %1 + Åpen til %1 + + + + Offline (%1 confirmations) + Frakoblet (%1 bekreftelser) + + + + Unconfirmed (%1 of %2 confirmations) + Ubekreftet (%1 av %2 bekreftelser) + + + + Confirmed (%1 confirmations) + Bekreftet (%1 bekreftelser) + + + + Mined balance will be available when it matures in %n more block(s) + Minet saldo blir tilgjengelig når den modner om %n blokkMinet saldo blir tilgjengelig når den modner om %n blokker + + + + This block was not received by any other nodes and will probably not be accepted! + Denne blokken har ikke blitt mottatt av noen andre noder og vil sannsynligvis ikke bli akseptert! + + + + Generated but not accepted + Generert men ikke akseptert + + + + Received with + Mottatt med + + + + Received from + Mottatt fra + + + + Sent to + Sendt til + + + + Payment to yourself + Betaling til deg selv + + + + Mined + Utvunnet + + + + (n/a) + - + + + + Transaction status. Hover over this field to show number of confirmations. + Transaksjonsstatus. Hold muspekeren over dette feltet for å se antall bekreftelser. + + + + Date and time that the transaction was received. + Dato og tid for da transaksjonen ble mottat. + + + + Type of transaction. + Type transaksjon. + + + + Destination address of transaction. + Mottaksadresse for transaksjonen + + + + Amount removed from or added to balance. + Beløp fjernet eller lagt til saldo. + + + + TransactionView + + + + All + Alle + + + + Today + I dag + + + + This week + Denne uken + + + + This month + Denne måneden + + + + Last month + Forrige måned + + + + This year + Dette året + + + + Range... + Intervall... + + + + Received with + Mottatt med + + + + Sent to + Sendt til + + + + To yourself + Til deg selv + + + + Mined + Utvunnet + + + + Other + Andre + + + + Enter address or label to search + Skriv inn adresse eller merkelapp for søk + + + + Min amount + Minimumsbeløp + + + + Copy address + Kopier adresse + + + + Copy label + Kopier merkelapp + + + + Copy amount + Kopiér beløp + + + + Copy transaction ID + Kopier transaksjons-ID + + + + Edit label + Rediger merkelapp + + + + Show transaction details + Vis transaksjonsdetaljer + + + + Export Transaction Data + Eksporter transaksjonsdata + + + + Comma separated file (*.csv) + Kommaseparert fil (*.csv) + + + + Confirmed + Bekreftet + + + + Date + Dato + + + + Type + Type + + + + Label + Merkelapp + + + + Address + Adresse + + + + Amount + Beløp + + + + ID + ID + + + + Error exporting + Feil ved eksport + + + + Could not write to file %1. + Kunne ikke skrive til filen %1. + + + + Range: + Intervall: + + + + to + til + + + + WalletModel + + + Send Coins + Send CryptoLottoTokens + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + Eksporter data fra nåværende fane til fil + + + + Backup Wallet + Sikkerhetskopier lommebok + + + + Wallet Data (*.dat) + Lommebokdata (*.dat) + + + + Backup Failed + Sikkerhetskopiering feilet + + + + There was an error trying to save the wallet data to the new location. + En feil oppstod under lagringen av lommeboken til den nye plasseringen. + + + + Backup Successful + Sikkerhetskopiering fullført + + + + The wallet data was successfully saved to the new location. + Lommebokdata ble lagret til den nye plasseringen. + + + + bitcoin-core + + + CryptoLottoToken version + CryptoLottoToken versjon + + + + Usage: + Bruk: + + + + Send command to -server or CryptoLottoTokend + Send kommando til -server eller CryptoLottoTokend + + + + List commands + List opp kommandoer + + + + Get help for a command + Vis hjelpetekst for en kommando + + + + Options: + Innstillinger: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Angi konfigurasjonsfil (standardverdi: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + Angi pid-fil (standardverdi: CryptoLottoTokend.pid) + + + + Specify data directory + Angi mappe for datafiler + + + + Set database cache size in megabytes (default: 25) + Sett størrelse på mellomlager for database i megabytes (standardverdi: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Lytt etter tilkoblinger på <port> (standardverdi: 22556 eller testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Hold maks <n> koblinger åpne til andre noder (standardverdi: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Koble til node for å hente adresser til andre noder, koble så fra igjen + + + + Specify your own public address + Angi din egen offentlige adresse + + + + Threshold for disconnecting misbehaving peers (default: 100) + Grenseverdi for å koble fra noder med dårlig oppførsel (standardverdi: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Antall sekunder noder med dårlig oppførsel hindres fra å koble til på nytt (standardverdi: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + En feil oppstod ved opprettelse av RPC port %u for lytting: %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Lytt etter JSON-RPC tilkoblinger på <port> (standardverdi: 22555 or testnet: 44555) + + + + Accept command line and JSON-RPC commands + Ta imot kommandolinje- og JSON-RPC-kommandoer + + + + Run in the background as a daemon and accept commands + Kjør i bakgrunnen som daemon og ta imot kommandoer + + + + Use the test network + Bruk testnettverket + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Ta imot tilkoblinger fra utsiden (standardverdi: 1 hvis uten -proxy eller -connect) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + %s, du må angi rpcpassord i konfigurasjonsfilen. +%s +Det anbefales at du bruker det følgende tilfeldige passordet: +rpcbruker=CryptoLottoTokenrpc +rpcpassord=%s +(du behøver ikke å huske passordet) +Brukernavnet og passordet MÅ IKKE være like. +Om filen ikke eksisterer, opprett den nå med eier-kun-les filrettigheter. +Det er også anbefalt at å sette varselsmelding slik du får melding om problemer. +For eksempel: varselmelding=echo %%s | mail -s "CryptoLottoToken varsel" admin@foo.com + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + En feil oppstod under oppsettet av RPC port %u for IPv6, tilbakestilles til IPv4: %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + Bind til angitt adresse. Bruk [vertsmaskin]:port notasjon for IPv6 + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + Kjør kommando når relevant varsel blir mottatt (%s i cmd er erstattet med TxID) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Kjør kommando når en lommeboktransaksjon endres (%s i cmd er erstattet med TxID) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Sett maks størrelse for transaksjoner med høy prioritet / lavt gebyr, i bytes (standardverdi: 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Advarsel: -paytxfee er satt veldig høyt! Dette er transaksjonsgebyret du betaler når du sender transaksjoner. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + Advarsel: Viste transaksjoner kan være feil! Du, eller andre noder, kan trenge en oppgradering. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Advarsel: Vennligst undersøk at din datamaskin har riktig dato og klokkeslett! Hvis klokken er stilt feil vil ikke CryptoLottoToken fungere riktig. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + Valg for opprettelse av blokker: + + + + Connect only to the specified node(s) + Koble kun til angitt(e) node(r) + + + + Corrupted block database detected + Oppdaget korrupt blokkdatabase + + + + Discover own IP address (default: 1 when listening and no -externalip) + Oppdag egen IP-adresse (standardverdi: 1 ved lytting og uten -externalip) + + + + Do you want to rebuild the block database now? + Ønsker du å gjenopprette blokkdatabasen nå? + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + Feil under oppstart av lommebokdatabasemiljø %s! + + + + Error loading block database + + + + + Error opening block database + Feil under åpning av blokkdatabase + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + Kunne ikke lytte på noen port. Bruk -listen=0 hvis det er dette du vil. + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + Finn andre noder gjennom DNS-oppslag (standardverdi: 1 med mindre -connect er oppgit) + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + Gjenopprett blokkjedeindex fra blk000??.dat filer + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + Verifiserer blokker... + + + + Verifying wallet... + Verifiserer lommebok... + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + Ugyldig -tor adresse: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Maks mottaksbuffer per forbindelse, <n>*1000 bytes (standardverdi: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Maks sendebuffer per forbindelse, <n>*1000 bytes (standardverdi: 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Koble kun til noder i nettverket <nett> (IPv4, IPv6 eller Tor) + + + + Output extra debugging information. Implies all other -debug* options + Skriv ekstra informasjon for feilsøk. Medfører at alle -debug* valg tas med + + + + Output extra network debugging information + Skriv ekstra informasjon for feilsøk av nettverk + + + + Prepend debug output with timestamp (default: 1) + Sett tidsstempel på debugmeldinger + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + SSL valg: (se CryptoLottoToken Wiki for instruksjoner for oppsett av SSL) + + + + Select the version of socks proxy to use (4-5, default: 5) + Velg versjon av socks proxy (4-5, standardverdi 5) + + + + Send trace/debug info to console instead of debug.log file + Send spor/debug informasjon til konsollet istedenfor debug.log filen + + + + Send trace/debug info to debugger + Send spor/debug informasjon til debugger + + + + Set maximum block size in bytes (default: 250000) + Sett maks blokkstørrelse i bytes (standardverdi: 250000) + + + + Set minimum block size in bytes (default: 0) + Sett minimum blokkstørrelse i bytes (standardverdi: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Krymp debug.log filen når klienten starter (standardverdi: 1 hvis uten -debug) + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + Angi tidsavbrudd for forbindelse i millisekunder (standardverdi: 5000) + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + Bruk UPnP for lytteport (standardverdi: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Bruk UPnP for lytteport (standardverdi: 1 ved lytting) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Bruk en proxy for å nå skjulte tor tjenester (standardverdi: samme som -proxy) + + + + Username for JSON-RPC connections + Brukernavn for JSON-RPC forbindelser + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + Advarsel: Denne versjonen er foreldet, oppgradering kreves! + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + Passord for JSON-RPC forbindelser + + + + Allow JSON-RPC connections from specified IP address + Tillat JSON-RPC tilkoblinger fra angitt IP-adresse + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Send kommandoer til node på <ip> (standardverdi: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Eksekvér kommando når beste blokk endrer seg (%s i kommandoen erstattes med blokkens hash) + + + + Upgrade wallet to latest format + Oppgradér lommebok til nyeste format + + + + Set key pool size to <n> (default: 100) + Angi størrelse på nøkkel-lager til <n> (standardverdi: 100) + + + + Rescan the block chain for missing wallet transactions + Se gjennom blokk-kjeden etter manglende lommeboktransaksjoner + + + + Use OpenSSL (https) for JSON-RPC connections + Bruk OpenSSL (https) for JSON-RPC forbindelser + + + + Server certificate file (default: server.cert) + Servers sertifikat (standardverdi: server.cert) + + + + Server private key (default: server.pem) + Servers private nøkkel (standardverdi: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Akseptable krypteringsmetoder (standardverdi: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Denne hjelpemeldingen + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Kan ikke binde til %s på denne datamaskinen (bind returnerte feil %d, %s) + + + + Connect through socks proxy + Koble til gjennom socks proxy + + + + Allow DNS lookups for -addnode, -seednode and -connect + Tillat DNS oppslag for -addnode, -seednode og -connect + + + + Loading addresses... + Laster adresser... + + + + Error loading wallet.dat: Wallet corrupted + Feil ved lasting av wallet.dat: Lommeboken er skadet + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Feil ved lasting av wallet.dat: Lommeboken krever en nyere versjon av CryptoLottoToken + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Lommeboken måtte skrives om: start CryptoLottoToken på nytt for å fullføre + + + + Error loading wallet.dat + Feil ved lasting av wallet.dat + + + + Invalid -proxy address: '%s' + Ugyldig -proxy adresse: '%s' + + + + Unknown network specified in -onlynet: '%s' + Ukjent nettverk angitt i -onlynet '%s' + + + + Unknown -socks proxy version requested: %i + Ukjent -socks proxy versjon angitt: %i + + + + Cannot resolve -bind address: '%s' + Kunne ikke slå opp -bind adresse: '%s' + + + + Cannot resolve -externalip address: '%s' + Kunne ikke slå opp -externalip adresse: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + Ugyldig beløp for -paytxfee=<beløp>: '%s' + + + + Invalid amount + Ugyldig beløp + + + + Insufficient funds + Utilstrekkelige midler + + + + Loading block index... + Laster blokkindeks... + + + + Add a node to connect to and attempt to keep the connection open + Legg til node for tilkobling og hold forbindelsen åpen + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Kan ikke binde til %s på denne datamaskinen. Sannsynligvis kjører CryptoLottoToken allerede. + + + + Fee per KB to add to transactions you send + Gebyr per KB for transaksjoner du sender + + + + Loading wallet... + Laster lommebok... + + + + Cannot downgrade wallet + Kan ikke nedgradere lommebok + + + + Cannot write default address + Kan ikke skrive standardadresse + + + + Rescanning... + Leser gjennom... + + + + Done loading + Ferdig med lasting + + + + To use the %s option + For å bruke %s opsjonen + + + + Error + Feil + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + Du må sette rpcpassword=<passord> i konfigurasjonsfilen: +%s +Hvis filen ikke finnes, opprett den med leserettighet kun for eier av filen. + + + diff --git a/src/qt/locale/bitcoin_nl.qm b/src/qt/locale/bitcoin_nl.qm new file mode 100644 index 0000000..6083f31 Binary files /dev/null and b/src/qt/locale/bitcoin_nl.qm differ diff --git a/src/qt/locale/bitcoin_nl.ts b/src/qt/locale/bitcoin_nl.ts new file mode 100644 index 0000000..b5e68c7 --- /dev/null +++ b/src/qt/locale/bitcoin_nl.ts @@ -0,0 +1,2938 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + Over CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> versie + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +Dit is experimentele software. + +Gedistribueerd onder de MIT/X11 software licentie, zie het bijgevoegde bestand COPYING of http://www.opensource.org/licenses/mit-license.php. + +Dit product bevat software ontwikkeld door het OpenSSL Project voor gebruik in de OpenSSL Toolkit (http://www.openssl.org/) en cryptografische software gemaakt door Eric Young (eay@cryptsoft.com) en UPnP software geschreven door Thomas Bernard. + + + + Copyright + Auteursrecht + + + + The CryptoLottoToken developers + De CryptoLottoToken-ontwikkelaars + + + + AddressBookPage + + + Address Book + Adresboek + + + + Double-click to edit address or label + Dubbelklik om adres of label te wijzigen + + + + Create a new address + Maak een nieuw adres aan + + + + Copy the currently selected address to the system clipboard + Kopieer het huidig geselecteerde adres naar het klembord + + + + &New Address + &Nieuw Adres + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Dit zijn uw CryptoLottoTokenadressen om betalingen mee te ontvangen. U kunt er voor kiezen om een uniek adres aan te maken voor elke afzender. Op deze manier kunt u bijhouden wie al aan u betaald heeft. + + + + &Copy Address + &Kopiëer Adres + + + + Show &QR Code + Toon &QR-Code + + + + Sign a message to prove you own a CryptoLottoToken address + Onderteken een bericht om te bewijzen dat u een bepaald CryptoLottoTokenadres bezit + + + + Sign &Message + &Onderteken Bericht + + + + Delete the currently selected address from the list + Verwijder het geselecteerde adres van de lijst + + + + Export the data in the current tab to a file + Exporteer de data in de huidige tab naar een bestand + + + + &Export + &Exporteer + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Controleer een bericht om te verifiëren dat het gespecificeerde CryptoLottoTokenadres het bericht heeft ondertekend. + + + + &Verify Message + &Verifiëer Bericht + + + + &Delete + &Verwijder + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + Dit zijn uw CryptoLottoTokenadressen om betalingen mee te verzenden. Check altijd het bedrag en het ontvangende adres voordat u uw CryptoLottoTokens verzendt. + + + + Copy &Label + Kopiëer &Label + + + + &Edit + &Bewerk + + + + Send &Coins + Verstuur &Coins + + + + Export Address Book Data + Exporteer Gegevens van het Adresboek + + + + Comma separated file (*.csv) + Kommagescheiden bestand (*.csv) + + + + Error exporting + Fout bij exporteren + + + + Could not write to file %1. + Kon niet schrijven naar bestand %1. + + + + AddressTableModel + + + Label + Label + + + + Address + Adres + + + + (no label) + (geen label) + + + + AskPassphraseDialog + + + Passphrase Dialog + Wachtwoorddialoogscherm + + + + Enter passphrase + Voer wachtwoord in + + + + New passphrase + Nieuw wachtwoord + + + + Repeat new passphrase + Herhaal wachtwoord + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Vul een nieuw wachtwoord in voor uw portemonnee. <br/> Gebruik een wachtwoord van <b>10 of meer lukrake karakters</b>, of <b> acht of meer woorden</b> . + + + + Encrypt wallet + Versleutel portemonnee + + + + This operation needs your wallet passphrase to unlock the wallet. + Deze operatie vereist uw portemonneewachtwoord om de portemonnee te openen. + + + + Unlock wallet + Open portemonnee + + + + This operation needs your wallet passphrase to decrypt the wallet. + Deze operatie vereist uw portemonneewachtwoord om de portemonnee te ontsleutelen + + + + Decrypt wallet + Ontsleutel portemonnee + + + + Change passphrase + Wijzig wachtwoord + + + + Enter the old and new passphrase to the wallet. + Vul uw oude en nieuwe portemonneewachtwoord in. + + + + Confirm wallet encryption + Bevestig versleuteling van de portemonnee + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Waarschuwing: Als u uw portemonnee versleutelt en uw wachtwoord vergeet, zult u <b>AL UW CryptoLottoTokenS VERLIEZEN</b>! + + + + Are you sure you wish to encrypt your wallet? + Weet u zeker dat u uw portemonnee wilt versleutelen? + + + + 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. + 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. + + + + + Warning: The Caps Lock key is on! + Waarschuwing: De Caps-Lock-toets staat aan! + + + + + Wallet encrypted + Portemonnee versleuteld + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken zal nu afsluiten om het versleutelingsproces te voltooien. Onthoud dat het versleutelen van uw portemonnee u niet volledig kan beschermen: Malware kan uw computer infecteren en uw CryptoLottoTokens stelen. + + + + + + + Wallet encryption failed + Portemonneeversleuteling mislukt + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Portemonneeversleuteling mislukt door een interne fout. Uw portemonnee is niet versleuteld. + + + + + The supplied passphrases do not match. + De opgegeven wachtwoorden komen niet overeen + + + + Wallet unlock failed + Portemonnee openen mislukt + + + + + + The passphrase entered for the wallet decryption was incorrect. + Het opgegeven wachtwoord voor de portemonnee-ontsleuteling is niet correct. + + + + Wallet decryption failed + Portemonnee-ontsleuteling mislukt + + + + Wallet passphrase was successfully changed. + Portemonneewachtwoord is met succes gewijzigd. + + + + BitcoinGUI + + + Sign &message... + &Onderteken bericht... + + + + Synchronizing with network... + Synchroniseren met netwerk... + + + + &Overview + &Overzicht + + + + Show general overview of wallet + Toon algemeen overzicht van de portemonnee + + + + &Transactions + &Transacties + + + + Browse transaction history + Blader door transactieverleden + + + + Edit the list of stored addresses and labels for sending + Bewerk de lijst van opgeslagen adressen en labels + + + + Show the list of addresses for receiving payments + Toon lijst van adressen om betalingen mee te ontvangen + + + + E&xit + &Afsluiten + + + + Quit application + Programma afsluiten + + + + Show information about CryptoLottoToken + Laat informatie zien over CryptoLottoToken + + + + About &Qt + Over &Qt + + + + Show information about Qt + Toon informatie over Qt + + + + &Options... + O&pties... + + + + &Encrypt Wallet... + &Versleutel Portemonnee... + + + + &Backup Wallet... + &Backup Portemonnee... + + + + &Change Passphrase... + &Wijzig Wachtwoord + + + + Importing blocks from disk... + Blokken aan het importeren vanaf harde schijf... + + + + Reindexing blocks on disk... + Bezig met herindexeren van blokken op harde schijf... + + + + Send coins to a CryptoLottoToken address + Verstuur munten naar een CryptoLottoTokenadres + + + + Modify configuration options for CryptoLottoToken + Wijzig instellingen van CryptoLottoToken + + + + Backup wallet to another location + &Backup portemonnee naar een andere locatie + + + + Change the passphrase used for wallet encryption + Wijzig het wachtwoord voor uw portemonneversleuteling + + + + &Debug window + &Debugscherm + + + + Open debugging and diagnostic console + Open debugging en diagnostische console + + + + &Verify message... + &Verifiëer bericht... + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Portemonnee + + + + &Send + &Versturen + + + + &Receive + &Ontvangen + + + + &Addresses + &Adressen + + + + &About CryptoLottoToken + &Over CryptoLottoToken + + + + &Show / Hide + &Toon / Verberg + + + + Show or hide the main Window + Toon of verberg het hoofdvenster + + + + Encrypt the private keys that belong to your wallet + Versleutel de geheime sleutels die bij uw portemonnee horen + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Onderteken berichten met uw CryptoLottoTokenadressen om te bewijzen dat u deze adressen bezit + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Verifiëer handtekeningen om zeker te zijn dat de berichten zijn ondertekend met de gespecificeerde CryptoLottoTokenadressen + + + + &File + &Bestand + + + + &Settings + &Instellingen + + + + &Help + &Hulp + + + + Tabs toolbar + Tab-werkbalk + + + + + [testnet] + [testnetwerk] + + + + CryptoLottoToken client + CryptoLottoToken client + + + + %n active connection(s) to CryptoLottoToken network + %n actieve connectie naar CryptoLottoTokennetwerk%n actieve connecties naar CryptoLottoTokennetwerk + + + + No block source available... + Geen bron van blokken beschikbaar... + + + + Processed %1 of %2 (estimated) blocks of transaction history. + %1 van %2 (geschat) blokken van de transactiehistorie verwerkt. + + + + Processed %1 blocks of transaction history. + %1 blokken van transactiehistorie verwerkt. + + + + %n hour(s) + %n uur%n uur + + + + %n day(s) + %n dag%n dagen + + + + %n week(s) + %n week%n weken + + + + %1 behind + %1 achter + + + + Last received block was generated %1 ago. + Laatst ontvangen blok was %1 geleden gegenereerd. + + + + Transactions after this will not yet be visible. + Transacties na dit moment zullen nu nog niet zichtbaar zijn. + + + + Error + Fout + + + + Warning + Waarschuwing + + + + Information + Informatie + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + Deze transactie overschrijdt de groottelimiet. Om de transactie alsnog te versturen kunt u transactiekosten betalen van %1. Deze transactiekosten gaan naar de nodes die uw transactie verwerken en het helpt op deze manier bij het ondersteunen van het CryptoLottoTokennetwerk. Wilt u de transactiekosten betalen? + + + + Up to date + Bijgewerkt + + + + Catching up... + Aan het bijwerken... + + + + Confirm transaction fee + Bevestig transactiekosten + + + + Sent transaction + Verzonden transactie + + + + Incoming transaction + Binnenkomende transactie + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Datum: %1 +Bedrag: %2 +Type: %3 +Adres: %4 + + + + + + URI handling + URI-behandeling + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + URI kan niet worden geïnterpreteerd. Dit kan komen door een ongeldig CryptoLottoTokenadres of misvormde URI-parameters. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Portemonnee is <b>versleuteld</b> en momenteel <b>geopend</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Portemonnee is <b>versleuteld</b> en momenteel <b>gesloten</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + Er is een fatale fout opgetreden. CryptoLottoToken kan niet meer veilig doorgaan en zal nu afgesloten worden. + + + + ClientModel + + + Network Alert + Netwerkwaarschuwing + + + + EditAddressDialog + + + Edit Address + Bewerk Adres + + + + &Label + &Label + + + + The label associated with this address book entry + Het label dat geassocieerd is met dit adres + + + + &Address + &Adres + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Het adres dat geassocieerd is met deze inschrijving in het adresboek. Dit kan alleen worden veranderd voor zend-adressen. + + + + New receiving address + Nieuw ontvangstadres + + + + New sending address + Nieuw adres om naar te verzenden + + + + Edit receiving address + Bewerk ontvangstadres + + + + Edit sending address + Bewerk adres om naar te verzenden + + + + The entered address "%1" is already in the address book. + Het opgegeven adres "%1" bestaat al in uw adresboek. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + Het opgegeven adres "%1" is een ongeldig CryptoLottoTokenadres + + + + Could not unlock wallet. + Kon de portemonnee niet openen. + + + + New key generation failed. + Genereren nieuwe sleutel mislukt. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + versie + + + + Usage: + Gebruik: + + + + command-line options + commandoregel-opties + + + + UI options + gebruikersinterfaceopties + + + + Set language, for example "de_DE" (default: system locale) + Stel taal in, bijvoorbeeld ''de_DE" (standaard: systeeminstellingen) + + + + Start minimized + Geminimaliseerd starten + + + + Show splash screen on startup (default: 1) + Laat laadscherm zien bij het opstarten. (standaard: 1) + + + + OptionsDialog + + + Options + Opties + + + + &Main + &Algemeen + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + Optionele transactiekosten per kB. Transactiekosten helpen ervoor te zorgen dat uw transacties snel verwerkt worden. De meeste transacties zijn 1kB. + + + + Pay transaction &fee + Betaal &transactiekosten + + + + Automatically start CryptoLottoToken after logging in to the system. + Start CryptoLottoToken automatisch na inloggen in het systeem + + + + &Start CryptoLottoToken on system login + Start &CryptoLottoToken bij het inloggen in het systeem + + + + Reset all client options to default. + Reset alle clientopties naar de standaardinstellingen. + + + + &Reset Options + &Reset Opties + + + + &Network + &Netwerk + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Open de CryptoLottoToken-poort automatisch op de router. Dit werkt alleen als de router UPnP ondersteunt en het aanstaat. + + + + Map port using &UPnP + Portmapping via &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Verbind met het CryptoLottoToken-netwerk via een SOCKS-proxy (bijv. wanneer u via Tor wilt verbinden) + + + + &Connect through SOCKS proxy: + &Verbind via een SOCKS-proxy + + + + Proxy &IP: + Proxy &IP: + + + + IP address of the proxy (e.g. 127.0.0.1) + IP-adres van de proxy (bijv. 127.0.0.1) + + + + &Port: + &Poort: + + + + Port of the proxy (e.g. 9050) + Poort van de proxy (bijv. 9050) + + + + SOCKS &Version: + SOCKS-&Versie: + + + + SOCKS version of the proxy (e.g. 5) + SOCKS-versie van de proxy (bijv. 5) + + + + &Window + &Scherm + + + + Show only a tray icon after minimizing the window. + Laat alleen een systeemvak-icoon zien wanneer het venster geminimaliseerd is + + + + &Minimize to the tray instead of the taskbar + &Minimaliseer naar het systeemvak in plaats van de taakbalk + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Minimaliseer het venster in de plaats van de applicatie af te sluiten als het venster gesloten wordt. Wanneer deze optie aan staan, kan de applicatie alleen worden afgesloten door Afsluiten te kiezen in het menu. + + + + M&inimize on close + Minimaliseer bij sluiten van het &venster + + + + &Display + &Interface + + + + User Interface &language: + Taal &Gebruikersinterface: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + De taal van de gebruikersinterface kan hier ingesteld worden. Deze instelling zal pas van kracht worden nadat CryptoLottoToken herstart wordt. + + + + &Unit to show amounts in: + &Eenheid om bedrag in te tonen: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Kies de standaard onderverdelingseenheid om weer te geven in uw programma, en voor het versturen van munten + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Of CryptoLottoTokenadressen getoond worden in de transactielijst + + + + &Display addresses in transaction list + Toon a&dressen in de transactielijst + + + + &OK + &OK + + + + &Cancel + Ann&uleren + + + + &Apply + &Toepassen + + + + default + standaard + + + + Confirm options reset + Bevestig reset opties + + + + Some settings may require a client restart to take effect. + Sommige instellingen vereisen het herstarten van de client voordat ze in werking treden. + + + + Do you want to proceed? + Wilt u doorgaan? + + + + + Warning + Waarschuwing + + + + + This setting will take effect after restarting CryptoLottoToken. + Deze instelling zal pas van kracht worden na het herstarten van CryptoLottoToken. + + + + The supplied proxy address is invalid. + Het opgegeven proxyadres is ongeldig. + + + + OverviewPage + + + Form + Vorm + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + De weergegeven informatie kan verouderd zijn. Uw portemonnee synchroniseert automaticsh met het CryptoLottoTokennetwerk nadat een verbinding is gelegd, maar dit proces is nog niet voltooid. + + + + Balance: + Saldo: + + + + Unconfirmed: + Onbevestigd: + + + + Wallet + Portemonnee + + + + Immature: + Immatuur: + + + + Mined balance that has not yet matured + Gedolven saldo dat nog niet tot wasdom is gekomen + + + + <b>Recent transactions</b> + <b>Recente transacties</b> + + + + Your current balance + Uw huidige saldo + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Totaal van de transacties die nog moeten worden bevestigd en nog niet zijn meegeteld in uw huidige saldo + + + + + out of sync + niet gesynchroniseerd + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + Kan CryptoLottoToken niet starten: click-to-pay handler + + + + QRCodeDialog + + + QR Code Dialog + QR-codescherm + + + + Request Payment + Vraag betaling aan + + + + Amount: + Bedrag: + + + + Label: + Label: + + + + Message: + Bericht: + + + + &Save As... + &Opslaan Als... + + + + Error encoding URI into QR Code. + Fout tijdens encoderen URI in QR-code + + + + The entered amount is invalid, please check. + Het opgegeven bedrag is ongeldig, controleer het s.v.p. + + + + Resulting URI too long, try to reduce the text for label / message. + Resulterende URI te lang, probeer de tekst korter te maken voor het label/bericht. + + + + Save QR Code + Sla QR-code op + + + + PNG Images (*.png) + PNG-Afbeeldingen (*.png) + + + + RPCConsole + + + Client name + Clientnaam + + + + + + + + + + + + + N/A + N.v.t. + + + + Client version + Clientversie + + + + &Information + &Informatie + + + + Using OpenSSL version + Gebruikt OpenSSL versie + + + + Startup time + Opstarttijd + + + + Network + Netwerk + + + + Number of connections + Aantal connecties + + + + On testnet + Op testnet + + + + Block chain + Blokketen + + + + Current number of blocks + Huidig aantal blokken + + + + Estimated total blocks + Geschat totaal aantal blokken + + + + Last block time + Tijd laatste blok + + + + &Open + &Open + + + + Command-line options + Commandoregel-opties + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Toon het CryptoLottoTokenQt-hulpbericht voor een lijst met mogelijke CryptoLottoToken commandoregel-opties. + + + + &Show + &Toon + + + + &Console + &Console + + + + Build date + Bouwdatum + + + + CryptoLottoToken - Debug window + CryptoLottoToken-debugscherm + + + + CryptoLottoToken Core + CryptoLottoToken Kern + + + + Debug log file + Debug-logbestand + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + Open het CryptoLottoTokendebug-logbestand van de huidige datamap. Dit kan een aantal seconden duren voor grote logbestanden. + + + + Clear console + Maak console leeg + + + + Welcome to the CryptoLottoToken RPC console. + Welkom bij de CryptoLottoToken RPC-console. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Gebruik de pijltjestoetsen om door de geschiedenis te navigeren, en <b>Ctrl-L</b> om het scherm leeg te maken. + + + + Type <b>help</b> for an overview of available commands. + Typ <b>help</b> voor een overzicht van de beschikbare commando's. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Verstuur munten + + + + Send to multiple recipients at once + Verstuur aan verschillende ontvangers ineens + + + + Add &Recipient + Voeg &Ontvanger Toe + + + + Remove all transaction fields + Verwijder alle transactievelden + + + + Clear &All + Verwijder &Alles + + + + Balance: + Saldo: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Bevestig de verstuuractie + + + + S&end + &Verstuur + + + + <b>%1</b> to %2 (%3) + <b>%1</b> aan %2 (%3) + + + + Confirm send coins + Bevestig versturen munten + + + + Are you sure you want to send %1? + Weet u zeker dat u %1 wil versturen? + + + + and + en + + + + The recipient address is not valid, please recheck. + Het ontvangstadres is niet geldig, controleer uw invoer. + + + + The amount to pay must be larger than 0. + Het ingevoerde bedrag moet groter zijn dan 0. + + + + The amount exceeds your balance. + Bedrag is hoger dan uw huidige saldo + + + + The total exceeds your balance when the %1 transaction fee is included. + Totaal overschrijdt uw huidige saldo wanneer de %1 transactiekosten worden meegerekend + + + + Duplicate address found, can only send to each address once per send operation. + Dubbel adres gevonden, u kunt slechts eenmaal naar een bepaald adres verzenden per verstuurtransactie + + + + Error: Transaction creation failed! + Fout: Aanmaak transactie mislukt! + + + + 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. + Fout: De transactie was afgewezen. Dit kan gebeuren als u eerder uitgegeven munten opnieuw wilt versturen, zoals wanneer u een kopie van uw portemonneebestand (wallet.dat) heeft gebruikt en in de kopie deze munten zijn uitgegeven, maar in de huidige portemonnee deze nog niet als zodanig zijn gemarkeerd. + + + + SendCoinsEntry + + + Form + Vorm + + + + A&mount: + Bedra&g: + + + + Pay &To: + Betaal &Aan: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Het adres waaraan u wilt betalen (bijv. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + Vul een label in voor dit adres om het toe te voegen aan uw adresboek + + + + &Label: + &Label: + + + + Choose address from address book + Kies adres uit adresboek + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Plak adres vanuit klembord + + + + Alt+P + Alt+P + + + + Remove this recipient + Verwijder deze ontvanger + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Vul een CryptoLottoTokenadres in (bijv. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Handtekeningen - Onderteken een bericht / Verifiëer een handtekening + + + + &Sign Message + O&nderteken Bericht + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + U kunt berichten ondertekenen met een van uw adressen om te bewijzen dat u dit adres bezit. Pas op dat u geen onduidelijke dingen ondertekent, want phishingaanvallen zouden u kunnen misleiden om zo uw identiteit te stelen. Onderteken alleen berichten waarmee u het volledig eens bent. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Het adres om het bericht mee te ondertekenen (Vb.: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2). + + + + + Choose an address from the address book + Kies een adres uit het adresboek + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Plak adres vanuit klembord + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Typ hier het bericht dat u wilt ondertekenen + + + + Signature + Handtekening + + + + Copy the current signature to the system clipboard + Kopieer de huidige handtekening naar het systeemklembord + + + + Sign the message to prove you own this CryptoLottoToken address + Onderteken een bericht om te bewijzen dat u een bepaald CryptoLottoTokenadres bezit + + + + Sign &Message + Onderteken &Bericht + + + + Reset all sign message fields + Verwijder alles in de invulvelden + + + + + Clear &All + Verwijder &Alles + + + + &Verify Message + &Verifiëer Bericht + + + + Enter the signing 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. + Voer het ondertekenende adres, bericht en handtekening hieronder in (let erop dat u nieuwe regels, spaties en tabs juist overneemt) om de handtekening te verifiëren. Let erop dat u niet meer uit het bericht interpreteert dan er daadwerkelijk staat, om te voorkomen dat u wordt misleid in een man-in-the-middle-aanval. + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Het adres waarmee bet bericht was ondertekend (Vb.: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2). + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Controleer een bericht om te verifiëren dat het gespecificeerde CryptoLottoTokenadres het bericht heeft ondertekend. + + + + Verify &Message + Verifiëer &Bericht + + + + Reset all verify message fields + Verwijder alles in de invulvelden + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Vul een CryptoLottoTokenadres in (bijv. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Klik "Onderteken Bericht" om de handtekening te genereren + + + + Enter CryptoLottoToken signature + Voer CryptoLottoToken-handtekening in + + + + + The entered address is invalid. + Het opgegeven adres is ongeldig. + + + + + + + Please check the address and try again. + Controleer s.v.p. het adres en probeer het opnieuw. + + + + + The entered address does not refer to a key. + Het opgegeven adres verwijst niet naar een sleutel. + + + + Wallet unlock was cancelled. + Portemonnee-ontsleuteling is geannuleerd + + + + Private key for the entered address is not available. + Geheime sleutel voor het ingevoerde adres is niet beschikbaar. + + + + Message signing failed. + Ondertekenen van het bericht is mislukt. + + + + Message signed. + Bericht ondertekend. + + + + The signature could not be decoded. + De handtekening kon niet worden gedecodeerd. + + + + + Please check the signature and try again. + Controleer s.v.p. de handtekening en probeer het opnieuw. + + + + The signature did not match the message digest. + De handtekening hoort niet bij het bericht. + + + + Message verification failed. + Berichtverificatie mislukt. + + + + Message verified. + Bericht correct geverifiëerd. + + + + SplashScreen + + + The CryptoLottoToken developers + De CryptoLottoToken-ontwikkelaars + + + + [testnet] + [testnetwerk] + + + + TransactionDesc + + + Open until %1 + Openen totdat %1 + + + + %1/offline + %1/offline + + + + %1/unconfirmed + %1/onbevestigd + + + + %1 confirmations + %1 bevestigingen + + + + Status + Status + + + + , broadcast through %n node(s) + , uitgezonden naar %n node, uitgezonden naar %n nodes + + + + Date + Datum + + + + Source + Bron + + + + Generated + Gegenereerd + + + + + From + Van + + + + + + To + Aan + + + + + own address + eigen adres + + + + label + label + + + + + + + + Credit + Credit + + + + matures in %n more block(s) + komt tot wasdom na %n nieuw blokkomt tot wasdom na %n nieuwe blokken + + + + not accepted + niet geaccepteerd + + + + + + + Debit + Debet + + + + Transaction fee + Transactiekosten + + + + Net amount + Netto bedrag + + + + Message + Bericht + + + + Comment + Opmerking + + + + Transaction ID + Transactie-ID: + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Gegeneerde munten moeten 120 blokken wachten voordat ze tot wasdom komen en kunnen worden uitgegeven. Uw net gegenereerde blok is uitgezonden aan het netwerk om te worden toegevoegd aan de blokketen. Als het niet wordt geaccepteerd in de keten, zal het blok als "niet geaccepteerd" worden aangemerkt en kan het niet worden uitgegeven. Dit kan soms gebeuren als een andere node net iets sneller een blok heeft gegenereerd; een paar seconden voor het uwe. + + + + Debug information + Debug-informatie + + + + Transaction + Transactie + + + + Inputs + Inputs + + + + Amount + Bedrag + + + + true + waar + + + + false + onwaar + + + + , has not been successfully broadcast yet + , is nog niet met succes uitgezonden + + + + Open for %n more block(s) + Open voor nog %n blokOpen voor nog %n blokken + + + + unknown + onbekend + + + + TransactionDescDialog + + + Transaction details + Transactiedetails + + + + This pane shows a detailed description of the transaction + Dit venster laat een uitgebreide beschrijving van de transactie zien + + + + TransactionTableModel + + + Date + Datum + + + + Type + Type + + + + Address + Adres + + + + Amount + Bedrag + + + + Open for %n more block(s) + Open voor nog %n blokOpen voor nog %n blokken + + + + Open until %1 + Open tot %1 + + + + Offline (%1 confirmations) + Niet verbonden (%1 bevestigingen) + + + + Unconfirmed (%1 of %2 confirmations) + Onbevestigd (%1 van %2 bevestigd) + + + + Confirmed (%1 confirmations) + Bevestigd (%1 bevestigingen) + + + + Mined balance will be available when it matures in %n more block(s) + Gedolven saldo zal beschikbaar komen als het tot wasdom komt na %n blokGedolven saldo zal beschikbaar komen als het tot wasdom komt na %n blokken + + + + This block was not received by any other nodes and will probably not be accepted! + Dit blok is niet ontvangen bij andere nodes en zal waarschijnlijk niet worden geaccepteerd! + + + + Generated but not accepted + Gegenereerd maar niet geaccepteerd + + + + Received with + Ontvangen met + + + + Received from + Ontvangen van + + + + Sent to + Verzonden aan + + + + Payment to yourself + Betaling aan uzelf + + + + Mined + Gedolven + + + + (n/a) + (nvt) + + + + Transaction status. Hover over this field to show number of confirmations. + Transactiestatus. Houd de muiscursor boven dit veld om het aantal bevestigingen te laten zien. + + + + Date and time that the transaction was received. + Datum en tijd waarop deze transactie is ontvangen. + + + + Type of transaction. + Type transactie. + + + + Destination address of transaction. + Ontvangend adres van transactie. + + + + Amount removed from or added to balance. + Bedrag verwijderd van of toegevoegd aan saldo + + + + TransactionView + + + + All + Alles + + + + Today + Vandaag + + + + This week + Deze week + + + + This month + Deze maand + + + + Last month + Vorige maand + + + + This year + Dit jaar + + + + Range... + Bereik... + + + + Received with + Ontvangen met + + + + Sent to + Verzonden aan + + + + To yourself + Aan uzelf + + + + Mined + Gedolven + + + + Other + Anders + + + + Enter address or label to search + Vul adres of label in om te zoeken + + + + Min amount + Min. bedrag + + + + Copy address + Kopieer adres + + + + Copy label + Kopieer label + + + + Copy amount + Kopieer bedrag + + + + Copy transaction ID + Kopieer transactie-ID + + + + Edit label + Bewerk label + + + + Show transaction details + Toon transactiedetails + + + + Export Transaction Data + Exporteer transactiegegevens + + + + Comma separated file (*.csv) + Kommagescheiden bestand (*.csv) + + + + Confirmed + Bevestigd + + + + Date + Datum + + + + Type + Type + + + + Label + Label + + + + Address + Adres + + + + Amount + Bedrag + + + + ID + ID + + + + Error exporting + Fout bij exporteren + + + + Could not write to file %1. + Kon niet schrijven naar bestand %1. + + + + Range: + Bereik: + + + + to + naar + + + + WalletModel + + + Send Coins + Verstuur munten + + + + WalletView + + + &Export + &Exporteer + + + + Export the data in the current tab to a file + Exporteer de data in de huidige tab naar een bestand + + + + Backup Wallet + Portomonnee backuppen + + + + Wallet Data (*.dat) + Portemonnee-data (*.dat) + + + + Backup Failed + Backup Mislukt + + + + There was an error trying to save the wallet data to the new location. + Er is een fout opgetreden bij het wegschrijven van de portemonnee-data naar de nieuwe locatie. + + + + Backup Successful + Backup Succesvol + + + + The wallet data was successfully saved to the new location. + De portemonneedata is succesvol opgeslagen op de nieuwe locatie. + + + + bitcoin-core + + + CryptoLottoToken version + CryptoLottoTokenversie + + + + Usage: + Gebruik: + + + + Send command to -server or CryptoLottoTokend + Stuur commando naar -server of CryptoLottoTokend + + + + List commands + Lijst van commando's + + + + Get help for a command + Toon hulp voor een commando + + + + Options: + Opties: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Specificeer configuratiebestand (standaard: CryptoLottoToken.conf) + + + + + Specify pid file (default: CryptoLottoTokend.pid) + Specificeer pid-bestand (standaard: CryptoLottoTokend.pid) + + + + + Specify data directory + Stel datamap in + + + + Set database cache size in megabytes (default: 25) + Stel databankcachegrootte in in megabytes (standaard: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Luister voor verbindingen op <poort> (standaard: 22556 of testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Onderhoud maximaal <n> verbindingen naar peers (standaard: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Verbind naar een node om adressen van anderen op te halen, en verbreek vervolgens de verbinding + + + + Specify your own public address + Specificeer uw eigen publieke adres + + + + Threshold for disconnecting misbehaving peers (default: 100) + Drempel om verbinding te verbreken naar zich misdragende peers (standaard: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Aantal seconden dat zich misdragende peers niet opnieuw mogen verbinden (standaard: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + Er is een fout opgetreden tijdens het instellen van de inkomende RPC-poort %u op IPv4: %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Wacht op JSON-RPC-connecties op poort <port> (standaard: 22555 of testnet: 44555) + + + + Accept command line and JSON-RPC commands + Aanvaard commandoregel- en JSON-RPC-commando's + + + + Run in the background as a daemon and accept commands + Draai in de achtergrond als daemon en aanvaard commando's + + + + Use the test network + Gebruik het testnetwerk + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Accepteer verbindingen van buitenaf (standaard: 1 als geen -proxy of -connect is opgegeven) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + %s, u moet een RPC-wachtwoord instellen in het configuratiebestand: %s +U wordt aangeraden het volgende willekeurige wachtwoord te gebruiken: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(u hoeft dit wachtwoord niet te onthouden) +De gebruikersnaam en wachtwoord mogen niet hetzelfde zijn. +Als het bestand niet bestaat, make hem dan aan met leesrechten voor enkel de eigenaar. +Het is ook aan te bevelen "alertnotify" in te stellen zodat u op de hoogte gesteld wordt van problemen; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + Er is een fout opgetreden tijdens het instellen van de inkomende RPC-poort %u op IPv6, terugval naar IPv4: %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + Bind aan opgegeven adres en luister er altijd op. Gebruik [host]:port notatie voor IPv6 + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + Kan geen lock op de datamap %s verkrijgen. CryptoLottoToken draait vermoedelijk reeds. + + + + 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. + Fout: De transactie was afgewezen! Dit kan gebeuren als sommige munten in uw portemonnee al eerder uitgegeven zijn, zoals wanneer u een kopie van uw wallet.dat heeft gebruikt en in de kopie deze munten zijn uitgegeven, maar in deze portemonnee die munten nog niet als zodanig zijn gemarkeerd. + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + Fout: Deze transactie vereist transactiekosten van tenminste %s, vanwege zijn grootte, complexiteit, of het gebruik van onlangs ontvangen munten! + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + Voer opdracht uit zodra een relevante melding ontvangen is (%s wordt in cmd vervangen door het bericht) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Voer opdracht uit zodra een portemonneetransactie verandert (%s in cmd wordt vervangen door TxID) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Stel maximumgrootte in in bytes voor hoge-prioriteits-/lage-transactiekosten-transacties (standaard: 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + Dit is een pre-release testversie - gebruik op eigen risico! Gebruik deze niet voor het delven van munten of handelsdoeleinden + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Waarschuwing: -paytxfee is zeer hoog ingesteld. Dit zijn de transactiekosten die u betaalt bij het versturen van een transactie. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + Waarschuwing: Weergegeven transacties zijn mogelijk niet correct! Mogelijk dient u te upgraden, of andere nodes dienen te upgraden. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Waarschuwing: Controleer dat de datum en tijd op uw computer correct zijn ingesteld. Als uw klok fout staat zal CryptoLottoToken niet correct werken. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Waarschuwing: Fout bij het lezen van wallet.dat! Alle sleutels zijn in goede orde uitgelezen, maar transactiedata of adresboeklemma's zouden kunnen ontbreken of fouten bevatten. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Waarschuwing: wallet.dat is corrupt, data is veiliggesteld! Originele wallet.dat is opgeslagen als wallet.{tijdstip}.bak in %s; als uw balans of transacties incorrect zijn dient u een backup terug te zetten. + + + + Attempt to recover private keys from a corrupt wallet.dat + Poog de geheime sleutels uit een corrupt wallet.dat bestand terug te halen + + + + Block creation options: + Blokcreatie-opties: + + + + Connect only to the specified node(s) + Verbind alleen naar de gespecificeerde node(s) + + + + Corrupted block database detected + Corrupte blokkendatabase gedetecteerd + + + + Discover own IP address (default: 1 when listening and no -externalip) + Ontdek eigen IP-adres (standaard: 1 als er wordt geluisterd en geen -externalip is opgegeven) + + + + Do you want to rebuild the block database now? + Wilt u de blokkendatabase nu herbouwen? + + + + Error initializing block database + Fout bij intialisatie blokkendatabase + + + + Error initializing wallet database environment %s! + Probleem met initializeren van de database-omgeving %s! + + + + Error loading block database + Fout bij het laden van blokkendatabase + + + + Error opening block database + Fout bij openen blokkendatabase + + + + Error: Disk space is low! + Fout: Weinig vrije diskruimte! + + + + Error: Wallet locked, unable to create transaction! + Fout: Portemonnee vergrendeld, aanmaak transactie niet mogelijk! + + + + Error: system error: + Fout: Systeemfout: + + + + Failed to listen on any port. Use -listen=0 if you want this. + Mislukt om op welke poort dan ook te luisteren. Gebruik -listen=0 as u dit wilt. + + + + Failed to read block info + Lezen van blokinformatie mislukt + + + + Failed to read block + Lezen van blok mislukt + + + + Failed to sync block index + Synchroniseren van blokindex mislukt + + + + Failed to write block index + Schrijven van blokindex mislukt + + + + Failed to write block info + Schrijven van blokinformatie mislukt + + + + Failed to write block + Schrijven van blok mislukt + + + + Failed to write file info + Schrijven van bestandsinformatie mislukt + + + + Failed to write to coin database + Schrijven naar coindatabase mislukt + + + + Failed to write transaction index + Schrijven van transactieindex mislukt + + + + Failed to write undo data + Schrijven van undo-data mislukt + + + + Find peers using DNS lookup (default: 1 unless -connect) + Vind andere nodes d.m.v. DNS-naslag (standaard: 1 tenzij -connect) + + + + Generate coins (default: 0) + Genereer munten (standaard: 0) + + + + How many blocks to check at startup (default: 288, 0 = all) + Aantal te checken blokken bij het opstarten (standaard: 288, 0 = allemaal) + + + + How thorough the block verification is (0-4, default: 3) + Hoe grondig de blokverificatie is (0-4, standaard: 3) + + + + Not enough file descriptors available. + Niet genoeg file descriptors beschikbaar. + + + + Rebuild block chain index from current blk000??.dat files + Blokketen opnieuw opbouwen van huidige blk000??.dat-bestanden + + + + Set the number of threads to service RPC calls (default: 4) + Stel het aantal threads in om RPC-aanvragen mee te bedienen (standaard: 4) + + + + Verifying blocks... + Blokken aan het controleren... + + + + Verifying wallet... + Portomonnee aan het controleren... + + + + Imports blocks from external blk000??.dat file + Importeert blokken van extern blk000??.dat bestand + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + Stel het aantal threads voor scriptverificatie in (max 16, 0 = auto, <0 = laat zoveel cores vrij, standaard: 0) + + + + Information + Informatie + + + + Invalid -tor address: '%s' + Ongeldig -tor adres: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + Ongeldig bedrag voor -minrelaytxfee=<bedrag>: '%s' + + + + Invalid amount for -mintxfee=<amount>: '%s' + Ongeldig bedrag voor -mintxfee=<bedrag>: '%s' + + + + Maintain a full transaction index (default: 0) + Onderhoud een volledige transactieindex (standaard: 0) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Maximum per-connectie ontvangstbuffer, <n>*1000 bytes (standaard: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Maximum per-connectie zendbuffer, <n>*1000 bytes (standaard: 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + Accepteer alleen blokketen die overeenkomt met de ingebouwde checkpoints (standaard: 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Verbind alleen naar nodes in netwerk <net> (IPv4, IPv6 of Tor) + + + + Output extra debugging information. Implies all other -debug* options + Output extra debugginginformatie. Impliceert alle andere -debug* opties + + + + Output extra network debugging information + Output extra netwerk-debugginginformatie + + + + Prepend debug output with timestamp (default: 1) + Voorzie de debuggingsuitvoer van een tijdsaanduiding + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + SSL-opties: (zie de CryptoLottoToken wiki voor SSL-instructies) + + + + Select the version of socks proxy to use (4-5, default: 5) + Selecteer de versie van de SOCKS-proxy om te gebruiken (4 of 5, standaard is 5) + + + + Send trace/debug info to console instead of debug.log file + Stuur trace/debug-info naar de console in plaats van het debug.log bestand + + + + Send trace/debug info to debugger + Stuur trace/debug-info naar debugger + + + + Set maximum block size in bytes (default: 250000) + Stel maximum blokgrootte in in bytes (standaard: 250000) + + + + Set minimum block size in bytes (default: 0) + Stel minimum blokgrootte in in bytes (standaard: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Verklein debug.log-bestand bij het opstarten van de client (standaard: 1 als geen -debug) + + + + Signing transaction failed + Ondertekenen van transactie mislukt + + + + Specify connection timeout in milliseconds (default: 5000) + Specificeer de time-outtijd in milliseconden (standaard: 5000) + + + + System error: + Systeemfout: + + + + Transaction amount too small + Transactiebedrag te klein + + + + Transaction amounts must be positive + Transactiebedragen moeten positief zijn + + + + Transaction too large + Transactie te groot + + + + Use UPnP to map the listening port (default: 0) + Gebruik UPnP om de luisterende poort te mappen (standaard: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Gebruik UPnP om de luisterende poort te mappen (standaard: 1 als er wordt geluisterd) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Gebruik proxy om 'tor hidden services' te bereiken (standaard: hetzelfde als -proxy) + + + + Username for JSON-RPC connections + Gebruikersnaam voor JSON-RPC-verbindingen + + + + Warning + Waarschuwing + + + + Warning: This version is obsolete, upgrade required! + Waarschuwing: Deze versie is verouderd, een upgrade is vereist! + + + + You need to rebuild the databases using -reindex to change -txindex + U moet de databases herbouwen met behulp van -reindex om -txindex te kunnen veranderen + + + + wallet.dat corrupt, salvage failed + wallet.dat corrupt, veiligstellen mislukt + + + + Password for JSON-RPC connections + Wachtwoord voor JSON-RPC-verbindingen + + + + Allow JSON-RPC connections from specified IP address + Sta JSON-RPC verbindingen van opgegeven IP-adres toe + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Verstuur commando's naar proces dat op <ip> draait (standaard: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Voer commando uit zodra het beste blok verandert (%s in cmd wordt vervangen door blockhash) + + + + Upgrade wallet to latest format + Vernieuw portemonnee naar nieuwste versie + + + + Set key pool size to <n> (default: 100) + Stel sleutelpoelgrootte in op <n> (standaard: 100) + + + + Rescan the block chain for missing wallet transactions + Doorzoek de blokketen op ontbrekende portemonnee-transacties + + + + Use OpenSSL (https) for JSON-RPC connections + Gebruik OpenSSL (https) voor JSON-RPC-verbindingen + + + + Server certificate file (default: server.cert) + Certificaat-bestand voor server (standaard: server.cert) + + + + Server private key (default: server.pem) + Geheime sleutel voor server (standaard: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Aanvaardbare ciphers (standaard: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Dit helpbericht + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Niet in staat om aan %s te binden op deze computer (bind gaf error %d, %s) + + + + Connect through socks proxy + Verbind via een socks-proxy + + + + Allow DNS lookups for -addnode, -seednode and -connect + Sta DNS-naslag toe voor -addnode, -seednode en -connect + + + + Loading addresses... + Adressen aan het laden... + + + + Error loading wallet.dat: Wallet corrupted + Fout bij laden wallet.dat: Portemonnee corrupt + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Fout bij laden wallet.dat: Portemonnee vereist een nieuwere versie van CryptoLottoToken + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Portemonnee moest herschreven worden: Herstart CryptoLottoToken om te voltooien + + + + Error loading wallet.dat + Fout bij laden wallet.dat + + + + Invalid -proxy address: '%s' + Ongeldig -proxy adres: '%s' + + + + Unknown network specified in -onlynet: '%s' + Onbekend netwerk gespecificeerd in -onlynet: '%s' + + + + Unknown -socks proxy version requested: %i + Onbekende -socks proxyversie aangegeven: %i + + + + Cannot resolve -bind address: '%s' + Kan -bind adres niet herleiden: '%s' + + + + Cannot resolve -externalip address: '%s' + Kan -externlip adres niet herleiden: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + Ongeldig bedrag voor -paytxfee=<bedrag>: '%s' + + + + Invalid amount + Ongeldig bedrag + + + + Insufficient funds + Ontoereikend saldo + + + + Loading block index... + Blokindex aan het laden... + + + + Add a node to connect to and attempt to keep the connection open + Voeg een node om naar te verbinden toe en probeer de verbinding open te houden + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Niet in staat om aan %s te binden op deze computer. CryptoLottoToken draait vermoedelijk reeds. + + + + Fee per KB to add to transactions you send + Kosten per KB om aan transacties toe te voegen die u verstuurt + + + + Loading wallet... + Portemonnee aan het laden... + + + + Cannot downgrade wallet + Kan portemonnee niet downgraden + + + + Cannot write default address + Kan standaardadres niet schrijven + + + + Rescanning... + Blokketen aan het doorzoeken... + + + + Done loading + Klaar met laden + + + + To use the %s option + Om de %s optie te gebruiken + + + + Error + Fout + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + U dient rpcpassword=<wachtwoord> in te stellen in het configuratiebestand: +%s +Als het bestand niet bestaat, maak het dan aan, met een alleen-lezen-permissie. + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_pl.qm b/src/qt/locale/bitcoin_pl.qm new file mode 100644 index 0000000..74a6d94 Binary files /dev/null and b/src/qt/locale/bitcoin_pl.qm differ diff --git a/src/qt/locale/bitcoin_pl.ts b/src/qt/locale/bitcoin_pl.ts new file mode 100644 index 0000000..7c7b583 --- /dev/null +++ b/src/qt/locale/bitcoin_pl.ts @@ -0,0 +1,2938 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + O CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + Wersja <b>CryptoLottoToken</b> + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +Oprogramowanie eksperymentalne. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + Copyright + Prawo autorskie + + + + The CryptoLottoToken developers + Deweloperzy CryptoLottoToken + + + + AddressBookPage + + + Address Book + Książka Adresowa + + + + Double-click to edit address or label + Kliknij dwukrotnie, aby edytować adres lub etykietę + + + + Create a new address + Utwórz nowy adres + + + + Copy the currently selected address to the system clipboard + Skopiuj aktualnie wybrany adres do schowka + + + + &New Address + &Nowy Adres + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Tutaj znajdują się twoje adresy CryptoLottoToken do odbioru płatności. Możesz nadać oddzielne adresy dla każdego z wysyłających monety, żeby śledzić oddzielnie ich opłaty. + + + + &Copy Address + &Kopiuj adres + + + + Show &QR Code + Pokaż Kod &QR + + + + Sign a message to prove you own a CryptoLottoToken address + Podpisz wiadomość aby dowieść, że ten adres jest twój + + + + Sign &Message + Podpisz wiado&mość + + + + Delete the currently selected address from the list + Usuń zaznaczony adres z listy + + + + Export the data in the current tab to a file + Eksportuj dane z aktywnej karty do pliku + + + + &Export + &Eksportuj + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Zweryfikuj wiadomość, aby upewnić się, że została podpisana odpowiednim adresem CryptoLottoToken. + + + + &Verify Message + &Zweryfikuj wiadomość + + + + &Delete + &Usuń + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + Tutaj znajdują się Twoje adresy CryptoLottoToken do wysyłania płatności. Zawsze sprawdzaj ilość i adres odbiorcy przed wysyłką monet. + + + + Copy &Label + Kopiuj &Etykietę + + + + &Edit + &Edytuj + + + + Send &Coins + Wyślij monety + + + + Export Address Book Data + Eksportuj książkę adresową + + + + Comma separated file (*.csv) + Plik *.CSV (rozdzielany przecinkami) + + + + Error exporting + Błąd podczas eksportowania + + + + Could not write to file %1. + Błąd zapisu do pliku %1. + + + + AddressTableModel + + + Label + Etykieta + + + + Address + Adres + + + + (no label) + (bez etykiety) + + + + AskPassphraseDialog + + + Passphrase Dialog + Okienko Hasła + + + + Enter passphrase + Wpisz hasło + + + + New passphrase + Nowe hasło + + + + Repeat new passphrase + Powtórz nowe hasło + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Wprowadź nowe hasło dla portfela.<br/>Proszę użyć hasła składającego się z <b>10 lub więcej losowych znaków</b> lub <b>ośmiu lub więcej słów</b>. + + + + Encrypt wallet + Zaszyfruj portfel + + + + This operation needs your wallet passphrase to unlock the wallet. + Ta operacja wymaga hasła do portfela ażeby odblokować portfel. + + + + Unlock wallet + Odblokuj portfel + + + + This operation needs your wallet passphrase to decrypt the wallet. + Ta operacja wymaga hasła do portfela ażeby odszyfrować portfel. + + + + Decrypt wallet + Odszyfruj portfel + + + + Change passphrase + Zmień hasło + + + + Enter the old and new passphrase to the wallet. + Podaj stare i nowe hasło do portfela. + + + + Confirm wallet encryption + Potwierdź szyfrowanie portfela + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Uwaga: Jeśli zaszyfrujesz swój portfel i zgubisz hasło to <b>STRACISZ WSZYSTKIE SWOJE CryptoLottoToken'Y</b>! + + + + Are you sure you wish to encrypt your wallet? + Jesteś pewien, że chcesz zaszyfrować swój portfel? + + + + 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. + WAŻNE: Wszystkie wykonane wcześniej kopie pliku portfela powinny być zamienione na nowe, szyfrowane pliki. Z powodów bezpieczeństwa, poprzednie kopie nieszyfrowanych plików portfela staną się bezużyteczne jak tylko zaczniesz korzystać z nowego, szyfrowanego portfela. + + + + + Warning: The Caps Lock key is on! + Uwaga: Klawisz Caps Lock jest włączony + + + + + Wallet encrypted + Portfel zaszyfrowany + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + Program CryptoLottoToken zamknie się aby dokończyć proces szyfrowania. Pamiętaj, że szyfrowanie portfela nie zabezpiecza w pełni Twoich CryptoLottoTokenów przed kradzieżą przez wirusy lub trojany mogące zainfekować Twój komputer. + + + + + + + Wallet encryption failed + Szyfrowanie portfela nie powiodło się + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Szyfrowanie portfela nie powiodło się z powodu wewnętrznego błędu. Twój portfel nie został zaszyfrowany. + + + + + The supplied passphrases do not match. + Podane hasła nie są takie same. + + + + Wallet unlock failed + Odblokowanie portfela nie powiodło się + + + + + + The passphrase entered for the wallet decryption was incorrect. + Wprowadzone hasło do odszyfrowania portfela jest niepoprawne. + + + + Wallet decryption failed + Odszyfrowywanie portfela nie powiodło się + + + + Wallet passphrase was successfully changed. + Hasło portfela zostało pomyślnie zmienione. + + + + BitcoinGUI + + + Sign &message... + Podpisz wiado&mość... + + + + Synchronizing with network... + Synchronizacja z siecią... + + + + &Overview + P&odsumowanie + + + + Show general overview of wallet + Pokazuje ogólny zarys portfela + + + + &Transactions + &Transakcje + + + + Browse transaction history + Przeglądaj historię transakcji + + + + Edit the list of stored addresses and labels for sending + Edytuj listę zapisanych adresów i i etykiet + + + + Show the list of addresses for receiving payments + Pokaż listę adresów do otrzymywania płatności + + + + E&xit + &Zakończ + + + + Quit application + Zamknij program + + + + Show information about CryptoLottoToken + Pokaż informację o CryptoLottoToken + + + + About &Qt + O &Qt + + + + Show information about Qt + Pokazuje informacje o Qt + + + + &Options... + &Opcje... + + + + &Encrypt Wallet... + Zaszyfruj Portf&el + + + + &Backup Wallet... + Wykonaj kopię zapasową... + + + + &Change Passphrase... + &Zmień hasło... + + + + Importing blocks from disk... + Importowanie bloków z dysku... + + + + Reindexing blocks on disk... + Ponowne indeksowanie bloków na dysku... + + + + Send coins to a CryptoLottoToken address + Wyślij monety na adres CryptoLottoToken + + + + Modify configuration options for CryptoLottoToken + Zmienia opcje konfiguracji CryptoLottoTokena + + + + Backup wallet to another location + Zapasowy portfel w innej lokalizacji + + + + Change the passphrase used for wallet encryption + Zmień hasło użyte do szyfrowania portfela + + + + &Debug window + &Okno debudowania + + + + Open debugging and diagnostic console + Otwórz konsolę debugowania i diagnostyki + + + + &Verify message... + &Zweryfikuj wiadomość... + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Portfel + + + + &Send + Wyślij + + + + &Receive + Odbie&rz + + + + &Addresses + &Adresy + + + + &About CryptoLottoToken + O CryptoLottoToken + + + + &Show / Hide + &Pokaż / Ukryj + + + + Show or hide the main Window + Pokazuje lub ukrywa główne okno + + + + Encrypt the private keys that belong to your wallet + Szyfruj klucze prywatne, które są powiązane z twoim portfelem + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Podpisz wiadomości swoim adresem aby udowodnić jego posiadanie + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Zweryfikuj wiadomość, aby upewnić się, że została podpisana odpowiednim adresem CryptoLottoToken. + + + + &File + &Plik + + + + &Settings + P&referencje + + + + &Help + Pomo&c + + + + Tabs toolbar + Pasek zakładek + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + CryptoLottoToken klient + + + + %n active connection(s) to CryptoLottoToken network + %n aktywne połączenie do sieci CryptoLottoToken%n aktywne połączenia do sieci CryptoLottoToken%n aktywnych połączeń do sieci CryptoLottoToken + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + Przetworzono (w przybliżeniu) %1 z %2 bloków historii transakcji. + + + + Processed %1 blocks of transaction history. + Pobrano %1 bloków z historią transakcji. + + + + %n hour(s) + %n godzina%n godzin%n godzin + + + + %n day(s) + %n dzień%n dni%n dni + + + + %n week(s) + %n tydzień%n tygodni%n tygodni + + + + %1 behind + + + + + Last received block was generated %1 ago. + Ostatni otrzymany blok został wygenerowany %1 temu. + + + + Transactions after this will not yet be visible. + + + + + Error + Błąd + + + + Warning + Ostrzeżenie + + + + Information + Informacja + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + Transakcja przekracza limit. Możesz wysłać ją płacąc prowizję %1, która zostaje przekazana do węzłów, które ją prześlą i pomoże wspierać sieć CryptoLottoToken. Czy chcesz zapłacić prowizję? + + + + Up to date + Aktualny + + + + Catching up... + Łapanie bloków... + + + + Confirm transaction fee + Potwierdź prowizję transakcyjną + + + + Sent transaction + Transakcja wysłana + + + + Incoming transaction + Transakcja przychodząca + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Data: %1 +Kwota: %2 +Typ: %3 +Adres: %4 + + + + + + URI handling + Obsługa URI + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + URI nie może zostać przetworzony! Prawdopodobnie błędny adres CryptoLottoToken bądź nieprawidłowe parametry URI. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Portfel jest <b>zaszyfrowany</b> i obecnie <b>niezablokowany</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Portfel jest <b>zaszyfrowany</b> i obecnie <b>zablokowany</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + Błąd krytyczny. CryptoLottoToken nie może kontynuować bezpiecznie więc zostanie zamknięty. + + + + ClientModel + + + Network Alert + Sieć Alert + + + + EditAddressDialog + + + Edit Address + Edytuj adres + + + + &Label + &Etykieta + + + + The label associated with this address book entry + Etykieta skojarzona z tym wpisem w książce adresowej + + + + &Address + &Adres + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Ten adres jest skojarzony z wpisem w książce adresowej. Może być zmodyfikowany jedynie dla adresów wysyłających. + + + + New receiving address + Nowy adres odbiorczy + + + + New sending address + Nowy adres wysyłania + + + + Edit receiving address + Edytuj adres odbioru + + + + Edit sending address + Edytuj adres wysyłania + + + + The entered address "%1" is already in the address book. + Wprowadzony adres "%1" już istnieje w książce adresowej. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + Wprowadzony adres "%1" nie jest poprawnym adresem CryptoLottoToken. + + + + Could not unlock wallet. + Nie można było odblokować portfela. + + + + New key generation failed. + Tworzenie nowego klucza nie powiodło się. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + wersja + + + + Usage: + Użycie: + + + + command-line options + opcje konsoli + + + + UI options + UI opcje + + + + Set language, for example "de_DE" (default: system locale) + Ustaw Język, na przykład "pl_PL" (domyślnie: systemowy) + + + + Start minimized + Uruchom zminimalizowany + + + + Show splash screen on startup (default: 1) + Pokazuj okno powitalne przy starcie (domyślnie: 1) + + + + OptionsDialog + + + Options + Opcje + + + + &Main + Główne + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + Płać prowizję za transakcje + + + + Automatically start CryptoLottoToken after logging in to the system. + Automatycznie uruchamia CryptoLottoToken po zalogowaniu do systemu. + + + + &Start CryptoLottoToken on system login + Uruchamiaj CryptoLottoToken wraz z zalogowaniem do &systemu + + + + Reset all client options to default. + Przywróć domyślne wszystkie ustawienia klienta. + + + + &Reset Options + Z&resetuj Ustawienia + + + + &Network + &Sieć + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Automatycznie otwiera port klienta CryptoLottoToken na routerze. Ta opcja dzieła tylko jeśli twój router wspiera UPnP i jest ono włączone. + + + + Map port using &UPnP + Mapuj port używając &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Podłącz się do sieci CryptoLottoToken przez proxy SOCKS (np. gdy łączysz się poprzez Tor'a) + + + + &Connect through SOCKS proxy: + &Połącz się przez proxy SOCKS + + + + Proxy &IP: + Proxy &IP: + + + + IP address of the proxy (e.g. 127.0.0.1) + Adres IP serwera proxy (np. 127.0.0.1) + + + + &Port: + &Port: + + + + Port of the proxy (e.g. 9050) + Port proxy (np. 9050) + + + + SOCKS &Version: + Wersja &SOCKS + + + + SOCKS version of the proxy (e.g. 5) + SOCKS wersja serwera proxy (np. 5) + + + + &Window + &Okno + + + + Show only a tray icon after minimizing the window. + Pokazuj tylko ikonę przy zegarku po zminimalizowaniu okna. + + + + &Minimize to the tray instead of the taskbar + &Minimalizuj do paska przy zegarku zamiast do paska zadań + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Minimalizuje zamiast zakończyć działanie programu przy zamykaniu okna. Kiedy ta opcja jest włączona, program zakończy działanie po wybieraniu Zamknij w menu. + + + + M&inimize on close + M&inimalizuj przy zamknięciu + + + + &Display + &Wyświetlanie + + + + User Interface &language: + Język &Użytkownika: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + Można tu ustawić język interfejsu uzytkownika. Żeby ustawienie przyniosło skutek trzeba uruchomić ponownie CryptoLottoToken. + + + + &Unit to show amounts in: + &Jednostka pokazywana przy kwocie: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Wybierz podział jednostki pokazywany w interfejsie oraz podczas wysyłania monet + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Pokazuj adresy CryptoLottoToken na liście transakcji. + + + + &Display addresses in transaction list + &Wyświetlaj adresy w liście transakcji + + + + &OK + &OK + + + + &Cancel + &Anuluj + + + + &Apply + Z&astosuj + + + + default + domyślny + + + + Confirm options reset + Potwierdź reset ustawień + + + + Some settings may require a client restart to take effect. + Niektóre ustawienia mogą wymagać ponownego uruchomienia klienta, żeby zacząć działać. + + + + Do you want to proceed? + Czy chcesz kontynuować? + + + + + Warning + Ostrzeżenie + + + + + This setting will take effect after restarting CryptoLottoToken. + To ustawienie zostanie zastosowane po restarcie CryptoLottoToken + + + + The supplied proxy address is invalid. + Adres podanego proxy jest nieprawidłowy + + + + OverviewPage + + + Form + Formularz + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + Wyświetlana informacja może być nieaktualna. Twój portfel synchronizuje się automatycznie z siecią CryptoLottoToken, zaraz po tym jak uzyskano połączenie, ale proces ten nie został jeszcze ukończony. + + + + Balance: + Saldo: + + + + Unconfirmed: + Niepotwierdzony: + + + + Wallet + Portfel + + + + Immature: + Niedojrzały: + + + + Mined balance that has not yet matured + Balans wydobycia, który jeszcze nie dojrzał + + + + <b>Recent transactions</b> + <b>Ostatnie transakcje</b> + + + + Your current balance + Twoje obecne saldo + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Suma transakcji, które nie zostały jeszcze potwierdzone, i które nie zostały wliczone do twojego obecnego salda + + + + + out of sync + desynchronizacja + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + Okno Dialogowe Kodu QR + + + + Request Payment + Prośba o płatność + + + + Amount: + Kwota: + + + + Label: + Etykieta: + + + + Message: + Wiadomość: + + + + &Save As... + Zapi&sz jako... + + + + Error encoding URI into QR Code. + Błąd kodowania URI w Kodzie QR. + + + + The entered amount is invalid, please check. + Podana ilość jest nieprawidłowa, proszę sprawdzić + + + + Resulting URI too long, try to reduce the text for label / message. + Wynikowy URI jest zbyt długi, spróbuj zmniejszyć tekst etykiety / wiadomości + + + + Save QR Code + Zapisz Kod QR + + + + PNG Images (*.png) + Obraz PNG (*.png) + + + + RPCConsole + + + Client name + Nazwa klienta + + + + + + + + + + + + + N/A + NIEDOSTĘPNE + + + + Client version + Wersja klienta + + + + &Information + &Informacje + + + + Using OpenSSL version + Używana wersja OpenSSL + + + + Startup time + Czas uruchomienia + + + + Network + Sieć + + + + Number of connections + Liczba połączeń + + + + On testnet + W sieci testowej + + + + Block chain + Ciąg bloków + + + + Current number of blocks + Aktualna liczba bloków + + + + Estimated total blocks + Szacowana ilość bloków + + + + Last block time + Czas ostatniego bloku + + + + &Open + &Otwórz + + + + Command-line options + Opcje konsoli + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Pokaż pomoc CryptoLottoToken-Qt, aby zobaczyć listę wszystkich opcji linii poleceń + + + + &Show + &Pokaż + + + + &Console + &Konsola + + + + Build date + Data kompilacji + + + + CryptoLottoToken - Debug window + CryptoLottoToken - Okno debudowania + + + + CryptoLottoToken Core + Rdzeń BitCoin + + + + Debug log file + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + Wyczyść konsolę + + + + Welcome to the CryptoLottoToken RPC console. + Witam w konsoli CryptoLottoToken RPC + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Użyj strzałek do przewijania historii i <b>Ctrl-L</b> aby wyczyścić ekran + + + + Type <b>help</b> for an overview of available commands. + Wpisz <b>help</b> aby uzyskać listę dostępnych komend + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Wyślij płatność + + + + Send to multiple recipients at once + Wyślij do wielu odbiorców na raz + + + + Add &Recipient + Dodaj Odbio&rce + + + + Remove all transaction fields + Wyczyść wszystkie pola transakcji + + + + Clear &All + Wyczyść &wszystko + + + + Balance: + Saldo: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Potwierdź akcję wysyłania + + + + S&end + Wy&syłka + + + + <b>%1</b> to %2 (%3) + <b>%1</b> do %2 (%3) + + + + Confirm send coins + Potwierdź wysyłanie monet + + + + Are you sure you want to send %1? + Czy na pewno chcesz wysłać %1? + + + + and + i + + + + The recipient address is not valid, please recheck. + Adres odbiorcy jest nieprawidłowy, proszę poprawić + + + + The amount to pay must be larger than 0. + Kwota do zapłacenia musi być większa od 0. + + + + The amount exceeds your balance. + Kwota przekracza twoje saldo. + + + + The total exceeds your balance when the %1 transaction fee is included. + Suma przekracza twoje saldo, gdy doliczymy %1 prowizji transakcyjnej. + + + + Duplicate address found, can only send to each address once per send operation. + Znaleziono powtórzony adres, można wysłać tylko raz na każdy adres podczas operacji wysyłania. + + + + Error: Transaction creation failed! + Błąd: Tworzenie transakcji zakończone niepowodzeniem! + + + + 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. + Błąd: transakcja została odrzucona. Może się to zdarzyć, gdy monety z Twojego portfela zostały już wydane, na przykład gdy używałeś kopii wallet.dat i CryptoLottoTokeny które tam wydałeś nie zostały jeszcze odjęte z portfela z którego teraz korzystasz. + + + + SendCoinsEntry + + + Form + Formularz + + + + A&mount: + Su&ma: + + + + Pay &To: + Zapłać dla: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Adres, na który wysłasz płatności (np. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + Wprowadź etykietę dla tego adresu by dodać go do książki adresowej + + + + &Label: + &Etykieta: + + + + Choose address from address book + Wybierz adres z książki adresowej + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Wklej adres ze schowka + + + + Alt+P + Alt+P + + + + Remove this recipient + Usuń tego odbiorce + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Wprowadź adres CryptoLottoToken (np. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Podpisy - Podpisz / zweryfikuj wiadomość + + + + &Sign Message + Podpi&sz Wiadomość + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Możesz podpisywać wiadomości swoimi adresami aby udowodnić, że jesteś ich właścicielem. Uważaj, aby nie podpisywać niczego co wzbudza Twoje podejrzenia, ponieważ ktoś może stosować phishing próbując nakłonić Cię do ich podpisania. Akceptuj i podpisuj tylko w pełni zrozumiałe komunikaty i wiadomości. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Wprowadź adres CryptoLottoToken (np. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Wybierz adres z książki kontaktowej + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Wklej adres ze schowka + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Wprowadź wiadomość, którą chcesz podpisać, tutaj + + + + Signature + Podpis + + + + Copy the current signature to the system clipboard + Kopiuje aktualny podpis do schowka systemowego + + + + Sign the message to prove you own this CryptoLottoToken address + Podpisz wiadomość aby dowieść, że ten adres jest twój + + + + Sign &Message + Podpisz Wiado&mość + + + + Reset all sign message fields + Zresetuj wszystkie pola podpisanej wiadomości + + + + + Clear &All + Wyczyść &wszystko + + + + &Verify Message + &Zweryfikuj wiadomość + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Wprowadź adres CryptoLottoToken (np. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Zweryfikuj wiadomość, aby upewnić się, że została podpisana odpowiednim adresem CryptoLottoToken. + + + + Verify &Message + Zweryfikuj Wiado&mość + + + + Reset all verify message fields + Resetuje wszystkie pola weryfikacji wiadomości + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Wprowadź adres CryptoLottoToken (np. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Kliknij "Podpisz Wiadomość" żeby uzyskać podpis + + + + Enter CryptoLottoToken signature + Wprowadź podpis CryptoLottoToken + + + + + The entered address is invalid. + Podany adres jest nieprawidłowy. + + + + + + + Please check the address and try again. + Proszę sprawdzić adres i spróbować ponownie. + + + + + The entered address does not refer to a key. + Wprowadzony adres nie odnosi się do klucza. + + + + Wallet unlock was cancelled. + Odblokowanie portfela zostało anulowane. + + + + Private key for the entered address is not available. + Klucz prywatny dla podanego adresu nie jest dostępny + + + + Message signing failed. + Podpisanie wiadomości nie powiodło się + + + + Message signed. + Wiadomość podpisana. + + + + The signature could not be decoded. + Podpis nie może zostać zdekodowany. + + + + + Please check the signature and try again. + Sprawdź podpis i spróbuj ponownie. + + + + The signature did not match the message digest. + + + + + Message verification failed. + Weryfikacja wiadomości nie powiodła się. + + + + Message verified. + Wiadomość zweryfikowana. + + + + SplashScreen + + + The CryptoLottoToken developers + Deweloperzy CryptoLottoToken + + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + Otwórz do %1 + + + + %1/offline + %1/offline + + + + %1/unconfirmed + %1/niezatwierdzone + + + + %1 confirmations + %1 potwierdzeń + + + + Status + Status + + + + , broadcast through %n node(s) + , emitowany przez %n węzeł, emitowany przez %n węzły, emitowany przez %n węzłów + + + + Date + Data + + + + Source + Źródło + + + + Generated + Wygenerowano + + + + + From + Od + + + + + + To + Do + + + + + own address + własny adres + + + + label + etykieta + + + + + + + + Credit + Przypisy + + + + matures in %n more block(s) + + + + + not accepted + niezaakceptowane + + + + + + + Debit + Debet + + + + Transaction fee + Prowizja transakcji + + + + Net amount + Kwota netto + + + + Message + Wiadomość + + + + Comment + Komentarz + + + + Transaction ID + ID transakcji + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Wygenerowane monety muszą zaczekać 120 bloków zanim będzie można je wydać. Kiedy wygenerowałeś ten blok, został on wyemitowany do sieci, aby dodać go do łańcucha bloków. Jeśli to się nie powiedzie nie zostanie on zaakceptowany i wygenerowanych monet nie będzie można wysyłać. Może się to czasami zdarzyć jeśli inny węzeł wygeneruje blok tuż przed tobą. + + + + Debug information + Informacje debugowania + + + + Transaction + Transakcja + + + + Inputs + Wejścia + + + + Amount + Kwota + + + + true + prawda + + + + false + fałsz + + + + , has not been successfully broadcast yet + , nie został jeszcze pomyślnie wyemitowany + + + + Open for %n more block(s) + Otwórz dla %n blokuOtwórz dla %n następnych blokówOtwórz dla %n następnych bloków + + + + unknown + nieznany + + + + TransactionDescDialog + + + Transaction details + Szczegóły transakcji + + + + This pane shows a detailed description of the transaction + Ten panel pokazuje szczegółowy opis transakcji + + + + TransactionTableModel + + + Date + Data + + + + Type + Typ + + + + Address + Adres + + + + Amount + Kwota + + + + Open for %n more block(s) + Otwórz dla %n następnych bloków + + + + Open until %1 + Otwórz do %1 + + + + Offline (%1 confirmations) + Offline (%1 potwierdzeń) + + + + Unconfirmed (%1 of %2 confirmations) + Niezatwierdzony (%1 z %2 potwierdzeń) + + + + Confirmed (%1 confirmations) + Zatwierdzony (%1 potwierdzeń) + + + + Mined balance will be available when it matures in %n more block(s) + Balans wydobycia będzie dostępny zaraz po tym, jak dojrzeje. Pozostał %n blokBalans wydobycia będzie dostępny zaraz po tym, jak dojrzeje. Pozostało %n blokówBalans wydobycia będzie dostępny zaraz po tym, jak dojrzeje. Pozostało %n bloków + + + + This block was not received by any other nodes and will probably not be accepted! + Ten blok nie został odebrany przez jakikolwiek inny węzeł i prawdopodobnie nie zostanie zaakceptowany! + + + + Generated but not accepted + Wygenerowano ale nie zaakceptowano + + + + Received with + Otrzymane przez + + + + Received from + Odebrano od + + + + Sent to + Wysłano do + + + + Payment to yourself + Płatność do siebie + + + + Mined + Wydobyto + + + + (n/a) + (brak) + + + + Transaction status. Hover over this field to show number of confirmations. + Status transakcji. Najedź na pole, aby zobaczyć liczbę potwierdzeń. + + + + Date and time that the transaction was received. + Data i czas odebrania transakcji. + + + + Type of transaction. + Rodzaj transakcji. + + + + Destination address of transaction. + Adres docelowy transakcji. + + + + Amount removed from or added to balance. + Kwota usunięta z lub dodana do konta. + + + + TransactionView + + + + All + Wszystko + + + + Today + Dzisiaj + + + + This week + W tym tygodniu + + + + This month + W tym miesiącu + + + + Last month + W zeszłym miesiącu + + + + This year + W tym roku + + + + Range... + Zakres... + + + + Received with + Otrzymane przez + + + + Sent to + Wysłano do + + + + To yourself + Do siebie + + + + Mined + Wydobyto + + + + Other + Inne + + + + Enter address or label to search + Wprowadź adres albo etykietę żeby wyszukać + + + + Min amount + Min suma + + + + Copy address + Kopiuj adres + + + + Copy label + Kopiuj etykietę + + + + Copy amount + Kopiuj kwotę + + + + Copy transaction ID + Skopiuj ID transakcji + + + + Edit label + Edytuj etykietę + + + + Show transaction details + Pokaż szczegóły transakcji + + + + Export Transaction Data + Eksportuj Dane Transakcyjne + + + + Comma separated file (*.csv) + CSV (rozdzielany przecinkami) + + + + Confirmed + Potwierdzony + + + + Date + Data + + + + Type + Typ + + + + Label + Etykieta + + + + Address + Adres + + + + Amount + Kwota + + + + ID + ID + + + + Error exporting + Błąd podczas eksportowania + + + + Could not write to file %1. + Błąd zapisu do pliku %1. + + + + Range: + Zakres: + + + + to + do + + + + WalletModel + + + Send Coins + Wyślij płatność + + + + WalletView + + + &Export + &Eksportuj + + + + Export the data in the current tab to a file + Eksportuj dane z aktywnej karty do pliku + + + + Backup Wallet + Kopia Zapasowa Portfela + + + + Wallet Data (*.dat) + Dane Portfela (*.dat) + + + + Backup Failed + Nie udało się wykonać kopii zapasowej + + + + There was an error trying to save the wallet data to the new location. + Wystąpił błąd przy zapisywaniu portfela do nowej lokalizacji. + + + + Backup Successful + Wykonano Kopię Zapasową + + + + The wallet data was successfully saved to the new location. + Dane portfela zostały poprawnie zapisane w nowym miejscu. + + + + bitcoin-core + + + CryptoLottoToken version + Wersja CryptoLottoToken + + + + Usage: + Użycie: + + + + Send command to -server or CryptoLottoTokend + Wyślij polecenie do -server lub CryptoLottoTokend + + + + List commands + Lista poleceń + + + + Get help for a command + Uzyskaj pomoc do polecenia + + + + Options: + Opcje: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Wskaż plik konfiguracyjny (domyślnie: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + Wskaż plik pid (domyślnie: CryptoLottoToken.pid) + + + + Specify data directory + Wskaż folder danych + + + + Set database cache size in megabytes (default: 25) + Ustaw rozmiar w megabajtach cache-u bazy danych (domyślnie: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Nasłuchuj połączeń na <port> (domyślnie: 22556 lub testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Utrzymuj maksymalnie <n> połączeń z peerami (domyślnie: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Podłącz się do węzła aby otrzymać adresy peerów i rozłącz + + + + Specify your own public address + Podaj swój publiczny adres + + + + Threshold for disconnecting misbehaving peers (default: 100) + Próg po którym nastąpi rozłączenie nietrzymających się zasad peerów (domyślnie: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Czas w sekundach, przez jaki nietrzymający się zasad peerzy nie będą mogli ponownie się podłączyć (domyślnie: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + Wystąpił błąd podczas ustawiania portu RPC %u w tryb nasłuchu: %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Nasłuchuj połączeń JSON-RPC na <port> (domyślnie: 22555 or testnet: 44555) + + + + Accept command line and JSON-RPC commands + Akceptuj linię poleceń oraz polecenia JSON-RPC + + + + Run in the background as a daemon and accept commands + Uruchom w tle jako daemon i przyjmuj polecenia + + + + Use the test network + Użyj sieci testowej + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Akceptuj połączenia z zewnątrz (domyślnie: 1 jeśli nie ustawiono -proxy lub -connect) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + %s, musisz ustawić rpcpassword w pliku konfiguracyjnym:⏎ +%s⏎ +Zalecane jest użycie losowego hasła:⏎ +rpcuser=CryptoLottoTokenrpc⏎ +rpcpassword=%s⏎ +(nie musisz pamiętać tego hasła)⏎ +Użytkownik i hasło nie mogą być takie same.⏎ +Jeśli plik nie istnieje, utwórz go z uprawnieniami tylko-do-odczytu dla właściciela.⏎ +Zalecane jest ustawienie alertnotify aby poinformować o problemach:⏎ +na przykład: alertnotify=echo %%s | mail -s "Alarm CryptoLottoToken" admin@foo.com⏎ + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + Wystąpił błąd podczas ustawiania portu RPC %u w tryb nasłuchu dla IPv6, korzystam z IPv4: %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + Skojarz z podanym adresem. Użyj formatu [host]:port dla IPv6 + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + Nie można zablokować folderu danych %s. CryptoLottoToken prawdopodobnie już działa. + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + Uruchom polecenie przy otrzymaniu odpowiedniego powiadomienia (%s w poleceniu jest podstawiane za komunikat) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Wykonaj polecenie, kiedy transakcja portfela ulegnie zmianie (%s w poleceniu zostanie zastąpione przez TxID) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Ustaw maksymalny rozmiar transakcji o wysokim priorytecie/niskiej prowizji w bajtach (domyślnie: 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Ostrzeżenie: -paytxfee jest bardzo duży. To jest prowizja za transakcje, którą płacisz, gdy wysyłasz monety. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + Uwaga: Wyświetlone transakcje mogą nie być poprawne! Możliwe, że potrzebujesz aktualizacji bądź inne węzły jej potrzebują + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Uwaga: Sprawdź czy data i czas na Twoim komputerze są prawidłowe! Jeśli nie to CryptoLottoToken nie będzie działał prawidłowo. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Ostrzeżenie: błąd odczytu wallet.dat! Wszystkie klucze zostały odczytane, ale może brakować pewnych danych transakcji lub wpisów w książce adresowej lub mogą one być nieprawidłowe. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Ostrzeżenie: Odtworzono dane z uszkodzonego pliku wallet.dat! Oryginalny wallet.dat został zapisany jako wallet.{timestamp}.bak w %s; jeśli twoje saldo lub transakcje są niepoprawne powinieneś odtworzyć kopię zapasową. + + + + Attempt to recover private keys from a corrupt wallet.dat + Próbuj odzyskać klucze prywatne z uszkodzonego wallet.dat + + + + Block creation options: + Opcje tworzenia bloku: + + + + Connect only to the specified node(s) + Łącz tylko do wskazanego węzła + + + + Corrupted block database detected + Wykryto uszkodzoną bazę bloków + + + + Discover own IP address (default: 1 when listening and no -externalip) + Odkryj własny adres IP (domyślnie: 1 kiedy w trybie nasłuchu i brak -externalip ) + + + + Do you want to rebuild the block database now? + Czy chcesz teraz przebudować bazę bloków? + + + + Error initializing block database + Błąd inicjowania bloku bazy danych + + + + Error initializing wallet database environment %s! + Błąd inicjowania środowiska bazy portfela %s! + + + + Error loading block database + Błąd ładowania bazy bloków + + + + Error opening block database + Błąd ładowania bazy bloków + + + + Error: Disk space is low! + Błąd: Mało miejsca na dysku! + + + + Error: Wallet locked, unable to create transaction! + Błąd: Zablokowany portfel, nie można utworzyć transakcji! + + + + Error: system error: + Błąd: błąd systemu: + + + + Failed to listen on any port. Use -listen=0 if you want this. + Próba otwarcia jakiegokolwiek portu nie powiodła się. Użyj -listen=0 jeśli tego chcesz. + + + + Failed to read block info + Nie udało się odczytać informacji bloku + + + + Failed to read block + Nie udało się odczytać bloku. + + + + Failed to sync block index + Nie udało się zsynchronizować indeksu bloków. + + + + Failed to write block index + Nie udało się zapisać indeksu bloków. + + + + Failed to write block info + Nie udało się zapisać informacji bloku + + + + Failed to write block + Nie udało się zapisać bloku + + + + Failed to write file info + + + + + Failed to write to coin database + Nie udało się zapisać do bazy monet + + + + Failed to write transaction index + Nie udało się zapisać indeksu transakcji + + + + Failed to write undo data + Nie udało się zapisać danych odtwarzających + + + + Find peers using DNS lookup (default: 1 unless -connect) + Wyszukaj połączenia wykorzystując zapytanie DNS (domyślnie 1 jeśli nie użyto -connect) + + + + Generate coins (default: 0) + Generuj monety (domyślnie: 0) + + + + How many blocks to check at startup (default: 288, 0 = all) + Ile bloków sprawdzić przy starcie (domyślnie: 288, 0 = wszystkie) + + + + How thorough the block verification is (0-4, default: 3) + Jak dokładna jest weryfikacja bloku (0-4, domyślnie: 3) + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + Odbuduj indeks łańcucha bloków z obecnych plików blk000??.dat + + + + Set the number of threads to service RPC calls (default: 4) + Ustaw liczbę wątków do odwołań RPC (domyślnie: 4) + + + + Verifying blocks... + Weryfikacja bloków... + + + + Verifying wallet... + Weryfikacja portfela... + + + + Imports blocks from external blk000??.dat file + Importuj bloki z zewnętrznego pliku blk000??.dat + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + Ustaw liczbę wątków skryptu weryfikacji (do 16, 0 = auto, <0 = zostawia taką ilość rdzenie wolnych, domyślnie: 0) + + + + Information + Informacja + + + + Invalid -tor address: '%s' + Nieprawidłowy adres -tor: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + Utrzymuj pełen indeks transakcji (domyślnie: 0) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Maksymalny bufor odbioru na połączenie, <n>*1000 bajtów (domyślnie: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Maksymalny bufor wysyłu na połączenie, <n>*1000 bajtów (domyślnie: 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + Akceptuj tylko łańcuch bloków zgodny z wbudowanymi punktami kontrolnymi (domyślnie: 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Łącz z węzłami tylko w sieci <net> (IPv4, IPv6 lub Tor) + + + + Output extra debugging information. Implies all other -debug* options + + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + Poprzedź informacje debugowania znacznikiem czasowym + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + Opcje SSL: (odwiedź CryptoLottoToken Wiki w celu uzyskania instrukcji) + + + + Select the version of socks proxy to use (4-5, default: 5) + Wybierz używaną wersje socks serwera proxy (4-5, domyślnie:5) + + + + Send trace/debug info to console instead of debug.log file + Wyślij informację/raport do konsoli zamiast do pliku debug.log. + + + + Send trace/debug info to debugger + Wyślij informację/raport do debuggera. + + + + Set maximum block size in bytes (default: 250000) + Ustaw maksymalny rozmiar bloku w bajtach (domyślnie: 250000) + + + + Set minimum block size in bytes (default: 0) + Ustaw minimalny rozmiar bloku w bajtach (domyślnie: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Zmniejsz plik debug.log przy starcie programu (domyślnie: 1 jeśli nie użyto -debug) + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + Wskaż czas oczekiwania bezczynności połączenia w milisekundach (domyślnie: 5000) + + + + System error: + Błąd systemu: + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + Używaj UPnP do mapowania portu nasłuchu (domyślnie: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Używaj UPnP do mapowania portu nasłuchu (domyślnie: 1 gdy nasłuchuje) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + Nazwa użytkownika dla połączeń JSON-RPC + + + + Warning + Ostrzeżenie + + + + Warning: This version is obsolete, upgrade required! + Uwaga: Ta wersja jest przestarzała, aktualizacja wymagana! + + + + You need to rebuild the databases using -reindex to change -txindex + Musisz przebudować bazę używając parametru -reindex aby zmienić -txindex + + + + wallet.dat corrupt, salvage failed + wallet.dat uszkodzony, odtworzenie się nie powiodło + + + + Password for JSON-RPC connections + Hasło do połączeń JSON-RPC + + + + Allow JSON-RPC connections from specified IP address + Przyjmuj połączenia JSON-RPC ze wskazanego adresu IP + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Wysyłaj polecenia do węzła działającego na <ip> (domyślnie: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Wykonaj polecenie kiedy najlepszy blok ulegnie zmianie (%s w komendzie zastanie zastąpione przez hash bloku) + + + + Upgrade wallet to latest format + Zaktualizuj portfel do najnowszego formatu. + + + + Set key pool size to <n> (default: 100) + Ustaw rozmiar puli kluczy na <n> (domyślnie: 100) + + + + Rescan the block chain for missing wallet transactions + Przeskanuj blok łańcuchów żeby znaleźć zaginione transakcje portfela + + + + Use OpenSSL (https) for JSON-RPC connections + Użyj OpenSSL (https) do połączeń JSON-RPC + + + + Server certificate file (default: server.cert) + Plik certyfikatu serwera (domyślnie: server.cert) + + + + Server private key (default: server.pem) + Klucz prywatny serwera (domyślnie: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Aceptowalne szyfry (domyślnie: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Ta wiadomość pomocy + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Nie można przywiązać %s na tym komputerze (bind returned error %d, %s) + + + + Connect through socks proxy + Łączy przez proxy socks + + + + Allow DNS lookups for -addnode, -seednode and -connect + Zezwól -addnode, -seednode i -connect na łączenie się z serwerem DNS + + + + Loading addresses... + Wczytywanie adresów... + + + + Error loading wallet.dat: Wallet corrupted + Błąd ładowania wallet.dat: Uszkodzony portfel + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Błąd ładowania wallet.dat: Portfel wymaga nowszej wersji CryptoLottoToken + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Portfel wymaga przepisania: zrestartuj CryptoLottoTokena żeby ukończyć + + + + Error loading wallet.dat + Błąd ładowania wallet.dat + + + + Invalid -proxy address: '%s' + Nieprawidłowy adres -proxy: '%s' + + + + Unknown network specified in -onlynet: '%s' + Nieznana sieć w -onlynet: '%s' + + + + Unknown -socks proxy version requested: %i + Nieznana wersja proxy w -socks: %i + + + + Cannot resolve -bind address: '%s' + Nie można uzyskać adresu -bind: '%s' + + + + Cannot resolve -externalip address: '%s' + Nie można uzyskać adresu -externalip: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + Nieprawidłowa kwota dla -paytxfee=<amount>: '%s' + + + + Invalid amount + Nieprawidłowa kwota + + + + Insufficient funds + Niewystarczające środki + + + + Loading block index... + Ładowanie indeksu bloku... + + + + Add a node to connect to and attempt to keep the connection open + Dodaj węzeł do łączenia się and attempt to keep the connection open + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Nie można przywiązać %s na tym komputerze. CryptoLottoToken prawdopodobnie już działa. + + + + Fee per KB to add to transactions you send + + + + + + Loading wallet... + Wczytywanie portfela... + + + + Cannot downgrade wallet + Nie można dezaktualizować portfela + + + + Cannot write default address + Nie można zapisać domyślnego adresu + + + + Rescanning... + Ponowne skanowanie... + + + + Done loading + Wczytywanie zakończone + + + + To use the %s option + Aby użyć opcji %s + + + + Error + Błąd + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + Musisz ustawić rpcpassword=<hasło> w pliku configuracyjnym: +%s +Jeżeli plik nie istnieje, utwórz go z uprawnieniami właściciela-tylko-do-odczytu. + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_pt_BR.qm b/src/qt/locale/bitcoin_pt_BR.qm new file mode 100644 index 0000000..938ebde Binary files /dev/null and b/src/qt/locale/bitcoin_pt_BR.qm differ diff --git a/src/qt/locale/bitcoin_pt_BR.ts b/src/qt/locale/bitcoin_pt_BR.ts new file mode 100644 index 0000000..a23213b --- /dev/null +++ b/src/qt/locale/bitcoin_pt_BR.ts @@ -0,0 +1,2937 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + Sobre o CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + Versão do <b>CryptoLottoToken</b> + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + ⏎ +Este é um software experimental.⏎ +⏎ +Distribuido sob a licença de software MIT/X11, veja o arquivo anexo COPYING ou http://www.opensource.org/licenses/mit-license.php.⏎ +⏎ +Este produto inclui software desenvolvido pelo Projeto OpenSSL para uso no OpenSSL Toolkit (http://www.openssl.org/), software de criptografia escrito por Eric Young (eay@cryptsoft.com) e sofware UPnP escrito por Thomas Bernard. + + + + Copyright + Copyright + + + + The CryptoLottoToken developers + Desenvolvedores do CryptoLottoToken + + + + AddressBookPage + + + Address Book + Catálogo de endereços + + + + Double-click to edit address or label + Clique duas vezes para editar o endereço ou o etiqueta + + + + Create a new address + Criar um novo endereço + + + + Copy the currently selected address to the system clipboard + Copie o endereço selecionado para a área de transferência do sistema + + + + &New Address + &Novo endereço + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Estes são os seus endereços CryptoLottoToken para receber pagamentos. Você pode querer enviar um endereço diferente para cada remetente, para acompanhar quem está pagando. + + + + &Copy Address + &Copiar Endereço + + + + Show &QR Code + Mostrar &QR Code + + + + Sign a message to prove you own a CryptoLottoToken address + Assine uma mensagem para provar que você é dono de um endereço CryptoLottoToken + + + + Sign &Message + &Assinar Mensagem + + + + Delete the currently selected address from the list + Excluir os endereços selecionados da lista + + + + Export the data in the current tab to a file + Exportar os dados na aba atual para um arquivo + + + + &Export + &Exportar + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Verificar mensagem para se assegurar que ela foi assinada pelo dono de um endereço CryptoLottoToken específico. + + + + &Verify Message + &Verificar Mensagem + + + + &Delete + &Excluir + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + Estes são os seus endereços CryptoLottoToken para receber pagamentos. Você pode querer enviar um endereço diferente para cada remetente, para acompanhar quem está pagando. + + + + Copy &Label + Copiar &Etiqueta + + + + &Edit + &Editar + + + + Send &Coins + Enviar bit&coins + + + + Export Address Book Data + Exportar Catálogo de Endereços + + + + Comma separated file (*.csv) + Arquivo separado por vírgulas (*. csv) + + + + Error exporting + Erro ao exportar + + + + Could not write to file %1. + Não foi possível gravar no arquivo %1. + + + + AddressTableModel + + + Label + Rótulo + + + + Address + Endereço + + + + (no label) + (Sem rótulo) + + + + AskPassphraseDialog + + + Passphrase Dialog + Janela da Frase de Segurança + + + + Enter passphrase + Digite a frase de segurança + + + + New passphrase + Nova frase de segurança + + + + Repeat new passphrase + Repita a nova frase de segurança + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Digite a nova frase de seguraça da sua carteira. <br/> Por favor, use uma frase de <b>10 ou mais caracteres aleatórios,</b> ou <b>oito ou mais palavras.</b> + + + + Encrypt wallet + Criptografar carteira + + + + This operation needs your wallet passphrase to unlock the wallet. + Esta operação precisa de sua frase de segurança para desbloquear a carteira. + + + + Unlock wallet + Desbloquear carteira + + + + This operation needs your wallet passphrase to decrypt the wallet. + Esta operação precisa de sua frase de segurança para descriptografar a carteira. + + + + Decrypt wallet + Descriptografar carteira + + + + Change passphrase + Alterar frase de segurança + + + + Enter the old and new passphrase to the wallet. + Digite a frase de segurança antiga e nova para a carteira. + + + + Confirm wallet encryption + Confirmar criptografia da carteira + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Aviso: Se você criptografar sua carteira e perder sua senha, você vai <b>perder todos os seus CryptoLottoTokenS!</b> + + + + Are you sure you wish to encrypt your wallet? + Tem certeza de que deseja criptografar sua carteira? + + + + 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. + IMPORTANTE: Qualquer backup prévio que você tenha feito do seu arquivo wallet deve ser substituído pelo novo e encriptado arquivo wallet gerado. Por razões de segurança, qualquer backup do arquivo wallet não criptografado se tornará inútil assim que você começar a usar uma nova carteira criptografada. + + + + + Warning: The Caps Lock key is on! + Cuidado: A tecla Caps Lock está ligada! + + + + + Wallet encrypted + Carteira criptografada + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + O CryptoLottoToken irá fechar agora para finalizar o processo de encriptação. Lembre-se de que encriptar sua carteira não protege totalmente suas CryptoLottoTokens de serem roubadas por malwares que tenham infectado o seu computador. + + + + + + + Wallet encryption failed + A criptografia da carteira falhou + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + A criptografia da carteira falhou devido a um erro interno. Sua carteira não estava criptografada. + + + + + The supplied passphrases do not match. + A frase de segurança fornecida não confere. + + + + Wallet unlock failed + A abertura da carteira falhou + + + + + + The passphrase entered for the wallet decryption was incorrect. + A frase de segurança digitada para a descriptografia da carteira estava incorreta. + + + + Wallet decryption failed + A descriptografia da carteira falhou + + + + Wallet passphrase was successfully changed. + A frase de segurança da carteira foi alterada com êxito. + + + + BitcoinGUI + + + Sign &message... + &Assinar Mensagem... + + + + Synchronizing with network... + Sincronizando com a rede... + + + + &Overview + &Visão geral + + + + Show general overview of wallet + Mostrar visão geral da carteira + + + + &Transactions + &Transações + + + + Browse transaction history + Navegar pelo histórico de transações + + + + Edit the list of stored addresses and labels for sending + Editar a lista de endereços e rótulos + + + + Show the list of addresses for receiving payments + Mostrar a lista de endereços para receber pagamentos + + + + E&xit + S&air + + + + Quit application + Sair da aplicação + + + + Show information about CryptoLottoToken + Mostrar informação sobre CryptoLottoToken + + + + About &Qt + Sobre &Qt + + + + Show information about Qt + Mostrar informações sobre o Qt + + + + &Options... + &Opções... + + + + &Encrypt Wallet... + &Criptografar Carteira... + + + + &Backup Wallet... + &Backup Carteira... + + + + &Change Passphrase... + &Mudar frase de segurança... + + + + Importing blocks from disk... + Importando blocos do disco... + + + + Reindexing blocks on disk... + Reindexando blocos no disco... + + + + Send coins to a CryptoLottoToken address + Enviar moedas para um endereço CryptoLottoToken + + + + Modify configuration options for CryptoLottoToken + Modificar opções de configuração para CryptoLottoToken + + + + Backup wallet to another location + Fazer cópia de segurança da carteira para uma outra localização + + + + Change the passphrase used for wallet encryption + Mudar a frase de segurança utilizada na criptografia da carteira + + + + &Debug window + Janela de &Depuração + + + + Open debugging and diagnostic console + Abrir console de depuração e diagnóstico + + + + &Verify message... + &Verificar mensagem... + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Carteira + + + + &Send + &Enviar + + + + &Receive + &Receber + + + + &Addresses + &Endereços + + + + &About CryptoLottoToken + &Sobre o CryptoLottoToken + + + + &Show / Hide + &Exibir/Ocultar + + + + Show or hide the main Window + Mostrar ou esconder a Janela Principal. + + + + Encrypt the private keys that belong to your wallet + Criptografar as chaves privadas que pertencem à sua carteira + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Assine mensagems com seus endereços CryptoLottoToken para provar que você é dono deles + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Verificar mensagens para se assegurar que elas foram assinadas pelo dono de Endereços CryptoLottoToken específicos + + + + &File + &Arquivo + + + + &Settings + &Configurações + + + + &Help + &Ajuda + + + + Tabs toolbar + Barra de ferramentas + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + Cliente CryptoLottoToken + + + + %n active connection(s) to CryptoLottoToken network + %n conexão ativa na rede CryptoLottoToken%n conexões ativas na rede CryptoLottoToken + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + Processado %1 de %2 blocos (estimado) de histórico de transações. + + + + Processed %1 blocks of transaction history. + Processado %1 blocos do histórico de transações. + + + + %n hour(s) + %n hora%n horas + + + + %n day(s) + %n dia%n dias + + + + %n week(s) + %n semana%n semanas + + + + %1 behind + %1 atrás + + + + Last received block was generated %1 ago. + Último bloco recebido foi gerado %1 atrás. + + + + Transactions after this will not yet be visible. + Transações após isso ainda não estão visíveis. + + + + Error + Erro + + + + Warning + Cuidado + + + + Information + Informação + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + A transação está acima do tamanho limite. Você ainda enviar ela com uma taxa de %1, que vai para os nós processam sua transação e ajuda a manter a rede. Você quer pagar a taxa? + + + + Up to date + Atualizado + + + + Catching up... + Recuperando o atraso ... + + + + Confirm transaction fee + Confirmar taxa de transação + + + + Sent transaction + Transação enviada + + + + Incoming transaction + Transação recebida + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Data: %1 +Quantidade: %2 +Tipo: %3 +Endereço: %4 + + + + + URI handling + Manipulação de URI + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + URI não pode ser decodificado! Isso pode ter sido causado por um endereço CryptoLottoToken inválido ou por parâmetros URI malformados. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Carteira está <b>criptografada</b> e atualmente <b>desbloqueada</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Carteira está <b>criptografada</b> e atualmente <b>bloqueada</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + Um erro fatal ocorreu. CryptoLottoToken não pode continuar em segurança e irá fechar. + + + + ClientModel + + + Network Alert + Alerta da Rede + + + + EditAddressDialog + + + Edit Address + Editar Endereço + + + + &Label + &Etiqueta + + + + The label associated with this address book entry + A etiqueta associada a esse endereço do catálogo + + + + &Address + &Endereço + + + + The address associated with this address book entry. This can only be modified for sending addresses. + O endereço associado à essa entrada do seu catálogo de endereços. Isso só pode ser modificado para endereço de envio. + + + + New receiving address + Novo endereço de recebimento + + + + New sending address + Novo endereço de envio + + + + Edit receiving address + Editar endereço de recebimento + + + + Edit sending address + Editar endereço de envio + + + + The entered address "%1" is already in the address book. + O endereço digitado "%1" já se encontra no catálogo de endereços. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + O endereço digitado "%1" não é um endereço CryptoLottoToken válido. + + + + Could not unlock wallet. + Não foi possível destravar a carteira. + + + + New key generation failed. + A geração de nova chave falhou. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + versão + + + + Usage: + Uso: + + + + command-line options + opções da linha de comando + + + + UI options + opções da UI + + + + Set language, for example "de_DE" (default: system locale) + Escolher língua, por exemplo "de_DE" (padrão: localização do sistema) + + + + Start minimized + Inicializar minimizado + + + + Show splash screen on startup (default: 1) + Mostrar tela inicial ao ligar (padrão: 1) + + + + OptionsDialog + + + Options + Opções + + + + &Main + Principal + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + Pagar taxa de &transação + + + + Automatically start CryptoLottoToken after logging in to the system. + Iniciar CryptoLottoToken automaticamente após se logar no sistema. + + + + &Start CryptoLottoToken on system login + Iniciar CryptoLottoToken no login do sistema + + + + Reset all client options to default. + Redefinir todas as opções do cliente para opções padrão. + + + + &Reset Options + &Redefinir opções + + + + &Network + Rede + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Abrir as portas do cliente CryptoLottoToken automaticamente no roteador. Isto só funcionará se seu roteador suportar UPnP e esta função estiver habilitada. + + + + Map port using &UPnP + Mapear porta usando &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Conectar à rede CryptoLottoToken através de um proxy SOCKS (ex. quando estiver usando através do Tor) + + + + &Connect through SOCKS proxy: + &Conectar através de um proxy SOCKS: + + + + Proxy &IP: + &IP do proxy: + + + + IP address of the proxy (e.g. 127.0.0.1) + Endereço &IP do proxy (ex. 127.0.0.1) + + + + &Port: + &Porta: + + + + Port of the proxy (e.g. 9050) + Porta do serviço de proxy (ex. 9050) + + + + SOCKS &Version: + &Versão do SOCKS: + + + + SOCKS version of the proxy (e.g. 5) + Versão do proxy SOCKS (ex. 5) + + + + &Window + &Janela + + + + Show only a tray icon after minimizing the window. + Mostrar apenas um ícone na bandeja ao minimizar a janela. + + + + &Minimize to the tray instead of the taskbar + &Minimizar para a bandeja em vez da barra de tarefas. + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Minimizar em vez de sair do aplicativo quando a janela for fechada. Quando esta opção é escolhida, o aplicativo só será fechado selecionando Sair no menu Arquivo. + + + + M&inimize on close + M&inimizar ao sair + + + + &Display + &Mostrar + + + + User Interface &language: + &Língua da interface com usuário: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + A língua da interface com usuário pode ser escolhida aqui. Esta configuração só surtirá efeito após reiniciar o CryptoLottoToken. + + + + &Unit to show amounts in: + &Unidade usada para mostrar quantidades: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Escolha a unidade padrão de subdivisão para interface mostrar quando enviar CryptoLottoTokens. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Mostrar ou não endereços CryptoLottoToken na lista de transações. + + + + &Display addresses in transaction list + Mostrar en&dereços na lista de transações + + + + &OK + &OK + + + + &Cancel + &Cancelar + + + + &Apply + &Aplicar + + + + default + padrão + + + + Confirm options reset + Confirmar redefinição de opções + + + + Some settings may require a client restart to take effect. + Algumas configurações requerem reinicialização para surtirem efeito. + + + + Do you want to proceed? + Você quer continuar? + + + + + Warning + Cuidado + + + + + This setting will take effect after restarting CryptoLottoToken. + Esta configuração surtirá efeito após reinicializar o aplicativo CryptoLottoToken + + + + The supplied proxy address is invalid. + O endereço proxy fornecido é inválido. + + + + OverviewPage + + + Form + Formulário + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + A informação mostrada pode estar desatualizada. Sua carteira sincroniza automaticamente com a rede CryptoLottoToken depois que a conexão é estabelecida, mas este processo pode não estar completo ainda. + + + + Balance: + Saldo: + + + + Unconfirmed: + Não confirmadas: + + + + Wallet + Carteira + + + + Immature: + Imaturo: + + + + Mined balance that has not yet matured + Saldo minerado que ainda não maturou + + + + <b>Recent transactions</b> + <b>Transações recentes</b> + + + + Your current balance + Seu saldo atual + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Total de transações ainda não confirmadas, e que ainda não contam no saldo atual + + + + + out of sync + fora de sincronia + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + Não foi possível iniciar CryptoLottoToken: manipulador clique-para-pagar + + + + QRCodeDialog + + + QR Code Dialog + Janela do código QR + + + + Request Payment + Requisitar Pagamento + + + + Amount: + Quantia: + + + + Label: + Etiqueta: + + + + Message: + Mensagem: + + + + &Save As... + &Salvar como... + + + + Error encoding URI into QR Code. + Erro ao codigicar o URI em código QR + + + + The entered amount is invalid, please check. + A quantidade digitada é inválida, favor verificar. + + + + Resulting URI too long, try to reduce the text for label / message. + URI resultante muito longa. Tente reduzir o texto do rótulo ou da mensagem. + + + + Save QR Code + Salvar código QR + + + + PNG Images (*.png) + Imagens PNG (*.png) + + + + RPCConsole + + + Client name + Nome do cliente + + + + + + + + + + + + + N/A + N/A + + + + Client version + Versão do cliente + + + + &Information + &Informação + + + + Using OpenSSL version + Usando OpenSSL versão + + + + Startup time + Horário de inicialização + + + + Network + Rede + + + + Number of connections + Número de conexões + + + + On testnet + Na rede de teste + + + + Block chain + Corrente de blocos + + + + Current number of blocks + Quantidade atual de blocos + + + + Estimated total blocks + Total estimado de blocos + + + + Last block time + Horário do último bloco + + + + &Open + &Abrir + + + + Command-line options + Opções da linha de comando + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Mostrar mensagem de ajuda do CryptoLottoToken-Qt para obter uma lista com possíveis opções da linha de comando do CryptoLottoToken. + + + + &Show + &Mostrar + + + + &Console + &Console + + + + Build date + Data do 'build' + + + + CryptoLottoToken - Debug window + CryptoLottoToken - Janela de Depuração + + + + CryptoLottoToken Core + Núcleo CryptoLottoToken + + + + Debug log file + Arquivo de log de Depuração + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + Abrir o arquivo de log de depuração do CryptoLottoToken do diretório atual de dados. Isso pode levar alguns segundos para arquivos de log grandes. + + + + Clear console + Limpar console + + + + Welcome to the CryptoLottoToken RPC console. + Bem-vindo ao console CryptoLottoToken RPC. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Use as setas para cima e para baixo para navegar pelo histórico, e <b>Ctrl-L</b> para limpar a tela. + + + + Type <b>help</b> for an overview of available commands. + Digite <b>help</b> para uma visão geral dos comandos disponíveis. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Enviar dinheiro + + + + Send to multiple recipients at once + Enviar para vários destinatários de uma só vez + + + + Add &Recipient + Adicionar destinatário + + + + Remove all transaction fields + Remover todos os campos da transação + + + + Clear &All + Limpar Tudo + + + + Balance: + Saldo: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Confirmar o envio + + + + S&end + Enviar + + + + <b>%1</b> to %2 (%3) + <b>%1</b> para %2 (%3) + + + + Confirm send coins + Confirmar envio de dinheiro + + + + Are you sure you want to send %1? + Você tem certeza que deseja enviar %1? + + + + and + e + + + + The recipient address is not valid, please recheck. + O endereço do destinatário não é válido, favor verificar. + + + + The amount to pay must be larger than 0. + A quantidade a ser paga precisa ser maior que 0. + + + + The amount exceeds your balance. + A quantidade excede seu saldo. + + + + The total exceeds your balance when the %1 transaction fee is included. + O total excede seu saldo quando uma taxa de transação de %1 é incluída. + + + + Duplicate address found, can only send to each address once per send operation. + Endereço duplicado: pode-se enviar para cada endereço apenas uma vez por transação. + + + + Error: Transaction creation failed! + Erro: Criação da transação falhou! + + + + 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. + Erro: A transação foi rejeitada. Isso pode acontecer se alguns dos CryptoLottoTokens de sua carteira já haviam sido gastos, por exemplo se você usou uma cópia do arquivo wallet.dat e alguns CryptoLottoTokens foram gastos na cópia mas não foram marcados como gastos aqui. + + + + SendCoinsEntry + + + Form + Formulário + + + + A&mount: + Q&uantidade: + + + + Pay &To: + Pagar &Para: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + O endereço para onde enviar o pagamento (ex. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + Digite uma etiqueta para este endereço para adicioná-lo ao catálogo de endereços + + + + &Label: + &Etiqueta: + + + + Choose address from address book + Escolha um endereço do seu catálogo + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Colar o endereço da área de transferência + + + + Alt+P + Alt+P + + + + Remove this recipient + Remover este destinatário + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Digite um endereço CryptoLottoToken (exemplo: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Assinaturas - Assinar / Verificar uma mensagem + + + + &Sign Message + &Assinar Mensagem + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Você pode assinar mensagens com seus endereços para provar que você é o dono deles. Seja cuidadoso para não assinar algo vago, pois ataques de pishing podem tentar te enganar para dar sua assinatura de identidade para eles. Apenas assine afirmações completamente detalhadas com as quais você concorda. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Endereço a ser usado para assinar a mensagem (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Escolha um endereço do catálogo + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Colar o endereço da área de transferência + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Entre a mensagem que você quer assinar aqui + + + + Signature + Assinatura + + + + Copy the current signature to the system clipboard + Copiar a assinatura para a área de transferência do sistema + + + + Sign the message to prove you own this CryptoLottoToken address + Assinar mensagem para provar que você é dono deste endereço CryptoLottoToken + + + + Sign &Message + Assinar &Mensagem + + + + Reset all sign message fields + Limpar todos os campos de assinatura da mensagem + + + + + Clear &All + Limpar Tudo + + + + &Verify Message + &Verificar Mensagem + + + + Enter the signing 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. + Forneça o endereço da assinatura, a mensagem (se assegure que você copiou quebras de linha, espaços, tabs, etc. exatamente) e a assinatura abaixo para verificar a mensagem. Cuidado para não ler mais na assinatura do que está escrito na mensagem propriamente, para evitar ser vítima de uma ataque do tipo "man-in-the-middle". + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + O endereço usado para assinar a mensagem (ex. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Verificar mensagem para se assegurar que ela foi assinada pelo dono de um endereço CryptoLottoToken específico. + + + + Verify &Message + Verificar %Mensagem + + + + Reset all verify message fields + Limpar todos os campos de assinatura da mensagem + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Digite um endereço CryptoLottoToken (exemplo: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Clique em "Assinar Mensagem" para gerar a assinatura + + + + Enter CryptoLottoToken signature + Entre com a assinatura CryptoLottoToken + + + + + The entered address is invalid. + O endereço fornecido é inválido. + + + + + + + Please check the address and try again. + Por favor, verifique o endereço e tente novamente. + + + + + The entered address does not refer to a key. + O endereço fornecido não se refere a uma chave. + + + + Wallet unlock was cancelled. + Destravamento da Carteira foi cancelado. + + + + Private key for the entered address is not available. + A chave privada para o endereço fornecido não está disponível. + + + + Message signing failed. + Assinatura da mensagem falhou. + + + + Message signed. + Mensagem assinada. + + + + The signature could not be decoded. + A assinatura não pode ser decodificada. + + + + + Please check the signature and try again. + Por favor, verifique a assinatura e tente novamente. + + + + The signature did not match the message digest. + A assinatura não corresponde ao "resumo da mensagem". + + + + Message verification failed. + Verificação da mensagem falhou. + + + + Message verified. + Mensagem verificada. + + + + SplashScreen + + + The CryptoLottoToken developers + Desenvolvedores do CryptoLottoToken + + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + Aberto até %1 + + + + %1/offline + %1/offline + + + + %1/unconfirmed + %1/não confirmadas + + + + %1 confirmations + %1 confirmações + + + + Status + Status + + + + , broadcast through %n node(s) + , difundir atráves de %n nó, difundir atráves de %n nós + + + + Date + Data + + + + Source + Fonte + + + + Generated + Gerados + + + + + From + De + + + + + + To + Para + + + + + own address + seu próprio endereço + + + + label + etiqueta + + + + + + + + Credit + Crédito + + + + matures in %n more block(s) + matura em mais %n blocomatura em mais %n blocos + + + + not accepted + não aceito + + + + + + + Debit + Débito + + + + Transaction fee + Taxa de transação + + + + Net amount + Valor líquido + + + + Message + Mensagem + + + + Comment + Comentário + + + + Transaction ID + ID da transação + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + CryptoLottoTokens gerados precisam maturar por 120 blocos antes de serem gastos. Quando você gera este bloco, ele é difundido na rede para ser adicionado ao blockchain. Se ele falhar ao ser acrescentado no blockchain, seu estado mudará para "não aceito" e não poderá ser gasto. Isso pode ocasionamente acontecer se outro nó gerou um bloco poucos segundos antes do seu. + + + + Debug information + Informação de depuração + + + + Transaction + Transação + + + + Inputs + Entradas + + + + Amount + Quantidade + + + + true + verdadeiro + + + + false + falso + + + + , has not been successfully broadcast yet + , ainda não foi propagada na rede com sucesso. + + + + Open for %n more block(s) + Abrir para mais %n blocoAbrir para mais %n blocos + + + + unknown + desconhecido + + + + TransactionDescDialog + + + Transaction details + Detalhes da transação + + + + This pane shows a detailed description of the transaction + Este painel mostra uma descrição detalhada da transação + + + + TransactionTableModel + + + Date + Data + + + + Type + Tipo + + + + Address + Endereço + + + + Amount + Quantidade + + + + Open for %n more block(s) + Abrir para mais %n blocoAbrir para mais %n blocos + + + + Open until %1 + Aberto até %1 + + + + Offline (%1 confirmations) + Offline (%1 confirmações) + + + + Unconfirmed (%1 of %2 confirmations) + Não confirmado (%1 of %2 confirmações) + + + + Confirmed (%1 confirmations) + Confirmado (%1 confirmações) + + + + Mined balance will be available when it matures in %n more block(s) + Saldo minerado vai estar disponível quando ele maturar em mais %n blocoSaldo minerado vai estar disponível quando ele maturar em mais %n blocos + + + + This block was not received by any other nodes and will probably not be accepted! + Este bloco não foi recebido por nenhum outro participante da rede e provavelmente não será aceito! + + + + Generated but not accepted + Gerado mas não aceito + + + + Received with + Recebido por + + + + Received from + Recebido de + + + + Sent to + Enviado para + + + + Payment to yourself + Pagamento para você mesmo + + + + Mined + Minerado + + + + (n/a) + (n/a) + + + + Transaction status. Hover over this field to show number of confirmations. + Status da transação. Passe o mouse sobre este campo para mostrar o número de confirmações. + + + + Date and time that the transaction was received. + Data e hora em que a transação foi recebida. + + + + Type of transaction. + Tipo de transação. + + + + Destination address of transaction. + Endereço de destino da transação. + + + + Amount removed from or added to balance. + Quantidade debitada ou creditada ao saldo. + + + + TransactionView + + + + All + Todos + + + + Today + Hoje + + + + This week + Esta semana + + + + This month + Este mês + + + + Last month + Mês passado + + + + This year + Este ano + + + + Range... + Intervalo... + + + + Received with + Recebido por + + + + Sent to + Enviado para + + + + To yourself + Para você mesmo + + + + Mined + Minerado + + + + Other + Outro + + + + Enter address or label to search + Procure um endereço ou etiqueta + + + + Min amount + Quantidade mínima + + + + Copy address + Copiar endereço + + + + Copy label + Copiar etiqueta + + + + Copy amount + Copiar quantia + + + + Copy transaction ID + Copiar ID da transação + + + + Edit label + Editar etiqueta + + + + Show transaction details + Mostrar detalhes da transação + + + + Export Transaction Data + Exportar Dados das Transações + + + + Comma separated file (*.csv) + Arquivo separado por vírgulas (*. csv) + + + + Confirmed + Confirmado + + + + Date + Data + + + + Type + Tipo + + + + Label + Etiqueta + + + + Address + Endereço + + + + Amount + Quantidade + + + + ID + ID + + + + Error exporting + Erro ao exportar + + + + Could not write to file %1. + Não foi possível gravar no arquivo %1. + + + + Range: + Intervalo: + + + + to + para + + + + WalletModel + + + Send Coins + Send Coins + + + + WalletView + + + &Export + &Exportar + + + + Export the data in the current tab to a file + Exportar os dados na aba atual para um arquivo + + + + Backup Wallet + Fazer cópia de segurança da Carteira + + + + Wallet Data (*.dat) + Dados da Carteira (*.dat) + + + + Backup Failed + Cópia de segurança Falhou + + + + There was an error trying to save the wallet data to the new location. + Houve um erro ao tentar salvar os dados da carteira para uma nova localização. + + + + Backup Successful + Backup feito com sucesso + + + + The wallet data was successfully saved to the new location. + Os dados da carteira foram salvos com sucesso na nova localização + + + + bitcoin-core + + + CryptoLottoToken version + Versão do CryptoLottoToken + + + + Usage: + Uso: + + + + Send command to -server or CryptoLottoTokend + Enviar comando para -server ou CryptoLottoTokend + + + + List commands + Lista de comandos + + + + Get help for a command + Obtenha ajuda sobre um comando + + + + Options: + Opções: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Especifique um arquivo de configurações (padrão: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + Especifique um arquivo de pid (padrão: CryptoLottoTokend.pid) + + + + Specify data directory + Especificar diretório de dados + + + + Set database cache size in megabytes (default: 25) + Definir o tamanho do cache do banco de dados em megabytes (padrão: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Procurar por conexões em <port> (padrão: 22556 ou testnet:44556) + + + + Maintain at most <n> connections to peers (default: 125) + Manter no máximo <n> conexões aos peers (padrão: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Conectar a um nó para receber endereços de participantes, e desconectar. + + + + Specify your own public address + Especificar seu próprio endereço público + + + + Threshold for disconnecting misbehaving peers (default: 100) + Limite para desconectar peers mal comportados (padrão: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Número de segundos para impedir que peers mal comportados reconectem (padrão: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + Um erro ocorreu ao configurar a porta RPC %u para escuta em IPv4: %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Escutar conexões JSON-RPC na porta <porta> (padrão: 22555 ou testnet: 44555) + + + + Accept command line and JSON-RPC commands + Aceitar linha de comando e comandos JSON-RPC + + + + Run in the background as a daemon and accept commands + Rodar em segundo plano como serviço e aceitar comandos + + + + Use the test network + Usar rede de teste + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Aceitar conexões externas (padrão: 1 se opções -proxy ou -connect não estiverem presentes) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + %s, você deve especificar uma senha rpcpassword no arquivo de configuração:⏎ +%s⏎ +É recomendado que você use a seguinte senha aleatória:⏎ +rpcuser=CryptoLottoTokenrpc⏎ +rpcpassword=%s⏎ +(você não precisa lembrar esta senha)⏎ +O nome de usuário e a senha NÃO PODEM ser os mesmos.⏎ +Se o arquivo não existir, crie um com permissão de leitura apenas para o dono.⏎ +É recomendado também definir um alertnotify para que você seja notificado de problemas;⏎ +por exemplo: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com⏎ + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + Um erro ocorreu ao configurar a porta RPC %u para escuta em IPv6, voltando ao IPv4: %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + Vincular ao endereço fornecido e sempre escutar nele. Use a notação [host]:port para IPv6 + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + Não foi possível obter exclusividade de escrita no endereço %s. O CryptoLottoToken provavelmente já está rodando. + + + + 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. + Erro: A transação foi rejeitada. Isso pode acontecer se alguns dos CryptoLottoTokens de sua carteira já haviam sido gastos, por exemplo se você usou uma cópia do arquivo wallet.dat e alguns CryptoLottoTokens foram gastos na cópia mas não foram marcados como gastos aqui. + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + Erro: Esta transação requer uma taxa de transação de pelo menos %s, por causa sua quantidade, complexidade ou uso de dinheiro recebido recentemente. + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + Executar comando quando um alerta relevante for recebido (%s no comando será substituído pela mensagem) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Executar comando quando uma transação da carteira mudar (%s no comando será substituído por TxID) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Determinar tamanho máximo de transações de alta-prioridade/baixa-taxa em bytes (padrão: 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + Este pode ser um build de teste pré-lançamento - use por sua conta e risco - não use para mineração ou aplicações de comércio. + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Cuidado: valor de -paytxfee escolhido é muito alto! Este é o valor da taxa de transação que você irá pagar se enviar a transação. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + Cuidado: Transações mostradas podem não estar corretas! Você pode precisar atualizar, ou outros nós podem precisar atualizar o cliente. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Cuidado: Por favor, verifique que a data e hora do seu computador estão corretas! If o seu relógio estiver errado, o CryptoLottoToken não irá funcionar corretamente. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Cuidado: erro ao ler arquivo wallet.dat! Todas as chaves foram lidas corretamente, mas dados transações e do catálogo de endereços podem estar faltando ou estar incorretas. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Aviso: wallet.dat corrompido, dados recuperados! Arquivo wallet.dat original salvo como wallet.{timestamp}.bak em %s; se seu saldo ou transações estiverem incorretos, você deve restauras o backup. + + + + Attempt to recover private keys from a corrupt wallet.dat + Tentar recuperar chaves privadas de um arquivo wallet.dat corrompido + + + + Block creation options: + Opções de criação de blocos: + + + + Connect only to the specified node(s) + Conectar apenas a nó(s) específico(s) + + + + Corrupted block database detected + Detectado Banco de dados de blocos corrompido + + + + Discover own IP address (default: 1 when listening and no -externalip) + Descobrir os próprios endereços IP (padrão: 1 quando no modo listening e opção -externalip não estiver presente) + + + + Do you want to rebuild the block database now? + Você quer reconstruir o banco de dados de blocos agora? + + + + Error initializing block database + Erro ao inicializar banco de dados de blocos + + + + Error initializing wallet database environment %s! + Erro ao inicializar ambiente de banco de dados de carteira %s! + + + + Error loading block database + Erro ao carregar banco de dados de blocos + + + + Error opening block database + Erro ao abrir banco de dados de blocos + + + + Error: Disk space is low! + Erro: Espaço em disco insuficiente! + + + + Error: Wallet locked, unable to create transaction! + Erro: Carteira travada, impossível criar transação! + + + + Error: system error: + Erro: erro de sistema + + + + Failed to listen on any port. Use -listen=0 if you want this. + Falha ao escutar em qualquer porta. Use -listen=0 se você quiser isso. + + + + Failed to read block info + Falha ao ler informação de bloco + + + + Failed to read block + Falha ao ler bloco + + + + Failed to sync block index + Falha ao sincronizar índice de blocos + + + + Failed to write block index + Falha ao escrever índice de blocos + + + + Failed to write block info + Falha ao escrever informações de bloco + + + + Failed to write block + Falha ao escrever bloco + + + + Failed to write file info + Falha ao escrever informções de arquivo + + + + Failed to write to coin database + Falha ao escrever banco de dados de moedas + + + + Failed to write transaction index + Falha ao escrever índice de transações + + + + Failed to write undo data + Falha ao escrever dados para desfazer ações + + + + Find peers using DNS lookup (default: 1 unless -connect) + Procurar pares usando consulta de DNS (padrão: 1 a menos que a opção -connect esteja presente) + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + Quantos blocos checar ao inicializar (padrão: 288, 0 = todos) + + + + How thorough the block verification is (0-4, default: 3) + Quão minuciosa é a verificação dos blocos (0-4, padrão: 3) + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + Reconstruir índice de blockchain a partir dos arquivos atuais blk000??.dat + + + + Set the number of threads to service RPC calls (default: 4) + Defina o número de threads de script de verificação. (Padrão: 4) + + + + Verifying blocks... + Verificando blocos... + + + + Verifying wallet... + Verificando carteira... + + + + Imports blocks from external blk000??.dat file + Importar blocos de um arquivo externo blk000??.dat + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + Informação + + + + Invalid -tor address: '%s' + Endereço -tor inválido: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + Manter índice completo de transações (padrão: 0) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Buffer máximo de recebimento por conexão, <n>*1000 bytes (padrão: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Buffer máximo de envio por conexão, <n>*1000 bytes (padrão: 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + Apenas aceitar cadeia de blocos correspondente a marcas de verificação internas (padrão: 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Apenas conectar em nós na rede <net> (IPv4, IPv6, ou Tor) + + + + Output extra debugging information. Implies all other -debug* options + Mostrar informações extras de depuração. Implica em outras opções -debug* + + + + Output extra network debugging information + Mostrar informações extras de depuração da rede + + + + Prepend debug output with timestamp (default: 1) + Pré anexar a saída de debug com estampa de tempo + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + Opções SSL: (veja a Wiki do CryptoLottoToken para instruções de configuração SSL) + + + + Select the version of socks proxy to use (4-5, default: 5) + Escolher versão do proxy socks a ser usada (4-5, padrão: 5) + + + + Send trace/debug info to console instead of debug.log file + Mandar informação de trace/debug para o console em vez de para o arquivo debug.log + + + + Send trace/debug info to debugger + Mandar informação de trace/debug para o debugger + + + + Set maximum block size in bytes (default: 250000) + Determinar tamanho máximo de bloco em bytes (padrão: 250000) + + + + Set minimum block size in bytes (default: 0) + Determinar tamanho mínimo de bloco em bytes (padrão: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Encolher arquivo debug.log ao iniciar o cliente (padrão 1 se opção -debug não estiver presente) + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + Especifique o tempo limite (timeout) da conexão em milissegundos (padrão: 5000) + + + + System error: + Erro de sistema: + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + Usar UPnP para mapear porta de escuta (padrão: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Usar UPnP para mapear porta de escuta (padrão: 1 quando estiver escutando) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Usar proxy para alcançar serviços escondidos (padrão: mesmo que -proxy) + + + + Username for JSON-RPC connections + Nome de usuário para conexões JSON-RPC + + + + Warning + Cuidado + + + + Warning: This version is obsolete, upgrade required! + Cuidado: Esta versão está obsoleta, atualização exigida! + + + + You need to rebuild the databases using -reindex to change -txindex + Você precisa reconstruir os bancos de dados usando -reindex para mudar -txindex + + + + wallet.dat corrupt, salvage failed + wallet.dat corrompido, recuperação falhou + + + + Password for JSON-RPC connections + Senha para conexões JSON-RPC + + + + Allow JSON-RPC connections from specified IP address + Permitir conexões JSON-RPC de endereços IP específicos + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Enviar comando para nó rodando em <ip> (pardão: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Executar comando quando o melhor bloco mudar (%s no comando será substituído pelo hash do bloco) + + + + Upgrade wallet to latest format + Atualizar carteira para o formato mais recente + + + + Set key pool size to <n> (default: 100) + Determinar tamanho do pool de endereços para <n> (padrão: 100) + + + + Rescan the block chain for missing wallet transactions + Re-escanear blocos procurando por transações perdidas da carteira + + + + Use OpenSSL (https) for JSON-RPC connections + Usar OpenSSL (https) para conexões JSON-RPC + + + + Server certificate file (default: server.cert) + Arquivo de certificado do servidor (padrão: server.cert) + + + + Server private key (default: server.pem) + Chave privada do servidor (padrão: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Algoritmos de criptografia aceitos (padrão: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Esta mensagem de ajuda + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Impossível vincular a %s neste computador (bind retornou erro %d, %s) + + + + Connect through socks proxy + Conectar através de um proxy socks + + + + Allow DNS lookups for -addnode, -seednode and -connect + Permitir consultas DNS para -addnode, -seednode e -connect + + + + Loading addresses... + Carregando endereços... + + + + Error loading wallet.dat: Wallet corrupted + Erro ao carregar wallet.dat: Carteira corrompida + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Erro ao carregar wallet.dat: Carteira requer uma versão mais nova do CryptoLottoToken + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + A Carteira precisou ser reescrita: reinicie o CryptoLottoToken para completar + + + + Error loading wallet.dat + Erro ao carregar wallet.dat + + + + Invalid -proxy address: '%s' + Endereço -proxy inválido: '%s' + + + + Unknown network specified in -onlynet: '%s' + Rede desconhecida especificada em -onlynet: '%s' + + + + Unknown -socks proxy version requested: %i + Versão desconhecida do proxy -socks requisitada: %i + + + + Cannot resolve -bind address: '%s' + Impossível encontrar o endereço -bind: '%s' + + + + Cannot resolve -externalip address: '%s' + Impossível encontrar endereço -externalip: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + Quantidade inválida para -paytxfee=<quantidade>: '%s' + + + + Invalid amount + Quantidade inválida + + + + Insufficient funds + Saldo insuficiente + + + + Loading block index... + Carregando índice de blocos... + + + + Add a node to connect to and attempt to keep the connection open + Adicionar um nó com o qual se conectar e tentar manter a conexão ativa + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Impossível vincular a %s neste computador. O CryptoLottoToken provavelmente já está rodando. + + + + Fee per KB to add to transactions you send + Taxa por KB a ser acrescida nas transações que você enviar + + + + Loading wallet... + Carregando carteira... + + + + Cannot downgrade wallet + Não é possível fazer downgrade da carteira + + + + Cannot write default address + Não foi possível escrever no endereço padrão + + + + Rescanning... + Re-escaneando... + + + + Done loading + Carregamento terminado + + + + To use the %s option + Para usar a opção %s + + + + Error + Erro + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + Você precisa especificar rpcpassword=<senha> no arquivo de configurações:⏎ +%s⏎ +Se o arquivo não existir, crie um com permissão de leitura apenas pelo dono + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_pt_PT.qm b/src/qt/locale/bitcoin_pt_PT.qm new file mode 100644 index 0000000..6e1820d Binary files /dev/null and b/src/qt/locale/bitcoin_pt_PT.qm differ diff --git a/src/qt/locale/bitcoin_pt_PT.ts b/src/qt/locale/bitcoin_pt_PT.ts new file mode 100644 index 0000000..f44ffe6 --- /dev/null +++ b/src/qt/locale/bitcoin_pt_PT.ts @@ -0,0 +1,2938 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + Sobre CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + Versão do <b>CryptoLottoToken</b> + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +Este é um programa experimental. + +Distribuído sob uma licença de software MIT/X11, por favor verifique o ficheiro anexo license.txt ou http://www.opensource.org/licenses/mit-license.php. + +Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no OpenSSL Toolkit (http://www.openssl.org/), software criptográfico escrito por Eric Young (eay@cryptsoft.com) e software UPnP escrito por Thomas Bernard. + + + + Copyright + Copyright + + + + The CryptoLottoToken developers + Os programadores CryptoLottoToken + + + + AddressBookPage + + + Address Book + Livro de endereços + + + + Double-click to edit address or label + Clique duas vezes para editar o endereço ou o rótulo + + + + Create a new address + Criar um novo endereço + + + + Copy the currently selected address to the system clipboard + Copie o endereço selecionado para a área de transferência + + + + &New Address + &Novo Endereço + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Estes são os seus endereços CryptoLottoToken para receber pagamentos. Poderá enviar um endereço diferente para cada remetente para poder identificar os pagamentos. + + + + &Copy Address + &Copiar Endereço + + + + Show &QR Code + Mostrar Código &QR + + + + Sign a message to prove you own a CryptoLottoToken address + Assine uma mensagem para provar que é dono de um endereço CryptoLottoToken + + + + Sign &Message + Assinar &Mensagem + + + + Delete the currently selected address from the list + Apagar o endereço selecionado da lista + + + + Export the data in the current tab to a file + Exportar os dados no separador actual para um ficheiro + + + + &Export + &Exportar + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Verifique a mensagem para assegurar que foi assinada com o endereço CryptoLottoToken especificado + + + + &Verify Message + &Verificar Mensagem + + + + &Delete + E&liminar + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + Estes são os seus endereços CryptoLottoToken para enviar pagamentos. Verifique sempre o valor e a morada de envio antes de enviar moedas. + + + + Copy &Label + Copiar &Rótulo + + + + &Edit + &Editar + + + + Send &Coins + Enviar &Moedas + + + + Export Address Book Data + Exportar dados do Livro de Endereços + + + + Comma separated file (*.csv) + Ficheiro separado por vírgulas (*.csv) + + + + Error exporting + Erro ao exportar + + + + Could not write to file %1. + Não foi possível escrever para o ficheiro %1. + + + + AddressTableModel + + + Label + Rótulo + + + + Address + Endereço + + + + (no label) + (Sem rótulo) + + + + AskPassphraseDialog + + + Passphrase Dialog + Diálogo de Frase-Passe + + + + Enter passphrase + Escreva a frase de segurança + + + + New passphrase + Nova frase de segurança + + + + Repeat new passphrase + Repita a nova frase de segurança + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Escreva a nova frase de seguraça da sua carteira. <br/> Por favor, use uma frase de <b>10 ou mais caracteres aleatórios,</b> ou <b>oito ou mais palavras</b>. + + + + Encrypt wallet + Encriptar carteira + + + + This operation needs your wallet passphrase to unlock the wallet. + A sua frase de segurança é necessária para desbloquear a carteira. + + + + Unlock wallet + Desbloquear carteira + + + + This operation needs your wallet passphrase to decrypt the wallet. + A sua frase de segurança é necessária para desencriptar a carteira. + + + + Decrypt wallet + Desencriptar carteira + + + + Change passphrase + Alterar frase de segurança + + + + Enter the old and new passphrase to the wallet. + Escreva a frase de segurança antiga seguida da nova para a carteira. + + + + Confirm wallet encryption + Confirmar encriptação da carteira + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Atenção: Se encriptar a carteira e perder a sua senha irá <b>PERDER TODOS OS SEUS CryptoLottoTokenS</b>! + + + + Are you sure you wish to encrypt your wallet? + Tem a certeza que deseja encriptar a carteira? + + + + 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. + IMPORTANTE: Qualquer cópia de segurança anterior da carteira deverá ser substituída com o novo, actualmente encriptado, ficheiro de carteira. Por razões de segurança, cópias de segurança não encriptadas efectuadas anteriormente do ficheiro da carteira tornar-se-ão inúteis assim que começar a usar a nova carteira encriptada. + + + + + Warning: The Caps Lock key is on! + Atenção: A tecla Caps Lock está activa! + + + + + Wallet encrypted + Carteira encriptada + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + O cliente CryptoLottoToken irá agora ser fechado para terminar o processo de encriptação. Recorde que a encriptação da sua carteira não protegerá totalmente os seus CryptoLottoTokens de serem roubados por programas maliciosos que infectem o seu computador. + + + + + + + Wallet encryption failed + A encriptação da carteira falhou + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + A encriptação da carteira falhou devido a um erro interno. A carteira não foi encriptada. + + + + + The supplied passphrases do not match. + As frases de segurança fornecidas não coincidem. + + + + Wallet unlock failed + O desbloqueio da carteira falhou + + + + + + The passphrase entered for the wallet decryption was incorrect. + A frase de segurança introduzida para a desencriptação da carteira estava incorreta. + + + + Wallet decryption failed + A desencriptação da carteira falhou + + + + Wallet passphrase was successfully changed. + A frase de segurança da carteira foi alterada com êxito. + + + + BitcoinGUI + + + Sign &message... + Assinar &mensagem... + + + + Synchronizing with network... + Sincronizando com a rede... + + + + &Overview + Visã&o geral + + + + Show general overview of wallet + Mostrar visão geral da carteira + + + + &Transactions + &Transações + + + + Browse transaction history + Navegar pelo histórico de transações + + + + Edit the list of stored addresses and labels for sending + Editar a lista de endereços e rótulos + + + + Show the list of addresses for receiving payments + Mostrar a lista de endereços para receber pagamentos + + + + E&xit + Fec&har + + + + Quit application + Sair da aplicação + + + + Show information about CryptoLottoToken + Mostrar informação sobre CryptoLottoToken + + + + About &Qt + Sobre &Qt + + + + Show information about Qt + Mostrar informação sobre Qt + + + + &Options... + &Opções... + + + + &Encrypt Wallet... + E&ncriptar Carteira... + + + + &Backup Wallet... + &Guardar Carteira... + + + + &Change Passphrase... + Mudar &Palavra-passe... + + + + Importing blocks from disk... + Importando blocos do disco... + + + + Reindexing blocks on disk... + Reindexando blocos no disco... + + + + Send coins to a CryptoLottoToken address + Enviar moedas para um endereço CryptoLottoToken + + + + Modify configuration options for CryptoLottoToken + Modificar opções de configuração para CryptoLottoToken + + + + Backup wallet to another location + Faça uma cópia de segurança da carteira para outra localização + + + + Change the passphrase used for wallet encryption + Mudar a frase de segurança utilizada na encriptação da carteira + + + + &Debug window + Janela de &depuração + + + + Open debugging and diagnostic console + Abrir consola de diagnóstico e depuração + + + + &Verify message... + &Verificar mensagem... + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Carteira + + + + &Send + &Enviar + + + + &Receive + &Receber + + + + &Addresses + E&ndereços + + + + &About CryptoLottoToken + &Sobre o CryptoLottoToken + + + + &Show / Hide + Mo&strar / Ocultar + + + + Show or hide the main Window + Mostrar ou esconder a Janela principal + + + + Encrypt the private keys that belong to your wallet + Encriptar as chaves privadas que pertencem à sua carteira + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Assine mensagens com os seus endereços CryptoLottoToken para provar que os controla + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Verifique mensagens para assegurar que foram assinadas com o endereço CryptoLottoToken especificado + + + + &File + &Ficheiro + + + + &Settings + Con&figurações + + + + &Help + A&juda + + + + Tabs toolbar + Barra de separadores + + + + + [testnet] + [rede de testes] + + + + CryptoLottoToken client + Cliente CryptoLottoToken + + + + %n active connection(s) to CryptoLottoToken network + %n ligação ativa à rede CryptoLottoToken%n ligações ativas à rede CryptoLottoToken + + + + No block source available... + Nenhum bloco fonto disponível + + + + Processed %1 of %2 (estimated) blocks of transaction history. + Processados %1 dos %2 blocos (estimados) do histórico de transacções. + + + + Processed %1 blocks of transaction history. + Processados %1 blocos do histórico de transações. + + + + %n hour(s) + %n hora%n horas + + + + %n day(s) + %n dia%n dias + + + + %n week(s) + %n semana%n semanas + + + + %1 behind + %1 em atraso + + + + Last received block was generated %1 ago. + Último bloco recebido foi gerado há %1 atrás. + + + + Transactions after this will not yet be visible. + Transações posteriores poderão não ser imediatamente visíveis. + + + + Error + Erro + + + + Warning + Aviso + + + + Information + Informação + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + Esta transação tem um tamanho superior ao limite máximo. Poderá enviá-la pagando uma taxa de %1, que será entregue ao nó que processar a sua transação e ajudará a suportar a rede. Deseja pagar a taxa? + + + + Up to date + Atualizado + + + + Catching up... + Recuperando... + + + + Confirm transaction fee + Confirme a taxa de transação + + + + Sent transaction + Transação enviada + + + + Incoming transaction + Transação recebida + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Data: %1 +Quantia: %2 +Tipo: %3 +Endereço: %4 + + + + + + URI handling + Manuseamento URI + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + URI não foi lido correctamente! Isto pode ser causado por um endereço CryptoLottoToken inválido ou por parâmetros URI malformados. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + A carteira está <b>encriptada</b> e atualmente <b>desbloqueada</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + A carteira está <b>encriptada</b> e atualmente <b>bloqueada</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + Ocorreu um erro fatal. O CryptoLottoToken não pode continuar com segurança e irá fechar. + + + + ClientModel + + + Network Alert + Alerta da Rede + + + + EditAddressDialog + + + Edit Address + Editar Endereço + + + + &Label + &Rótulo + + + + The label associated with this address book entry + O rótulo a ser associado com esta entrada do livro de endereços + + + + &Address + E&ndereço + + + + The address associated with this address book entry. This can only be modified for sending addresses. + O endereço associado com esta entrada do livro de endereços. Apenas poderá ser modificado para endereços de saída. + + + + New receiving address + Novo endereço de entrada + + + + New sending address + Novo endereço de saída + + + + Edit receiving address + Editar endereço de entrada + + + + Edit sending address + Editar endereço de saída + + + + The entered address "%1" is already in the address book. + O endereço introduzido "%1" já se encontra no livro de endereços. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + O endereço introduzido "%1" não é um endereço CryptoLottoToken válido. + + + + Could not unlock wallet. + Impossível desbloquear carteira. + + + + New key generation failed. + Falha ao gerar nova chave. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + versão + + + + Usage: + Utilização: + + + + command-line options + opções da linha de comandos + + + + UI options + Opções de UI + + + + Set language, for example "de_DE" (default: system locale) + Definir linguagem, por exemplo "pt_PT" (por defeito: linguagem do sistema) + + + + Start minimized + Iniciar minimizado + + + + Show splash screen on startup (default: 1) + Mostrar animação ao iniciar (por defeito: 1) + + + + OptionsDialog + + + Options + Opções + + + + &Main + &Principal + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + Taxa de transação opcional por KB que ajuda a assegurar que as suas transações serão processadas rapidamente. A maioria das transações tem 1 kB. + + + + Pay transaction &fee + Pagar &taxa de transação + + + + Automatically start CryptoLottoToken after logging in to the system. + Começar o CryptoLottoToken automaticamente ao iniciar sessão no sistema. + + + + &Start CryptoLottoToken on system login + &Começar o CryptoLottoToken ao iniciar o sistema + + + + Reset all client options to default. + Repôr todas as opções. + + + + &Reset Options + &Repôr Opções + + + + &Network + &Rede + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Abrir a porta do cliente CryptoLottoToken automaticamente no seu router. Isto penas funciona se o seu router suportar UPnP e este se encontrar ligado. + + + + Map port using &UPnP + Mapear porta usando &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Ligar à rede CryptoLottoToken através de um proxy SOCKS (p.ex. quando ligar através de Tor). + + + + &Connect through SOCKS proxy: + Ligar através de proxy SO&CKS: + + + + Proxy &IP: + &IP do proxy: + + + + IP address of the proxy (e.g. 127.0.0.1) + Endereço IP do proxy (p.ex. 127.0.0.1) + + + + &Port: + &Porta: + + + + Port of the proxy (e.g. 9050) + Porta do proxy (p.ex. 9050) + + + + SOCKS &Version: + &Versão SOCKS: + + + + SOCKS version of the proxy (e.g. 5) + Versão do proxy SOCKS (p.ex. 5) + + + + &Window + &Janela + + + + Show only a tray icon after minimizing the window. + Apenas mostrar o ícone da bandeja após minimizar a janela. + + + + &Minimize to the tray instead of the taskbar + &Minimizar para a bandeja e não para a barra de ferramentas + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Minimize ao invés de sair da aplicação quando a janela é fechada. Com esta opção selecionada, a aplicação apenas será encerrada quando escolher Sair da aplicação no menú. + + + + M&inimize on close + M&inimizar ao fechar + + + + &Display + Vis&ualização + + + + User Interface &language: + &Linguagem da interface de utilizador: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + A linguagem da interface do utilizador pode ser definida aqui. Esta definição entrará em efeito após reiniciar o CryptoLottoToken. + + + + &Unit to show amounts in: + &Unidade a usar em quantias: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Escolha a subdivisão unitária a ser mostrada por defeito na aplicação e ao enviar moedas. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Se mostrar, ou não, os endereços CryptoLottoToken na lista de transações. + + + + &Display addresses in transaction list + Mostrar en&dereços na lista de transações + + + + &OK + &OK + + + + &Cancel + &Cancelar + + + + &Apply + &Aplicar + + + + default + padrão + + + + Confirm options reset + Confirme a reposição de opções + + + + Some settings may require a client restart to take effect. + Algumas opções requerem o reinício do programa para funcionar. + + + + Do you want to proceed? + Deseja proceder? + + + + + Warning + Aviso + + + + + This setting will take effect after restarting CryptoLottoToken. + Esta opção entrará em efeito após reiniciar o CryptoLottoToken. + + + + The supplied proxy address is invalid. + O endereço de proxy introduzido é inválido. + + + + OverviewPage + + + Form + Formulário + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + A informação mostrada poderá estar desatualizada. A sua carteira sincroniza automaticamente com a rede CryptoLottoToken depois de estabelecer ligação, mas este processo ainda não está completo. + + + + Balance: + Saldo: + + + + Unconfirmed: + Não confirmado: + + + + Wallet + Carteira + + + + Immature: + Imaturo: + + + + Mined balance that has not yet matured + O saldo minado ainda não maturou + + + + <b>Recent transactions</b> + <b>Transações recentes</b> + + + + Your current balance + O seu saldo atual + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Total de transações ainda não confirmadas, e que não estão contabilizadas ainda no seu saldo actual + + + + + out of sync + fora de sincronia + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + Impossível começar o modo clicar-para-pagar com CryptoLottoToken: + + + + QRCodeDialog + + + QR Code Dialog + Diálogo de Código QR + + + + Request Payment + Requisitar Pagamento + + + + Amount: + Quantia: + + + + Label: + Rótulo: + + + + Message: + Mensagem: + + + + &Save As... + &Salvar Como... + + + + Error encoding URI into QR Code. + Erro ao codificar URI em Código QR. + + + + The entered amount is invalid, please check. + A quantia introduzida é inválida, por favor verifique. + + + + Resulting URI too long, try to reduce the text for label / message. + URI resultante muito longo. Tente reduzir o texto do rótulo / mensagem. + + + + Save QR Code + Guardar Código QR + + + + PNG Images (*.png) + Imagens PNG (*.png) + + + + RPCConsole + + + Client name + Nome do Cliente + + + + + + + + + + + + + N/A + N/D + + + + Client version + Versão do Cliente + + + + &Information + &Informação + + + + Using OpenSSL version + Usando versão OpenSSL + + + + Startup time + Tempo de início + + + + Network + Rede + + + + Number of connections + Número de ligações + + + + On testnet + Em rede de testes + + + + Block chain + Cadeia de blocos + + + + Current number of blocks + Número actual de blocos + + + + Estimated total blocks + Total estimado de blocos + + + + Last block time + Tempo do último bloco + + + + &Open + &Abrir + + + + Command-line options + Opções de linha de comandos + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Mostrar a mensagem de ajuda do CryptoLottoToken-Qt para obter uma lista com possíveis opções a usar na linha de comandos. + + + + &Show + Mo&strar + + + + &Console + &Consola + + + + Build date + Data de construção + + + + CryptoLottoToken - Debug window + CryptoLottoToken - Janela de depuração + + + + CryptoLottoToken Core + Núcleo CryptoLottoToken + + + + Debug log file + Ficheiro de registo de depuração + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + Abrir o ficheiro de registo de depuração da pasta de dados actual. Isto pode demorar alguns segundos para ficheiros de registo maiores. + + + + Clear console + Limpar consola + + + + Welcome to the CryptoLottoToken RPC console. + Bem-vindo à consola RPC CryptoLottoToken. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Use as setas para cima e para baixo para navegar no histórico e <b>Ctrl-L</b> para limpar o ecrã. + + + + Type <b>help</b> for an overview of available commands. + Digite <b>help</b> para visualizar os comandos disponíveis. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Enviar Moedas + + + + Send to multiple recipients at once + Enviar para múltiplos destinatários de uma vez + + + + Add &Recipient + Adicionar &Destinatário + + + + Remove all transaction fields + Remover todos os campos da transação + + + + Clear &All + &Limpar Tudo + + + + Balance: + Saldo: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Confirme ação de envio + + + + S&end + &Enviar + + + + <b>%1</b> to %2 (%3) + <b>%1</b> para %2 (%3) + + + + Confirm send coins + Confirme envio de moedas + + + + Are you sure you want to send %1? + Tem a certeza que deseja enviar %1? + + + + and + e + + + + The recipient address is not valid, please recheck. + O endereço de destino não é válido, por favor verifique. + + + + The amount to pay must be larger than 0. + A quantia a pagar deverá ser maior que 0. + + + + The amount exceeds your balance. + A quantia excede o seu saldo. + + + + The total exceeds your balance when the %1 transaction fee is included. + O total excede o seu saldo quando a taxa de transação de %1 for incluída. + + + + Duplicate address found, can only send to each address once per send operation. + Endereço duplicado encontrado, apenas poderá enviar uma vez para cada endereço por cada operação de envio. + + + + Error: Transaction creation failed! + Erro: A criação da transacção falhou! + + + + 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. + Erro: A transação foi rejeitada. Isso poderá acontecer se algumas das moedas na sua carteira já tiverem sido gastas, se por exemplo tiver usado uma cópia do ficheiro wallet.dat e as moedas foram gastas na cópia mas não foram marcadas como gastas aqui. + + + + SendCoinsEntry + + + Form + Formulário + + + + A&mount: + Qu&antia: + + + + Pay &To: + &Pagar A: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + O endereço para onde enviar o pagamento (p.ex. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + Escreva um rótulo para este endereço para o adicionar ao seu livro de endereços + + + + &Label: + Rótu&lo: + + + + Choose address from address book + Escolher endereço do livro de endereços + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Cole endereço da área de transferência + + + + Alt+P + Alt+P + + + + Remove this recipient + Remover este destinatário + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Introduza um endereço CryptoLottoToken (p.ex. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Assinaturas - Assinar / Verificar uma Mensagem + + + + &Sign Message + A&ssinar Mensagem + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Pode assinar mensagens com os seus endereços para provar que são seus. Tenha atenção ao assinar mensagens ambíguas, pois ataques de phishing podem tentar enganá-lo, de modo a assinar a sua identidade para os atacantes. Apenas assine declarações completamente detalhadas com as quais concorde. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + O endereço a utilizar para assinar a mensagem (p.ex. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Escolher endereço do livro de endereços + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Cole endereço da área de transferência + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Escreva aqui a mensagem que deseja assinar + + + + Signature + Assinatura + + + + Copy the current signature to the system clipboard + Copiar a assinatura actual para a área de transferência + + + + Sign the message to prove you own this CryptoLottoToken address + Assine uma mensagem para provar que é dono deste endereço CryptoLottoToken + + + + Sign &Message + Assinar &Mensagem + + + + Reset all sign message fields + Repôr todos os campos de assinatura de mensagem + + + + + Clear &All + Limpar &Tudo + + + + &Verify Message + &Verificar Mensagem + + + + Enter the signing 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. + Introduza o endereço de assinatura, mensagem (assegure-se de copiar quebras de linha, espaços, tabuladores, etc. exactamente) e assinatura abaixo para verificar a mensagem. Tenha atenção para não ler mais na assinatura do que o que estiver na mensagem assinada, para evitar ser enganado por um atacante que se encontre entre si e quem assinou a mensagem. + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + O endereço utilizado para assinar a mensagem (p.ex. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Verifique a mensagem para assegurar que foi assinada com o endereço CryptoLottoToken especificado + + + + Verify &Message + Verificar &Mensagem + + + + Reset all verify message fields + Repôr todos os campos de verificação de mensagem + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Introduza um endereço CryptoLottoToken (p.ex. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Clique "Assinar mensagem" para gerar a assinatura + + + + Enter CryptoLottoToken signature + Introduza assinatura CryptoLottoToken + + + + + The entered address is invalid. + O endereço introduzido é inválido. + + + + + + + Please check the address and try again. + Por favor verifique o endereço e tente de novo. + + + + + The entered address does not refer to a key. + O endereço introduzido não refere a chave alguma. + + + + Wallet unlock was cancelled. + O desbloqueio da carteira foi cancelado. + + + + Private key for the entered address is not available. + A chave privada para o endereço introduzido não está disponível. + + + + Message signing failed. + Assinatura de mensagem falhou. + + + + Message signed. + Mensagem assinada. + + + + The signature could not be decoded. + A assinatura não pôde ser descodificada. + + + + + Please check the signature and try again. + Por favor verifique a assinatura e tente de novo. + + + + The signature did not match the message digest. + A assinatura não condiz com o conteúdo da mensagem. + + + + Message verification failed. + Verificação da mensagem falhou. + + + + Message verified. + Mensagem verificada. + + + + SplashScreen + + + The CryptoLottoToken developers + Os programadores CryptoLottoToken + + + + [testnet] + [rede de testes] + + + + TransactionDesc + + + Open until %1 + Aberto até %1 + + + + %1/offline + %1/desligado + + + + %1/unconfirmed + %1/não confirmada + + + + %1 confirmations + %1 confirmações + + + + Status + Estado + + + + , broadcast through %n node(s) + , transmitida através de %n nó, transmitida através de %n nós + + + + Date + Data + + + + Source + Origem + + + + Generated + Gerado + + + + + From + De + + + + + + To + Para + + + + + own address + endereço próprio + + + + label + rótulo + + + + + + + + Credit + Crédito + + + + matures in %n more block(s) + matura daqui por %n blocomatura daqui por %n blocos + + + + not accepted + não aceite + + + + + + + Debit + Débito + + + + Transaction fee + Taxa de transação + + + + Net amount + Valor líquido + + + + Message + Mensagem + + + + Comment + Comentário + + + + Transaction ID + ID da Transação + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Moedas geradas deverão maturar por 120 blocos antes de poderem ser gastas. Quando gerou este bloco, ele foi transmitido para a rede para ser incluído na cadeia de blocos. Se a inclusão na cadeia de blocos falhar, irá mudar o estado para "não aceite" e as moedas não poderão ser gastas. Isto poderá acontecer ocasionalmente se outro nó da rede gerar um bloco a poucos segundos de diferença do seu. + + + + Debug information + Informação de depuração + + + + Transaction + Transação + + + + Inputs + Entradas + + + + Amount + Quantia + + + + true + verdadeiro + + + + false + falso + + + + , has not been successfully broadcast yet + , ainda não foi transmitida com sucesso + + + + Open for %n more block(s) + Aberta por mais %n blocoAberta por mais %n blocos + + + + unknown + desconhecido + + + + TransactionDescDialog + + + Transaction details + Detalhes da transação + + + + This pane shows a detailed description of the transaction + Esta janela mostra uma descrição detalhada da transação + + + + TransactionTableModel + + + Date + Data + + + + Type + Tipo + + + + Address + Endereço + + + + Amount + Quantia + + + + Open for %n more block(s) + Aberta por mais %n blocoAberta por mais %n blocos + + + + Open until %1 + Aberto até %1 + + + + Offline (%1 confirmations) + Desligado (%1 confirmação) + + + + Unconfirmed (%1 of %2 confirmations) + Não confirmada (%1 de %2 confirmações) + + + + Confirmed (%1 confirmations) + Confirmada (%1 confirmação) + + + + Mined balance will be available when it matures in %n more block(s) + Saldo minado ficará disponível quando maturar, daqui por %n blocoSaldo minado ficará disponível quando maturar, daqui por %n blocos + + + + This block was not received by any other nodes and will probably not be accepted! + Este bloco não foi recebido por outros nós e provavelmente não será aceite pela rede! + + + + Generated but not accepted + Gerado mas não aceite + + + + Received with + Recebido com + + + + Received from + Recebido de + + + + Sent to + Enviado para + + + + Payment to yourself + Pagamento ao próprio + + + + Mined + Minado + + + + (n/a) + (n/d) + + + + Transaction status. Hover over this field to show number of confirmations. + Estado da transação. Pairar por cima deste campo para mostrar o número de confirmações. + + + + Date and time that the transaction was received. + Data e hora a que esta transação foi recebida. + + + + Type of transaction. + Tipo de transação. + + + + Destination address of transaction. + Endereço de destino da transação. + + + + Amount removed from or added to balance. + Quantia retirada ou adicionada ao saldo. + + + + TransactionView + + + + All + Todas + + + + Today + Hoje + + + + This week + Esta semana + + + + This month + Este mês + + + + Last month + Mês passado + + + + This year + Este ano + + + + Range... + Período... + + + + Received with + Recebida com + + + + Sent to + Enviada para + + + + To yourself + Para si + + + + Mined + Minadas + + + + Other + Outras + + + + Enter address or label to search + Escreva endereço ou rótulo a procurar + + + + Min amount + Quantia mínima + + + + Copy address + Copiar endereço + + + + Copy label + Copiar rótulo + + + + Copy amount + Copiar quantia + + + + Copy transaction ID + Copiar ID da Transação + + + + Edit label + Editar rótulo + + + + Show transaction details + Mostrar detalhes da transação + + + + Export Transaction Data + Exportar Dados das Transações + + + + Comma separated file (*.csv) + Ficheiro separado por vírgula (*.csv) + + + + Confirmed + Confirmada + + + + Date + Data + + + + Type + Tipo + + + + Label + Rótulo + + + + Address + Endereço + + + + Amount + Quantia + + + + ID + ID + + + + Error exporting + Erro ao exportar + + + + Could not write to file %1. + Impossível escrever para o ficheiro %1. + + + + Range: + Período: + + + + to + até + + + + WalletModel + + + Send Coins + Enviar Moedas + + + + WalletView + + + &Export + &Exportar + + + + Export the data in the current tab to a file + Exportar os dados no separador actual para um ficheiro + + + + Backup Wallet + Cópia de Segurança da Carteira + + + + Wallet Data (*.dat) + Dados da Carteira (*.dat) + + + + Backup Failed + Cópia de Segurança Falhou + + + + There was an error trying to save the wallet data to the new location. + Ocorreu um erro ao tentar guardar os dados da carteira na nova localização. + + + + Backup Successful + Cópia de Segurança Bem Sucedida + + + + The wallet data was successfully saved to the new location. + Os dados da carteira foram salvos com sucesso numa nova localização. + + + + bitcoin-core + + + CryptoLottoToken version + Versão CryptoLottoToken + + + + Usage: + Utilização: + + + + Send command to -server or CryptoLottoTokend + Enviar comando para -server ou CryptoLottoTokend + + + + List commands + Listar comandos + + + + Get help for a command + Obter ajuda para um comando + + + + Options: + Opções: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Especificar ficheiro de configuração (por defeito: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + Especificar ficheiro pid (por defeito: CryptoLottoTokend.pid) + + + + Specify data directory + Especificar pasta de dados + + + + Set database cache size in megabytes (default: 25) + Definir o tamanho da cache de base de dados em megabytes (por defeito: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Escute por ligações em <port> (por defeito: 22556 ou testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Manter no máximo <n> ligações a outros nós da rede (por defeito: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Ligar a um nó para recuperar endereços de pares, e desligar + + + + Specify your own public address + Especifique o seu endereço público + + + + Threshold for disconnecting misbehaving peers (default: 100) + Tolerância para desligar nós mal-formados (por defeito: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Número de segundos a impedir que nós mal-formados se liguem de novo (por defeito: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + Ocorreu um erro ao definir a porta %u do serviço RPC a escutar em IPv4: %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Escutar por ligações JSON-RPC em <port> (por defeito: 22555 ou rede de testes: 44555) + + + + Accept command line and JSON-RPC commands + Aceitar comandos da consola e JSON-RPC + + + + Run in the background as a daemon and accept commands + Correr o processo como um daemon e aceitar comandos + + + + Use the test network + Utilizar a rede de testes - testnet + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Aceitar ligações externas (padrão: 1 sem -proxy ou -connect) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + %s, deverá definir rpcpassword no ficheiro de configuração : + %s +É recomendado que use a seguinte palavra-passe aleatória: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(não precisa recordar esta palavra-passe) +O nome de utilizador e password NÃO DEVEM ser iguais. +Se o ficheiro não existir, crie-o com permissões de leitura apenas para o dono. +Também é recomendado definir alertnotify para que seja alertado sobre problemas; +por exemplo: alertnotify=echo %%s | mail -s "Alerta CryptoLottoToken" admin@foo.com + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + Ocorreu um erro ao definir a porta %u do serviço RPC a escutar em IPv6, a usar IPv4: %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + Trancar a endereço específio e sempre escutar nele. Use a notação [anfitrião]:porta para IPv6 + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + Impossível trancar a pasta de dados %s. Provavelmente o CryptoLottoToken já está a ser executado. + + + + 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. + Erro: A transação foi rejeitada. Isso poderá acontecer se algumas das moedas na sua carteira já tiverem sido gastas, se por exemplo tiver usado uma cópia do ficheiro wallet.dat e as moedas foram gastas na cópia mas não foram marcadas como gastas aqui. + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + Erro: Esta transação requer uma taxa de transação mínima de %s devido á sua quantia, complexidade, ou uso de fundos recebidos recentemente! + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + Executar comando quando um alerta relevante for recebido (no comando, %s é substituído pela mensagem) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Executar comando quando uma das transações na carteira mudar (no comando, %s é substituído pelo ID da Transação) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Definir tamanho máximo de transações de alta-/baixa-prioridade em bytes (por defeito: 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + Esta é uma versão de pré-lançamento - use à sua responsabilidade - não usar para minar ou aplicações comerciais + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Atenção: -paytxfee está definida com um valor muito alto! Esta é a taxa que irá pagar se enviar uma transação. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + Atenção: As transações mostradas poderão não estar correctas! Poderá ter que atualizar ou outros nós poderão ter que atualizar. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Atenção: Por favor verifique que a data e hora do seu computador estão correctas! Se o seu relógio não estiver certo o CryptoLottoToken não irá funcionar correctamente. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Atenção: erro ao ler wallet.dat! Todas as chaves foram lidas correctamente, mas dados de transação ou do livro de endereços podem estar em falta ou incorrectos. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Atenção: wallet.dat corrupto, dados recuperados! wallet.dat original salvo como wallet.{timestamp}.bak em %s; se o seu saldo ou transações estiverem incorrectos deverá recuperar de uma cópia de segurança. + + + + Attempt to recover private keys from a corrupt wallet.dat + Tentar recuperar chaves privadas de um wallet.dat corrupto + + + + Block creation options: + Opções de criação de bloco: + + + + Connect only to the specified node(s) + Apenas ligar ao(s) nó(s) especificado(s) + + + + Corrupted block database detected + Cadeia de blocos corrompida detectada + + + + Discover own IP address (default: 1 when listening and no -externalip) + Descobrir endereço IP próprio (padrão: 1 ao escutar e sem -externalip) + + + + Do you want to rebuild the block database now? + Deseja reconstruir agora a cadeia de blocos? + + + + Error initializing block database + Erro ao inicializar a cadeia de blocos + + + + Error initializing wallet database environment %s! + Erro ao inicializar o ambiente de base de dados da carteira %s! + + + + Error loading block database + Erro ao carregar cadeia de blocos + + + + Error opening block database + Erro ao abrir a cadeia de blocos + + + + Error: Disk space is low! + Erro: Pouco espaço em disco! + + + + Error: Wallet locked, unable to create transaction! + Erro: Carteira bloqueada, incapaz de criar transação! + + + + Error: system error: + Erro: erro do sistema: + + + + Failed to listen on any port. Use -listen=0 if you want this. + Falhou a escutar em qualquer porta. Use -listen=0 se quer isto. + + + + Failed to read block info + Falha ao ler info do bloco + + + + Failed to read block + Falha ao ler bloco + + + + Failed to sync block index + Falha ao sincronizar índice do bloco + + + + Failed to write block index + Falha ao escrever índice do bloco + + + + Failed to write block info + Falha ao escrever info do bloco + + + + Failed to write block + Falha ao escrever o bloco + + + + Failed to write file info + Falha ao escrever info do ficheiro + + + + Failed to write to coin database + Falha ao escrever na base de dados de moedas + + + + Failed to write transaction index + Falha ao escrever índice de transações + + + + Failed to write undo data + Falha ao escrever histórico de modificações + + + + Find peers using DNS lookup (default: 1 unless -connect) + Encontrar pares usando procura DNS (por defeito: 1 excepto -connect) + + + + Generate coins (default: 0) + Gerar moedas (por defeito: 0) + + + + How many blocks to check at startup (default: 288, 0 = all) + Quantos blocos verificar ao começar (por defeito: 288, 0 = todos) + + + + How thorough the block verification is (0-4, default: 3) + Qual a minúcia na verificação de blocos (0-4, por defeito: 3) + + + + Not enough file descriptors available. + Descritores de ficheiros disponíveis são insuficientes. + + + + Rebuild block chain index from current blk000??.dat files + Reconstruir a cadeia de blocos dos ficheiros blk000??.dat actuais + + + + Set the number of threads to service RPC calls (default: 4) + Defina o número de processos para servir as chamadas RPC (por defeito: 4) + + + + Verifying blocks... + Verificando blocos... + + + + Verifying wallet... + Verificando a carteira... + + + + Imports blocks from external blk000??.dat file + Importar blocos de um ficheiro blk000??.dat externo + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + Defina o número de processos de verificação (até 16, 0 = automático, <0 = disponibiliza esse número de núcleos livres, por defeito: 0) + + + + Information + Informação + + + + Invalid -tor address: '%s' + Endereço -tor inválido: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + Quantia inválida para -minrelaytxfee=<amount>: '%s' + + + + Invalid amount for -mintxfee=<amount>: '%s' + Quantia inválida para -mintxfee=<amount>: '%s' + + + + Maintain a full transaction index (default: 0) + Manter índice de transações completo (por defeito: 0) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Armazenamento intermédio de recepção por ligação, <n>*1000 bytes (por defeito: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Armazenamento intermédio de envio por ligação, <n>*1000 bytes (por defeito: 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + Apenas aceitar cadeia de blocos coincidente com marcas de verificação internas (por defeito: 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Apenas ligar a nós na rede <net> (IPv4, IPv6 ou Tor) + + + + Output extra debugging information. Implies all other -debug* options + Produzir informação de depuração extra. Implica todas as outras opções -debug* + + + + Output extra network debugging information + Produzir informação de depuração extraordinária + + + + Prepend debug output with timestamp (default: 1) + Preceder informação de depuração com selo temporal + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + Opções SSL: (ver a Wiki CryptoLottoToken para instruções de configuração SSL) + + + + Select the version of socks proxy to use (4-5, default: 5) + Selecione a versão do proxy socks a usar (4-5, padrão: 5) + + + + Send trace/debug info to console instead of debug.log file + Enviar informação de rastreio/depuração para a consola e não para o ficheiro debug.log + + + + Send trace/debug info to debugger + Enviar informação de rastreio/depuração para o depurador + + + + Set maximum block size in bytes (default: 250000) + Definir tamanho máximo de um bloco em bytes (por defeito: 250000) + + + + Set minimum block size in bytes (default: 0) + Definir tamanho minímo de um bloco em bytes (por defeito: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Encolher ficheiro debug.log ao iniciar o cliente (por defeito: 1 sem -debug definido) + + + + Signing transaction failed + Falhou assinatura da transação + + + + Specify connection timeout in milliseconds (default: 5000) + Especificar tempo de espera da ligação em millisegundos (por defeito: 5000) + + + + System error: + Erro de sistema: + + + + Transaction amount too small + Quantia da transação é muito baixa + + + + Transaction amounts must be positive + Quantia da transação deverá ser positiva + + + + Transaction too large + Transação grande demais + + + + Use UPnP to map the listening port (default: 0) + Usar UPnP para mapear a porta de escuta (padrão: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Usar UPnP para mapear a porta de escuta (padrão: 1 ao escutar) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Utilizar proxy para aceder a serviços escondidos Tor (por defeito: mesmo que -proxy) + + + + Username for JSON-RPC connections + Nome de utilizador para ligações JSON-RPC + + + + Warning + Aviso + + + + Warning: This version is obsolete, upgrade required! + Atenção: Esta versão está obsoleta, é necessário actualizar! + + + + You need to rebuild the databases using -reindex to change -txindex + Necessita reconstruir as bases de dados usando -reindex para mudar -txindex + + + + wallet.dat corrupt, salvage failed + wallet.dat corrupta, recuperação falhou + + + + Password for JSON-RPC connections + Palavra-passe para ligações JSON-RPC + + + + Allow JSON-RPC connections from specified IP address + Permitir ligações JSON-RPC do endereço IP especificado + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Enviar comandos para o nó a correr em <ip> (por defeito: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Executar comando quando mudar o melhor bloco (no comando, %s é substituído pela hash do bloco) + + + + Upgrade wallet to latest format + Atualize a carteira para o formato mais recente + + + + Set key pool size to <n> (default: 100) + Definir o tamanho da memória de chaves para <n> (por defeito: 100) + + + + Rescan the block chain for missing wallet transactions + Reexaminar a cadeia de blocos para transações em falta na carteira + + + + Use OpenSSL (https) for JSON-RPC connections + Usar OpenSSL (https) para ligações JSON-RPC + + + + Server certificate file (default: server.cert) + Ficheiro de certificado do servidor (por defeito: server.cert) + + + + Server private key (default: server.pem) + Chave privada do servidor (por defeito: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Cifras aceitáveis (por defeito: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Esta mensagem de ajuda + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Incapaz de vincular a %s neste computador (vínculo retornou erro %d, %s) + + + + Connect through socks proxy + Ligar através de um proxy socks + + + + Allow DNS lookups for -addnode, -seednode and -connect + Permitir procuras DNS para -addnode, -seednode e -connect + + + + Loading addresses... + Carregar endereços... + + + + Error loading wallet.dat: Wallet corrupted + Erro ao carregar wallet.dat: Carteira danificada + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Erro ao carregar wallet.dat: A Carteira requer uma versão mais recente do CryptoLottoToken + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + A Carteira precisou ser reescrita: reinicie o CryptoLottoToken para completar + + + + Error loading wallet.dat + Erro ao carregar wallet.dat + + + + Invalid -proxy address: '%s' + Endereço -proxy inválido: '%s' + + + + Unknown network specified in -onlynet: '%s' + Rede desconhecida especificada em -onlynet: '%s' + + + + Unknown -socks proxy version requested: %i + Versão desconhecida de proxy -socks requisitada: %i + + + + Cannot resolve -bind address: '%s' + Não conseguiu resolver endereço -bind: '%s' + + + + Cannot resolve -externalip address: '%s' + Não conseguiu resolver endereço -externalip: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + Quantia inválida para -paytxfee=<amount>: '%s' + + + + Invalid amount + Quantia inválida + + + + Insufficient funds + Fundos insuficientes + + + + Loading block index... + Carregar índice de blocos... + + + + Add a node to connect to and attempt to keep the connection open + Adicione um nó ao qual se ligar e tentar manter a ligação aberta + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Incapaz de vincular à porta %s neste computador. Provavelmente o CryptoLottoToken já está a funcionar. + + + + Fee per KB to add to transactions you send + Taxa por KB a adicionar a transações enviadas + + + + Loading wallet... + Carregar carteira... + + + + Cannot downgrade wallet + Impossível mudar a carteira para uma versão anterior + + + + Cannot write default address + Impossível escrever endereço por defeito + + + + Rescanning... + Reexaminando... + + + + Done loading + Carregamento completo + + + + To use the %s option + Para usar a opção %s + + + + Error + Erro + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + Deverá definir rpcpassword=<password> no ficheiro de configuração: +%s +Se o ficheiro não existir, crie-o com permissões de leitura apenas para o dono. + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_ro_RO.qm b/src/qt/locale/bitcoin_ro_RO.qm new file mode 100644 index 0000000..cbadda4 Binary files /dev/null and b/src/qt/locale/bitcoin_ro_RO.qm differ diff --git a/src/qt/locale/bitcoin_ro_RO.ts b/src/qt/locale/bitcoin_ro_RO.ts new file mode 100644 index 0000000..437eca5 --- /dev/null +++ b/src/qt/locale/bitcoin_ro_RO.ts @@ -0,0 +1,2922 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + Despre CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> versiunea + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + Listă de adrese + + + + Double-click to edit address or label + Dublu-click pentru a edita adresa sau eticheta + + + + Create a new address + Creaţi o adresă nouă + + + + Copy the currently selected address to the system clipboard + Copiați adresa selectată în clipboard + + + + &New Address + &Adresă nouă + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Acestea sunt adresele dumneavoastră CryptoLottoToken pentru a primi plăţi. Dacă doriţi, puteți da o adresa diferită fiecărui expeditor, pentru a putea ţine evidenţa plăţilor. + + + + &Copy Address + &Copiază adresa + + + + Show &QR Code + Arata codul QR + + + + Sign a message to prove you own a CryptoLottoToken address + Semneaza mesajul pentru a dovedi ca detii aceasta adresa Bitocin + + + + Sign &Message + Semneaza mesajul + + + + Delete the currently selected address from the list + Sterge adresele curent selectate din lista + + + + Export the data in the current tab to a file + + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Verifica mesajul pentru a te asigura ca a fost insemnat cu o adresa CryptoLottoToken specifica + + + + &Verify Message + Verifica mesajele + + + + &Delete + &Șterge + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + Copiază &eticheta + + + + &Edit + &Editează + + + + Send &Coins + + + + + Export Address Book Data + Exportă Lista de adrese + + + + Comma separated file (*.csv) + Fisier csv: valori separate prin virgulă (*.csv) + + + + Error exporting + Eroare la exportare. + + + + Could not write to file %1. + Eroare la scrierea în fişerul %1. + + + + AddressTableModel + + + Label + Etichetă + + + + Address + Adresă + + + + (no label) + (fără etichetă) + + + + AskPassphraseDialog + + + Passphrase Dialog + + + + + Enter passphrase + Introduceți fraza de acces. + + + + New passphrase + Frază de acces nouă + + + + Repeat new passphrase + Repetaţi noua frază de acces + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Introduceţi noua parolă a portofelului electronic.<br/>Vă rugăm să folosiţi <b>minimum 10 caractere aleatoare</b>, sau <b>minimum 8 cuvinte</b>. + + + + Encrypt wallet + Criptează portofelul + + + + This operation needs your wallet passphrase to unlock the wallet. + Aceasta operație are nevoie de un portofel deblocat. + + + + Unlock wallet + Deblochează portofelul + + + + This operation needs your wallet passphrase to decrypt the wallet. + Această operaţiune necesită parola pentru decriptarea portofelului electronic. + + + + Decrypt wallet + Decriptează portofelul. + + + + Change passphrase + Schimbă fraza de acces + + + + Enter the old and new passphrase to the wallet. + Introduceţi vechea parola a portofelului eletronic şi apoi pe cea nouă. + + + + Confirm wallet encryption + Confirmă criptarea portofelului. + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Atenție: Dacă pierdeţi parola portofelului electronic dupa criptare, <b>VEŢI PIERDE ÎNTREAGA SUMĂ DE CryptoLottoToken ACUMULATĂ</b>! + + + + Are you sure you wish to encrypt your wallet? + Sunteţi sigur că doriţi să criptaţi portofelul electronic? + + + + 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. + + + + + + Warning: The Caps Lock key is on! + Atentie! Caps Lock este pornit + + + + + Wallet encrypted + Portofel criptat + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken se va închide acum pentru a termina procesul de criptare. Amintiți-vă că criptarea portofelului dumneavoastră nu poate proteja în totalitate CryptoLottoTokens dvs. de a fi furate de intentii rele. + + + + + + + Wallet encryption failed + Criptarea portofelului a eșuat. + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Criptarea portofelului a eșuat din cauza unei erori interne. Portofelul tău nu a fost criptat. + + + + + The supplied passphrases do not match. + Fraza de acces introdusă nu se potrivește. + + + + Wallet unlock failed + Deblocarea portofelului electronic a eşuat. + + + + + + The passphrase entered for the wallet decryption was incorrect. + Parola introdusă pentru decriptarea portofelului electronic a fost incorectă. + + + + Wallet decryption failed + Decriptarea portofelului electronic a eşuat. + + + + Wallet passphrase was successfully changed. + Parola portofelului electronic a fost schimbată. + + + + BitcoinGUI + + + Sign &message... + Semneaza &mesaj... + + + + Synchronizing with network... + Se sincronizează cu reţeaua... + + + + &Overview + &Detalii + + + + Show general overview of wallet + Afişează detalii despre portofelul electronic + + + + &Transactions + &Tranzacţii + + + + Browse transaction history + Istoricul tranzacţiilor + + + + Edit the list of stored addresses and labels for sending + Editaţi lista de adrese şi etichete. + + + + Show the list of addresses for receiving payments + Lista de adrese pentru recepţionarea plăţilor + + + + E&xit + Ieșire + + + + Quit application + Părăsiţi aplicaţia + + + + Show information about CryptoLottoToken + Informaţii despre CryptoLottoToken + + + + About &Qt + Despre &Qt + + + + Show information about Qt + Informaţii despre Qt + + + + &Options... + &Setări... + + + + &Encrypt Wallet... + Criptează portofelul electronic... + + + + &Backup Wallet... + &Backup portofelul electronic... + + + + &Change Passphrase... + &Schimbă parola... + + + + Importing blocks from disk... + Importare blocks de pe disk... + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + &Trimiteţi CryptoLottoToken către o anumită adresă + + + + Modify configuration options for CryptoLottoToken + Modifică setările pentru CryptoLottoToken + + + + Backup wallet to another location + Creaza copie de rezerva a portofelului intr-o locatie diferita + + + + Change the passphrase used for wallet encryption + &Schimbă parola folosită pentru criptarea portofelului electronic + + + + &Debug window + & Fereastra debug + + + + Open debugging and diagnostic console + Deschide consola de debug si diagnosticare + + + + &Verify message... + Verifica mesajul + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Portofelul + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + &Despre CryptoLottoToken + + + + &Show / Hide + Arata/Ascunde + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + &Fişier + + + + &Settings + &Setări + + + + &Help + &Ajutor + + + + Tabs toolbar + Bara de ferestre de lucru + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + Client CryptoLottoToken + + + + %n active connection(s) to CryptoLottoToken network + %n active connections to CryptoLottoToken network%n active connections to CryptoLottoToken network%n active connections to CryptoLottoToken network + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + + + + + Warning + + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + Actualizat + + + + Catching up... + Se actualizează... + + + + Confirm transaction fee + Confirma taxa tranzactiei + + + + Sent transaction + Tranzacţie expediată + + + + Incoming transaction + Tranzacţie recepţionată + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Data: %1⏎ Suma: %2⏎ Tipul: %3⏎ Addresa: %4⏎ + + + + + URI handling + + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Portofelul electronic este <b>criptat</b> iar in momentul de faţă este <b>deblocat</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Portofelul electronic este <b>criptat</b> iar in momentul de faţă este <b>blocat</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + Alerta retea + + + + EditAddressDialog + + + Edit Address + Editează adresa + + + + &Label + &Eticheta + + + + The label associated with this address book entry + Eticheta asociată cu această înregistrare în Lista de adrese + + + + &Address + &Adresă + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Adresa asociată cu această înregistrare în Lista de adrese. Aceasta poate fi modificată doar pentru expediere. + + + + New receiving address + Noua adresă de primire + + + + New sending address + Noua adresă de trimitere + + + + Edit receiving address + Editează adresa de primire + + + + Edit sending address + Editează adresa de trimitere + + + + The entered address "%1" is already in the address book. + Adresa introdusă "%1" se află deja în Lista de adrese. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + Adresa introdusă "%1" nu este o adresă CryptoLottoToken valabilă. + + + + Could not unlock wallet. + Portofelul electronic nu a putut fi deblocat . + + + + New key generation failed. + New key generation failed. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + versiunea + + + + Usage: + Uz: + + + + command-line options + command-line setări + + + + UI options + UI setări + + + + Set language, for example "de_DE" (default: system locale) + Seteaza limba, de exemplu: "de_DE" (initialt: system locale) + + + + Start minimized + Incepe miniaturizare + + + + Show splash screen on startup (default: 1) + Afișează pe ecran splash la pornire (implicit: 1) + + + + OptionsDialog + + + Options + Setări + + + + &Main + &Principal + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + Plăteşte comision pentru tranzacţie &f + + + + Automatically start CryptoLottoToken after logging in to the system. + Porneşte automat programul CryptoLottoToken la pornirea computerului. + + + + &Start CryptoLottoToken on system login + &S Porneşte CryptoLottoToken la pornirea sistemului + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + &Retea + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Deschide automat în router portul aferent clientului CryptoLottoToken. Funcţionează doar în cazul în care routerul e compatibil UPnP şi opţiunea e activată. + + + + Map port using &UPnP + Mapeaza portul folosind &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Conectare la reţeaua CryptoLottoToken folosind un proxy SOCKS (de exemplu, când conexiunea se stabileşte prin reţeaua Tor) + + + + &Connect through SOCKS proxy: + &Conectează prin proxy SOCKS: + + + + Proxy &IP: + Proxy &IP: + + + + IP address of the proxy (e.g. 127.0.0.1) + Adresa de IP a proxy serverului (de exemplu: 127.0.0.1) + + + + &Port: + &Port: + + + + Port of the proxy (e.g. 9050) + Portul pe care se concetează proxy serverul (de exemplu: 9050) + + + + SOCKS &Version: + SOCKS &Versiune: + + + + SOCKS version of the proxy (e.g. 5) + Versiunea SOCKS a proxiului (ex. 5) + + + + &Window + &Fereastra + + + + Show only a tray icon after minimizing the window. + Afişează doar un icon in tray la ascunderea ferestrei + + + + &Minimize to the tray instead of the taskbar + &M Ascunde în tray în loc de taskbar + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Ascunde 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 Quit din menu. + + + + M&inimize on close + &i Ascunde fereastra în locul închiderii programului + + + + &Display + &Afişare + + + + User Interface &language: + Interfata & limba userului + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + Limba interfeței utilizatorului poate fi setat aici. Această setare va avea efect după repornirea CryptoLottoToken. + + + + &Unit to show amounts in: + &Unitatea de măsură pentru afişarea sumelor: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Alege subdiviziunea folosită la afişarea interfeţei şi la trimiterea de CryptoLottoToken. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Vezi dacă adresele CryptoLottoToken sunt în lista de tranzacție sau nu + + + + &Display addresses in transaction list + &Afişează adresele în lista de tranzacţii + + + + &OK + & OK + + + + &Cancel + & Renunta + + + + &Apply + Aplica + + + + default + Initial + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + Atentie! + + + + + This setting will take effect after restarting CryptoLottoToken. + + + + + The supplied proxy address is invalid. + Adresa CryptoLottoToken pe care a-ti specificat-o este invalida + + + + OverviewPage + + + Form + Form + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + Informațiile afișate pot fi expirate. Portofelul tău se sincronizează automat cu rețeaua CryptoLottoToken după ce o conexiune este stabilita, dar acest proces nu a fost finalizat încă. + + + + Balance: + Balanţă: + + + + Unconfirmed: + Neconfirmat: + + + + Wallet + Portofelul + + + + Immature: + Nematurizat: + + + + Mined balance that has not yet matured + Balanta minata care nu s-a maturizat inca + + + + <b>Recent transactions</b> + <b>Ultimele tranzacţii</b> + + + + Your current balance + Soldul contul + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Totalul tranzacţiilor care aşteaptă să fie confirmate şi care nu sunt încă luate în calcul la afişarea soldului contului. + + + + + out of sync + Nu este sincronizat + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + Dialogul codului QR + + + + Request Payment + Cerere de plata + + + + Amount: + Sumă: + + + + Label: + Etichetă: + + + + Message: + Mesaj: + + + + &Save As... + Salvare ca... + + + + Error encoding URI into QR Code. + Eroare la incercarea codarii URl-ului in cod QR + + + + The entered amount is invalid, please check. + Suma introdusa nu este valida, verifica suma. + + + + Resulting URI too long, try to reduce the text for label / message. + + + + + Save QR Code + Salveaza codul QR + + + + PNG Images (*.png) + Imagini de tip PNG (*.png) + + + + RPCConsole + + + Client name + Numaele clientului + + + + + + + + + + + + + N/A + N/A + + + + Client version + Versiunea clientului + + + + &Information + & Informatie + + + + Using OpenSSL version + Foloseste versiunea OpenSSL + + + + Startup time + Data pornirii + + + + Network + Retea + + + + Number of connections + Numarul de conexiuni + + + + On testnet + Pe testnet + + + + Block chain + Lant bloc + + + + Current number of blocks + Numarul curent de blockuri + + + + Estimated total blocks + Estimarea totala a blocks + + + + Last block time + Ultimul block a fost gasit la: + + + + &Open + &Deschide + + + + Command-line options + Command-line setări + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Arata mesajul de ajutor CryptoLottoToken-QT pentru a obtine o lista cu posibilele optiuni ale comenzilor CryptoLottoToken + + + + &Show + & Arata + + + + &Console + &Consola + + + + Build date + Construit la data: + + + + CryptoLottoToken - Debug window + CryptoLottoToken-Fereastra pentru debug + + + + CryptoLottoToken Core + CryptoLottoToken Core + + + + Debug log file + Loguri debug + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + Deschide logurile debug din directorul curent. Aceasta poate dura cateva secunde pentru fisierele mai mari + + + + Clear console + Curata consola + + + + Welcome to the CryptoLottoToken RPC console. + Bun venit la consola CryptoLottoToken RPC + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Foloseste sagetile sus si jos pentru a naviga in istoric si <b>Ctrl-L</b> pentru a curata. + + + + Type <b>help</b> for an overview of available commands. + Scrie <b>help</b> pentru a vedea comenzile disponibile + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Trimite CryptoLottoToken + + + + Send to multiple recipients at once + Trimite simultan către mai mulţi destinatari + + + + Add &Recipient + &Adaugă destinatar + + + + Remove all transaction fields + Sterge toate spatiile de tranzactie + + + + Clear &All + Şterge &tot + + + + Balance: + Balanţă: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Confirmă operaţiunea de trimitere + + + + S&end + &S Trimite + + + + <b>%1</b> to %2 (%3) + <b>%1</b> la %2 (%3) + + + + Confirm send coins + Confirmaţi trimiterea de CryptoLottoToken + + + + Are you sure you want to send %1? + Sunteţi sigur că doriţi să trimiteţi %1? + + + + and + şi + + + + The recipient address is not valid, please recheck. + Adresa destinatarului nu este validă, vă rugăm să o verificaţi. + + + + The amount to pay must be larger than 0. + Suma de plată trebuie să fie mai mare decât 0. + + + + The amount exceeds your balance. + Suma depăşeşte soldul contului. + + + + The total exceeds your balance when the %1 transaction fee is included. + Total depăşeşte soldul contului in cazul plăţii comisionului de %1. + + + + Duplicate address found, can only send to each address once per send operation. + S-a descoperit o adresă care figurează de două ori. Expedierea se poate realiza către fiecare adresă doar o singură dată pe operaţiune. + + + + Error: Transaction creation failed! + + + + + 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. + Eroare: Tranyacţia a fost respinsă. Acesta poate fi rezultatul cheltuirii prealabile a unei sume de CryptoLottoToken din portofelul electronic, ca în cazul folosirii unei copii a fisierului wallet.dat, în care s-au efectuat tranzacţii neînregistrate în fisierul curent. + + + + SendCoinsEntry + + + Form + Form + + + + A&mount: + Su&mă : + + + + Pay &To: + Plăteşte Că&tre: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + Adaugă o etichetă acestei adrese pentru a o trece în Lista de adrese + + + + &Label: + &L Etichetă: + + + + Choose address from address book + Alegeţi adresa din Listă + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Lipiţi adresa copiată in clipboard. + + + + Alt+P + Alt+P + + + + Remove this recipient + Şterge destinatarul + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Introduceţi o adresă CryptoLottoToken (de exemplu: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Semnatura- Semneaza/verifica un mesaj + + + + &Sign Message + Semneaza Mesajul + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Introduceţi o adresă CryptoLottoToken (de exemplu: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Alegeţi adresa din Listă + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Lipiţi adresa copiată in clipboard. + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Introduce mesajul pe care vrei sa il semnezi, aici. + + + + Signature + + + + + Copy the current signature to the system clipboard + Copiaza semnatura curenta in clipboard-ul sistemului + + + + Sign the message to prove you own this CryptoLottoToken address + Semneaza mesajul pentru a dovedi ca detii acesta adresa CryptoLottoToken + + + + Sign &Message + + + + + Reset all sign message fields + Reseteaza toate spatiile mesajelor semnate. + + + + + Clear &All + Şterge &tot + + + + &Verify Message + Verifica mesajul + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Introduceţi o adresă CryptoLottoToken (de exemplu: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Verifica mesajul pentru a fi sigur ca a fost semnat cu adresa CryptoLottoToken specifica + + + + Verify &Message + + + + + Reset all verify message fields + Reseteaza toate spatiile mesajelor semnate. + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Introduceţi o adresă CryptoLottoToken (de exemplu: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Click "Semneaza msajul" pentru a genera semnatura + + + + Enter CryptoLottoToken signature + Introduce semnatura bitocin + + + + + The entered address is invalid. + Adresa introdusa nu este valida + + + + + + + Please check the address and try again. + Te rugam verifica adresa si introduce-o din nou + + + + + The entered address does not refer to a key. + Adresa introdusa nu se refera la o cheie. + + + + Wallet unlock was cancelled. + Blocarea portofelului a fost intrerupta + + + + Private key for the entered address is not available. + Cheia privata pentru adresa introdusa nu este valida. + + + + Message signing failed. + Semnarea mesajului a esuat + + + + Message signed. + Mesaj Semnat! + + + + The signature could not be decoded. + Aceasta semnatura nu a putut fi decodata + + + + + Please check the signature and try again. + Verifica semnatura si incearca din nou + + + + The signature did not match the message digest. + Semnatura nu seamana! + + + + Message verification failed. + Verificarea mesajului a esuat + + + + Message verified. + Mesaj verificat + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + Deschis până la %1 + + + + %1/offline + + + + + %1/unconfirmed + %1/neconfirmat + + + + %1 confirmations + %1 confirmări + + + + Status + Stare + + + + , broadcast through %n node(s) + + + + + Date + Data + + + + Source + Sursa + + + + Generated + Generat + + + + + From + De la + + + + + + To + Către + + + + + own address + Adresa posedata + + + + label + etichetă + + + + + + + + Credit + Credit + + + + matures in %n more block(s) + + + + + not accepted + nu este acceptat + + + + + + + Debit + Debit + + + + Transaction fee + Comisionul tranzacţiei + + + + Net amount + Suma netă + + + + Message + Mesaj + + + + Comment + Comentarii + + + + Transaction ID + ID-ul tranzactiei + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Monedele CryptoLottoToken generate se pot cheltui dupa parcurgerea a 120 de blocuri. După ce a fost generat, s-a propagat în reţea, urmând să fie adăugat lanţului de blocuri. Dacă nu poate fi inclus in lanţ, starea sa va deveni "neacceptat" si nu va putea fi folosit la tranzacţii. Acest fenomen se întâmplă atunci cand un alt nod a generat un bloc la o diferenţa de câteva secunde. + + + + Debug information + Informatii pentru debug + + + + Transaction + Tranzacţie + + + + Inputs + Intrari + + + + Amount + Sumă + + + + true + Adevarat! + + + + false + Fals! + + + + , has not been successfully broadcast yet + , nu s-a propagat încă + + + + Open for %n more block(s) + + + + + unknown + necunoscut + + + + TransactionDescDialog + + + Transaction details + Detaliile tranzacţiei + + + + This pane shows a detailed description of the transaction + Afişează detalii despre tranzacţie + + + + TransactionTableModel + + + Date + Data + + + + Type + Tipul + + + + Address + Adresa + + + + Amount + Cantitate + + + + Open for %n more block(s) + + + + + Open until %1 + Deschis până la %1 + + + + Offline (%1 confirmations) + Neconectat (%1 confirmări) + + + + Unconfirmed (%1 of %2 confirmations) + Neconfirmat (%1 din %2 confirmări) + + + + Confirmed (%1 confirmations) + Confirmat (%1 confirmări) + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + Blocul nu a fost recepţionat de niciun alt nod şi e probabil că nu va fi acceptat. + + + + Generated but not accepted + Generat, dar neacceptat + + + + Received with + Recepţionat cu + + + + Received from + Primit de la: + + + + Sent to + Trimis către + + + + Payment to yourself + Plată către un cont propriu + + + + Mined + Produs + + + + (n/a) + (n/a) + + + + Transaction status. Hover over this field to show number of confirmations. + Starea tranzacţiei. Treceţi cu mouse-ul peste acest câmp pentru afişarea numărului de confirmări. + + + + Date and time that the transaction was received. + Data şi ora la care a fost recepţionată tranzacţia. + + + + Type of transaction. + Tipul tranzacţiei. + + + + Destination address of transaction. + Adresa de destinaţie a tranzacţiei. + + + + Amount removed from or added to balance. + Suma extrasă sau adăugată la sold. + + + + TransactionView + + + + All + Toate + + + + Today + Astăzi + + + + This week + Săptămâna aceasta + + + + This month + Luna aceasta + + + + Last month + Luna trecută + + + + This year + Anul acesta + + + + Range... + Între... + + + + Received with + Recepţionat cu... + + + + Sent to + Trimis către + + + + To yourself + Către propriul cont + + + + Mined + Produs + + + + Other + Altele + + + + Enter address or label to search + Introduceţi adresa sau eticheta pentru căutare + + + + Min amount + Cantitatea produsă + + + + Copy address + Copiază adresa + + + + Copy label + Copiază eticheta + + + + Copy amount + Copiază sumă + + + + Copy transaction ID + + + + + Edit label + Editează eticheta + + + + Show transaction details + Arata detaliile tranzactiei + + + + Export Transaction Data + Exportă tranzacţiile + + + + Comma separated file (*.csv) + Fişier text cu valori separate prin virgulă (*.csv) + + + + Confirmed + Confirmat + + + + Date + Data + + + + Type + Tipul + + + + Label + Etichetă + + + + Address + Adresă + + + + Amount + Sumă + + + + ID + ID + + + + Error exporting + Eroare în timpul exportului + + + + Could not write to file %1. + Fisierul %1 nu a putut fi accesat pentru scriere. + + + + Range: + Interval: + + + + to + către + + + + WalletModel + + + Send Coins + Trimite CryptoLottoToken + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + + + + + Backup Wallet + + + + + Wallet Data (*.dat) + Date portofel (*.dat) + + + + Backup Failed + Copia de rezerva a esuat + + + + There was an error trying to save the wallet data to the new location. + A apărut o eroare la încercarea de a salva datele din portofel intr-o noua locație. + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + versiunea CryptoLottoToken + + + + Usage: + Uz: + + + + Send command to -server or CryptoLottoTokend + Trimite comanda la -server sau CryptoLottoTokend + + + + List commands + Listă de comenzi + + + + Get help for a command + Ajutor pentru o comandă + + + + Options: + Setări: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Specifica-ți configurația fisierului (in mod normal: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + + + + + Specify data directory + Specifica datele directorului + + + + Set database cache size in megabytes (default: 25) + Seteaza marimea cache a bazei de date in MB (initial: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Lista a conectiunile in <port> (initial: 22556 sau testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Se menține la cele mai multe conexiuni <n> cu colegii (implicit: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Conecteaza-te la nod pentru a optine adresa peer, si deconecteaza-te + + + + Specify your own public address + Specifica adresa ta publica + + + + Threshold for disconnecting misbehaving peers (default: 100) + Prag pentru deconectarea colegii funcționează corect (implicit: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Numărul de secunde pentru a păstra colegii funcționează corect la reconectare (implicit: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + + + + + Accept command line and JSON-RPC commands + Se accepta command line si comenzi JSON-RPC + + + + Run in the background as a daemon and accept commands + Ruleaza în background ca un demon și accepta comenzi. + + + + Use the test network + Utilizeaza test de retea + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Accepta conexiuni de la straini (initial: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Seteaza marimea maxima a tranzactie mare/mica in bytes (initial:27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + Optiuni creare block + + + + Connect only to the specified node(s) + Conecteaza-te doar la nod(urile) specifice + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + Descopera propria ta adresa IP (intial: 1) + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + Copie de ieșire de depanare cu timestamp + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + Optiuni SSl (vezi CryptoLottoToken wiki pentru intructiunile de instalare) + + + + Select the version of socks proxy to use (4-5, default: 5) + Selecteaza versiunea socks-ului pe care vrei sa il folosesti (4-5, initial: 5) + + + + Send trace/debug info to console instead of debug.log file + Trimite urmări / debug info la consola loc de debug.log fișier + + + + Send trace/debug info to debugger + Trimite urmări / debug info la depanatorul + + + + Set maximum block size in bytes (default: 250000) + + + + + Set minimum block size in bytes (default: 0) + + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + Foloseste UPnP pentru a vedea porturile (initial: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Foloseste UPnP pentru a vedea porturile (initial: 1 cand listezi) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + Username pentru conectiunile JSON-RPC + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + Parola pentru conectiunile JSON-RPC + + + + Allow JSON-RPC connections from specified IP address + Permiteti conectiunile JSON-RPC de la o adresa IP specifica. + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Trimite comenzi la nod, ruland pe ip-ul (initial: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Executa comanda cand cel mai bun block se schimba (%s in cmd se inlocuieste cu block hash) + + + + Upgrade wallet to latest format + Actualizeaza portofelul la ultimul format + + + + Set key pool size to <n> (default: 100) + Setarea marimii cheii bezinului la <n>(initial 100) + + + + Rescan the block chain for missing wallet transactions + Rescanare lanțul de bloc pentru tranzacțiile portofel lipsă + + + + Use OpenSSL (https) for JSON-RPC connections + Foloseste Open SSL(https) pentru coneciunile JSON-RPC + + + + Server certificate file (default: server.cert) + Certificatul serverulu (initial: server.cert) + + + + Server private key (default: server.pem) + Cheia privata a serverului ( initial: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Accepta cifruri (initial: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Acest mesaj de ajutor. + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Nu se poate lega %s cu acest calculator (retunare eroare legatura %d, %s) + + + + + + Connect through socks proxy + Conectează prin proxy SOCKS + + + + Allow DNS lookups for -addnode, -seednode and -connect + Permite DNS-ului sa se uite dupa -addnode, -seednode si -connect + + + + Loading addresses... + Încarc adrese... + + + + Error loading wallet.dat: Wallet corrupted + Eroare incarcand wallet.dat: Portofel corupt + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Eroare incarcare wallet.dat: Portofelul are nevoie de o versiune CryptoLottoToken mai noua + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Portofelul trebuie rescris: restarteaza aplicatia CryptoLottoToken pentru a face asta. + + + + Error loading wallet.dat + Eroare incarcand wallet.dat + + + + Invalid -proxy address: '%s' + Adresa proxy invalida: '%s' + + + + Unknown network specified in -onlynet: '%s' + Retea specificata necunoscuta -onlynet: '%s' + + + + Unknown -socks proxy version requested: %i + Necunoscut -socks proxy version requested: %i + + + + Cannot resolve -bind address: '%s' + Nu se poate rezolca -bind address: '%s' + + + + Cannot resolve -externalip address: '%s' + Nu se poate rezolva -externalip address: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + Suma invalida pentru -paytxfee=<amount>: '%s' + + + + Invalid amount + Suma invalida + + + + Insufficient funds + Fonduri insuficiente + + + + Loading block index... + Încarc indice bloc... + + + + Add a node to connect to and attempt to keep the connection open + Add a node to connect to and attempt to keep the connection open +details suggestions history + + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Imposibilitatea de a lega la% s pe acest computer. CryptoLottoToken este, probabil, deja în execuție. + + + + Fee per KB to add to transactions you send + Taxa pe kb pentru a adauga tranzactii trimise + + + + Loading wallet... + Încarc portofel... + + + + Cannot downgrade wallet + Nu se poate face downgrade la portofel + + + + Cannot write default address + Nu se poate scrie adresa initiala + + + + Rescanning... + Rescanez... + + + + Done loading + Încărcare terminată + + + + To use the %s option + Pentru a folosii optiunea %s + + + + Error + Eroare + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_ru.qm b/src/qt/locale/bitcoin_ru.qm new file mode 100644 index 0000000..791c201 Binary files /dev/null and b/src/qt/locale/bitcoin_ru.qm differ diff --git a/src/qt/locale/bitcoin_ru.ts b/src/qt/locale/bitcoin_ru.ts new file mode 100644 index 0000000..d37a47e --- /dev/null +++ b/src/qt/locale/bitcoin_ru.ts @@ -0,0 +1,2940 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + &О CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> версия + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +Это экспериментальная программа. + +Распространяется на правах лицензии MIT/X11, см. файл license.txt или http://www.opensource.org/licenses/mit-license.php. + +Этот продукт включает ПО, разработанное OpenSSL Project для использования в OpenSSL Toolkit (http://www.openssl.org/) и криптографическое ПО, написанное Eric Young (eay@cryptsoft.com) и ПО для работы с UPnP, написанное Thomas Bernard. + + + + Copyright + Все права защищены + + + + The CryptoLottoToken developers + Разработчики CryptoLottoToken + + + + AddressBookPage + + + Address Book + &Адресная книга + + + + Double-click to edit address or label + Для того, чтобы изменить адрес или метку, дважды кликните по изменяемому объекту + + + + Create a new address + Создать новый адрес + + + + Copy the currently selected address to the system clipboard + Копировать текущий выделенный адрес в буфер обмена + + + + &New Address + &Новый адрес + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Это Ваши адреса для получения платежей. Вы можете дать разные адреса отправителям, чтобы отслеживать, кто именно вам платит. + + + + &Copy Address + &Копировать адрес + + + + Show &QR Code + Показать &QR код + + + + Sign a message to prove you own a CryptoLottoToken address + Подписать сообщение, чтобы доказать владение адресом CryptoLottoToken + + + + Sign &Message + &Подписать сообщение + + + + Delete the currently selected address from the list + Удалить выбранный адрес из списка + + + + Export the data in the current tab to a file + Экспортировать данные из вкладки в файл + + + + &Export + &Экспорт + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Проверить сообщение, чтобы убедиться, что оно было подписано указанным адресом CryptoLottoToken + + + + &Verify Message + &Проверить сообщение + + + + &Delete + &Удалить + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + Ваши адреса для получения средств. Совет: проверьте сумму и адрес назначения перед переводом. + + + + Copy &Label + Копировать &метку + + + + &Edit + &Правка + + + + Send &Coins + &Отправить монеты + + + + Export Address Book Data + Экспортировать адресную книгу + + + + Comma separated file (*.csv) + Текст, разделённый запятыми (*.csv) + + + + Error exporting + Ошибка экспорта + + + + Could not write to file %1. + Невозможно записать в файл %1. + + + + AddressTableModel + + + Label + Метка + + + + Address + Адрес + + + + (no label) + [нет метки] + + + + AskPassphraseDialog + + + Passphrase Dialog + Диалог ввода пароля + + + + Enter passphrase + Введите пароль + + + + New passphrase + Новый пароль + + + + Repeat new passphrase + Повторите новый пароль + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Введите новый пароль для бумажника. <br/> Пожалуйста, используйте фразы из <b>10 или более случайных символов,</b> или <b>восьми и более слов.</b> + + + + Encrypt wallet + Зашифровать бумажник + + + + This operation needs your wallet passphrase to unlock the wallet. + Для выполнения операции требуется пароль вашего бумажника. + + + + Unlock wallet + Разблокировать бумажник + + + + This operation needs your wallet passphrase to decrypt the wallet. + Для выполнения операции требуется пароль вашего бумажника. + + + + Decrypt wallet + Расшифровать бумажник + + + + Change passphrase + Сменить пароль + + + + Enter the old and new passphrase to the wallet. + Введите старый и новый пароль для бумажника. + + + + Confirm wallet encryption + Подтвердите шифрование бумажника + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Внимание: если вы зашифруете бумажник и потеряете пароль, вы <b>ПОТЕРЯЕТЕ ВСЕ ВАШИ CryptoLottoToken</b>! + + + + Are you sure you wish to encrypt your wallet? + Вы уверены, что хотите зашифровать ваш бумажник? + + + + 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. + ВАЖНО: все предыдущие резервные копии вашего кошелька должны быть заменены новым зашифрованным файлом. В целях безопасности предыдущие резервные копии нешифрованного кошелька станут бесполезны, как только вы начнёте использовать новый шифрованный кошелёк. + + + + + Warning: The Caps Lock key is on! + Внимание: Caps Lock включен! + + + + + Wallet encrypted + Бумажник зашифрован + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + Сейчас программа закроется для завершения процесса шифрования. Помните, что шифрование вашего бумажника не может полностью защитить ваши CryptoLottoToken от кражи с помощью инфицирования вашего компьютера вредоносным ПО. + + + + + + + Wallet encryption failed + Не удалось зашифровать бумажник + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Шифрование бумажника не удалось из-за внутренней ошибки. Ваш бумажник не был зашифрован. + + + + + The supplied passphrases do not match. + Введённые пароли не совпадают. + + + + Wallet unlock failed + Разблокировка бумажника не удалась + + + + + + The passphrase entered for the wallet decryption was incorrect. + Указанный пароль не подходит. + + + + Wallet decryption failed + Расшифрование бумажника не удалось + + + + Wallet passphrase was successfully changed. + Пароль бумажника успешно изменён. + + + + BitcoinGUI + + + Sign &message... + &Подписать сообщение... + + + + Synchronizing with network... + Синхронизация с сетью... + + + + &Overview + О&бзор + + + + Show general overview of wallet + Показать общий обзор действий с бумажником + + + + &Transactions + &Транзакции + + + + Browse transaction history + Показать историю транзакций + + + + Edit the list of stored addresses and labels for sending + Изменить список сохранённых адресов и меток к ним + + + + Show the list of addresses for receiving payments + Показать список адресов для получения платежей + + + + E&xit + В&ыход + + + + Quit application + Закрыть приложение + + + + Show information about CryptoLottoToken + Показать информацию о CryptoLottoToken'е + + + + About &Qt + О &Qt + + + + Show information about Qt + Показать информацию о Qt + + + + &Options... + Оп&ции... + + + + &Encrypt Wallet... + &Зашифровать бумажник... + + + + &Backup Wallet... + &Сделать резервную копию бумажника... + + + + &Change Passphrase... + &Изменить пароль... + + + + Importing blocks from disk... + Импортируются блоки с диска... + + + + Reindexing blocks on disk... + Идёт переиндексация блоков на диске... + + + + Send coins to a CryptoLottoToken address + Отправить монеты на указанный адрес CryptoLottoToken + + + + Modify configuration options for CryptoLottoToken + Изменить параметры конфигурации CryptoLottoToken + + + + Backup wallet to another location + Сделать резервную копию бумажника в другом месте + + + + Change the passphrase used for wallet encryption + Изменить пароль шифрования бумажника + + + + &Debug window + &Окно отладки + + + + Open debugging and diagnostic console + Открыть консоль отладки и диагностики + + + + &Verify message... + &Проверить сообщение... + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Бумажник + + + + &Send + &Отправить + + + + &Receive + &Получить + + + + &Addresses + &Адреса + + + + &About CryptoLottoToken + &О CryptoLottoToken + + + + &Show / Hide + &Показать / Скрыть + + + + Show or hide the main Window + Показать или скрыть главное окно + + + + Encrypt the private keys that belong to your wallet + Зашифровать приватные ключи, принадлежащие вашему бумажнику + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Подписать сообщения вашим адресом CryptoLottoToken, чтобы доказать, что вы им владеете + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Проверить сообщения, чтобы удостовериться, что они были подписаны определённым адресом CryptoLottoToken + + + + &File + &Файл + + + + &Settings + &Настройки + + + + &Help + &Помощь + + + + Tabs toolbar + Панель вкладок + + + + + [testnet] + [тестовая сеть] + + + + CryptoLottoToken client + CryptoLottoToken клиент + + + + %n active connection(s) to CryptoLottoToken network + %n активное соединение с сетью%n активных соединений с сетью%n активных соединений с сетью + + + + No block source available... + Источник блоков недоступен... + + + + Processed %1 of %2 (estimated) blocks of transaction history. + Обработано %1 из %2 (примерно) блоков истории транзакций. + + + + Processed %1 blocks of transaction history. + Обработано %1 блоков истории транзакций. + + + + %n hour(s) + %n час%n часа%n часов + + + + %n day(s) + %n день%n дня%n дней + + + + %n week(s) + %n неделя%n недели%n недель + + + + %1 behind + %1 позади + + + + Last received block was generated %1 ago. + Последний полученный блок был сгенерирован %1 назад. + + + + Transactions after this will not yet be visible. + Транзакции после этой пока не будут видны. + + + + Error + Ошибка + + + + Warning + Внимание + + + + Information + Информация + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + Транзакция превышает максимальный размер. Вы можете провести её, заплатив комиссию %1, которая достанется узлам, обрабатывающим эту транзакцию, и поможет работе сети. Вы действительно хотите заплатить комиссию? + + + + Up to date + Синхронизировано + + + + Catching up... + Синхронизируется... + + + + Confirm transaction fee + Подтвердите комиссию + + + + Sent transaction + Исходящая транзакция + + + + Incoming transaction + Входящая транзакция + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Дата: %1 +Количество: %2 +Тип: %3 +Адрес: %4 + + + + + + URI handling + Обработка URI + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + Не удалось обработать URI! Это может быть связано с неверным адресом CryptoLottoToken или неправильными параметрами URI. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Бумажник <b>зашифрован</b> и в настоящее время <b>разблокирован</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Бумажник <b>зашифрован</b> и в настоящее время <b>заблокирован</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + Произошла неисправимая ошибка. CryptoLottoToken не может безопасно продолжать работу и будет закрыт. + + + + ClientModel + + + Network Alert + Сетевая Тревога + + + + EditAddressDialog + + + Edit Address + Изменить адрес + + + + &Label + &Метка + + + + The label associated with this address book entry + Метка, связанная с данной записью + + + + &Address + &Адрес + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Адрес, связанный с данной записью. + + + + New receiving address + Новый адрес для получения + + + + New sending address + Новый адрес для отправки + + + + Edit receiving address + Изменение адреса для получения + + + + Edit sending address + Изменение адреса для отправки + + + + The entered address "%1" is already in the address book. + Введённый адрес «%1» уже находится в адресной книге. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + Введённый адрес "%1" не является правильным CryptoLottoToken-адресом. + + + + Could not unlock wallet. + Не удается разблокировать бумажник. + + + + New key generation failed. + Генерация нового ключа не удалась. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + версия + + + + Usage: + Использование: + + + + command-line options + параметры командной строки + + + + UI options + Опции интерфейса + + + + Set language, for example "de_DE" (default: system locale) + Выберите язык, например "de_DE" (по умолчанию: как в системе) + + + + Start minimized + Запускать свёрнутым + + + + Show splash screen on startup (default: 1) + Показывать сплэш при запуске (по умолчанию: 1) + + + + OptionsDialog + + + Options + Опции + + + + &Main + &Главная + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + Необязательная комиссия за каждый КБ транзакции, которая ускоряет обработку Ваших транзакций. Большинство транзакций занимают 1КБ. + + + + Pay transaction &fee + Заплатить ко&миссию + + + + Automatically start CryptoLottoToken after logging in to the system. + Автоматически запускать CryptoLottoToken после входа в систему + + + + &Start CryptoLottoToken on system login + &Запускать CryptoLottoToken при входе в систему + + + + Reset all client options to default. + Сбросить все опции клиента на значения по умолчанию. + + + + &Reset Options + &Сбросить опции + + + + &Network + &Сеть + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Автоматически открыть порт для CryptoLottoToken-клиента на роутере. Работает только если Ваш роутер поддерживает UPnP, и данная функция включена. + + + + Map port using &UPnP + Пробросить порт через &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Подключаться к сети CryptoLottoToken через прокси SOCKS (например, при подключении через Tor). + + + + &Connect through SOCKS proxy: + &Подключаться через SOCKS прокси: + + + + Proxy &IP: + &IP Прокси: + + + + IP address of the proxy (e.g. 127.0.0.1) + IP-адрес прокси (например 127.0.0.1) + + + + &Port: + По&рт: + + + + Port of the proxy (e.g. 9050) + Порт прокси-сервера (например, 9050) + + + + SOCKS &Version: + &Версия SOCKS: + + + + SOCKS version of the proxy (e.g. 5) + Версия SOCKS-прокси (например, 5) + + + + &Window + &Окно + + + + Show only a tray icon after minimizing the window. + Показывать только иконку в системном лотке после сворачивания окна. + + + + &Minimize to the tray instead of the taskbar + &Cворачивать в системный лоток вместо панели задач + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Сворачивать вместо закрытия. Если данная опция будет выбрана — приложение закроется только после выбора соответствующего пункта в меню. + + + + M&inimize on close + С&ворачивать при закрытии + + + + &Display + О&тображение + + + + User Interface &language: + &Язык интерфейса: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + Здесь можно выбрать язык интерфейса. Настройки вступят в силу после перезапуска CryptoLottoToken. + + + + &Unit to show amounts in: + &Отображать суммы в единицах: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Выберите единицу измерения монет при отображении и отправке. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Показывать ли адреса CryptoLottoToken в списке транзакций. + + + + &Display addresses in transaction list + &Показывать адреса в списке транзакций + + + + &OK + О&К + + + + &Cancel + &Отмена + + + + &Apply + &Применить + + + + default + по умолчанию + + + + Confirm options reset + Подтвердите сброс опций + + + + Some settings may require a client restart to take effect. + Некоторые настройки могут потребовать перезапуск клиента, чтобы вступить в силу. + + + + Do you want to proceed? + Желаете продолжить? + + + + + Warning + Внимание + + + + + This setting will take effect after restarting CryptoLottoToken. + Эта настройка вступит в силу после перезапуска CryptoLottoToken + + + + The supplied proxy address is invalid. + Адрес прокси неверен. + + + + OverviewPage + + + Form + Форма + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + Отображаемая информация может быть устаревшей. Ваш бумажник автоматически синхронизируется с сетью CryptoLottoToken после подключения, но этот процесс пока не завершён. + + + + Balance: + Баланс: + + + + Unconfirmed: + Не подтверждено: + + + + Wallet + Бумажник + + + + Immature: + Незрелые: + + + + Mined balance that has not yet matured + Баланс добытых монет, который ещё не созрел + + + + <b>Recent transactions</b> + <b>Последние транзакции</b> + + + + Your current balance + Ваш текущий баланс + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Общая сумма всех транзакций, которые до сих пор не подтверждены, и до сих пор не учитываются в текущем балансе + + + + + out of sync + не синхронизировано + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + Не удаётся запустить CryptoLottoToken: обработчик click-to-pay + + + + QRCodeDialog + + + QR Code Dialog + Диалог QR-кода + + + + Request Payment + Запросить платёж + + + + Amount: + Количество: + + + + Label: + Метка: + + + + Message: + Сообщение: + + + + &Save As... + &Сохранить как... + + + + Error encoding URI into QR Code. + Ошибка кодирования URI в QR-код + + + + The entered amount is invalid, please check. + Введено неверное количество, проверьте ещё раз. + + + + Resulting URI too long, try to reduce the text for label / message. + Получившийся URI слишком длинный, попробуйте сократить текст метки / сообщения. + + + + Save QR Code + Сохранить QR-код + + + + PNG Images (*.png) + PNG Изображения (*.png) + + + + RPCConsole + + + Client name + Имя клиента + + + + + + + + + + + + + N/A + Н/Д + + + + Client version + Версия клиента + + + + &Information + &Информация + + + + Using OpenSSL version + Используется версия OpenSSL + + + + Startup time + Время запуска + + + + Network + Сеть + + + + Number of connections + Число подключений + + + + On testnet + В тестовой сети + + + + Block chain + Цепь блоков + + + + Current number of blocks + Текущее число блоков + + + + Estimated total blocks + Расчётное число блоков + + + + Last block time + Время последнего блока + + + + &Open + &Открыть + + + + Command-line options + Параметры командной строки + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Показать помощь по CryptoLottoToken-Qt, чтобы получить список доступных параметров командной строки. + + + + &Show + &Показать + + + + &Console + Консоль + + + + Build date + Дата сборки + + + + CryptoLottoToken - Debug window + CryptoLottoToken - Окно отладки + + + + CryptoLottoToken Core + Ядро CryptoLottoToken + + + + Debug log file + Отладочный лог-файл + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + Открыть отладочный лог-файл CryptoLottoToken из текущего каталога данных. Это может занять несколько секунд для больших лог-файлов. + + + + Clear console + Очистить консоль + + + + Welcome to the CryptoLottoToken RPC console. + Добро пожаловать в RPC-консоль CryptoLottoToken. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Используйте стрелки вверх и вниз для просмотра истории и <b>Ctrl-L</b> для очистки экрана. + + + + Type <b>help</b> for an overview of available commands. + Напишите <b>help</b> для просмотра доступных команд. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Отправка + + + + Send to multiple recipients at once + Отправить нескольким получателям одновременно + + + + Add &Recipient + &Добавить получателя + + + + Remove all transaction fields + Удалить все поля транзакции + + + + Clear &All + Очистить &всё + + + + Balance: + Баланс: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Подтвердить отправку + + + + S&end + &Отправить + + + + <b>%1</b> to %2 (%3) + <b>%1</b> адресату %2 (%3) + + + + Confirm send coins + Подтвердите отправку монет + + + + Are you sure you want to send %1? + Вы уверены, что хотите отправить %1? + + + + and + и + + + + The recipient address is not valid, please recheck. + Адрес получателя неверный, пожалуйста, перепроверьте. + + + + The amount to pay must be larger than 0. + Количество монет для отправки должно быть больше 0. + + + + The amount exceeds your balance. + Количество отправляемых монет превышает Ваш баланс + + + + The total exceeds your balance when the %1 transaction fee is included. + Сумма превысит Ваш баланс, если комиссия в размере %1 будет добавлена к транзакции + + + + Duplicate address found, can only send to each address once per send operation. + Обнаружен дублирующийся адрес. Отправка на один и тот же адрес возможна только один раз за одну операцию отправки + + + + Error: Transaction creation failed! + Ошибка: не удалось создать транзакцию! + + + + 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. + Ошибка: В транзакции отказано. Такое может произойти, если некоторые монеты уже были потрачены, например, если Вы используете одну копию файла wallet.dat, а монеты были потрачены из другой копии, но не были отмечены как потраченные в этой. + + + + SendCoinsEntry + + + Form + Форма + + + + A&mount: + Ко&личество: + + + + Pay &To: + Полу&чатель: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Адрес, на который будет выслан платёж (например Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + Введите метку для данного адреса (для добавления в адресную книгу) + + + + &Label: + &Метка: + + + + Choose address from address book + Выберите адрес из адресной книги + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Вставить адрес из буфера обмена + + + + Alt+P + Alt+P + + + + Remove this recipient + Удалить этого получателя + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Введите CryptoLottoToken-адрес (например 1LA5FtQhnnWnkK6zjFfutR7Stiit4wKd63) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Подписи - подписать/проверить сообщение + + + + &Sign Message + &Подписать сообщение + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Вы можете подписывать сообщения своими адресами, чтобы доказать владение ими. Будьте осторожны, не подписывайте что-то неопределённое, так как фишинговые атаки могут обманным путём заставить вас подписать нежелательные сообщения. Подписывайте только те сообщения, с которыми вы согласны вплоть до мелочей. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Адрес, которым вы хотите подписать сообщение (напр. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Выберите адрес из адресной книги + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Вставить адрес из буфера обмена + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Введите сообщение для подписи + + + + Signature + Подпись + + + + Copy the current signature to the system clipboard + Скопировать текущую подпись в системный буфер обмена + + + + Sign the message to prove you own this CryptoLottoToken address + Подписать сообщение, чтобы доказать владение адресом CryptoLottoToken + + + + Sign &Message + Подписать &Сообщение + + + + Reset all sign message fields + Сбросить значения всех полей подписывания сообщений + + + + + Clear &All + Очистить &всё + + + + &Verify Message + &Проверить сообщение + + + + Enter the signing 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. + Введите ниже адрес для подписи, сообщение (убедитесь, что переводы строк, пробелы, табы и т.п. в точности скопированы) и подпись, чтобы проверить сообщение. Убедитесь, что не скопировали лишнего в подпись, по сравнению с самим подписываемым сообщением, чтобы не стать жертвой атаки "man-in-the-middle". + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Адрес, которым было подписано сообщение (напр. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Проверить сообщение, чтобы убедиться, что оно было подписано указанным адресом CryptoLottoToken + + + + Verify &Message + Проверить &Сообщение + + + + Reset all verify message fields + Сбросить все поля проверки сообщения + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Введите адрес CryptoLottoToken (напр. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Нажмите "Подписать сообщение" для создания подписи + + + + Enter CryptoLottoToken signature + Введите подпись CryptoLottoToken + + + + + The entered address is invalid. + Введённый адрес неверен + + + + + + + Please check the address and try again. + Пожалуйста, проверьте адрес и попробуйте ещё раз. + + + + + The entered address does not refer to a key. + Введённый адрес не связан с ключом + + + + Wallet unlock was cancelled. + Разблокировка бумажника была отменена. + + + + Private key for the entered address is not available. + Для введённого адреса недоступен закрытый ключ + + + + Message signing failed. + Не удалось подписать сообщение + + + + Message signed. + Сообщение подписано + + + + The signature could not be decoded. + Подпись не может быть раскодирована. + + + + + Please check the signature and try again. + Пожалуйста, проверьте подпись и попробуйте ещё раз. + + + + The signature did not match the message digest. + Подпись не соответствует отпечатку сообщения. + + + + Message verification failed. + Проверка сообщения не удалась. + + + + Message verified. + Сообщение проверено. + + + + SplashScreen + + + The CryptoLottoToken developers + Разработчики CryptoLottoToken + + + + [testnet] + [тестовая сеть] + + + + TransactionDesc + + + Open until %1 + Открыто до %1 + + + + %1/offline + %1/отключен + + + + %1/unconfirmed + %1/не подтверждено + + + + %1 confirmations + %1 подтверждений + + + + Status + Статус + + + + , broadcast through %n node(s) + , разослано через %n узел, разослано через %n узла, разослано через %n узлов + + + + Date + Дата + + + + Source + Источник + + + + Generated + Сгенерированно + + + + + From + От + + + + + + To + Для + + + + + own address + свой адрес + + + + label + метка + + + + + + + + Credit + Кредит + + + + matures in %n more block(s) + будет доступно через %n блокбудет доступно через %n блокабудет доступно через %n блоков + + + + not accepted + не принято + + + + + + + Debit + Дебет + + + + Transaction fee + Комиссия + + + + Net amount + Чистая сумма + + + + Message + Сообщение + + + + Comment + Комментарий: + + + + Transaction ID + ID транзакции + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Сгенерированные монеты должны подождать 120 блоков, прежде чем они могут быть потрачены. Когда Вы сгенерировали этот блок, он был отправлен в сеть для добавления в цепочку блоков. Если данная процедура не удастся, статус изменится на «не подтверждено», и монеты будут недействительны. Это иногда происходит в случае, если другой узел сгенерирует блок на несколько секунд раньше вас. + + + + Debug information + Отладочная информация + + + + Transaction + Транзакция + + + + Inputs + Входы + + + + Amount + Количество + + + + true + истина + + + + false + ложь + + + + , has not been successfully broadcast yet + , ещё не было успешно разослано + + + + Open for %n more block(s) + Открыто для ещё %n блокаОткрыто для ещё %n блоковОткрыто для ещё %n блоков + + + + unknown + неизвестно + + + + TransactionDescDialog + + + Transaction details + Детали транзакции + + + + This pane shows a detailed description of the transaction + Данный диалог показывает детализированную статистику по выбранной транзакции + + + + TransactionTableModel + + + Date + Дата + + + + Type + Тип + + + + Address + Адрес + + + + Amount + Количество + + + + Open for %n more block(s) + Открыто для ещё %n блокаОткрыто для ещё %n блоковОткрыто для ещё %n блоков + + + + Open until %1 + Открыто до %1 + + + + Offline (%1 confirmations) + Оффлайн (%1 подтверждений) + + + + Unconfirmed (%1 of %2 confirmations) + Не подтверждено (%1 из %2 подтверждений) + + + + Confirmed (%1 confirmations) + Подтверждено (%1 подтверждений) + + + + Mined balance will be available when it matures in %n more block(s) + Добытыми монетами можно будет воспользоваться через %n блокДобытыми монетами можно будет воспользоваться через %n блокаДобытыми монетами можно будет воспользоваться через %n блоков + + + + This block was not received by any other nodes and will probably not be accepted! + Этот блок не был получен другими узлами и, возможно, не будет принят! + + + + Generated but not accepted + Сгенерированно, но не подтверждено + + + + Received with + Получено + + + + Received from + Получено от + + + + Sent to + Отправлено + + + + Payment to yourself + Отправлено себе + + + + Mined + Добыто + + + + (n/a) + [не доступно] + + + + Transaction status. Hover over this field to show number of confirmations. + Статус транзакции. Подведите курсор к нужному полю для того, чтобы увидеть количество подтверждений. + + + + Date and time that the transaction was received. + Дата и время, когда транзакция была получена. + + + + Type of transaction. + Тип транзакции. + + + + Destination address of transaction. + Адрес назначения транзакции. + + + + Amount removed from or added to balance. + Сумма, добавленная, или снятая с баланса. + + + + TransactionView + + + + All + Все + + + + Today + Сегодня + + + + This week + На этой неделе + + + + This month + В этом месяце + + + + Last month + За последний месяц + + + + This year + В этом году + + + + Range... + Промежуток... + + + + Received with + Получено на + + + + Sent to + Отправлено на + + + + To yourself + Отправленные себе + + + + Mined + Добытые + + + + Other + Другое + + + + Enter address or label to search + Введите адрес или метку для поиска + + + + Min amount + Мин. сумма + + + + Copy address + Копировать адрес + + + + Copy label + Копировать метку + + + + Copy amount + Скопировать сумму + + + + Copy transaction ID + Скопировать ID транзакции + + + + Edit label + Изменить метку + + + + Show transaction details + Показать подробности транзакции + + + + Export Transaction Data + Экспортировать данные транзакций + + + + Comma separated file (*.csv) + Текст, разделённый запятыми (*.csv) + + + + Confirmed + Подтверждено + + + + Date + Дата + + + + Type + Тип + + + + Label + Метка + + + + Address + Адрес + + + + Amount + Количество + + + + ID + ID + + + + Error exporting + Ошибка экспорта + + + + Could not write to file %1. + Невозможно записать в файл %1. + + + + Range: + Промежуток от: + + + + to + до + + + + WalletModel + + + Send Coins + Отправка + + + + WalletView + + + &Export + &Экспорт + + + + Export the data in the current tab to a file + Экспортировать данные из вкладки в файл + + + + Backup Wallet + Сделать резервную копию бумажника + + + + Wallet Data (*.dat) + Данные бумажника (*.dat) + + + + Backup Failed + Резервное копирование не удалось + + + + There was an error trying to save the wallet data to the new location. + При попытке сохранения данных бумажника в новое место произошла ошибка. + + + + Backup Successful + Резервное копирование успешно завершено + + + + The wallet data was successfully saved to the new location. + Данные бумажника успешно сохранены в новое место. + + + + bitcoin-core + + + CryptoLottoToken version + Версия + + + + Usage: + Использование: + + + + Send command to -server or CryptoLottoTokend + Отправить команду на -server или CryptoLottoTokend + + + + List commands + Список команд + + + + + Get help for a command + Получить помощь по команде + + + + Options: + Опции: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Указать конфигурационный файл (по умолчанию: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + Задать pid-файл (по умолчанию: CryptoLottoToken.pid) + + + + Specify data directory + Задать каталог данных + + + + Set database cache size in megabytes (default: 25) + Установить размер кэша базы данных в мегабайтах (по умолчанию: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Принимать входящие подключения на <port> (по умолчанию: 22556 или 44556 в тестовой сети) + + + + Maintain at most <n> connections to peers (default: 125) + Поддерживать не более <n> подключений к узлам (по умолчанию: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Подключиться к узлу, чтобы получить список адресов других участников и отключиться + + + + Specify your own public address + Укажите ваш собственный публичный адрес + + + + Threshold for disconnecting misbehaving peers (default: 100) + Порог для отключения неправильно ведущих себя узлов (по умолчанию: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Число секунд блокирования неправильно ведущих себя узлов (по умолчанию: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + Произошла ошибка при открытии RPC-порта %u для прослушивания на IPv4: %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Прослушивать подключения JSON-RPC на <порту> (по умолчанию: 22555 или для testnet: 44555) + + + + Accept command line and JSON-RPC commands + Принимать командную строку и команды JSON-RPC + + + + Run in the background as a daemon and accept commands + Запускаться в фоне как демон и принимать команды + + + + Use the test network + Использовать тестовую сеть + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Принимать подключения извне (по умолчанию: 1, если не используется -proxy или -connect) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + %s, вы должны установить опцию rpcpassword в конфигурационном файле: + %s +Рекомендуется использовать следующий случайный пароль: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(вам не нужно запоминать этот пароль) +Имя и пароль ДОЛЖНЫ различаться. +Если файл не существует, создайте его и установите права доступа только для владельца, только для чтения. +Также рекомендуется включить alertnotify для оповещения о проблемах; +Например: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + Произошла ошибка при открытии на прослушивание IPv6 RCP-порта %u, возвращаемся к IPv4: %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + Привязаться к указанному адресу и всегда прослушивать только его. Используйте [хост]:порт для IPv6 + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + Не удаётся установить блокировку на каталог данных %s. Возможно, CryptoLottoToken уже работает. + + + + 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. + Ошибка: транзакция была отклонена! Это могло произойти в случае, если некоторые монеты в вашем бумажнике уже были потрачены, например, если вы используете копию wallet.dat, и монеты были использованы в копии, но не отмечены как потраченные здесь. + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + Ошибка: эта транзакция требует комиссию как минимум %s из-за суммы, сложности или использования недавно полученных средств! + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + Выполнить команду, когда приходит сообщение о тревоге (%s в команде заменяется на сообщение) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Выполнить команду, когда меняется транзакция в бумажнике (%s в команде заменяется на TxID) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Максимальный размер высокоприоритетных/низкокомиссионных транзакций в байтах (по умолчанию: 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + Это пре-релизная тестовая сборка - используйте на свой страх и риск - не используйте для добычи или торговых приложений + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Внимание: установлено очень большое значение -paytxfee. Это комиссия, которую вы заплатите при проведении транзакции. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + Внимание: отображаемые транзакции могут быть некорректны! Вам или другим узлам, возможно, следует обновиться. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Внимание: убедитесь, что дата и время на Вашем компьютере выставлены верно. Если Ваши часы идут неправильно, CryptoLottoToken будет работать некорректно. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Внимание: ошибка чтения wallet.dat! Все ключи прочитаны верно, но данные транзакций или записи адресной книги могут отсутствовать или быть неправильными. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Внимание: wallet.dat повреждён, данные спасены! Оригинальный wallet.dat сохранён как wallet.{timestamp}.bak в %s; если ваш баланс или транзакции некорректны, вы должны восстановить файл из резервной копии. + + + + Attempt to recover private keys from a corrupt wallet.dat + Попытаться восстановить приватные ключи из повреждённого wallet.dat + + + + Block creation options: + Параметры создания блоков: + + + + Connect only to the specified node(s) + Подключаться только к указанному узлу(ам) + + + + Corrupted block database detected + БД блоков повреждена + + + + Discover own IP address (default: 1 when listening and no -externalip) + Определить свой IP (по умолчанию: 1 при прослушивании и если не используется -externalip) + + + + Do you want to rebuild the block database now? + Пересобрать БД блоков прямо сейчас? + + + + Error initializing block database + Ошибка инициализации БД блоков + + + + Error initializing wallet database environment %s! + Ошибка инициализации окружения БД бумажника %s! + + + + Error loading block database + Ошибка чтения базы данных блоков + + + + Error opening block database + Не удалось открыть БД блоков + + + + Error: Disk space is low! + Ошибка: мало места на диске! + + + + Error: Wallet locked, unable to create transaction! + Ошибка: бумажник заблокирован, невозможно создать транзакцию! + + + + Error: system error: + Ошибка: системная ошибка: + + + + Failed to listen on any port. Use -listen=0 if you want this. + Не удалось начать прослушивание на порту. Используйте -listen=0 если вас это устраивает. + + + + Failed to read block info + Не удалось прочитать информацию блока + + + + Failed to read block + Не удалось прочитать блок + + + + Failed to sync block index + Не удалось синхронизировать индекс блоков + + + + Failed to write block index + Не удалось записать индекс блоков + + + + Failed to write block info + Не удалось записать информацию блока + + + + Failed to write block + Не удалось записать блок + + + + Failed to write file info + Не удалось записать информацию файла + + + + Failed to write to coin database + Не удалось записать БД монет + + + + Failed to write transaction index + Не удалось записать индекс транзакций + + + + Failed to write undo data + Не удалось записать данные для отмены + + + + Find peers using DNS lookup (default: 1 unless -connect) + Искать узлы с помощью DNS (по умолчанию: 1, если не указан -connect) + + + + Generate coins (default: 0) + Включить добычу монет (по умолчанию: 0) + + + + How many blocks to check at startup (default: 288, 0 = all) + Сколько блоков проверять при запуске (по умолчанию: 288, 0 = все) + + + + How thorough the block verification is (0-4, default: 3) + Насколько тщательно проверять блок (0-4, по умолчанию: 3) + + + + Not enough file descriptors available. + Недостаточно файловых дескрипторов. + + + + Rebuild block chain index from current blk000??.dat files + Перестроить индекс цепи блоков из текущих файлов blk000??.dat + + + + Set the number of threads to service RPC calls (default: 4) + Задать число потоков выполнения(по умолчанию: 4) + + + + Verifying blocks... + Проверка блоков... + + + + Verifying wallet... + Проверка бумажника... + + + + Imports blocks from external blk000??.dat file + Импортировать блоки из внешнего файла blk000??.dat + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + Задать число потоков проверки скрипта (вплоть до 16, 0=авто, <0 = оставить столько ядер свободными, по умолчанию: 0) + + + + Information + Информация + + + + Invalid -tor address: '%s' + Неверный адрес -tor: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + Неверное количество в параметре -minrelaytxfee=<кол-во>: '%s' + + + + Invalid amount for -mintxfee=<amount>: '%s' + Неверное количество в параметре -mintxfee=<кол-во>: '%s' + + + + Maintain a full transaction index (default: 0) + Держать полный индекс транзакций (по умолчанию: 0) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Максимальный размер буфера приёма на соединение, <n>*1000 байт (по умолчанию: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Максимальный размер буфера отправки на соединение, <n>*1000 байт (по умолчанию: 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + Принимать цепь блоков, только если она соответствует встроенным контрольным точкам (по умолчанию: 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Подключаться только к узлам из сети <net> (IPv4, IPv6 или Tor) + + + + Output extra debugging information. Implies all other -debug* options + Выводить больше отладочной информации. Включает все остальные опции -debug* + + + + Output extra network debugging information + Выводить дополнительную сетевую отладочную информацию + + + + Prepend debug output with timestamp (default: 1) + Дописывать отметки времени к отладочному выводу + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + +Параметры SSL: (см. CryptoLottoToken Wiki для инструкций по настройке SSL) + + + + Select the version of socks proxy to use (4-5, default: 5) + Выбрать версию SOCKS-прокси (4-5, по умолчанию: 5) + + + + Send trace/debug info to console instead of debug.log file + Выводить информацию трассировки/отладки на консоль вместо файла debug.log + + + + Send trace/debug info to debugger + Отправлять информацию трассировки/отладки в отладчик + + + + Set maximum block size in bytes (default: 250000) + Максимальный размер блока в байтах (по умолчанию: 250000) + + + + Set minimum block size in bytes (default: 0) + Минимальный размер блока в байтах (по умолчанию: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Сжимать файл debug.log при запуске клиента (по умолчанию: 1, если нет -debug) + + + + Signing transaction failed + Не удалось подписать транзакцию + + + + Specify connection timeout in milliseconds (default: 5000) + Таймаут соединения в миллисекундах (по умолчанию: 5000) + + + + System error: + Системная ошибка: + + + + Transaction amount too small + Объём транзакции слишком мал + + + + Transaction amounts must be positive + Объём транзакции должен быть положителен + + + + Transaction too large + Транзакция слишком большая + + + + Use UPnP to map the listening port (default: 0) + Использовать UPnP для проброса порта (по умолчанию: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Использовать UPnP для проброса порта (по умолчанию: 1, если используется прослушивание) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Использовать прокси для скрытых сервисов (по умолчанию: тот же, что и в -proxy) + + + + Username for JSON-RPC connections + Имя для подключений JSON-RPC + + + + Warning + Внимание + + + + Warning: This version is obsolete, upgrade required! + Внимание: эта версия устарела, требуется обновление! + + + + You need to rebuild the databases using -reindex to change -txindex + Вам необходимо пересобрать базы данных с помощью -reindex, чтобы изменить -txindex + + + + wallet.dat corrupt, salvage failed + wallet.dat повреждён, спасение данных не удалось + + + + Password for JSON-RPC connections + Пароль для подключений JSON-RPC + + + + Allow JSON-RPC connections from specified IP address + Разрешить подключения JSON-RPC с указанного IP + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Посылать команды узлу, запущенному на <ip> (по умолчанию: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Выполнить команду, когда появляется новый блок (%s в команде заменяется на хэш блока) + + + + Upgrade wallet to latest format + Обновить бумажник до последнего формата + + + + Set key pool size to <n> (default: 100) + Установить размер запаса ключей в <n> (по умолчанию: 100) + + + + Rescan the block chain for missing wallet transactions + Перепроверить цепь блоков на предмет отсутствующих в бумажнике транзакций + + + + Use OpenSSL (https) for JSON-RPC connections + Использовать OpenSSL (https) для подключений JSON-RPC + + + + Server certificate file (default: server.cert) + Файл серверного сертификата (по умолчанию: server.cert) + + + + Server private key (default: server.pem) + Приватный ключ сервера (по умолчанию: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Разрешённые алгоритмы (по умолчанию: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Эта справка + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Невозможно привязаться к %s на этом компьютере (bind вернул ошибку %d, %s) + + + + Connect through socks proxy + Подключаться через socks прокси + + + + Allow DNS lookups for -addnode, -seednode and -connect + Разрешить поиск в DNS для -addnode, -seednode и -connect + + + + Loading addresses... + Загрузка адресов... + + + + Error loading wallet.dat: Wallet corrupted + Ошибка загрузки wallet.dat: Бумажник поврежден + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Ошибка загрузки wallet.dat: бумажник требует более новую версию CryptoLottoToken + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Необходимо перезаписать бумажник, перезапустите CryptoLottoToken для завершения операции. + + + + Error loading wallet.dat + Ошибка при загрузке wallet.dat + + + + Invalid -proxy address: '%s' + Неверный адрес -proxy: '%s' + + + + Unknown network specified in -onlynet: '%s' + В параметре -onlynet указана неизвестная сеть: '%s' + + + + Unknown -socks proxy version requested: %i + В параметре -socks запрошена неизвестная версия: %i + + + + Cannot resolve -bind address: '%s' + Не удаётся разрешить адрес в параметре -bind: '%s' + + + + Cannot resolve -externalip address: '%s' + Не удаётся разрешить адрес в параметре -externalip: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + Неверное количество в параметре -paytxfee=<кол-во>: '%s' + + + + Invalid amount + Неверное количество + + + + Insufficient funds + Недостаточно монет + + + + Loading block index... + Загрузка индекса блоков... + + + + Add a node to connect to and attempt to keep the connection open + Добавить узел для подключения и пытаться поддерживать соединение открытым + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Невозможно привязаться к %s на этом компьютере. Возможно, CryptoLottoToken уже работает. + + + + Fee per KB to add to transactions you send + Комиссия на килобайт, добавляемая к вашим транзакциям + + + + Loading wallet... + Загрузка бумажника... + + + + Cannot downgrade wallet + Не удаётся понизить версию бумажника + + + + Cannot write default address + Не удаётся записать адрес по умолчанию + + + + Rescanning... + Сканирование... + + + + Done loading + Загрузка завершена + + + + To use the %s option + Чтобы использовать опцию %s + + + + Error + Ошибка + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + Вы должны установить rpcpassword=<password> в конфигурационном файле: +%s +Если файл не существует, создайте его и установите права доступа только для владельца. + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_sk.qm b/src/qt/locale/bitcoin_sk.qm new file mode 100644 index 0000000..30fdfba Binary files /dev/null and b/src/qt/locale/bitcoin_sk.qm differ diff --git a/src/qt/locale/bitcoin_sk.ts b/src/qt/locale/bitcoin_sk.ts new file mode 100644 index 0000000..2271a58 --- /dev/null +++ b/src/qt/locale/bitcoin_sk.ts @@ -0,0 +1,2922 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + O CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> verzia + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + Adresár + + + + Double-click to edit address or label + Dvojklikom editovať adresu alebo popis + + + + Create a new address + Vytvoriť novú adresu + + + + Copy the currently selected address to the system clipboard + Kopírovať práve zvolenú adresu do systémového klipbordu + + + + &New Address + &Nová adresa + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Toto sú Vaše CryptoLottoToken adresy pre prijímanie platieb. Môžete dať každému odosielateľovi inú rôznu adresu a tak udržiavať prehľad o platbách. + + + + &Copy Address + &Kopírovať adresu + + + + Show &QR Code + Zobraz &QR Kód + + + + Sign a message to prove you own a CryptoLottoToken address + Podpísať správu a dokázať že vlastníte túto adresu + + + + Sign &Message + Podpísať &správu + + + + Delete the currently selected address from the list + + + + + Export the data in the current tab to a file + Exportovať tento náhľad do súboru + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + + + + + &Verify Message + + + + + &Delete + &Zmazať + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + Kopírovať &popis + + + + &Edit + &Upraviť + + + + Send &Coins + + + + + Export Address Book Data + Exportovať dáta z adresára + + + + Comma separated file (*.csv) + Čiarkou oddelený súbor (*.csv) + + + + Error exporting + Chyba exportu. + + + + Could not write to file %1. + Nedalo sa zapisovať do súboru %1. + + + + AddressTableModel + + + Label + Popis + + + + Address + Adresa + + + + (no label) + (bez popisu) + + + + AskPassphraseDialog + + + Passphrase Dialog + + + + + Enter passphrase + Zadajte heslo + + + + New passphrase + Nové heslo + + + + Repeat new passphrase + Zopakujte nové heslo + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Zadajte nové heslo k peňaženke.<br/>Prosím použite heslo s dĺžkou aspon <b>10 alebo viac náhodných znakov</b>, alebo <b>8 alebo viac slov</b>. + + + + Encrypt wallet + Zašifrovať peňaženku + + + + This operation needs your wallet passphrase to unlock the wallet. + Táto operácia potrebuje heslo k vašej peňaženke aby ju mohla dešifrovať. + + + + Unlock wallet + Odomknúť peňaženku + + + + This operation needs your wallet passphrase to decrypt the wallet. + Táto operácia potrebuje heslo k vašej peňaženke na dešifrovanie peňaženky. + + + + Decrypt wallet + Dešifrovať peňaženku + + + + Change passphrase + Zmena hesla + + + + Enter the old and new passphrase to the wallet. + Zadajte staré a nové heslo k peňaženke. + + + + Confirm wallet encryption + Potvrďte šifrovanie peňaženky + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Varovanie: Ak zašifrujete peňaženku a stratíte heslo, <b>STRATÍTE VŠETKY VAŠE CryptoLottoTokenY</b>!⏎ + + + + Are you sure you wish to encrypt your wallet? + Ste si istí, že si želáte zašifrovať peňaženku? + + + + 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. + + + + + + Warning: The Caps Lock key is on! + Varovanie: Caps Lock je zapnutý + + + + + Wallet encrypted + Peňaženka zašifrovaná + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken sa teraz ukončí pre dokončenie procesu šifrovania. Pamätaj že šifrovanie peňaženky Ťa nemôže úplne ochrániť pred kráďežou CryptoLottoTokenov pomocou škodlivého software. + + + + + + + Wallet encryption failed + Šifrovanie peňaženky zlyhalo + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Šifrovanie peňaženky zlyhalo kôli internej chybe. Vaša peňaženka nebola zašifrovaná. + + + + + The supplied passphrases do not match. + Zadané heslá nesúhlasia. + + + + Wallet unlock failed + Odomykanie peňaženky zlyhalo + + + + + + The passphrase entered for the wallet decryption was incorrect. + Zadané heslo pre dešifrovanie peňaženky bolo nesprávne. + + + + Wallet decryption failed + Zlyhalo šifrovanie peňaženky. + + + + Wallet passphrase was successfully changed. + Heslo k peňaženke bolo úspešne zmenené. + + + + BitcoinGUI + + + Sign &message... + Podpísať &správu... + + + + Synchronizing with network... + Synchronizácia so sieťou... + + + + &Overview + &Prehľad + + + + Show general overview of wallet + Zobraziť celkový prehľad o peňaženke + + + + &Transactions + &Transakcie + + + + Browse transaction history + Prechádzať históriu transakcií + + + + Edit the list of stored addresses and labels for sending + Editovať zoznam uložených adries a popisov + + + + Show the list of addresses for receiving payments + Zobraziť zoznam adries pre prijímanie platieb. + + + + E&xit + U&končiť + + + + Quit application + Ukončiť program + + + + Show information about CryptoLottoToken + Zobraziť informácie o CryptoLottoToken + + + + About &Qt + O &Qt + + + + Show information about Qt + Zobrazit informácie o Qt + + + + &Options... + &Možnosti... + + + + &Encrypt Wallet... + &Zašifrovať Peňaženku... + + + + &Backup Wallet... + &Backup peňaženku... + + + + &Change Passphrase... + &Zmena Hesla... + + + + Importing blocks from disk... + + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + Poslať CryptoLottoTokens na adresu + + + + Modify configuration options for CryptoLottoToken + Upraviť možnosti nastavenia pre CryptoLottoToken + + + + Backup wallet to another location + Zálohovať peňaženku na iné miesto + + + + Change the passphrase used for wallet encryption + Zmeniť heslo použité na šifrovanie peňaženky + + + + &Debug window + &Okno pre ladenie + + + + Open debugging and diagnostic console + Otvor konzolu pre ladenie a diagnostiku + + + + &Verify message... + + + + + + CryptoLottoToken + + + + + Wallet + Peňaženka + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + &O CryptoLottoToken + + + + &Show / Hide + + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + &Súbor + + + + &Settings + &Nastavenia + + + + &Help + &Pomoc + + + + Tabs toolbar + Lišta záložiek + + + + + [testnet] + [testovacia sieť] + + + + CryptoLottoToken client + CryptoLottoToken klient + + + + %n active connection(s) to CryptoLottoToken network + %n aktívne spojenie v CryptoLottoToken sieti%n aktívne spojenia v CryptoLottoToken sieti%n aktívnych spojení v Bitconi sieti + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + + + + + Warning + + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + Aktualizovaný + + + + Catching up... + Sťahujem... + + + + Confirm transaction fee + Potvrď poplatok za transakciu. + + + + Sent transaction + Odoslané transakcie + + + + Incoming transaction + Prijaté transakcie + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Dátum: %1 +Suma: %2 +Typ: %3 +Adresa: %4 + + + + + URI handling + + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Peňaženka je <b>zašifrovaná</b> a momentálne <b>odomknutá</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Peňaženka je <b>zašifrovaná</b> a momentálne <b>zamknutá</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + + + + + EditAddressDialog + + + Edit Address + Upraviť adresu + + + + &Label + &Popis + + + + The label associated with this address book entry + Popis priradený k tomuto záznamu v adresári + + + + &Address + &Adresa + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Adresa spojená s týmto záznamom v adresári. Možno upravovať len pre odosielajúce adresy. + + + + New receiving address + Nová adresa pre prijímanie + + + + New sending address + Nová adresa pre odoslanie + + + + Edit receiving address + Upraviť prijímacie adresy + + + + Edit sending address + Upraviť odosielaciu adresu + + + + The entered address "%1" is already in the address book. + Vložená adresa "%1" sa už nachádza v adresári. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + Vložená adresa "%1" nieje platnou adresou CryptoLottoToken. + + + + Could not unlock wallet. + Nepodarilo sa odomknúť peňaženku. + + + + New key generation failed. + Generovanie nového kľúča zlyhalo. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + + + + + version + verzia + + + + Usage: + Použitie: + + + + command-line options + + + + + UI options + UI možnosti + + + + Set language, for example "de_DE" (default: system locale) + + + + + Start minimized + Spustiť minimalizované + + + + Show splash screen on startup (default: 1) + + + + + OptionsDialog + + + Options + Možnosti + + + + &Main + &Hlavné + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + Zaplatiť transakčné &poplatky + + + + Automatically start CryptoLottoToken after logging in to the system. + Automaticky spustiť CryptoLottoToken po zapnutí počítača + + + + &Start CryptoLottoToken on system login + &Spustiť CryptoLottoToken pri spustení systému správy okien + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Automaticky otvorit port pre CryptoLottoToken na routeri. Toto funguje len ak router podporuje UPnP a je táto podpora aktivovaná. + + + + Map port using &UPnP + Mapovať port pomocou &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Pripojiť do siete CryptoLottoToken cez SOCKS proxy (napr. keď sa pripájate cez Tor) + + + + &Connect through SOCKS proxy: + &Pripojiť cez SOCKS proxy: + + + + Proxy &IP: + + + + + IP address of the proxy (e.g. 127.0.0.1) + IP addresa proxy (napr. 127.0.0.1) + + + + &Port: + + + + + Port of the proxy (e.g. 9050) + Port proxy (napr. 9050) + + + + SOCKS &Version: + + + + + SOCKS version of the proxy (e.g. 5) + + + + + &Window + + + + + Show only a tray icon after minimizing the window. + Zobraziť len ikonu na lište po minimalizovaní okna. + + + + &Minimize to the tray instead of the taskbar + Zobraziť len ikonu na lište po minimalizovaní okna. + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Minimalizovat namiesto ukončenia aplikácie keď sa okno zavrie. Keď je zvolená táto možnosť, aplikácia sa zavrie len po zvolení Ukončiť v menu. + + + + M&inimize on close + M&inimalizovať pri zavretí + + + + &Display + &Displej + + + + User Interface &language: + + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + + + + + &Unit to show amounts in: + &Zobrazovať hodnoty v jednotkách: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + + + &Display addresses in transaction list + &Zobraziť adresy zo zoznamu transakcií + + + + &OK + + + + + &Cancel + + + + + &Apply + + + + + default + + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + Varovanie + + + + + This setting will take effect after restarting CryptoLottoToken. + + + + + The supplied proxy address is invalid. + + + + + OverviewPage + + + Form + Forma + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + + + + + Balance: + Zostatok: + + + + Unconfirmed: + Nepotvrdené: + + + + Wallet + Peňaženka + + + + Immature: + + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + <b>Nedávne transakcie</b> + + + + Your current balance + Váš súčasný zostatok + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Suma transakcií ktoré ešte neboli potvrdené a nezapočítavaju sa do celkového zostatku. + + + + + out of sync + + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + + + + + Request Payment + Vyžiadať platbu + + + + Amount: + Suma: + + + + Label: + Popis: + + + + Message: + Správa: + + + + &Save As... + &Uložiť ako... + + + + Error encoding URI into QR Code. + Chyba v zakódovaní URI do QR kódu + + + + The entered amount is invalid, please check. + + + + + Resulting URI too long, try to reduce the text for label / message. + Výsledné URI príliš dlhé, skráť text pre názov / správu. + + + + Save QR Code + Ukladanie QR kódu + + + + PNG Images (*.png) + PNG obrázky (*.png) + + + + RPCConsole + + + Client name + Meno klienta + + + + + + + + + + + + + N/A + nie je k dispozícii + + + + Client version + Verzia klienta + + + + &Information + + + + + Using OpenSSL version + + + + + Startup time + + + + + Network + Sieť + + + + Number of connections + Počet pripojení + + + + On testnet + Na testovacej sieti + + + + Block chain + Reťazec blokov + + + + Current number of blocks + Aktuálny počet blokov + + + + Estimated total blocks + + + + + Last block time + + + + + &Open + + + + + Command-line options + + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + + &Show + + + + + &Console + + + + + Build date + + + + + CryptoLottoToken - Debug window + + + + + CryptoLottoToken Core + + + + + Debug log file + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + + + + + Welcome to the CryptoLottoToken RPC console. + + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + + + + + Type <b>help</b> for an overview of available commands. + + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Poslať CryptoLottoTokens + + + + Send to multiple recipients at once + Poslať viacerým príjemcom naraz + + + + Add &Recipient + &Pridať príjemcu + + + + Remove all transaction fields + Odobrať všetky políčka transakcie + + + + Clear &All + Zmazať &všetko + + + + Balance: + Zostatok: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Potvrďte odoslanie + + + + S&end + &Odoslať + + + + <b>%1</b> to %2 (%3) + <b>%1</b> do %2 (%3) + + + + Confirm send coins + Potvrdiť odoslanie CryptoLottoTokens + + + + Are you sure you want to send %1? + Ste si istí, že chcete odoslať %1? + + + + and + a + + + + The recipient address is not valid, please recheck. + Adresa príjemcu je neplatná, prosím, overte ju. + + + + The amount to pay must be larger than 0. + Suma na úhradu musí byť väčšia ako 0. + + + + The amount exceeds your balance. + Suma je vyššia ako Váš zostatok. + + + + The total exceeds your balance when the %1 transaction fee is included. + Suma celkom prevyšuje Váš zostatok ak sú započítané %1 transakčné poplatky. + + + + Duplicate address found, can only send to each address once per send operation. + Duplikát adresy objavený, je možné poslať na každú adresu len raz v jednej odchádzajúcej transakcii. + + + + Error: Transaction creation failed! + + + + + 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. + Chyba: Transakcia bola odmietnutá. Toto sa môže stať ak niektoré z mincí vo vašej peňaženke boli už utratené, napríklad ak používaš kópiu wallet.dat a mince označené v druhej kópií neboli označené ako utratené v tejto. + + + + SendCoinsEntry + + + Form + Forma + + + + A&mount: + Su&ma: + + + + Pay &To: + Zapla&tiť: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + Vložte popis pre túto adresu aby sa pridala do adresára + + + + &Label: + &Popis: + + + + Choose address from address book + Zvoľte adresu z adresára + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Vložiť adresu z klipbordu + + + + Alt+P + Alt+P + + + + Remove this recipient + Odstrániť tohto príjemcu + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Zadajte CryptoLottoToken adresu (napr. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + + + + + &Sign Message + &Podpísať Správu + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Môžete podpísať správy svojou adresou a dokázať, že ju vlastníte. Buďte opatrní a podpíšte len prehlásenia s ktorými plne súhlasíte, nakoľko útoky typu "phishing" Vás môžu lákať k ich podpísaniu. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Zadajte CryptoLottoToken adresu (napr. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Zvoľte adresu z adresára + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Vložte adresu z klipbordu + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Sem vložte správu ktorú chcete podpísať + + + + Signature + + + + + Copy the current signature to the system clipboard + + + + + Sign the message to prove you own this CryptoLottoToken address + Podpíšte správu aby ste dokázali že vlastníte túto adresu + + + + Sign &Message + + + + + Reset all sign message fields + + + + + + Clear &All + Zmazať &všetko + + + + &Verify Message + + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Zadajte CryptoLottoToken adresu (napr. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + + + + + Verify &Message + + + + + Reset all verify message fields + + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Zadajte CryptoLottoToken adresu (napr. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Kliknite "Podpísať Správu" na získanie podpisu + + + + Enter CryptoLottoToken signature + + + + + + The entered address is invalid. + + + + + + + + Please check the address and try again. + + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + + + + + Private key for the entered address is not available. + + + + + Message signing failed. + + + + + Message signed. + + + + + The signature could not be decoded. + + + + + + Please check the signature and try again. + + + + + The signature did not match the message digest. + + + + + Message verification failed. + + + + + Message verified. + + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + [testovacia sieť] + + + + TransactionDesc + + + Open until %1 + Otvorené do %1 + + + + %1/offline + + + + + %1/unconfirmed + %1/nepotvrdené + + + + %1 confirmations + %1 potvrdení + + + + Status + Stav + + + + , broadcast through %n node(s) + + + + + Date + Dátum + + + + Source + + + + + Generated + + + + + + From + od + + + + + + To + + + + + + own address + + + + + label + popis + + + + + + + + Credit + Kredit + + + + matures in %n more block(s) + + + + + not accepted + neprijaté + + + + + + + Debit + Debet + + + + Transaction fee + Transakčný poplatok + + + + Net amount + Suma netto + + + + Message + Správa + + + + Comment + Komentár + + + + Transaction ID + ID transakcie + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + + + + + Debug information + + + + + Transaction + Transakcie + + + + Inputs + + + + + Amount + Suma + + + + true + + + + + false + + + + + , has not been successfully broadcast yet + , ešte nebola úspešne odoslaná + + + + Open for %n more block(s) + + + + + unknown + neznámy + + + + TransactionDescDialog + + + Transaction details + Detaily transakcie + + + + This pane shows a detailed description of the transaction + Táto časť obrazovky zobrazuje detailný popis transakcie + + + + TransactionTableModel + + + Date + Dátum + + + + Type + Typ + + + + Address + Adresa + + + + Amount + Hodnota + + + + Open for %n more block(s) + + + + + Open until %1 + Otvorené do %1 + + + + Offline (%1 confirmations) + Offline (%1 potvrdení) + + + + Unconfirmed (%1 of %2 confirmations) + Nepotvrdené (%1 z %2 potvrdení) + + + + Confirmed (%1 confirmations) + Potvrdené (%1 potvrdení) + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + Ten blok nebol prijatý žiadnou inou nódou a pravdepodobne nebude akceptovaný! + + + + Generated but not accepted + Vypočítané ale neakceptované + + + + Received with + Prijaté s + + + + Received from + Prijaté od: + + + + Sent to + Odoslané na + + + + Payment to yourself + Platba sebe samému + + + + Mined + Vyfárané + + + + (n/a) + (n/a) + + + + Transaction status. Hover over this field to show number of confirmations. + Status transakcie. Pohybujte myšou nad týmto poľom a zjaví sa počet potvrdení. + + + + Date and time that the transaction was received. + Dátum a čas prijatia transakcie. + + + + Type of transaction. + Typ transakcie. + + + + Destination address of transaction. + Cieľová adresa transakcie. + + + + Amount removed from or added to balance. + Suma pridaná alebo odobraná k zostatku. + + + + TransactionView + + + + All + Všetko + + + + Today + Dnes + + + + This week + Tento týždeň + + + + This month + Tento mesiac + + + + Last month + Minulý mesiac + + + + This year + Tento rok + + + + Range... + Rozsah... + + + + Received with + Prijaté s + + + + Sent to + Odoslané na + + + + To yourself + Samému sebe + + + + Mined + Vyfárané + + + + Other + Iné + + + + Enter address or label to search + Vložte adresu alebo popis pre vyhľadávanie + + + + Min amount + Min množstvo + + + + Copy address + Kopírovať adresu + + + + Copy label + Kopírovať popis + + + + Copy amount + Kopírovať sumu + + + + Copy transaction ID + + + + + Edit label + Editovať popis + + + + Show transaction details + + + + + Export Transaction Data + Exportovať transakčné dáta + + + + Comma separated file (*.csv) + Čiarkou oddelovaný súbor (*.csv) + + + + Confirmed + Potvrdené + + + + Date + Dátum + + + + Type + Typ + + + + Label + Popis + + + + Address + Adresa + + + + Amount + Suma + + + + ID + ID + + + + Error exporting + Chyba exportu + + + + Could not write to file %1. + Nedalo sa zapisovať do súboru %1. + + + + Range: + Rozsah: + + + + to + do + + + + WalletModel + + + Send Coins + Poslať CryptoLottoTokens + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + Exportovať tento náhľad do súboru + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + CryptoLottoToken verzia + + + + Usage: + Použitie: + + + + Send command to -server or CryptoLottoTokend + Odoslať príkaz -server alebo CryptoLottoTokend + + + + List commands + Zoznam príkazov + + + + Get help for a command + Dostať pomoc pre príkaz + + + + Options: + Možnosti: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Určiť súbor s nastaveniami (predvolené: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + Určiť súbor pid (predvolené: CryptoLottoTokend.pid) + + + + Specify data directory + Určiť priečinok s dátami + + + + Set database cache size in megabytes (default: 25) + Veľkosť vyrovnávajúcej pamäte pre databázu v megabytoch (predvolené:25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Načúvať spojeniam na <port> (prednastavené: 22556 alebo testovacia sieť: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Udržiavať maximálne <n> spojení (predvolené: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + + + + + Specify your own public address + + + + + Threshold for disconnecting misbehaving peers (default: 100) + Hranica pre odpojenie zle sa správajúcich peerov (predvolené: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Počet sekúnd kedy sa zabráni zle sa správajúcim peerom znovupripojenie (predvolené: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Počúvať JSON-RPC spojeniam na <port> (predvolené: 22555 or testnet: 44555) + + + + Accept command line and JSON-RPC commands + Prijímať príkazy z príkazového riadku a JSON-RPC + + + + Run in the background as a daemon and accept commands + Bežať na pozadí ako démon a prijímať príkazy + + + + Use the test network + Použiť testovaciu sieť + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Varovanie: -paytxfee je nastavené veľmi vysoko. Toto sú transakčné poplatky ktoré zaplatíte ak odošlete transakciu. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + + + + + Connect only to the specified node(s) + Pripojiť sa len k určenej nóde + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + Neplatná adresa tor: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + Produkovať extra ladiace informácie. Implies all other -debug* options + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + Pridať na začiatok ladiaceho výstupu časový údaj + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + SSL možnosť: (pozrite CryptoLottoToken Wiki pre návod na nastavenie SSL) + + + + Select the version of socks proxy to use (4-5, default: 5) + + + + + Send trace/debug info to console instead of debug.log file + Odoslať trace/debug informácie na konzolu namiesto debug.info žurnálu + + + + Send trace/debug info to debugger + Odoslať trace/debug informácie do ladiaceho programu + + + + Set maximum block size in bytes (default: 250000) + + + + + Set minimum block size in bytes (default: 0) + + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + Určiť aut spojenia v milisekundách (predvolené: 5000) + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + Skúsiť použiť UPnP pre mapovanie počúvajúceho portu (default: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Skúsiť použiť UPnP pre mapovanie počúvajúceho portu (default: 1 when listening) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + Užívateľské meno pre JSON-RPC spojenia + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + Heslo pre JSON-rPC spojenia + + + + Allow JSON-RPC connections from specified IP address + Povoliť JSON-RPC spojenia z určenej IP adresy. + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Poslať príkaz nóde bežiacej na <ip> (predvolené: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Vykonaj príkaz, ak zmeny v najlepšom bloku (%s v príkaze nahradí blok hash) + + + + Upgrade wallet to latest format + Aktualizuj peňaženku na najnovší formát. + + + + Set key pool size to <n> (default: 100) + Nastaviť zásobu adries na <n> (predvolené: 100) + + + + Rescan the block chain for missing wallet transactions + Znovu skenovať reťaz blokov pre chýbajúce transakcie + + + + Use OpenSSL (https) for JSON-RPC connections + Použiť OpenSSL (https) pre JSON-RPC spojenia + + + + Server certificate file (default: server.cert) + Súbor s certifikátom servra (predvolené: server.cert) + + + + Server private key (default: server.pem) + Súkromný kľúč servra (predvolené: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Prijateľné šifry (predvolené: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Táto pomocná správa + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + + + + + Connect through socks proxy + Pripojenie cez socks proxy + + + + Allow DNS lookups for -addnode, -seednode and -connect + Povoliť vyhľadávanie DNS pre pridanie nódy a spojenie + + + + Loading addresses... + Načítavanie adries... + + + + Error loading wallet.dat: Wallet corrupted + Chyba načítania wallet.dat: Peňaženka je poškodená + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Chyba načítania wallet.dat: Peňaženka vyžaduje novšiu verziu CryptoLottoToken + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Bolo potrebné prepísať peňaženku: dokončite reštartovaním CryptoLottoToken + + + + Error loading wallet.dat + Chyba načítania wallet.dat + + + + Invalid -proxy address: '%s' + Neplatná adresa proxy: '%s' + + + + Unknown network specified in -onlynet: '%s' + + + + + Unknown -socks proxy version requested: %i + + + + + Cannot resolve -bind address: '%s' + + + + + Cannot resolve -externalip address: '%s' + + + + + Invalid amount for -paytxfee=<amount>: '%s' + Neplatná suma pre -paytxfee=<amount>: '%s' + + + + Invalid amount + Neplatná suma + + + + Insufficient funds + Nedostatok prostriedkov + + + + Loading block index... + Načítavanie zoznamu blokov... + + + + Add a node to connect to and attempt to keep the connection open + Pridať nód na pripojenie a pokus o udržanie pripojenia otvoreného + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + + + + + Fee per KB to add to transactions you send + Poplatok za kB ktorý treba pridať k odoslanej transakcii + + + + Loading wallet... + Načítavam peňaženku... + + + + Cannot downgrade wallet + Nie je možné prejsť na nižšiu verziu peňaženky + + + + Cannot write default address + Nie je možné zapísať predvolenú adresu. + + + + Rescanning... + + + + + Done loading + Dokončené načítavanie + + + + To use the %s option + Použiť %s možnosť. + + + + Error + Chyba + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + Musíš nastaviť rpcpassword=<heslo> v konfiguračnom súbore: +%s +Ak súbor neexistuje, vytvor ho s oprávnením pre čítanie len vlastníkom (owner-readable-only) + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_sr.qm b/src/qt/locale/bitcoin_sr.qm new file mode 100644 index 0000000..4ba186f Binary files /dev/null and b/src/qt/locale/bitcoin_sr.qm differ diff --git a/src/qt/locale/bitcoin_sr.ts b/src/qt/locale/bitcoin_sr.ts new file mode 100644 index 0000000..96822a7 --- /dev/null +++ b/src/qt/locale/bitcoin_sr.ts @@ -0,0 +1,2919 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + О CryptoLottoToken-у + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> верзија + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + Адресар + + + + Double-click to edit address or label + Кликните два пута да промените адресу и/или етикету + + + + Create a new address + Прави нову адресу + + + + Copy the currently selected address to the system clipboard + Копира изабрану адресу на системски клипборд + + + + &New Address + &Нова адреса + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Ово су Ваше CryptoLottoToken адресе за примање уплата. Можете да сваком пошиљаоцу дате другачију адресу да би пратили ко је вршио уплате. + + + + &Copy Address + + + + + Show &QR Code + Prikaži &QR kod + + + + Sign a message to prove you own a CryptoLottoToken address + + + + + Sign &Message + + + + + Delete the currently selected address from the list + + + + + Export the data in the current tab to a file + + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + + + + + &Verify Message + + + + + &Delete + &Избриши + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + + + + + &Edit + + + + + Send &Coins + + + + + Export Address Book Data + Извоз података из адресара + + + + Comma separated file (*.csv) + Зарезом одвојене вредности (*.csv) + + + + Error exporting + Грешка током извоза + + + + Could not write to file %1. + Није могуће писати у фајл %1. + + + + AddressTableModel + + + Label + Етикета + + + + Address + Адреса + + + + (no label) + (без етикете) + + + + AskPassphraseDialog + + + Passphrase Dialog + + + + + Enter passphrase + Унесите лозинку + + + + New passphrase + Нова лозинка + + + + Repeat new passphrase + Поновите нову лозинку + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Унесите нову лозинку за приступ новчанику.<br/>Молимо Вас да лозинка буде <b>10 или више насумице одабраних знакова</b>, или <b>осам или више речи</b>. + + + + Encrypt wallet + Шифровање новчаника + + + + This operation needs your wallet passphrase to unlock the wallet. + Ова акција захтева лозинку Вашег новчаника да би га откључала. + + + + Unlock wallet + Откључавање новчаника + + + + This operation needs your wallet passphrase to decrypt the wallet. + Ова акција захтева да унесете лозинку да би дешифловала новчаник. + + + + Decrypt wallet + Дешифровање новчаника + + + + Change passphrase + Промена лозинке + + + + Enter the old and new passphrase to the wallet. + Унесите стару и нову лозинку за шифровање новчаника. + + + + Confirm wallet encryption + Одобрите шифровање новчаника + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Упозорење: Ако се ваш новчаник шифрује а потом изгубите лозинкзу, ви ћете <b>ИЗГУБИТИ СВЕ CryptoLottoToken-Е</b>! + + + + Are you sure you wish to encrypt your wallet? + Да ли сте сигурни да желите да се новчаник шифује? + + + + 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. + + + + + + Warning: The Caps Lock key is on! + + + + + + Wallet encrypted + Новчаник је шифрован + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + CryptoLottoToken će se sad zatvoriti da bi završio proces enkripcije. Zapamti da enkripcija tvog novčanika ne može u potpunosti da zaštiti tvoje CryptoLottoTokene da ne budu ukradeni od malawarea koji bi inficirao tvoj kompjuter. + + + + + + + Wallet encryption failed + Неуспело шифровање новчаника + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Настала је унутрашња грешка током шифровања новчаника. Ваш новчаник није шифрован. + + + + + The supplied passphrases do not match. + Лозинке које сте унели се не подударају. + + + + Wallet unlock failed + Неуспело откључавање новчаника + + + + + + The passphrase entered for the wallet decryption was incorrect. + Лозинка коју сте унели за откључавање новчаника је нетачна. + + + + Wallet decryption failed + Неуспело дешифровање новчаника + + + + Wallet passphrase was successfully changed. + Лозинка за приступ новчанику је успешно промењена. + + + + BitcoinGUI + + + Sign &message... + + + + + Synchronizing with network... + Синхронизација са мрежом у току... + + + + &Overview + &Општи преглед + + + + Show general overview of wallet + Погледајте општи преглед новчаника + + + + &Transactions + &Трансакције + + + + Browse transaction history + Претражите историјат трансакција + + + + Edit the list of stored addresses and labels for sending + Уредите запамћене адресе и њихове етикете + + + + Show the list of addresses for receiving payments + Прегледајте листу адреса на којима прихватате уплате + + + + E&xit + I&zlaz + + + + Quit application + Напустите програм + + + + Show information about CryptoLottoToken + Прегледајте информације о CryptoLottoToken-у + + + + About &Qt + О &Qt-у + + + + Show information about Qt + Прегледајте информације о Qt-у + + + + &Options... + П&оставке... + + + + &Encrypt Wallet... + &Шифровање новчаника... + + + + &Backup Wallet... + &Backup новчаника + + + + &Change Passphrase... + Промени &лозинку... + + + + Importing blocks from disk... + + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + Пошаљите новац на CryptoLottoToken адресу + + + + Modify configuration options for CryptoLottoToken + Изаберите могућности CryptoLottoToken-а + + + + Backup wallet to another location + + + + + Change the passphrase used for wallet encryption + Мењање лозинке којом се шифрује новчаник + + + + &Debug window + + + + + Open debugging and diagnostic console + + + + + &Verify message... + + + + + + CryptoLottoToken + + + + + Wallet + новчаник + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + &О CryptoLottoToken-у + + + + &Show / Hide + + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + &Фајл + + + + &Settings + &Подешавања + + + + &Help + П&омоћ + + + + Tabs toolbar + Трака са картицама + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + + + + + %n active connection(s) to CryptoLottoToken network + %n активна веза са CryptoLottoToken мрежом%n активне везе са CryptoLottoToken мрежом%n активних веза са CryptoLottoToken мрежом + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + + + + + Warning + + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + Ажурно + + + + Catching up... + Ажурирање у току... + + + + Confirm transaction fee + + + + + Sent transaction + Послана трансакција + + + + Incoming transaction + Придошла трансакција + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Datum: %1⏎ Iznos: %2⏎ Tip: %3⏎ Adresa: %4⏎ + + + + + URI handling + + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Новчаник јс <b>шифрован</b> и тренутно <b>откључан</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Новчаник јс <b>шифрован</b> и тренутно <b>закључан</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + + + + + EditAddressDialog + + + Edit Address + Измени адресу + + + + &Label + &Етикета + + + + The label associated with this address book entry + + + + + &Address + &Адреса + + + + The address associated with this address book entry. This can only be modified for sending addresses. + + + + + New receiving address + + + + + New sending address + + + + + Edit receiving address + + + + + Edit sending address + + + + + The entered address "%1" is already in the address book. + Унешена адреса "%1" се већ налази у адресару. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + + + + + Could not unlock wallet. + Немогуће откључати новчаник. + + + + New key generation failed. + + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + + + + + version + верзија + + + + Usage: + Korišćenje: + + + + command-line options + + + + + UI options + + + + + Set language, for example "de_DE" (default: system locale) + + + + + Start minimized + + + + + Show splash screen on startup (default: 1) + + + + + OptionsDialog + + + Options + Поставке + + + + &Main + + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + + + + + Automatically start CryptoLottoToken after logging in to the system. + + + + + &Start CryptoLottoToken on system login + + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + + + + + Map port using &UPnP + + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + + + + + &Connect through SOCKS proxy: + + + + + Proxy &IP: + + + + + IP address of the proxy (e.g. 127.0.0.1) + + + + + &Port: + + + + + Port of the proxy (e.g. 9050) + + + + + SOCKS &Version: + + + + + SOCKS version of the proxy (e.g. 5) + + + + + &Window + + + + + Show only a tray icon after minimizing the window. + + + + + &Minimize to the tray instead of the taskbar + + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + + + + + M&inimize on close + + + + + &Display + + + + + User Interface &language: + + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + + + + + &Unit to show amounts in: + &Јединица за приказивање износа: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + + + &Display addresses in transaction list + + + + + &OK + + + + + &Cancel + + + + + &Apply + + + + + default + + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + + + + + + This setting will take effect after restarting CryptoLottoToken. + + + + + The supplied proxy address is invalid. + + + + + OverviewPage + + + Form + Форма + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + + + + + Balance: + + + + + Unconfirmed: + Непотврђено: + + + + Wallet + новчаник + + + + Immature: + + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + <b>Недавне трансакције</b> + + + + Your current balance + + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + + + + + + out of sync + + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + + + + + Request Payment + Zatraži isplatu + + + + Amount: + Iznos: + + + + Label: + &Етикета + + + + Message: + Poruka: + + + + &Save As... + &Snimi kao... + + + + Error encoding URI into QR Code. + + + + + The entered amount is invalid, please check. + + + + + Resulting URI too long, try to reduce the text for label / message. + + + + + Save QR Code + + + + + PNG Images (*.png) + + + + + RPCConsole + + + Client name + + + + + + + + + + + + + + N/A + + + + + Client version + + + + + &Information + + + + + Using OpenSSL version + + + + + Startup time + + + + + Network + + + + + Number of connections + + + + + On testnet + + + + + Block chain + + + + + Current number of blocks + + + + + Estimated total blocks + + + + + Last block time + + + + + &Open + + + + + Command-line options + + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + + &Show + + + + + &Console + + + + + Build date + + + + + CryptoLottoToken - Debug window + + + + + CryptoLottoToken Core + + + + + Debug log file + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + + + + + Welcome to the CryptoLottoToken RPC console. + + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + + + + + Type <b>help</b> for an overview of available commands. + + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Слање новца + + + + Send to multiple recipients at once + + + + + Add &Recipient + + + + + Remove all transaction fields + Ukloni sva polja sa transakcijama + + + + Clear &All + + + + + Balance: + + + + + 123.456 BTC + + + + + Confirm the send action + Потврди акцију слања + + + + S&end + &Пошаљи + + + + <b>%1</b> to %2 (%3) + + + + + Confirm send coins + + + + + Are you sure you want to send %1? + Да ли сте сигурни да желите да пошаљете %1? + + + + and + и + + + + The recipient address is not valid, please recheck. + + + + + The amount to pay must be larger than 0. + + + + + The amount exceeds your balance. + + + + + The total exceeds your balance when the %1 transaction fee is included. + + + + + Duplicate address found, can only send to each address once per send operation. + + + + + Error: Transaction creation failed! + + + + + 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. + + + + + SendCoinsEntry + + + Form + Форма + + + + A&mount: + + + + + Pay &To: + + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + + + + + &Label: + &Етикета + + + + Choose address from address book + Izaberite adresu iz adresara + + + + Alt+A + + + + + Paste address from clipboard + + + + + Alt+P + + + + + Remove this recipient + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Unesite CryptoLottoToken adresu (n.pr. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + + + + + &Sign Message + + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Choose an address from the address book + + + + + + Alt+A + + + + + Paste address from clipboard + + + + + Alt+P + + + + + Enter the message you want to sign here + + + + + Signature + + + + + Copy the current signature to the system clipboard + + + + + Sign the message to prove you own this CryptoLottoToken address + + + + + Sign &Message + + + + + Reset all sign message fields + + + + + + Clear &All + + + + + &Verify Message + + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + + + + + Verify &Message + + + + + Reset all verify message fields + + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Unesite CryptoLottoToken adresu (n.pr. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + + + + + Enter CryptoLottoToken signature + + + + + + The entered address is invalid. + + + + + + + + Please check the address and try again. + + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + + + + + Private key for the entered address is not available. + + + + + Message signing failed. + + + + + Message signed. + + + + + The signature could not be decoded. + + + + + + Please check the signature and try again. + + + + + The signature did not match the message digest. + + + + + Message verification failed. + + + + + Message verified. + + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + Otvorite do %1 + + + + %1/offline + + + + + %1/unconfirmed + %1/nepotvrdjeno + + + + %1 confirmations + %1 potvrde + + + + Status + + + + + , broadcast through %n node(s) + + + + + Date + datum + + + + Source + + + + + Generated + + + + + + From + + + + + + + To + + + + + + own address + + + + + label + етикета + + + + + + + + Credit + + + + + matures in %n more block(s) + + + + + not accepted + + + + + + + + Debit + + + + + Transaction fee + + + + + Net amount + + + + + Message + + + + + Comment + + + + + Transaction ID + + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + + + + + Debug information + + + + + Transaction + + + + + Inputs + + + + + Amount + iznos + + + + true + + + + + false + + + + + , has not been successfully broadcast yet + , nije još uvek uspešno emitovan + + + + Open for %n more block(s) + + + + + unknown + nepoznato + + + + TransactionDescDialog + + + Transaction details + detalji transakcije + + + + This pane shows a detailed description of the transaction + Ovaj odeljak pokazuje detaljan opis transakcije + + + + TransactionTableModel + + + Date + datum + + + + Type + tip + + + + Address + Адреса + + + + Amount + iznos + + + + Open for %n more block(s) + + + + + Open until %1 + Otvoreno do %1 + + + + Offline (%1 confirmations) + Offline * van mreže (%1 potvrdjenih) + + + + Unconfirmed (%1 of %2 confirmations) + Nepotvrdjeno (%1 of %2 potvrdjenih) + + + + Confirmed (%1 confirmations) + Potvrdjena (%1 potvrdjenih) + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + Ovaj blok nije primljen od ostalih čvorova (nodova) i verovatno neće biti prihvaćen! + + + + Generated but not accepted + Generisan ali nije prihvaćen + + + + Received with + Primljen sa + + + + Received from + Primljeno od + + + + Sent to + Poslat ka + + + + Payment to yourself + Isplata samom sebi + + + + Mined + Minirano + + + + (n/a) + (n/a) + + + + Transaction status. Hover over this field to show number of confirmations. + Status vaše transakcije. Predjite mišem preko ovog polja da bi ste videli broj konfirmacija + + + + Date and time that the transaction was received. + Datum i vreme primljene transakcije. + + + + Type of transaction. + Tip transakcije + + + + Destination address of transaction. + Destinacija i adresa transakcije + + + + Amount removed from or added to balance. + Iznos odbijen ili dodat balansu. + + + + TransactionView + + + + All + Sve + + + + Today + Danas + + + + This week + ove nedelje + + + + This month + Ovog meseca + + + + Last month + Prošlog meseca + + + + This year + Ove godine + + + + Range... + Opseg... + + + + Received with + Primljen sa + + + + Sent to + Poslat ka + + + + To yourself + Vama - samom sebi + + + + Mined + Minirano + + + + Other + Drugi + + + + Enter address or label to search + Navedite adresu ili naziv koji bi ste potražili + + + + Min amount + Min iznos + + + + Copy address + kopiraj adresu + + + + Copy label + kopiraj naziv + + + + Copy amount + kopiraj iznos + + + + Copy transaction ID + + + + + Edit label + promeni naziv + + + + Show transaction details + + + + + Export Transaction Data + Izvezi podatke o transakcijama + + + + Comma separated file (*.csv) + Зарезом одвојене вредности (*.csv) + + + + Confirmed + Potvrdjen + + + + Date + datum + + + + Type + tip + + + + Label + Етикета + + + + Address + Адреса + + + + Amount + iznos + + + + ID + + + + + Error exporting + Грешка током извоза + + + + Could not write to file %1. + Није могуће писати у фајл %1. + + + + Range: + Opseg: + + + + to + do + + + + WalletModel + + + Send Coins + Слање новца + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + CryptoLottoToken верзија + + + + Usage: + Korišćenje: + + + + Send command to -server or CryptoLottoTokend + Pošalji naredbu na -server ili CryptoLottoTokenid + + + + + List commands + Listaj komande + + + + Get help for a command + Zatraži pomoć za komande + + + + Options: + Opcije + + + + Specify configuration file (default: CryptoLottoToken.conf) + Potvrdi željeni konfiguracioni fajl (podrazumevani:CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + Konkretizuj pid fajl (podrazumevani: CryptoLottoTokend.pid) + + + + Specify data directory + Gde je konkretni data direktorijum + + + + Set database cache size in megabytes (default: 25) + + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Slušaj konekcije na <port> (default: 22556 or testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Održavaj najviše <n> konekcija po priključku (default: 125) + + + + + Connect to a node to retrieve peer addresses, and disconnect + + + + + Specify your own public address + + + + + Threshold for disconnecting misbehaving peers (default: 100) + + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + + + + + Accept command line and JSON-RPC commands + Prihvati komandnu liniju i JSON-RPC komande + + + + Run in the background as a daemon and accept commands + Radi u pozadini kao daemon servis i prihvati komande + + + + Use the test network + Koristi testnu mrežu + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + + + + + Connect only to the specified node(s) + + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + + + + + Select the version of socks proxy to use (4-5, default: 5) + + + + + Send trace/debug info to console instead of debug.log file + + + + + Send trace/debug info to debugger + + + + + Set maximum block size in bytes (default: 250000) + + + + + Set minimum block size in bytes (default: 0) + + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + + + + + Use UPnP to map the listening port (default: 1 when listening) + + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + Korisničko ime za JSON-RPC konekcije + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + Lozinka za JSON-RPC konekcije + + + + Allow JSON-RPC connections from specified IP address + Dozvoli JSON-RPC konekcije sa posebne IP adrese + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Pošalji komande to nodu koji radi na <ip> (default: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + + + + + Upgrade wallet to latest format + + + + + Set key pool size to <n> (default: 100) + Odredi veličinu zaštićenih ključeva na <n> (default: 100) + + + + Rescan the block chain for missing wallet transactions + Ponovo skeniraj lanac blokova za nedostajuće transakcije iz novčanika + + + + Use OpenSSL (https) for JSON-RPC connections + Koristi OpenSSL (https) za JSON-RPC konekcije + + + + Server certificate file (default: server.cert) + + + + + Server private key (default: server.pem) + privatni ključ za Server (podrazumevan: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Prihvatljive cifre (podrazumevano: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Ova poruka Pomoći + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + + + + + Connect through socks proxy + + + + + Allow DNS lookups for -addnode, -seednode and -connect + + + + + Loading addresses... + učitavam adrese.... + + + + Error loading wallet.dat: Wallet corrupted + + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + + + + + Error loading wallet.dat + + + + + Invalid -proxy address: '%s' + + + + + Unknown network specified in -onlynet: '%s' + + + + + Unknown -socks proxy version requested: %i + + + + + Cannot resolve -bind address: '%s' + + + + + Cannot resolve -externalip address: '%s' + + + + + Invalid amount for -paytxfee=<amount>: '%s' + + + + + Invalid amount + + + + + Insufficient funds + + + + + Loading block index... + Učitavam blok indeksa... + + + + Add a node to connect to and attempt to keep the connection open + + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + + + + + Fee per KB to add to transactions you send + + + + + Loading wallet... + Новчаник се учитава... + + + + Cannot downgrade wallet + + + + + Cannot write default address + + + + + Rescanning... + Ponovo skeniram... + + + + Done loading + Završeno učitavanje + + + + To use the %s option + + + + + Error + + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_sv.qm b/src/qt/locale/bitcoin_sv.qm new file mode 100644 index 0000000..1cb3d1b Binary files /dev/null and b/src/qt/locale/bitcoin_sv.qm differ diff --git a/src/qt/locale/bitcoin_sv.ts b/src/qt/locale/bitcoin_sv.ts new file mode 100644 index 0000000..07863ba --- /dev/null +++ b/src/qt/locale/bitcoin_sv.ts @@ -0,0 +1,2939 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + Om CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b>-version + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +Detta är experimentell mjukvara. + + +Distribuerad under mjukvarulicensen MIT/X11, se den medföljande filen COPYING eller http://www.opensource.org/licenses/mit-license.php. + +Denna produkten innehåller mjukvara utvecklad av OpenSSL Project för användning i OpenSSL Toolkit (http://www.openssl.org/) och kryptografisk mjukvara utvecklad av Eric Young (eay@cryptsoft.com) samt UPnP-mjukvara skriven av Thomas Bernard. + + + + Copyright + Copyright + + + + The CryptoLottoToken developers + CryptoLottoToken-utvecklarna + + + + AddressBookPage + + + Address Book + Adressbok + + + + Double-click to edit address or label + Dubbel-klicka för att ändra adressen eller etiketten + + + + Create a new address + Skapa ny adress + + + + Copy the currently selected address to the system clipboard + Kopiera den markerade adressen till systemets Urklipp + + + + &New Address + &Ny adress + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Detta är dina CryptoLottoToken-adresser för att ta emot betalningar. Du kan ge varje avsändare en egen adress så att du kan hålla reda på vem som betalar dig. + + + + &Copy Address + &Kopiera adress + + + + Show &QR Code + Visa &QR-kod + + + + Sign a message to prove you own a CryptoLottoToken address + Signera ett meddelande för att bevisa att du äger denna adress + + + + Sign &Message + Signera &Meddelande + + + + Delete the currently selected address from the list + Ta bort den valda adressen från listan + + + + Export the data in the current tab to a file + Exportera informationen i den nuvarande fliken till en fil + + + + &Export + &Exportera + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Verifiera meddelandet för att vara säker på att den var signerad med den specificerade CryptoLottoToken-adressen + + + + &Verify Message + &Verifiera Meddelande + + + + &Delete + &Radera + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + Detta är dina CryptoLottoToken adresser för att skicka betalningar. Kolla alltid summan och den mottagande adressen innan du skickar CryptoLottoTokens. + + + + Copy &Label + Kopiera &etikett + + + + &Edit + &Editera + + + + Send &Coins + Skicka &CryptoLottoTokens + + + + Export Address Book Data + Exportera Adressbok + + + + Comma separated file (*.csv) + Kommaseparerad fil (*.csv) + + + + Error exporting + Fel vid export + + + + Could not write to file %1. + Kunde inte skriva till filen %1. + + + + AddressTableModel + + + Label + Etikett + + + + Address + Adress + + + + (no label) + (Ingen etikett) + + + + AskPassphraseDialog + + + Passphrase Dialog + Lösenords Dialog + + + + Enter passphrase + Ange lösenord + + + + New passphrase + Nytt lösenord + + + + Repeat new passphrase + Upprepa nytt lösenord + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Ange plånbokens nya lösenord. <br/> Använd ett lösenord på <b>10 eller fler slumpmässiga tecken,</b> eller <b>åtta eller fler ord.</b> + + + + Encrypt wallet + Kryptera plånbok + + + + This operation needs your wallet passphrase to unlock the wallet. + Denna operation behöver din plånboks lösenord för att låsa upp plånboken. + + + + Unlock wallet + Lås upp plånbok + + + + This operation needs your wallet passphrase to decrypt the wallet. + Denna operation behöver din plånboks lösenord för att dekryptera plånboken. + + + + Decrypt wallet + Dekryptera plånbok + + + + Change passphrase + Ändra lösenord + + + + Enter the old and new passphrase to the wallet. + Ange plånbokens gamla och nya lösenord. + + + + Confirm wallet encryption + Bekräfta kryptering av plånbok + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + VARNING: Om du krypterar din plånbok och glömmer ditt lösenord, kommer du att <b>FÖRLORA ALLA DINA TILLGÅNGAR</b>! + + + + Are you sure you wish to encrypt your wallet? + Är du säker på att du vill kryptera din plånbok? + + + + 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. + VIKTIGT: Alla tidigare säkerhetskopior du har gjort av plånbokens fil ska ersättas med den nya genererade, krypterade plånboks filen. Av säkerhetsskäl kommer tidigare säkerhetskopior av den okrypterade plånboks filen blir oanvändbara när du börjar använda en ny, krypterad plånbok. + + + + + Warning: The Caps Lock key is on! + Varning: Caps Lock är påslaget! + + + + + Wallet encrypted + Plånboken är krypterad + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + Programmet kommer nu att stänga ner för att färdigställa krypteringen. Tänk på att en krypterad plånbok inte skyddar mot stöld om din dator är infekterad med en keylogger. + + + + + + + Wallet encryption failed + Kryptering av plånbok misslyckades + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Kryptering av plånbok misslyckades på grund av ett internt fel. Din plånbok blev inte krypterad. + + + + + The supplied passphrases do not match. + De angivna lösenorden överensstämmer inte. + + + + Wallet unlock failed + Upplåsning av plånbok misslyckades + + + + + + The passphrase entered for the wallet decryption was incorrect. + Lösenordet för dekryptering av plånbok var felaktig. + + + + Wallet decryption failed + Dekryptering av plånbok misslyckades + + + + Wallet passphrase was successfully changed. + Plånbokens lösenord har ändrats. + + + + BitcoinGUI + + + Sign &message... + Signera &meddelande... + + + + Synchronizing with network... + Synkroniserar med nätverk... + + + + &Overview + &Översikt + + + + Show general overview of wallet + Visa översiktsvy av plånbok + + + + &Transactions + &Transaktioner + + + + Browse transaction history + Bläddra i transaktionshistorik + + + + Edit the list of stored addresses and labels for sending + Redigera listan med lagrade adresser och etiketter + + + + Show the list of addresses for receiving payments + Visa listan med adresser för att ta emot betalningar + + + + E&xit + &Avsluta + + + + Quit application + Avsluta programmet + + + + Show information about CryptoLottoToken + Visa information om CryptoLottoToken + + + + About &Qt + Om &Qt + + + + Show information about Qt + Visa information om Qt + + + + &Options... + &Alternativ... + + + + &Encrypt Wallet... + &Kryptera plånbok... + + + + &Backup Wallet... + &Säkerhetskopiera plånbok... + + + + &Change Passphrase... + &Byt Lösenord... + + + + Importing blocks from disk... + Importerar block från disk... + + + + Reindexing blocks on disk... + Återindexerar block på disken... + + + + Send coins to a CryptoLottoToken address + Skicka mynt till en CryptoLottoToken-adress + + + + Modify configuration options for CryptoLottoToken + Ändra konfigurationsalternativ för CryptoLottoToken + + + + Backup wallet to another location + Säkerhetskopiera plånboken till en annan plats + + + + Change the passphrase used for wallet encryption + Byt lösenord för kryptering av plånbok + + + + &Debug window + &Debug fönster + + + + Open debugging and diagnostic console + Öppna debug- och diagnostikkonsolen + + + + &Verify message... + &Verifiera meddelande... + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Plånbok + + + + &Send + &Skicka + + + + &Receive + &Ta emot + + + + &Addresses + &Adresser + + + + &About CryptoLottoToken + &Om CryptoLottoToken + + + + &Show / Hide + &Visa / Göm + + + + Show or hide the main Window + Visa eller göm huvudfönstret + + + + Encrypt the private keys that belong to your wallet + Kryptera de privata nycklar som tillhör din plånbok + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Signera meddelanden med din CryptoLottoTokenadress för att bevisa att du äger dem + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Verifiera meddelanden för att vara säker på att de var signerade med den specificerade CryptoLottoToken-adressen + + + + &File + &Arkiv + + + + &Settings + &Inställningar + + + + &Help + &Hjälp + + + + Tabs toolbar + Verktygsfält för Tabbar + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + CryptoLottoToken-klient + + + + %n active connection(s) to CryptoLottoToken network + %n aktiv anslutning till CryptoLottoToken-nätverket%n aktiva anslutningar till CryptoLottoToken-nätverket + + + + No block source available... + Ingen block-källa tillgänglig... + + + + Processed %1 of %2 (estimated) blocks of transaction history. + Bearbetat %1 av %2 (uppskattade) block av transaktionshistorik. + + + + Processed %1 blocks of transaction history. + Bearbetat %1 block i transaktionshistoriken. + + + + %n hour(s) + %n timme%n timmar + + + + %n day(s) + %n dag%n dagar + + + + %n week(s) + %n vecka%n veckor + + + + %1 behind + %1 efter + + + + Last received block was generated %1 ago. + Senast mottagna block genererades %1 sen. + + + + Transactions after this will not yet be visible. + Transaktioner efter denna kommer inte ännu vara synliga. + + + + Error + Fel + + + + Warning + Varning + + + + Information + Information + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + Transaktionen överskrider storleksgränsen. Du kan dock fortfarande skicka den mot en kostnad av %1, som går till noderna som behandlar din transaktion och bidrar till nätverket. Vill du betala denna avgift? + + + + Up to date + Uppdaterad + + + + Catching up... + Hämtar senaste... + + + + Confirm transaction fee + Bekräfta överföringsavgift + + + + Sent transaction + Transaktion skickad + + + + Incoming transaction + Inkommande transaktion + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Datum: %1 +Belopp: %2 +Typ: %3 +Adress: %4 + + + + + + URI handling + URI hantering + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + URI går inte att tolkas! Detta kan orsakas av en ogiltig CryptoLottoToken-adress eller felaktiga URI parametrar. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Denna plånbok är <b>krypterad</b> och för närvarande <b>olåst</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Denna plånbok är <b>krypterad</b> och för närvarande <b>låst</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + Ett allvarligt fel har uppstått. CryptoLottoToken kan inte längre köras säkert och kommer att avslutas. + + + + ClientModel + + + Network Alert + Nätverkslarm + + + + EditAddressDialog + + + Edit Address + Redigera Adress + + + + &Label + &Etikett + + + + The label associated with this address book entry + Den etikett som är associerad med detta adressboksinlägg + + + + &Address + &Adress + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Adressen som är associerad med detta adressboksinlägg. Detta kan enbart ändras för sändande adresser. + + + + New receiving address + Ny mottagaradress + + + + New sending address + Ny avsändaradress + + + + Edit receiving address + Redigera mottagaradress + + + + Edit sending address + Redigera avsändaradress + + + + The entered address "%1" is already in the address book. + Den angivna adressen "%1" finns redan i adressboken. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + Den angivna adressen "%1" är inte en giltig CryptoLottoToken-adress. + + + + Could not unlock wallet. + Plånboken kunde inte låsas upp. + + + + New key generation failed. + Misslyckades med generering av ny nyckel. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + version + + + + Usage: + Användning: + + + + command-line options + kommandoradsalternativ + + + + UI options + UI alternativ + + + + Set language, for example "de_DE" (default: system locale) + Ändra språk, till exempel "de_DE" (förvalt: systemets språk) + + + + Start minimized + Starta som minimerad + + + + Show splash screen on startup (default: 1) + Visa startbilden vid uppstart (förvalt: 1) + + + + OptionsDialog + + + Options + Alternativ + + + + &Main + &Allmänt + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + Valfri transaktionsavgift per kB som ser till att dina transaktioner behandlas snabbt. De flesta transaktioner är 1 kB. + + + + Pay transaction &fee + Betala överförings&avgift + + + + Automatically start CryptoLottoToken after logging in to the system. + Starta CryptoLottoToken automatiskt efter inloggning. + + + + &Start CryptoLottoToken on system login + &Starta CryptoLottoToken vid systemstart + + + + Reset all client options to default. + Återställ alla klient inställningar till förvalen. + + + + &Reset Options + &Återställ Alternativ + + + + &Network + &Nätverk + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Öppna automatiskt CryptoLottoToken-klientens port på routern. Detta fungerar endast om din router har UPnP aktiverat. + + + + Map port using &UPnP + Tilldela port med hjälp av &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Anslut till CryptoLottoToken-nätverket genom en SOCKS-proxy (t.ex. när du ansluter genom Tor). + + + + &Connect through SOCKS proxy: + &Anslut genom SOCKS-proxy: + + + + Proxy &IP: + Proxy-&IP: + + + + IP address of the proxy (e.g. 127.0.0.1) + Proxyns IP-adress (t.ex. 127.0.0.1) + + + + &Port: + &Port: + + + + Port of the proxy (e.g. 9050) + Proxyns port (t.ex. 9050) + + + + SOCKS &Version: + SOCKS &Version: + + + + SOCKS version of the proxy (e.g. 5) + SOCKS version av proxyn (t.ex. 5) + + + + &Window + &Fönster + + + + Show only a tray icon after minimizing the window. + Visa endast en systemfältsikon vid minimering. + + + + &Minimize to the tray instead of the taskbar + &Minimera till systemfältet istället för aktivitetsfältet + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Minimera applikationen istället för att stänga ner den när fönstret stängs. Detta innebär att programmet fotrsätter att köras tills du väljer Avsluta i menyn. + + + + M&inimize on close + M&inimera vid stängning + + + + &Display + &Visa + + + + User Interface &language: + Användargränssnittets &språk: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + Användargränssnittets språk kan ställas in här. Denna inställning träder i kraft efter en omstart av CryptoLottoToken. + + + + &Unit to show amounts in: + &Måttenhet att visa belopp i: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Välj en måttenhet att visa när du skickar mynt. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Anger om CryptoLottoToken-adresser skall visas i transaktionslistan. + + + + &Display addresses in transaction list + &Visa adresser i transaktionslistan + + + + &OK + &OK + + + + &Cancel + &Avbryt + + + + &Apply + &Verkställ + + + + default + standard + + + + Confirm options reset + Bekräfta att alternativen ska återställs + + + + Some settings may require a client restart to take effect. + Vissa inställningar kan behöva en omstart av klienten för att börja gälla. + + + + Do you want to proceed? + Vill du fortsätta? + + + + + Warning + Varning + + + + + This setting will take effect after restarting CryptoLottoToken. + Denna inställning träder i kraft efter en omstart av CryptoLottoToken. + + + + The supplied proxy address is invalid. + Den medföljande proxy adressen är ogiltig. + + + + OverviewPage + + + Form + Formulär + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + Den visade informationen kan vara inaktuell. Plånboken synkroniseras automatiskt med CryptoLottoToken-nätverket efter att anslutningen är upprättad, men denna process har inte slutförts ännu. + + + + Balance: + Saldo: + + + + Unconfirmed: + Obekräftade: + + + + Wallet + Plånbok + + + + Immature: + Omogen: + + + + Mined balance that has not yet matured + Den genererade balansen som ännu inte har mognat + + + + <b>Recent transactions</b> + <b>Nyligen genomförda transaktioner</b> + + + + Your current balance + Ditt nuvarande saldo + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Totalt antal transaktioner som ännu inte bekräftats, och som ännu inte räknas med i aktuellt saldo + + + + + out of sync + osynkroniserad + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + Kan inte starta CryptoLottoToken: klicka-och-betala handhavare + + + + QRCodeDialog + + + QR Code Dialog + QR-kod dialogruta + + + + Request Payment + Begär Betalning + + + + Amount: + Belopp: + + + + Label: + Etikett: + + + + Message: + Meddelande: + + + + &Save As... + &Spara som... + + + + Error encoding URI into QR Code. + Fel vid skapande av QR-kod från URI. + + + + The entered amount is invalid, please check. + Det angivna beloppet är ogiltigt, vänligen kontrollera. + + + + Resulting URI too long, try to reduce the text for label / message. + URI:n är för lång, försöka minska texten för etikett / meddelande. + + + + Save QR Code + Spara QR-kod + + + + PNG Images (*.png) + PNG-bilder (*.png) + + + + RPCConsole + + + Client name + Klientnamn + + + + + + + + + + + + + N/A + ej tillgänglig + + + + Client version + Klient-version + + + + &Information + &Information + + + + Using OpenSSL version + Använder OpenSSL version + + + + Startup time + Uppstartstid + + + + Network + Nätverk + + + + Number of connections + Antalet anslutningar + + + + On testnet + På testnet + + + + Block chain + Blockkedja + + + + Current number of blocks + Aktuellt antal block + + + + Estimated total blocks + Beräknade totala block + + + + Last block time + Sista blocktid + + + + &Open + &Öppna + + + + Command-line options + Kommandoradsalternativ + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Visa CryptoLottoToken-Qt hjälpmeddelande för att få en lista med möjliga CryptoLottoToken kommandoradsalternativ. + + + + &Show + &Visa + + + + &Console + &Konsol + + + + Build date + Kompileringsdatum + + + + CryptoLottoToken - Debug window + CryptoLottoToken - Debug fönster + + + + CryptoLottoToken Core + CryptoLottoToken Kärna + + + + Debug log file + Debugloggfil + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + Öppna CryptoLottoToken debug-loggfilen som finns i datakatalogen. Detta kan ta några sekunder för stora loggfiler. + + + + Clear console + Rensa konsollen + + + + Welcome to the CryptoLottoToken RPC console. + Välkommen till CryptoLottoToken RPC-konsollen. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Använd upp- och ner-pilarna för att navigera i historiken, och <b>Ctrl-L</b> för att rensa skärmen. + + + + Type <b>help</b> for an overview of available commands. + Skriv <b>help</b> för en översikt av alla kommandon. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Skicka pengar + + + + Send to multiple recipients at once + Skicka till flera mottagare samtidigt + + + + Add &Recipient + Lägg till &mottagare + + + + Remove all transaction fields + Ta bort alla transaktions-fält + + + + Clear &All + Rensa &alla + + + + Balance: + Balans: + + + + 123.456 BTC + 123,456 BTC + + + + Confirm the send action + Bekräfta sändordern + + + + S&end + &Skicka + + + + <b>%1</b> to %2 (%3) + <b>%1</b> till %2 (%3) + + + + Confirm send coins + Bekräfta skickade mynt + + + + Are you sure you want to send %1? + Är du säker på att du vill skicka %1? + + + + and + och + + + + The recipient address is not valid, please recheck. + Mottagarens adress är inte giltig, vänligen kontrollera igen. + + + + The amount to pay must be larger than 0. + Det betalade beloppet måste vara större än 0. + + + + The amount exceeds your balance. + Värdet överstiger ditt saldo. + + + + The total exceeds your balance when the %1 transaction fee is included. + Totalvärdet överstiger ditt saldo när transaktionsavgiften %1 är pålagd. + + + + Duplicate address found, can only send to each address once per send operation. + Dubblett av adress funnen, kan bara skicka till varje adress en gång per sändning. + + + + Error: Transaction creation failed! + Fel: Transaktionen gick inte att skapa! + + + + 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. + Fel: Transaktionen avslogs. Detta kan hända om några av mynten i plånboken redan spenderats, t.ex om du använt en kopia av wallet.dat och mynt spenderades i kopian men inte markerats som spenderas här. + + + + SendCoinsEntry + + + Form + Formulär + + + + A&mount: + &Belopp: + + + + Pay &To: + Betala &Till: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Adressen som betalningen skall skickas till (t.ex. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + Ange ett namn för den här adressen och lägg till den i din adressbok + + + + &Label: + &Etikett: + + + + Choose address from address book + Välj adress från adresslistan + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Klistra in adress från Urklipp + + + + Alt+P + Alt+P + + + + Remove this recipient + Ta bort denna mottagare + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Ange en CryptoLottoToken-adress (t.ex. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Signaturer - Signera / Verifiera ett Meddelande + + + + &Sign Message + &Signera Meddelande + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Du kan signera meddelanden med dina adresser för att bevisa att du äger dem. Var försiktig med vad du signerar eftersom phising-attacker kan försöka få dig att skriva över din identitet till någon annan. Signera bara väldetaljerade påståenden du kan gå i god för. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Adressen att signera meddelandet med (t.ex. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Välj en adress från adressboken + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Klistra in adress från Urklipp + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Skriv in meddelandet du vill signera här + + + + Signature + Signatur + + + + Copy the current signature to the system clipboard + Kopiera signaturen till systemets Urklipp + + + + Sign the message to prove you own this CryptoLottoToken address + Signera meddelandet för att bevisa att du äger denna adress + + + + Sign &Message + Signera &Meddelande + + + + Reset all sign message fields + Rensa alla fält + + + + + Clear &All + Rensa &alla + + + + &Verify Message + &Verifiera Meddelande + + + + Enter the signing 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. + Skriv in din adress, meddelande (se till att du kopierar radbrytningar, mellanslag, tabbar, osv. exakt) och signatur nedan för att verifiera meddelandet. Var noga med att inte läsa in mer i signaturen än vad som finns i det signerade meddelandet, för att undvika att luras av en man-in-the-middle attack. + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Adressen som meddelandet var signerat med (t.ex. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Verifiera meddelandet för att vara säker på att den var signerad med den angivna CryptoLottoToken-adressen + + + + Verify &Message + Verifiera &Meddelande + + + + Reset all verify message fields + Rensa alla fält + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Ange en CryptoLottoToken-adress (t.ex. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Klicka "Signera Meddelande" för att få en signatur + + + + Enter CryptoLottoToken signature + Ange CryptoLottoToken-signatur + + + + + The entered address is invalid. + Den angivna adressen är ogiltig. + + + + + + + Please check the address and try again. + Vad god kontrollera adressen och försök igen. + + + + + The entered address does not refer to a key. + Den angivna adressen refererar inte till en nyckel. + + + + Wallet unlock was cancelled. + Upplåsningen av plånboken avbröts. + + + + Private key for the entered address is not available. + Privata nyckel för den angivna adressen är inte tillgänglig. + + + + Message signing failed. + Signeringen av meddelandet misslyckades. + + + + Message signed. + Meddelandet är signerat. + + + + The signature could not be decoded. + Signaturen kunde inte avkodas. + + + + + Please check the signature and try again. + Kontrollera signaturen och försök igen. + + + + The signature did not match the message digest. + Signaturen matchade inte meddelandesammanfattningen. + + + + Message verification failed. + Meddelandet verifikation misslyckades. + + + + Message verified. + Meddelandet är verifierad. + + + + SplashScreen + + + The CryptoLottoToken developers + CryptoLottoToken-utvecklarna + + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + Öppet till %1 + + + + %1/offline + %1/nerkopplad + + + + %1/unconfirmed + %1/obekräftade + + + + %1 confirmations + %1 bekräftelser + + + + Status + Status + + + + , broadcast through %n node(s) + , sänd genom %n nod, sänd genom %n noder + + + + Date + Datum + + + + Source + Källa + + + + Generated + Genererad + + + + + From + Från + + + + + + To + Till + + + + + own address + egen adress + + + + label + etikett + + + + + + + + Credit + Kredit + + + + matures in %n more block(s) + mognar om %n blockmognar om %n fler block + + + + not accepted + inte accepterad + + + + + + + Debit + Belasta + + + + Transaction fee + Transaktionsavgift + + + + Net amount + Nettobelopp + + + + Message + Meddelande + + + + Comment + Kommentar + + + + Transaction ID + Transaktions-ID + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Genererade mynt måste vänta 120 block innan de kan användas. När du skapade detta block sändes det till nätverket för att läggas till i blockkedjan. Om blocket inte kommer in i kedjan kommer det att ändras till "accepteras inte" och kommer ej att gå att spendera. Detta kan ibland hända om en annan nod genererar ett block nästan samtidigt som dig. + + + + Debug information + Debug information + + + + Transaction + Transaktion + + + + Inputs + Inputs + + + + Amount + Mängd + + + + true + sant + + + + false + falsk + + + + , has not been successfully broadcast yet + , har inte lyckats skickas ännu + + + + Open for %n more block(s) + Öppet för %n mer blockÖppet för %n mer block + + + + unknown + okänd + + + + TransactionDescDialog + + + Transaction details + Transaktionsdetaljer + + + + This pane shows a detailed description of the transaction + Den här panelen visar en detaljerad beskrivning av transaktionen + + + + TransactionTableModel + + + Date + Datum + + + + Type + Typ + + + + Address + Adress + + + + Amount + Mängd + + + + Open for %n more block(s) + Öppet för %n mer blockÖppet för %n mer block + + + + Open until %1 + Öppet till %1 + + + + Offline (%1 confirmations) + Offline (%1 bekräftelser) + + + + Unconfirmed (%1 of %2 confirmations) + Obekräftad (%1 av %2 bekräftelser) + + + + Confirmed (%1 confirmations) + Bekräftad (%1 bekräftelser) + + + + Mined balance will be available when it matures in %n more block(s) + Genererade balansen kommer att finnas tillgänglig när den mognar om %n mer blockGenererade balansen kommer att finnas tillgänglig när den mognar om %n fler block + + + + This block was not received by any other nodes and will probably not be accepted! + Det här blocket togs inte emot av några andra noder och kommer antagligen inte att bli godkänt. + + + + Generated but not accepted + Genererad men inte accepterad + + + + Received with + Mottagen med + + + + Received from + Mottaget från + + + + Sent to + Skickad till + + + + Payment to yourself + Betalning till dig själv + + + + Mined + Genererade + + + + (n/a) + (n/a) + + + + Transaction status. Hover over this field to show number of confirmations. + Transaktionsstatus. Håll muspekaren över för att se antal bekräftelser. + + + + Date and time that the transaction was received. + Tidpunkt då transaktionen mottogs. + + + + Type of transaction. + Transaktionstyp. + + + + Destination address of transaction. + Transaktionens destinationsadress. + + + + Amount removed from or added to balance. + Belopp draget eller tillagt till balans. + + + + TransactionView + + + + All + Alla + + + + Today + Idag + + + + This week + Denna vecka + + + + This month + Denna månad + + + + Last month + Föregående månad + + + + This year + Det här året + + + + Range... + Period... + + + + Received with + Mottagen med + + + + Sent to + Skickad till + + + + To yourself + Till dig själv + + + + Mined + Genererade + + + + Other + Övriga + + + + Enter address or label to search + Sök efter adress eller etikett + + + + Min amount + Minsta mängd + + + + Copy address + Kopiera adress + + + + Copy label + Kopiera etikett + + + + Copy amount + Kopiera belopp + + + + Copy transaction ID + Kopiera transaktions ID + + + + Edit label + Ändra etikett + + + + Show transaction details + Visa transaktionsdetaljer + + + + Export Transaction Data + Exportera Transaktionsdata + + + + Comma separated file (*.csv) + Kommaseparerad fil (*. csv) + + + + Confirmed + Bekräftad + + + + Date + Datum + + + + Type + Typ + + + + Label + Etikett + + + + Address + Adress + + + + Amount + Mängd + + + + ID + ID + + + + Error exporting + Fel vid export + + + + Could not write to file %1. + Kunde inte skriva till filen %1. + + + + Range: + Intervall: + + + + to + till + + + + WalletModel + + + Send Coins + Skicka pengar + + + + WalletView + + + &Export + &Exportera + + + + Export the data in the current tab to a file + Exportera informationen i den nuvarande fliken till en fil + + + + Backup Wallet + Säkerhetskopiera Plånbok + + + + Wallet Data (*.dat) + Plånboks-data (*.dat) + + + + Backup Failed + Säkerhetskopiering misslyckades + + + + There was an error trying to save the wallet data to the new location. + Det inträffade ett fel när plånboken skulle sparas till den nya platsen. + + + + Backup Successful + Säkerhetskopiering lyckades + + + + The wallet data was successfully saved to the new location. + Plånbokens data har sparats till den nya platsen. + + + + bitcoin-core + + + CryptoLottoToken version + CryptoLottoToken version + + + + Usage: + Användning: + + + + Send command to -server or CryptoLottoTokend + Skicka kommando till -server eller CryptoLottoTokend + + + + List commands + Lista kommandon + + + + Get help for a command + Få hjälp med ett kommando + + + + Options: + Inställningar: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Ange konfigurationsfil (förvalt: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + Ange pid fil (förvalt: CryptoLottoTokend.pid) + + + + Specify data directory + Ange katalog för data + + + + Set database cache size in megabytes (default: 25) + Sätt databas cache storleken i megabyte (förvalt: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Lyssna efter anslutningar på <port> (förvalt: 22556 eller testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Ha som mest <n> anslutningar till andra klienter (förvalt: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Anslut till en nod för att hämta klientadresser, och koppla från + + + + Specify your own public address + Ange din egen publika adress + + + + Threshold for disconnecting misbehaving peers (default: 100) + Tröskelvärde för att koppla ifrån klienter som missköter sig (förvalt: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Antal sekunder att hindra klienter som missköter sig från att ansluta (förvalt: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + Ett fel uppstod vid upprättandet av RPC port %u för att lyssna på IPv4: %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Lyssna på JSON-RPC-anslutningar på <port> (förvalt: 22555 eller testnet: 44555) + + + + Accept command line and JSON-RPC commands + Tillåt kommandon från kommandotolken och JSON-RPC-kommandon + + + + Run in the background as a daemon and accept commands + Kör i bakgrunden som tjänst och acceptera kommandon + + + + Use the test network + Använd testnätverket + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Acceptera anslutningar utifrån (förvalt: 1 om ingen -proxy eller -connect) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + %s, du behöver sätta ett rpclösensord i konfigurationsfilen: +%s +Det är rekommenderat att använda följande slumpade lösenord: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(du behöver inte komma ihåg lösenordet) +Användarnamnet och lösenordet FÅR INTE bara detsamma. +Om filen inte existerar, skapa den med enbart ägarläsbara filrättigheter. +Det är också rekommenderat att sätta alertnotify så du meddelas om problem; +till exempel: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + Ett fel uppstod vid upprättandet av RPC port %u för att lyssna på IPv6, faller tillbaka till IPV4: %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + Bind till given adress och lyssna alltid på den. Använd [värd]:port notation för IPv6 + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + Kan inte låsa data-mappen %s. CryptoLottoToken körs förmodligen redan. + + + + 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. + Fel: Transaktionen avslogs! Detta kan hända om några av mynten i plånboken redan spenderats, t.ex om du använt en kopia av wallet.dat och mynt spenderades i kopian men inte markerats som spenderas här. + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + Fel: Denna transaktion kräver en transaktionsavgift på minst %s på grund av dess storlek, komplexitet, eller användning av senast mottagna CryptoLottoTokens! + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + Exekvera kommando när ett relevant meddelande är mottagen (%s i cmd är utbytt med ett meddelande) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Exekvera kommando när en plånbokstransaktion ändras (%s i cmd är ersatt av TxID) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Sätt den maximala storleken av hög-prioriterade/låg-avgifts transaktioner i byte (förvalt: 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + Detta är ett förhands testbygge - använd på egen risk - använd inte för mining eller handels applikationer + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Varning: -paytxfee är satt väldigt hög! Detta är avgiften du kommer betala för varje transaktion. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + Varning: Visade transaktioner kanske inte är korrekt! Du kan behöva uppgradera, eller andra noder kan behöva uppgradera. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Varning: Vänligen kolla så att din dators datum och tid är korrekt! Om din klocka går fel kommer CryptoLottoToken inte fungera korrekt. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Varning: fel vid läsning av wallet.dat! Alla nycklar lästes korrekt, men transaktionsdatan eller adressbokens poster kanske saknas eller är felaktiga. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Varning: wallet.dat korrupt, datan har räddats! Den ursprungliga wallet.dat har sparas som wallet.{timestamp}.bak i %s; om ditt saldo eller transaktioner är felaktiga ska du återställa från en säkerhetskopia. + + + + Attempt to recover private keys from a corrupt wallet.dat + Försök att rädda de privata nycklarna från en korrupt wallet.dat + + + + Block creation options: + Block skapande inställningar: + + + + Connect only to the specified node(s) + Koppla enbart upp till den/de specificerade noden/noder + + + + Corrupted block database detected + Korrupt blockdatabas har upptäckts + + + + Discover own IP address (default: 1 when listening and no -externalip) + Hitta egen IP-adress (förvalt: 1 under lyssning och utan -externalip) + + + + Do you want to rebuild the block database now? + Vill du bygga om blockdatabasen nu? + + + + Error initializing block database + Fel vid initiering av blockdatabasen + + + + Error initializing wallet database environment %s! + Fel vid initiering av plånbokens databasmiljö %s! + + + + Error loading block database + Fel vid inläsning av blockdatabasen + + + + Error opening block database + Fel vid öppning av blockdatabasen + + + + Error: Disk space is low! + Fel: Hårddiskutrymme är lågt! + + + + Error: Wallet locked, unable to create transaction! + Fel: Plånboken är låst, det går ej att skapa en transaktion! + + + + Error: system error: + Fel: systemfel: + + + + Failed to listen on any port. Use -listen=0 if you want this. + Misslyckades att lyssna på någon port. Använd -listen=0 om du vill detta. + + + + Failed to read block info + Misslyckades att läsa blockinformation + + + + Failed to read block + Misslyckades att läsa blocket + + + + Failed to sync block index + Misslyckades att synkronisera blockindex + + + + Failed to write block index + Misslyckades att skriva blockindex + + + + Failed to write block info + Misslyckades att skriva blockinformation + + + + Failed to write block + Misslyckades att skriva blocket + + + + Failed to write file info + Misslyckades att skriva filinformation + + + + Failed to write to coin database + Misslyckades att skriva till myntdatabas + + + + Failed to write transaction index + Misslyckades att skriva transaktionsindex + + + + Failed to write undo data + Misslyckades att skriva ångradata + + + + Find peers using DNS lookup (default: 1 unless -connect) + Sök efter klienter med DNS sökningen (förvalt: 1 om inte -connect) + + + + Generate coins (default: 0) + Generera mynt (förvalt: 0) + + + + How many blocks to check at startup (default: 288, 0 = all) + Hur många block att kontrollera vid uppstart (standardvärde: 288, 0 = alla) + + + + How thorough the block verification is (0-4, default: 3) + Hur grundlig blockverifikationen är (0-4, förvalt: 3) + + + + Not enough file descriptors available. + Inte tillräckligt med filbeskrivningar tillgängliga. + + + + Rebuild block chain index from current blk000??.dat files + Återskapa blockkedjans index från nuvarande blk000??.dat filer + + + + Set the number of threads to service RPC calls (default: 4) + Ange antalet trådar för att hantera RPC anrop (standard: 4) + + + + Verifying blocks... + Verifierar block... + + + + Verifying wallet... + Verifierar plånboken... + + + + Imports blocks from external blk000??.dat file + Importerar block från extern blk000??.dat fil + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + Ange antalet skriptkontrolltrådar (upp till 16, 0 = auto, <0 = lämna så många kärnor lediga, förval: 0) + + + + Information + Information + + + + Invalid -tor address: '%s' + Ogiltig -tor adress: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + Ogiltigt belopp för -minrelaytxfee=<belopp>: '%s' + + + + Invalid amount for -mintxfee=<amount>: '%s' + Ogiltigt belopp för -mintxfee=<belopp>: '%s' + + + + Maintain a full transaction index (default: 0) + Upprätthåll ett fullständigt transaktionsindex (förval: 0) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Maximal buffert för mottagning per anslutning, <n>*1000 byte (förvalt: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Maximal buffert för sändning per anslutning, <n>*1000 byte (förvalt: 5000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + Acceptera bara blockkedjans matchande inbyggda kontrollpunkter (förvalt: 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Anslut enbart till noder i nätverket <net> (IPv4, IPv6 eller Tor) + + + + Output extra debugging information. Implies all other -debug* options + Skriv ut extra felsökningsinformation. Gäller alla andra -debug* alternativ + + + + Output extra network debugging information + Skriv ut extra felsökningsinformation om nätverk + + + + Prepend debug output with timestamp (default: 1) + Skriv ut tid i felsökningsinformationen + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + SSL-inställningar: (se CryptoLottoToken-wikin för SSL-setup instruktioner) + + + + Select the version of socks proxy to use (4-5, default: 5) + Välj socks-proxy version att använda (4-5, förvalt: 5) + + + + Send trace/debug info to console instead of debug.log file + Skicka trace-/debuginformation till terminalen istället för till debug.log + + + + Send trace/debug info to debugger + Skicka trace-/debuginformation till debugger + + + + Set maximum block size in bytes (default: 250000) + Sätt maximal blockstorlek i byte (förvalt: 250000) + + + + Set minimum block size in bytes (default: 0) + Sätt minsta blockstorlek i byte (förvalt: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Krymp debug.log filen vid klient start (förvalt: 1 vid ingen -debug) + + + + Signing transaction failed + Signering av transaktion misslyckades + + + + Specify connection timeout in milliseconds (default: 5000) + Ange timeout för uppkoppling i millisekunder (förvalt: 5000) + + + + System error: + Systemfel: + + + + Transaction amount too small + Transaktions belopp för liten + + + + Transaction amounts must be positive + Transaktionens belopp måste vara positiva + + + + Transaction too large + Transaktionen är för stor + + + + Use UPnP to map the listening port (default: 0) + Använd UPnP för att mappa den lyssnande porten (förvalt: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Använd UPnP för att mappa den lyssnande porten (förvalt: 1 under lyssning) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Använd en proxy för att nå tor (förvalt: samma som -proxy) + + + + Username for JSON-RPC connections + Användarnamn för JSON-RPC-anslutningar + + + + Warning + Varning + + + + Warning: This version is obsolete, upgrade required! + Varning: denna version är föråldrad, uppgradering krävs! + + + + You need to rebuild the databases using -reindex to change -txindex + Du måste återskapa databaserna med -reindex för att ändra -txindex + + + + wallet.dat corrupt, salvage failed + wallet.dat korrupt, räddning misslyckades + + + + Password for JSON-RPC connections + Lösenord för JSON-RPC-anslutningar + + + + Allow JSON-RPC connections from specified IP address + Tillåt JSON-RPC-anslutningar från specifika IP-adresser + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Skicka kommandon till klient på <ip> (förvalt: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Exekvera kommando när det bästa blocket ändras (%s i cmd är utbytt av blockhash) + + + + Upgrade wallet to latest format + Uppgradera plånboken till senaste formatet + + + + Set key pool size to <n> (default: 100) + Sätt storleken på nyckelpoolen till <n> (förvalt: 100) + + + + Rescan the block chain for missing wallet transactions + Sök i blockkedjan efter saknade plånboks transaktioner + + + + Use OpenSSL (https) for JSON-RPC connections + Använd OpenSSL (https) för JSON-RPC-anslutningar + + + + Server certificate file (default: server.cert) + Serverns certifikatfil (förvalt: server.cert) + + + + Server private key (default: server.pem) + Serverns privata nyckel (förvalt: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Accepterade krypteringsalgoritmer (förvalt: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Det här hjälp medelandet + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Det går inte att binda till %s på den här datorn (bind returnerade felmeddelande %d, %s) + + + + Connect through socks proxy + Anslut genom socks-proxy + + + + Allow DNS lookups for -addnode, -seednode and -connect + Tillåt DNS-sökningar för -addnode, -seednode och -connect + + + + Loading addresses... + Laddar adresser... + + + + Error loading wallet.dat: Wallet corrupted + Fel vid inläsningen av wallet.dat: Plånboken är skadad + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Fel vid inläsningen av wallet.dat: Plånboken kräver en senare version av CryptoLottoToken + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Plånboken behöver skrivas om: Starta om CryptoLottoToken för att färdigställa + + + + Error loading wallet.dat + Fel vid inläsning av plånboksfilen wallet.dat + + + + Invalid -proxy address: '%s' + Ogiltig -proxy adress: '%s' + + + + Unknown network specified in -onlynet: '%s' + Okänt nätverk som anges i -onlynet: '%s' + + + + Unknown -socks proxy version requested: %i + Okänd -socks proxy version begärd: %i + + + + Cannot resolve -bind address: '%s' + Kan inte matcha -bind adress: '%s' + + + + Cannot resolve -externalip address: '%s' + Kan inte matcha -externalip adress: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + Ogiltigt belopp för -paytxfee=<belopp>:'%s' + + + + Invalid amount + Ogiltig mängd + + + + Insufficient funds + Otillräckligt med CryptoLottoTokens + + + + Loading block index... + Laddar blockindex... + + + + Add a node to connect to and attempt to keep the connection open + Lägg till en nod att koppla upp mot och försök att hålla anslutningen öppen + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Det går inte att binda till %s på den här datorn. CryptoLottoToken är förmodligen redan igång. + + + + Fee per KB to add to transactions you send + Avgift per KB att lägga till på transaktioner du skickar + + + + Loading wallet... + Laddar plånbok... + + + + Cannot downgrade wallet + Kan inte nedgradera plånboken + + + + Cannot write default address + Kan inte skriva standardadress + + + + Rescanning... + Söker igen... + + + + Done loading + Klar med laddning + + + + To use the %s option + Att använda %s alternativet + + + + Error + Fel + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + Du behöver välja ett rpclösensord i konfigurationsfilen: +%s +Om filen inte existerar, skapa den med filrättigheten endast läsbar för ägaren. + + + diff --git a/src/qt/locale/bitcoin_th_TH.ts b/src/qt/locale/bitcoin_th_TH.ts new file mode 100644 index 0000000..eedd521 --- /dev/null +++ b/src/qt/locale/bitcoin_th_TH.ts @@ -0,0 +1,2917 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + เกี่ยวกับ บิตคอย์น + + + + <b>CryptoLottoToken</b> version + <b>บิตคอย์น<b>รุ่น + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + + Copyright + + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + สมุดรายชื่อ + + + + Double-click to edit address or label + ดับเบิลคลิก เพื่อแก้ไขที่อยู่ หรือชื่อ + + + + Create a new address + สร้างที่อยู่ใหม่ + + + + Copy the currently selected address to the system clipboard + คัดลอกที่อยู่ที่ถูกเลือกไปยัง คลิปบอร์ดของระบบ + + + + &New Address + + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + + + + + &Copy Address + + + + + Show &QR Code + + + + + Sign a message to prove you own a CryptoLottoToken address + + + + + Sign &Message + + + + + Delete the currently selected address from the list + + + + + Export the data in the current tab to a file + + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + + + + + &Verify Message + + + + + &Delete + ลบ + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + + + + + &Edit + + + + + Send &Coins + + + + + Export Address Book Data + ส่งออกรายชื่อทั้งหมด + + + + Comma separated file (*.csv) + + + + + Error exporting + ส่งออกผิดพลาด + + + + Could not write to file %1. + ไม่สามารถเขียนไปยังไฟล์ %1 + + + + AddressTableModel + + + Label + ชื่อ + + + + Address + ที่อยู่ + + + + (no label) + (ไม่มีชื่อ) + + + + AskPassphraseDialog + + + Passphrase Dialog + + + + + Enter passphrase + ใส่รหัสผ่าน + + + + New passphrase + รหัสผา่นใหม่ + + + + Repeat new passphrase + กรุณากรอกรหัสผ่านใหม่อีกครั้งหนึ่ง + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + + + + + Encrypt wallet + กระเป๋าสตางค์ที่เข้ารหัส + + + + This operation needs your wallet passphrase to unlock the wallet. + + + + + Unlock wallet + เปิดกระเป๋าสตางค์ + + + + This operation needs your wallet passphrase to decrypt the wallet. + + + + + Decrypt wallet + ถอดรหัสกระเป๋าสตางค์ + + + + Change passphrase + เปลี่ยนรหัสผ่าน + + + + Enter the old and new passphrase to the wallet. + กรอกรหัสผ่านเก่าและรหัสผ่านใหม่สำหรับกระเป๋าสตางค์ + + + + Confirm wallet encryption + ยืนยันการเข้ารหัสกระเป๋าสตางค์ + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + + + + + Are you sure you wish to encrypt your wallet? + + + + + 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. + + + + + + Warning: The Caps Lock key is on! + + + + + + Wallet encrypted + กระเป๋าสตางค์ถูกเข้ารหัสเรียบร้อยแล้ว + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + + + + + + + + Wallet encryption failed + การเข้ารหัสกระเป๋าสตางค์ผิดพลาด + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + + + + + + The supplied passphrases do not match. + รหัสผ่านที่คุณกรอกไม่ตรงกัน + + + + Wallet unlock failed + + + + + + + The passphrase entered for the wallet decryption was incorrect. + + + + + Wallet decryption failed + + + + + Wallet passphrase was successfully changed. + + + + + BitcoinGUI + + + Sign &message... + + + + + Synchronizing with network... + + + + + &Overview + + + + + Show general overview of wallet + + + + + &Transactions + + + + + Browse transaction history + + + + + Edit the list of stored addresses and labels for sending + + + + + Show the list of addresses for receiving payments + + + + + E&xit + + + + + Quit application + + + + + Show information about CryptoLottoToken + + + + + About &Qt + + + + + Show information about Qt + + + + + &Options... + + + + + &Encrypt Wallet... + + + + + &Backup Wallet... + + + + + &Change Passphrase... + + + + + Importing blocks from disk... + + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + + + + + Modify configuration options for CryptoLottoToken + + + + + Backup wallet to another location + + + + + Change the passphrase used for wallet encryption + + + + + &Debug window + + + + + Open debugging and diagnostic console + + + + + &Verify message... + + + + + + CryptoLottoToken + + + + + Wallet + + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + + + + + &Show / Hide + + + + + Show or hide the main Window + + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + + + + + &File + + + + + &Settings + + + + + &Help + + + + + Tabs toolbar + + + + + + [testnet] + + + + + CryptoLottoToken client + + + + + %n active connection(s) to CryptoLottoToken network + + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + + + + + Warning + + + + + Information + + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + + + + + Catching up... + + + + + Confirm transaction fee + + + + + Sent transaction + + + + + Incoming transaction + + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + + + + + + URI handling + + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + + + + + EditAddressDialog + + + Edit Address + + + + + &Label + + + + + The label associated with this address book entry + + + + + &Address + + + + + The address associated with this address book entry. This can only be modified for sending addresses. + + + + + New receiving address + + + + + New sending address + + + + + Edit receiving address + + + + + Edit sending address + + + + + The entered address "%1" is already in the address book. + + + + + The entered address "%1" is not a valid CryptoLottoToken address. + + + + + Could not unlock wallet. + + + + + New key generation failed. + + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + + + + + version + + + + + Usage: + + + + + command-line options + + + + + UI options + + + + + Set language, for example "de_DE" (default: system locale) + + + + + Start minimized + + + + + Show splash screen on startup (default: 1) + + + + + OptionsDialog + + + Options + + + + + &Main + + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + + + + + Automatically start CryptoLottoToken after logging in to the system. + + + + + &Start CryptoLottoToken on system login + + + + + Reset all client options to default. + + + + + &Reset Options + + + + + &Network + + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + + + + + Map port using &UPnP + + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + + + + + &Connect through SOCKS proxy: + + + + + Proxy &IP: + + + + + IP address of the proxy (e.g. 127.0.0.1) + + + + + &Port: + + + + + Port of the proxy (e.g. 9050) + + + + + SOCKS &Version: + + + + + SOCKS version of the proxy (e.g. 5) + + + + + &Window + + + + + Show only a tray icon after minimizing the window. + + + + + &Minimize to the tray instead of the taskbar + + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + + + + + M&inimize on close + + + + + &Display + + + + + User Interface &language: + + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + + + + + &Unit to show amounts in: + + + + + Choose the default subdivision unit to show in the interface and when sending coins. + + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + + + &Display addresses in transaction list + + + + + &OK + + + + + &Cancel + + + + + &Apply + + + + + default + + + + + Confirm options reset + + + + + Some settings may require a client restart to take effect. + + + + + Do you want to proceed? + + + + + + Warning + + + + + + This setting will take effect after restarting CryptoLottoToken. + + + + + The supplied proxy address is invalid. + + + + + OverviewPage + + + Form + + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + + + + + Balance: + + + + + Unconfirmed: + + + + + Wallet + + + + + Immature: + + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + + + + + Your current balance + + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + + + + + + out of sync + + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + + + + + Request Payment + + + + + Amount: + + + + + Label: + + + + + Message: + + + + + &Save As... + + + + + Error encoding URI into QR Code. + + + + + The entered amount is invalid, please check. + + + + + Resulting URI too long, try to reduce the text for label / message. + + + + + Save QR Code + + + + + PNG Images (*.png) + + + + + RPCConsole + + + Client name + + + + + + + + + + + + + + N/A + + + + + Client version + + + + + &Information + + + + + Using OpenSSL version + + + + + Startup time + + + + + Network + + + + + Number of connections + + + + + On testnet + + + + + Block chain + + + + + Current number of blocks + + + + + Estimated total blocks + + + + + Last block time + + + + + &Open + + + + + Command-line options + + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + + + + + &Show + + + + + &Console + + + + + Build date + + + + + CryptoLottoToken - Debug window + + + + + CryptoLottoToken Core + + + + + Debug log file + + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + + + + + Welcome to the CryptoLottoToken RPC console. + + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + + + + + Type <b>help</b> for an overview of available commands. + + + + + SendCoinsDialog + + + + + + + + + + Send Coins + + + + + Send to multiple recipients at once + + + + + Add &Recipient + + + + + Remove all transaction fields + + + + + Clear &All + + + + + Balance: + + + + + 123.456 BTC + + + + + Confirm the send action + + + + + S&end + + + + + <b>%1</b> to %2 (%3) + + + + + Confirm send coins + + + + + Are you sure you want to send %1? + + + + + and + + + + + The recipient address is not valid, please recheck. + + + + + The amount to pay must be larger than 0. + + + + + The amount exceeds your balance. + + + + + The total exceeds your balance when the %1 transaction fee is included. + + + + + Duplicate address found, can only send to each address once per send operation. + + + + + Error: Transaction creation failed! + + + + + 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. + + + + + SendCoinsEntry + + + Form + + + + + A&mount: + + + + + Pay &To: + + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + + + + + &Label: + + + + + Choose address from address book + + + + + Alt+A + + + + + Paste address from clipboard + + + + + Alt+P + + + + + Remove this recipient + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + + + + + &Sign Message + + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Choose an address from the address book + + + + + + Alt+A + + + + + Paste address from clipboard + + + + + Alt+P + + + + + Enter the message you want to sign here + + + + + Signature + + + + + Copy the current signature to the system clipboard + + + + + Sign the message to prove you own this CryptoLottoToken address + + + + + Sign &Message + + + + + Reset all sign message fields + + + + + + Clear &All + + + + + &Verify Message + + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + + + + + Verify &Message + + + + + Reset all verify message fields + + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Click "Sign Message" to generate signature + + + + + Enter CryptoLottoToken signature + + + + + + The entered address is invalid. + + + + + + + + Please check the address and try again. + + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + + + + + Private key for the entered address is not available. + + + + + Message signing failed. + + + + + Message signed. + + + + + The signature could not be decoded. + + + + + + Please check the signature and try again. + + + + + The signature did not match the message digest. + + + + + Message verification failed. + + + + + Message verified. + + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + + + + + TransactionDesc + + + Open until %1 + + + + + %1/offline + + + + + %1/unconfirmed + + + + + %1 confirmations + + + + + Status + + + + + , broadcast through %n node(s) + + + + + Date + + + + + Source + + + + + Generated + + + + + + From + + + + + + + To + + + + + + own address + + + + + label + + + + + + + + + Credit + + + + + matures in %n more block(s) + + + + + not accepted + + + + + + + + Debit + + + + + Transaction fee + + + + + Net amount + + + + + Message + + + + + Comment + + + + + Transaction ID + + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + + + + + Debug information + + + + + Transaction + + + + + Inputs + + + + + Amount + + + + + true + + + + + false + + + + + , has not been successfully broadcast yet + + + + + Open for %n more block(s) + + + + + unknown + + + + + TransactionDescDialog + + + Transaction details + + + + + This pane shows a detailed description of the transaction + + + + + TransactionTableModel + + + Date + + + + + Type + + + + + Address + ที่อยู่ + + + + Amount + + + + + Open for %n more block(s) + + + + + Open until %1 + + + + + Offline (%1 confirmations) + + + + + Unconfirmed (%1 of %2 confirmations) + + + + + Confirmed (%1 confirmations) + + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + + + + + Generated but not accepted + + + + + Received with + + + + + Received from + + + + + Sent to + + + + + Payment to yourself + + + + + Mined + + + + + (n/a) + + + + + Transaction status. Hover over this field to show number of confirmations. + + + + + Date and time that the transaction was received. + + + + + Type of transaction. + + + + + Destination address of transaction. + + + + + Amount removed from or added to balance. + + + + + TransactionView + + + + All + + + + + Today + วันนี้ + + + + This week + + + + + This month + + + + + Last month + + + + + This year + + + + + Range... + + + + + Received with + + + + + Sent to + + + + + To yourself + + + + + Mined + + + + + Other + + + + + Enter address or label to search + + + + + Min amount + + + + + Copy address + + + + + Copy label + + + + + Copy amount + + + + + Copy transaction ID + + + + + Edit label + + + + + Show transaction details + + + + + Export Transaction Data + + + + + Comma separated file (*.csv) + + + + + Confirmed + + + + + Date + + + + + Type + + + + + Label + ชื่อ + + + + Address + ที่อยู่ + + + + Amount + + + + + ID + + + + + Error exporting + ส่งออกผิดพลาด + + + + Could not write to file %1. + ไม่สามารถเขียนไปยังไฟล์ %1 + + + + Range: + + + + + to + + + + + WalletModel + + + Send Coins + + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + + + + + There was an error trying to save the wallet data to the new location. + + + + + Backup Successful + + + + + The wallet data was successfully saved to the new location. + + + + + bitcoin-core + + + CryptoLottoToken version + + + + + Usage: + + + + + Send command to -server or CryptoLottoTokend + + + + + List commands + + + + + Get help for a command + + + + + Options: + + + + + Specify configuration file (default: CryptoLottoToken.conf) + + + + + Specify pid file (default: CryptoLottoTokend.pid) + + + + + Specify data directory + + + + + Set database cache size in megabytes (default: 25) + + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + + + + + Maintain at most <n> connections to peers (default: 125) + + + + + Connect to a node to retrieve peer addresses, and disconnect + + + + + Specify your own public address + + + + + Threshold for disconnecting misbehaving peers (default: 100) + + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + + + + + Accept command line and JSON-RPC commands + + + + + Run in the background as a daemon and accept commands + + + + + Use the test network + + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + + + + + Attempt to recover private keys from a corrupt wallet.dat + + + + + Block creation options: + + + + + Connect only to the specified node(s) + + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + + + + + Error opening block database + + + + + Error: Disk space is low! + + + + + Error: Wallet locked, unable to create transaction! + + + + + Error: system error: + + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + + + + + Invalid -tor address: '%s' + + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + + + + + Select the version of socks proxy to use (4-5, default: 5) + + + + + Send trace/debug info to console instead of debug.log file + + + + + Send trace/debug info to debugger + + + + + Set maximum block size in bytes (default: 250000) + + + + + Set minimum block size in bytes (default: 0) + + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + + + + + System error: + + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + + + + + Use UPnP to map the listening port (default: 1 when listening) + + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + + + + + Warning + + + + + Warning: This version is obsolete, upgrade required! + + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + + + + + Password for JSON-RPC connections + + + + + Allow JSON-RPC connections from specified IP address + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + + + + + Upgrade wallet to latest format + + + + + Set key pool size to <n> (default: 100) + + + + + Rescan the block chain for missing wallet transactions + + + + + Use OpenSSL (https) for JSON-RPC connections + + + + + Server certificate file (default: server.cert) + + + + + Server private key (default: server.pem) + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + + This help message + + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + + + + + Connect through socks proxy + + + + + Allow DNS lookups for -addnode, -seednode and -connect + + + + + Loading addresses... + + + + + Error loading wallet.dat: Wallet corrupted + + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + + + + + Error loading wallet.dat + + + + + Invalid -proxy address: '%s' + + + + + Unknown network specified in -onlynet: '%s' + + + + + Unknown -socks proxy version requested: %i + + + + + Cannot resolve -bind address: '%s' + + + + + Cannot resolve -externalip address: '%s' + + + + + Invalid amount for -paytxfee=<amount>: '%s' + + + + + Invalid amount + + + + + Insufficient funds + + + + + Loading block index... + + + + + Add a node to connect to and attempt to keep the connection open + + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + + + + + Fee per KB to add to transactions you send + + + + + Loading wallet... + + + + + Cannot downgrade wallet + + + + + Cannot write default address + + + + + Rescanning... + + + + + Done loading + + + + + To use the %s option + + + + + Error + + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_tr.qm b/src/qt/locale/bitcoin_tr.qm new file mode 100644 index 0000000..65c2b8a Binary files /dev/null and b/src/qt/locale/bitcoin_tr.qm differ diff --git a/src/qt/locale/bitcoin_tr.ts b/src/qt/locale/bitcoin_tr.ts new file mode 100644 index 0000000..9a4d82d --- /dev/null +++ b/src/qt/locale/bitcoin_tr.ts @@ -0,0 +1,2938 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + CryptoLottoToken hakkında + + + + <b>CryptoLottoToken</b> version + <b>CryptoLottoToken</b> sürüm + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + Bu yazılım deneme safhasındadır. + + MIT/X11 yazılım lisansı kapsamında yayınlanmıştır, COPYING dosyasına ya da http://www.opensource.org/licenses/mit-license.php sayfasına bakınız. + + 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. + + + + Copyright + Telif hakkı + + + + The CryptoLottoToken developers + CryptoLottoToken geliştiricileri + + + + AddressBookPage + + + Address Book + Adres defteri + + + + Double-click to edit address or label + Adresi ya da etiketi düzenlemek için çift tıklayınız + + + + Create a new address + Yeni bir adres oluştur + + + + Copy the currently selected address to the system clipboard + Şu anda seçili olan adresi panoya kopyala + + + + &New Address + &Yeni adres + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Bunlar, ödeme almak için CryptoLottoToken adresleridir. Kimin ödeme yaptığını izleyebilmek için her ödeme yollaması gereken kişiye değişik bir adres verebilirsiniz. + + + + &Copy Address + Adresi &kopyala + + + + Show &QR Code + &QR kodunu göster + + + + Sign a message to prove you own a CryptoLottoToken address + Bir CryptoLottoToken adresinin sizin olduğunu ispatlamak için mesaj imzalayın + + + + Sign &Message + &Mesaj imzala + + + + Delete the currently selected address from the list + Seçili adresi listeden sil + + + + Export the data in the current tab to a file + Güncel sekmedeki verileri bir dosyaya aktar + + + + &Export + &Dışa aktar + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Belirtilen CryptoLottoToken adresi ile imzalandığını doğrulamak için bir mesajı kontrol et + + + + &Verify Message + Mesaj &kontrol et + + + + &Delete + &Sil + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + Bunlar ödeme yapmak için kullanacağınız CryptoLottoToken adreslerinizdir. CryptoLottoToken yollamadan önce meblağı ve alıcı adresini daima kontrol ediniz. + + + + Copy &Label + &Etiketi kopyala + + + + &Edit + &Düzenle + + + + Send &Coins + Bit&coin Gönder + + + + Export Address Book Data + Adres defteri verilerini dışa aktar + + + + Comma separated file (*.csv) + Virgülle ayrılmış değerler dosyası (*.csv) + + + + Error exporting + Dışa aktarımda hata oluştu + + + + Could not write to file %1. + %1 dosyasına yazılamadı. + + + + AddressTableModel + + + Label + Etiket + + + + Address + Adres + + + + (no label) + (boş etiket) + + + + AskPassphraseDialog + + + Passphrase Dialog + Parola diyaloğu + + + + Enter passphrase + Parolayı giriniz + + + + New passphrase + Yeni parola + + + + Repeat new passphrase + Yeni parolayı tekrarlayınız + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Cüzdanınız için yeni parolayı giriniz.<br/>Lütfen <b>10 ya da daha fazla rastgele karakter</b> veya <b>sekiz ya da daha fazla kelime</b> içeren bir parola seçiniz. + + + + Encrypt wallet + Cüzdanı şifrele + + + + This operation needs your wallet passphrase to unlock the wallet. + Bu işlem cüzdan kilidini açmak için cüzdan parolanızı gerektirir. + + + + Unlock wallet + Cüzdan kilidini aç + + + + This operation needs your wallet passphrase to decrypt the wallet. + Bu işlem, cüzdan şifresini açmak için cüzdan parolasını gerektirir. + + + + Decrypt wallet + Cüzdan şifresini aç + + + + Change passphrase + Parolayı değiştir + + + + Enter the old and new passphrase to the wallet. + Cüzdan için eski ve yeni parolaları giriniz. + + + + Confirm wallet encryption + Cüzdan şifrelenmesini teyit eder + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + Uyarı: Eğer cüzdanınızı şifrelerseniz ve parolanızı kaybederseniz, <b>TÜM BİTCOİNLERİNİZİ KAYBEDERSİNİZ</b>! + + + + Are you sure you wish to encrypt your wallet? + Cüzdanınızı şifrelemek istediğinizden emin misiniz? + + + + 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. + Ö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. + + + + + Warning: The Caps Lock key is on! + Uyarı: Caps Lock tuşu faal durumda! + + + + + Wallet encrypted + Cüzdan şifrelendi + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + Şifreleme işlemini tamamlamak için CryptoLottoToken şimdi kapanacaktır. Cüzdanınızı şifrelemenin, CryptoLottoTokenlerinizin bilgisayara bulaşan kötücül bir yazılım tarafından çalınmaya karşı tamamen koruyamayacağını unutmayınız. + + + + + + + Wallet encryption failed + Cüzdan şifrelemesi başarısız oldu + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Dahili bir hata sebebiyle cüzdan şifrelemesi başarısız oldu. Cüzdanınız şifrelenmedi. + + + + + The supplied passphrases do not match. + Girilen parolalar birbirleriyle uyumlu değil. + + + + Wallet unlock failed + Cüzdan kilidinin açılması başarısız oldu + + + + + + The passphrase entered for the wallet decryption was incorrect. + Cüzdan şifresinin açılması için girilen parola yanlıştı. + + + + Wallet decryption failed + Cüzdan şifresinin açılması başarısız oldu + + + + Wallet passphrase was successfully changed. + Cüzdan parolası başarılı bir şekilde değiştirildi. + + + + BitcoinGUI + + + Sign &message... + &Mesaj imzala... + + + + Synchronizing with network... + Şebeke ile senkronizasyon... + + + + &Overview + &Genel bakış + + + + Show general overview of wallet + Cüzdana genel bakışı göster + + + + &Transactions + &Muameleler + + + + Browse transaction history + Muamele tarihçesini tara + + + + Edit the list of stored addresses and labels for sending + Saklanan adres ve etiket listesini düzenle + + + + Show the list of addresses for receiving payments + Ödeme alma adreslerinin listesini göster + + + + E&xit + &Çık + + + + Quit application + Uygulamadan çık + + + + Show information about CryptoLottoToken + CryptoLottoToken hakkında bilgi göster + + + + About &Qt + &Qt hakkında + + + + Show information about Qt + Qt hakkında bilgi görüntü + + + + &Options... + &Seçenekler... + + + + &Encrypt Wallet... + Cüzdanı &şifrele... + + + + &Backup Wallet... + Cüzdanı &yedekle... + + + + &Change Passphrase... + Parolayı &değiştir... + + + + Importing blocks from disk... + Bloklar diskten içe aktarılıyor... + + + + Reindexing blocks on disk... + Diskteki bloklar yeniden endeksleniyor... + + + + Send coins to a CryptoLottoToken address + Bir CryptoLottoToken adresine CryptoLottoToken yolla + + + + Modify configuration options for CryptoLottoToken + CryptoLottoToken seçeneklerinin yapılandırmasını değiştir + + + + Backup wallet to another location + Cüzdanı diğer bir konumda yedekle + + + + Change the passphrase used for wallet encryption + Cüzdan şifrelemesi için kullanılan parolayı değiştir + + + + &Debug window + &Hata ayıklama penceresi + + + + Open debugging and diagnostic console + Hata ayıklama ve teşhis penceresini aç + + + + &Verify message... + Mesaj &kontrol et... + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Cüzdan + + + + &Send + &Gönder + + + + &Receive + &Al + + + + &Addresses + &Adresler + + + + &About CryptoLottoToken + CryptoLottoToken &Hakkında + + + + &Show / Hide + &Göster / Sakla + + + + Show or hide the main Window + Ana pencereyi görüntüle ya da sakla + + + + Encrypt the private keys that belong to your wallet + Cüzdanınızın özel anahtarlarını şifrele + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Mesajları adreslerin size ait olduğunu ispatlamak için CryptoLottoToken adresleri ile imzala + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Belirtilen CryptoLottoToken adresleri ile imzalandıklarından emin olmak için mesajları kontrol et + + + + &File + &Dosya + + + + &Settings + &Ayarlar + + + + &Help + &Yardım + + + + Tabs toolbar + Sekme araç çubuğu + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + CryptoLottoToken istemcisi + + + + %n active connection(s) to CryptoLottoToken network + CryptoLottoToken şebekesine %n faal bağlantıCryptoLottoToken şebekesine %n faal bağlantı + + + + No block source available... + Hiçbir blok kaynağı mevcut değil... + + + + Processed %1 of %2 (estimated) blocks of transaction history. + Muamele tarihçesinin toplam (tahmini) %2 blokundan %1 blok işlendi. + + + + Processed %1 blocks of transaction history. + Muamele tarihçesinde %1 blok işlendi. + + + + %n hour(s) + %n saat%n saat + + + + %n day(s) + %n gün%n gün + + + + %n week(s) + %n hafta%n hafta + + + + %1 behind + %1 geride + + + + Last received block was generated %1 ago. + Son alınan blok %1 evvel oluşturulmuştu. + + + + Transactions after this will not yet be visible. + Bundan sonraki muameleler henüz görüntülenemez. + + + + Error + Hata + + + + Warning + Uyarı + + + + Information + Bilgi + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + Bu muamele boyut sınırlarını aşmıştır. Gene de %1 ücret ödeyerek gönderebilirsiniz, ki bu ücret muamelenizi işleyen ve şebekeye yardım eden düğümlere ödenecektir. Ücreti ödemek istiyor musunuz? + + + + Up to date + Güncel + + + + Catching up... + Aralık kapatılıyor... + + + + Confirm transaction fee + Muamele ücretini teyit et + + + + Sent transaction + Muamele yollandı + + + + Incoming transaction + Gelen muamele + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Tarih: %1 +Miktar: %2 +Tür: %3 +Adres: %4 + + + + + + URI handling + URI yönetimi + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + URI okunamadı! Sebebi geçersiz bir CryptoLottoToken adresi veya hatalı URI parametreleri olabilir. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + Cüzdan <b>şifrelenmiştir</b> ve şu anda <b>kilidi açıktır</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + Cüzdan <b>şifrelenmiştir</b> ve şu anda <b>kilitlidir</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + Ciddi bir hata oluştu. CryptoLottoToken artık güvenli bir şekilde işlemeye devam edemez ve kapanacaktır. + + + + ClientModel + + + Network Alert + Şebeke hakkında uyarı + + + + EditAddressDialog + + + Edit Address + Adresi düzenle + + + + &Label + &Etiket + + + + The label associated with this address book entry + Bu adres defteri unsuru ile ilişkili etiket + + + + &Address + &Adres + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Bu adres defteri unsuru ile ilişkili adres. Bu, sadece gönderi adresi için değiştirilebilir. + + + + New receiving address + Yeni alım adresi + + + + New sending address + Yeni gönderi adresi + + + + Edit receiving address + Alım adresini düzenle + + + + Edit sending address + Gönderi adresini düzenle + + + + The entered address "%1" is already in the address book. + Girilen "%1" adresi hâlihazırda adres defterinde mevcuttur. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + Girilen "%1" adresi geçerli bir CryptoLottoToken adresi değildir. + + + + Could not unlock wallet. + Cüzdan kilidi açılamadı. + + + + New key generation failed. + Yeni anahtar oluşturulması başarısız oldu. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + sürüm + + + + Usage: + Kullanım: + + + + command-line options + komut satırı seçenekleri + + + + UI options + Kullanıcı arayüzü seçenekleri + + + + Set language, for example "de_DE" (default: system locale) + Lisan belirt, mesela "de_De" (varsayılan: sistem dili) + + + + Start minimized + Küçültülmüş olarak başlat + + + + Show splash screen on startup (default: 1) + Başlatıldığında başlangıç ekranını göster (varsayılan: 1) + + + + OptionsDialog + + + Options + Seçenekler + + + + &Main + &Esas ayarlar + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + Muamelelerin hızlı işlenmesini garantilemeye yardım eden, seçime dayalı kB başı muamele ücreti. Muamelelerin çoğunluğunun boyutu 1 kB'dir. + + + + Pay transaction &fee + Muamele ücreti &öde + + + + Automatically start CryptoLottoToken after logging in to the system. + Sistemde oturum açıldığında CryptoLottoToken'i otomatik olarak başlat. + + + + &Start CryptoLottoToken on system login + CryptoLottoToken'i sistem oturumuyla &başlat + + + + Reset all client options to default. + İstemcinin tüm seçeneklerini varsayılan değerlere geri al. + + + + &Reset Options + Seçenekleri &sıfırla + + + + &Network + &Şebeke + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Yönlendiricide CryptoLottoToken istemci portlarını otomatik olarak açar. Bu, sadece yönlendiricinizin UPnP desteği bulunuyorsa ve etkinse çalışabilir. + + + + Map port using &UPnP + Portları &UPnP kullanarak haritala + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + CryptoLottoToken şebekesine SOCKS vekil sunucusu vasıtasıyla bağlan (mesela Tor ile bağlanıldığında). + + + + &Connect through SOCKS proxy: + SOCKS vekil sunucusu vasıtasıyla ba&ğlan: + + + + Proxy &IP: + Vekil &İP: + + + + IP address of the proxy (e.g. 127.0.0.1) + Vekil sunucunun İP adresi (mesela 127.0.0.1) + + + + &Port: + &Port: + + + + Port of the proxy (e.g. 9050) + Vekil sunucunun portu (mesela 9050) + + + + SOCKS &Version: + SOCKS &sürümü: + + + + SOCKS version of the proxy (e.g. 5) + Vekil sunucunun SOCKS sürümü (mesela 5) + + + + &Window + &Pencere + + + + Show only a tray icon after minimizing the window. + Küçültüldükten sonra sadece çekmece ikonu göster. + + + + &Minimize to the tray instead of the taskbar + İşlem çubuğu yerine sistem çekmecesine &küçült + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + 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. + + + + M&inimize on close + Kapatma sırasında k&üçült + + + + &Display + &Görünüm + + + + User Interface &language: + Kullanıcı arayüzü &lisanı: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + Kullanıcı arayüzünün dili burada belirtilebilir. Bu ayar CryptoLottoToken tekrar başlatıldığında etkinleşecektir. + + + + &Unit to show amounts in: + Miktarı göstermek için &birim: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + CryptoLottoToken gönderildiğinde arayüzde gösterilecek varsayılan alt birimi seçiniz. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + Muamele listesinde CryptoLottoToken adreslerinin gösterilip gösterilmeyeceklerini belirler. + + + + &Display addresses in transaction list + Muamele listesinde adresleri &göster + + + + &OK + &Tamam + + + + &Cancel + &İptal + + + + &Apply + &Uygula + + + + default + varsayılan + + + + Confirm options reset + Seçeneklerin sıfırlanmasını teyit et + + + + Some settings may require a client restart to take effect. + Bazı ayarların dikkate alınması istemcinin tekrar başlatılmasını gerektirebilir. + + + + Do you want to proceed? + Devam etmek istiyor musunuz? + + + + + Warning + Uyarı + + + + + This setting will take effect after restarting CryptoLottoToken. + Bu ayarlar CryptoLottoToken tekrar başlatıldığında etkinleşecektir. + + + + The supplied proxy address is invalid. + Girilen vekil sunucu adresi geçersizdir. + + + + OverviewPage + + + Form + Form + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + Görüntülenen veriler zaman aşımına uğramış olabilir. Bağlantı kurulduğunda cüzdanınız otomatik olarak şebeke ile eşleşir ancak bu işlem henüz tamamlanmamıştır. + + + + Balance: + Bakiye: + + + + Unconfirmed: + Doğrulanmamış: + + + + Wallet + Cüzdan + + + + Immature: + Olgunlaşmamış: + + + + Mined balance that has not yet matured + Oluşturulan bakiye henüz olgunlaşmamıştır + + + + <b>Recent transactions</b> + <b>Son muameleler</b> + + + + Your current balance + Güncel bakiyeniz + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Doğrulanması beklenen ve henüz güncel bakiyeye ilâve edilmemiş muamelelerin toplamı + + + + + out of sync + eşleşme dışı + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + CryptoLottoToken başlatılamadı: tıkla-ve-öde yöneticisi + + + + QRCodeDialog + + + QR Code Dialog + QR kodu diyaloğu + + + + Request Payment + Ödeme talebi + + + + Amount: + Miktar: + + + + Label: + Etiket: + + + + Message: + Mesaj: + + + + &Save As... + &Farklı kaydet... + + + + Error encoding URI into QR Code. + URI'nin QR koduna kodlanmasında hata oluştu. + + + + The entered amount is invalid, please check. + Girilen miktar geçersizdir, kontrol ediniz. + + + + Resulting URI too long, try to reduce the text for label / message. + Sonuç URI çok uzun, etiket ya da mesaj metnini kısaltmayı deneyiniz. + + + + Save QR Code + QR kodu kaydet + + + + PNG Images (*.png) + PNG resimleri (*.png) + + + + RPCConsole + + + Client name + İstemci ismi + + + + + + + + + + + + + N/A + Mevcut değil + + + + Client version + İstemci sürümü + + + + &Information + &Malumat + + + + Using OpenSSL version + Kullanılan OpenSSL sürümü + + + + Startup time + Başlama zamanı + + + + Network + Şebeke + + + + Number of connections + Bağlantı sayısı + + + + On testnet + Testnet üzerinde + + + + Block chain + Blok zinciri + + + + Current number of blocks + Güncel blok sayısı + + + + Estimated total blocks + Tahmini toplam blok sayısı + + + + Last block time + Son blok zamanı + + + + &Open + &Aç + + + + Command-line options + Komut satırı seçenekleri + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Mevcut CryptoLottoToken komut satırı seçeneklerinin listesini içeren CryptoLottoToken-Qt yardımını göster. + + + + &Show + &Göster + + + + &Console + &Konsol + + + + Build date + Derleme tarihi + + + + CryptoLottoToken - Debug window + CryptoLottoToken - Hata ayıklama penceresi + + + + CryptoLottoToken Core + CryptoLottoToken Çekirdeği + + + + Debug log file + Hata ayıklama kütük dosyası + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + Güncel veri klasöründen CryptoLottoToken hata ayıklama kütük dosyasını açar. Büyük kütük dosyaları için bu birkaç saniye alabilir. + + + + Clear console + Konsolu temizle + + + + Welcome to the CryptoLottoToken RPC console. + CryptoLottoToken RPC konsoluna hoş geldiniz. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Tarihçede gezinmek için imleç tuşlarını kullanınız, <b>Ctrl-L</b> ile de ekranı temizleyebilirsiniz. + + + + Type <b>help</b> for an overview of available commands. + Mevcut komutların listesi için <b>help</b> yazınız. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + CryptoLottoToken yolla + + + + Send to multiple recipients at once + Birçok alıcıya aynı anda gönder + + + + Add &Recipient + &Alıcı ekle + + + + Remove all transaction fields + Bütün muamele alanlarını kaldır + + + + Clear &All + Tümünü &temizle + + + + Balance: + Bakiye: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Yollama etkinliğini teyit ediniz + + + + S&end + G&önder + + + + <b>%1</b> to %2 (%3) + <b>%1</b> şu adrese: %2 (%3) + + + + Confirm send coins + Gönderiyi teyit ediniz + + + + Are you sure you want to send %1? + %1 göndermek istediğinizden emin misiniz? + + + + and + ve + + + + The recipient address is not valid, please recheck. + Alıcı adresi geçerli değildir, lütfen denetleyiniz. + + + + The amount to pay must be larger than 0. + Ödeyeceğiniz tutarın sıfırdan yüksek olması gerekir. + + + + The amount exceeds your balance. + Tutar bakiyenizden yüksektir. + + + + The total exceeds your balance when the %1 transaction fee is included. + Toplam, %1 muamele ücreti ilâve edildiğinde bakiyenizi geçmektedir. + + + + Duplicate address found, can only send to each address once per send operation. + Çift adres bulundu, belli bir gönderi sırasında her adrese sadece tek bir gönderide bulunulabilir. + + + + Error: Transaction creation failed! + Hata: Muamele oluşturması başarısız oldu! + + + + 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. + Hata: 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. + + + + SendCoinsEntry + + + Form + Form + + + + A&mount: + M&iktar: + + + + Pay &To: + &Şu kişiye öde: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Ödemenin gönderileceği adres (mesela Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + Adres defterinize eklemek için bu adrese ilişik bir etiket giriniz + + + + &Label: + &Etiket: + + + + Choose address from address book + Adres defterinden adres seç + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Panodan adres yapıştır + + + + Alt+P + Alt+P + + + + Remove this recipient + Bu alıcıyı kaldır + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + CryptoLottoToken adresi giriniz (mesela Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + İmzalar - Mesaj İmzala / Kontrol et + + + + &Sign Message + Mesaj &imzala + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Bir adresin sizin olduğunu ispatlamak için adresinizle mesaj imzalayabilirsiniz. Oltalama saldırılarının kimliğinizi imzanızla elde etmeyi deneyebilecekleri için belirsiz 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. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Mesajın imzalanmasında kullanılacak adres (mesela Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Adres defterinden bir adres seç + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Panodan adres yapıştır + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + İmzalamak istediğiniz mesajı burada giriniz + + + + Signature + İmza + + + + Copy the current signature to the system clipboard + Güncel imzayı sistem panosuna kopyala + + + + Sign the message to prove you own this CryptoLottoToken address + Bu CryptoLottoToken adresinin sizin olduğunu ispatlamak için mesajı imzalayın + + + + Sign &Message + &Mesajı imzala + + + + Reset all sign message fields + Tüm mesaj alanlarını sıfırla + + + + + Clear &All + Tümünü &temizle + + + + &Verify Message + Mesaj &kontrol et + + + + Enter the signing 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. + İmza için kullanılan adresi, 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. + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Mesajı imzalamak için kullanılmış olan adres (mesela Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Belirtilen CryptoLottoToken adresi ile imzalandığını doğrulamak için mesajı kontrol et + + + + Verify &Message + &Mesaj kontrol et + + + + Reset all verify message fields + Tüm mesaj kontrolü alanlarını sıfırla + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + CryptoLottoToken adresi giriniz (mesela Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + İmzayı oluşturmak için "Mesaj İmzala" unsurunu tıklayın + + + + Enter CryptoLottoToken signature + CryptoLottoToken imzası gir + + + + + The entered address is invalid. + Girilen adres geçersizdir. + + + + + + + Please check the address and try again. + Adresi kontrol edip tekrar deneyiniz. + + + + + The entered address does not refer to a key. + Girilen adres herhangi bir anahtara işaret etmemektedir. + + + + Wallet unlock was cancelled. + Cüzdan kilidinin açılması iptal edildi. + + + + Private key for the entered address is not available. + Girilen adres için özel anahtar mevcut değildir. + + + + Message signing failed. + Mesajın imzalanması başarısız oldu. + + + + Message signed. + Mesaj imzalandı. + + + + The signature could not be decoded. + İmzanın kodu çözülemedi. + + + + + Please check the signature and try again. + İmzayı kontrol edip tekrar deneyiniz. + + + + The signature did not match the message digest. + İmza mesajın hash değeri ile eşleşmedi. + + + + Message verification failed. + Mesaj doğrulaması başarısız oldu. + + + + Message verified. + Mesaj doğrulandı. + + + + SplashScreen + + + The CryptoLottoToken developers + CryptoLottoToken geliştiricileri + + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + %1 değerine dek açık + + + + %1/offline + %1/çevrim dışı + + + + %1/unconfirmed + %1/doğrulanmadı + + + + %1 confirmations + %1 teyit + + + + Status + Durum + + + + , broadcast through %n node(s) + , %n düğüm vasıtasıyla yayınlandı, %n düğüm vasıtasıyla yayınlandı + + + + Date + Tarih + + + + Source + Kaynak + + + + Generated + Oluşturuldu + + + + + From + Gönderen + + + + + + To + Alıcı + + + + + own address + kendi adresiniz + + + + label + etiket + + + + + + + + Credit + Gider + + + + matures in %n more block(s) + %n ek blok sonrasında olgunlaşacak%n ek blok sonrasında olgunlaşacak + + + + not accepted + kabul edilmedi + + + + + + + Debit + Gelir + + + + Transaction fee + Muamele ücreti + + + + Net amount + Net miktar + + + + Message + Mesaj + + + + Comment + Yorum + + + + Transaction ID + Muamele tanımlayıcı + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Oluşturulan CryptoLottoToken'lerin harcanabilmelerinden önce 120 blok beklemeleri gerekmektedir. Bu blok, oluşturduğunuzda blok zincirine eklenmesi için ağda yayınlandı. Zincire eklenmesi başarısız olursa, durumu "kabul edilmedi" olarak değiştirilecek ve harcanamayacaktır. Bu, bazen başka bir düğüm sizden birkaç saniye önce ya da sonra blok oluşturursa meydana gelebilir. + + + + Debug information + Hata ayıklama verileri + + + + Transaction + Muamele + + + + Inputs + Girdiler + + + + Amount + Miktar + + + + true + doğru + + + + false + yanlış + + + + , has not been successfully broadcast yet + , henüz başarılı bir şekilde yayınlanmadı + + + + Open for %n more block(s) + %n ilâve blok için açık%n ilâve blok için açık + + + + unknown + bilinmiyor + + + + TransactionDescDialog + + + Transaction details + Muamele detayları + + + + This pane shows a detailed description of the transaction + Bu pano muamelenin ayrıntılı açıklamasını gösterir + + + + TransactionTableModel + + + Date + Tarih + + + + Type + Tür + + + + Address + Adres + + + + Amount + Miktar + + + + Open for %n more block(s) + %n ilâve blok için açık%n ilâve blok için açık + + + + Open until %1 + %1 değerine dek açık + + + + Offline (%1 confirmations) + Çevrimdışı (%1 teyit) + + + + Unconfirmed (%1 of %2 confirmations) + Doğrulanmadı (%1 (toplam %2 üzerinden) teyit) + + + + Confirmed (%1 confirmations) + Doğrulandı (%1 teyit) + + + + Mined balance will be available when it matures in %n more block(s) + Madenden çıkarılan bakiye %n ek blok sonrasında olgunlaştığında kullanılabilecektirMadenden çıkarılan bakiye %n ek blok sonrasında olgunlaştığında kullanılabilecektir + + + + This block was not received by any other nodes and will probably not be accepted! + Bu blok başka hiçbir düğüm tarafından alınmamıştır ve muhtemelen kabul edilmeyecektir! + + + + Generated but not accepted + Oluşturuldu ama kabul edilmedi + + + + Received with + Şununla alındı + + + + Received from + Alındığı kişi + + + + Sent to + Gönderildiği adres + + + + Payment to yourself + Kendinize ödeme + + + + Mined + Madenden çıkarılan + + + + (n/a) + (mevcut değil) + + + + Transaction status. Hover over this field to show number of confirmations. + Muamele durumu. Doğrulama sayısını görüntülemek için imleci bu alanda tutunuz. + + + + Date and time that the transaction was received. + Muamelenin alındığı tarih ve zaman. + + + + Type of transaction. + Muamele türü. + + + + Destination address of transaction. + Muamelenin alıcı adresi. + + + + Amount removed from or added to balance. + Bakiyeden alınan ya da bakiyeye eklenen miktar. + + + + TransactionView + + + + All + Hepsi + + + + Today + Bugün + + + + This week + Bu hafta + + + + This month + Bu ay + + + + Last month + Geçen ay + + + + This year + Bu sene + + + + Range... + Aralık... + + + + Received with + Şununla alınan + + + + Sent to + Gönderildiği adres + + + + To yourself + Kendinize + + + + Mined + Oluşturulan + + + + Other + Diğer + + + + Enter address or label to search + Aranacak adres ya da etiket giriniz + + + + Min amount + Asgari miktar + + + + Copy address + Adresi kopyala + + + + Copy label + Etiketi kopyala + + + + Copy amount + Miktarı kopyala + + + + Copy transaction ID + Muamele kimliğini kopyala + + + + Edit label + Etiketi düzenle + + + + Show transaction details + Muamele detaylarını göster + + + + Export Transaction Data + Muamele verilerini dışa aktar + + + + Comma separated file (*.csv) + Virgülle ayrılmış değerler dosyası (*.csv) + + + + Confirmed + Doğrulandı + + + + Date + Tarih + + + + Type + Tür + + + + Label + Etiket + + + + Address + Adres + + + + Amount + Miktar + + + + ID + Tanımlayıcı + + + + Error exporting + Dışa aktarımda hata oluştu + + + + Could not write to file %1. + %1 dosyasına yazılamadı. + + + + Range: + Aralık: + + + + to + ilâ + + + + WalletModel + + + Send Coins + CryptoLottoToken yolla + + + + WalletView + + + &Export + &Dışa aktar + + + + Export the data in the current tab to a file + Güncel sekmedeki verileri bir dosyaya aktar + + + + Backup Wallet + Cüzdanı yedekle + + + + Wallet Data (*.dat) + Cüzdan verileri (*.dat) + + + + Backup Failed + Yedekleme başarısız oldu + + + + There was an error trying to save the wallet data to the new location. + Cüzdanı değişik bir konuma kaydetmek denenirken bir hata meydana geldi. + + + + Backup Successful + Yedekleme başarılı + + + + The wallet data was successfully saved to the new location. + Cüzdan verileri başarılı bir şekilde yeni konuma kaydedildi. + + + + bitcoin-core + + + CryptoLottoToken version + CryptoLottoToken sürümü + + + + Usage: + Kullanım: + + + + Send command to -server or CryptoLottoTokend + -server ya da CryptoLottoTokend'ye komut gönder + + + + List commands + Komutları listele + + + + Get help for a command + Bir komut için yardım al + + + + Options: + Seçenekler: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Yapılandırma dosyası belirt (varsayılan: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + Pid dosyası belirt (varsayılan: CryptoLottoTokend.pid) + + + + Specify data directory + Veri dizinini belirt + + + + Set database cache size in megabytes (default: 25) + Veritabanı önbellek boyutunu megabayt olarak belirt (varsayılan: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Bağlantılar için dinlenecek <port> (varsayılan: 22556 ya da testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Eşler ile en çok <n> adet bağlantı kur (varsayılan: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + Eş adresleri elde etmek için bir düğüme bağlan ve ardından bağlantıyı kes + + + + Specify your own public address + Kendi genel adresinizi tanımlayın + + + + Threshold for disconnecting misbehaving peers (default: 100) + Aksaklık gösteren eşlerle bağlantıyı kesme sınırı (varsayılan: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Aksaklık gösteren eşlerle yeni bağlantıları engelleme süresi, saniye olarak (varsayılan: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + IPv4 üzerinde dinlemek için %u numaralı RPC portunun kurulumu sırasında hata meydana geldi: %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + JSON-RPC bağlantılarını <port> üzerinde dinle (varsayılan: 22555 veya tesnet: 44555) + + + + Accept command line and JSON-RPC commands + Konut satırı ve JSON-RPC komutlarını kabul et + + + + Run in the background as a daemon and accept commands + Arka planda daemon (servis) olarak çalış ve komutları kabul et + + + + Use the test network + Deneme şebekesini kullan + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + Dışarıdan gelen bağlantıları kabul et (varsayılan: -proxy veya -connect yoksa 1) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + %s, şu yapılandırma dosyasında rpc parolası belirtmeniz gerekir: +%s +Aşağıdaki rastgele oluşturulan parolayı kullanmanız tavsiye edilir: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(bu parolayı hatırlamanız gerekli değildir) +Kullanıcı ismi ile parolanın FARKLI olmaları gerekir. +Dosya mevcut değilse, sadece sahibi için okumayla sınırlı izin ile oluşturunuz. +Sorunlar hakkında bildiri almak için alertnotify unsurunu ayarlamanız tavsiye edilir; +mesela: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + IPv6 üzerinde dinlemek için %u numaralı RPC portu kurulurken bir hata meydana geldi, IPv4'e dönülüyor: %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + Belirtilen adrese bağlan ve daima ondan dinle. IPv6 için [makine]:port yazımını kullanınız + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + %s veri dizininde kilit elde edilemedi. CryptoLottoToken muhtemelen hâlihazırda çalışmaktadır. + + + + 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. + Hata: 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. + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + Hata: Muamelenin miktarı, karmaşıklığı ya da yakın geçmişte alınan fonların kullanılması nedeniyle bu muamele en az %s tutarında ücret gerektirmektedir! + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + İlgili bir uyarı alındığında komut çalıştır (komuttaki %s mesaj ile değiştirilecektir) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Bir cüzdan muamelesi değiştiğinde komutu çalıştır (komuttaki %s TxID ile değiştirilecektir) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Yüksek öncelikli/düşük ücretli muamelelerin boyutunu bayt olarak tanımla (varsayılan: 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + Bu yayın öncesi bir deneme sürümüdür - tüm riski siz üstlenmiş olursunuz - CryptoLottoToken oluşturmak ya da ticari uygulamalar için kullanmayınız + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Uyarı: -paytxfee çok yüksek bir değere ayarlanmış! Bu, muamele gönderirseniz ödeyeceğiniz muamele ücretidir. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + Uyarı: Görüntülenen muameleler doğru olmayabilir! Sizin ya da diğer düğümlerin güncelleme yapması gerekebilir. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Uyarı: Lütfen bilgisayarınızın tarih ve saatinin doğru olup olmadığını kontrol ediniz! Saatiniz doğru değilse CryptoLottoToken gerektiği gibi çalışamaz. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Uyarı: wallet.dat dosyasının okunması sırasında bir hata meydana geldi! Tüm anahtarlar doğru bir şekilde okundu, ancak muamele verileri ya da adres defteri unsurları hatalı veya eksik olabilir. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Uyarı: wallet.dat bozuk, veriler geri kazanıldı! Özgün wallet.dat, wallet.{zamandamgası}.bak olarak %s klasörüne kaydedildi; bakiyeniz ya da muameleleriniz yanlışsa bir yedeklemeden tekrar yüklemeniz gerekir. + + + + Attempt to recover private keys from a corrupt wallet.dat + Bozuk bir wallet.dat dosyasından özel anahtarları geri kazanmayı dene + + + + Block creation options: + Blok oluşturma seçenekleri: + + + + Connect only to the specified node(s) + Sadece belirtilen düğüme veya düğümlere bağlan + + + + Corrupted block database detected + Bozuk blok veritabanı tespit edildi + + + + Discover own IP address (default: 1 when listening and no -externalip) + Kendi IP adresini keşfet (varsayılan: dinlenildiğinde ve -externalip yoksa 1) + + + + Do you want to rebuild the block database now? + Blok veritabanını şimdi yeniden inşa etmek istiyor musunuz? + + + + Error initializing block database + Blok veritabanını başlatılırken bir hata meydana geldi + + + + Error initializing wallet database environment %s! + %s cüzdan veritabanı ortamının başlatılmasında hata meydana geldi! + + + + Error loading block database + Blok veritabanının yüklenmesinde hata + + + + Error opening block database + Blok veritabanının açılışı sırasında hata + + + + Error: Disk space is low! + Hata: Disk alanı düşük! + + + + Error: Wallet locked, unable to create transaction! + Hata: Cüzdan kilitli, muamele oluşturulamadı! + + + + Error: system error: + Hata: sistem hatası: + + + + Failed to listen on any port. Use -listen=0 if you want this. + Herhangi bir portun dinlenmesi başarısız oldu. Bunu istiyorsanız -listen=0 seçeneğini kullanınız. + + + + Failed to read block info + Blok verileri okunamadı + + + + Failed to read block + Blok okunamadı + + + + Failed to sync block index + Blok indeksi eşleştirilemedi + + + + Failed to write block index + Blok indeksi yazılamadı + + + + Failed to write block info + Blok verileri yazılamadı + + + + Failed to write block + Blok yazılamadı + + + + Failed to write file info + Dosya verileri yazılamadı + + + + Failed to write to coin database + Madenî para veritabanına yazılamadı + + + + Failed to write transaction index + Muamele indeksi yazılamadı + + + + Failed to write undo data + Geri alma verilerinin yazılamadı + + + + Find peers using DNS lookup (default: 1 unless -connect) + Eşleri DNS araması vasıtasıyla bul (varsayılan: 1, eğer -connect kullanılmadıysa) + + + + Generate coins (default: 0) + CryptoLottoToken oluştur (varsayılan: 0) + + + + How many blocks to check at startup (default: 288, 0 = all) + Başlangıçta kontrol edilecek blok sayısı (varsayılan: 288, 0 = hepsi) + + + + How thorough the block verification is (0-4, default: 3) + Blok kontrolünün ne kadar derin olacağı (0 ilâ 4, varsayılan: 3) + + + + Not enough file descriptors available. + Kafi derecede dosya tanımlayıcıları mevcut değil. + + + + Rebuild block chain index from current blk000??.dat files + Blok zinciri indeksini güncel blk000??.dat dosyalarından tekrar inşa et + + + + Set the number of threads to service RPC calls (default: 4) + RPC aramaları için iş parçacığı sayısını belirle (varsayılan: 4) + + + + Verifying blocks... + Bloklar kontrol ediliyor... + + + + Verifying wallet... + Cüzdan kontrol ediliyor... + + + + Imports blocks from external blk000??.dat file + Harici blk000??.dat dosyasından blokları içe aktarır + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + Betik kontrolü iş parçacığı sayısını belirt (azami 16, 0 = otomatik, <0 = bu sayıda çekirdeği boş bırak, varsayılan: 0) + + + + Information + Bilgi + + + + Invalid -tor address: '%s' + Geçersiz -tor adresi: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + -minrelaytxfee=<amount> için geçersiz meblağ: '%s' + + + + Invalid amount for -mintxfee=<amount>: '%s' + -mintxfee=<amount> için geçersiz meblağ: '%s' + + + + Maintain a full transaction index (default: 0) + Muamelelerin tamamının indeksini tut (varsayılan: 0) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Bağlantı başına azami alım tamponu, <n>*1000 bayt (varsayılan: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Bağlantı başına azami yollama tamponu, <n>*1000 bayt (varsayılan: 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + Sadece yerleşik kontrol noktalarıyla eşleşen blok zincirini kabul et (varsayılan: 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + Sadece <net> şebekesindeki düğümlere bağlan (IPv4, IPv6 ya da Tor) + + + + Output extra debugging information. Implies all other -debug* options + İlâve hata ayıklama verileri çıkart. Diğer tüm -debug* seçeneklerini ima eder + + + + Output extra network debugging information + İlâve şebeke hata ayıklama verileri çıkart + + + + Prepend debug output with timestamp (default: 1) + Hata ayıklama çıktısına tarih ön ekleri ilâve et + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + SSL seçenekleri: (SSL kurulum bilgisi için CryptoLottoToken vikisine bakınız) + + + + Select the version of socks proxy to use (4-5, default: 5) + Kullanılacak socks vekil sunucu sürümünü seç (4-5, varsayılan: 5) + + + + Send trace/debug info to console instead of debug.log file + Trace/hata ayıklama verilerini debug.log dosyası yerine konsola gönder + + + + Send trace/debug info to debugger + Hata ayıklayıcıya -debugger- trace/hata ayıklama verileri gönder + + + + Set maximum block size in bytes (default: 250000) + Bayt olarak azami blok boyutunu tanımla (varsayılan: 250000) + + + + Set minimum block size in bytes (default: 0) + Bayt olarak asgari blok boyutunu tanımla (varsayılan: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + İstemci başlatıldığında debug.log dosyasını küçült (varsayılan: -debug bulunmadığında 1) + + + + Signing transaction failed + Muamelenin imzalanması başarısız oldu + + + + Specify connection timeout in milliseconds (default: 5000) + Bağlantı zaman aşım süresini milisaniye olarak belirt (varsayılan: 5000) + + + + System error: + Sistem hatası: + + + + Transaction amount too small + Muamele meblağı çok düşük + + + + Transaction amounts must be positive + Muamele tutarının pozitif olması lazımdır + + + + Transaction too large + Muamele çok büyük + + + + Use UPnP to map the listening port (default: 0) + Dinlenecek portu haritalamak için UPnP kullan (varsayılan: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Dinlenecek portu haritalamak için UPnP kullan (varsayılan: dinlenildiğinde 1) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + Gizli tor servislerine erişmek için vekil sunucu kullan (varsayılan: -proxy ile aynısı) + + + + Username for JSON-RPC connections + JSON-RPC bağlantıları için kullanıcı ismi + + + + Warning + Uyarı + + + + Warning: This version is obsolete, upgrade required! + Uyarı: Bu sürüm çok eskidir, güncellemeniz gerekir! + + + + You need to rebuild the databases using -reindex to change -txindex + -txindex'i değiştirmek için veritabanlarını -reindex kullanarak yeniden inşa etmeniz gerekir. + + + + wallet.dat corrupt, salvage failed + wallet.dat bozuk, geri kazanım başarısız oldu + + + + Password for JSON-RPC connections + JSON-RPC bağlantıları için parola + + + + Allow JSON-RPC connections from specified IP address + Belirtilen İP adresinden JSON-RPC bağlantılarını kabul et + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Şu <ip> adresinde (varsayılan: 127.0.0.1) çalışan düğüme komut yolla + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + En iyi blok değiştiğinde komutu çalıştır (komut için %s parametresi blok hash değeri ile değiştirilecektir) + + + + Upgrade wallet to latest format + Cüzdanı en yeni biçime güncelle + + + + Set key pool size to <n> (default: 100) + Anahtar alan boyutunu <n> değerine ayarla (varsayılan: 100) + + + + Rescan the block chain for missing wallet transactions + Blok zincirini eksik cüzdan muameleleri için tekrar tara + + + + Use OpenSSL (https) for JSON-RPC connections + JSON-RPC bağlantıları için OpenSSL (https) kullan + + + + Server certificate file (default: server.cert) + Sunucu sertifika dosyası (varsayılan: server.cert) + + + + Server private key (default: server.pem) + Sunucu özel anahtarı (varsayılan: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Kabul edilebilir şifreler (varsayılan: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Bu yardım mesajı + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Bu bilgisayarda %s unsuruna bağlanılamadı. (bind şu hatayı iletti: %d, %s) + + + + Connect through socks proxy + Socks vekil sunucusu vasıtasıyla bağlan + + + + Allow DNS lookups for -addnode, -seednode and -connect + -addnode, -seednode ve -connect için DNS aramalarına izin ver + + + + Loading addresses... + Adresler yükleniyor... + + + + Error loading wallet.dat: Wallet corrupted + wallet.dat dosyasının yüklenmesinde hata oluştu: bozuk cüzdan + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + wallet.dat dosyasının yüklenmesinde hata oluştu: cüzdanın daha yeni bir CryptoLottoToken sürümüne ihtiyacı var + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Cüzdanın tekrar yazılması gerekiyordu: işlemi tamamlamak için CryptoLottoToken'i yeniden başlatınız + + + + Error loading wallet.dat + wallet.dat dosyasının yüklenmesinde hata oluştu + + + + Invalid -proxy address: '%s' + Geçersiz -proxy adresi: '%s' + + + + Unknown network specified in -onlynet: '%s' + -onlynet için bilinmeyen bir şebeke belirtildi: '%s' + + + + Unknown -socks proxy version requested: %i + Bilinmeyen bir -socks vekil sürümü talep edildi: %i + + + + Cannot resolve -bind address: '%s' + -bind adresi çözümlenemedi: '%s' + + + + Cannot resolve -externalip address: '%s' + -externalip adresi çözümlenemedi: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + -paytxfee=<miktar> için geçersiz miktar: '%s' + + + + Invalid amount + Geçersiz miktar + + + + Insufficient funds + Yetersiz bakiye + + + + Loading block index... + Blok indeksi yükleniyor... + + + + Add a node to connect to and attempt to keep the connection open + Bağlanılacak düğüm ekle ve bağlantıyı zinde tutmaya çalış + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Bu bilgisayarda %s unsuruna bağlanılamadı. CryptoLottoToken muhtemelen hâlihazırda çalışmaktadır. + + + + Fee per KB to add to transactions you send + Yolladığınız muameleler için eklenecek KB başı ücret + + + + Loading wallet... + Cüzdan yükleniyor... + + + + Cannot downgrade wallet + Cüzdan eski biçime geri alınamaz + + + + Cannot write default address + Varsayılan adres yazılamadı + + + + Rescanning... + Yeniden tarama... + + + + Done loading + Yükleme tamamlandı + + + + To use the %s option + %s seçeneğini kullanmak için + + + + Error + Hata + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + rpcpassword=<parola> şu yapılandırma dosyasında belirtilmelidir: +%s +Dosya mevcut değilse, sadece sahibi için okumayla sınırlı izin ile oluşturunuz. + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_uk.qm b/src/qt/locale/bitcoin_uk.qm new file mode 100644 index 0000000..f0cdf0d Binary files /dev/null and b/src/qt/locale/bitcoin_uk.qm differ diff --git a/src/qt/locale/bitcoin_uk.ts b/src/qt/locale/bitcoin_uk.ts new file mode 100644 index 0000000..f893f67 --- /dev/null +++ b/src/qt/locale/bitcoin_uk.ts @@ -0,0 +1,2928 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + Про CryptoLottoToken + + + + <b>CryptoLottoToken</b> version + Версія <b>CryptoLottoToken'a<b> + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +Це програмне забезпечення є експериментальним. + +Поширюється за ліцензією MIT/X11, додаткова інформація міститься у файлі COPYING, а також за адресою http://www.opensource.org/licenses/mit-license.php. + +Цей продукт включає в себе програмне забезпечення, розроблене в рамках проекту OpenSSL (http://www.openssl.org/), криптографічне програмне забезпечення, написане Еріком Янгом (eay@cryptsoft.com), та функції для роботи з UPnP, написані Томасом Бернардом. + + + + Copyright + Авторське право + + + + The CryptoLottoToken developers + + + + + AddressBookPage + + + Address Book + Адресна книга + + + + Double-click to edit address or label + Двічі клікніть на адресу чи назву для їх зміни + + + + Create a new address + Створити нову адресу + + + + Copy the currently selected address to the system clipboard + Копіювати виділену адресу в буфер обміну + + + + &New Address + &Створити адресу + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + Це ваші адреси для отримання платежів. Ви можете давати різні адреси різним людям, таким чином маючи можливість відслідкувати хто конкретно і скільки вам заплатив. + + + + &Copy Address + &Скопіювати адресу + + + + Show &QR Code + Показати QR-&Код + + + + Sign a message to prove you own a CryptoLottoToken address + Підпишіть повідомлення щоб довести, що ви є власником цієї адреси + + + + Sign &Message + &Підписати повідомлення + + + + Delete the currently selected address from the list + Вилучити вибрані адреси з переліку + + + + Export the data in the current tab to a file + Експортувати дані з поточної вкладки в файл + + + + &Export + + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + Перевірте повідомлення для впевненості, що воно підписано вказаною CryptoLottoToken-адресою + + + + &Verify Message + Перевірити повідомлення + + + + &Delete + &Видалити + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + + + + + Copy &Label + Скопіювати &мітку + + + + &Edit + &Редагувати + + + + Send &Coins + + + + + Export Address Book Data + Експортувати адресну книгу + + + + Comma separated file (*.csv) + Файли відділені комами (*.csv) + + + + Error exporting + Помилка при експортуванні + + + + Could not write to file %1. + Неможливо записати у файл %1. + + + + AddressTableModel + + + Label + Назва + + + + Address + Адреса + + + + (no label) + (немає назви) + + + + AskPassphraseDialog + + + Passphrase Dialog + Діалог введення паролю + + + + Enter passphrase + Введіть пароль + + + + New passphrase + Новий пароль + + + + Repeat new passphrase + Повторіть пароль + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + Введіть новий пароль для гаманця.<br/>Будь ласка, використовуйте паролі що містять <b>як мінімум 10 випадкових символів</b>, або <b>як мінімум 8 слів</b>. + + + + Encrypt wallet + Зашифрувати гаманець + + + + This operation needs your wallet passphrase to unlock the wallet. + Ця операція потребує пароль для розблокування гаманця. + + + + Unlock wallet + Розблокувати гаманець + + + + This operation needs your wallet passphrase to decrypt the wallet. + Ця операція потребує пароль для дешифрування гаманця. + + + + Decrypt wallet + Дешифрувати гаманець + + + + Change passphrase + Змінити пароль + + + + Enter the old and new passphrase to the wallet. + Ввести старий та новий паролі для гаманця. + + + + Confirm wallet encryption + Підтвердити шифрування гаманця + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + УВАГА: Якщо ви зашифруєте гаманець і забудете пароль, ви <b>ВТРАТИТЕ ВСІ СВОЇ БІТКОІНИ</b>! + + + + Are you sure you wish to encrypt your wallet? + Ви дійсно хочете зашифрувати свій гаманець? + + + + 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. + + + + + + Warning: The Caps Lock key is on! + Увага: Ввімкнено Caps Lock! + + + + + Wallet encrypted + Гаманець зашифровано + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + Біткоін-клієнт буде закрито для завершення процесу шифрування. Пам'ятайте, що шифрування гаманця не може повністю захистити ваші біткоіни від крадіжки, у випадку якщо ваш комп'ютер буде інфіковано шкідливими програмами. + + + + + + + Wallet encryption failed + Не вдалося зашифрувати гаманець + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + Виникла помилка під час шифрування гаманця. Ваш гаманець не було зашифровано. + + + + + The supplied passphrases do not match. + Введені паролі не співпадають. + + + + Wallet unlock failed + Не вдалося розблокувати гаманець + + + + + + The passphrase entered for the wallet decryption was incorrect. + Введений пароль є невірним. + + + + Wallet decryption failed + Не вдалося розшифрувати гаманець + + + + Wallet passphrase was successfully changed. + Пароль було успішно змінено. + + + + BitcoinGUI + + + Sign &message... + &Підписати повідомлення... + + + + Synchronizing with network... + Синхронізація з мережею... + + + + &Overview + &Огляд + + + + Show general overview of wallet + Показати загальний огляд гаманця + + + + &Transactions + Транзакції + + + + Browse transaction history + Переглянути історію транзакцій + + + + Edit the list of stored addresses and labels for sending + Редагувати список збережених адрес та міток + + + + Show the list of addresses for receiving payments + Показати список адрес для отримання платежів + + + + E&xit + &Вихід + + + + Quit application + Вийти + + + + Show information about CryptoLottoToken + Показати інформацію про CryptoLottoToken + + + + About &Qt + &Про Qt + + + + Show information about Qt + Показати інформацію про Qt + + + + &Options... + &Параметри... + + + + &Encrypt Wallet... + &Шифрування гаманця... + + + + &Backup Wallet... + &Резервне копіювання гаманця... + + + + &Change Passphrase... + Змінити парол&ь... + + + + Importing blocks from disk... + Імпорт блоків з диску... + + + + Reindexing blocks on disk... + + + + + Send coins to a CryptoLottoToken address + Відправити монети на вказану адресу + + + + Modify configuration options for CryptoLottoToken + Редагувати параметри + + + + Backup wallet to another location + Резервне копіювання гаманця в інше місце + + + + Change the passphrase used for wallet encryption + Змінити пароль, який використовується для шифрування гаманця + + + + &Debug window + Вікно зневадження + + + + Open debugging and diagnostic console + Відкрити консоль зневадження і діагностики + + + + &Verify message... + Перевірити повідомлення... + + + + + CryptoLottoToken + CryptoLottoToken + + + + Wallet + Гаманець + + + + &Send + + + + + &Receive + + + + + &Addresses + + + + + &About CryptoLottoToken + &Про CryptoLottoToken + + + + &Show / Hide + Показати / Приховати + + + + Show or hide the main Window + Показує або приховує головне вікно + + + + Encrypt the private keys that belong to your wallet + + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + Підтвердіть, що Ви є власником повідомлення підписавши його Вашою CryptoLottoToken-адресою + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + Перевірте повідомлення для впевненості, що воно підписано вказаною CryptoLottoToken-адресою + + + + &File + &Файл + + + + &Settings + &Налаштування + + + + &Help + &Довідка + + + + Tabs toolbar + Панель вкладок + + + + + [testnet] + [тестова мережа] + + + + CryptoLottoToken client + CryptoLottoToken-клієнт + + + + %n active connection(s) to CryptoLottoToken network + %n активне з'єднання з мережею%n активні з'єднання з мережею%n активних з'єднань з мережею + + + + No block source available... + + + + + Processed %1 of %2 (estimated) blocks of transaction history. + + + + + Processed %1 blocks of transaction history. + Оброблено %1 блоків історії транзакцій. + + + + %n hour(s) + + + + + %n day(s) + + + + + %n week(s) + + + + + %1 behind + + + + + Last received block was generated %1 ago. + + + + + Transactions after this will not yet be visible. + + + + + Error + Помилка + + + + Warning + Увага + + + + Information + Інформація + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + + + + + Up to date + Синхронізовано + + + + Catching up... + Синхронізується... + + + + Confirm transaction fee + Підтвердити комісію + + + + Sent transaction + Надіслані транзакції + + + + Incoming transaction + Отримані перекази + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + Дата: %1 +Кількість: %2 +Тип: %3 +Адреса: %4 + + + + + + URI handling + Обробка URI + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + Неможливо обробити URI! Це може бути викликано неправильною CryptoLottoToken-адресою, чи невірними параметрами URI. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + <b>Зашифрований</b> гаманець <b>розблоковано</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + <b>Зашифрований</b> гаманець <b>заблоковано</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + + + + + ClientModel + + + Network Alert + Сповіщення мережі + + + + EditAddressDialog + + + Edit Address + Редагувати адресу + + + + &Label + &Мітка + + + + The label associated with this address book entry + Мітка, пов'язана з цим записом адресної книги + + + + &Address + &Адреса + + + + The address associated with this address book entry. This can only be modified for sending addresses. + Адреса, пов'язана з цим записом адресної книги. Може бути змінено тільки для адреси відправника. + + + + New receiving address + Нова адреса для отримання + + + + New sending address + Нова адреса для відправлення + + + + Edit receiving address + Редагувати адресу для отримання + + + + Edit sending address + Редагувати адресу для відправлення + + + + The entered address "%1" is already in the address book. + Введена адреса «%1» вже присутня в адресній книзі. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + Введена адреса «%1» не є коректною адресою в мережі CryptoLottoToken. + + + + Could not unlock wallet. + Неможливо розблокувати гаманець. + + + + New key generation failed. + Не вдалося згенерувати нові ключі. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + версія + + + + Usage: + Використання: + + + + command-line options + параметри командного рядка + + + + UI options + Параметри інтерфейсу + + + + Set language, for example "de_DE" (default: system locale) + Встановлення мови, наприклад "de_DE" (типово: системна) + + + + Start minimized + Запускати згорнутим + + + + Show splash screen on startup (default: 1) + Показувати заставку під час запуску (типово: 1) + + + + OptionsDialog + + + Options + Параметри + + + + &Main + &Головні + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + Заплатити комісі&ю + + + + Automatically start CryptoLottoToken after logging in to the system. + Автоматично запускати гаманець при вході до системи. + + + + &Start CryptoLottoToken on system login + &Запускати гаманець при вході в систему + + + + Reset all client options to default. + Скинути всі параметри клієнта на типові. + + + + &Reset Options + Скинути параметри + + + + &Network + &Мережа + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + Автоматично відкривати порт для клієнту біткоін на роутері. Працює лише якщо ваш роутер підтримує UPnP і ця функція увімкнена. + + + + Map port using &UPnP + Відображення порту через &UPnP + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + Підключатись до мережі CryptoLottoToken через SOCKS-проксі (наприклад при використанні Tor). + + + + &Connect through SOCKS proxy: + Підключатись через &SOCKS-проксі: + + + + Proxy &IP: + &IP проксі: + + + + IP address of the proxy (e.g. 127.0.0.1) + IP-адреса проксі-сервера (наприклад 127.0.0.1) + + + + &Port: + &Порт: + + + + Port of the proxy (e.g. 9050) + Порт проксі-сервера (наприклад 9050) + + + + SOCKS &Version: + SOCKS версії: + + + + SOCKS version of the proxy (e.g. 5) + Версія SOCKS-проксі (наприклад 5) + + + + &Window + &Вікно + + + + Show only a tray icon after minimizing the window. + Показувати лише іконку в треї після згортання вікна. + + + + &Minimize to the tray instead of the taskbar + Мінімізувати &у трей + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + Згортати замість закриття. Якщо ця опція включена, програма закриється лише після вибору відповідного пункту в меню. + + + + M&inimize on close + Згортати замість закритт&я + + + + &Display + &Відображення + + + + User Interface &language: + Мова інтерфейсу користувача: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + Встановлює мову інтерфейсу. Зміни набудуть чинності після перезапуску CryptoLottoToken. + + + + &Unit to show amounts in: + В&имірювати монети в: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + Виберіть одиницю вимірювання монет, яка буде відображатись в гаманці та при відправленні. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + + + + + &Display addresses in transaction list + &Відображати адресу в списку транзакцій + + + + &OK + &Гаразд + + + + &Cancel + &Скасувати + + + + &Apply + &Застосувати + + + + default + типово + + + + Confirm options reset + Підтвердження скидання параметрів + + + + Some settings may require a client restart to take effect. + Деякі параметри потребують перезапуск клієнта для набуття чинності. + + + + Do you want to proceed? + Продовжувати? + + + + + Warning + Увага + + + + + This setting will take effect after restarting CryptoLottoToken. + Цей параметр набуде чинності після перезапуску CryptoLottoToken. + + + + The supplied proxy address is invalid. + Невірно вказано адресу проксі. + + + + OverviewPage + + + Form + Форма + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + Показана інформація вже може бути застарілою. Ваш гаманець буде автоматично синхронізовано з мережею CryptoLottoToken після встановлення підключення, але цей процес ще не завершено. + + + + Balance: + Баланс: + + + + Unconfirmed: + Непідтверджені: + + + + Wallet + Гаманець + + + + Immature: + + + + + Mined balance that has not yet matured + + + + + <b>Recent transactions</b> + <b>Недавні транзакції</b> + + + + Your current balance + Ваш поточний баланс + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + Загальна сума всіх транзакцій, які ще не підтверджені, та до цих пір не враховуються в загальному балансі + + + + + out of sync + не синхронізовано + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + + + + + QRCodeDialog + + + QR Code Dialog + Діалог QR-коду + + + + Request Payment + Запросити Платіж + + + + Amount: + Кількість: + + + + Label: + Мітка: + + + + Message: + Повідомлення: + + + + &Save As... + &Зберегти як... + + + + Error encoding URI into QR Code. + Помилка при кодуванні URI в QR-код. + + + + The entered amount is invalid, please check. + Невірно введено кількість, будь ласка, перевірте. + + + + Resulting URI too long, try to reduce the text for label / message. + Кінцевий URI занадто довгий, спробуйте зменшити текст для мітки / повідомлення. + + + + Save QR Code + Зберегти QR-код + + + + PNG Images (*.png) + PNG-зображення (*.png) + + + + RPCConsole + + + Client name + Назва клієнту + + + + + + + + + + + + + N/A + Н/Д + + + + Client version + Версія клієнту + + + + &Information + &Інформація + + + + Using OpenSSL version + Використовується OpenSSL версії + + + + Startup time + + + + + Network + Мережа + + + + Number of connections + Кількість підключень + + + + On testnet + В тестовій мережі + + + + Block chain + + + + + Current number of blocks + Поточне число блоків + + + + Estimated total blocks + + + + + Last block time + + + + + &Open + Відкрити + + + + Command-line options + Параметри командного рядка + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + Показати довідку CryptoLottoToken-Qt для отримання переліку можливих параметрів командного рядка. + + + + &Show + Показати + + + + &Console + Консоль + + + + Build date + Дата збирання + + + + CryptoLottoToken - Debug window + CryptoLottoToken - Вікно зневадження + + + + CryptoLottoToken Core + + + + + Debug log file + Файл звіту зневадження + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + + + + + Clear console + Очистити консоль + + + + Welcome to the CryptoLottoToken RPC console. + Вітаємо у консолі CryptoLottoToken RPC. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + Використовуйте стрілки вгору вниз для навігації по історії, і <b>Ctrl-L</b> для очищення екрана. + + + + Type <b>help</b> for an overview of available commands. + Наберіть <b>help</b> для перегляду доступних команд. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + Відправити + + + + Send to multiple recipients at once + Відправити на декілька адрес + + + + Add &Recipient + Дод&ати одержувача + + + + Remove all transaction fields + Видалити всі поля транзакції + + + + Clear &All + Очистити &все + + + + Balance: + Баланс: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + Підтвердити відправлення + + + + S&end + &Відправити + + + + <b>%1</b> to %2 (%3) + <b>%1</b> адресату %2 (%3) + + + + Confirm send coins + Підтвердіть відправлення + + + + Are you sure you want to send %1? + Ви впевнені що хочете відправити %1? + + + + and + і + + + + The recipient address is not valid, please recheck. + Адреса отримувача невірна, будь ласка перепровірте. + + + + The amount to pay must be larger than 0. + Кількість монет для відправлення повинна бути більшою 0. + + + + The amount exceeds your balance. + Кількість монет для відправлення перевищує ваш баланс. + + + + The total exceeds your balance when the %1 transaction fee is included. + Сума перевищить ваш баланс, якщо комісія %1 буде додана до вашої транзакції. + + + + Duplicate address found, can only send to each address once per send operation. + Знайдено адресу що дублюється. Відправлення на кожну адресу дозволяється лише один раз на кожну операцію переказу. + + + + Error: Transaction creation failed! + Помилка: Не вдалося створити транзакцію! + + + + 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. + Помилка: транзакцію було відхилено. Це може статись, якщо декілька монет з вашого гаманця вже використані, наприклад, якщо ви використовуєте одну копію гаманця (wallet.dat), а монети були використані з іншої копії, але не позначені як використані в цій. + + + + SendCoinsEntry + + + Form + Форма + + + + A&mount: + &Кількість: + + + + Pay &To: + &Отримувач: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + + Enter a label for this address to add it to your address book + Введіть мітку для цієї адреси для додавання її в адресну книгу + + + + &Label: + &Мітка: + + + + Choose address from address book + Вибрати адресу з адресної книги + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Вставити адресу + + + + Alt+P + Alt+P + + + + Remove this recipient + Видалити цього отримувача + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Введіть адресу CryptoLottoToken (наприклад Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + Підписи - Підпис / Перевірка повідомлення + + + + &Sign Message + &Підписати повідомлення + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Введіть адресу CryptoLottoToken (наприклад Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + Вибрати адресу з адресної книги + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + Вставити адресу + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + Введіть повідомлення, яке ви хочете підписати тут + + + + Signature + Підпис + + + + Copy the current signature to the system clipboard + Копіювати поточну сигнатуру до системного буферу обміну + + + + Sign the message to prove you own this CryptoLottoToken address + Підпишіть повідомлення щоб довести, що ви є власником цієї адреси + + + + Sign &Message + &Підписати повідомлення + + + + Reset all sign message fields + Скинути всі поля підпису повідомлення + + + + + Clear &All + Очистити &все + + + + &Verify Message + Перевірити повідомлення + + + + Enter the signing 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. + + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Введіть адресу CryptoLottoToken (наприклад Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + Перевірте повідомлення для впевненості, що воно підписано вказаною CryptoLottoToken-адресою + + + + Verify &Message + Перевірити повідомлення + + + + Reset all verify message fields + Скинути всі поля перевірки повідомлення + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + Введіть адресу CryptoLottoToken (наприклад Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + Натисніть кнопку «Підписати повідомлення», для отримання підпису + + + + Enter CryptoLottoToken signature + Введіть сигнатуру CryptoLottoToken + + + + + The entered address is invalid. + Введена нечинна адреса. + + + + + + + Please check the address and try again. + Будь ласка, перевірте адресу та спробуйте ще. + + + + + The entered address does not refer to a key. + + + + + Wallet unlock was cancelled. + + + + + Private key for the entered address is not available. + + + + + Message signing failed. + Не вдалося підписати повідомлення. + + + + Message signed. + Повідомлення підписано. + + + + The signature could not be decoded. + Підпис не можливо декодувати. + + + + + Please check the signature and try again. + Будь ласка, перевірте підпис та спробуйте ще. + + + + The signature did not match the message digest. + + + + + Message verification failed. + Не вдалося перевірити повідомлення. + + + + Message verified. + Повідомлення перевірено. + + + + SplashScreen + + + The CryptoLottoToken developers + + + + + [testnet] + [тестова мережа] + + + + TransactionDesc + + + Open until %1 + Відкрити до %1 + + + + %1/offline + %1/поза інтернетом + + + + %1/unconfirmed + %1/не підтверджено + + + + %1 confirmations + %1 підтверджень + + + + Status + Статус + + + + , broadcast through %n node(s) + + + + + Date + Дата + + + + Source + + + + + Generated + Згенеровано + + + + + From + Відправник + + + + + + To + Отримувач + + + + + own address + + + + + label + Мітка + + + + + + + + Credit + Кредит + + + + matures in %n more block(s) + + + + + not accepted + не прийнято + + + + + + + Debit + Дебет + + + + Transaction fee + Комісія за транзакцію + + + + Net amount + Загальна сума + + + + Message + Повідомлення + + + + Comment + Коментар + + + + Transaction ID + ID транзакції + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + Після генерації монет, потрібно зачекати 120 блоків, перш ніж їх можна буде використати. Коли ви згенерували цей блок, його було відправлено в мережу для того, щоб він був доданий до ланцюжка блоків. Якщо ця процедура не вдасться, статус буде змінено на «не підтверджено» і ви не зможете потратити згенеровані монету. Таке може статись, якщо хтось інший згенерував блок на декілька секунд раніше. + + + + Debug information + + + + + Transaction + Транзакція + + + + Inputs + + + + + Amount + Кількість + + + + true + true + + + + false + false + + + + , has not been successfully broadcast yet + , ще не було успішно розіслано + + + + Open for %n more block(s) + + + + + unknown + невідомий + + + + TransactionDescDialog + + + Transaction details + Деталі транзакції + + + + This pane shows a detailed description of the transaction + Даний діалог показує детальну статистику по вибраній транзакції + + + + TransactionTableModel + + + Date + Дата + + + + Type + Тип + + + + Address + Адреса + + + + Amount + Кількість + + + + Open for %n more block(s) + + + + + Open until %1 + Відкрити до %1 + + + + Offline (%1 confirmations) + Поза інтернетом (%1 підтверджень) + + + + Unconfirmed (%1 of %2 confirmations) + Непідтверджено (%1 із %2 підтверджень) + + + + Confirmed (%1 confirmations) + Підтверджено (%1 підтверджень) + + + + Mined balance will be available when it matures in %n more block(s) + + + + + This block was not received by any other nodes and will probably not be accepted! + Цей блок не був отриманий жодними іншими вузлами і, ймовірно, не буде прийнятий! + + + + Generated but not accepted + Згенеровано, але не підтверджено + + + + Received with + Отримано + + + + Received from + Отримано від + + + + Sent to + Відправлено + + + + Payment to yourself + Відправлено собі + + + + Mined + Добуто + + + + (n/a) + (недоступно) + + + + Transaction status. Hover over this field to show number of confirmations. + Статус транзакції. Наведіть вказівник на це поле, щоб показати кількість підтверджень. + + + + Date and time that the transaction was received. + Дата і час, коли транзакцію було отримано. + + + + Type of transaction. + Тип транзакції. + + + + Destination address of transaction. + Адреса отримувача транзакції. + + + + Amount removed from or added to balance. + Сума, додана чи знята з балансу. + + + + TransactionView + + + + All + Всі + + + + Today + Сьогодні + + + + This week + На цьому тижні + + + + This month + На цьому місяці + + + + Last month + Минулого місяця + + + + This year + Цього року + + + + Range... + Проміжок... + + + + Received with + Отримані на + + + + Sent to + Відправлені на + + + + To yourself + Відправлені собі + + + + Mined + Добуті + + + + Other + Інше + + + + Enter address or label to search + Введіть адресу чи мітку для пошуку + + + + Min amount + Мінімальна сума + + + + Copy address + Скопіювати адресу + + + + Copy label + Скопіювати мітку + + + + Copy amount + Копіювати кількість + + + + Copy transaction ID + + + + + Edit label + Редагувати мітку + + + + Show transaction details + Показати деталі транзакції + + + + Export Transaction Data + Експортувати дані транзакцій + + + + Comma separated file (*.csv) + Файли, розділені комою (*.csv) + + + + Confirmed + Підтверджені + + + + Date + Дата + + + + Type + Тип + + + + Label + Мітка + + + + Address + Адреса + + + + Amount + Кількість + + + + ID + Ідентифікатор + + + + Error exporting + Помилка експорту + + + + Could not write to file %1. + Неможливо записати у файл %1. + + + + Range: + Діапазон від: + + + + to + до + + + + WalletModel + + + Send Coins + Відправити + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + Експортувати дані з поточної вкладки в файл + + + + Backup Wallet + + + + + Wallet Data (*.dat) + + + + + Backup Failed + + + + + There was an error trying to save the wallet data to the new location. + Виникла помилка при спробі зберегти гаманець в новому місці. + + + + Backup Successful + Успішне створення резервної копії + + + + The wallet data was successfully saved to the new location. + Данні гаманця успішно збережено в новому місці призначення. + + + + bitcoin-core + + + CryptoLottoToken version + Версія + + + + Usage: + Використання: + + + + Send command to -server or CryptoLottoTokend + Відправити команду серверу -server чи демону + + + + List commands + Список команд + + + + Get help for a command + Отримати довідку по команді + + + + Options: + Параметри: + + + + Specify configuration file (default: CryptoLottoToken.conf) + Вкажіть файл конфігурації (типово: CryptoLottoToken.conf) + + + + Specify pid file (default: CryptoLottoTokend.pid) + Вкажіть pid-файл (типово: CryptoLottoTokend.pid) + + + + Specify data directory + Вкажіть робочий каталог + + + + Set database cache size in megabytes (default: 25) + Встановити розмір кешу бази даних в мегабайтах (типово: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + Чекати на з'єднання на <port> (типово: 22556 або тестова мережа: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + Підтримувати не більше <n> зв'язків з колегами (типово: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + + + + + Specify your own public address + + + + + Threshold for disconnecting misbehaving peers (default: 100) + Поріг відключення неправильно під'єднаних пірів (типово: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Максимальній розмір вхідного буферу на одне з'єднання (типово: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + Прослуховувати <port> для JSON-RPC-з'єднань (типово: 22555 або тестова мережа: 44555) + + + + Accept command line and JSON-RPC commands + Приймати команди із командного рядка та команди JSON-RPC + + + + Run in the background as a daemon and accept commands + Запустити в фоновому режимі (як демон) та приймати команди + + + + Use the test network + Використовувати тестову мережу + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + + + + + 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. + Помилка: транзакцію було відхилено. Це може статись, якщо декілька монет з вашого гаманця вже використані, наприклад, якщо ви використовуєте одну копію гаманця (wallet.dat), а монети були використані з іншої копії, але не позначені як використані в цій. + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + Увага: встановлено занадто велику комісію (-paytxfee). Комісія зніматиметься кожен раз коли ви проводитимете транзакції. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + Увага: будь ласка, перевірте дату і час на своєму комп'ютері. Якщо ваш годинник йде неправильно, CryptoLottoToken може працювати некоректно. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Увага: помилка читання wallet.dat! Всі ключі прочитано коректно, але дані транзакцій чи записи адресної книги можуть бути пропущені, або пошкоджені. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + Увага: файл wallet.dat пошкоджено, дані врятовано! Оригінальний wallet.dat збережено як wallet.{timestamp}.bak до %s; якщо Ваш баланс чи транзакції неправильні, Ви можете відновити їх з резервної копії. + + + + Attempt to recover private keys from a corrupt wallet.dat + Спроба відновити закриті ключі з пошкодженого wallet.dat + + + + Block creation options: + + + + + Connect only to the specified node(s) + Підключитись лише до вказаного вузла + + + + Corrupted block database detected + + + + + Discover own IP address (default: 1 when listening and no -externalip) + + + + + Do you want to rebuild the block database now? + + + + + Error initializing block database + Помилка ініціалізації бази даних блоків + + + + Error initializing wallet database environment %s! + + + + + Error loading block database + Помилка завантаження бази даних блоків + + + + Error opening block database + + + + + Error: Disk space is low! + Помилка: Мало вільного місця на диску! + + + + Error: Wallet locked, unable to create transaction! + Помилка: Гаманець заблокований, неможливо створити транзакцію! + + + + Error: system error: + Помилка: системна помилка: + + + + Failed to listen on any port. Use -listen=0 if you want this. + + + + + Failed to read block info + + + + + Failed to read block + + + + + Failed to sync block index + + + + + Failed to write block index + + + + + Failed to write block info + + + + + Failed to write block + + + + + Failed to write file info + + + + + Failed to write to coin database + + + + + Failed to write transaction index + + + + + Failed to write undo data + + + + + Find peers using DNS lookup (default: 1 unless -connect) + + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + Скільки блоків перевіряти під час запуску (типово: 288, 0 = всі) + + + + How thorough the block verification is (0-4, default: 3) + + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + + + + + Set the number of threads to service RPC calls (default: 4) + + + + + Verifying blocks... + + + + + Verifying wallet... + + + + + Imports blocks from external blk000??.dat file + Імпорт блоків з зовнішнього файлу blk000??.dat + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + Інформація + + + + Invalid -tor address: '%s' + Помилка в адресі -tor: «%s» + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + Максимальний буфер, <n>*1000 байт (типово: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + Максимальній розмір вихідного буферу на одне з'єднання, <n>*1000 байт (типово: 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + + + + + Output extra debugging information. Implies all other -debug* options + Виводити більше налагоджувальної інформації. Мається на увазі всі шнші -debug* параметри + + + + Output extra network debugging information + + + + + Prepend debug output with timestamp (default: 1) + Доповнювати налагоджувальний вивід відміткою часу + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + Параметри SSL: (див. CryptoLottoToken Wiki для налаштування SSL) + + + + Select the version of socks proxy to use (4-5, default: 5) + Вибір версії socks-проксі для використання (4-5, типово: 5) + + + + Send trace/debug info to console instead of debug.log file + Відсилати налагоджувальну інформацію на консоль, а не у файл debug.log + + + + Send trace/debug info to debugger + Відсилати налагоджувальну інформацію до налагоджувача + + + + Set maximum block size in bytes (default: 250000) + Встановити максимальний розмір блоку у байтах (типово: 250000) + + + + Set minimum block size in bytes (default: 0) + Встановити мінімальний розмір блоку у байтах (типово: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + Стискати файл debug.log під час старту клієнта (типово: 1 коли відсутутній параметр -debug) + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + Вказати тайм-аут підключення у мілісекундах (типово: 5000) + + + + System error: + Системна помилка: + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + Намагатись використовувати UPnP для відображення порту, що прослуховується на роутері (default: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + Намагатись використовувати UPnP для відображення порту, що прослуховується на роутері (default: 1 when listening) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + + + + + Username for JSON-RPC connections + Ім'я користувача для JSON-RPC-з'єднань + + + + Warning + Попередження + + + + Warning: This version is obsolete, upgrade required! + Увага: Поточна версія застаріла, необхідне оновлення! + + + + You need to rebuild the databases using -reindex to change -txindex + + + + + wallet.dat corrupt, salvage failed + wallet.dat пошкоджено, відновлення не вдалося + + + + Password for JSON-RPC connections + Пароль для JSON-RPC-з'єднань + + + + Allow JSON-RPC connections from specified IP address + Дозволити JSON-RPC-з'єднання з вказаної IP-адреси + + + + Send commands to node running on <ip> (default: 127.0.0.1) + Відправляти команди на вузол, запущений на <ip> (типово: 127.0.0.1) + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + + + + + Upgrade wallet to latest format + Модернізувати гаманець до останнього формату + + + + Set key pool size to <n> (default: 100) + Встановити розмір пулу ключів <n> (типово: 100) + + + + Rescan the block chain for missing wallet transactions + Пересканувати ланцюжок блоків, в пошуку втрачених транзакцій + + + + Use OpenSSL (https) for JSON-RPC connections + Використовувати OpenSSL (https) для JSON-RPC-з'єднань + + + + Server certificate file (default: server.cert) + Файл сертифіката сервера (типово: server.cert) + + + + Server private key (default: server.pem) + Закритий ключ сервера (типово: server.pem) + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + Допустимі шифри (типово: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + This help message + Дана довідка + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + Неможливо прив'язати до порту %s на цьому комп'ютері (bind returned error %d, %s) + + + + Connect through socks proxy + Підключитись через SOCKS-проксі + + + + Allow DNS lookups for -addnode, -seednode and -connect + Дозволити пошук в DNS для команд -addnode, -seednode та -connect + + + + Loading addresses... + Завантаження адрес... + + + + Error loading wallet.dat: Wallet corrupted + Помилка при завантаженні wallet.dat: Гаманець пошкоджено + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + Помилка при завантаженні wallet.dat: Гаманець потребує новішої версії Біткоін-клієнта + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + Потрібно перезаписати гаманець: перезапустіть Біткоін-клієнт для завершення + + + + Error loading wallet.dat + Помилка при завантаженні wallet.dat + + + + Invalid -proxy address: '%s' + Помилка в адресі проксі-сервера: «%s» + + + + Unknown network specified in -onlynet: '%s' + Невідома мережа вказана в -onlynet: «%s» + + + + Unknown -socks proxy version requested: %i + + + + + Cannot resolve -bind address: '%s' + + + + + Cannot resolve -externalip address: '%s' + + + + + Invalid amount for -paytxfee=<amount>: '%s' + Помилка у величині комісії -paytxfee=<amount>: «%s» + + + + Invalid amount + Некоректна кількість + + + + Insufficient funds + Недостатньо коштів + + + + Loading block index... + Завантаження індексу блоків... + + + + Add a node to connect to and attempt to keep the connection open + Додати вузол до підключення і лишити його відкритим + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + Неможливо прив'язати до порту %s на цьому комп'ютері. Можливо гаманець вже запущено. + + + + Fee per KB to add to transactions you send + Комісія за КБ + + + + Loading wallet... + Завантаження гаманця... + + + + Cannot downgrade wallet + + + + + Cannot write default address + Неможливо записати типову адресу + + + + Rescanning... + Сканування... + + + + Done loading + Завантаження завершене + + + + To use the %s option + + + + + Error + Помилка + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + Ви мусите встановити rpcpassword=<password> в файлі конфігурації: +%s +Якщо файл не існує, створіть його із правами тільки для читання власником (owner-readable-only). + + + \ No newline at end of file diff --git a/src/qt/locale/bitcoin_zh_CN.qm b/src/qt/locale/bitcoin_zh_CN.qm new file mode 100644 index 0000000..620cfd5 Binary files /dev/null and b/src/qt/locale/bitcoin_zh_CN.qm differ diff --git a/src/qt/locale/bitcoin_zh_CN.ts b/src/qt/locale/bitcoin_zh_CN.ts new file mode 100644 index 0000000..783b1c8 --- /dev/null +++ b/src/qt/locale/bitcoin_zh_CN.ts @@ -0,0 +1,2960 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + 关于叶子币 + + + + <b>CryptoLottoToken</b> version + <b>叶子币</b>版本 + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + + + + Copyright + 版权 + + + + The CryptoLottoToken developers + CryptoLottoToken-qt 客户端开发团队 + + + + AddressBookPage + + + Address Book + 通讯录 + + + + Double-click to edit address or label + 双击以编辑地址或标签 + + + + Create a new address + 创建新地址 + + + + Copy the currently selected address to the system clipboard + 复制当前选中地址到系统剪贴板 + + + + &New Address + &新建地址 + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + 这是您用来收款的叶子币地址。为了标记不同的资金来源,建议为每个付款人保留不同的收款地址。 + + + + &Copy Address + &复制地址 + + + + Show &QR Code + 显示二维码 + + + + Sign a message to prove you own a CryptoLottoToken address + 签名消息,证明这个地址属于您。 + + + + Sign &Message + 对消息签名 + + + + Delete the currently selected address from the list + 从列表中删除选中的地址 + + + + Export the data in the current tab to a file + 导出当前数据到文件 + + + + &Export + &导出 + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + 验证消息,确保消息是由指定的叶子币地址签名过的。 + + + + &Verify Message + &验证消息 + + + + &Delete + &删除 + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + 这是您用来付款的叶子币地址。在付款前,请总是核实付款金额和收款地址。 + + + + Copy &Label + 复制 &标签 + + + + &Edit + &编辑 + + + + Send &Coins + 付款 + + + + Export Address Book Data + 导出通讯录数据 + + + + Comma separated file (*.csv) + 逗号分隔文件 (*.csv) + + + + Error exporting + 导出错误 + + + + Could not write to file %1. + 无法写入文件 %1。 + + + + AddressTableModel + + + Label + 标签 + + + + Address + 地址 + + + + (no label) + (没有标签) + + + + AskPassphraseDialog + + + Passphrase Dialog + 密码对话框 + + + + Enter passphrase + 输入密码 + + + + New passphrase + 新密码 + + + + Repeat new passphrase + 重复新密码 + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + 输入钱包的新密码。<br/>使用的密码请至少包含<b>10个以上随机字符</>,或者是<b>8个以上的单词</b>。 + + + + Encrypt wallet + 加密钱包 + + + + This operation needs your wallet passphrase to unlock the wallet. + 该操作需要您首先使用密码解锁钱包。 + + + + Unlock wallet + 解锁钱包 + + + + This operation needs your wallet passphrase to decrypt the wallet. + 该操作需要您首先使用密码解密钱包。 + + + + Decrypt wallet + 解密钱包 + + + + Change passphrase + 修改密码 + + + + Enter the old and new passphrase to the wallet. + 请输入钱包的旧密码与新密码。 + + + + Confirm wallet encryption + 确认加密钱包 + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR CryptoLottoTokenS</b>! + 警告:如果您加密了您的钱包,但是忘记了密码,你将会<b>丢失所有的叶子币</b>! + + + + Are you sure you wish to encrypt your wallet? + 您确定需要为钱包加密吗? + + + + 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. + 重要提示:您以前备份的钱包文件应该替换成最新生成的加密钱包文件(重新备份)。从安全性上考虑,您以前备份的未加密的钱包文件,在您使用新的加密钱包后将无效,请重新备份。 + + + + + Warning: The Caps Lock key is on! + 警告:大写锁定键处于打开状态! + + + + + Wallet encrypted + 钱包已加密 + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + 将关闭软件以完成加密过程。 请您谨记:钱包加密并不是万能的,电脑中毒,您的叶子币还是有可能丢失。 + + + + + + + Wallet encryption failed + 钱包加密失败 + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + 由于一个本地错误,加密钱包操作已经失败。您的钱包没有被加密。 + + + + + The supplied passphrases do not match. + 密码不匹配。 + + + + Wallet unlock failed + 钱包解锁失败 + + + + + + The passphrase entered for the wallet decryption was incorrect. + 用于解密钱包的密码不正确。 + + + + Wallet decryption failed + 钱包解密失败。 + + + + Wallet passphrase was successfully changed. + 修改钱包密码成功。 + + + + BitcoinGUI + + + Sign &message... + 对&消息签名... + + + + Synchronizing with network... + 正在与网络同步... + + + + &Overview + &概况 + + + + Show general overview of wallet + 显示钱包概况 + + + + &Transactions + &交易记录 + + + + Browse transaction history + 查看交易历史 + + + + Edit the list of stored addresses and labels for sending + 修改存储的地址和标签列表 + + + + Show the list of addresses for receiving payments + 显示接收支付的地址列表 + + + + E&xit + 退出 + + + + Quit application + 退出程序 + + + + Show information about CryptoLottoToken + 显示叶子币的相关信息 + + + + About &Qt + 关于 &Qt + + + + Show information about Qt + 显示Qt相关信息 + + + + &Options... + &选项... + + + + &Encrypt Wallet... + &加密钱包... + + + + &Backup Wallet... + &备份钱包... + + + + &Change Passphrase... + &修改密码... + + + + Importing blocks from disk... + 正在从磁盘导入数据块... + + + + Reindexing blocks on disk... + 正在为数据块建立索引... + + + + Send coins to a CryptoLottoToken address + 向一个叶子币地址发送叶子币 + + + + Modify configuration options for CryptoLottoToken + 设置选项 + + + + Backup wallet to another location + 备份钱包到其它文件夹 + + + + Change the passphrase used for wallet encryption + 修改钱包加密口令 + + + + &Debug window + &调试窗口 + + + + Open debugging and diagnostic console + 在诊断控制台调试 + + + + &Verify message... + &验证消息... + + + + + CryptoLottoToken + 叶子币 + + + + Wallet + 钱包 + + + + &Send + &发送 + + + + &Receive + &接收 + + + + &Addresses + &地址 + + + + &About CryptoLottoToken + &关于叶子币 + + + + &Show / Hide + &显示 / 隐藏 + + + + Show or hide the main Window + 显示或隐藏主窗口 + + + + Encrypt the private keys that belong to your wallet + 对钱包中的私钥加密 + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + 用叶子币地址关联的私钥为消息签名,以证明您拥有这个叶子币地址 + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + 校验消息,确保该消息是由指定的叶子币地址所有者签名的 + + + + &File + &文件 + + + + &Settings + &设置 + + + + &Help + &帮助 + + + + Tabs toolbar + 分页工具栏 + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + 叶子币客户端 + + + + %n active connection(s) to CryptoLottoToken network + 到叶子币网络的连接共有%n条 + + + + No block source available... + No block source available... + + + + Processed %1 of %2 (estimated) blocks of transaction history. + %1 / %2 个交易历史的区块已下载 + + + + Processed %1 blocks of transaction history. + 已处理 %1 个交易历史数据块。 + + + + %n hour(s) + %n 小时前 + + + + %n day(s) + %n 天前 + + + + %n week(s) + %n 周前 + + + + %1 behind + 落后 %1 + + + + Last received block was generated %1 ago. + 最新收到的区块产生于 %1。 + + + + Transactions after this will not yet be visible. + 在此之后的交易尚未可见 + + + + Error + 错误 + + + + Warning + 警告 + + + + Information + 信息 + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + 该交易的字节数超标。您可以选择支付%1的交易费给处理您的交易的网络节点,有助于叶子币网络的运行。您愿意支付这笔交易费用吗? + + + + Up to date + 最新状态 + + + + Catching up... + 更新中... + + + + Confirm transaction fee + 确认交易费 + + + + Sent transaction + 已发送交易 + + + + Incoming transaction + 流入交易 + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + 日期: %1 +金额: %2 +类别: %3 +地址: %4 + + + + + + URI handling + URI 处理 + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + URI无法解析!原因可能是叶子币地址不正确,或者URI参数错误。 + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + 钱包已被<b>加密</b>,当前为<b>解锁</b>状态 + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + 钱包已被<b>加密</b>,当前为<b>锁定</b>状态 + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + 发生严重错误。 + + + + ClientModel + + + Network Alert + 网络警报 + + + + EditAddressDialog + + + Edit Address + 编辑地址 + + + + &Label + &标签 + + + + The label associated with this address book entry + 与此地址条目关联的标签 + + + + &Address + &地址 + + + + The address associated with this address book entry. This can only be modified for sending addresses. + 该地址与地址簿中的条目已关联,无法作为发送地址编辑。 + + + + New receiving address + 新接收地址 + + + + New sending address + 新发送地址 + + + + Edit receiving address + 编辑接收地址 + + + + Edit sending address + 编辑发送地址 + + + + The entered address "%1" is already in the address book. + 输入的地址 "%1" 已经存在于地址簿。 + + + + The entered address "%1" is not a valid CryptoLottoToken address. + 您输入的 "%1" 不是合法的叶子币地址. + + + + Could not unlock wallet. + 无法解锁钱包 + + + + New key generation failed. + 密钥创建失败. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + CryptoLottoToken-Qt + + + + version + 版本 + + + + Usage: + 使用: + + + + command-line options + 命令行选项 + + + + UI options + UI选项 + + + + Set language, for example "de_DE" (default: system locale) + 设置语言, 例如 "de_DE" (缺省: 系统语言) + + + + Start minimized + 启动时最小化 + + + + + Show splash screen on startup (default: 1) + 启动时显示版权页 (缺省: 1) + + + + OptionsDialog + + + Options + 选项 + + + + &Main + &主要的 + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + + + + + Pay transaction &fee + 支付交易 &费用 + + + + Automatically start CryptoLottoToken after logging in to the system. + 登录系统后自动开启叶子币客户端 + + + + &Start CryptoLottoToken on system login + 启动时&运行 + + + + Reset all client options to default. + 恢复客户端的缺省设置 + + + + &Reset Options + 恢复缺省设置 + + + + &Network + &网络 + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + 自动在路由器中打开叶子币端口。只有当您的路由器开启 UPnP 选项时此功能才有效。 + + + + Map port using &UPnP + 使用 &UPnP 映射端口 + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + 通过代理服务器连接叶子币网络(例如:通过Tor连接) + + + + &Connect through SOCKS proxy: + &通过Socks代理连接: + + + + Proxy &IP: + 代理服务器&IP: + + + + IP address of the proxy (e.g. 127.0.0.1) + 代理服务器IP (如 127.0.0.1) + + + + &Port: + &端口: + + + + Port of the proxy (e.g. 9050) + 代理端口(例如 9050) + + + + SOCKS &Version: + Socks &版本 + + + + SOCKS version of the proxy (e.g. 5) + Socks代理版本 (例如 5) + + + + &Window + &窗口 + + + + Show only a tray icon after minimizing the window. + 最小化窗口后仅显示托盘图标 + + + + &Minimize to the tray instead of the taskbar + &最小化到托盘 + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + 当窗口关闭时程序最小化而不是退出。当使用该选项时,程序只能通过在菜单中选择退出来关闭 + + + + M&inimize on close + 单击关闭按钮最小化 + + + + &Display + &显示 + + + + User Interface &language: + 用户界面&语言: + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + 在这里设置用户界面的语言。设置将在客户端重启后生效。 + + + + &Unit to show amounts in: + &叶子币金额单位: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + 选择叶子币单位。 + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + 是否需要在交易清单中显示叶子币地址。 + + + + &Display addresses in transaction list + 在交易清单中&显示叶子币地址 + + + + &OK + &确定 + + + + &Cancel + &取消 + + + + &Apply + &应用 + + + + default + 缺省 + + + + Confirm options reset + 确认恢复缺省设置 + + + + Some settings may require a client restart to take effect. + 某些设置选项需要重启客户端才能生效 + + + + Do you want to proceed? + 您希望继续吗? + + + + + Warning + 警告 + + + + + This setting will take effect after restarting CryptoLottoToken. + 需要重启客户端软件才能生效。 + + + + The supplied proxy address is invalid. + 提供的代理服务器地址无效。 + + + + OverviewPage + + + Form + 表单 + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + 现在显示的消息可能是过期的. 在连接上叶子币网络节点后,您的钱包将自动与网络同步,但是这个过程还没有完成. + + + + Balance: + 余额: + + + + Unconfirmed: + 未确认: + + + + Wallet + 钱包 + + + + Immature: + 未成熟的: + + + + Mined balance that has not yet matured + 尚未成熟的挖矿收入余额 + + + + <b>Recent transactions</b> + <b>最近交易记录</b> + + + + Your current balance + 您的当前余额 + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + 尚未确认的交易总额, 未计入当前余额 + + + + + out of sync + 数据同步中 + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + 暂时无法启动叶子币:点击支付功能 + + + + QRCodeDialog + + + QR Code Dialog + 二维码对话框 + + + + Request Payment + 请求付款 + + + + Amount: + 金额: + + + + Label: + 标签: + + + + Message: + 消息: + + + + &Save As... + &另存为 + + + + Error encoding URI into QR Code. + 将 URI 转换成二维码失败. + + + + The entered amount is invalid, please check. + 输入的金额非法,请检查。 + + + + Resulting URI too long, try to reduce the text for label / message. + URI 太长, 请试着精简标签/消息的内容. + + + + Save QR Code + 保存二维码 + + + + PNG Images (*.png) + PNG图像文件(*.png) + + + + RPCConsole + + + Client name + 客户端名称 + + + + + + + + + + + + + N/A + 不可用 + + + + Client version + 客户端版本 + + + + &Information + &信息 + + + + Using OpenSSL version + 使用OpenSSL版本 + + + + Startup time + 启动时间 + + + + Network + 网络 + + + + Number of connections + 连接数 + + + + On testnet + 当前为叶子币测试网络 + + + + Block chain + 数据链 + + + + Current number of blocks + 当前数据块数量 + + + + Estimated total blocks + 预计数据块数量 + + + + Last block time + 上一数据块时间 + + + + &Open + &打开 + + + + Command-line options + 命令行选项 + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + 显示CryptoLottoToken命令行选项帮助信息 + + + + &Show + &显示 + + + + &Console + &控制台 + + + + Build date + 创建时间 + + + + CryptoLottoToken - Debug window + 叶子币 - 调试窗口 + + + + CryptoLottoToken Core + 叶子币核心 + + + + Debug log file + 调试日志文件 + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + 打开当前目录中的调试日志文件。日志文件大的话可能要等上几秒钟。 + + + + Clear console + 清空控制台 + + + + Welcome to the CryptoLottoToken RPC console. + 欢迎来到 RPC 控制台. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + 使用上下方向键浏览历史, <b>Ctrl-L</b>清除屏幕. + + + + Type <b>help</b> for an overview of available commands. + 使用 <b>help</b> 命令显示帮助信息. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + 发送货币 + + + + Send to multiple recipients at once + 一次发送给多个接收者 + + + + Add &Recipient + 添加收款人 + + + + Remove all transaction fields + 移除所有交易项 + + + + Clear &All + 清除 &所有 + + + + Balance: + 余额: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + 确认并发送货币 + + + + S&end + 发送 + + + + <b>%1</b> to %2 (%3) + <b>%1</b> 到 %2 (%3) + + + + Confirm send coins + 确认发送货币 + + + + Are you sure you want to send %1? + 确定您要发送 %1? + + + + and + + + + + The recipient address is not valid, please recheck. + 收款人地址不合法,请检查。 + + + + The amount to pay must be larger than 0. + 支付金额必须大于0. + + + + The amount exceeds your balance. + 金额超出您的账上余额。 + + + + The total exceeds your balance when the %1 transaction fee is included. + 计入 %1 交易费后的金额超出您的账上余额。 + + + + Duplicate address found, can only send to each address once per send operation. + 发现重复的地址, 每次只能对同一地址发送一次. + + + + Error: Transaction creation failed! + 错误:创建交易失败! + + + + 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. + 错误: 交易被拒绝. 如果您使用的是备份钱包,可能存在两个钱包不同步的情况,另一个钱包中的叶子币已经被使用,但本地的这个钱包尚没有记录。 + + + + SendCoinsEntry + + + Form + 表单 + + + + A&mount: + 金额 + + + + Pay &To: + 付款&给: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + 付款给这个地址 (例如 Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + 为这个地址输入一个标签,以便将它添加到您的地址簿 + + + + &Label: + &标签: + + + + Choose address from address book + 从地址簿选择地址 + + + + Alt+A + Alt+A + + + + Paste address from clipboard + 从剪贴板粘贴地址 + + + + Alt+P + Alt+P + + + + Remove this recipient + 移除此接收者 + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + 请输入叶子币地址 (例如: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + 签名 - 为消息签名/验证签名消息 + + + + &Sign Message + &签名消息 + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + 您可以用你的地址对消息进行签名,以证明您是该地址的所有人。注意不要对模棱两可的消息签名,以免遭受钓鱼式攻击。请确保消息内容准确的表达了您的真实意愿。 + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + 用于签名消息的地址(例如: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + 从地址簿选择地址 + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + 从剪贴板粘贴地址 + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + 请输入您要发送的签名消息 + + + + Signature + 签名 + + + + Copy the current signature to the system clipboard + 复制当前签名至剪切板 + + + + Sign the message to prove you own this CryptoLottoToken address + 签名消息,证明这个地址属于您。 + + + + Sign &Message + 消息签名 + + + + Reset all sign message fields + 清空所有签名消息栏 + + + + + Clear &All + 清除 &所有 + + + + &Verify Message + &验证消息 + + + + Enter the signing 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. + 在下面输入签名地址,消息(请确保换行符、空格符、制表符等等一个不漏)和签名以验证消息。请确保签名信息准确,提防中间人攻击。 + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + 用于签名消息的地址(例如: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + 验证消息,确保消息是由指定的叶子币地址签名过的。 + + + + Verify &Message + 验证消息签名 + + + + Reset all verify message fields + 清空所有验证消息栏 + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + 请输入叶子币地址 (例如: Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + 单击“签名消息“产生签名。 + + + + Enter CryptoLottoToken signature + 输入叶子币签名 + + + + + The entered address is invalid. + 输入的地址非法。 + + + + + + + Please check the address and try again. + 请检查地址后重试。 + + + + + The entered address does not refer to a key. + 输入的地址没有关联的公私钥对。 + + + + Wallet unlock was cancelled. + 钱包解锁动作取消。 + + + + Private key for the entered address is not available. + 找不到输入地址关联的私钥。 + + + + Message signing failed. + 消息签名失败。 + + + + Message signed. + 消息已签名。 + + + + The signature could not be decoded. + 签名无法解码。 + + + + + Please check the signature and try again. + 请检查签名后重试。 + + + + The signature did not match the message digest. + 签名与消息摘要不匹配。 + + + + Message verification failed. + 消息验证失败。 + + + + Message verified. + 消息验证成功。 + + + + SplashScreen + + + The CryptoLottoToken developers + CryptoLottoToken-qt 客户端开发团队 + + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + 至 %1 个数据块时开启 + + + + %1/offline + %1 / 离线 + + + + %1/unconfirmed + %1/未确认 + + + + %1 confirmations + %1 确认项 + + + + Status + 状态 + + + + , broadcast through %n node(s) + 通过 %n 个节点广播 + + + + Date + 日期 + + + + Source + + + + + Generated + 生成 + + + + + From + 来自 + + + + + + To + + + + + + own address + 自己的地址 + + + + label + 标签 + + + + + + + + Credit + 收入 + + + + matures in %n more block(s) + 将在 %n 个数据块后成熟 + + + + not accepted + 未被接受 + + + + + + + Debit + 支出 + + + + Transaction fee + 交易费 + + + + Net amount + 净额 + + + + Message + 消息 + + + + Comment + 备注 + + + + Transaction ID + 交易ID + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + 新挖出的叶子币必须等确120个确认才能使用。您生产出的数据块,将被广播到全网并添加到数据块链。如果入链失败,状态将变为“未被接受”,意味着您的数据块竞争失败,挖出的叶子币将不能使用。当某个节点先于你几秒生产出新的数据块,这种情况会偶尔发生。 + + + + Debug information + 调试信息 + + + + Transaction + 交易 + + + + Inputs + 输入 + + + + Amount + 金额 + + + + true + 正确 + + + + false + 错误 + + + + , has not been successfully broadcast yet + , 未被成功广播 + + + + Open for %n more block(s) + Open for %n more block + + + + unknown + 未知 + + + + TransactionDescDialog + + + Transaction details + 交易明细 + + + + This pane shows a detailed description of the transaction + 当前面板显示了交易的详细信息 + + + + TransactionTableModel + + + Date + 日期 + + + + Type + 类型 + + + + Address + 地址 + + + + Amount + 数量 + + + + Open for %n more block(s) + Open for %n more block + + + + Open until %1 + 至 %1 个数据块时开启 + + + + Offline (%1 confirmations) + 离线 (%1 个确认项) + + + + Unconfirmed (%1 of %2 confirmations) + 未确认 (%1 / %2 条确认信息) + + + + Confirmed (%1 confirmations) + 已确认 (%1 条确认信息) + + + + Mined balance will be available when it matures in %n more block(s) + 挖矿收入余额将在 %n 个数据块后可用 + + + + This block was not received by any other nodes and will probably not be accepted! + 此数据块未被其他节点接收,并可能不被接受! + + + + Generated but not accepted + 已生成但未被接受 + + + + Received with + 接收于 + + + + Received from + 收款来自 + + + + Sent to + 发送到 + + + + Payment to yourself + 付款给自己 + + + + Mined + 挖矿所得 + + + + (n/a) + (n/a) + + + + Transaction status. Hover over this field to show number of confirmations. + 交易状态。 鼠标移到此区域上可显示确认消息项的数目。 + + + + Date and time that the transaction was received. + 接收叶子币的时间 + + + + Type of transaction. + 交易类别。 + + + + Destination address of transaction. + 交易目的地址。 + + + + Amount removed from or added to balance. + 从余额添加或移除的金额。 + + + + TransactionView + + + + All + 全部 + + + + Today + 今天 + + + + This week + 本周 + + + + This month + 本月 + + + + Last month + 上月 + + + + This year + 今年 + + + + Range... + 范围... + + + + Received with + 接收于 + + + + Sent to + 发送到 + + + + To yourself + 到自己 + + + + Mined + 挖矿所得 + + + + Other + 其他 + + + + Enter address or label to search + 输入地址或标签进行搜索 + + + + Min amount + 最小金额 + + + + Copy address + 复制地址 + + + + Copy label + 复制标签 + + + + Copy amount + 复制金额 + + + + Copy transaction ID + 复制交易编号 + + + + Edit label + 编辑标签 + + + + Show transaction details + 显示交易详情 + + + + Export Transaction Data + 导出交易数据 + + + + Comma separated file (*.csv) + 逗号分隔文件(*.csv) + + + + Confirmed + 已确认 + + + + Date + 日期 + + + + Type + 类别 + + + + Label + 标签 + + + + Address + 地址 + + + + Amount + 金额 + + + + ID + ID + + + + Error exporting + 导出错误 + + + + Could not write to file %1. + 无法写入文件 %1。 + + + + Range: + 范围: + + + + to + + + + + WalletModel + + + Send Coins + 发送叶子币 + + + + WalletView + + + &Export + + + + + Export the data in the current tab to a file + 导出当前数据到文件 + + + + Backup Wallet + 备份钱包 + + + + Wallet Data (*.dat) + 钱包文件(*.dat) + + + + Backup Failed + 备份失败 + + + + There was an error trying to save the wallet data to the new location. + 备份钱包到其它文件夹失败. + + + + Backup Successful + 备份成功 + + + + The wallet data was successfully saved to the new location. + 钱包数据成功存储到新位置 + + + + bitcoin-core + + + CryptoLottoToken version + 叶子币版本 + + + + Usage: + 使用: + + + + Send command to -server or CryptoLottoTokend + 发送命令到服务器或者 CryptoLottoTokend + + + + + List commands + 列出命令 + + + + + Get help for a command + 获得某条命令的帮助 + + + + + Options: + 选项: + + + + + Specify configuration file (default: CryptoLottoToken.conf) + 指定配置文件 (默认为 CryptoLottoToken.conf) + + + + + Specify pid file (default: CryptoLottoTokend.pid) + 指定 pid 文件 (默认为 CryptoLottoTokend.pid) + + + + + Specify data directory + 指定数据目录 + + + + + Set database cache size in megabytes (default: 25) + 设置数据库缓冲区大小 (缺省: 25MB) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + 监听端口连接 <port> (缺省: 22556 or testnet: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + 最大连接数 <n> (缺省: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + 连接一个节点并获取对端地址, 然后断开连接 + + + + Specify your own public address + 指定您的公共地址 + + + + Threshold for disconnecting misbehaving peers (default: 100) + Threshold for disconnecting misbehaving peers (缺省: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + Number of seconds to keep misbehaving peers from reconnecting (缺省: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + 设置RPC监听端口%u时发生错误, IPv4:%s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + JSON-RPC连接监听端口<port> (缺省:22555 testnet:44555) + + + + Accept command line and JSON-RPC commands + 接受命令行和 JSON-RPC 命令 + + + + + Run in the background as a daemon and accept commands + 在后台运行并接受命令 + + + + + + Use the test network + 使用测试网络 + + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + 接受来自外部的连接 (缺省: 如果不带 -proxy or -connect 参数设置为1) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + %s, 您必须在配置文件设置rpcpassword: + %s +建议您使用下面的随机密码: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(您无需记住此密码) +用户名和密码 必! 须! 不一样。 +如果配置文件不存在,请自行建立一个只有所有者拥有只读权限的文件。 +推荐您开启提示通知以便收到错误通知, +像这样: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + 在IPv6模式下设置RPC监听端口 %u 失败,返回到IPv4模式: %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + 绑定指定的IP地址开始监听。IPv6地址请使用[host]:port 格式 + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + 无法给数据目录 %s上锁。本软件可能已经在运行。 + + + + 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. + 错误:该交易被拒绝!发生这种错误的原因可能是:钱包中的叶子币已经被用掉,有可能您复制了wallet.dat钱包文件,然后用复制的钱包文件支付了叶子币,但是这个钱包文件中没有记录。 + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + 错误:因为该交易的数量、复杂度或者动用了刚收到不久的资金,您需要支付不少于%s的交易费用。 + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + 当收到相关通知时执行命令(命令行中的 %s 的替换为消息) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + 当最佳区块变化时执行命令 (命令行中的 %s 会被替换成区块哈希值) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + 这是测试用的预发布版本 - 请谨慎使用 - 不要用来挖矿,或者在正式商用环境下使用 + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + 警告:-paytxfee 交易费设置得太高了!每笔交易都将支付交易费。 + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + 警告:显示的交易可能不正确!您需要升级客户端软件,或者网络上的其他节点需要升级。 + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + 警告:请检查电脑的日期时间设置是否正确!时间错误可能会导致叶子币客户端运行异常。 + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + 警告:钱包文件wallet.dat读取失败!最重要的公钥、私钥数据都没有问题,但是交易记录或地址簿数据不正确,或者存在数据丢失。 + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + 警告:钱包文件wallet.dat损坏! 原始的钱包文件已经备份到%s目录下并重命名为{timestamp}.bak 。如果您的账户余额或者交易记录不正确,请使用您的钱包备份文件恢复。 + + + + Attempt to recover private keys from a corrupt wallet.dat + 尝试从损坏的钱包文件wallet.dat中恢复私钥 + + + + Block creation options: + 数据块创建选项: + + + + Connect only to the specified node(s) + 仅连接到指定节点 + + + + Corrupted block database detected + 检测发现数据块数据库损坏。请使用 -reindex参数重启客户端。 + + + + Discover own IP address (default: 1 when listening and no -externalip) + 发现自己的IP地址(缺省:不带 -externalip 参数监听时设置为1) + + + + Do you want to rebuild the block database now? + 你想现在就重建块数据库吗? + + + + Error initializing block database + 初始化数据块数据库出错 + + + + Error initializing wallet database environment %s! + Error initializing wallet database environment %s! + + + + Error loading block database + 导入数据块数据库出错 + + + + Error opening block database + 导入数据块数据库出错 + + + + Error: Disk space is low! + 错误:磁盘剩余空间低! + + + + Error: Wallet locked, unable to create transaction! + 错误:钱包被锁定,无法创建交易! + + + + Error: system error: + 错误:系统出错。 + + + + Failed to listen on any port. Use -listen=0 if you want this. + 监听端口失败。请使用 -listen=0 参数。 + + + + Failed to read block info + 无法读取数据块信息 + + + + Failed to read block + 读取数据块失败 + + + + Failed to sync block index + 无法同步数据块索引 + + + + Failed to write block index + 无法写入数据块索引 + + + + Failed to write block info + 无法写入数据块信息 + + + + Failed to write block + 无法写数据块 + + + + Failed to write file info + 无法写入文件信息 + + + + Failed to write to coin database + 无法写入coin数据库 + + + + Failed to write transaction index + 无法写入交易索引 + + + + Failed to write undo data + 无法写入回滚信息 + + + + Find peers using DNS lookup (default: 1 unless -connect) + 通过DNS查找节点(缺省:1 除非使用 -connect 选项) + + + + Generate coins (default: 0) + + + + + How many blocks to check at startup (default: 288, 0 = all) + 启动时检测多少个数据块(缺省:288,0=所有) + + + + How thorough the block verification is (0-4, default: 3) + How thorough the block verification is (0-4, default: 3) + + + + Not enough file descriptors available. + + + + + Rebuild block chain index from current blk000??.dat files + 重新为当前的blk000??.dat文件建立索引 + + + + Set the number of threads to service RPC calls (default: 4) + 设置使用调用服务 RPC 的线程数量(默认:4) + + + + Verifying blocks... + 正在验证数据库的完整性... + + + + Verifying wallet... + 正在检测钱包的完整性... + + + + Imports blocks from external blk000??.dat file + 从blk000??.dat文件导入数据块 + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + + + + + Information + 信息 + + + + Invalid -tor address: '%s' + 非法的 -tor 地址:'%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + + + + + Invalid amount for -mintxfee=<amount>: '%s' + + + + + Maintain a full transaction index (default: 0) + 维护一份完整的交易索引(缺省:0) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + 每个连接的最大接收缓存,<n>*1000 字节(缺省:5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + 每个连接的最大发送缓存,<n>*1000 字节(缺省:1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + 仅接受符合客户端检查点设置的数据块文件 + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + 仅连接至指定网络的节点<net>(IPv4, IPv6 或者 Tor) + + + + Output extra debugging information. Implies all other -debug* options + 输出额外的调试信息。打开所有 -debug* 开关 + + + + Output extra network debugging information + 输出额外的网络调试信息 + + + + Prepend debug output with timestamp (default: 1) + 为调试输出信息添加时间戳 + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + SSL选项:(参见CryptoLottoToken Wiki关于SSL设置栏目) + + + + Select the version of socks proxy to use (4-5, default: 5) + 请选择Socks代理服务器版本 (4 或 5, 缺省: 5) + + + + Send trace/debug info to console instead of debug.log file + 跟踪/调试信息输出到控制台,不输出到debug.log文件 + + + + Send trace/debug info to debugger + 跟踪/调试信息输出到 调试器debugger + + + + Set maximum block size in bytes (default: 250000) + 设置最大数据块大小(缺省:250000) + + + + Set minimum block size in bytes (default: 0) + 设置最小数据块大小(缺省:0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + 客户端启动时压缩debug.log文件(缺省:no-debug模式时为1) + + + + Signing transaction failed + + + + + Specify connection timeout in milliseconds (default: 5000) + 设置连接超时时间(缺省:5000毫秒) + + + + System error: + 系统错误: + + + + Transaction amount too small + + + + + Transaction amounts must be positive + + + + + Transaction too large + + + + + Use UPnP to map the listening port (default: 0) + 使用UPnp映射监听端口(缺省: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + 使用UPnp映射监听端口(缺省: 监听状态设为1) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + 使用代理服务器访问隐藏服务(缺省:同 -proxy) + + + + Username for JSON-RPC connections + JSON-RPC连接用户名 + + + + + Warning + 警告 + + + + Warning: This version is obsolete, upgrade required! + 警告:该软件版本已过时,请升级! + + + + You need to rebuild the databases using -reindex to change -txindex + You need to rebuild the databases using -reindex to change -txindex + + + + wallet.dat corrupt, salvage failed + 钱包文件wallet.dat损坏,抢救备份失败 + + + + Password for JSON-RPC connections + JSON-RPC连接密码 + + + + + Allow JSON-RPC connections from specified IP address + 允许从指定IP接受到的JSON-RPC连接 + + + + + Send commands to node running on <ip> (default: 127.0.0.1) + 向IP地址为 <ip> 的节点发送指令 (缺省: 127.0.0.1) + + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + 当最佳数据块变化时执行命令 (命令行中的 %s 会被替换成数据块哈希值) + + + + Upgrade wallet to latest format + 将钱包升级到最新的格式 + + + + Set key pool size to <n> (default: 100) + 设置密钥池大小为 <n> (缺省: 100) + + + + + Rescan the block chain for missing wallet transactions + 重新扫描数据链以查找遗漏的交易 + + + + + Use OpenSSL (https) for JSON-RPC connections + 为 JSON-RPC 连接使用 OpenSSL (https)连接 + + + + Server certificate file (default: server.cert) + 服务器证书 (默认为 server.cert) + + + + + Server private key (default: server.pem) + 服务器私钥 (默认为 server.pem) + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + 可接受的加密器 (默认为 TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + + This help message + 该帮助信息 + + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + 无法绑定本机端口 %s (返回错误消息 %d, %s) + + + + Connect through socks proxy + 通过 socks 代理连接 + + + + Allow DNS lookups for -addnode, -seednode and -connect + 使用 -addnode, -seednode 和 -connect选项时允许DNS查找 + + + + Loading addresses... + 正在加载地址... + + + + Error loading wallet.dat: Wallet corrupted + wallet.dat钱包文件加载错误:钱包损坏 + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + wallet.dat钱包文件加载错误:请升级到最新CryptoLottoToken客户端 + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + 钱包文件需要重写:请退出并重新启动CryptoLottoToken客户端 + + + + Error loading wallet.dat + wallet.dat钱包文件加载错误 + + + + Invalid -proxy address: '%s' + 非法的代理地址: '%s' + + + + Unknown network specified in -onlynet: '%s' + 被指定的是未知网络 -onlynet: '%s' + + + + Unknown -socks proxy version requested: %i + 被指定的是未知socks代理版本: %i + + + + Cannot resolve -bind address: '%s' + 无法解析 -bind 端口地址: '%s' + + + + Cannot resolve -externalip address: '%s' + 无法解析 -externalip 地址: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + 非法金额 -paytxfee=<amount>: '%s' + + + + Invalid amount + 金额不对 + + + + Insufficient funds + 金额不足 + + + + Loading block index... + 加载数据块索引... + + + + Add a node to connect to and attempt to keep the connection open + 添加节点并与其保持连接 + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + 无法在本机绑定 %s 端口 . 叶子币客户端软件可能已经在运行. + + + + Fee per KB to add to transactions you send + 每发送1KB交易所需的费用 + + + + Loading wallet... + 正在加载钱包... + + + + Cannot downgrade wallet + 无法降级钱包格式 + + + + Cannot write default address + 无法写入缺省地址 + + + + Rescanning... + 正在重新扫描... + + + + Done loading + 加载完成 + + + + To use the %s option + 使用 %s 选项 + + + + Error + 错误 + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + 您必须在配置文件中加入选项 rpcpassword : + %s +如果配置文件不存在,请新建,并将文件权限设置为仅允许文件所有者读取. + + + diff --git a/src/qt/locale/bitcoin_zh_TW.qm b/src/qt/locale/bitcoin_zh_TW.qm new file mode 100644 index 0000000..df14883 Binary files /dev/null and b/src/qt/locale/bitcoin_zh_TW.qm differ diff --git a/src/qt/locale/bitcoin_zh_TW.ts b/src/qt/locale/bitcoin_zh_TW.ts new file mode 100644 index 0000000..6779053 --- /dev/null +++ b/src/qt/locale/bitcoin_zh_TW.ts @@ -0,0 +1,2955 @@ + +UTF-8 + + AboutDialog + + + About CryptoLottoToken + 關於葉子幣 + + + + <b>CryptoLottoToken</b> version + <b>葉子幣</b>版本 + + + + +This is experimental software. + +Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php. + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard. + +這是一套實驗性的軟體. + +此軟體是依據 MIT/X11 軟體授權條款散布, 詳情請見附帶的 COPYING 檔案, 或是以下網站: http://www.opensource.org/licenses/mit-license.php. + +此產品也包含了由 OpenSSL Project 所開發的 OpenSSL Toolkit (http://www.openssl.org/) 軟體, 由 Eric Young (eay@cryptsoft.com) 撰寫的加解密軟體, 以及由 Thomas Bernard 所撰寫的 UPnP 軟體. + + + + Copyright + 版權 + + + + The CryptoLottoToken developers + 葉子幣開發人員 + + + + AddressBookPage + + + Address Book + 位址簿 + + + + Double-click to edit address or label + 點兩下來修改位址或標記 + + + + Create a new address + 產生新位址 + + + + Copy the currently selected address to the system clipboard + 複製目前選取的位址到系統剪貼簿 + + + + &New Address + 新增位址 + + + + These are your CryptoLottoToken addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + 這些是你用來收款的葉子幣位址. 你可以提供不同的位址給不同的付款人, 來追蹤是誰支付給你. + + + + &Copy Address + 複製位址 + + + + Show &QR Code + 顯示 &QR 條碼 + + + + Sign a message to prove you own a CryptoLottoToken address + 簽署訊息是用來證明葉子幣位址是你的 + + + + Sign &Message + 訊息簽署 + + + + Delete the currently selected address from the list + 從列表中刪除目前選取的位址 + + + + Export the data in the current tab to a file + 將目前分頁的資料匯出存成檔案 + + + + &Export + 匯出 + + + + Verify a message to ensure it was signed with a specified CryptoLottoToken address + 驗證訊息是用來確認訊息是用指定的葉子幣位址簽署的 + + + + &Verify Message + 訊息驗證 + + + + &Delete + 刪除 + + + + These are your CryptoLottoToken addresses for sending payments. Always check the amount and the receiving address before sending coins. + 這是你用來付款的葉子幣位址. 在付錢之前, 務必要檢查金額和收款位址是否正確. + + + + Copy &Label + 複製標記 + + + + &Edit + 編輯 + + + + Send &Coins + 付錢 + + + + Export Address Book Data + 匯出位址簿資料 + + + + Comma separated file (*.csv) + 逗號區隔資料檔 (*.csv) + + + + Error exporting + 匯出失敗 + + + + Could not write to file %1. + 無法寫入檔案 %1. + + + + AddressTableModel + + + Label + 標記 + + + + Address + 位址 + + + + (no label) + (沒有標記) + + + + AskPassphraseDialog + + + Passphrase Dialog + 密碼對話視窗 + + + + Enter passphrase + 輸入密碼 + + + + New passphrase + 新的密碼 + + + + Repeat new passphrase + 重複新密碼 + + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>. + 輸入錢包的新密碼.<br/>請用<b>10個以上的字元</b>, 或是<b>8個以上的單字</b>. + + + + Encrypt wallet + 錢包加密 + + + + This operation needs your wallet passphrase to unlock the wallet. + 這個動作需要用你的錢包密碼來解鎖 + + + + Unlock wallet + 錢包解鎖 + + + + This operation needs your wallet passphrase to decrypt the wallet. + 這個動作需要用你的錢包密碼來解密 + + + + Decrypt wallet + 錢包解密 + + + + Change passphrase + 變更密碼 + + + + Enter the old and new passphrase to the wallet. + 輸入錢包的新舊密碼. + + + + Confirm wallet encryption + 錢包加密確認 + + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR DOGECOINS</b>! + 警告: 如果將錢包加密後忘記密碼, 你會<b>失去其中所有的葉子幣</b>! + + + + Are you sure you wish to encrypt your wallet? + 你確定要將錢包加密嗎? + + + + 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. + 重要: 請改用新產生有加密的錢包檔, 來取代之前錢包檔的備份. 為了安全性的理由, 當你開始使用新的有加密的錢包時, 舊錢包的備份就不能再使用了. + + + + + Warning: The Caps Lock key is on! + 警告: 大寫字母鎖定作用中! + + + + + Wallet encrypted + 錢包已加密 + + + + CryptoLottoToken will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your CryptoLottoTokens from being stolen by malware infecting your computer. + 葉子幣現在要關閉以完成加密程序. 請記住, 加密錢包無法完全防止入侵電腦的惡意程式偷取你的葉子幣. + + + + + + + Wallet encryption failed + 錢包加密失敗 + + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + 錢包加密因程式內部錯誤而失敗. 你的錢包還是沒有加密. + + + + + The supplied passphrases do not match. + 提供的密碼不符. + + + + Wallet unlock failed + 錢包解鎖失敗 + + + + + + The passphrase entered for the wallet decryption was incorrect. + 用來解密錢包的密碼輸入錯誤. + + + + Wallet decryption failed + 錢包解密失敗 + + + + Wallet passphrase was successfully changed. + 錢包密碼變更成功. + + + + BitcoinGUI + + + Sign &message... + 訊息簽署... + + + + Synchronizing with network... + 網路同步中... + + + + &Overview + 總覽 + + + + Show general overview of wallet + 顯示錢包一般總覽 + + + + &Transactions + 交易 + + + + Browse transaction history + 瀏覽交易紀錄 + + + + Edit the list of stored addresses and labels for sending + 編輯位址與標記的儲存列表 + + + + Show the list of addresses for receiving payments + 顯示收款位址的列表 + + + + E&xit + 結束 + + + + Quit application + 結束應用程式 + + + + Show information about CryptoLottoToken + 顯示葉子幣相關資訊 + + + + About &Qt + 關於 &Qt + + + + Show information about Qt + 顯示有關於 Qt 的資訊 + + + + &Options... + 選項... + + + + &Encrypt Wallet... + 錢包加密... + + + + &Backup Wallet... + 錢包備份... + + + + &Change Passphrase... + 密碼變更... + + + + Importing blocks from disk... + 從磁碟匯入區塊中... + + + + Reindexing blocks on disk... + 重建磁碟區塊索引中... + + + + Send coins to a CryptoLottoToken address + 付錢到葉子幣位址 + + + + Modify configuration options for CryptoLottoToken + 修改葉子幣的設定選項 + + + + Backup wallet to another location + 將錢包備份到其它地方 + + + + Change the passphrase used for wallet encryption + 變更錢包加密用的密碼 + + + + &Debug window + 除錯視窗 + + + + Open debugging and diagnostic console + 開啓除錯與診斷主控台 + + + + &Verify message... + 驗證訊息... + + + + + CryptoLottoToken + 葉子幣 + + + + Wallet + 錢包 + + + + &Send + 付出 + + + + &Receive + 收受 + + + + &Addresses + 位址 + + + + &About CryptoLottoToken + 關於葉子幣 + + + + &Show / Hide + 顯示或隱藏 + + + + Show or hide the main Window + 顯示或隱藏主視窗 + + + + Encrypt the private keys that belong to your wallet + 將屬於你的錢包的密鑰加密 + + + + Sign messages with your CryptoLottoToken addresses to prove you own them + 用葉子幣位址簽署訊息來證明那是你的 + + + + Verify messages to ensure they were signed with specified CryptoLottoToken addresses + 驗證訊息來確認是用指定的葉子幣位址簽署的 + + + + &File + 檔案 + + + + &Settings + 設定 + + + + &Help + 求助 + + + + Tabs toolbar + 分頁工具列 + + + + + [testnet] + [testnet] + + + + CryptoLottoToken client + 葉子幣客戶端軟體 + + + + %n active connection(s) to CryptoLottoToken network + 與葉子幣網路有 %n 個連線在使用中 + + + + No block source available... + 目前沒有區塊來源... + + + + Processed %1 of %2 (estimated) blocks of transaction history. + 已處理了估計全部 %2 個中的 %1 個區塊的交易紀錄. + + + + Processed %1 blocks of transaction history. + 已處理了 %1 個區塊的交易紀錄. + + + + %n hour(s) + %n 個小時 + + + + %n day(s) + %n 天 + + + + %n week(s) + %n 個星期 + + + + %1 behind + 落後 %1 + + + + Last received block was generated %1 ago. + 最近收到的區塊是在 %1 之前生產出來. + + + + Transactions after this will not yet be visible. + 會看不見在這之後的交易. + + + + Error + 錯誤 + + + + Warning + 警告 + + + + Information + 資訊 + + + + This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee? + 這筆交易的資料大小超過限制了. 你還是可以付出 %1 的費用來傳送, 這筆費用會付給處理你的交易的節點, 並幫助維持整個網路. 你願意支付這項費用嗎? + + + + Up to date + 最新狀態 + + + + Catching up... + 進度追趕中... + + + + Confirm transaction fee + 確認交易手續費 + + + + Sent transaction + 付款交易 + + + + Incoming transaction + 收款交易 + + + + Date: %1 +Amount: %2 +Type: %3 +Address: %4 + + 日期: %1 +金額: %2 +類別: %3 +位址: %4 + + + + + URI handling + URI 處理 + + + + + URI can not be parsed! This can be caused by an invalid CryptoLottoToken address or malformed URI parameters. + 無法解析 URI! 也許葉子幣位址無效或 URI 參數有誤. + + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + 錢包<b>已加密</b>並且正<b>解鎖中</b> + + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + 錢包<b>已加密</b>並且正<b>上鎖中</b> + + + + A fatal error occurred. CryptoLottoToken can no longer continue safely and will quit. + 發生了致命的錯誤. 葉子幣程式無法再繼續安全執行, 只好結束. + + + + ClientModel + + + Network Alert + 網路警報 + + + + EditAddressDialog + + + Edit Address + 編輯位址 + + + + &Label + 標記 + + + + The label associated with this address book entry + 與這個位址簿項目關聯的標記 + + + + &Address + 位址 + + + + The address associated with this address book entry. This can only be modified for sending addresses. + 與這個位址簿項目關聯的位址. 付款位址才能被更改. + + + + New receiving address + 新收款位址 + + + + New sending address + 新增付款位址 + + + + Edit receiving address + 編輯收款位址 + + + + Edit sending address + 編輯付款位址 + + + + The entered address "%1" is already in the address book. + 輸入的位址"%1"已存在於位址簿中. + + + + The entered address "%1" is not a valid CryptoLottoToken address. + 輸入的位址 "%1" 並不是有效的葉子幣位址. + + + + Could not unlock wallet. + 無法將錢包解鎖. + + + + New key generation failed. + 新密鑰產生失敗. + + + + GUIUtil::HelpMessageBox + + + + CryptoLottoToken-Qt + 葉子幣-Qt + + + + version + 版本 + + + + Usage: + 用法: + + + + command-line options + 命令列選項 + + + + UI options + 使用界面選項 + + + + Set language, for example "de_DE" (default: system locale) + 設定語言, 比如說 "de_DE" (預設: 系統語系) + + + + Start minimized + 啓動時最小化 + + + + + Show splash screen on startup (default: 1) + 顯示啓動畫面 (預設: 1) + + + + OptionsDialog + + + Options + 選項 + + + + &Main + 主要 + + + + Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. + 非必要的交易手續費, 以 kB 為計費單位, 且有助於縮短你的交易處理時間. 大部份交易資料的大小是 1 kB. + + + + Pay transaction &fee + 付交易手續費 + + + + Automatically start CryptoLottoToken after logging in to the system. + 在登入系統後自動啓動葉子幣. + + + + &Start CryptoLottoToken on system login + 系統登入時啟動葉子幣 + + + + Reset all client options to default. + 回復所有客戶端軟體選項成預設值. + + + + &Reset Options + 選項回復 + + + + &Network + 網路 + + + + Automatically open the CryptoLottoToken client port on the router. This only works when your router supports UPnP and it is enabled. + 自動在路由器上開啟 CryptoLottoToken 的客戶端通訊埠. 只有在你的路由器支援 UPnP 且開啟時才有作用. + + + + Map port using &UPnP + 用 &UPnP 設定通訊埠對應 + + + + Connect to the CryptoLottoToken network through a SOCKS proxy (e.g. when connecting through Tor). + 透過 SOCKS 代理伺服器連線至葉子幣網路 (比如說要透過 Tor 連線). + + + + &Connect through SOCKS proxy: + 透過 SOCKS 代理伺服器連線: + + + + Proxy &IP: + 代理伺服器位址: + + + + IP address of the proxy (e.g. 127.0.0.1) + 代理伺服器的網際網路位址 (比如說 127.0.0.1) + + + + &Port: + 通訊埠: + + + + Port of the proxy (e.g. 9050) + 代理伺服器的通訊埠 (比如說 9050) + + + + SOCKS &Version: + SOCKS 協定版本: + + + + SOCKS version of the proxy (e.g. 5) + 代理伺服器的 SOCKS 協定版本 (比如說 5) + + + + &Window + 視窗 + + + + Show only a tray icon after minimizing the window. + 最小化視窗後只在通知區域顯示圖示 + + + + &Minimize to the tray instead of the taskbar + 最小化至通知區域而非工作列 + + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. + 當視窗關閉時將其最小化, 而非結束應用程式. 當勾選這個選項時, 應用程式只能用選單中的結束來停止執行. + + + + M&inimize on close + 關閉時最小化 + + + + &Display + 顯示 + + + + User Interface &language: + 使用界面語言 + + + + The user interface language can be set here. This setting will take effect after restarting CryptoLottoToken. + 可以在這裡設定使用者介面的語言. 這個設定在葉子幣程式重啓後才會生效. + + + + &Unit to show amounts in: + 金額顯示單位: + + + + Choose the default subdivision unit to show in the interface and when sending coins. + 選擇操作界面與付錢時預設顯示的細分單位. + + + + Whether to show CryptoLottoToken addresses in the transaction list or not. + 是否要在交易列表中顯示葉子幣位址. + + + + &Display addresses in transaction list + 在交易列表顯示位址 + + + + &OK + + + + + &Cancel + 取消 + + + + &Apply + 套用 + + + + default + 預設 + + + + Confirm options reset + 確認回復選項 + + + + Some settings may require a client restart to take effect. + 有些設定可能需要重新啓動客戶端軟體才會生效. + + + + Do you want to proceed? + 你想要就做下去嗎? + + + + + Warning + 警告 + + + + + This setting will take effect after restarting CryptoLottoToken. + 這個設定會在葉子幣程式重啓後生效. + + + + The supplied proxy address is invalid. + 提供的代理伺服器位址無效 + + + + OverviewPage + + + Form + 表單 + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the CryptoLottoToken network after a connection is established, but this process has not completed yet. + 顯示的資訊可能是過期的. 與葉子幣網路的連線建立後, 你的錢包會自動和網路同步, 但這個步驟還沒完成. + + + + Balance: + 餘額: + + + + Unconfirmed: + 未確認額: + + + + Wallet + 錢包 + + + + Immature: + 未熟成 + + + + Mined balance that has not yet matured + 尚未熟成的開採金額 + + + + <b>Recent transactions</b> + <b>最近交易</b> + + + + Your current balance + 目前餘額 + + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + 尚未確認之交易的總額, 不包含在目前餘額中 + + + + + out of sync + 沒同步 + + + + PaymentServer + + + Cannot start CryptoLottoToken: click-to-pay handler + 無法啟動 CryptoLottoToken 隨按隨付處理器 + + + + QRCodeDialog + + + QR Code Dialog + QR 條碼對話視窗 + + + + Request Payment + 付款單 + + + + Amount: + 金額: + + + + Label: + 標記: + + + + Message: + 訊息: + + + + &Save As... + 儲存為... + + + + Error encoding URI into QR Code. + 將 URI 編碼成 QR 條碼失敗 + + + + The entered amount is invalid, please check. + 輸入的金額無效, 請檢查看看. + + + + Resulting URI too long, try to reduce the text for label / message. + 造出的網址太長了,請把標籤或訊息的文字縮短再試看看. + + + + Save QR Code + 儲存 QR 條碼 + + + + PNG Images (*.png) + PNG 圖檔 (*.png) + + + + RPCConsole + + + Client name + 客戶端軟體名稱 + + + + + + + + + + + + + N/A + + + + + Client version + 客戶端軟體版本 + + + + &Information + 資訊 + + + + Using OpenSSL version + 使用 OpenSSL 版本 + + + + Startup time + 啓動時間 + + + + Network + 網路 + + + + Number of connections + 連線數 + + + + On testnet + 位於測試網路 + + + + Block chain + 區塊鎖鏈 + + + + Current number of blocks + 目前區塊數 + + + + Estimated total blocks + 估計總區塊數 + + + + Last block time + 最近區塊時間 + + + + &Open + 開啓 + + + + Command-line options + 命令列選項 + + + + Show the CryptoLottoToken-Qt help message to get a list with possible CryptoLottoToken command-line options. + 顯示葉子幣-Qt的求助訊息, 來取得可用的命令列選項列表. + + + + &Show + 顯示 + + + + &Console + 主控台 + + + + Build date + 建置日期 + + + + CryptoLottoToken - Debug window + 葉子幣 - 除錯視窗 + + + + CryptoLottoToken Core + 葉子幣核心 + + + + Debug log file + 除錯紀錄檔 + + + + Open the CryptoLottoToken debug log file from the current data directory. This can take a few seconds for large log files. + 從目前的資料目錄下開啓葉子幣的除錯紀錄檔. 當紀錄檔很大時可能要花好幾秒的時間. + + + + Clear console + 清主控台 + + + + Welcome to the CryptoLottoToken RPC console. + 歡迎使用葉子幣 RPC 主控台. + + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + 請用上下游標鍵來瀏覽歷史指令, 且可用 <b>Ctrl-L</b> 來清理畫面. + + + + Type <b>help</b> for an overview of available commands. + 請打 <b>help</b> 來看可用指令的簡介. + + + + SendCoinsDialog + + + + + + + + + + Send Coins + 付錢 + + + + Send to multiple recipients at once + 一次付給多個人 + + + + Add &Recipient + 加收款人 + + + + Remove all transaction fields + 移除所有交易欄位 + + + + Clear &All + 全部清掉 + + + + Balance: + 餘額: + + + + 123.456 BTC + 123.456 BTC + + + + Confirm the send action + 確認付款動作 + + + + S&end + 付出 + + + + <b>%1</b> to %2 (%3) + <b>%1</b> 給 %2 (%3) + + + + Confirm send coins + 確認要付錢 + + + + Are you sure you want to send %1? + 確定要付出 %1 嗎? + + + + and + + + + + The recipient address is not valid, please recheck. + 無效的收款位址, 請再檢查看看. + + + + The amount to pay must be larger than 0. + 付款金額必須大於 0. + + + + The amount exceeds your balance. + 金額超過餘額了. + + + + The total exceeds your balance when the %1 transaction fee is included. + 包含 %1 的交易手續費後, 總金額超過你的餘額了. + + + + Duplicate address found, can only send to each address once per send operation. + 發現有重複的位址. 每個付款動作中, 只能付給個別的位址一次. + + + + Error: Transaction creation failed! + 錯誤: 交易產生失敗! + + + + 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. + 錯誤: 交易被拒絕. 有時候會發生這種錯誤, 是因為你錢包中的一些錢已經被花掉了. 比如說你複製了錢包檔 wallet.dat, 然後用複製的錢包花掉了錢, 你現在所用的原來的錢包中卻沒有該筆交易紀錄. + + + + SendCoinsEntry + + + Form + 表單 + + + + A&mount: + 金額: + + + + Pay &To: + 付給: + + + + The address to send the payment to (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + 付款的目標位址 (比如說 Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Enter a label for this address to add it to your address book + 輸入一個標記給這個位址, 並加到位址簿中 + + + + &Label: + 標記: + + + + Choose address from address book + 從位址簿中選一個位址 + + + + Alt+A + Alt+A + + + + Paste address from clipboard + 從剪貼簿貼上位址 + + + + Alt+P + Alt+P + + + + Remove this recipient + 去掉這個收款人 + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + 輸入葉子幣位址 (比如說 Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + SignVerifyMessageDialog + + + Signatures - Sign / Verify a Message + 簽章 - 簽署或驗證訊息 + + + + &Sign Message + 訊息簽署 + + + + You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + 你可以用自己的位址來簽署訊息, 以證明你對它的所有權. 但是請小心, 不要簽署語意含糊不清的內容, 因為釣魚式詐騙可能會用騙你簽署的手法來冒充是你. 只有在語句中的細節你都同意時才簽署. + + + + The address to sign the message with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + 用來簽署訊息的位址 (比如說 Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + + Choose an address from the address book + 從位址簿選一個位址 + + + + + Alt+A + Alt+A + + + + Paste address from clipboard + 從剪貼簿貼上位址 + + + + Alt+P + Alt+P + + + + Enter the message you want to sign here + 在這裡輸入你想簽署的訊息 + + + + Signature + 簽章 + + + + Copy the current signature to the system clipboard + 複製目前的簽章到系統剪貼簿 + + + + Sign the message to prove you own this CryptoLottoToken address + 簽署訊息是用來證明這個葉子幣位址是你的 + + + + Sign &Message + 訊息簽署 + + + + Reset all sign message fields + 重置所有訊息簽署欄位 + + + + + Clear &All + 全部清掉 + + + + &Verify Message + 訊息驗證 + + + + Enter the signing 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. + 請在下面輸入簽署的位址, 訊息(請確認完整複製了所包含的換行, 空格, 跳位符號等等), 與簽章, 以驗證該訊息. 請小心, 除了訊息內容外, 不要對簽章本身過度解讀, 以避免被用"中間人攻擊法"詐騙. + + + + The address the message was signed with (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + 簽署該訊息的位址 (比如說 Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Verify the message to ensure it was signed with the specified CryptoLottoToken address + 驗證訊息是用來確認訊息是用指定的葉子幣位址簽署的 + + + + Verify &Message + 訊息驗證 + + + + Reset all verify message fields + 重置所有訊息驗證欄位 + + + + + Enter a CryptoLottoToken address (e.g. Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + 輸入葉子幣位址 (比如說 Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2) + + + + Click "Sign Message" to generate signature + 按"訊息簽署"來產生簽章 + + + + Enter CryptoLottoToken signature + 輸入葉子幣簽章 + + + + + The entered address is invalid. + 輸入的位址無效. + + + + + + + Please check the address and try again. + 請檢查位址是否正確後再試一次. + + + + + The entered address does not refer to a key. + 輸入的位址沒有指到任何密鑰. + + + + Wallet unlock was cancelled. + 錢包解鎖已取消. + + + + Private key for the entered address is not available. + 沒有所輸入位址的密鑰. + + + + Message signing failed. + 訊息簽署失敗. + + + + Message signed. + 訊息已簽署. + + + + The signature could not be decoded. + 無法將這個簽章解碼. + + + + + Please check the signature and try again. + 請檢查簽章是否正確後再試一次. + + + + The signature did not match the message digest. + 這個簽章與訊息的數位摘要不符. + + + + Message verification failed. + 訊息驗證失敗. + + + + Message verified. + 訊息已驗證. + + + + SplashScreen + + + The CryptoLottoToken developers + 葉子幣開發人員 + + + + [testnet] + [testnet] + + + + TransactionDesc + + + Open until %1 + 在 %1 前未定 + + + + %1/offline + %1/離線中 + + + + %1/unconfirmed + %1/未確認 + + + + %1 confirmations + 經確認 %1 次 + + + + Status + 狀態 + + + + , broadcast through %n node(s) + , 已公告至 %n 個節點 + + + + Date + 日期 + + + + Source + 來源 + + + + Generated + 生產出 + + + + + From + 來處 + + + + + + To + 目的 + + + + + own address + 自己的位址 + + + + label + 標籤 + + + + + + + + Credit + 入帳 + + + + matures in %n more block(s) + 將在 %n 個區塊產出後熟成 + + + + not accepted + 不被接受 + + + + + + + Debit + 出帳 + + + + Transaction fee + 交易手續費 + + + + Net amount + 淨額 + + + + Message + 訊息 + + + + Comment + 附註 + + + + Transaction ID + 交易識別碼 + + + + Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + 生產出來的錢要再等 120 個區塊熟成之後, 才能夠花用. 當你產出區塊時, 它會被公布到網路上, 以被串連至區塊鎖鏈. 如果串連失敗了, 它的狀態就會變成"不被接受", 且不能被花用. 當你產出區塊的幾秒鐘內, 也有其他節點產出區塊的話, 有時候就會發生這種情形. + + + + Debug information + 除錯資訊 + + + + Transaction + 交易 + + + + Inputs + 輸入 + + + + Amount + 金額 + + + + true + + + + + false + + + + + , has not been successfully broadcast yet + , 尚未成功公告出去 + + + + Open for %n more block(s) + 在接下來 %n 個區塊產出前未定 + + + + unknown + 未知 + + + + TransactionDescDialog + + + Transaction details + 交易明細 + + + + This pane shows a detailed description of the transaction + 此版面顯示交易的詳細說明 + + + + TransactionTableModel + + + Date + 日期 + + + + Type + 種類 + + + + Address + 位址 + + + + Amount + 金額 + + + + Open for %n more block(s) + 在接下來 %n 個區塊產出前未定 + + + + Open until %1 + 在 %1 前未定 + + + + Offline (%1 confirmations) + 離線中 (經確認 %1 次) + + + + Unconfirmed (%1 of %2 confirmations) + 未確認 (經確認 %1 次, 應確認 %2 次) + + + + Confirmed (%1 confirmations) + 已確認 (經確認 %1 次) + + + + Mined balance will be available when it matures in %n more block(s) + 開採金額將可在 %n 個區塊熟成後可用 + + + + This block was not received by any other nodes and will probably not be accepted! + 沒有其他節點收到這個區塊, 也許它不被接受! + + + + Generated but not accepted + 生產出但不被接受 + + + + Received with + 收受於 + + + + Received from + 收受自 + + + + Sent to + 付出至 + + + + Payment to yourself + 付給自己 + + + + Mined + 開採所得 + + + + (n/a) + (不適用) + + + + Transaction status. Hover over this field to show number of confirmations. + 交易狀態. 移動游標至欄位上方來顯示確認次數. + + + + Date and time that the transaction was received. + 收到交易的日期與時間. + + + + Type of transaction. + 交易的種類. + + + + Destination address of transaction. + 交易的目標位址. + + + + Amount removed from or added to balance. + 減去或加入至餘額的金額 + + + + TransactionView + + + + All + 全部 + + + + Today + 今天 + + + + This week + 這週 + + + + This month + 這個月 + + + + Last month + 上個月 + + + + This year + 今年 + + + + Range... + 指定範圍... + + + + Received with + 收受於 + + + + Sent to + 付出至 + + + + To yourself + 給自己 + + + + Mined + 開採所得 + + + + Other + 其他 + + + + Enter address or label to search + 輸入位址或標記來搜尋 + + + + Min amount + 最小金額 + + + + Copy address + 複製位址 + + + + Copy label + 複製標記 + + + + Copy amount + 複製金額 + + + + Copy transaction ID + 複製交易識別碼 + + + + Edit label + 編輯標記 + + + + Show transaction details + 顯示交易明細 + + + + Export Transaction Data + 匯出交易資料 + + + + Comma separated file (*.csv) + 逗號分隔資料檔 (*.csv) + + + + Confirmed + 已確認 + + + + Date + 日期 + + + + Type + 種類 + + + + Label + 標記 + + + + Address + 位址 + + + + Amount + 金額 + + + + ID + 識別碼 + + + + Error exporting + 匯出失敗 + + + + Could not write to file %1. + 無法寫入至 %1 檔案. + + + + Range: + 範圍: + + + + to + + + + + WalletModel + + + Send Coins + 付錢 + + + + WalletView + + + &Export + 匯出 + + + + Export the data in the current tab to a file + 將目前分頁的資料匯出存成檔案 + + + + Backup Wallet + 錢包備份 + + + + Wallet Data (*.dat) + 錢包資料檔 (*.dat) + + + + Backup Failed + 備份失敗 + + + + There was an error trying to save the wallet data to the new location. + 儲存錢包資料到新的地方失敗 + + + + Backup Successful + 備份成功 + + + + The wallet data was successfully saved to the new location. + 錢包的資料已經成功儲存到新的地方了. + + + + bitcoin-core + + + CryptoLottoToken version + 葉子幣版本 + + + + Usage: + 用法: + + + + Send command to -server or CryptoLottoTokend + 送指令給 -server 或 CryptoLottoTokend + + + + + List commands + 列出指令 + + + + + Get help for a command + 取得指令說明 + + + + + Options: + 選項: + + + + + Specify configuration file (default: CryptoLottoToken.conf) + 指定設定檔 (預設: CryptoLottoToken.conf) + + + + + Specify pid file (default: CryptoLottoTokend.pid) + 指定行程識別碼檔案 (預設: CryptoLottoTokend.pid) + + + + + Specify data directory + 指定資料目錄 + + + + + Set database cache size in megabytes (default: 25) + 設定資料庫快取大小為多少百萬位元組(MB, 預設: 25) + + + + Listen for connections on <port> (default: 22556 or testnet: 44556) + 在通訊埠 <port> 聽候連線 (預設: 22556, 或若為測試網路: 44556) + + + + Maintain at most <n> connections to peers (default: 125) + 維持與節點連線數的上限為 <n> 個 (預設: 125) + + + + Connect to a node to retrieve peer addresses, and disconnect + 連線到某個節點以取得其它節點的位址, 然後斷線 + + + + Specify your own public address + 指定自己公開的位址 + + + + Threshold for disconnecting misbehaving peers (default: 100) + 與亂搞的節點斷線的臨界值 (預設: 100) + + + + Number of seconds to keep misbehaving peers from reconnecting (default: 86400) + 避免與亂搞的節點連線的秒數 (預設: 86400) + + + + An error occurred while setting up the RPC port %u for listening on IPv4: %s + 在 IPv4 網路上以通訊埠 %u 聽取 RPC 連線時發生錯誤: %s + + + + Listen for JSON-RPC connections on <port> (default: 22555 or testnet: 44555) + 在通訊埠 <port> 聽候 JSON-RPC 連線 (預設: 22555, 或若為測試網路: 44555) + + + + Accept command line and JSON-RPC commands + 接受命令列與 JSON-RPC 指令 + + + + + Run in the background as a daemon and accept commands + 以背景程式執行並接受指令 + + + + Use the test network + 使用測試網路 + + + + + Accept connections from outside (default: 1 if no -proxy or -connect) + 是否接受外來連線 (預設: 當沒有 -proxy 或 -connect 時預設為 1) + + + + %s, you must set a rpcpassword in the configuration file: +%s +It is recommended you use the following random password: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(you do not need to remember this password) +The username and password MUST NOT be the same. +If the file does not exist, create it with owner-readable-only file permissions. +It is also recommended to set alertnotify so you are notified of problems; +for example: alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + %s, 你必須要在以下設定檔中設定 RPC 密碼(rpcpassword): +%s +建議你使用以下隨機產生的密碼: +rpcuser=CryptoLottoTokenrpc +rpcpassword=%s +(你不用記住這個密碼) +使用者名稱(rpcuser)和密碼(rpcpassword)不可以相同! +如果設定檔還不存在, 請在新增時, 設定檔案權限為"只有主人才能讀取". +也建議你設定警示通知, 發生問題時你才會被通知到; +比如說設定為: +alertnotify=echo %%s | mail -s "CryptoLottoToken Alert" admin@foo.com + + + + + An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s + 設定在 IPv6 網路的通訊埠 %u 上聽候 RPC 連線失敗, 退而改用 IPv4 網路: %s + + + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + 和指定的位址繫結, 並總是在該位址聽候連線. IPv6 請用 "[主機]:通訊埠" 這種格式 + + + + Cannot obtain a lock on data directory %s. CryptoLottoToken is probably already running. + 無法鎖定資料目錄 %s. 也許葉子幣已經在執行了. + + + + 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. + 錯誤: 交易被拒絕了! 有時候會發生這種錯誤, 是因為你錢包中的一些錢已經被花掉了. 比如說你複製了錢包檔 wallet.dat, 然後用複製的錢包花掉了錢, 你現在所用的原來的錢包中卻沒有該筆交易紀錄. + + + + Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds! + 錯誤: 這筆交易需要至少 %s 的手續費! 因為它的金額太大, 或複雜度太高, 或是使用了最近才剛收到的款項. + + + + Execute command when a relevant alert is received (%s in cmd is replaced by message) + 當收到相關警示時所要執行的指令 (指令中的 %s 會被取代為警示訊息) + + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + 當錢包有交易改變時所要執行的指令 (指令中的 %s 會被取代為交易識別碼) + + + + Set maximum size of high-priority/low-fee transactions in bytes (default: 27000) + 設定高優先權或低手續費的交易資料大小上限為多少位元組 (預設: 27000) + + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + 這是尚未發表的測試版本 - 使用請自負風險 - 請不要用於開採或商業應用 + + + + Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. + 警告: -paytxfee 設定了很高的金額! 這可是你交易付款所要付的手續費. + + + + Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade. + 警告: 顯示的交易可能不正確! 你可能需要升級, 或者需要等其它的節點升級. + + + + Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly. + 警告: 請檢查電腦時間與日期是否正確! 葉子幣無法在時鐘不準的情況下正常運作. + + + + Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + 警告: 讀取錢包檔 wallet.dat 失敗了! 所有的密鑰都正確讀取了, 但是交易資料或位址簿資料可能會缺少或不正確. + + + + Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. + 警告: 錢包檔 wallet.dat 壞掉, 但資料被拯救回來了! 原來的 wallet.dat 會改儲存在 %s, 檔名為 wallet.{timestamp}.bak. 如果餘額或交易資料有誤, 你應該要用備份資料復原回來. + + + + Attempt to recover private keys from a corrupt wallet.dat + 嘗試從壞掉的錢包檔 wallet.dat 復原密鑰 + + + + Block creation options: + 區塊產生選項: + + + + Connect only to the specified node(s) + 只連線至指定節點(可多個) + + + + Corrupted block database detected + 發現區塊資料庫壞掉了 + + + + Discover own IP address (default: 1 when listening and no -externalip) + 找出自己的網際網路位址 (預設: 當有聽候連線且沒有 -externalip 時為 1) + + + + Do you want to rebuild the block database now? + 你要現在重建區塊資料庫嗎? + + + + Error initializing block database + 初始化區塊資料庫失敗 + + + + Error initializing wallet database environment %s! + 錢包資料庫環境 %s 初始化錯誤! + + + + Error loading block database + 載入區塊資料庫失敗 + + + + Error opening block database + 打開區塊資料庫檔案失敗 + + + + Error: Disk space is low! + 錯誤: 磁碟空間很少! + + + + Error: Wallet locked, unable to create transaction! + 錯誤: 錢包被上鎖了, 無法產生新的交易! + + + + Error: system error: + 錯誤: 系統錯誤: + + + + Failed to listen on any port. Use -listen=0 if you want this. + 在任意的通訊埠聽候失敗. 如果你想的話可以用 -listen=0. + + + + Failed to read block info + 讀取區塊資訊失敗 + + + + Failed to read block + 讀取區塊失敗 + + + + Failed to sync block index + 同步區塊索引失敗 + + + + Failed to write block index + 寫入區塊索引失敗 + + + + Failed to write block info + 寫入區塊資訊失敗 + + + + Failed to write block + 寫入區塊失敗 + + + + Failed to write file info + 寫入檔案資訊失敗 + + + + Failed to write to coin database + 寫入葉子幣資料庫失敗 + + + + Failed to write transaction index + 寫入交易索引失敗 + + + + Failed to write undo data + 寫入回復資料失敗 + + + + Find peers using DNS lookup (default: 1 unless -connect) + 是否允許在找節點時使用域名查詢 (預設: 當沒用 -connect 時為 1) + + + + Generate coins (default: 0) + 生產葉子幣 (預設值: 0) + + + + How many blocks to check at startup (default: 288, 0 = all) + 啓動時檢查的區塊數 (預設: 288, 指定 0 表示全部) + + + + How thorough the block verification is (0-4, default: 3) + 區塊檢查的仔細程度 (0 至 4, 預設: 3) + + + + Not enough file descriptors available. + 檔案描述器不足. + + + + Rebuild block chain index from current blk000??.dat files + 從目前的區塊檔 blk000??.dat 重建鎖鏈索引 + + + + Set the number of threads to service RPC calls (default: 4) + 設定處理 RPC 服務請求的執行緒數目 (預設為 4) + + + + Verifying blocks... + 驗證區塊資料中... + + + + Verifying wallet... + 驗證錢包資料中... + + + + Imports blocks from external blk000??.dat file + 從其它來源的 blk000??.dat 檔匯入區塊 + + + + Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0) + 設定指令碼驗證的執行緒數目 (最多為 16, 若為 0 表示程式自動決定, 小於 0 表示保留不用的處理器核心數目, 預設為 0) + + + + Information + 資訊 + + + + Invalid -tor address: '%s' + 無效的 -tor 位址: '%s' + + + + Invalid amount for -minrelaytxfee=<amount>: '%s' + 設定 -minrelaytxfee=<金額> 的金額無效: '%s' + + + + Invalid amount for -mintxfee=<amount>: '%s' + 設定 -mintxfee=<amount> 的金額無效: '%s' + + + + Maintain a full transaction index (default: 0) + 維護全部交易的索引 (預設為 0) + + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000) + 每個連線的接收緩衝區大小上限為 <n>*1000 個位元組 (預設: 5000) + + + + Maximum per-connection send buffer, <n>*1000 bytes (default: 1000) + 每個連線的傳送緩衝區大小上限為 <n>*1000 位元組 (預設: 1000) + + + + Only accept block chain matching built-in checkpoints (default: 1) + 只接受與內建的檢查段點吻合的區塊鎖鏈 (預設: 1) + + + + Only connect to nodes in network <net> (IPv4, IPv6 or Tor) + 只和 <net> 網路上的節點連線 (IPv4, IPv6, 或 Tor) + + + + Output extra debugging information. Implies all other -debug* options + 輸出額外的除錯資訊. 包含了其它所有的 -debug* 選項 + + + + Output extra network debugging information + 輸出額外的網路除錯資訊 + + + + Prepend debug output with timestamp (default: 1) + 在除錯輸出內容前附加時間 + + + + SSL options: (see the CryptoLottoToken Wiki for SSL setup instructions) + SSL 選項: (SSL 設定程序請見 CryptoLottoToken Wiki) + + + + Select the version of socks proxy to use (4-5, default: 5) + 選擇 SOCKS 代理伺服器的協定版本(4 或 5, 預設: 5) + + + + Send trace/debug info to console instead of debug.log file + 在終端機顯示追蹤或除錯資訊, 而非寫到 debug.log 檔 + + + + Send trace/debug info to debugger + 輸出追蹤或除錯資訊給除錯器 + + + + Set maximum block size in bytes (default: 250000) + 設定區塊大小上限為多少位元組 (預設: 250000) + + + + Set minimum block size in bytes (default: 0) + 設定區塊大小下限為多少位元組 (預設: 0) + + + + Shrink debug.log file on client startup (default: 1 when no -debug) + 客戶端軟體啓動時將 debug.log 檔縮小 (預設: 當沒有 -debug 時為 1) + + + + Signing transaction failed + 簽署交易失敗 + + + + Specify connection timeout in milliseconds (default: 5000) + 指定連線在幾毫秒後逾時 (預設: 5000) + + + + System error: + 系統錯誤: + + + + Transaction amount too small + 交易金額太小 + + + + Transaction amounts must be positive + 交易金額必須是正的 + + + + Transaction too large + 交易位元量太大 + + + + Use UPnP to map the listening port (default: 0) + 是否使用通用即插即用(UPnP)協定來設定聽候連線的通訊埠 (預設: 0) + + + + Use UPnP to map the listening port (default: 1 when listening) + 是否使用通用即插即用(UPnP)協定來設定聽候連線的通訊埠 (預設: 當有聽候連線為 1) + + + + Use proxy to reach tor hidden services (default: same as -proxy) + 透過代理伺服器來使用 Tor 隱藏服務 (預設: 同 -proxy) + + + + Username for JSON-RPC connections + JSON-RPC 連線使用者名稱 + + + + Warning + 警告 + + + + Warning: This version is obsolete, upgrade required! + 警告: 這個版本已經被淘汰掉了, 必須要升級! + + + + You need to rebuild the databases using -reindex to change -txindex + 改變 -txindex 參數後, 必須要用 -reindex 參數來重建資料庫 + + + + wallet.dat corrupt, salvage failed + 錢包檔 weallet.dat 壞掉了, 拯救失敗 + + + + Password for JSON-RPC connections + JSON-RPC 連線密碼 + + + + Allow JSON-RPC connections from specified IP address + 只允許從指定網路位址來的 JSON-RPC 連線 + + + + Send commands to node running on <ip> (default: 127.0.0.1) + 送指令給在 <ip> 的節點 (預設: 127.0.0.1) + + + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + 當最新區塊改變時所要執行的指令 (指令中的 %s 會被取代為區塊的雜湊值) + + + + Upgrade wallet to latest format + 將錢包升級成最新的格式 + + + + Set key pool size to <n> (default: 100) + 設定密鑰池大小為 <n> (預設: 100) + + + + + Rescan the block chain for missing wallet transactions + 重新掃描區塊鎖鏈, 以尋找錢包所遺漏的交易. + + + + Use OpenSSL (https) for JSON-RPC connections + 於 JSON-RPC 連線使用 OpenSSL (https) + + + + + Server certificate file (default: server.cert) + 伺服器憑證檔 (預設: server.cert) + + + + + Server private key (default: server.pem) + 伺服器密鑰檔 (預設: server.pem) + + + + + Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + 可以接受的加密法 (預設: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH) + + + + + This help message + 此協助訊息 + + + + + Unable to bind to %s on this computer (bind returned error %d, %s) + 無法和這台電腦上的 %s 繫結 (繫結回傳錯誤 %d, %s) + + + + Connect through socks proxy + 透過 SOCKS 代理伺服器連線 + + + + Allow DNS lookups for -addnode, -seednode and -connect + 允許對 -addnode, -seednode, -connect 的參數使用域名查詢 + + + + Loading addresses... + 載入位址中... + + + + Error loading wallet.dat: Wallet corrupted + 載入檔案 wallet.dat 失敗: 錢包壞掉了 + + + + Error loading wallet.dat: Wallet requires newer version of CryptoLottoToken + 載入檔案 wallet.dat 失敗: 此錢包需要新版的 CryptoLottoToken + + + + Wallet needed to be rewritten: restart CryptoLottoToken to complete + 錢包需要重寫: 請重啟葉子幣來完成 + + + + Error loading wallet.dat + 載入檔案 wallet.dat 失敗 + + + + Invalid -proxy address: '%s' + 無效的 -proxy 位址: '%s' + + + + Unknown network specified in -onlynet: '%s' + 在 -onlynet 指定了不明的網路別: '%s' + + + + Unknown -socks proxy version requested: %i + 在 -socks 指定了不明的代理協定版本: %i + + + + Cannot resolve -bind address: '%s' + 無法解析 -bind 位址: '%s' + + + + Cannot resolve -externalip address: '%s' + 無法解析 -externalip 位址: '%s' + + + + Invalid amount for -paytxfee=<amount>: '%s' + 設定 -paytxfee=<金額> 的金額無效: '%s' + + + + Invalid amount + 無效的金額 + + + + Insufficient funds + 累積金額不足 + + + + Loading block index... + 載入區塊索引中... + + + + Add a node to connect to and attempt to keep the connection open + 加入一個要連線的節線, 並試著保持對它的連線暢通 + + + + Unable to bind to %s on this computer. CryptoLottoToken is probably already running. + 無法和這台電腦上的 %s 繫結. 也許葉子幣已經在執行了. + + + + Fee per KB to add to transactions you send + 交易付款時每 KB 的交易手續費 + + + + Loading wallet... + 載入錢包中... + + + + Cannot downgrade wallet + 無法將錢包格式降級 + + + + Cannot write default address + 無法寫入預設位址 + + + + Rescanning... + 重新掃描中... + + + + Done loading + 載入完成 + + + + To use the %s option + 為了要使用 %s 選項 + + + + Error + 錯誤 + + + + You must set rpcpassword=<password> in the configuration file: +%s +If the file does not exist, create it with owner-readable-only file permissions. + 你必須在下列設定檔中設定 RPC 密碼(rpcpassword=<password>): +%s +如果這個檔案還不存在, 請在新增時, 設定檔案權限為"只有主人才能讀取". + + + diff --git a/src/qt/macdockiconhandler.h b/src/qt/macdockiconhandler.h new file mode 100644 index 0000000..765b004 --- /dev/null +++ b/src/qt/macdockiconhandler.h @@ -0,0 +1,47 @@ +#ifndef MACDOCKICONHANDLER_H +#define MACDOCKICONHANDLER_H + +#include +#include + +QT_BEGIN_NAMESPACE +class QMenu; +class QIcon; +class QWidget; +QT_END_NAMESPACE + +#ifdef __OBJC__ +@class DockIconClickEventHandler; +#else +class DockIconClickEventHandler; +#endif + +/** Macintosh-specific dock icon handler. + */ +class MacDockIconHandler : public QObject +{ + Q_OBJECT + +public: + ~MacDockIconHandler(); + + QMenu *dockMenu(); + void setIcon(const QIcon &icon); + void setMainWindow(QMainWindow *window); + static MacDockIconHandler *instance(); + + void handleDockIconClickEvent(); + +signals: + void dockIconClicked(); + +private: + MacDockIconHandler(); + + DockIconClickEventHandler *m_dockIconClickEventHandler; + QWidget *m_dummyWidget; + QMenu *m_dockMenu; + QMainWindow *mainWindow; +}; + +#endif // MACDOCKICONCLICKHANDLER_H diff --git a/src/qt/macdockiconhandler.mm b/src/qt/macdockiconhandler.mm new file mode 100644 index 0000000..86b8c83 --- /dev/null +++ b/src/qt/macdockiconhandler.mm @@ -0,0 +1,131 @@ +#include "macdockiconhandler.h" + +#include +#include +#include +#include + +#undef slots +#include + +#if QT_VERSION < 0x050000 +extern void qt_mac_set_dock_menu(QMenu *); +#endif + +@interface DockIconClickEventHandler : NSObject +{ + MacDockIconHandler* dockIconHandler; +} + +@end + +@implementation DockIconClickEventHandler + +- (id)initWithDockIconHandler:(MacDockIconHandler *)aDockIconHandler +{ + self = [super init]; + if (self) { + dockIconHandler = aDockIconHandler; + + [[NSAppleEventManager sharedAppleEventManager] + setEventHandler:self + andSelector:@selector(handleDockClickEvent:withReplyEvent:) + forEventClass:kCoreEventClass + andEventID:kAEReopenApplication]; + } + return self; +} + +- (void)handleDockClickEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAppleEventDescriptor*)replyEvent +{ + Q_UNUSED(event) + Q_UNUSED(replyEvent) + + if (dockIconHandler) { + dockIconHandler->handleDockIconClickEvent(); + } +} + +@end + +MacDockIconHandler::MacDockIconHandler() : QObject() +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + this->m_dockIconClickEventHandler = [[DockIconClickEventHandler alloc] initWithDockIconHandler:this]; + this->m_dummyWidget = new QWidget(); + this->m_dockMenu = new QMenu(this->m_dummyWidget); + this->setMainWindow(NULL); +#if QT_VERSION < 0x050000 + qt_mac_set_dock_menu(this->m_dockMenu); +#endif + [pool release]; +} + +void MacDockIconHandler::setMainWindow(QMainWindow *window) { + this->mainWindow = window; +} + +MacDockIconHandler::~MacDockIconHandler() +{ + [this->m_dockIconClickEventHandler release]; + delete this->m_dummyWidget; + this->setMainWindow(NULL); +} + +QMenu *MacDockIconHandler::dockMenu() +{ + return this->m_dockMenu; +} + +void MacDockIconHandler::setIcon(const QIcon &icon) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSImage *image = nil; + if (icon.isNull()) + image = [[NSImage imageNamed:@"NSApplicationIcon"] retain]; + else { + // generate NSImage from QIcon and use this as dock icon. + QSize size = icon.actualSize(QSize(128, 128)); + QPixmap pixmap = icon.pixmap(size); + + // write temp file hack (could also be done through QIODevice [memory]) + QTemporaryFile notificationIconFile; + if (!pixmap.isNull() && notificationIconFile.open()) { + QImageWriter writer(¬ificationIconFile, "PNG"); + if (writer.write(pixmap.toImage())) { + const char *cString = notificationIconFile.fileName().toUtf8().data(); + NSString *macString = [NSString stringWithCString:cString encoding:NSUTF8StringEncoding]; + image = [[NSImage alloc] initWithContentsOfFile:macString]; + } + } + + if(!image) { + // if testnet image could not be created, load std. app icon + image = [[NSImage imageNamed:@"NSApplicationIcon"] retain]; + } + } + + [NSApp setApplicationIconImage:image]; + [image release]; + [pool release]; +} + +MacDockIconHandler *MacDockIconHandler::instance() +{ + static MacDockIconHandler *s_instance = NULL; + if (!s_instance) + s_instance = new MacDockIconHandler(); + return s_instance; +} + +void MacDockIconHandler::handleDockIconClickEvent() +{ + if (this->mainWindow) + { + this->mainWindow->activateWindow(); + this->mainWindow->show(); + } + + emit this->dockIconClicked(); +} diff --git a/src/qt/macnotificationhandler.h b/src/qt/macnotificationhandler.h new file mode 100644 index 0000000..cd8064c --- /dev/null +++ b/src/qt/macnotificationhandler.h @@ -0,0 +1,25 @@ +#ifndef MACNOTIFICATIONHANDLER_H +#define MACNOTIFICATIONHANDLER_H +#include + +/** Macintosh-specific notification handler (supports UserNotificationCenter and Growl). + */ +class MacNotificationHandler : public QObject +{ + Q_OBJECT + +public: + /** shows a 10.8+ UserNotification in the UserNotificationCenter + */ + void showNotification(const QString &title, const QString &text); + + /** executes AppleScript */ + void sendAppleScript(const QString &script); + + /** check if OS can handle UserNotifications */ + bool hasUserNotificationCenterSupport(void); + static MacNotificationHandler *instance(); +}; + + +#endif // MACNOTIFICATIONHANDLER_H diff --git a/src/qt/macnotificationhandler.mm b/src/qt/macnotificationhandler.mm new file mode 100644 index 0000000..8bb9b88 --- /dev/null +++ b/src/qt/macnotificationhandler.mm @@ -0,0 +1,65 @@ +#include "macnotificationhandler.h" + +#undef slots +#include + +void MacNotificationHandler::showNotification(const QString &title, const QString &text) +{ + // check if users OS has support for NSUserNotification + if(this->hasUserNotificationCenterSupport()) { + // okay, seems like 10.8+ + QByteArray utf8 = title.toUtf8(); + char* cString = (char *)utf8.constData(); + NSString *titleMac = [[NSString alloc] initWithUTF8String:cString]; + + utf8 = text.toUtf8(); + cString = (char *)utf8.constData(); + NSString *textMac = [[NSString alloc] initWithUTF8String:cString]; + + // do everything weak linked (because we will keep <10.8 compatibility) + id userNotification = [[NSClassFromString(@"NSUserNotification") alloc] init]; + [userNotification performSelector:@selector(setTitle:) withObject:titleMac]; + [userNotification performSelector:@selector(setInformativeText:) withObject:textMac]; + + id notificationCenterInstance = [NSClassFromString(@"NSUserNotificationCenter") performSelector:@selector(defaultUserNotificationCenter)]; + [notificationCenterInstance performSelector:@selector(deliverNotification:) withObject:userNotification]; + + [titleMac release]; + [textMac release]; + [userNotification release]; + } +} + +// sendAppleScript just take a QString and executes it as apple script +void MacNotificationHandler::sendAppleScript(const QString &script) +{ + QByteArray utf8 = script.toUtf8(); + char* cString = (char *)utf8.constData(); + NSString *scriptApple = [[NSString alloc] initWithUTF8String:cString]; + + NSAppleScript *as = [[NSAppleScript alloc] initWithSource:scriptApple]; + NSDictionary *err = nil; + [as executeAndReturnError:&err]; + [as release]; + [scriptApple release]; +} + +bool MacNotificationHandler::hasUserNotificationCenterSupport(void) +{ + Class possibleClass = NSClassFromString(@"NSUserNotificationCenter"); + + // check if users OS has support for NSUserNotification + if(possibleClass!=nil) { + return true; + } + return false; +} + + +MacNotificationHandler *MacNotificationHandler::instance() +{ + static MacNotificationHandler *s_instance = NULL; + if (!s_instance) + s_instance = new MacNotificationHandler(); + return s_instance; +} diff --git a/src/qt/monitoreddatamapper.cpp b/src/qt/monitoreddatamapper.cpp new file mode 100644 index 0000000..4c20183 --- /dev/null +++ b/src/qt/monitoreddatamapper.cpp @@ -0,0 +1,35 @@ +#include "monitoreddatamapper.h" + +#include +#include +#include + +MonitoredDataMapper::MonitoredDataMapper(QObject *parent) : + QDataWidgetMapper(parent) +{ +} + +void MonitoredDataMapper::addMapping(QWidget *widget, int section) +{ + QDataWidgetMapper::addMapping(widget, section); + addChangeMonitor(widget); +} + +void MonitoredDataMapper::addMapping(QWidget *widget, int section, const QByteArray &propertyName) +{ + QDataWidgetMapper::addMapping(widget, section, propertyName); + addChangeMonitor(widget); +} + +void MonitoredDataMapper::addChangeMonitor(QWidget *widget) +{ + // Watch user property of widget for changes, and connect + // the signal to our viewModified signal. + QMetaProperty prop = widget->metaObject()->userProperty(); + int signal = prop.notifySignalIndex(); + int method = this->metaObject()->indexOfMethod("viewModified()"); + if(signal != -1 && method != -1) + { + QMetaObject::connect(widget, signal, this, method); + } +} diff --git a/src/qt/monitoreddatamapper.h b/src/qt/monitoreddatamapper.h new file mode 100644 index 0000000..de55c86 --- /dev/null +++ b/src/qt/monitoreddatamapper.h @@ -0,0 +1,30 @@ +#ifndef MONITOREDDATAMAPPER_H +#define MONITOREDDATAMAPPER_H + +#include + +QT_BEGIN_NAMESPACE +class QWidget; +QT_END_NAMESPACE + +/** Data to Widget mapper that watches for edits and notifies listeners when a field is edited. + This can be used, for example, to enable a commit/apply button in a configuration dialog. + */ +class MonitoredDataMapper : public QDataWidgetMapper +{ + Q_OBJECT + +public: + explicit MonitoredDataMapper(QObject *parent=0); + + void addMapping(QWidget *widget, int section); + void addMapping(QWidget *widget, int section, const QByteArray &propertyName); + +private: + void addChangeMonitor(QWidget *widget); + +signals: + void viewModified(); +}; + +#endif // MONITOREDDATAMAPPER_H diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp new file mode 100644 index 0000000..7cfaef6 --- /dev/null +++ b/src/qt/notificator.cpp @@ -0,0 +1,317 @@ +#include "notificator.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_DBUS +#include +#include +#endif + +#ifdef Q_OS_MAC +#include +#include "macnotificationhandler.h" +#endif + +// https://wiki.ubuntu.com/NotificationDevelopmentGuidelines recommends at least 128 +const int FREEDESKTOP_NOTIFICATION_ICON_SIZE = 128; + +Notificator::Notificator(const QString &programName, QSystemTrayIcon *trayicon, QWidget *parent): + QObject(parent), + parent(parent), + programName(programName), + mode(None), + trayIcon(trayicon) +#ifdef USE_DBUS + ,interface(0) +#endif +{ + if(trayicon && trayicon->supportsMessages()) + { + mode = QSystemTray; + } +#ifdef USE_DBUS + interface = new QDBusInterface("org.freedesktop.Notifications", + "/org/freedesktop/Notifications", "org.freedesktop.Notifications"); + if(interface->isValid()) + { + mode = Freedesktop; + } +#endif +#ifdef Q_OS_MAC + // check if users OS has support for NSUserNotification + if( MacNotificationHandler::instance()->hasUserNotificationCenterSupport()) { + mode = UserNotificationCenter; + } + else { + // Check if Growl is installed (based on Qt's tray icon implementation) + CFURLRef cfurl; + OSStatus status = LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator, CFSTR("growlTicket"), kLSRolesAll, 0, &cfurl); + if (status != kLSApplicationNotFoundErr) { + CFBundleRef bundle = CFBundleCreate(0, cfurl); + if (CFStringCompare(CFBundleGetIdentifier(bundle), CFSTR("com.Growl.GrowlHelperApp"), kCFCompareCaseInsensitive | kCFCompareBackwards) == kCFCompareEqualTo) { + if (CFStringHasSuffix(CFURLGetString(cfurl), CFSTR("/Growl.app/"))) + mode = Growl13; + else + mode = Growl12; + } + CFRelease(cfurl); + CFRelease(bundle); + } + } +#endif +} + +Notificator::~Notificator() +{ +#ifdef USE_DBUS + delete interface; +#endif +} + +#ifdef USE_DBUS + +// Loosely based on http://www.qtcentre.org/archive/index.php/t-25879.html +class FreedesktopImage +{ +public: + FreedesktopImage() {} + FreedesktopImage(const QImage &img); + + static int metaType(); + + // Image to variant that can be marshalled over DBus + static QVariant toVariant(const QImage &img); + +private: + int width, height, stride; + bool hasAlpha; + int channels; + int bitsPerSample; + QByteArray image; + + friend QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i); + friend const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i); +}; + +Q_DECLARE_METATYPE(FreedesktopImage); + +// Image configuration settings +const int CHANNELS = 4; +const int BYTES_PER_PIXEL = 4; +const int BITS_PER_SAMPLE = 8; + +FreedesktopImage::FreedesktopImage(const QImage &img): + width(img.width()), + height(img.height()), + stride(img.width() * BYTES_PER_PIXEL), + hasAlpha(true), + channels(CHANNELS), + bitsPerSample(BITS_PER_SAMPLE) +{ + // Convert 00xAARRGGBB to RGBA bytewise (endian-independent) format + QImage tmp = img.convertToFormat(QImage::Format_ARGB32); + const uint32_t *data = reinterpret_cast(tmp.bits()); + + unsigned int num_pixels = width * height; + image.resize(num_pixels * BYTES_PER_PIXEL); + + for(unsigned int ptr = 0; ptr < num_pixels; ++ptr) + { + image[ptr*BYTES_PER_PIXEL+0] = data[ptr] >> 16; // R + image[ptr*BYTES_PER_PIXEL+1] = data[ptr] >> 8; // G + image[ptr*BYTES_PER_PIXEL+2] = data[ptr]; // B + image[ptr*BYTES_PER_PIXEL+3] = data[ptr] >> 24; // A + } +} + +QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i) +{ + a.beginStructure(); + a << i.width << i.height << i.stride << i.hasAlpha << i.bitsPerSample << i.channels << i.image; + a.endStructure(); + return a; +} + +const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i) +{ + a.beginStructure(); + a >> i.width >> i.height >> i.stride >> i.hasAlpha >> i.bitsPerSample >> i.channels >> i.image; + a.endStructure(); + return a; +} + +int FreedesktopImage::metaType() +{ + return qDBusRegisterMetaType(); +} + +QVariant FreedesktopImage::toVariant(const QImage &img) +{ + FreedesktopImage fimg(img); + return QVariant(FreedesktopImage::metaType(), &fimg); +} + +void Notificator::notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout) +{ + Q_UNUSED(cls); + // Arguments for DBus call: + QList args; + + // Program Name: + args.append(programName); + + // Unique ID of this notification type: + args.append(0U); + + // Application Icon, empty string + args.append(QString()); + + // Summary + args.append(title); + + // Body + args.append(text); + + // Actions (none, actions are deprecated) + QStringList actions; + args.append(actions); + + // Hints + QVariantMap hints; + + // If no icon specified, set icon based on class + QIcon tmpicon; + if(icon.isNull()) + { + QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion; + switch(cls) + { + case Information: sicon = QStyle::SP_MessageBoxInformation; break; + case Warning: sicon = QStyle::SP_MessageBoxWarning; break; + case Critical: sicon = QStyle::SP_MessageBoxCritical; break; + default: break; + } + tmpicon = QApplication::style()->standardIcon(sicon); + } + else + { + tmpicon = icon; + } + hints["icon_data"] = FreedesktopImage::toVariant(tmpicon.pixmap(FREEDESKTOP_NOTIFICATION_ICON_SIZE).toImage()); + args.append(hints); + + // Timeout (in msec) + args.append(millisTimeout); + + // "Fire and forget" + interface->callWithArgumentList(QDBus::NoBlock, "Notify", args); +} +#endif + +void Notificator::notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout) +{ + Q_UNUSED(icon); + QSystemTrayIcon::MessageIcon sicon = QSystemTrayIcon::NoIcon; + switch(cls) // Set icon based on class + { + case Information: sicon = QSystemTrayIcon::Information; break; + case Warning: sicon = QSystemTrayIcon::Warning; break; + case Critical: sicon = QSystemTrayIcon::Critical; break; + } + trayIcon->showMessage(title, text, sicon, millisTimeout); +} + +// Based on Qt's tray icon implementation +#ifdef Q_OS_MAC +void Notificator::notifyGrowl(Class cls, const QString &title, const QString &text, const QIcon &icon) +{ + const QString script( + "tell application \"%5\"\n" + " set the allNotificationsList to {\"Notification\"}\n" // -- Make a list of all the notification types (all) + " set the enabledNotificationsList to {\"Notification\"}\n" // -- Make a list of the notifications (enabled) + " register as application \"%1\" all notifications allNotificationsList default notifications enabledNotificationsList\n" // -- Register our script with Growl + " notify with name \"Notification\" title \"%2\" description \"%3\" application name \"%1\"%4\n" // -- Send a Notification + "end tell" + ); + + QString notificationApp(QApplication::applicationName()); + if (notificationApp.isEmpty()) + notificationApp = "Application"; + + QPixmap notificationIconPixmap; + if (icon.isNull()) { // If no icon specified, set icon based on class + QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion; + switch (cls) + { + case Information: sicon = QStyle::SP_MessageBoxInformation; break; + case Warning: sicon = QStyle::SP_MessageBoxWarning; break; + case Critical: sicon = QStyle::SP_MessageBoxCritical; break; + } + notificationIconPixmap = QApplication::style()->standardPixmap(sicon); + } + else { + QSize size = icon.actualSize(QSize(48, 48)); + notificationIconPixmap = icon.pixmap(size); + } + + QString notificationIcon; + QTemporaryFile notificationIconFile; + if (!notificationIconPixmap.isNull() && notificationIconFile.open()) { + QImageWriter writer(¬ificationIconFile, "PNG"); + if (writer.write(notificationIconPixmap.toImage())) + notificationIcon = QString(" image from location \"file://%1\"").arg(notificationIconFile.fileName()); + } + + QString quotedTitle(title), quotedText(text); + quotedTitle.replace("\\", "\\\\").replace("\"", "\\"); + quotedText.replace("\\", "\\\\").replace("\"", "\\"); + QString growlApp(this->mode == Notificator::Growl13 ? "Growl" : "GrowlHelperApp"); + MacNotificationHandler::instance()->sendAppleScript(script.arg(notificationApp, quotedTitle, quotedText, notificationIcon, growlApp)); +} + +void Notificator::notifyMacUserNotificationCenter(Class cls, const QString &title, const QString &text, const QIcon &icon) { + // icon is not supported by the user notification center yet. OSX will use the app icon. + MacNotificationHandler::instance()->showNotification(title, text); +} + +#endif + +void Notificator::notify(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout) +{ + switch(mode) + { +#ifdef USE_DBUS + case Freedesktop: + notifyDBus(cls, title, text, icon, millisTimeout); + break; +#endif + case QSystemTray: + notifySystray(cls, title, text, icon, millisTimeout); + break; +#ifdef Q_OS_MAC + case UserNotificationCenter: + notifyMacUserNotificationCenter(cls, title, text, icon); + break; + case Growl12: + case Growl13: + notifyGrowl(cls, title, text, icon); + break; +#endif + default: + if(cls == Critical) + { + // Fall back to old fashioned pop-up dialog if critical and no other notification available + QMessageBox::critical(parent, title, text, QMessageBox::Ok, QMessageBox::Ok); + } + break; + } +} diff --git a/src/qt/notificator.h b/src/qt/notificator.h new file mode 100644 index 0000000..d1fe37f --- /dev/null +++ b/src/qt/notificator.h @@ -0,0 +1,71 @@ +#ifndef NOTIFICATOR_H +#define NOTIFICATOR_H + +#include +#include + +QT_BEGIN_NAMESPACE +class QSystemTrayIcon; +#ifdef USE_DBUS +class QDBusInterface; +#endif +QT_END_NAMESPACE + +/** Cross-platform desktop notification client. */ +class Notificator: public QObject +{ + Q_OBJECT + +public: + /** Create a new notificator. + @note Ownership of trayIcon is not transferred to this object. + */ + Notificator(const QString &programName=QString(), QSystemTrayIcon *trayIcon=0, QWidget *parent=0); + ~Notificator(); + + // Message class + enum Class + { + Information, /**< Informational message */ + Warning, /**< Notify user of potential problem */ + Critical /**< An error occurred */ + }; + +public slots: + /** Show notification message. + @param[in] cls general message class + @param[in] title title shown with message + @param[in] text message content + @param[in] icon optional icon to show with message + @param[in] millisTimeout notification timeout in milliseconds (defaults to 10 seconds) + @note Platform implementations are free to ignore any of the provided fields except for \a text. + */ + void notify(Class cls, const QString &title, const QString &text, + const QIcon &icon = QIcon(), int millisTimeout = 10000); + +private: + QWidget *parent; + enum Mode { + None, /**< Ignore informational notifications, and show a modal pop-up dialog for Critical notifications. */ + Freedesktop, /**< Use DBus org.freedesktop.Notifications */ + QSystemTray, /**< Use QSystemTray::showMessage */ + Growl12, /**< Use the Growl 1.2 notification system (Mac only) */ + Growl13, /**< Use the Growl 1.3 notification system (Mac only) */ + UserNotificationCenter /**< Use the 10.8+ User Notification Center (Mac only) */ + }; + QString programName; + Mode mode; + QSystemTrayIcon *trayIcon; +#ifdef USE_DBUS + QDBusInterface *interface; + + void notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout); +#endif + void notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout); +#ifdef Q_OS_MAC + void notifyGrowl(Class cls, const QString &title, const QString &text, const QIcon &icon); + void notifyMacUserNotificationCenter(Class cls, const QString &title, const QString &text, const QIcon &icon); +#endif +}; + +#endif // NOTIFICATOR_H diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp new file mode 100644 index 0000000..ff4108c --- /dev/null +++ b/src/qt/optionsdialog.cpp @@ -0,0 +1,282 @@ +#include "optionsdialog.h" +#include "ui_optionsdialog.h" + +#include "bitcoinunits.h" +#include "monitoreddatamapper.h" +#include "netbase.h" +#include "optionsmodel.h" + +#include +#include +#include +#include + +OptionsDialog::OptionsDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::OptionsDialog), + model(0), + mapper(0), + fRestartWarningDisplayed_Proxy(false), + fRestartWarningDisplayed_Lang(false), + fProxyIpValid(true) +{ + ui->setupUi(this); + + /* Network elements init */ +#ifndef USE_UPNP + ui->mapPortUpnp->setEnabled(false); +#endif + + ui->proxyIp->setEnabled(false); + ui->proxyPort->setEnabled(false); + ui->proxyPort->setValidator(new QIntValidator(1, 65535, this)); + + ui->socksVersion->setEnabled(false); + ui->socksVersion->addItem("5", 5); + ui->socksVersion->addItem("4", 4); + ui->socksVersion->setCurrentIndex(0); + + connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyIp, SLOT(setEnabled(bool))); + connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyPort, SLOT(setEnabled(bool))); + connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->socksVersion, SLOT(setEnabled(bool))); + connect(ui->connectSocks, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning_Proxy())); + + ui->proxyIp->installEventFilter(this); + + /* Window elements init */ +#ifdef Q_OS_MAC + /* remove Window tab on Mac */ + ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabWindow)); +#endif + + /* Display elements init */ + QDir translations(":translations"); + ui->lang->addItem(QString("(") + tr("default") + QString(")"), QVariant("")); + foreach(const QString &langStr, translations.entryList()) + { + QLocale locale(langStr); + + /** check if the locale name consists of 2 parts (language_country) */ + if(langStr.contains("_")) + { +#if QT_VERSION >= 0x040800 + /** display language strings as "native language - native country (locale name)", e.g. "Deutsch - Deutschland (de)" */ + ui->lang->addItem(locale.nativeLanguageName() + QString(" - ") + locale.nativeCountryName() + QString(" (") + langStr + QString(")"), QVariant(langStr)); +#else + /** display language strings as "language - country (locale name)", e.g. "German - Germany (de)" */ + ui->lang->addItem(QLocale::languageToString(locale.language()) + QString(" - ") + QLocale::countryToString(locale.country()) + QString(" (") + langStr + QString(")"), QVariant(langStr)); +#endif + } + else + { +#if QT_VERSION >= 0x040800 + /** display language strings as "native language (locale name)", e.g. "Deutsch (de)" */ + ui->lang->addItem(locale.nativeLanguageName() + QString(" (") + langStr + QString(")"), QVariant(langStr)); +#else + /** display language strings as "language (locale name)", e.g. "German (de)" */ + ui->lang->addItem(QLocale::languageToString(locale.language()) + QString(" (") + langStr + QString(")"), QVariant(langStr)); +#endif + } + } + + ui->unit->setModel(new BitcoinUnits(this)); + + /* Widget-to-option mapper */ + mapper = new MonitoredDataMapper(this); + mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); + mapper->setOrientation(Qt::Vertical); + + /* enable apply button when data modified */ + connect(mapper, SIGNAL(viewModified()), this, SLOT(enableApplyButton())); + /* disable apply button when new data loaded */ + connect(mapper, SIGNAL(currentIndexChanged(int)), this, SLOT(disableApplyButton())); + /* setup/change UI elements when proxy IP is invalid/valid */ + connect(this, SIGNAL(proxyIpValid(QValidatedLineEdit *, bool)), this, SLOT(handleProxyIpValid(QValidatedLineEdit *, bool))); +} + +OptionsDialog::~OptionsDialog() +{ + delete ui; +} + +void OptionsDialog::setModel(OptionsModel *model) +{ + this->model = model; + + if(model) + { + connect(model, SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + + mapper->setModel(model); + setMapper(); + mapper->toFirst(); + } + + /* update the display unit, to not use the default ("BTC") */ + updateDisplayUnit(); + + /* warn only when language selection changes by user action (placed here so init via mapper doesn't trigger this) */ + connect(ui->lang, SIGNAL(valueChanged()), this, SLOT(showRestartWarning_Lang())); + + /* disable apply button after settings are loaded as there is nothing to save */ + disableApplyButton(); +} + +void OptionsDialog::setMapper() +{ + /* Main */ + mapper->addMapping(ui->transactionFee, OptionsModel::Fee); + mapper->addMapping(ui->bitcoinAtStartup, OptionsModel::StartAtStartup); + + /* Network */ + mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP); + + mapper->addMapping(ui->connectSocks, OptionsModel::ProxyUse); + mapper->addMapping(ui->proxyIp, OptionsModel::ProxyIP); + mapper->addMapping(ui->proxyPort, OptionsModel::ProxyPort); + mapper->addMapping(ui->socksVersion, OptionsModel::ProxySocksVersion); + + /* Window */ +#ifndef Q_OS_MAC + mapper->addMapping(ui->minimizeToTray, OptionsModel::MinimizeToTray); + mapper->addMapping(ui->minimizeOnClose, OptionsModel::MinimizeOnClose); +#endif + + /* Display */ + mapper->addMapping(ui->lang, OptionsModel::Language); + mapper->addMapping(ui->unit, OptionsModel::DisplayUnit); + mapper->addMapping(ui->displayAddresses, OptionsModel::DisplayAddresses); + mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures); +} + +void OptionsDialog::enableApplyButton() +{ + ui->applyButton->setEnabled(true); +} + +void OptionsDialog::disableApplyButton() +{ + ui->applyButton->setEnabled(false); +} + +void OptionsDialog::enableSaveButtons() +{ + /* prevent enabling of the save buttons when data modified, if there is an invalid proxy address present */ + if(fProxyIpValid) + setSaveButtonState(true); +} + +void OptionsDialog::disableSaveButtons() +{ + setSaveButtonState(false); +} + +void OptionsDialog::setSaveButtonState(bool fState) +{ + ui->applyButton->setEnabled(fState); + ui->okButton->setEnabled(fState); +} + +void OptionsDialog::on_resetButton_clicked() +{ + if(model) + { + // confirmation dialog + QMessageBox::StandardButton btnRetVal = QMessageBox::question(this, tr("Confirm options reset"), + tr("Some settings may require a client restart to take effect.") + "

" + tr("Do you want to proceed?"), + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); + + if(btnRetVal == QMessageBox::Cancel) + return; + + disableApplyButton(); + + /* disable restart warning messages display */ + fRestartWarningDisplayed_Lang = fRestartWarningDisplayed_Proxy = true; + + /* reset all options and save the default values (QSettings) */ + model->Reset(); + mapper->toFirst(); + mapper->submit(); + + /* re-enable restart warning messages display */ + fRestartWarningDisplayed_Lang = fRestartWarningDisplayed_Proxy = false; + } +} + +void OptionsDialog::on_okButton_clicked() +{ + mapper->submit(); + accept(); +} + +void OptionsDialog::on_cancelButton_clicked() +{ + reject(); +} + +void OptionsDialog::on_applyButton_clicked() +{ + mapper->submit(); + disableApplyButton(); +} + +void OptionsDialog::showRestartWarning_Proxy() +{ + if(!fRestartWarningDisplayed_Proxy) + { + QMessageBox::warning(this, tr("Warning"), tr("This setting will take effect after restarting CryptoLottoToken."), QMessageBox::Ok); + fRestartWarningDisplayed_Proxy = true; + } +} + +void OptionsDialog::showRestartWarning_Lang() +{ + if(!fRestartWarningDisplayed_Lang) + { + QMessageBox::warning(this, tr("Warning"), tr("This setting will take effect after restarting CryptoLottoToken."), QMessageBox::Ok); + fRestartWarningDisplayed_Lang = true; + } +} + +void OptionsDialog::updateDisplayUnit() +{ + if(model) + { + /* Update transactionFee with the current unit */ + ui->transactionFee->setDisplayUnit(model->getDisplayUnit()); + } +} + +void OptionsDialog::handleProxyIpValid(QValidatedLineEdit *object, bool fState) +{ + // this is used in a check before re-enabling the save buttons + fProxyIpValid = fState; + + if(fProxyIpValid) + { + enableSaveButtons(); + ui->statusLabel->clear(); + } + else + { + disableSaveButtons(); + object->setValid(fProxyIpValid); + ui->statusLabel->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel->setText(tr("The supplied proxy address is invalid.")); + } +} + +bool OptionsDialog::eventFilter(QObject *object, QEvent *event) +{ + if(event->type() == QEvent::FocusOut) + { + if(object == ui->proxyIp) + { + CService addr; + /* Check proxyIp for a valid IPv4/IPv6 address and emit the proxyIpValid signal */ + emit proxyIpValid(ui->proxyIp, LookupNumeric(ui->proxyIp->text().toStdString().c_str(), addr)); + } + } + return QDialog::eventFilter(object, event); +} diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h new file mode 100644 index 0000000..d64ed0b --- /dev/null +++ b/src/qt/optionsdialog.h @@ -0,0 +1,61 @@ +#ifndef OPTIONSDIALOG_H +#define OPTIONSDIALOG_H + +#include + +namespace Ui { +class OptionsDialog; +} +class OptionsModel; +class MonitoredDataMapper; +class QValidatedLineEdit; + +/** Preferences dialog. */ +class OptionsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit OptionsDialog(QWidget *parent = 0); + ~OptionsDialog(); + + void setModel(OptionsModel *model); + void setMapper(); + +protected: + bool eventFilter(QObject *object, QEvent *event); + +private slots: + /* enable only apply button */ + void enableApplyButton(); + /* disable only apply button */ + void disableApplyButton(); + /* enable apply button and OK button */ + void enableSaveButtons(); + /* disable apply button and OK button */ + void disableSaveButtons(); + /* set apply button and OK button state (enabled / disabled) */ + void setSaveButtonState(bool fState); + void on_resetButton_clicked(); + void on_okButton_clicked(); + void on_cancelButton_clicked(); + void on_applyButton_clicked(); + + void showRestartWarning_Proxy(); + void showRestartWarning_Lang(); + void updateDisplayUnit(); + void handleProxyIpValid(QValidatedLineEdit *object, bool fState); + +signals: + void proxyIpValid(QValidatedLineEdit *object, bool fValid); + +private: + Ui::OptionsDialog *ui; + OptionsModel *model; + MonitoredDataMapper *mapper; + bool fRestartWarningDisplayed_Proxy; + bool fRestartWarningDisplayed_Lang; + bool fProxyIpValid; +}; + +#endif // OPTIONSDIALOG_H diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp new file mode 100644 index 0000000..4f60f96 --- /dev/null +++ b/src/qt/optionsmodel.cpp @@ -0,0 +1,308 @@ +#include "optionsmodel.h" + +#include "bitcoinunits.h" +#include "init.h" +#include "walletdb.h" +#include "guiutil.h" + +#include + +OptionsModel::OptionsModel(QObject *parent) : + QAbstractListModel(parent) +{ + Init(); +} + +bool static ApplyProxySettings() +{ + QSettings settings; + CService addrProxy(settings.value("addrProxy", "127.0.0.1:9050").toString().toStdString()); + int nSocksVersion(settings.value("nSocksVersion", 5).toInt()); + if (!settings.value("fUseProxy", false).toBool()) { + addrProxy = CService(); + nSocksVersion = 0; + return false; + } + if (nSocksVersion && !addrProxy.IsValid()) + return false; + if (!IsLimited(NET_IPV4)) + SetProxy(NET_IPV4, addrProxy, nSocksVersion); + if (nSocksVersion > 4) { +#ifdef USE_IPV6 + if (!IsLimited(NET_IPV6)) + SetProxy(NET_IPV6, addrProxy, nSocksVersion); +#endif + SetNameProxy(addrProxy, nSocksVersion); + } + return true; +} + +void OptionsModel::Init() +{ + QSettings settings; + + // These are Qt-only settings: + nDisplayUnit = settings.value("nDisplayUnit", BitcoinUnits::BTC).toInt(); + bDisplayAddresses = settings.value("bDisplayAddresses", false).toBool(); + fMinimizeToTray = settings.value("fMinimizeToTray", false).toBool(); + fMinimizeOnClose = settings.value("fMinimizeOnClose", false).toBool(); + nTransactionFee = settings.value("nTransactionFee").toLongLong(); + language = settings.value("language", "").toString(); + fCoinControlFeatures = settings.value("fCoinControlFeatures", false).toBool(); + + // These are shared with core Bitcoin; we want + // command-line options to override the GUI settings: + if (settings.contains("fUseUPnP")) + SoftSetBoolArg("-upnp", settings.value("fUseUPnP").toBool()); + if (settings.contains("addrProxy") && settings.value("fUseProxy").toBool()) + SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString()); + if (settings.contains("nSocksVersion") && settings.value("fUseProxy").toBool()) + SoftSetArg("-socks", settings.value("nSocksVersion").toString().toStdString()); + if (!language.isEmpty()) + SoftSetArg("-lang", language.toStdString()); +} + +void OptionsModel::Reset() +{ + QSettings settings; + + // Remove all entries in this QSettings object + settings.clear(); + + // default setting for OptionsModel::StartAtStartup - disabled + if (GUIUtil::GetStartOnSystemStartup()) + GUIUtil::SetStartOnSystemStartup(false); + + // Re-Init to get default values + Init(); + + // Ensure Upgrade() is not running again by setting the bImportFinished flag + settings.setValue("bImportFinished", true); +} + +bool OptionsModel::Upgrade() +{ + QSettings settings; + + if (settings.contains("bImportFinished")) + return false; // Already upgraded + + settings.setValue("bImportFinished", true); + + // Move settings from old wallet.dat (if any): + CWalletDB walletdb("wallet.dat"); + + QList intOptions; + intOptions << "nDisplayUnit" << "nTransactionFee"; + foreach(QString key, intOptions) + { + int value = 0; + if (walletdb.ReadSetting(key.toStdString(), value)) + { + settings.setValue(key, value); + walletdb.EraseSetting(key.toStdString()); + } + } + QList boolOptions; + boolOptions << "bDisplayAddresses" << "fMinimizeToTray" << "fMinimizeOnClose" << "fUseProxy" << "fUseUPnP"; + foreach(QString key, boolOptions) + { + bool value = false; + if (walletdb.ReadSetting(key.toStdString(), value)) + { + settings.setValue(key, value); + walletdb.EraseSetting(key.toStdString()); + } + } + try + { + CAddress addrProxyAddress; + if (walletdb.ReadSetting("addrProxy", addrProxyAddress)) + { + settings.setValue("addrProxy", addrProxyAddress.ToStringIPPort().c_str()); + walletdb.EraseSetting("addrProxy"); + } + } + catch (std::ios_base::failure &e) + { + // 0.6.0rc1 saved this as a CService, which causes failure when parsing as a CAddress + CService addrProxy; + if (walletdb.ReadSetting("addrProxy", addrProxy)) + { + settings.setValue("addrProxy", addrProxy.ToStringIPPort().c_str()); + walletdb.EraseSetting("addrProxy"); + } + } + ApplyProxySettings(); + Init(); + + return true; +} + + +int OptionsModel::rowCount(const QModelIndex & parent) const +{ + return OptionIDRowCount; +} + +QVariant OptionsModel::data(const QModelIndex & index, int role) const +{ + if(role == Qt::EditRole) + { + QSettings settings; + switch(index.row()) + { + case StartAtStartup: + return QVariant(GUIUtil::GetStartOnSystemStartup()); + case MinimizeToTray: + return QVariant(fMinimizeToTray); + case MapPortUPnP: +#ifdef USE_UPNP + return settings.value("fUseUPnP", GetBoolArg("-upnp", true)); +#else + return QVariant(false); +#endif + case MinimizeOnClose: + return QVariant(fMinimizeOnClose); + case ProxyUse: { + proxyType proxy; + return QVariant(GetProxy(NET_IPV4, proxy)); + } + case ProxyIP: { + proxyType proxy; + if (GetProxy(NET_IPV4, proxy)) + return QVariant(QString::fromStdString(proxy.first.ToStringIP())); + else + return QVariant(QString::fromStdString("127.0.0.1")); + } + case ProxyPort: { + proxyType proxy; + if (GetProxy(NET_IPV4, proxy)) + return QVariant(proxy.first.GetPort()); + else + return QVariant(9050); + } + case ProxySocksVersion: { + proxyType proxy; + if (GetProxy(NET_IPV4, proxy)) + return QVariant(proxy.second); + else + return QVariant(5); + } + case Fee: + return QVariant(nTransactionFee); + case DisplayUnit: + return QVariant(nDisplayUnit); + case DisplayAddresses: + return QVariant(bDisplayAddresses); + case Language: + return settings.value("language", ""); + case CoinControlFeatures: + return QVariant(fCoinControlFeatures); + default: + return QVariant(); + } + } + return QVariant(); +} + +bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, int role) +{ + bool successful = true; /* set to false on parse error */ + if(role == Qt::EditRole) + { + QSettings settings; + switch(index.row()) + { + case StartAtStartup: + successful = GUIUtil::SetStartOnSystemStartup(value.toBool()); + break; + case MinimizeToTray: + fMinimizeToTray = value.toBool(); + settings.setValue("fMinimizeToTray", fMinimizeToTray); + break; + case MapPortUPnP: + settings.setValue("fUseUPnP", value.toBool()); + MapPort(value.toBool()); + break; + case MinimizeOnClose: + fMinimizeOnClose = value.toBool(); + settings.setValue("fMinimizeOnClose", fMinimizeOnClose); + break; + case ProxyUse: + settings.setValue("fUseProxy", value.toBool()); + successful = ApplyProxySettings(); + break; + case ProxyIP: { + proxyType proxy; + proxy.first = CService("127.0.0.1", 9050); + GetProxy(NET_IPV4, proxy); + + CNetAddr addr(value.toString().toStdString()); + proxy.first.SetIP(addr); + settings.setValue("addrProxy", proxy.first.ToStringIPPort().c_str()); + successful = ApplyProxySettings(); + } + break; + case ProxyPort: { + proxyType proxy; + proxy.first = CService("127.0.0.1", 9050); + GetProxy(NET_IPV4, proxy); + + proxy.first.SetPort(value.toInt()); + settings.setValue("addrProxy", proxy.first.ToStringIPPort().c_str()); + successful = ApplyProxySettings(); + } + break; + case ProxySocksVersion: { + proxyType proxy; + proxy.second = 5; + GetProxy(NET_IPV4, proxy); + + proxy.second = value.toInt(); + settings.setValue("nSocksVersion", proxy.second); + successful = ApplyProxySettings(); + } + break; + case Fee: + nTransactionFee = value.toLongLong(); + settings.setValue("nTransactionFee", nTransactionFee); + emit transactionFeeChanged(nTransactionFee); + break; + case DisplayUnit: + nDisplayUnit = value.toInt(); + settings.setValue("nDisplayUnit", nDisplayUnit); + emit displayUnitChanged(nDisplayUnit); + break; + case DisplayAddresses: + bDisplayAddresses = value.toBool(); + settings.setValue("bDisplayAddresses", bDisplayAddresses); + break; + case Language: + settings.setValue("language", value); + break; + case CoinControlFeatures: { + fCoinControlFeatures = value.toBool(); + settings.setValue("fCoinControlFeatures", fCoinControlFeatures); + emit coinControlFeaturesChanged(fCoinControlFeatures); + } + break; + default: + break; + } + } + emit dataChanged(index, index); + + return successful; +} + +qint64 OptionsModel::getTransactionFee() +{ + return nTransactionFee; +} + +bool OptionsModel::getCoinControlFeatures() +{ + return fCoinControlFeatures; +} + diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h new file mode 100644 index 0000000..be94c1a --- /dev/null +++ b/src/qt/optionsmodel.h @@ -0,0 +1,69 @@ +#ifndef OPTIONSMODEL_H +#define OPTIONSMODEL_H + +#include + +/** Interface from Qt to configuration data structure for Bitcoin client. + To Qt, the options are presented as a list with the different options + laid out vertically. + This can be changed to a tree once the settings become sufficiently + complex. + */ +class OptionsModel : public QAbstractListModel +{ + Q_OBJECT + +public: + explicit OptionsModel(QObject *parent = 0); + + enum OptionID { + StartAtStartup, // bool + MinimizeToTray, // bool + MapPortUPnP, // bool + MinimizeOnClose, // bool + ProxyUse, // bool + ProxyIP, // QString + ProxyPort, // int + ProxySocksVersion, // int + Fee, // qint64 + DisplayUnit, // BitcoinUnits::Unit + DisplayAddresses, // bool + Language, // QString + CoinControlFeatures, // bool + OptionIDRowCount, + }; + + void Init(); + void Reset(); + + /* Migrate settings from wallet.dat after app initialization */ + bool Upgrade(); /* returns true if settings upgraded */ + + int rowCount(const QModelIndex & parent = QModelIndex()) const; + QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; + bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole); + + /* Explicit getters */ + qint64 getTransactionFee(); + bool getMinimizeToTray() { return fMinimizeToTray; } + bool getMinimizeOnClose() { return fMinimizeOnClose; } + int getDisplayUnit() { return nDisplayUnit; } + bool getDisplayAddresses() { return bDisplayAddresses; } + QString getLanguage() { return language; } + bool getCoinControlFeatures(); + +private: + int nDisplayUnit; + bool bDisplayAddresses; + bool fMinimizeToTray; + bool fMinimizeOnClose; + QString language; + bool fCoinControlFeatures; + +signals: + void displayUnitChanged(int unit); + void transactionFeeChanged(qint64); + void coinControlFeaturesChanged(bool); +}; + +#endif // OPTIONSMODEL_H diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp new file mode 100644 index 0000000..ad2d682 --- /dev/null +++ b/src/qt/overviewpage.cpp @@ -0,0 +1,213 @@ +#include "overviewpage.h" +#include "ui_overviewpage.h" + +#include "clientmodel.h" +#include "walletmodel.h" +#include "bitcoinunits.h" +#include "optionsmodel.h" +#include "transactiontablemodel.h" +#include "transactionfilterproxy.h" +#include "guiutil.h" +#include "guiconstants.h" + +#include +#include + +#define DECORATION_SIZE 64 +#define NUM_ITEMS 3 + +class TxViewDelegate : public QAbstractItemDelegate +{ + Q_OBJECT +public: + TxViewDelegate(): QAbstractItemDelegate(), unit(BitcoinUnits::BTC) + { + + } + + inline void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index ) const + { + painter->save(); + + QIcon icon = qvariant_cast(index.data(Qt::DecorationRole)); + QRect mainRect = option.rect; + QRect decorationRect(mainRect.topLeft(), QSize(DECORATION_SIZE, DECORATION_SIZE)); + int xspace = DECORATION_SIZE + 8; + int ypad = 6; + int halfheight = (mainRect.height() - 2*ypad)/2; + QRect amountRect(mainRect.left() + xspace, mainRect.top()+ypad, mainRect.width() - xspace, halfheight); + QRect addressRect(mainRect.left() + xspace, mainRect.top()+ypad+halfheight, mainRect.width() - xspace, halfheight); + icon.paint(painter, decorationRect); + + QDateTime date = index.data(TransactionTableModel::DateRole).toDateTime(); + QString address = index.data(Qt::DisplayRole).toString(); + qint64 amount = index.data(TransactionTableModel::AmountRole).toLongLong(); + bool confirmed = index.data(TransactionTableModel::ConfirmedRole).toBool(); + QVariant value = index.data(Qt::ForegroundRole); + QColor foreground = option.palette.color(QPalette::Text); + if(value.canConvert()) + { + QBrush brush = qvariant_cast(value); + foreground = brush.color(); + } + + painter->setPen(foreground); + painter->drawText(addressRect, Qt::AlignLeft|Qt::AlignVCenter, address); + + if(amount < 0) + { + foreground = COLOR_NEGATIVE; + } + else if(!confirmed) + { + foreground = COLOR_UNCONFIRMED; + } + else + { + foreground = option.palette.color(QPalette::Text); + } + painter->setPen(foreground); + QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true); + if(!confirmed) + { + amountText = QString("[") + amountText + QString("]"); + } + painter->drawText(amountRect, Qt::AlignRight|Qt::AlignVCenter, amountText); + + painter->setPen(option.palette.color(QPalette::Text)); + painter->drawText(amountRect, Qt::AlignLeft|Qt::AlignVCenter, GUIUtil::dateTimeStr(date)); + + painter->restore(); + } + + inline QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const + { + return QSize(DECORATION_SIZE, DECORATION_SIZE); + } + + int unit; + +}; +#include "overviewpage.moc" + +OverviewPage::OverviewPage(QWidget *parent) : + QWidget(parent), + ui(new Ui::OverviewPage), + clientModel(0), + walletModel(0), + currentBalance(-1), + currentUnconfirmedBalance(-1), + currentImmatureBalance(-1), + txdelegate(new TxViewDelegate()), + filter(0) +{ + ui->setupUi(this); + + // Recent transactions + ui->listTransactions->setItemDelegate(txdelegate); + ui->listTransactions->setIconSize(QSize(DECORATION_SIZE, DECORATION_SIZE)); + ui->listTransactions->setMinimumHeight(NUM_ITEMS * (DECORATION_SIZE + 2)); + ui->listTransactions->setAttribute(Qt::WA_MacShowFocusRect, false); + + 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); +} + +void OverviewPage::handleTransactionClicked(const QModelIndex &index) +{ + if(filter) + emit transactionClicked(filter->mapToSource(index)); +} + +OverviewPage::~OverviewPage() +{ + delete ui; +} + +void OverviewPage::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance) +{ + int unit = walletModel->getOptionsModel()->getDisplayUnit(); + currentBalance = balance; + currentUnconfirmedBalance = unconfirmedBalance; + currentImmatureBalance = immatureBalance; + ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance)); + ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance)); + ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, immatureBalance)); + + // only show immature (newly mined) balance if it's non-zero, so as not to complicate things + // for the non-mining users + bool showImmature = immatureBalance != 0; + ui->labelImmature->setVisible(showImmature); + ui->labelImmatureText->setVisible(showImmature); +} + +void OverviewPage::setClientModel(ClientModel *model) +{ + this->clientModel = model; + if(model) + { + // Show warning if this is a prerelease version + connect(model, SIGNAL(alertsChanged(QString)), this, SLOT(updateAlerts(QString))); + updateAlerts(model->getStatusBarWarnings()); + } +} + +void OverviewPage::setWalletModel(WalletModel *model) +{ + this->walletModel = model; + if(model && model->getOptionsModel()) + { + // Set up transaction list + filter = new TransactionFilterProxy(); + filter->setSourceModel(model->getTransactionTableModel()); + filter->setLimit(NUM_ITEMS); + filter->setDynamicSortFilter(true); + filter->setSortRole(Qt::EditRole); + filter->sort(TransactionTableModel::Status, Qt::DescendingOrder); + + ui->listTransactions->setModel(filter); + ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress); + + // Keep up to date with wallet + setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance()); + connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64))); + + connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + } + + // update the display unit, to not use the default ("BTC") + updateDisplayUnit(); +} + +void OverviewPage::updateDisplayUnit() +{ + if(walletModel && walletModel->getOptionsModel()) + { + if(currentBalance != -1) + setBalance(currentBalance, currentUnconfirmedBalance, currentImmatureBalance); + + // Update txdelegate->unit with the current unit + txdelegate->unit = walletModel->getOptionsModel()->getDisplayUnit(); + + ui->listTransactions->update(); + } +} + +void OverviewPage::updateAlerts(const QString &warnings) +{ + this->ui->labelAlerts->setVisible(!warnings.isEmpty()); + this->ui->labelAlerts->setText(warnings); +} + +void OverviewPage::showOutOfSyncWarning(bool fShow) +{ + ui->labelWalletStatus->setVisible(fShow); + ui->labelTransactionsStatus->setVisible(fShow); +} diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h new file mode 100644 index 0000000..59ba3c6 --- /dev/null +++ b/src/qt/overviewpage.h @@ -0,0 +1,54 @@ +#ifndef OVERVIEWPAGE_H +#define OVERVIEWPAGE_H + +#include + +namespace Ui { + class OverviewPage; +} +class ClientModel; +class WalletModel; +class TxViewDelegate; +class TransactionFilterProxy; + +QT_BEGIN_NAMESPACE +class QModelIndex; +QT_END_NAMESPACE + +/** Overview ("home") page widget */ +class OverviewPage : public QWidget +{ + Q_OBJECT + +public: + explicit OverviewPage(QWidget *parent = 0); + ~OverviewPage(); + + void setClientModel(ClientModel *clientModel); + void setWalletModel(WalletModel *walletModel); + void showOutOfSyncWarning(bool fShow); + +public slots: + void setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance); + +signals: + void transactionClicked(const QModelIndex &index); + +private: + Ui::OverviewPage *ui; + ClientModel *clientModel; + WalletModel *walletModel; + qint64 currentBalance; + qint64 currentUnconfirmedBalance; + qint64 currentImmatureBalance; + + TxViewDelegate *txdelegate; + TransactionFilterProxy *filter; + +private slots: + void updateDisplayUnit(); + void handleTransactionClicked(const QModelIndex &index); + void updateAlerts(const QString &warnings); +}; + +#endif // OVERVIEWPAGE_H diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp new file mode 100644 index 0000000..999f847 --- /dev/null +++ b/src/qt/paymentserver.cpp @@ -0,0 +1,162 @@ +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include "paymentserver.h" + +#include "guiconstants.h" +#include "ui_interface.h" +#include "util.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#if QT_VERSION < 0x050000 +#include +#endif + +using namespace boost; + +const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds +const QString BITCOIN_IPC_PREFIX("CryptoLottoToken:"); + +// +// Create a name that is unique for: +// testnet / non-testnet +// data directory +// +static QString ipcServerName() +{ + QString name("BitcoinQt"); + + // Append a simple hash of the datadir + // Note that GetDataDir(true) returns a different path + // for -testnet versus main net + QString ddir(GetDataDir(true).string().c_str()); + name.append(QString::number(qHash(ddir))); + + return name; +} + +// +// This stores payment requests received before +// the main GUI window is up and ready to ask the user +// to send payment. +// +static QStringList savedPaymentRequests; + +// +// Sending to the server is done synchronously, at startup. +// If the server isn't already running, startup continues, +// and the items in savedPaymentRequest will be handled +// when uiReady() is called. +// +bool PaymentServer::ipcSendCommandLine() +{ + bool fResult = false; + + const QStringList& args = qApp->arguments(); + for (int i = 1; i < args.size(); i++) + { + if (!args[i].startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) + continue; + savedPaymentRequests.append(args[i]); + } + + foreach (const QString& arg, savedPaymentRequests) + { + QLocalSocket* socket = new QLocalSocket(); + socket->connectToServer(ipcServerName(), QIODevice::WriteOnly); + if (!socket->waitForConnected(BITCOIN_IPC_CONNECT_TIMEOUT)) + return false; + + QByteArray block; + QDataStream out(&block, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_0); + out << arg; + out.device()->seek(0); + socket->write(block); + socket->flush(); + + socket->waitForBytesWritten(BITCOIN_IPC_CONNECT_TIMEOUT); + socket->disconnectFromServer(); + delete socket; + fResult = true; + } + return fResult; +} + +PaymentServer::PaymentServer(QApplication* parent) : QObject(parent), saveURIs(true) +{ + // Install global event filter to catch QFileOpenEvents on the mac (sent when you click bitcoin: links) + parent->installEventFilter(this); + + QString name = ipcServerName(); + + // Clean up old socket leftover from a crash: + QLocalServer::removeServer(name); + + uriServer = new QLocalServer(this); + + if (!uriServer->listen(name)) + qDebug() << tr("Cannot start CryptoLottoToken: click-to-pay handler"); + else + connect(uriServer, SIGNAL(newConnection()), this, SLOT(handleURIConnection())); +} + +bool PaymentServer::eventFilter(QObject *object, QEvent *event) +{ + // clicking on bitcoin: URLs creates FileOpen events on the Mac: + if (event->type() == QEvent::FileOpen) + { + QFileOpenEvent* fileEvent = static_cast(event); + if (!fileEvent->url().isEmpty()) + { + if (saveURIs) // Before main window is ready: + savedPaymentRequests.append(fileEvent->url().toString()); + else + emit receivedURI(fileEvent->url().toString()); + return true; + } + } + return false; +} + +void PaymentServer::uiReady() +{ + saveURIs = false; + foreach (const QString& s, savedPaymentRequests) + emit receivedURI(s); + savedPaymentRequests.clear(); +} + +void PaymentServer::handleURIConnection() +{ + QLocalSocket *clientConnection = uriServer->nextPendingConnection(); + + while (clientConnection->bytesAvailable() < (int)sizeof(quint32)) + clientConnection->waitForReadyRead(); + + connect(clientConnection, SIGNAL(disconnected()), + clientConnection, SLOT(deleteLater())); + + QDataStream in(clientConnection); + in.setVersion(QDataStream::Qt_4_0); + if (clientConnection->bytesAvailable() < (int)sizeof(quint16)) { + return; + } + QString message; + in >> message; + + if (saveURIs) + savedPaymentRequests.append(message); + else + emit receivedURI(message); +} diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h new file mode 100644 index 0000000..1f2fdd4 --- /dev/null +++ b/src/qt/paymentserver.h @@ -0,0 +1,67 @@ +#ifndef PAYMENTSERVER_H +#define PAYMENTSERVER_H + +// +// This class handles payment requests from clicking on +// bitcoin: URIs +// +// This is somewhat tricky, because we have to deal with +// the situation where the user clicks on a link during +// startup/initialization, when the splash-screen is up +// but the main window (and the Send Coins tab) is not. +// +// So, the strategy is: +// +// Create the server, and register the event handler, +// when the application is created. Save any URIs +// received at or during startup in a list. +// +// When startup is finished and the main window is +// show, a signal is sent to slot uiReady(), which +// emits a receivedURL() signal for any payment +// requests that happened during startup. +// +// After startup, receivedURL() happens as usual. +// +// This class has one more feature: a static +// method that finds URIs passed in the command line +// and, if a server is running in another process, +// sends them to the server. +// +#include +#include + +class QApplication; +class QLocalServer; + +class PaymentServer : public QObject +{ + Q_OBJECT + +private: + bool saveURIs; + QLocalServer* uriServer; + +public: + // Returns true if there were URIs on the command line + // which were successfully sent to an already-running + // process. + static bool ipcSendCommandLine(); + + PaymentServer(QApplication* parent); + + bool eventFilter(QObject *object, QEvent *event); + +signals: + void receivedURI(QString); + +public slots: + // Signal this when the main window's UI is ready + // to display payment requests to the user + void uiReady(); + +private slots: + void handleURIConnection(); +}; + +#endif // PAYMENTSERVER_H diff --git a/src/qt/qrcodedialog.cpp b/src/qt/qrcodedialog.cpp new file mode 100644 index 0000000..0730aff --- /dev/null +++ b/src/qt/qrcodedialog.cpp @@ -0,0 +1,173 @@ +#include "qrcodedialog.h" +#include "ui_qrcodedialog.h" + +#include "bitcoinunits.h" +#include "guiconstants.h" +#include "guiutil.h" +#include "optionsmodel.h" + +#include +#if QT_VERSION < 0x050000 +#include +#endif + +#include + +QRCodeDialog::QRCodeDialog(const QString &addr, const QString &label, bool enableReq, QWidget *parent) : + QDialog(parent), + ui(new Ui::QRCodeDialog), + model(0), + address(addr) +{ + ui->setupUi(this); + + setWindowTitle(QString("%1").arg(address)); + + ui->chkReqPayment->setVisible(enableReq); + ui->lblAmount->setVisible(enableReq); + ui->lnReqAmount->setVisible(enableReq); + + ui->lnLabel->setText(label); + + ui->btnSaveAs->setEnabled(false); + + genCode(); +} + +QRCodeDialog::~QRCodeDialog() +{ + delete ui; +} + +void QRCodeDialog::setModel(OptionsModel *model) +{ + this->model = model; + + if (model) + connect(model, SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + + // update the display unit, to not use the default ("BTC") + updateDisplayUnit(); +} + +void QRCodeDialog::genCode() +{ + QString uri = getURI(); + + if (uri != "") + { + ui->lblQRCode->setText(""); + + QRcode *code = QRcode_encodeString(uri.toUtf8().constData(), 0, QR_ECLEVEL_L, QR_MODE_8, 1); + if (!code) + { + ui->lblQRCode->setText(tr("Error encoding URI into QR Code.")); + return; + } + myImage = QImage(code->width + 8, code->width + 8, QImage::Format_RGB32); + myImage.fill(0xffffff); + unsigned char *p = code->data; + for (int y = 0; y < code->width; y++) + { + for (int x = 0; x < code->width; x++) + { + myImage.setPixel(x + 4, y + 4, ((*p & 1) ? 0x0 : 0xffffff)); + p++; + } + } + QRcode_free(code); + + ui->lblQRCode->setPixmap(QPixmap::fromImage(myImage).scaled(300, 300)); + + ui->outUri->setPlainText(uri); + } +} + +QString QRCodeDialog::getURI() +{ + QString ret = QString("CryptoLottoToken:%1").arg(address); + int paramCount = 0; + + ui->outUri->clear(); + + if (ui->chkReqPayment->isChecked()) + { + if (ui->lnReqAmount->validate()) + { + // even if we allow a non BTC unit input in lnReqAmount, we generate the URI with BTC as unit (as defined in BIP21) + ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, ui->lnReqAmount->value())); + paramCount++; + } + else + { + ui->btnSaveAs->setEnabled(false); + ui->lblQRCode->setText(tr("The entered amount is invalid, please check.")); + return QString(""); + } + } + + if (!ui->lnLabel->text().isEmpty()) + { + QString lbl(QUrl::toPercentEncoding(ui->lnLabel->text())); + ret += QString("%1label=%2").arg(paramCount == 0 ? "?" : "&").arg(lbl); + paramCount++; + } + + if (!ui->lnMessage->text().isEmpty()) + { + QString msg(QUrl::toPercentEncoding(ui->lnMessage->text())); + ret += QString("%1message=%2").arg(paramCount == 0 ? "?" : "&").arg(msg); + paramCount++; + } + + // limit URI length to prevent a DoS against the QR-Code dialog + if (ret.length() > MAX_URI_LENGTH) + { + ui->btnSaveAs->setEnabled(false); + ui->lblQRCode->setText(tr("Resulting URI too long, try to reduce the text for label / message.")); + return QString(""); + } + + ui->btnSaveAs->setEnabled(true); + return ret; +} + +void QRCodeDialog::on_lnReqAmount_textChanged() +{ + genCode(); +} + +void QRCodeDialog::on_lnLabel_textChanged() +{ + genCode(); +} + +void QRCodeDialog::on_lnMessage_textChanged() +{ + genCode(); +} + +void QRCodeDialog::on_btnSaveAs_clicked() +{ + QString fn = GUIUtil::getSaveFileName(this, tr("Save QR Code"), QString(), tr("PNG Images (*.png)")); + if (!fn.isEmpty()) + myImage.scaled(EXPORT_IMAGE_SIZE, EXPORT_IMAGE_SIZE).save(fn); +} + +void QRCodeDialog::on_chkReqPayment_toggled(bool fChecked) +{ + if (!fChecked) + // if chkReqPayment is not active, don't display lnReqAmount as invalid + ui->lnReqAmount->setValid(true); + + genCode(); +} + +void QRCodeDialog::updateDisplayUnit() +{ + if (model) + { + // Update lnReqAmount with the current unit + ui->lnReqAmount->setDisplayUnit(model->getDisplayUnit()); + } +} diff --git a/src/qt/qrcodedialog.h b/src/qt/qrcodedialog.h new file mode 100644 index 0000000..c55c34b --- /dev/null +++ b/src/qt/qrcodedialog.h @@ -0,0 +1,41 @@ +#ifndef QRCODEDIALOG_H +#define QRCODEDIALOG_H + +#include +#include + +namespace Ui { + class QRCodeDialog; +} +class OptionsModel; + +class QRCodeDialog : public QDialog +{ + Q_OBJECT + +public: + explicit QRCodeDialog(const QString &addr, const QString &label, bool enableReq, QWidget *parent = 0); + ~QRCodeDialog(); + + void setModel(OptionsModel *model); + +private slots: + void on_lnReqAmount_textChanged(); + void on_lnLabel_textChanged(); + void on_lnMessage_textChanged(); + void on_btnSaveAs_clicked(); + void on_chkReqPayment_toggled(bool fChecked); + + void updateDisplayUnit(); + +private: + Ui::QRCodeDialog *ui; + OptionsModel *model; + QString address; + QImage myImage; + + void genCode(); + QString getURI(); +}; + +#endif // QRCODEDIALOG_H diff --git a/src/qt/qvalidatedlineedit.cpp b/src/qt/qvalidatedlineedit.cpp new file mode 100644 index 0000000..8ca230c --- /dev/null +++ b/src/qt/qvalidatedlineedit.cpp @@ -0,0 +1,45 @@ +#include "qvalidatedlineedit.h" + +#include "guiconstants.h" + +QValidatedLineEdit::QValidatedLineEdit(QWidget *parent) : + QLineEdit(parent), valid(true) +{ + connect(this, SIGNAL(textChanged(QString)), this, SLOT(markValid())); +} + +void QValidatedLineEdit::setValid(bool valid) +{ + if(valid == this->valid) + { + return; + } + + if(valid) + { + setStyleSheet(""); + } + else + { + setStyleSheet(STYLE_INVALID); + } + this->valid = valid; +} + +void QValidatedLineEdit::focusInEvent(QFocusEvent *evt) +{ + // Clear invalid flag on focus + setValid(true); + QLineEdit::focusInEvent(evt); +} + +void QValidatedLineEdit::markValid() +{ + setValid(true); +} + +void QValidatedLineEdit::clear() +{ + setValid(true); + QLineEdit::clear(); +} diff --git a/src/qt/qvalidatedlineedit.h b/src/qt/qvalidatedlineedit.h new file mode 100644 index 0000000..ec74633 --- /dev/null +++ b/src/qt/qvalidatedlineedit.h @@ -0,0 +1,30 @@ +#ifndef QVALIDATEDLINEEDIT_H +#define QVALIDATEDLINEEDIT_H + +#include + +/** Line edit that can be marked as "invalid" to show input validation feedback. When marked as invalid, + it will get a red background until it is focused. + */ +class QValidatedLineEdit : public QLineEdit +{ + Q_OBJECT + +public: + explicit QValidatedLineEdit(QWidget *parent = 0); + void clear(); + +protected: + void focusInEvent(QFocusEvent *evt); + +private: + bool valid; + +public slots: + void setValid(bool valid); + +private slots: + void markValid(); +}; + +#endif // QVALIDATEDLINEEDIT_H diff --git a/src/qt/qvaluecombobox.cpp b/src/qt/qvaluecombobox.cpp new file mode 100644 index 0000000..d7ce3d0 --- /dev/null +++ b/src/qt/qvaluecombobox.cpp @@ -0,0 +1,27 @@ +#include "qvaluecombobox.h" + +QValueComboBox::QValueComboBox(QWidget *parent) : + QComboBox(parent), role(Qt::UserRole) +{ + connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(handleSelectionChanged(int))); +} + +QVariant QValueComboBox::value() const +{ + return itemData(currentIndex(), role); +} + +void QValueComboBox::setValue(const QVariant &value) +{ + setCurrentIndex(findData(value, role)); +} + +void QValueComboBox::setRole(int role) +{ + this->role = role; +} + +void QValueComboBox::handleSelectionChanged(int idx) +{ + emit valueChanged(); +} diff --git a/src/qt/qvaluecombobox.h b/src/qt/qvaluecombobox.h new file mode 100644 index 0000000..64a7da9 --- /dev/null +++ b/src/qt/qvaluecombobox.h @@ -0,0 +1,33 @@ +#ifndef QVALUECOMBOBOX_H +#define QVALUECOMBOBOX_H + +#include +#include + +/* QComboBox that can be used with QDataWidgetMapper to select ordinal values from a model. */ +class QValueComboBox : public QComboBox +{ + Q_OBJECT + + Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged USER true) + +public: + explicit QValueComboBox(QWidget *parent = 0); + + QVariant value() const; + void setValue(const QVariant &value); + + /** Specify model role to use as ordinal value (defaults to Qt::UserRole) */ + void setRole(int role); + +signals: + void valueChanged(); + +private: + int role; + +private slots: + void handleSelectionChanged(int idx); +}; + +#endif // QVALUECOMBOBOX_H diff --git a/src/qt/res/bitcoin-qt.rc b/src/qt/res/bitcoin-qt.rc new file mode 100644 index 0000000..21f67fd --- /dev/null +++ b/src/qt/res/bitcoin-qt.rc @@ -0,0 +1,39 @@ +IDI_ICON1 ICON DISCARDABLE "icons/bitcoin.ico" +IDI_ICON2 ICON DISCARDABLE "icons/bitcoin.ico" + +#include // 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 +#define COPYRIGHT_STR "2009-" STRINGIZE(COPYRIGHT_YEAR) " The Bitcoin developers 2011-" STRINGIZE(COPYRIGHT_YEAR) " The CryptoLottoToken developers" + +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", "CryptoLottoToken" + VALUE "FileDescription", "CryptoLottoToken-Qt (OSS GUI client for CryptoLottoToken)" + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "InternalName", "CryptoLottoToken-qt" + VALUE "LegalCopyright", COPYRIGHT_STR + VALUE "LegalTrademarks1", "Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php." + VALUE "OriginalFilename", "CryptoLottoToken-qt.exe" + VALUE "ProductName", "CryptoLottoToken-Qt" + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1252 // language neutral - multilingual (decimal) + END +END diff --git a/src/qt/res/icons/128x.png b/src/qt/res/icons/128x.png new file mode 100644 index 0000000..5da6a7c Binary files /dev/null and b/src/qt/res/icons/128x.png differ diff --git a/src/qt/res/icons/512x.png b/src/qt/res/icons/512x.png new file mode 100644 index 0000000..345e018 Binary files /dev/null and b/src/qt/res/icons/512x.png differ diff --git a/src/qt/res/icons/add.png b/src/qt/res/icons/add.png new file mode 100644 index 0000000..f98e2a8 Binary files /dev/null and b/src/qt/res/icons/add.png differ diff --git a/src/qt/res/icons/address-book.png b/src/qt/res/icons/address-book.png new file mode 100644 index 0000000..d41dbe6 Binary files /dev/null and b/src/qt/res/icons/address-book.png differ diff --git a/src/qt/res/icons/bitcoin.icns b/src/qt/res/icons/bitcoin.icns new file mode 100644 index 0000000..99b461d Binary files /dev/null and b/src/qt/res/icons/bitcoin.icns differ diff --git a/src/qt/res/icons/bitcoin.ico b/src/qt/res/icons/bitcoin.ico new file mode 100644 index 0000000..19425b0 Binary files /dev/null and b/src/qt/res/icons/bitcoin.ico differ diff --git a/src/qt/res/icons/bitcoin.png b/src/qt/res/icons/bitcoin.png new file mode 100644 index 0000000..a1f847d Binary files /dev/null and b/src/qt/res/icons/bitcoin.png differ diff --git a/src/qt/res/icons/clock1.png b/src/qt/res/icons/clock1.png new file mode 100644 index 0000000..448e47f Binary files /dev/null and b/src/qt/res/icons/clock1.png differ diff --git a/src/qt/res/icons/clock2.png b/src/qt/res/icons/clock2.png new file mode 100644 index 0000000..c1a6e99 Binary files /dev/null and b/src/qt/res/icons/clock2.png differ diff --git a/src/qt/res/icons/clock3.png b/src/qt/res/icons/clock3.png new file mode 100644 index 0000000..e429a40 Binary files /dev/null and b/src/qt/res/icons/clock3.png differ diff --git a/src/qt/res/icons/clock4.png b/src/qt/res/icons/clock4.png new file mode 100644 index 0000000..ba036f4 Binary files /dev/null and b/src/qt/res/icons/clock4.png differ diff --git a/src/qt/res/icons/clock5.png b/src/qt/res/icons/clock5.png new file mode 100644 index 0000000..411d7a7 Binary files /dev/null and b/src/qt/res/icons/clock5.png differ diff --git a/src/qt/res/icons/configure.png b/src/qt/res/icons/configure.png new file mode 100644 index 0000000..95bd319 Binary files /dev/null and b/src/qt/res/icons/configure.png differ diff --git a/src/qt/res/icons/connect0_16.png b/src/qt/res/icons/connect0_16.png new file mode 100644 index 0000000..cebd84a Binary files /dev/null and b/src/qt/res/icons/connect0_16.png differ diff --git a/src/qt/res/icons/connect1_16.png b/src/qt/res/icons/connect1_16.png new file mode 100644 index 0000000..4514ae6 Binary files /dev/null and b/src/qt/res/icons/connect1_16.png differ diff --git a/src/qt/res/icons/connect2_16.png b/src/qt/res/icons/connect2_16.png new file mode 100644 index 0000000..82e597c Binary files /dev/null and b/src/qt/res/icons/connect2_16.png differ diff --git a/src/qt/res/icons/connect3_16.png b/src/qt/res/icons/connect3_16.png new file mode 100644 index 0000000..3d9d5d2 Binary files /dev/null and b/src/qt/res/icons/connect3_16.png differ diff --git a/src/qt/res/icons/connect4_16.png b/src/qt/res/icons/connect4_16.png new file mode 100644 index 0000000..6813b5a Binary files /dev/null and b/src/qt/res/icons/connect4_16.png differ diff --git a/src/qt/res/icons/debugwindow.png b/src/qt/res/icons/debugwindow.png new file mode 100644 index 0000000..1712adf Binary files /dev/null and b/src/qt/res/icons/debugwindow.png differ diff --git a/src/qt/res/icons/edit.png b/src/qt/res/icons/edit.png new file mode 100644 index 0000000..1d69145 Binary files /dev/null and b/src/qt/res/icons/edit.png differ diff --git a/src/qt/res/icons/editcopy.png b/src/qt/res/icons/editcopy.png new file mode 100644 index 0000000..f882aa2 Binary files /dev/null and b/src/qt/res/icons/editcopy.png differ diff --git a/src/qt/res/icons/editpaste.png b/src/qt/res/icons/editpaste.png new file mode 100644 index 0000000..a192060 Binary files /dev/null and b/src/qt/res/icons/editpaste.png differ diff --git a/src/qt/res/icons/export.png b/src/qt/res/icons/export.png new file mode 100644 index 0000000..67b8c5c Binary files /dev/null and b/src/qt/res/icons/export.png differ diff --git a/src/qt/res/icons/filesave.png b/src/qt/res/icons/filesave.png new file mode 100644 index 0000000..ae13a15 Binary files /dev/null and b/src/qt/res/icons/filesave.png differ diff --git a/src/qt/res/icons/history.png b/src/qt/res/icons/history.png new file mode 100644 index 0000000..5405a80 Binary files /dev/null and b/src/qt/res/icons/history.png differ diff --git a/src/qt/res/icons/key.png b/src/qt/res/icons/key.png new file mode 100644 index 0000000..ece0164 Binary files /dev/null and b/src/qt/res/icons/key.png differ diff --git a/src/qt/res/icons/lock_closed.png b/src/qt/res/icons/lock_closed.png new file mode 100644 index 0000000..c566510 Binary files /dev/null and b/src/qt/res/icons/lock_closed.png differ diff --git a/src/qt/res/icons/lock_open.png b/src/qt/res/icons/lock_open.png new file mode 100644 index 0000000..c98ca86 Binary files /dev/null and b/src/qt/res/icons/lock_open.png differ diff --git a/src/qt/res/icons/mining.png b/src/qt/res/icons/mining.png new file mode 100644 index 0000000..72b067a Binary files /dev/null and b/src/qt/res/icons/mining.png differ diff --git a/src/qt/res/icons/mining_active.png b/src/qt/res/icons/mining_active.png new file mode 100644 index 0000000..af91858 Binary files /dev/null and b/src/qt/res/icons/mining_active.png differ diff --git a/src/qt/res/icons/mining_inactive.png b/src/qt/res/icons/mining_inactive.png new file mode 100644 index 0000000..227f505 Binary files /dev/null and b/src/qt/res/icons/mining_inactive.png differ diff --git a/src/qt/res/icons/notsynced.png b/src/qt/res/icons/notsynced.png new file mode 100644 index 0000000..c9e7118 Binary files /dev/null and b/src/qt/res/icons/notsynced.png differ diff --git a/src/qt/res/icons/overview.png b/src/qt/res/icons/overview.png new file mode 100644 index 0000000..110ede3 Binary files /dev/null and b/src/qt/res/icons/overview.png differ diff --git a/src/qt/res/icons/qrcode.png b/src/qt/res/icons/qrcode.png new file mode 100644 index 0000000..a8d9717 Binary files /dev/null and b/src/qt/res/icons/qrcode.png differ diff --git a/src/qt/res/icons/quit.png b/src/qt/res/icons/quit.png new file mode 100644 index 0000000..0dde6f3 Binary files /dev/null and b/src/qt/res/icons/quit.png differ diff --git a/src/qt/res/icons/receive.png b/src/qt/res/icons/receive.png new file mode 100644 index 0000000..4b72ff8 Binary files /dev/null and b/src/qt/res/icons/receive.png differ diff --git a/src/qt/res/icons/remove.png b/src/qt/res/icons/remove.png new file mode 100644 index 0000000..a44b6d1 Binary files /dev/null and b/src/qt/res/icons/remove.png differ diff --git a/src/qt/res/icons/send.png b/src/qt/res/icons/send.png new file mode 100644 index 0000000..dae2b71 Binary files /dev/null and b/src/qt/res/icons/send.png differ diff --git a/src/qt/res/icons/synced.png b/src/qt/res/icons/synced.png new file mode 100644 index 0000000..4d7e0e8 Binary files /dev/null and b/src/qt/res/icons/synced.png differ diff --git a/src/qt/res/icons/transaction0.png b/src/qt/res/icons/transaction0.png new file mode 100644 index 0000000..4578666 Binary files /dev/null and b/src/qt/res/icons/transaction0.png differ diff --git a/src/qt/res/icons/transaction2.png b/src/qt/res/icons/transaction2.png new file mode 100644 index 0000000..01bb558 Binary files /dev/null and b/src/qt/res/icons/transaction2.png differ diff --git a/src/qt/res/icons/transaction_conflicted.png b/src/qt/res/icons/transaction_conflicted.png new file mode 100644 index 0000000..51fff64 Binary files /dev/null and b/src/qt/res/icons/transaction_conflicted.png differ diff --git a/src/qt/res/icons/tx_inout.png b/src/qt/res/icons/tx_inout.png new file mode 100644 index 0000000..5f092f9 Binary files /dev/null and b/src/qt/res/icons/tx_inout.png differ diff --git a/src/qt/res/icons/tx_input.png b/src/qt/res/icons/tx_input.png new file mode 100644 index 0000000..6c5fb0e Binary files /dev/null and b/src/qt/res/icons/tx_input.png differ diff --git a/src/qt/res/icons/tx_mined.png b/src/qt/res/icons/tx_mined.png new file mode 100644 index 0000000..cedc61b Binary files /dev/null and b/src/qt/res/icons/tx_mined.png differ diff --git a/src/qt/res/icons/tx_output.png b/src/qt/res/icons/tx_output.png new file mode 100644 index 0000000..b42e717 Binary files /dev/null and b/src/qt/res/icons/tx_output.png differ diff --git a/src/qt/res/images/about.png b/src/qt/res/images/about.png new file mode 100644 index 0000000..6ba0478 Binary files /dev/null and b/src/qt/res/images/about.png differ diff --git a/src/qt/res/images/splash.png b/src/qt/res/images/splash.png new file mode 100644 index 0000000..a1f847d Binary files /dev/null and b/src/qt/res/images/splash.png differ diff --git a/src/qt/res/movies/update_spinner.mng b/src/qt/res/movies/update_spinner.mng new file mode 100644 index 0000000..7df3baa Binary files /dev/null and b/src/qt/res/movies/update_spinner.mng differ diff --git a/src/qt/res/src/bitcoin.svg b/src/qt/res/src/bitcoin.svg new file mode 100644 index 0000000..96f1017 --- /dev/null +++ b/src/qt/res/src/bitcoin.svg @@ -0,0 +1,115 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/clock1.svg b/src/qt/res/src/clock1.svg new file mode 100644 index 0000000..958915d --- /dev/null +++ b/src/qt/res/src/clock1.svg @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/clock2.svg b/src/qt/res/src/clock2.svg new file mode 100644 index 0000000..8abc91a --- /dev/null +++ b/src/qt/res/src/clock2.svg @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/clock3.svg b/src/qt/res/src/clock3.svg new file mode 100644 index 0000000..3edd9ba --- /dev/null +++ b/src/qt/res/src/clock3.svg @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/clock4.svg b/src/qt/res/src/clock4.svg new file mode 100644 index 0000000..241dec8 --- /dev/null +++ b/src/qt/res/src/clock4.svg @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/clock5.svg b/src/qt/res/src/clock5.svg new file mode 100644 index 0000000..befa608 --- /dev/null +++ b/src/qt/res/src/clock5.svg @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/clock_green.svg b/src/qt/res/src/clock_green.svg new file mode 100644 index 0000000..dac5c8a --- /dev/null +++ b/src/qt/res/src/clock_green.svg @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/inout.svg b/src/qt/res/src/inout.svg new file mode 100644 index 0000000..bfab8ef --- /dev/null +++ b/src/qt/res/src/inout.svg @@ -0,0 +1,122 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/src/qt/res/src/questionmark.svg b/src/qt/res/src/questionmark.svg new file mode 100644 index 0000000..c03c159 --- /dev/null +++ b/src/qt/res/src/questionmark.svg @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + ? + ? + + + ? + + diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp new file mode 100644 index 0000000..80d728d --- /dev/null +++ b/src/qt/rpcconsole.cpp @@ -0,0 +1,429 @@ +#include "rpcconsole.h" +#include "ui_rpcconsole.h" + +#include "clientmodel.h" +#include "bitcoinrpc.h" +#include "guiutil.h" + +#include +#include +#include +#if QT_VERSION < 0x050000 +#include +#endif +#include + +#include + +// TODO: add a scrollback limit, as there is currently none +// TODO: make it possible to filter out categories (esp debug messages when implemented) +// TODO: receive errors and debug messages through ClientModel + +const int CONSOLE_HISTORY = 50; +const QSize ICON_SIZE(24, 24); + +const struct { + const char *url; + const char *source; +} ICON_MAPPING[] = { + {"cmd-request", ":/icons/tx_input"}, + {"cmd-reply", ":/icons/tx_output"}, + {"cmd-error", ":/icons/tx_output"}, + {"misc", ":/icons/tx_inout"}, + {NULL, NULL} +}; + +/* Object for executing console RPC commands in a separate thread. +*/ +class RPCExecutor : public QObject +{ + Q_OBJECT + +public slots: + void request(const QString &command); + +signals: + void reply(int category, const QString &command); +}; + +#include "rpcconsole.moc" + +/** + * Split shell command line into a list of arguments. Aims to emulate \c bash and friends. + * + * - Arguments are delimited with whitespace + * - Extra whitespace at the beginning and end and between arguments will be ignored + * - Text can be "double" or 'single' quoted + * - The backslash \c \ is used as escape character + * - Outside quotes, any character can be escaped + * - Within double quotes, only escape \c " and backslashes before a \c " or another backslash + * - Within single quotes, no escaping is possible and no special interpretation takes place + * + * @param[out] args Parsed arguments will be appended to this list + * @param[in] strCommand Command line to split + */ +bool parseCommandLine(std::vector &args, const std::string &strCommand) +{ + enum CmdParseState + { + STATE_EATING_SPACES, + STATE_ARGUMENT, + STATE_SINGLEQUOTED, + STATE_DOUBLEQUOTED, + STATE_ESCAPE_OUTER, + STATE_ESCAPE_DOUBLEQUOTED + } state = STATE_EATING_SPACES; + std::string curarg; + foreach(char ch, strCommand) + { + switch(state) + { + case STATE_ARGUMENT: // In or after argument + case STATE_EATING_SPACES: // Handle runs of whitespace + switch(ch) + { + case '"': state = STATE_DOUBLEQUOTED; break; + case '\'': state = STATE_SINGLEQUOTED; break; + case '\\': state = STATE_ESCAPE_OUTER; break; + case ' ': case '\n': case '\t': + if(state == STATE_ARGUMENT) // Space ends argument + { + args.push_back(curarg); + curarg.clear(); + } + state = STATE_EATING_SPACES; + break; + default: curarg += ch; state = STATE_ARGUMENT; + } + break; + case STATE_SINGLEQUOTED: // Single-quoted string + switch(ch) + { + case '\'': state = STATE_ARGUMENT; break; + default: curarg += ch; + } + break; + case STATE_DOUBLEQUOTED: // Double-quoted string + switch(ch) + { + case '"': state = STATE_ARGUMENT; break; + case '\\': state = STATE_ESCAPE_DOUBLEQUOTED; break; + default: curarg += ch; + } + break; + case STATE_ESCAPE_OUTER: // '\' outside quotes + curarg += ch; state = STATE_ARGUMENT; + break; + case STATE_ESCAPE_DOUBLEQUOTED: // '\' in double-quoted text + if(ch != '"' && ch != '\\') curarg += '\\'; // keep '\' for everything but the quote and '\' itself + curarg += ch; state = STATE_DOUBLEQUOTED; + break; + } + } + switch(state) // final state + { + case STATE_EATING_SPACES: + return true; + case STATE_ARGUMENT: + args.push_back(curarg); + return true; + default: // ERROR to end in one of the other states + return false; + } +} + +void RPCExecutor::request(const QString &command) +{ + std::vector args; + if(!parseCommandLine(args, command.toStdString())) + { + emit reply(RPCConsole::CMD_ERROR, QString("Parse error: unbalanced ' or \"")); + return; + } + if(args.empty()) + return; // Nothing to do + try + { + 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( + args[0], + RPCConvertValues(args[0], std::vector(args.begin() + 1, args.end()))); + + // Format result reply + if (result.type() == json_spirit::null_type) + strPrint = ""; + else if (result.type() == json_spirit::str_type) + strPrint = result.get_str(); + else + strPrint = write_string(result, true); + + emit reply(RPCConsole::CMD_REPLY, QString::fromStdString(strPrint)); + } + catch (json_spirit::Object& objError) + { + try // Nice formatting for standard-format error + { + int code = find_value(objError, "code").get_int(); + std::string message = find_value(objError, "message").get_str(); + emit reply(RPCConsole::CMD_ERROR, QString::fromStdString(message) + " (code " + QString::number(code) + ")"); + } + catch(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))); + } + } + catch (std::exception& e) + { + emit reply(RPCConsole::CMD_ERROR, QString("Error: ") + QString::fromStdString(e.what())); + } +} + +RPCConsole::RPCConsole(QWidget *parent) : + QDialog(parent), + ui(new Ui::RPCConsole), + clientModel(0), + historyPtr(0) +{ + ui->setupUi(this); + +#ifndef Q_OS_MAC + ui->openDebugLogfileButton->setIcon(QIcon(":/icons/export")); + ui->showCLOptionsButton->setIcon(QIcon(":/icons/options")); +#endif + + // Install event filter for up and down arrow + ui->lineEdit->installEventFilter(this); + ui->messagesWidget->installEventFilter(this); + + connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear())); + + // set OpenSSL version label + ui->openSSLVersion->setText(SSLeay_version(SSLEAY_VERSION)); + + startExecutor(); + + clear(); +} + +RPCConsole::~RPCConsole() +{ + emit stopExecutor(); + delete ui; +} + +bool RPCConsole::eventFilter(QObject* obj, QEvent *event) +{ + if(event->type() == QEvent::KeyPress) // Special key handling + { + QKeyEvent *keyevt = static_cast(event); + int key = keyevt->key(); + Qt::KeyboardModifiers mod = keyevt->modifiers(); + switch(key) + { + case Qt::Key_Up: if(obj == ui->lineEdit) { browseHistory(-1); return true; } break; + case Qt::Key_Down: if(obj == ui->lineEdit) { browseHistory(1); return true; } break; + case Qt::Key_PageUp: /* pass paging keys to messages widget */ + case Qt::Key_PageDown: + if(obj == ui->lineEdit) + { + QApplication::postEvent(ui->messagesWidget, new QKeyEvent(*keyevt)); + return true; + } + break; + default: + // Typing in messages widget brings focus to line edit, and redirects key there + // Exclude most combinations and keys that emit no text, except paste shortcuts + if(obj == ui->messagesWidget && ( + (!mod && !keyevt->text().isEmpty() && key != Qt::Key_Tab) || + ((mod & Qt::ControlModifier) && key == Qt::Key_V) || + ((mod & Qt::ShiftModifier) && key == Qt::Key_Insert))) + { + ui->lineEdit->setFocus(); + QApplication::postEvent(ui->lineEdit, new QKeyEvent(*keyevt)); + return true; + } + } + } + return QDialog::eventFilter(obj, event); +} + +void RPCConsole::setClientModel(ClientModel *model) +{ + this->clientModel = model; + if(model) + { + // Subscribe to information, replies, messages, errors + connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); + connect(model, SIGNAL(numBlocksChanged(int,int)), this, SLOT(setNumBlocks(int,int))); + + // Provide initial values + ui->clientVersion->setText(model->formatFullVersion()); + ui->clientName->setText(model->clientName()); + ui->buildDate->setText(model->formatBuildDate()); + ui->startupTime->setText(model->formatClientStartupTime()); + + setNumConnections(model->getNumConnections()); + ui->isTestNet->setChecked(model->isTestNet()); + } +} + +static QString categoryClass(int category) +{ + switch(category) + { + case RPCConsole::CMD_REQUEST: return "cmd-request"; break; + case RPCConsole::CMD_REPLY: return "cmd-reply"; break; + case RPCConsole::CMD_ERROR: return "cmd-error"; break; + default: return "misc"; + } +} + +void RPCConsole::clear() +{ + ui->messagesWidget->clear(); + history.clear(); + historyPtr = 0; + ui->lineEdit->clear(); + ui->lineEdit->setFocus(); + + // Add smoothly scaled icon images. + // (when using width/height on an img, Qt uses nearest instead of linear interpolation) + for(int i=0; ICON_MAPPING[i].url; ++i) + { + ui->messagesWidget->document()->addResource( + QTextDocument::ImageResource, + QUrl(ICON_MAPPING[i].url), + QImage(ICON_MAPPING[i].source).scaled(ICON_SIZE, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + } + + // Set default style sheet + ui->messagesWidget->document()->setDefaultStyleSheet( + "table { }" + "td.time { color: #808080; padding-top: 3px; } " + "td.message { font-family: Monospace; font-size: 12px; } " + "td.cmd-request { color: #006060; } " + "td.cmd-error { color: red; } " + "b { color: #006060; } " + ); + + message(CMD_REPLY, (tr("Welcome to the CryptoLottoToken RPC console.") + "
" + + tr("Use up and down arrows to navigate history, and Ctrl-L to clear screen.") + "
" + + tr("Type help for an overview of available commands.")), true); +} + +void RPCConsole::message(int category, const QString &message, bool html) +{ + QTime time = QTime::currentTime(); + QString timeString = time.toString(); + QString out; + out += ""; + out += ""; + out += "
" + timeString + ""; + if(html) + out += message; + else + out += GUIUtil::HtmlEscape(message, true); + out += "
"; + ui->messagesWidget->append(out); +} + +void RPCConsole::setNumConnections(int count) +{ + ui->numberOfConnections->setText(QString::number(count)); +} + +void RPCConsole::setNumBlocks(int count, int countOfPeers) +{ + ui->numberOfBlocks->setText(QString::number(count)); + // If there is no current countOfPeers available display N/A instead of 0, which can't ever be true + ui->totalBlocks->setText(countOfPeers == 0 ? tr("N/A") : QString::number(countOfPeers)); + if(clientModel) + ui->lastBlockTime->setText(clientModel->getLastBlockDate().toString()); +} + +void RPCConsole::on_lineEdit_returnPressed() +{ + QString cmd = ui->lineEdit->text(); + ui->lineEdit->clear(); + + if(!cmd.isEmpty()) + { + message(CMD_REQUEST, cmd); + emit cmdRequest(cmd); + // Truncate history from current position + history.erase(history.begin() + historyPtr, history.end()); + // Append command to history + history.append(cmd); + // Enforce maximum history size + while(history.size() > CONSOLE_HISTORY) + history.removeFirst(); + // Set pointer to end of history + historyPtr = history.size(); + // Scroll console view to end + scrollToEnd(); + } +} + +void RPCConsole::browseHistory(int offset) +{ + historyPtr += offset; + if(historyPtr < 0) + historyPtr = 0; + if(historyPtr > history.size()) + historyPtr = history.size(); + QString cmd; + if(historyPtr < history.size()) + cmd = history.at(historyPtr); + ui->lineEdit->setText(cmd); +} + +void RPCConsole::startExecutor() +{ + QThread *thread = new QThread; + RPCExecutor *executor = new RPCExecutor(); + executor->moveToThread(thread); + + // Replies from executor object must go to this object + connect(executor, SIGNAL(reply(int,QString)), this, SLOT(message(int,QString))); + // Requests from this object must go to executor + connect(this, SIGNAL(cmdRequest(QString)), executor, SLOT(request(QString))); + + // On stopExecutor signal + // - queue executor for deletion (in execution thread) + // - quit the Qt event loop in the execution thread + connect(this, SIGNAL(stopExecutor()), executor, SLOT(deleteLater())); + connect(this, SIGNAL(stopExecutor()), thread, SLOT(quit())); + // Queue the thread for deletion (in this thread) when it is finished + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + + // Default implementation of QThread::run() simply spins up an event loop in the thread, + // which is what we want. + thread->start(); +} + +void RPCConsole::on_tabWidget_currentChanged(int index) +{ + if(ui->tabWidget->widget(index) == ui->tab_console) + { + ui->lineEdit->setFocus(); + } +} + +void RPCConsole::on_openDebugLogfileButton_clicked() +{ + GUIUtil::openDebugLogfile(); +} + +void RPCConsole::scrollToEnd() +{ + QScrollBar *scrollbar = ui->messagesWidget->verticalScrollBar(); + scrollbar->setValue(scrollbar->maximum()); +} + +void RPCConsole::on_showCLOptionsButton_clicked() +{ + GUIUtil::HelpMessageBox help; + help.exec(); +} diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h new file mode 100644 index 0000000..3c38b4b --- /dev/null +++ b/src/qt/rpcconsole.h @@ -0,0 +1,66 @@ +#ifndef RPCCONSOLE_H +#define RPCCONSOLE_H + +#include + +namespace Ui { + class RPCConsole; +} +class ClientModel; + +/** Local Bitcoin RPC console. */ +class RPCConsole: public QDialog +{ + Q_OBJECT + +public: + explicit RPCConsole(QWidget *parent = 0); + ~RPCConsole(); + + void setClientModel(ClientModel *model); + + enum MessageClass { + MC_ERROR, + MC_DEBUG, + CMD_REQUEST, + CMD_REPLY, + CMD_ERROR + }; + +protected: + virtual bool eventFilter(QObject* obj, QEvent *event); + +private slots: + void on_lineEdit_returnPressed(); + void on_tabWidget_currentChanged(int index); + /** open the debug.log from the current datadir */ + void on_openDebugLogfileButton_clicked(); + /** display messagebox with program parameters (same as bitcoin-qt --help) */ + void on_showCLOptionsButton_clicked(); + +public slots: + void clear(); + void message(int category, const QString &message, bool html = false); + /** Set number of connections shown in the UI */ + void setNumConnections(int count); + /** Set number of blocks shown in the UI */ + void setNumBlocks(int count, int countOfPeers); + /** Go forward or back in history */ + void browseHistory(int offset); + /** Scroll console view to end */ + void scrollToEnd(); +signals: + // For RPC command executor + void stopExecutor(); + void cmdRequest(const QString &command); + +private: + Ui::RPCConsole *ui; + ClientModel *clientModel; + QStringList history; + int historyPtr; + + void startExecutor(); +}; + +#endif // RPCCONSOLE_H diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp new file mode 100644 index 0000000..a56dc53 --- /dev/null +++ b/src/qt/sendcoinsdialog.cpp @@ -0,0 +1,538 @@ +#include "sendcoinsdialog.h" +#include "ui_sendcoinsdialog.h" + +#include "init.h" +#include "walletmodel.h" +#include "addresstablemodel.h" +#include "bitcoinunits.h" +#include "addressbookpage.h" +#include "optionsmodel.h" +#include "sendcoinsentry.h" +#include "guiutil.h" +#include "askpassphrasedialog.h" +#include "coincontrol.h" +#include "coincontroldialog.h" + +#include +#include +#include +#include + +SendCoinsDialog::SendCoinsDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::SendCoinsDialog), + model(0) +{ + ui->setupUi(this); + +#ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac + ui->addButton->setIcon(QIcon()); + ui->clearButton->setIcon(QIcon()); + ui->sendButton->setIcon(QIcon()); +#endif +#if QT_VERSION >= 0x040700 + /* Do not move this to the XML file, Qt before 4.7 will choke on it */ + ui->lineEditCoinControlChange->setPlaceholderText(tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)")); +#endif + + addEntry(); + + connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry())); + connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear())); + + // Coin Control + ui->lineEditCoinControlChange->setFont(GUIUtil::bitcoinAddressFont()); + connect(ui->pushButtonCoinControl, SIGNAL(clicked()), this, SLOT(coinControlButtonClicked())); + connect(ui->checkBoxCoinControlChange, SIGNAL(stateChanged(int)), this, SLOT(coinControlChangeChecked(int))); + connect(ui->lineEditCoinControlChange, SIGNAL(textEdited(const QString &)), this, SLOT(coinControlChangeEdited(const QString &))); + + // Coin Control: clipboard actions + QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this); + QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this); + QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this); + QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this); + QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this); + QAction *clipboardPriorityAction = new QAction(tr("Copy priority"), this); + QAction *clipboardLowOutputAction = new QAction(tr("Copy low output"), this); + QAction *clipboardChangeAction = new QAction(tr("Copy change"), this); + connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardQuantity())); + connect(clipboardAmountAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAmount())); + connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardFee())); + connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAfterFee())); + connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardBytes())); + connect(clipboardPriorityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardPriority())); + connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardLowOutput())); + connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardChange())); + ui->labelCoinControlQuantity->addAction(clipboardQuantityAction); + ui->labelCoinControlAmount->addAction(clipboardAmountAction); + ui->labelCoinControlFee->addAction(clipboardFeeAction); + ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction); + ui->labelCoinControlBytes->addAction(clipboardBytesAction); + ui->labelCoinControlPriority->addAction(clipboardPriorityAction); + ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction); + ui->labelCoinControlChange->addAction(clipboardChangeAction); + + fNewRecipientAllowed = true; +} + +void SendCoinsDialog::setModel(WalletModel *model) +{ + this->model = model; + + for(int i = 0; i < ui->entries->count(); ++i) + { + SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(i)->widget()); + if(entry) + { + entry->setModel(model); + } + } + if(model && model->getOptionsModel()) + { + setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance()); + connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64))); + connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + + // Coin Control + connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(coinControlUpdateLabels())); + connect(model->getOptionsModel(), SIGNAL(coinControlFeaturesChanged(bool)), this, SLOT(coinControlFeatureChanged(bool))); + connect(model->getOptionsModel(), SIGNAL(transactionFeeChanged(qint64)), this, SLOT(coinControlUpdateLabels())); + ui->frameCoinControl->setVisible(model->getOptionsModel()->getCoinControlFeatures()); + coinControlUpdateLabels(); + } +} + +SendCoinsDialog::~SendCoinsDialog() +{ + delete ui; +} + +void SendCoinsDialog::on_sendButton_clicked() +{ + QList recipients; + bool valid = true; + + if(!model) + return; + + for(int i = 0; i < ui->entries->count(); ++i) + { + SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(i)->widget()); + if(entry) + { + if(entry->validate()) + { + recipients.append(entry->getValue()); + } + else + { + valid = false; + } + } + } + + if(!valid || recipients.isEmpty()) + { + return; + } + + // Format confirmation message + QStringList formatted; + foreach(const SendCoinsRecipient &rcp, recipients) + { +#if QT_VERSION < 0x050000 + formatted.append(tr("%1 to %2 (%3)").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount), Qt::escape(rcp.label), rcp.address)); +#else + formatted.append(tr("%1 to %2 (%3)").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount), rcp.label.toHtmlEscaped(), rcp.address)); +#endif + } + + fNewRecipientAllowed = false; + + QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"), + tr("Are you sure you want to send %1?").arg(formatted.join(tr(" and "))), + QMessageBox::Yes|QMessageBox::Cancel, + QMessageBox::Cancel); + + if(retval != QMessageBox::Yes) + { + fNewRecipientAllowed = true; + return; + } + + WalletModel::UnlockContext ctx(model->requestUnlock()); + if(!ctx.isValid()) + { + // Unlock wallet was cancelled + fNewRecipientAllowed = true; + return; + } + + WalletModel::SendCoinsReturn sendstatus; + if (!model->getOptionsModel() || !model->getOptionsModel()->getCoinControlFeatures()) + sendstatus = model->sendCoins(recipients); + else + sendstatus = model->sendCoins(recipients, CoinControlDialog::coinControl); + switch(sendstatus.status) + { + case WalletModel::InvalidAddress: + QMessageBox::warning(this, tr("Send Coins"), + tr("The recipient address is not valid, please recheck."), + QMessageBox::Ok, QMessageBox::Ok); + break; + case WalletModel::InvalidAmount: + QMessageBox::warning(this, tr("Send Coins"), + tr("The amount to pay must be larger than 0."), + QMessageBox::Ok, QMessageBox::Ok); + break; + case WalletModel::AmountExceedsBalance: + QMessageBox::warning(this, tr("Send Coins"), + tr("The amount exceeds your balance."), + QMessageBox::Ok, QMessageBox::Ok); + break; + case WalletModel::AmountWithFeeExceedsBalance: + QMessageBox::warning(this, tr("Send Coins"), + tr("The total exceeds your balance when the %1 transaction fee is included."). + arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, sendstatus.fee)), + QMessageBox::Ok, QMessageBox::Ok); + break; + case WalletModel::DuplicateAddress: + QMessageBox::warning(this, tr("Send Coins"), + tr("Duplicate address found, can only send to each address once per send operation."), + QMessageBox::Ok, QMessageBox::Ok); + break; + case WalletModel::TransactionCreationFailed: + QMessageBox::warning(this, tr("Send Coins"), + tr("Error: Transaction creation failed!"), + QMessageBox::Ok, QMessageBox::Ok); + break; + case WalletModel::TransactionCommitFailed: + QMessageBox::warning(this, tr("Send Coins"), + tr("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."), + QMessageBox::Ok, QMessageBox::Ok); + break; + case WalletModel::Aborted: // User aborted, nothing to do + break; + case WalletModel::OK: + accept(); + CoinControlDialog::coinControl->UnSelectAll(); + coinControlUpdateLabels(); + break; + } + fNewRecipientAllowed = true; +} + +void SendCoinsDialog::clear() +{ + // Remove entries until only one left + while(ui->entries->count()) + { + ui->entries->takeAt(0)->widget()->deleteLater(); + } + addEntry(); + + updateRemoveEnabled(); + + ui->sendButton->setDefault(true); +} + +void SendCoinsDialog::reject() +{ + clear(); +} + +void SendCoinsDialog::accept() +{ + clear(); +} + +SendCoinsEntry *SendCoinsDialog::addEntry() +{ + SendCoinsEntry *entry = new SendCoinsEntry(this); + entry->setModel(model); + ui->entries->addWidget(entry); + connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*))); + connect(entry, SIGNAL(payAmountChanged()), this, SLOT(coinControlUpdateLabels())); + + updateRemoveEnabled(); + + // Focus the field, so that entry can start immediately + entry->clear(); + entry->setFocus(); + ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint()); + qApp->processEvents(); + QScrollBar* bar = ui->scrollArea->verticalScrollBar(); + if(bar) + bar->setSliderPosition(bar->maximum()); + return entry; +} + +void SendCoinsDialog::updateRemoveEnabled() +{ + // Remove buttons are enabled as soon as there is more than one send-entry + bool enabled = (ui->entries->count() > 1); + for(int i = 0; i < ui->entries->count(); ++i) + { + SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(i)->widget()); + if(entry) + { + entry->setRemoveEnabled(enabled); + } + } + setupTabChain(0); + + coinControlUpdateLabels(); +} + +void SendCoinsDialog::removeEntry(SendCoinsEntry* entry) +{ + entry->deleteLater(); + updateRemoveEnabled(); +} + +QWidget *SendCoinsDialog::setupTabChain(QWidget *prev) +{ + for(int i = 0; i < ui->entries->count(); ++i) + { + SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(i)->widget()); + if(entry) + { + prev = entry->setupTabChain(prev); + } + } + QWidget::setTabOrder(prev, ui->addButton); + QWidget::setTabOrder(ui->addButton, ui->sendButton); + return ui->sendButton; +} + +void SendCoinsDialog::setAddress(const QString &address) +{ + SendCoinsEntry *entry = 0; + // Replace the first entry if it is still unused + if(ui->entries->count() == 1) + { + SendCoinsEntry *first = qobject_cast(ui->entries->itemAt(0)->widget()); + if(first->isClear()) + { + entry = first; + } + } + if(!entry) + { + entry = addEntry(); + } + + entry->setAddress(address); +} + +void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv) +{ + if(!fNewRecipientAllowed) + return; + + SendCoinsEntry *entry = 0; + // Replace the first entry if it is still unused + if(ui->entries->count() == 1) + { + SendCoinsEntry *first = qobject_cast(ui->entries->itemAt(0)->widget()); + if(first->isClear()) + { + entry = first; + } + } + if(!entry) + { + entry = addEntry(); + } + + entry->setValue(rv); +} + +bool SendCoinsDialog::handleURI(const QString &uri) +{ + SendCoinsRecipient rv; + // URI has to be valid + if (GUIUtil::parseBitcoinURI(uri, &rv)) + { + CBitcoinAddress address(rv.address.toStdString()); + if (!address.IsValid()) + return false; + pasteEntry(rv); + return true; + } + + return false; +} + +void SendCoinsDialog::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance) +{ + Q_UNUSED(unconfirmedBalance); + Q_UNUSED(immatureBalance); + if(!model || !model->getOptionsModel()) + return; + + int unit = model->getOptionsModel()->getDisplayUnit(); + ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance)); +} + +void SendCoinsDialog::updateDisplayUnit() +{ + if(model && model->getOptionsModel()) + { + // Update labelBalance with the current balance and the current unit + ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->getBalance())); + } +} + +// Coin Control: copy label "Quantity" to clipboard +void SendCoinsDialog::coinControlClipboardQuantity() +{ + GUIUtil::setClipboard(ui->labelCoinControlQuantity->text()); +} + +// Coin Control: copy label "Amount" to clipboard +void SendCoinsDialog::coinControlClipboardAmount() +{ + GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" "))); +} + +// Coin Control: copy label "Fee" to clipboard +void SendCoinsDialog::coinControlClipboardFee() +{ + GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" "))); +} + +// Coin Control: copy label "After fee" to clipboard +void SendCoinsDialog::coinControlClipboardAfterFee() +{ + GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" "))); +} + +// Coin Control: copy label "Bytes" to clipboard +void SendCoinsDialog::coinControlClipboardBytes() +{ + GUIUtil::setClipboard(ui->labelCoinControlBytes->text()); +} + +// Coin Control: copy label "Priority" to clipboard +void SendCoinsDialog::coinControlClipboardPriority() +{ + GUIUtil::setClipboard(ui->labelCoinControlPriority->text()); +} + +// Coin Control: copy label "Low output" to clipboard +void SendCoinsDialog::coinControlClipboardLowOutput() +{ + GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text()); +} + +// Coin Control: copy label "Change" to clipboard +void SendCoinsDialog::coinControlClipboardChange() +{ + GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" "))); +} + +// Coin Control: settings menu - coin control enabled/disabled by user +void SendCoinsDialog::coinControlFeatureChanged(bool checked) +{ + ui->frameCoinControl->setVisible(checked); + + if (!checked && model) // coin control features disabled + CoinControlDialog::coinControl->SetNull(); +} + +// Coin Control: button inputs -> show actual coin control dialog +void SendCoinsDialog::coinControlButtonClicked() +{ + CoinControlDialog dlg; + dlg.setModel(model); + dlg.exec(); + coinControlUpdateLabels(); +} + +// Coin Control: checkbox custom change address +void SendCoinsDialog::coinControlChangeChecked(int state) +{ + if (model) + { + if (state == Qt::Checked) + CoinControlDialog::coinControl->destChange = CBitcoinAddress(ui->lineEditCoinControlChange->text().toStdString()).Get(); + else + CoinControlDialog::coinControl->destChange = CNoDestination(); + } + + ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked)); + ui->labelCoinControlChangeLabel->setVisible((state == Qt::Checked)); +} + +// Coin Control: custom change address changed +void SendCoinsDialog::coinControlChangeEdited(const QString & text) +{ + if (model) + { + CoinControlDialog::coinControl->destChange = CBitcoinAddress(text.toStdString()).Get(); + + // label for the change address + ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}"); + if (text.isEmpty()) + ui->labelCoinControlChangeLabel->setText(""); + else if (!CBitcoinAddress(text.toStdString()).IsValid()) + { + ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}"); + ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Bitcoin address")); + } + else + { + QString associatedLabel = model->getAddressTableModel()->labelForAddress(text); + if (!associatedLabel.isEmpty()) + ui->labelCoinControlChangeLabel->setText(associatedLabel); + else + { + CPubKey pubkey; + CKeyID keyid; + CBitcoinAddress(text.toStdString()).GetKeyID(keyid); + if (model->getPubKey(keyid, pubkey)) + ui->labelCoinControlChangeLabel->setText(tr("(no label)")); + else + { + ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}"); + ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address")); + } + } + } + } +} + +// Coin Control: update labels +void SendCoinsDialog::coinControlUpdateLabels() +{ + if (!model || !model->getOptionsModel() || !model->getOptionsModel()->getCoinControlFeatures()) + return; + + // set pay amounts + CoinControlDialog::payAmounts.clear(); + for(int i = 0; i < ui->entries->count(); ++i) + { + SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(i)->widget()); + if(entry) + CoinControlDialog::payAmounts.append(entry->getValue().amount); + } + + if (CoinControlDialog::coinControl->HasSelected()) + { + // actual coin control calculation + CoinControlDialog::updateLabels(model, this); + + // show coin control stats + ui->labelCoinControlAutomaticallySelected->hide(); + ui->widgetCoinControl->show(); + } + else + { + // hide coin control stats + ui->labelCoinControlAutomaticallySelected->show(); + ui->widgetCoinControl->hide(); + ui->labelCoinControlInsuffFunds->hide(); + } +} + diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h new file mode 100644 index 0000000..d5fba9b --- /dev/null +++ b/src/qt/sendcoinsdialog.h @@ -0,0 +1,69 @@ +#ifndef SENDCOINSDIALOG_H +#define SENDCOINSDIALOG_H + +#include +#include + +namespace Ui { + class SendCoinsDialog; +} +class WalletModel; +class SendCoinsEntry; +class SendCoinsRecipient; + +QT_BEGIN_NAMESPACE +class QUrl; +QT_END_NAMESPACE + +/** Dialog for sending bitcoins */ +class SendCoinsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit SendCoinsDialog(QWidget *parent = 0); + ~SendCoinsDialog(); + + void setModel(WalletModel *model); + + /** Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://bugreports.qt-project.org/browse/QTBUG-10907). + */ + QWidget *setupTabChain(QWidget *prev); + + void setAddress(const QString &address); + void pasteEntry(const SendCoinsRecipient &rv); + bool handleURI(const QString &uri); + +public slots: + void clear(); + void reject(); + void accept(); + SendCoinsEntry *addEntry(); + void updateRemoveEnabled(); + void setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance); + +private: + Ui::SendCoinsDialog *ui; + WalletModel *model; + bool fNewRecipientAllowed; + +private slots: + void on_sendButton_clicked(); + void removeEntry(SendCoinsEntry* entry); + void updateDisplayUnit(); + void coinControlFeatureChanged(bool); + void coinControlButtonClicked(); + void coinControlChangeChecked(int); + void coinControlChangeEdited(const QString &); + void coinControlUpdateLabels(); + void coinControlClipboardQuantity(); + void coinControlClipboardAmount(); + void coinControlClipboardFee(); + void coinControlClipboardAfterFee(); + void coinControlClipboardBytes(); + void coinControlClipboardPriority(); + void coinControlClipboardLowOutput(); + void coinControlClipboardChange(); +}; + +#endif // SENDCOINSDIALOG_H diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp new file mode 100644 index 0000000..c429c1c --- /dev/null +++ b/src/qt/sendcoinsentry.cpp @@ -0,0 +1,181 @@ +#include "sendcoinsentry.h" +#include "ui_sendcoinsentry.h" + +#include "guiutil.h" +#include "bitcoinunits.h" +#include "addressbookpage.h" +#include "walletmodel.h" +#include "optionsmodel.h" +#include "addresstablemodel.h" + +#include +#include + +SendCoinsEntry::SendCoinsEntry(QWidget *parent) : + QFrame(parent), + ui(new Ui::SendCoinsEntry), + model(0) +{ + ui->setupUi(this); + +#ifdef Q_OS_MAC + ui->payToLayout->setSpacing(4); +#endif +#if QT_VERSION >= 0x040700 + /* Do not move this to the XML file, Qt before 4.7 will choke on it */ + ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book")); + ui->payTo->setPlaceholderText(tr("Enter a CryptoLottoToken address")); +#endif + setFocusPolicy(Qt::TabFocus); + setFocusProxy(ui->payTo); + + GUIUtil::setupAddressWidget(ui->payTo, this); +} + +SendCoinsEntry::~SendCoinsEntry() +{ + delete ui; +} + +void SendCoinsEntry::on_pasteButton_clicked() +{ + // Paste text from clipboard into recipient field + ui->payTo->setText(QApplication::clipboard()->text()); +} + +void SendCoinsEntry::on_addressBookButton_clicked() +{ + if(!model) + return; + AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::SendingTab, this); + dlg.setModel(model->getAddressTableModel()); + if(dlg.exec()) + { + ui->payTo->setText(dlg.getReturnValue()); + ui->payAmount->setFocus(); + } +} + +void SendCoinsEntry::on_payTo_textChanged(const QString &address) +{ + if(!model) + return; + // Fill in label from address book, if address has an associated label + QString associatedLabel = model->getAddressTableModel()->labelForAddress(address); + if(!associatedLabel.isEmpty()) + ui->addAsLabel->setText(associatedLabel); +} + +void SendCoinsEntry::setModel(WalletModel *model) +{ + this->model = model; + + if(model && model->getOptionsModel()) + connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + + connect(ui->payAmount, SIGNAL(textChanged()), this, SIGNAL(payAmountChanged())); + + clear(); +} + +void SendCoinsEntry::setRemoveEnabled(bool enabled) +{ + ui->deleteButton->setEnabled(enabled); +} + +void SendCoinsEntry::clear() +{ + ui->payTo->clear(); + ui->addAsLabel->clear(); + ui->payAmount->clear(); + ui->payTo->setFocus(); + // update the display unit, to not use the default ("BTC") + updateDisplayUnit(); +} + +void SendCoinsEntry::on_deleteButton_clicked() +{ + emit removeEntry(this); +} + +bool SendCoinsEntry::validate() +{ + // Check input validity + bool retval = true; + + if(!ui->payAmount->validate()) + { + retval = false; + } + else + { + if(ui->payAmount->value() <= 0) + { + // Cannot send 0 coins or less + ui->payAmount->setValid(false); + retval = false; + } + } + + if(!ui->payTo->hasAcceptableInput() || + (model && !model->validateAddress(ui->payTo->text()))) + { + ui->payTo->setValid(false); + retval = false; + } + + return retval; +} + +SendCoinsRecipient SendCoinsEntry::getValue() +{ + SendCoinsRecipient rv; + + rv.address = ui->payTo->text(); + rv.label = ui->addAsLabel->text(); + rv.amount = ui->payAmount->value(); + + return rv; +} + +QWidget *SendCoinsEntry::setupTabChain(QWidget *prev) +{ + QWidget::setTabOrder(prev, ui->payTo); + QWidget::setTabOrder(ui->payTo, ui->addressBookButton); + QWidget::setTabOrder(ui->addressBookButton, ui->pasteButton); + QWidget::setTabOrder(ui->pasteButton, ui->deleteButton); + QWidget::setTabOrder(ui->deleteButton, ui->addAsLabel); + return ui->payAmount->setupTabChain(ui->addAsLabel); +} + +void SendCoinsEntry::setValue(const SendCoinsRecipient &value) +{ + ui->payTo->setText(value.address); + ui->addAsLabel->setText(value.label); + ui->payAmount->setValue(value.amount); +} + +void SendCoinsEntry::setAddress(const QString &address) +{ + ui->payTo->setText(address); + ui->payAmount->setFocus(); +} + +bool SendCoinsEntry::isClear() +{ + return ui->payTo->text().isEmpty(); +} + +void SendCoinsEntry::setFocus() +{ + ui->payTo->setFocus(); +} + +void SendCoinsEntry::updateDisplayUnit() +{ + if(model && model->getOptionsModel()) + { + // Update payAmount with the current unit + ui->payAmount->setDisplayUnit(model->getOptionsModel()->getDisplayUnit()); + } +} diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h new file mode 100644 index 0000000..bec25c1 --- /dev/null +++ b/src/qt/sendcoinsentry.h @@ -0,0 +1,57 @@ +#ifndef SENDCOINSENTRY_H +#define SENDCOINSENTRY_H + +#include + +namespace Ui { + class SendCoinsEntry; +} +class WalletModel; +class SendCoinsRecipient; + +/** A single entry in the dialog for sending bitcoins. */ +class SendCoinsEntry : public QFrame +{ + Q_OBJECT + +public: + explicit SendCoinsEntry(QWidget *parent = 0); + ~SendCoinsEntry(); + + void setModel(WalletModel *model); + bool validate(); + SendCoinsRecipient getValue(); + + /** Return whether the entry is still empty and unedited */ + bool isClear(); + + void setValue(const SendCoinsRecipient &value); + void setAddress(const QString &address); + + /** Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://bugreports.qt-project.org/browse/QTBUG-10907). + */ + QWidget *setupTabChain(QWidget *prev); + + void setFocus(); + +public slots: + void setRemoveEnabled(bool enabled); + void clear(); + +signals: + void removeEntry(SendCoinsEntry *entry); + void payAmountChanged(); + +private slots: + void on_deleteButton_clicked(); + void on_payTo_textChanged(const QString &address); + void on_addressBookButton_clicked(); + void on_pasteButton_clicked(); + void updateDisplayUnit(); + +private: + Ui::SendCoinsEntry *ui; + WalletModel *model; +}; + +#endif // SENDCOINSENTRY_H diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp new file mode 100644 index 0000000..6b9827b --- /dev/null +++ b/src/qt/signverifymessagedialog.cpp @@ -0,0 +1,274 @@ +#include "signverifymessagedialog.h" +#include "ui_signverifymessagedialog.h" + +#include "addressbookpage.h" +#include "base58.h" +#include "guiutil.h" +#include "init.h" +#include "main.h" +#include "optionsmodel.h" +#include "walletmodel.h" +#include "wallet.h" + +#include + +#include +#include + +SignVerifyMessageDialog::SignVerifyMessageDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::SignVerifyMessageDialog), + model(0) +{ + ui->setupUi(this); + +#if (QT_VERSION >= 0x040700) + /* Do not move this to the XML file, Qt before 4.7 will choke on it */ + ui->addressIn_SM->setPlaceholderText(tr("Enter a CryptoLottoToken address")); + ui->signatureOut_SM->setPlaceholderText(tr("Click \"Sign Message\" to generate signature")); + + ui->addressIn_VM->setPlaceholderText(tr("Enter a CryptoLottoToken address")); + ui->signatureIn_VM->setPlaceholderText(tr("Enter CryptoLottoToken signature")); +#endif + + GUIUtil::setupAddressWidget(ui->addressIn_SM, this); + GUIUtil::setupAddressWidget(ui->addressIn_VM, this); + + ui->addressIn_SM->installEventFilter(this); + ui->messageIn_SM->installEventFilter(this); + ui->signatureOut_SM->installEventFilter(this); + ui->addressIn_VM->installEventFilter(this); + ui->messageIn_VM->installEventFilter(this); + ui->signatureIn_VM->installEventFilter(this); + + ui->signatureOut_SM->setFont(GUIUtil::bitcoinAddressFont()); + ui->signatureIn_VM->setFont(GUIUtil::bitcoinAddressFont()); +} + +SignVerifyMessageDialog::~SignVerifyMessageDialog() +{ + delete ui; +} + +void SignVerifyMessageDialog::setModel(WalletModel *model) +{ + this->model = model; +} + +void SignVerifyMessageDialog::setAddress_SM(const QString &address) +{ + ui->addressIn_SM->setText(address); + ui->messageIn_SM->setFocus(); +} + +void SignVerifyMessageDialog::setAddress_VM(const QString &address) +{ + ui->addressIn_VM->setText(address); + ui->messageIn_VM->setFocus(); +} + +void SignVerifyMessageDialog::showTab_SM(bool fShow) +{ + ui->tabWidget->setCurrentIndex(0); + + if (fShow) + this->show(); +} + +void SignVerifyMessageDialog::showTab_VM(bool fShow) +{ + ui->tabWidget->setCurrentIndex(1); + if (fShow) + this->show(); +} + +void SignVerifyMessageDialog::on_addressBookButton_SM_clicked() +{ + if (model && model->getAddressTableModel()) + { + AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::ReceivingTab, this); + dlg.setModel(model->getAddressTableModel()); + if (dlg.exec()) + { + setAddress_SM(dlg.getReturnValue()); + } + } +} + +void SignVerifyMessageDialog::on_pasteButton_SM_clicked() +{ + setAddress_SM(QApplication::clipboard()->text()); +} + +void SignVerifyMessageDialog::on_signMessageButton_SM_clicked() +{ + /* Clear old signature to ensure users don't get confused on error with an old signature displayed */ + ui->signatureOut_SM->clear(); + + CBitcoinAddress addr(ui->addressIn_SM->text().toStdString()); + if (!addr.IsValid()) + { + ui->addressIn_SM->setValid(false); + ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel_SM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again.")); + return; + } + CKeyID keyID; + if (!addr.GetKeyID(keyID)) + { + ui->addressIn_SM->setValid(false); + ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel_SM->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again.")); + return; + } + + WalletModel::UnlockContext ctx(model->requestUnlock()); + if (!ctx.isValid()) + { + ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel_SM->setText(tr("Wallet unlock was cancelled.")); + return; + } + + CKey key; + if (!pwalletMain->GetKey(keyID, key)) + { + ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel_SM->setText(tr("Private key for the entered address is not available.")); + return; + } + + CDataStream ss(SER_GETHASH, 0); + ss << strMessageMagic; + ss << ui->messageIn_SM->document()->toPlainText().toStdString(); + + std::vector vchSig; + if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig)) + { + ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel_SM->setText(QString("") + tr("Message signing failed.") + QString("")); + return; + } + + ui->statusLabel_SM->setStyleSheet("QLabel { color: green; }"); + ui->statusLabel_SM->setText(QString("") + tr("Message signed.") + QString("")); + + ui->signatureOut_SM->setText(QString::fromStdString(EncodeBase64(&vchSig[0], vchSig.size()))); +} + +void SignVerifyMessageDialog::on_copySignatureButton_SM_clicked() +{ + QApplication::clipboard()->setText(ui->signatureOut_SM->text()); +} + +void SignVerifyMessageDialog::on_clearButton_SM_clicked() +{ + ui->addressIn_SM->clear(); + ui->messageIn_SM->clear(); + ui->signatureOut_SM->clear(); + ui->statusLabel_SM->clear(); + + ui->addressIn_SM->setFocus(); +} + +void SignVerifyMessageDialog::on_addressBookButton_VM_clicked() +{ + if (model && model->getAddressTableModel()) + { + AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::SendingTab, this); + dlg.setModel(model->getAddressTableModel()); + if (dlg.exec()) + { + setAddress_VM(dlg.getReturnValue()); + } + } +} + +void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked() +{ + CBitcoinAddress addr(ui->addressIn_VM->text().toStdString()); + if (!addr.IsValid()) + { + ui->addressIn_VM->setValid(false); + ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel_VM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again.")); + return; + } + CKeyID keyID; + if (!addr.GetKeyID(keyID)) + { + ui->addressIn_VM->setValid(false); + ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel_VM->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again.")); + return; + } + + bool fInvalid = false; + std::vector vchSig = DecodeBase64(ui->signatureIn_VM->text().toStdString().c_str(), &fInvalid); + + if (fInvalid) + { + ui->signatureIn_VM->setValid(false); + ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel_VM->setText(tr("The signature could not be decoded.") + QString(" ") + tr("Please check the signature and try again.")); + return; + } + + CDataStream ss(SER_GETHASH, 0); + ss << strMessageMagic; + ss << ui->messageIn_VM->document()->toPlainText().toStdString(); + + CPubKey pubkey; + if (!pubkey.RecoverCompact(Hash(ss.begin(), ss.end()), vchSig)) + { + ui->signatureIn_VM->setValid(false); + ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel_VM->setText(tr("The signature did not match the message digest.") + QString(" ") + tr("Please check the signature and try again.")); + return; + } + + if (!(CBitcoinAddress(pubkey.GetID()) == addr)) + { + ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel_VM->setText(QString("") + tr("Message verification failed.") + QString("")); + return; + } + + ui->statusLabel_VM->setStyleSheet("QLabel { color: green; }"); + ui->statusLabel_VM->setText(QString("") + tr("Message verified.") + QString("")); +} + +void SignVerifyMessageDialog::on_clearButton_VM_clicked() +{ + ui->addressIn_VM->clear(); + ui->signatureIn_VM->clear(); + ui->messageIn_VM->clear(); + ui->statusLabel_VM->clear(); + + ui->addressIn_VM->setFocus(); +} + +bool SignVerifyMessageDialog::eventFilter(QObject *object, QEvent *event) +{ + if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::FocusIn) + { + if (ui->tabWidget->currentIndex() == 0) + { + /* Clear status message on focus change */ + ui->statusLabel_SM->clear(); + + /* Select generated signature */ + if (object == ui->signatureOut_SM) + { + ui->signatureOut_SM->selectAll(); + return true; + } + } + else if (ui->tabWidget->currentIndex() == 1) + { + /* Clear status message on focus change */ + ui->statusLabel_VM->clear(); + } + } + return QDialog::eventFilter(object, event); +} diff --git a/src/qt/signverifymessagedialog.h b/src/qt/signverifymessagedialog.h new file mode 100644 index 0000000..558f24e --- /dev/null +++ b/src/qt/signverifymessagedialog.h @@ -0,0 +1,46 @@ +#ifndef SIGNVERIFYMESSAGEDIALOG_H +#define SIGNVERIFYMESSAGEDIALOG_H + +#include + +namespace Ui { + class SignVerifyMessageDialog; +} +class WalletModel; + +class SignVerifyMessageDialog : public QDialog +{ + Q_OBJECT + +public: + explicit SignVerifyMessageDialog(QWidget *parent = 0); + ~SignVerifyMessageDialog(); + + void setModel(WalletModel *model); + void setAddress_SM(const QString &address); + void setAddress_VM(const QString &address); + + void showTab_SM(bool fShow); + void showTab_VM(bool fShow); + +protected: + bool eventFilter(QObject *object, QEvent *event); + +private: + Ui::SignVerifyMessageDialog *ui; + WalletModel *model; + +private slots: + /* sign message */ + void on_addressBookButton_SM_clicked(); + void on_pasteButton_SM_clicked(); + void on_signMessageButton_SM_clicked(); + void on_copySignatureButton_SM_clicked(); + void on_clearButton_SM_clicked(); + /* verify message */ + void on_addressBookButton_VM_clicked(); + void on_verifyMessageButton_VM_clicked(); + void on_clearButton_VM_clicked(); +}; + +#endif // SIGNVERIFYMESSAGEDIALOG_H diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp new file mode 100644 index 0000000..c1dee9d --- /dev/null +++ b/src/qt/splashscreen.cpp @@ -0,0 +1,52 @@ +#include "splashscreen.h" +#include "clientversion.h" +#include "util.h" + +#include +#undef loop /* ugh, remove this when the #define loop is gone from util.h */ +#include + +SplashScreen::SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f) : + QSplashScreen(pixmap, f) +{ + // set reference point, paddings + int paddingLeftCol2 = 230; + int paddingTopCol2 = 376; + int line1 = 0; + int line2 = 13; + int line3 = 26; + + float fontFactor = 1.0; + + // define text to place + QString titleText = QString(QApplication::applicationName()).replace(QString("-testnet"), QString(""), Qt::CaseSensitive); // cut of testnet, place it as single object further down + QString versionText = QString("Version %1 ").arg(QString::fromStdString(FormatFullVersion())); + QString copyrightText1 = QChar(0xA9)+QString(" 2009-%1 ").arg(COPYRIGHT_YEAR) + QString(tr("The Bitcoin developers")); + QString copyrightText2 = QChar(0xA9)+QString(" 2011-%1 ").arg(COPYRIGHT_YEAR) + QString(tr("The CryptoLottoToken developers")); + + QString font = "Arial"; + + // load the bitmap for writing some text over it + QPixmap newPixmap; + if(GetBoolArg("-testnet")) { + newPixmap = QPixmap(":/images/splash_testnet"); + } + else { + newPixmap = QPixmap(":/images/splash"); + } + + QPainter pixPaint(&newPixmap); + pixPaint.setPen(QColor(70,70,70)); + + pixPaint.setFont(QFont(font, 9*fontFactor)); + pixPaint.drawText(paddingLeftCol2,paddingTopCol2+line3,versionText); + + // draw copyright stuff + pixPaint.setFont(QFont(font, 9*fontFactor)); + pixPaint.drawText(paddingLeftCol2,paddingTopCol2+line1,copyrightText1); + pixPaint.drawText(paddingLeftCol2,paddingTopCol2+line2,copyrightText2); + + pixPaint.end(); + + this->setPixmap(newPixmap); +} diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h new file mode 100644 index 0000000..6a6249d --- /dev/null +++ b/src/qt/splashscreen.h @@ -0,0 +1,16 @@ +#ifndef SPLASHSCREEN_H +#define SPLASHSCREEN_H + +#include + +/** class for the splashscreen with information of the running client + */ +class SplashScreen : public QSplashScreen +{ + Q_OBJECT + +public: + explicit SplashScreen(const QPixmap &pixmap = QPixmap(), Qt::WindowFlags f = 0); +}; + +#endif // SPLASHSCREEN_H diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp new file mode 100644 index 0000000..3c7794b --- /dev/null +++ b/src/qt/test/test_main.cpp @@ -0,0 +1,16 @@ +#include +#include + +#include "uritests.h" + +// This is all you need to run all the tests +int main(int argc, char *argv[]) +{ + bool fInvalid = false; + + URITests test1; + if (QTest::qExec(&test1) != 0) + fInvalid = true; + + return fInvalid; +} diff --git a/src/qt/test/uritests.cpp b/src/qt/test/uritests.cpp new file mode 100644 index 0000000..9d8bb46 --- /dev/null +++ b/src/qt/test/uritests.cpp @@ -0,0 +1,62 @@ +#include "uritests.h" +#include "../guiutil.h" +#include "../walletmodel.h" + +#include + +void URITests::uriTests() +{ + SendCoinsRecipient rv; + QUrl uri; + uri.setUrl(QString("CryptoLottoToken:LQDPC5rbjDB72fGFVHu4enYhxGAZuRiFh9?req-dontexist=")); + QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); + + uri.setUrl(QString("CryptoLottoToken:LQDPC5rbjDB72fGFVHu4enYhxGAZuRiFh9?dontexist=")); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(rv.address == QString("LQDPC5rbjDB72fGFVHu4enYhxGAZuRiFh9")); + QVERIFY(rv.label == QString()); + QVERIFY(rv.amount == 0); + + uri.setUrl(QString("CryptoLottoToken:LQDPC5rbjDB72fGFVHu4enYhxGAZuRiFh9?label=Wikipedia Example Address")); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(rv.address == QString("LQDPC5rbjDB72fGFVHu4enYhxGAZuRiFh9")); + QVERIFY(rv.label == QString("Wikipedia Example Address")); + QVERIFY(rv.amount == 0); + + uri.setUrl(QString("CryptoLottoToken:LQDPC5rbjDB72fGFVHu4enYhxGAZuRiFh9?amount=0.001")); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(rv.address == QString("LQDPC5rbjDB72fGFVHu4enYhxGAZuRiFh9")); + QVERIFY(rv.label == QString()); + QVERIFY(rv.amount == 100000); + + uri.setUrl(QString("CryptoLottoToken:LQDPC5rbjDB72fGFVHu4enYhxGAZuRiFh9?amount=1.001")); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(rv.address == QString("LQDPC5rbjDB72fGFVHu4enYhxGAZuRiFh9")); + QVERIFY(rv.label == QString()); + QVERIFY(rv.amount == 100100000); + + uri.setUrl(QString("CryptoLottoToken:LQDPC5rbjDB72fGFVHu4enYhxGAZuRiFh9?amount=100&label=Wikipedia Example")); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(rv.address == QString("LQDPC5rbjDB72fGFVHu4enYhxGAZuRiFh9")); + QVERIFY(rv.amount == 10000000000LL); + QVERIFY(rv.label == QString("Wikipedia Example")); + + uri.setUrl(QString("CryptoLottoToken:LQDPC5rbjDB72fGFVHu4enYhxGAZuRiFh9?message=Wikipedia Example Address")); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(rv.address == QString("LQDPC5rbjDB72fGFVHu4enYhxGAZuRiFh9")); + QVERIFY(rv.label == QString()); + + QVERIFY(GUIUtil::parseBitcoinURI("CryptoLottoToken://LQDPC5rbjDB72fGFVHu4enYhxGAZuRiFh9?message=Wikipedia Example Address", &rv)); + QVERIFY(rv.address == QString("LQDPC5rbjDB72fGFVHu4enYhxGAZuRiFh9")); + QVERIFY(rv.label == QString()); + + // We currently don't implement the message parameter (ok, yea, we break spec...) + uri.setUrl(QString("CryptoLottoToken:LQDPC5rbjDB72fGFVHu4enYhxGAZuRiFh9?req-message=Wikipedia Example Address")); + QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); + + uri.setUrl(QString("CryptoLottoToken:LQDPC5rbjDB72fGFVHu4enYhxGAZuRiFh9?amount=1,000&label=Wikipedia Example")); + QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); + + uri.setUrl(QString("CryptoLottoToken:LQDPC5rbjDB72fGFVHu4enYhxGAZuRiFh9?amount=1,000.0&label=Wikipedia Example")); + QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); +} diff --git a/src/qt/test/uritests.h b/src/qt/test/uritests.h new file mode 100644 index 0000000..1237516 --- /dev/null +++ b/src/qt/test/uritests.h @@ -0,0 +1,15 @@ +#ifndef URITESTS_H +#define URITESTS_H + +#include +#include + +class URITests : public QObject +{ + Q_OBJECT + +private slots: + void uriTests(); +}; + +#endif // URITESTS_H diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp new file mode 100644 index 0000000..e4dbf9c --- /dev/null +++ b/src/qt/transactiondesc.cpp @@ -0,0 +1,274 @@ +#include "transactiondesc.h" + +#include "guiutil.h" +#include "bitcoinunits.h" +#include "main.h" +#include "wallet.h" +#include "db.h" +#include "ui_interface.h" +#include "base58.h" + +#include + +QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) +{ + if (!wtx.IsFinal()) + { + if (wtx.nLockTime < LOCKTIME_THRESHOLD) + return tr("Open for %n more block(s)", "", wtx.nLockTime - nBestHeight + 1); + else + return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.nLockTime)); + } + else + { + int nDepth = wtx.GetDepthInMainChain(); + if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + return tr("%1/offline").arg(nDepth); + else if (nDepth < 6) + return tr("%1/unconfirmed").arg(nDepth); + else + return tr("%1 confirmations").arg(nDepth); + } +} + +QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) +{ + QString strHTML; + + { + LOCK(wallet->cs_wallet); + strHTML.reserve(4000); + strHTML += ""; + + int64 nTime = wtx.GetTxTime(); + int64 nCredit = wtx.GetCredit(); + int64 nDebit = wtx.GetDebit(); + int64 nNet = nCredit - nDebit; + + strHTML += "" + tr("Status") + ": " + FormatTxStatus(wtx); + int nRequests = wtx.GetRequestCount(); + if (nRequests != -1) + { + if (nRequests == 0) + strHTML += tr(", has not been successfully broadcast yet"); + else if (nRequests > 0) + strHTML += tr(", broadcast through %n node(s)", "", nRequests); + } + strHTML += "
"; + + strHTML += "" + tr("Date") + ": " + (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "
"; + + // + // From + // + if (wtx.IsCoinBase()) + { + strHTML += "" + tr("Source") + ": " + tr("Generated") + "
"; + } + else if (wtx.mapValue.count("from") && !wtx.mapValue["from"].empty()) + { + // Online transaction + strHTML += "" + tr("From") + ": " + GUIUtil::HtmlEscape(wtx.mapValue["from"]) + "
"; + } + else + { + // Offline transaction + if (nNet > 0) + { + // Credit + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + if (wallet->IsMine(txout)) + { + CTxDestination address; + if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address)) + { + if (wallet->mapAddressBook.count(address)) + { + strHTML += "" + tr("From") + ": " + tr("unknown") + "
"; + strHTML += "" + tr("To") + ": "; + strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(address).ToString()); + if (!wallet->mapAddressBook[address].empty()) + strHTML += " (" + tr("own address") + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[address]) + ")"; + else + strHTML += " (" + tr("own address") + ")"; + strHTML += "
"; + } + } + break; + } + } + } + } + + // + // To + // + if (wtx.mapValue.count("to") && !wtx.mapValue["to"].empty()) + { + // Online transaction + std::string strAddress = wtx.mapValue["to"]; + strHTML += "" + tr("To") + ": "; + CTxDestination dest = CBitcoinAddress(strAddress).Get(); + if (wallet->mapAddressBook.count(dest) && !wallet->mapAddressBook[dest].empty()) + strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[dest]) + " "; + strHTML += GUIUtil::HtmlEscape(strAddress) + "
"; + } + + // + // Amount + // + if (wtx.IsCoinBase() && nCredit == 0) + { + // + // Coinbase + // + int64 nUnmatured = 0; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + nUnmatured += wallet->GetCredit(txout); + strHTML += "" + tr("Credit") + ": "; + if (wtx.IsInMainChain()) + strHTML += BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")"; + else + strHTML += "(" + tr("not accepted") + ")"; + strHTML += "
"; + } + else if (nNet > 0) + { + // + // Credit + // + strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nNet) + "
"; + } + else + { + bool fAllFromMe = true; + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + fAllFromMe = fAllFromMe && wallet->IsMine(txin); + + bool fAllToMe = true; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + fAllToMe = fAllToMe && wallet->IsMine(txout); + + if (fAllFromMe) + { + // + // Debit + // + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + if (wallet->IsMine(txout)) + continue; + + if (!wtx.mapValue.count("to") || wtx.mapValue["to"].empty()) + { + // Offline transaction + CTxDestination address; + if (ExtractDestination(txout.scriptPubKey, address)) + { + strHTML += "" + tr("To") + ": "; + if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].empty()) + strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address]) + " "; + strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(address).ToString()); + strHTML += "
"; + } + } + + strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -txout.nValue) + "
"; + } + + if (fAllToMe) + { + // Payment to self + int64 nChange = wtx.GetChange(); + int64 nValue = nCredit - nChange; + strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -nValue) + "
"; + strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nValue) + "
"; + } + + int64 nTxFee = nDebit - wtx.GetValueOut(); + if (nTxFee > 0) + strHTML += "" + tr("Transaction fee") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -nTxFee) + "
"; + } + else + { + // + // Mixed debit transaction + // + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + if (wallet->IsMine(txin)) + strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -wallet->GetDebit(txin)) + "
"; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (wallet->IsMine(txout)) + strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, wallet->GetCredit(txout)) + "
"; + } + } + + strHTML += "" + tr("Net amount") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nNet, true) + "
"; + + // + // Message + // + if (wtx.mapValue.count("message") && !wtx.mapValue["message"].empty()) + strHTML += "
" + tr("Message") + ":
" + GUIUtil::HtmlEscape(wtx.mapValue["message"], true) + "
"; + if (wtx.mapValue.count("comment") && !wtx.mapValue["comment"].empty()) + strHTML += "
" + tr("Comment") + ":
" + GUIUtil::HtmlEscape(wtx.mapValue["comment"], true) + "
"; + + strHTML += "" + tr("Transaction ID") + ": " + wtx.GetHash().ToString().c_str() + "
"; + + if (wtx.IsCoinBase()) + strHTML += "
" + tr("Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to \"not accepted\" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.") + "
"; + + // + // Debug view + // + if (fDebug) + { + strHTML += "

" + tr("Debug information") + "

"; + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + if(wallet->IsMine(txin)) + strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -wallet->GetDebit(txin)) + "
"; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if(wallet->IsMine(txout)) + strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, wallet->GetCredit(txout)) + "
"; + + strHTML += "
" + tr("Transaction") + ":
"; + strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true); + + strHTML += "
" + tr("Inputs") + ":"; + strHTML += "
    "; + + { + LOCK(wallet->cs_wallet); + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + { + COutPoint prevout = txin.prevout; + + CCoins prev; + if(pcoinsTip->GetCoins(prevout.hash, prev)) + { + if (prevout.n < prev.vout.size()) + { + strHTML += "
  • "; + const CTxOut &vout = prev.vout[prevout.n]; + CTxDestination address; + if (ExtractDestination(vout.scriptPubKey, address)) + { + if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].empty()) + strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address]) + " "; + strHTML += QString::fromStdString(CBitcoinAddress(address).ToString()); + } + strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, vout.nValue); + strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) ? tr("true") : tr("false")) + "
  • "; + } + } + } + } + + strHTML += "
"; + } + + strHTML += "
"; + } + return strHTML; +} diff --git a/src/qt/transactiondesc.h b/src/qt/transactiondesc.h new file mode 100644 index 0000000..cb0dda5 --- /dev/null +++ b/src/qt/transactiondesc.h @@ -0,0 +1,25 @@ +#ifndef TRANSACTIONDESC_H +#define TRANSACTIONDESC_H + +#include +#include + +class CWallet; +class CWalletTx; + +/** Provide a human-readable extended HTML description of a transaction. + */ +class TransactionDesc: public QObject +{ + Q_OBJECT + +public: + static QString toHTML(CWallet *wallet, CWalletTx &wtx); + +private: + TransactionDesc() {} + + static QString FormatTxStatus(const CWalletTx& wtx); +}; + +#endif // TRANSACTIONDESC_H diff --git a/src/qt/transactiondescdialog.cpp b/src/qt/transactiondescdialog.cpp new file mode 100644 index 0000000..3bd4808 --- /dev/null +++ b/src/qt/transactiondescdialog.cpp @@ -0,0 +1,20 @@ +#include "transactiondescdialog.h" +#include "ui_transactiondescdialog.h" + +#include "transactiontablemodel.h" + +#include + +TransactionDescDialog::TransactionDescDialog(const QModelIndex &idx, QWidget *parent) : + QDialog(parent), + ui(new Ui::TransactionDescDialog) +{ + ui->setupUi(this); + QString desc = idx.data(TransactionTableModel::LongDescriptionRole).toString(); + ui->detailText->setHtml(desc); +} + +TransactionDescDialog::~TransactionDescDialog() +{ + delete ui; +} diff --git a/src/qt/transactiondescdialog.h b/src/qt/transactiondescdialog.h new file mode 100644 index 0000000..f7ceacb --- /dev/null +++ b/src/qt/transactiondescdialog.h @@ -0,0 +1,27 @@ +#ifndef TRANSACTIONDESCDIALOG_H +#define TRANSACTIONDESCDIALOG_H + +#include + +namespace Ui { + class TransactionDescDialog; +} + +QT_BEGIN_NAMESPACE +class QModelIndex; +QT_END_NAMESPACE + +/** Dialog showing transaction details. */ +class TransactionDescDialog : public QDialog +{ + Q_OBJECT + +public: + explicit TransactionDescDialog(const QModelIndex &idx, QWidget *parent = 0); + ~TransactionDescDialog(); + +private: + Ui::TransactionDescDialog *ui; +}; + +#endif // TRANSACTIONDESCDIALOG_H diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp new file mode 100644 index 0000000..068e555 --- /dev/null +++ b/src/qt/transactionfilterproxy.cpp @@ -0,0 +1,87 @@ +#include "transactionfilterproxy.h" + +#include "transactiontablemodel.h" + +#include + +#include + +// Earliest date that can be represented (far in the past) +const QDateTime TransactionFilterProxy::MIN_DATE = QDateTime::fromTime_t(0); +// Last date that can be represented (far in the future) +const QDateTime TransactionFilterProxy::MAX_DATE = QDateTime::fromTime_t(0xFFFFFFFF); + +TransactionFilterProxy::TransactionFilterProxy(QObject *parent) : + QSortFilterProxyModel(parent), + dateFrom(MIN_DATE), + dateTo(MAX_DATE), + addrPrefix(), + typeFilter(ALL_TYPES), + minAmount(0), + limitRows(-1) +{ +} + +bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); + + int type = index.data(TransactionTableModel::TypeRole).toInt(); + QDateTime datetime = index.data(TransactionTableModel::DateRole).toDateTime(); + QString address = index.data(TransactionTableModel::AddressRole).toString(); + QString label = index.data(TransactionTableModel::LabelRole).toString(); + qint64 amount = llabs(index.data(TransactionTableModel::AmountRole).toLongLong()); + + if(!(TYPE(type) & typeFilter)) + return false; + if(datetime < dateFrom || datetime > dateTo) + return false; + if (!address.contains(addrPrefix, Qt::CaseInsensitive) && !label.contains(addrPrefix, Qt::CaseInsensitive)) + return false; + if(amount < minAmount) + return false; + + return true; +} + +void TransactionFilterProxy::setDateRange(const QDateTime &from, const QDateTime &to) +{ + this->dateFrom = from; + this->dateTo = to; + invalidateFilter(); +} + +void TransactionFilterProxy::setAddressPrefix(const QString &addrPrefix) +{ + this->addrPrefix = addrPrefix; + invalidateFilter(); +} + +void TransactionFilterProxy::setTypeFilter(quint32 modes) +{ + this->typeFilter = modes; + invalidateFilter(); +} + +void TransactionFilterProxy::setMinAmount(qint64 minimum) +{ + this->minAmount = minimum; + invalidateFilter(); +} + +void TransactionFilterProxy::setLimit(int limit) +{ + this->limitRows = limit; +} + +int TransactionFilterProxy::rowCount(const QModelIndex &parent) const +{ + if(limitRows != -1) + { + return std::min(QSortFilterProxyModel::rowCount(parent), limitRows); + } + else + { + return QSortFilterProxyModel::rowCount(parent); + } +} diff --git a/src/qt/transactionfilterproxy.h b/src/qt/transactionfilterproxy.h new file mode 100644 index 0000000..1aea85a --- /dev/null +++ b/src/qt/transactionfilterproxy.h @@ -0,0 +1,49 @@ +#ifndef TRANSACTIONFILTERPROXY_H +#define TRANSACTIONFILTERPROXY_H + +#include +#include + +/** Filter the transaction list according to pre-specified rules. */ +class TransactionFilterProxy : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + explicit TransactionFilterProxy(QObject *parent = 0); + + /** Earliest date that can be represented (far in the past) */ + static const QDateTime MIN_DATE; + /** Last date that can be represented (far in the future) */ + static const QDateTime MAX_DATE; + /** Type filter bit field (all types) */ + static const quint32 ALL_TYPES = 0xFFFFFFFF; + + static quint32 TYPE(int type) { return 1< TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx) +{ + QList parts; + int64 nTime = wtx.GetTxTime(); + int64 nCredit = wtx.GetCredit(true); + int64 nDebit = wtx.GetDebit(); + int64 nNet = nCredit - nDebit; + uint256 hash = wtx.GetHash(); + std::map mapValue = wtx.mapValue; + + if (nNet > 0 || wtx.IsCoinBase()) + { + // + // Credit + // + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + if(wallet->IsMine(txout)) + { + TransactionRecord sub(hash, nTime); + CTxDestination address; + sub.idx = parts.size(); // sequence number + sub.credit = txout.nValue; + if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address)) + { + // Received by Bitcoin Address + sub.type = TransactionRecord::RecvWithAddress; + sub.address = CBitcoinAddress(address).ToString(); + } + else + { + // Received by IP connection (deprecated features), or a multisignature or other non-simple transaction + sub.type = TransactionRecord::RecvFromOther; + sub.address = mapValue["from"]; + } + if (wtx.IsCoinBase()) + { + // Generated + sub.type = TransactionRecord::Generated; + } + + parts.append(sub); + } + } + } + else + { + bool fAllFromMe = true; + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + fAllFromMe = fAllFromMe && wallet->IsMine(txin); + + bool fAllToMe = true; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + fAllToMe = fAllToMe && wallet->IsMine(txout); + + if (fAllFromMe && fAllToMe) + { + // Payment to self + int64 nChange = wtx.GetChange(); + + parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "", + -(nDebit - nChange), nCredit - nChange)); + } + else if (fAllFromMe) + { + // + // Debit + // + int64 nTxFee = nDebit - wtx.GetValueOut(); + + for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++) + { + const CTxOut& txout = wtx.vout[nOut]; + TransactionRecord sub(hash, nTime); + sub.idx = parts.size(); + + if(wallet->IsMine(txout)) + { + // Ignore parts sent to self, as this is usually the change + // from a transaction sent back to our own address. + continue; + } + + CTxDestination address; + if (ExtractDestination(txout.scriptPubKey, address)) + { + // Sent to Bitcoin Address + sub.type = TransactionRecord::SendToAddress; + sub.address = CBitcoinAddress(address).ToString(); + } + else + { + // Sent to IP, or other non-address transaction like OP_EVAL + sub.type = TransactionRecord::SendToOther; + sub.address = mapValue["to"]; + } + + int64 nValue = txout.nValue; + /* Add fee to first output */ + if (nTxFee > 0) + { + nValue += nTxFee; + nTxFee = 0; + } + sub.debit = -nValue; + + parts.append(sub); + } + } + else + { + // + // Mixed debit transaction, can't break down payees + // + parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0)); + } + } + + return parts; +} + +void TransactionRecord::updateStatus(const CWalletTx &wtx) +{ + // Determine transaction status + + // Find the block the tx is in + CBlockIndex* pindex = NULL; + std::map::iterator mi = mapBlockIndex.find(wtx.hashBlock); + if (mi != mapBlockIndex.end()) + pindex = (*mi).second; + + // Sort order, unrecorded transactions sort to the top + status.sortKey = strprintf("%010d-%01d-%010u-%03d", + (pindex ? pindex->nHeight : std::numeric_limits::max()), + (wtx.IsCoinBase() ? 1 : 0), + wtx.nTimeReceived, + idx); + status.confirmed = wtx.IsConfirmed(); + status.depth = wtx.GetDepthInMainChain(); + status.cur_num_blocks = nBestHeight; + + if (!wtx.IsFinal()) + { + if (wtx.nLockTime < LOCKTIME_THRESHOLD) + { + status.status = TransactionStatus::OpenUntilBlock; + status.open_for = wtx.nLockTime - nBestHeight + 1; + } + else + { + status.status = TransactionStatus::OpenUntilDate; + status.open_for = wtx.nLockTime; + } + } + else + { + if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + { + status.status = TransactionStatus::Offline; + } + else if (status.depth < NumConfirmations) + { + status.status = TransactionStatus::Unconfirmed; + } + else + { + status.status = TransactionStatus::HaveConfirmations; + } + } + + // For generated transactions, determine maturity + if(type == TransactionRecord::Generated) + { + int64 nCredit = wtx.GetCredit(true); + if (nCredit == 0) + { + status.maturity = TransactionStatus::Immature; + + if (wtx.IsInMainChain()) + { + status.matures_in = wtx.GetBlocksToMaturity(); + + // Check if the block was requested by anyone + if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + status.maturity = TransactionStatus::MaturesWarning; + } + else + { + status.maturity = TransactionStatus::NotAccepted; + } + } + else + { + status.maturity = TransactionStatus::Mature; + } + } +} + +bool TransactionRecord::statusUpdateNeeded() +{ + return status.cur_num_blocks != nBestHeight; +} + +std::string TransactionRecord::getTxID() +{ + return hash.ToString() + strprintf("-%03d", idx); +} + diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h new file mode 100644 index 0000000..9581427 --- /dev/null +++ b/src/qt/transactionrecord.h @@ -0,0 +1,131 @@ +#ifndef TRANSACTIONRECORD_H +#define TRANSACTIONRECORD_H + +#include "uint256.h" + +#include + +class CWallet; +class CWalletTx; + +/** UI model for transaction status. The transaction status is the part of a transaction that will change over time. + */ +class TransactionStatus +{ +public: + TransactionStatus(): + confirmed(false), sortKey(""), maturity(Mature), + matures_in(0), status(Offline), depth(0), open_for(0), cur_num_blocks(-1) + { } + + enum Maturity + { + Immature, + Mature, + MaturesWarning, /**< Transaction will likely not mature because no nodes have confirmed */ + NotAccepted + }; + + enum Status { + OpenUntilDate, + OpenUntilBlock, + Offline, + Unconfirmed, + HaveConfirmations + }; + + bool confirmed; + std::string sortKey; + + /** @name Generated (mined) transactions + @{*/ + Maturity maturity; + int matures_in; + /**@}*/ + + /** @name Reported status + @{*/ + Status status; + int64 depth; + int64 open_for; /**< Timestamp if status==OpenUntilDate, otherwise number + of additional blocks that need to be mined before + finalization */ + /**@}*/ + + /** Current number of blocks (to know whether cached status is still valid) */ + int cur_num_blocks; +}; + +/** UI model for a transaction. A core transaction can be represented by multiple UI transactions if it has + multiple outputs. + */ +class TransactionRecord +{ +public: + enum Type + { + Other, + Generated, + SendToAddress, + SendToOther, + RecvWithAddress, + RecvFromOther, + SendToSelf + }; + + /** Number of confirmation needed for transaction */ + static const int NumConfirmations = 4; + + TransactionRecord(): + hash(), time(0), type(Other), address(""), debit(0), credit(0), idx(0) + { + } + + TransactionRecord(uint256 hash, int64 time): + hash(hash), time(time), type(Other), address(""), debit(0), + credit(0), idx(0) + { + } + + TransactionRecord(uint256 hash, int64 time, + Type type, const std::string &address, + int64 debit, int64 credit): + hash(hash), time(time), type(type), address(address), debit(debit), credit(credit), + idx(0) + { + } + + /** Decompose CWallet transaction to model transaction records. + */ + static bool showTransaction(const CWalletTx &wtx); + static QList decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx); + + /** @name Immutable transaction attributes + @{*/ + uint256 hash; + int64 time; + Type type; + std::string address; + int64 debit; + int64 credit; + /**@}*/ + + /** Subtransaction index, for sort key */ + int idx; + + /** Status: can change with block chain update */ + TransactionStatus status; + + /** Return the unique identifier for this transaction (part) */ + std::string getTxID(); + + /** Update status from core wallet tx. + */ + void updateStatus(const CWalletTx &wtx); + + /** Return whether a status update is needed. + */ + bool statusUpdateNeeded(); +}; + +#endif // TRANSACTIONRECORD_H diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp new file mode 100644 index 0000000..baf1e16 --- /dev/null +++ b/src/qt/transactiontablemodel.cpp @@ -0,0 +1,632 @@ +#include "transactiontablemodel.h" + +#include "guiutil.h" +#include "transactionrecord.h" +#include "guiconstants.h" +#include "transactiondesc.h" +#include "walletmodel.h" +#include "optionsmodel.h" +#include "addresstablemodel.h" +#include "bitcoinunits.h" + +#include "wallet.h" +#include "ui_interface.h" + +#include +#include +#include +#include +#include + +// Amount column is right-aligned it contains numbers +static int column_alignments[] = { + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignLeft|Qt::AlignVCenter, + Qt::AlignRight|Qt::AlignVCenter + }; + +// Comparison operator for sort/binary search of model tx list +struct TxLessThan +{ + bool operator()(const TransactionRecord &a, const TransactionRecord &b) const + { + return a.hash < b.hash; + } + bool operator()(const TransactionRecord &a, const uint256 &b) const + { + return a.hash < b; + } + bool operator()(const uint256 &a, const TransactionRecord &b) const + { + return a < b.hash; + } +}; + +// Private implementation +class TransactionTablePriv +{ +public: + TransactionTablePriv(CWallet *wallet, TransactionTableModel *parent): + wallet(wallet), + parent(parent) + { + } + CWallet *wallet; + TransactionTableModel *parent; + + /* Local cache of wallet. + * As it is in the same order as the CWallet, by definition + * this is sorted by sha256. + */ + QList cachedWallet; + + /* Query entire wallet anew from core. + */ + void refreshWallet() + { + OutputDebugStringF("refreshWallet\n"); + cachedWallet.clear(); + { + LOCK(wallet->cs_wallet); + for(std::map::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it) + { + if(TransactionRecord::showTransaction(it->second)) + cachedWallet.append(TransactionRecord::decomposeTransaction(wallet, it->second)); + } + } + } + + /* Update our model of the wallet incrementally, to synchronize our model of the wallet + with that of the core. + + Call with transaction that was added, removed or changed. + */ + void updateWallet(const uint256 &hash, int status) + { + OutputDebugStringF("updateWallet %s %i\n", hash.ToString().c_str(), status); + { + LOCK(wallet->cs_wallet); + + // Find transaction in wallet + std::map::iterator mi = wallet->mapWallet.find(hash); + bool inWallet = mi != wallet->mapWallet.end(); + + // Find bounds of this transaction in model + QList::iterator lower = qLowerBound( + cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan()); + QList::iterator upper = qUpperBound( + cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan()); + int lowerIndex = (lower - cachedWallet.begin()); + int upperIndex = (upper - cachedWallet.begin()); + bool inModel = (lower != upper); + + // Determine whether to show transaction or not + bool showTransaction = (inWallet && TransactionRecord::showTransaction(mi->second)); + + if(status == CT_UPDATED) + { + if(showTransaction && !inModel) + status = CT_NEW; /* Not in model, but want to show, treat as new */ + if(!showTransaction && inModel) + status = CT_DELETED; /* In model, but want to hide, treat as deleted */ + } + + OutputDebugStringF(" inWallet=%i inModel=%i Index=%i-%i showTransaction=%i derivedStatus=%i\n", + inWallet, inModel, lowerIndex, upperIndex, showTransaction, status); + + switch(status) + { + case CT_NEW: + if(inModel) + { + OutputDebugStringF("Warning: updateWallet: Got CT_NEW, but transaction is already in model\n"); + break; + } + if(!inWallet) + { + OutputDebugStringF("Warning: updateWallet: Got CT_NEW, but transaction is not in wallet\n"); + break; + } + if(showTransaction) + { + // Added -- insert at the right position + QList toInsert = + TransactionRecord::decomposeTransaction(wallet, mi->second); + if(!toInsert.isEmpty()) /* only if something to insert */ + { + parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1); + int insert_idx = lowerIndex; + foreach(const TransactionRecord &rec, toInsert) + { + cachedWallet.insert(insert_idx, rec); + insert_idx += 1; + } + parent->endInsertRows(); + } + } + break; + case CT_DELETED: + if(!inModel) + { + OutputDebugStringF("Warning: updateWallet: Got CT_DELETED, but transaction is not in model\n"); + break; + } + // Removed -- remove entire transaction from table + parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); + cachedWallet.erase(lower, upper); + parent->endRemoveRows(); + break; + case CT_UPDATED: + // Miscellaneous updates -- nothing to do, status update will take care of this, and is only computed for + // visible transactions. + break; + } + } + } + + int size() + { + return cachedWallet.size(); + } + + TransactionRecord *index(int idx) + { + if(idx >= 0 && idx < cachedWallet.size()) + { + TransactionRecord *rec = &cachedWallet[idx]; + + // If a status update is needed (blocks came in since last check), + // update the status of this transaction from the wallet. Otherwise, + // simply re-use the cached status. + if(rec->statusUpdateNeeded()) + { + { + LOCK(wallet->cs_wallet); + std::map::iterator mi = wallet->mapWallet.find(rec->hash); + + if(mi != wallet->mapWallet.end()) + { + rec->updateStatus(mi->second); + } + } + } + return rec; + } + else + { + return 0; + } + } + + QString describe(TransactionRecord *rec) + { + { + LOCK(wallet->cs_wallet); + std::map::iterator mi = wallet->mapWallet.find(rec->hash); + if(mi != wallet->mapWallet.end()) + { + return TransactionDesc::toHTML(wallet, mi->second); + } + } + return QString(""); + } + +}; + +TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel *parent): + QAbstractTableModel(parent), + wallet(wallet), + walletModel(parent), + priv(new TransactionTablePriv(wallet, this)), + cachedNumBlocks(0) +{ + columns << QString() << tr("Date") << tr("Type") << tr("Address") << tr("Amount"); + + priv->refreshWallet(); + + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(updateConfirmations())); + timer->start(MODEL_UPDATE_DELAY); + + connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); +} + +TransactionTableModel::~TransactionTableModel() +{ + delete priv; +} + +void TransactionTableModel::updateTransaction(const QString &hash, int status) +{ + uint256 updated; + updated.SetHex(hash.toStdString()); + + priv->updateWallet(updated, status); +} + +void TransactionTableModel::updateConfirmations() +{ + if(nBestHeight != cachedNumBlocks) + { + cachedNumBlocks = nBestHeight; + // Blocks came in since last poll. + // Invalidate status (number of confirmations) and (possibly) description + // for all rows. Qt is smart enough to only actually request the data for the + // visible rows. + emit dataChanged(index(0, Status), index(priv->size()-1, Status)); + emit dataChanged(index(0, ToAddress), index(priv->size()-1, ToAddress)); + } +} + +int TransactionTableModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return priv->size(); +} + +int TransactionTableModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return columns.length(); +} + +QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) const +{ + QString status; + + switch(wtx->status.status) + { + case TransactionStatus::OpenUntilBlock: + status = tr("Open for %n more block(s)","",wtx->status.open_for); + break; + case TransactionStatus::OpenUntilDate: + status = tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx->status.open_for)); + break; + case TransactionStatus::Offline: + status = tr("Offline (%1 confirmations)").arg(wtx->status.depth); + break; + case TransactionStatus::Unconfirmed: + status = tr("Unconfirmed (%1 of %2 confirmations)").arg(wtx->status.depth).arg(TransactionRecord::NumConfirmations); + break; + case TransactionStatus::HaveConfirmations: + status = tr("Confirmed (%1 confirmations)").arg(wtx->status.depth); + break; + } + if(wtx->type == TransactionRecord::Generated) + { + switch(wtx->status.maturity) + { + case TransactionStatus::Immature: + status += "\n" + tr("Mined balance will be available when it matures in %n more block(s)", "", wtx->status.matures_in); + break; + case TransactionStatus::Mature: + break; + case TransactionStatus::MaturesWarning: + status += "\n" + tr("This block was not received by any other nodes and will probably not be accepted!"); + break; + case TransactionStatus::NotAccepted: + status += "\n" + tr("Generated but not accepted"); + break; + } + } + + return status; +} + +QString TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const +{ + if(wtx->time) + { + return GUIUtil::dateTimeStr(wtx->time); + } + else + { + return QString(); + } +} + +/* Look up address in address book, if found return label (address) + otherwise just return (address) + */ +QString TransactionTableModel::lookupAddress(const std::string &address, bool tooltip) const +{ + QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(address)); + QString description; + if(!label.isEmpty()) + { + description += label + QString(" "); + } + if(label.isEmpty() || walletModel->getOptionsModel()->getDisplayAddresses() || tooltip) + { + description += QString("(") + QString::fromStdString(address) + QString(")"); + } + return description; +} + +QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const +{ + switch(wtx->type) + { + case TransactionRecord::RecvWithAddress: + return tr("Received with"); + case TransactionRecord::RecvFromOther: + return tr("Received from"); + case TransactionRecord::SendToAddress: + case TransactionRecord::SendToOther: + return tr("Sent to"); + case TransactionRecord::SendToSelf: + return tr("Payment to yourself"); + case TransactionRecord::Generated: + return tr("Mined"); + default: + return QString(); + } +} + +QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx) const +{ + switch(wtx->type) + { + case TransactionRecord::Generated: + return QIcon(":/icons/tx_mined"); + case TransactionRecord::RecvWithAddress: + case TransactionRecord::RecvFromOther: + return QIcon(":/icons/tx_input"); + case TransactionRecord::SendToAddress: + case TransactionRecord::SendToOther: + return QIcon(":/icons/tx_output"); + default: + return QIcon(":/icons/tx_inout"); + } + return QVariant(); +} + +QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const +{ + switch(wtx->type) + { + case TransactionRecord::RecvFromOther: + return QString::fromStdString(wtx->address); + case TransactionRecord::RecvWithAddress: + case TransactionRecord::SendToAddress: + case TransactionRecord::Generated: + return lookupAddress(wtx->address, tooltip); + case TransactionRecord::SendToOther: + return QString::fromStdString(wtx->address); + case TransactionRecord::SendToSelf: + default: + return tr("(n/a)"); + } +} + +QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const +{ + // Show addresses without label in a less visible color + switch(wtx->type) + { + case TransactionRecord::RecvWithAddress: + case TransactionRecord::SendToAddress: + case TransactionRecord::Generated: + { + QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(wtx->address)); + if(label.isEmpty()) + return COLOR_BAREADDRESS; + } break; + case TransactionRecord::SendToSelf: + return COLOR_BAREADDRESS; + default: + break; + } + return QVariant(); +} + +QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed) const +{ + QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit); + if(showUnconfirmed) + { + if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature) + { + str = QString("[") + str + QString("]"); + } + } + return QString(str); +} + +QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) const +{ + if(wtx->type == TransactionRecord::Generated) + { + switch(wtx->status.maturity) + { + case TransactionStatus::Immature: { + int total = wtx->status.depth + wtx->status.matures_in; + int part = (wtx->status.depth * 4 / total) + 1; + return QIcon(QString(":/icons/transaction_%1").arg(part)); + } + case TransactionStatus::Mature: + return QIcon(":/icons/transaction_confirmed"); + case TransactionStatus::MaturesWarning: + case TransactionStatus::NotAccepted: + return QIcon(":/icons/transaction_0"); + } + } + else + { + switch(wtx->status.status) + { + case TransactionStatus::OpenUntilBlock: + case TransactionStatus::OpenUntilDate: + return QColor(64,64,255); + break; + case TransactionStatus::Offline: + return QColor(192,192,192); + case TransactionStatus::Unconfirmed: + switch(wtx->status.depth) + { + case 0: return QIcon(":/icons/transaction_0"); + case 1: return QIcon(":/icons/transaction_1"); + case 2: return QIcon(":/icons/transaction_2"); + case 3: return QIcon(":/icons/transaction_3"); + case 4: return QIcon(":/icons/transaction_4"); + default: return QIcon(":/icons/transaction_5"); + }; + case TransactionStatus::HaveConfirmations: + return QIcon(":/icons/transaction_confirmed"); + } + } + return QColor(0,0,0); +} + +QString TransactionTableModel::formatTooltip(const TransactionRecord *rec) const +{ + QString tooltip = formatTxStatus(rec) + QString("\n") + formatTxType(rec); + if(rec->type==TransactionRecord::RecvFromOther || rec->type==TransactionRecord::SendToOther || + rec->type==TransactionRecord::SendToAddress || rec->type==TransactionRecord::RecvWithAddress) + { + tooltip += QString(" ") + formatTxToAddress(rec, true); + } + return tooltip; +} + +QVariant TransactionTableModel::data(const QModelIndex &index, int role) const +{ + if(!index.isValid()) + return QVariant(); + TransactionRecord *rec = static_cast(index.internalPointer()); + + switch(role) + { + case Qt::DecorationRole: + switch(index.column()) + { + case Status: + return txStatusDecoration(rec); + case ToAddress: + return txAddressDecoration(rec); + } + break; + case Qt::DisplayRole: + switch(index.column()) + { + case Date: + return formatTxDate(rec); + case Type: + return formatTxType(rec); + case ToAddress: + return formatTxToAddress(rec, false); + case Amount: + return formatTxAmount(rec); + } + break; + case Qt::EditRole: + // Edit role is used for sorting, so return the unformatted values + switch(index.column()) + { + case Status: + return QString::fromStdString(rec->status.sortKey); + case Date: + return rec->time; + case Type: + return formatTxType(rec); + case ToAddress: + return formatTxToAddress(rec, true); + case Amount: + return rec->credit + rec->debit; + } + break; + case Qt::ToolTipRole: + return formatTooltip(rec); + case Qt::TextAlignmentRole: + return column_alignments[index.column()]; + case Qt::ForegroundRole: + // Non-confirmed transactions are grey + if(!rec->status.confirmed) + { + return COLOR_UNCONFIRMED; + } + if(index.column() == Amount && (rec->credit+rec->debit) < 0) + { + return COLOR_NEGATIVE; + } + if(index.column() == ToAddress) + { + return addressColor(rec); + } + break; + case TypeRole: + return rec->type; + case DateRole: + return QDateTime::fromTime_t(static_cast(rec->time)); + case LongDescriptionRole: + return priv->describe(rec); + case AddressRole: + return QString::fromStdString(rec->address); + case LabelRole: + return walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(rec->address)); + case AmountRole: + return rec->credit + rec->debit; + case TxIDRole: + return QString::fromStdString(rec->getTxID()); + case ConfirmedRole: + // Return True if transaction counts for balance + return rec->status.confirmed && !(rec->type == TransactionRecord::Generated && + rec->status.maturity != TransactionStatus::Mature); + case FormattedAmountRole: + return formatTxAmount(rec, false); + } + return QVariant(); +} + +QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(orientation == Qt::Horizontal) + { + if(role == Qt::DisplayRole) + { + return columns[section]; + } + else if (role == Qt::TextAlignmentRole) + { + return column_alignments[section]; + } else if (role == Qt::ToolTipRole) + { + switch(section) + { + case Status: + return tr("Transaction status. Hover over this field to show number of confirmations."); + case Date: + return tr("Date and time that the transaction was received."); + case Type: + return tr("Type of transaction."); + case ToAddress: + return tr("Destination address of transaction."); + case Amount: + return tr("Amount removed from or added to balance."); + } + } + } + return QVariant(); +} + +QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + TransactionRecord *data = priv->index(row); + if(data) + { + return createIndex(row, column, priv->index(row)); + } + else + { + return QModelIndex(); + } +} + +void TransactionTableModel::updateDisplayUnit() +{ + // emit dataChanged to update Amount column with the current unit + emit dataChanged(index(0, Amount), index(priv->size()-1, Amount)); +} diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h new file mode 100644 index 0000000..6b2961c --- /dev/null +++ b/src/qt/transactiontablemodel.h @@ -0,0 +1,86 @@ +#ifndef TRANSACTIONTABLEMODEL_H +#define TRANSACTIONTABLEMODEL_H + +#include +#include + +class CWallet; +class TransactionTablePriv; +class TransactionRecord; +class WalletModel; + +/** UI model for the transaction table of a wallet. + */ +class TransactionTableModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + explicit TransactionTableModel(CWallet* wallet, WalletModel *parent = 0); + ~TransactionTableModel(); + + enum ColumnIndex { + Status = 0, + Date = 1, + Type = 2, + ToAddress = 3, + Amount = 4 + }; + + /** Roles to get specific information from a transaction row. + These are independent of column. + */ + enum RoleIndex { + /** Type of transaction */ + TypeRole = Qt::UserRole, + /** Date and time this transaction was created */ + DateRole, + /** Long description (HTML format) */ + LongDescriptionRole, + /** Address of transaction */ + AddressRole, + /** Label of address related to transaction */ + LabelRole, + /** Net amount of transaction */ + AmountRole, + /** Unique identifier */ + TxIDRole, + /** Is transaction confirmed? */ + ConfirmedRole, + /** Formatted amount, without brackets when unconfirmed */ + FormattedAmountRole + }; + + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; + +private: + CWallet* wallet; + WalletModel *walletModel; + QStringList columns; + TransactionTablePriv *priv; + int cachedNumBlocks; + + QString lookupAddress(const std::string &address, bool tooltip) const; + QVariant addressColor(const TransactionRecord *wtx) const; + QString formatTxStatus(const TransactionRecord *wtx) const; + QString formatTxDate(const TransactionRecord *wtx) const; + QString formatTxType(const TransactionRecord *wtx) const; + QString formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const; + QString formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true) const; + QString formatTooltip(const TransactionRecord *rec) const; + QVariant txStatusDecoration(const TransactionRecord *wtx) const; + QVariant txAddressDecoration(const TransactionRecord *wtx) const; + +public slots: + void updateTransaction(const QString &hash, int status); + void updateConfirmations(); + void updateDisplayUnit(); + + friend class TransactionTablePriv; +}; + +#endif // TRANSACTIONTABLEMODEL_H diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp new file mode 100644 index 0000000..a43e29c --- /dev/null +++ b/src/qt/transactionview.cpp @@ -0,0 +1,435 @@ +#include "transactionview.h" + +#include "transactionfilterproxy.h" +#include "transactionrecord.h" +#include "walletmodel.h" +#include "addresstablemodel.h" +#include "transactiontablemodel.h" +#include "bitcoinunits.h" +#include "csvmodelwriter.h" +#include "transactiondescdialog.h" +#include "editaddressdialog.h" +#include "optionsmodel.h" +#include "guiutil.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TransactionView::TransactionView(QWidget *parent) : + QWidget(parent), model(0), transactionProxyModel(0), + transactionView(0) +{ + // Build filter row + setContentsMargins(0,0,0,0); + + QHBoxLayout *hlayout = new QHBoxLayout(); + hlayout->setContentsMargins(0,0,0,0); +#ifdef Q_OS_MAC + hlayout->setSpacing(5); + hlayout->addSpacing(26); +#else + hlayout->setSpacing(0); + hlayout->addSpacing(23); +#endif + + dateWidget = new QComboBox(this); +#ifdef Q_OS_MAC + dateWidget->setFixedWidth(121); +#else + dateWidget->setFixedWidth(120); +#endif + dateWidget->addItem(tr("All"), All); + dateWidget->addItem(tr("Today"), Today); + dateWidget->addItem(tr("This week"), ThisWeek); + dateWidget->addItem(tr("This month"), ThisMonth); + dateWidget->addItem(tr("Last month"), LastMonth); + dateWidget->addItem(tr("This year"), ThisYear); + dateWidget->addItem(tr("Range..."), Range); + hlayout->addWidget(dateWidget); + + typeWidget = new QComboBox(this); +#ifdef Q_OS_MAC + typeWidget->setFixedWidth(121); +#else + typeWidget->setFixedWidth(120); +#endif + + typeWidget->addItem(tr("All"), TransactionFilterProxy::ALL_TYPES); + typeWidget->addItem(tr("Received with"), TransactionFilterProxy::TYPE(TransactionRecord::RecvWithAddress) | + TransactionFilterProxy::TYPE(TransactionRecord::RecvFromOther)); + typeWidget->addItem(tr("Sent to"), TransactionFilterProxy::TYPE(TransactionRecord::SendToAddress) | + TransactionFilterProxy::TYPE(TransactionRecord::SendToOther)); + typeWidget->addItem(tr("To yourself"), TransactionFilterProxy::TYPE(TransactionRecord::SendToSelf)); + typeWidget->addItem(tr("Mined"), TransactionFilterProxy::TYPE(TransactionRecord::Generated)); + typeWidget->addItem(tr("Other"), TransactionFilterProxy::TYPE(TransactionRecord::Other)); + + hlayout->addWidget(typeWidget); + + addressWidget = new QLineEdit(this); +#if QT_VERSION >= 0x040700 + /* Do not move this to the XML file, Qt before 4.7 will choke on it */ + addressWidget->setPlaceholderText(tr("Enter address or label to search")); +#endif + hlayout->addWidget(addressWidget); + + amountWidget = new QLineEdit(this); +#if QT_VERSION >= 0x040700 + /* Do not move this to the XML file, Qt before 4.7 will choke on it */ + amountWidget->setPlaceholderText(tr("Min amount")); +#endif +#ifdef Q_OS_MAC + amountWidget->setFixedWidth(97); +#else + amountWidget->setFixedWidth(100); +#endif + amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this)); + hlayout->addWidget(amountWidget); + + QVBoxLayout *vlayout = new QVBoxLayout(this); + vlayout->setContentsMargins(0,0,0,0); + vlayout->setSpacing(0); + + QTableView *view = new QTableView(this); + vlayout->addLayout(hlayout); + vlayout->addWidget(createDateRangeWidget()); + vlayout->addWidget(view); + vlayout->setSpacing(0); + int width = view->verticalScrollBar()->sizeHint().width(); + // Cover scroll bar width with spacing +#ifdef Q_OS_MAC + hlayout->addSpacing(width+2); +#else + hlayout->addSpacing(width); +#endif + // Always show scroll bar + view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + view->setTabKeyNavigation(false); + view->setContextMenuPolicy(Qt::CustomContextMenu); + + transactionView = view; + + // Actions + QAction *copyAddressAction = new QAction(tr("Copy address"), this); + QAction *copyLabelAction = new QAction(tr("Copy label"), this); + QAction *copyAmountAction = new QAction(tr("Copy amount"), this); + QAction *copyTxIDAction = new QAction(tr("Copy transaction ID"), this); + QAction *editLabelAction = new QAction(tr("Edit label"), this); + QAction *showDetailsAction = new QAction(tr("Show transaction details"), this); + + contextMenu = new QMenu(); + contextMenu->addAction(copyAddressAction); + contextMenu->addAction(copyLabelAction); + contextMenu->addAction(copyAmountAction); + contextMenu->addAction(copyTxIDAction); + contextMenu->addAction(editLabelAction); + contextMenu->addAction(showDetailsAction); + + // Connect actions + connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int))); + connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int))); + connect(addressWidget, SIGNAL(textChanged(QString)), this, SLOT(changedPrefix(QString))); + connect(amountWidget, SIGNAL(textChanged(QString)), this, SLOT(changedAmount(QString))); + + connect(view, SIGNAL(doubleClicked(QModelIndex)), this, SIGNAL(doubleClicked(QModelIndex))); + connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint))); + + connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress())); + connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel())); + connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount())); + connect(copyTxIDAction, SIGNAL(triggered()), this, SLOT(copyTxID())); + connect(editLabelAction, SIGNAL(triggered()), this, SLOT(editLabel())); + connect(showDetailsAction, SIGNAL(triggered()), this, SLOT(showDetails())); +} + +void TransactionView::setModel(WalletModel *model) +{ + this->model = model; + if(model) + { + transactionProxyModel = new TransactionFilterProxy(this); + transactionProxyModel->setSourceModel(model->getTransactionTableModel()); + transactionProxyModel->setDynamicSortFilter(true); + transactionProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); + transactionProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + + transactionProxyModel->setSortRole(Qt::EditRole); + + transactionView->setModel(transactionProxyModel); + transactionView->setAlternatingRowColors(true); + transactionView->setSelectionBehavior(QAbstractItemView::SelectRows); + transactionView->setSelectionMode(QAbstractItemView::ExtendedSelection); + transactionView->setSortingEnabled(true); + transactionView->sortByColumn(TransactionTableModel::Status, Qt::DescendingOrder); + transactionView->verticalHeader()->hide(); + + transactionView->horizontalHeader()->resizeSection(TransactionTableModel::Status, 23); + transactionView->horizontalHeader()->resizeSection(TransactionTableModel::Date, 120); + transactionView->horizontalHeader()->resizeSection(TransactionTableModel::Type, 120); +#if QT_VERSION < 0x050000 + transactionView->horizontalHeader()->setResizeMode(TransactionTableModel::ToAddress, QHeaderView::Stretch); +#else + transactionView->horizontalHeader()->setSectionResizeMode(TransactionTableModel::ToAddress, QHeaderView::Stretch); +#endif + transactionView->horizontalHeader()->resizeSection(TransactionTableModel::Amount, 100); + } +} + +void TransactionView::chooseDate(int idx) +{ + if(!transactionProxyModel) + return; + QDate current = QDate::currentDate(); + dateRangeWidget->setVisible(false); + switch(dateWidget->itemData(idx).toInt()) + { + case All: + transactionProxyModel->setDateRange( + TransactionFilterProxy::MIN_DATE, + TransactionFilterProxy::MAX_DATE); + break; + case Today: + transactionProxyModel->setDateRange( + QDateTime(current), + TransactionFilterProxy::MAX_DATE); + break; + case ThisWeek: { + // Find last Monday + QDate startOfWeek = current.addDays(-(current.dayOfWeek()-1)); + transactionProxyModel->setDateRange( + QDateTime(startOfWeek), + TransactionFilterProxy::MAX_DATE); + + } break; + case ThisMonth: + transactionProxyModel->setDateRange( + QDateTime(QDate(current.year(), current.month(), 1)), + TransactionFilterProxy::MAX_DATE); + break; + case LastMonth: + transactionProxyModel->setDateRange( + QDateTime(QDate(current.year(), current.month()-1, 1)), + QDateTime(QDate(current.year(), current.month(), 1))); + break; + case ThisYear: + transactionProxyModel->setDateRange( + QDateTime(QDate(current.year(), 1, 1)), + TransactionFilterProxy::MAX_DATE); + break; + case Range: + dateRangeWidget->setVisible(true); + dateRangeChanged(); + break; + } +} + +void TransactionView::chooseType(int idx) +{ + if(!transactionProxyModel) + return; + transactionProxyModel->setTypeFilter( + typeWidget->itemData(idx).toInt()); +} + +void TransactionView::changedPrefix(const QString &prefix) +{ + if(!transactionProxyModel) + return; + transactionProxyModel->setAddressPrefix(prefix); +} + +void TransactionView::changedAmount(const QString &amount) +{ + if(!transactionProxyModel) + return; + qint64 amount_parsed = 0; + if(BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), amount, &amount_parsed)) + { + transactionProxyModel->setMinAmount(amount_parsed); + } + else + { + transactionProxyModel->setMinAmount(0); + } +} + +void TransactionView::exportClicked() +{ + // CSV is currently the only supported format + QString filename = GUIUtil::getSaveFileName( + this, + tr("Export Transaction Data"), QString(), + tr("Comma separated file (*.csv)")); + + if (filename.isNull()) return; + + CSVModelWriter writer(filename); + + // name, column, role + writer.setModel(transactionProxyModel); + writer.addColumn(tr("Confirmed"), 0, TransactionTableModel::ConfirmedRole); + writer.addColumn(tr("Date"), 0, TransactionTableModel::DateRole); + writer.addColumn(tr("Type"), TransactionTableModel::Type, Qt::EditRole); + writer.addColumn(tr("Label"), 0, TransactionTableModel::LabelRole); + writer.addColumn(tr("Address"), 0, TransactionTableModel::AddressRole); + writer.addColumn(tr("Amount"), 0, TransactionTableModel::FormattedAmountRole); + writer.addColumn(tr("ID"), 0, TransactionTableModel::TxIDRole); + + if(!writer.write()) + { + QMessageBox::critical(this, tr("Error exporting"), tr("Could not write to file %1.").arg(filename), + QMessageBox::Abort, QMessageBox::Abort); + } +} + +void TransactionView::contextualMenu(const QPoint &point) +{ + QModelIndex index = transactionView->indexAt(point); + if(index.isValid()) + { + contextMenu->exec(QCursor::pos()); + } +} + +void TransactionView::copyAddress() +{ + GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::AddressRole); +} + +void TransactionView::copyLabel() +{ + GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::LabelRole); +} + +void TransactionView::copyAmount() +{ + GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::FormattedAmountRole); +} + +void TransactionView::copyTxID() +{ + GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::TxIDRole); +} + +void TransactionView::editLabel() +{ + if(!transactionView->selectionModel() ||!model) + return; + QModelIndexList selection = transactionView->selectionModel()->selectedRows(); + if(!selection.isEmpty()) + { + AddressTableModel *addressBook = model->getAddressTableModel(); + if(!addressBook) + return; + QString address = selection.at(0).data(TransactionTableModel::AddressRole).toString(); + if(address.isEmpty()) + { + // If this transaction has no associated address, exit + return; + } + // Is address in address book? Address book can miss address when a transaction is + // sent from outside the UI. + int idx = addressBook->lookupAddress(address); + if(idx != -1) + { + // Edit sending / receiving address + QModelIndex modelIdx = addressBook->index(idx, 0, QModelIndex()); + // Determine type of address, launch appropriate editor dialog type + QString type = modelIdx.data(AddressTableModel::TypeRole).toString(); + + EditAddressDialog dlg(type==AddressTableModel::Receive + ? EditAddressDialog::EditReceivingAddress + : EditAddressDialog::EditSendingAddress, + this); + dlg.setModel(addressBook); + dlg.loadRow(idx); + dlg.exec(); + } + else + { + // Add sending address + EditAddressDialog dlg(EditAddressDialog::NewSendingAddress, + this); + dlg.setModel(addressBook); + dlg.setAddress(address); + dlg.exec(); + } + } +} + +void TransactionView::showDetails() +{ + if(!transactionView->selectionModel()) + return; + QModelIndexList selection = transactionView->selectionModel()->selectedRows(); + if(!selection.isEmpty()) + { + TransactionDescDialog dlg(selection.at(0)); + dlg.exec(); + } +} + +QWidget *TransactionView::createDateRangeWidget() +{ + dateRangeWidget = new QFrame(); + dateRangeWidget->setFrameStyle(QFrame::Panel | QFrame::Raised); + dateRangeWidget->setContentsMargins(1,1,1,1); + QHBoxLayout *layout = new QHBoxLayout(dateRangeWidget); + layout->setContentsMargins(0,0,0,0); + layout->addSpacing(23); + layout->addWidget(new QLabel(tr("Range:"))); + + dateFrom = new QDateTimeEdit(this); + dateFrom->setDisplayFormat("dd/MM/yy"); + dateFrom->setCalendarPopup(true); + dateFrom->setMinimumWidth(100); + dateFrom->setDate(QDate::currentDate().addDays(-7)); + layout->addWidget(dateFrom); + layout->addWidget(new QLabel(tr("to"))); + + dateTo = new QDateTimeEdit(this); + dateTo->setDisplayFormat("dd/MM/yy"); + dateTo->setCalendarPopup(true); + dateTo->setMinimumWidth(100); + dateTo->setDate(QDate::currentDate()); + layout->addWidget(dateTo); + layout->addStretch(); + + // Hide by default + dateRangeWidget->setVisible(false); + + // Notify on change + connect(dateFrom, SIGNAL(dateChanged(QDate)), this, SLOT(dateRangeChanged())); + connect(dateTo, SIGNAL(dateChanged(QDate)), this, SLOT(dateRangeChanged())); + + return dateRangeWidget; +} + +void TransactionView::dateRangeChanged() +{ + if(!transactionProxyModel) + return; + transactionProxyModel->setDateRange( + QDateTime(dateFrom->date()), + QDateTime(dateTo->date()).addDays(1)); +} + +void TransactionView::focusTransaction(const QModelIndex &idx) +{ + if(!transactionProxyModel) + return; + QModelIndex targetIdx = transactionProxyModel->mapFromSource(idx); + transactionView->scrollTo(targetIdx); + transactionView->setCurrentIndex(targetIdx); + transactionView->setFocus(); +} diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h new file mode 100644 index 0000000..bb41a83 --- /dev/null +++ b/src/qt/transactionview.h @@ -0,0 +1,84 @@ +#ifndef TRANSACTIONVIEW_H +#define TRANSACTIONVIEW_H + +#include + +class WalletModel; +class TransactionFilterProxy; + +QT_BEGIN_NAMESPACE +class QTableView; +class QComboBox; +class QLineEdit; +class QModelIndex; +class QMenu; +class QFrame; +class QDateTimeEdit; +QT_END_NAMESPACE + +/** Widget showing the transaction list for a wallet, including a filter row. + Using the filter row, the user can view or export a subset of the transactions. + */ +class TransactionView : public QWidget +{ + Q_OBJECT + +public: + explicit TransactionView(QWidget *parent = 0); + + void setModel(WalletModel *model); + + // Date ranges for filter + enum DateEnum + { + All, + Today, + ThisWeek, + ThisMonth, + LastMonth, + ThisYear, + Range + }; + +private: + WalletModel *model; + TransactionFilterProxy *transactionProxyModel; + QTableView *transactionView; + + QComboBox *dateWidget; + QComboBox *typeWidget; + QLineEdit *addressWidget; + QLineEdit *amountWidget; + + QMenu *contextMenu; + + QFrame *dateRangeWidget; + QDateTimeEdit *dateFrom; + QDateTimeEdit *dateTo; + + QWidget *createDateRangeWidget(); + +private slots: + void contextualMenu(const QPoint &); + void dateRangeChanged(); + void showDetails(); + void copyAddress(); + void editLabel(); + void copyLabel(); + void copyAmount(); + void copyTxID(); + +signals: + void doubleClicked(const QModelIndex&); + +public slots: + void chooseDate(int idx); + void chooseType(int idx); + void changedPrefix(const QString &prefix); + void changedAmount(const QString &amount); + void exportClicked(); + void focusTransaction(const QModelIndex&); + +}; + +#endif // TRANSACTIONVIEW_H diff --git a/src/qt/version.h b/src/qt/version.h new file mode 100644 index 0000000..57368c5 --- /dev/null +++ b/src/qt/version.h @@ -0,0 +1,55 @@ +// Copyright (c) 2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_VERSION_H +#define BITCOIN_VERSION_H + +#include "clientversion.h" +#include + +// +// client versioning +// +// added stuff for gravity update ( changed proto version to 80005, 80003 unsupported ) + + +static const int CLIENT_VERSION = + 1000000 * CLIENT_VERSION_MAJOR + + 10000 * CLIENT_VERSION_MINOR + + 100 * CLIENT_VERSION_REVISION + + 1 * CLIENT_VERSION_BUILD; + +extern const std::string CLIENT_NAME; +extern const std::string CLIENT_BUILD; +extern const std::string CLIENT_DATE; + +// +// network protocol versioning +// + + +static const int PROTOCOL_VERSION_SHORT = 7; + +static const int PROTOCOL_VERSION = 80007; + +// intial proto version, to be increased after version/verack negotiation +static const int INIT_PROTO_VERSION = 209; + +// disconnect from peers older than this proto version +static const int MIN_PEER_PROTO_VERSION = 80004; + +// nTime field added to CAddress, starting with this version; +// if possible, avoid requesting addresses nodes older than this +static const int CADDR_TIME_VERSION = 31402; + +// only request blocks from nodes outside this range of versions +static const int NOBLKS_VERSION_START = 80000; +static const int NOBLKS_VERSION_END = 80002; + +// BIP 0031, pong message, is enabled for all versions AFTER this one +static const int BIP0031_VERSION = 60000; + +// "mempool" command, enhanced "getdata" behavior starts with this version: +static const int MEMPOOL_GD_VERSION = 60002; + +#endif diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp new file mode 100644 index 0000000..dc074e8 --- /dev/null +++ b/src/qt/walletframe.cpp @@ -0,0 +1,161 @@ +/* + * Qt4 bitcoin GUI. + * + * W.J. van der Laan 2011-2012 + * The Bitcoin Developers 2011-2013 + */ +#include "walletframe.h" +#include "bitcoingui.h" +#include "walletstack.h" +#include "walletview.h" + +#include +#include +#include + +WalletFrame::WalletFrame(BitcoinGUI *_gui) : + QFrame(_gui), + gui(_gui), + clientModel(0) +{ + // Leave HBox hook for adding a list view later + QHBoxLayout *walletFrameLayout = new QHBoxLayout(this); + setContentsMargins(0,0,0,0); + walletStack = new WalletStack(this); + walletStack->setBitcoinGUI(gui); + walletFrameLayout->setContentsMargins(0,0,0,0); + walletFrameLayout->addWidget(walletStack); + + QLabel *noWallet = new QLabel(tr("No wallet has been loaded.")); + noWallet->setAlignment(Qt::AlignCenter); + walletStack->addWidget(noWallet); +} + +WalletFrame::~WalletFrame() +{ +} + +void WalletFrame::setClientModel(ClientModel *clientModel) +{ + this->clientModel = clientModel; + walletStack->setClientModel(clientModel); +} + +bool WalletFrame::addWallet(const QString& name, WalletModel *walletModel) +{ + return walletStack->addWallet(name, walletModel); +} + +bool WalletFrame::setCurrentWallet(const QString& name) +{ + // TODO: Check if valid name + walletStack->setCurrentWallet(name); + return true; +} + +void WalletFrame::removeAllWallets() +{ + walletStack->removeAllWallets(); +} + +bool WalletFrame::handleURI(const QString &uri) +{ + WalletView *walletView = currentWalletView(); + if (!walletView) + return false; + + return walletStack->handleURI(uri); +} + +void WalletFrame::showOutOfSyncWarning(bool fShow) +{ + if (!walletStack) { + QMessageBox box; + box.setText("walletStack is null"); + box.exec(); + return; + } + walletStack->showOutOfSyncWarning(fShow); +} + +void WalletFrame::gotoOverviewPage() +{ + walletStack->gotoOverviewPage(); +} + +void WalletFrame::gotoHistoryPage() +{ + walletStack->gotoHistoryPage(); +} + +void WalletFrame::gotoAddressBookPage() +{ + WalletView *walletView = currentWalletView(); + if (walletView) + walletStack->gotoAddressBookPage(); +} + +void WalletFrame::gotoReceiveCoinsPage() +{ + walletStack->gotoReceiveCoinsPage(); +} + +void WalletFrame::gotoSendCoinsPage(QString addr) +{ + walletStack->gotoSendCoinsPage(addr); +} + +void WalletFrame::gotoSignMessageTab(QString addr) +{ + WalletView *walletView = currentWalletView(); + if (walletView) + walletView->gotoSignMessageTab(addr); +} + +void WalletFrame::gotoVerifyMessageTab(QString addr) +{ + WalletView *walletView = currentWalletView(); + if (walletView) + walletView->gotoVerifyMessageTab(addr); +} + +void WalletFrame::encryptWallet(bool status) +{ + WalletView *walletView = currentWalletView(); + if (walletView) + walletView->encryptWallet(status); +} + +void WalletFrame::backupWallet() +{ + WalletView *walletView = currentWalletView(); + if (walletView) + walletView->backupWallet(); +} + +void WalletFrame::changePassphrase() +{ + WalletView *walletView = currentWalletView(); + if (walletView) + walletView->changePassphrase(); +} + +void WalletFrame::unlockWallet() +{ + WalletView *walletView = currentWalletView(); + if (walletView) + walletView->unlockWallet(); +} + +void WalletFrame::setEncryptionStatus() +{ + WalletView *walletView = currentWalletView(); + if (walletView) + walletStack->setEncryptionStatus(); +} + +WalletView *WalletFrame::currentWalletView() +{ + return qobject_cast(walletStack->currentWidget()); +} + diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h new file mode 100644 index 0000000..74454b1 --- /dev/null +++ b/src/qt/walletframe.h @@ -0,0 +1,77 @@ +/* + * Qt4 bitcoin GUI. + * + * W.J. van der Laan 2011-2012 + * The Bitcoin Developers 2011-2013 + */ +#ifndef WALLETFRAME_H +#define WALLETFRAME_H + +#include + +class BitcoinGUI; +class ClientModel; +class WalletModel; +class WalletStack; +class WalletView; + +class WalletFrame : public QFrame +{ + Q_OBJECT + +public: + explicit WalletFrame(BitcoinGUI *_gui = 0); + ~WalletFrame(); + + void setClientModel(ClientModel *clientModel); + + bool addWallet(const QString& name, WalletModel *walletModel); + bool setCurrentWallet(const QString& name); + + void removeAllWallets(); + + bool handleURI(const QString &uri); + + void showOutOfSyncWarning(bool fShow); + +private: + BitcoinGUI *gui; + ClientModel *clientModel; + WalletStack *walletStack; + + WalletView *currentWalletView(); + +public slots: + /** Switch to overview (home) page */ + void gotoOverviewPage(); + /** Switch to history (transactions) page */ + void gotoHistoryPage(); + /** Switch to address book page */ + void gotoAddressBookPage(); + /** Switch to receive coins page */ + void gotoReceiveCoinsPage(); + /** Switch to send coins page */ + void gotoSendCoinsPage(QString addr = ""); + + /** Show Sign/Verify Message dialog and switch to sign message tab */ + void gotoSignMessageTab(QString addr = ""); + /** Show Sign/Verify Message dialog and switch to verify message tab */ + void gotoVerifyMessageTab(QString addr = ""); + + /** Encrypt the wallet */ + void encryptWallet(bool status); + /** Backup the wallet */ + void backupWallet(); + /** Change encrypted wallet passphrase */ + void changePassphrase(); + /** Ask for passphrase to unlock wallet temporarily */ + void unlockWallet(); + + /** Set the encryption status as shown in the UI. + @param[in] status current encryption status + @see WalletModel::EncryptionStatus + */ + void setEncryptionStatus(); +}; + +#endif // WALLETFRAME_H diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp new file mode 100644 index 0000000..a95df81 --- /dev/null +++ b/src/qt/walletmodel.cpp @@ -0,0 +1,463 @@ +#include "walletmodel.h" +#include "guiconstants.h" +#include "optionsmodel.h" +#include "addresstablemodel.h" +#include "transactiontablemodel.h" + +#include "ui_interface.h" +#include "wallet.h" +#include "walletdb.h" // for BackupWallet +#include "base58.h" + +#include +#include + +WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent) : + QObject(parent), wallet(wallet), optionsModel(optionsModel), addressTableModel(0), + transactionTableModel(0), + cachedBalance(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0), + cachedNumTransactions(0), + cachedEncryptionStatus(Unencrypted), + cachedNumBlocks(0) +{ + addressTableModel = new AddressTableModel(wallet, this); + transactionTableModel = new TransactionTableModel(wallet, this); + + // This timer will be fired repeatedly to update the balance + pollTimer = new QTimer(this); + connect(pollTimer, SIGNAL(timeout()), this, SLOT(pollBalanceChanged())); + pollTimer->start(MODEL_UPDATE_DELAY); + + subscribeToCoreSignals(); +} + +WalletModel::~WalletModel() +{ + unsubscribeFromCoreSignals(); +} + +qint64 WalletModel::getBalance(const CCoinControl *coinControl) const +{ + if (coinControl) + { + int64 nBalance = 0; + std::vector vCoins; + wallet->AvailableCoins(vCoins, true, coinControl); + BOOST_FOREACH(const COutput& out, vCoins) + nBalance += out.tx->vout[out.i].nValue; + + return nBalance; + } + + return wallet->GetBalance(); +} + +qint64 WalletModel::getUnconfirmedBalance() const +{ + return wallet->GetUnconfirmedBalance(); +} + +qint64 WalletModel::getImmatureBalance() const +{ + return wallet->GetImmatureBalance(); +} + +int WalletModel::getNumTransactions() const +{ + int numTransactions = 0; + { + LOCK(wallet->cs_wallet); + // the size of mapWallet contains the number of unique transaction IDs + // (e.g. payments to yourself generate 2 transactions, but both share the same transaction ID) + numTransactions = wallet->mapWallet.size(); + } + return numTransactions; +} + +void WalletModel::updateStatus() +{ + EncryptionStatus newEncryptionStatus = getEncryptionStatus(); + + if(cachedEncryptionStatus != newEncryptionStatus) + emit encryptionStatusChanged(newEncryptionStatus); +} + +void WalletModel::pollBalanceChanged() +{ + if(nBestHeight != cachedNumBlocks) + { + // Balance and number of transactions might have changed + cachedNumBlocks = nBestHeight; + checkBalanceChanged(); + } +} + +void WalletModel::checkBalanceChanged() +{ + qint64 newBalance = getBalance(); + qint64 newUnconfirmedBalance = getUnconfirmedBalance(); + qint64 newImmatureBalance = getImmatureBalance(); + + if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance) + { + cachedBalance = newBalance; + cachedUnconfirmedBalance = newUnconfirmedBalance; + cachedImmatureBalance = newImmatureBalance; + emit balanceChanged(newBalance, newUnconfirmedBalance, newImmatureBalance); + } +} + +void WalletModel::updateTransaction(const QString &hash, int status) +{ + if(transactionTableModel) + transactionTableModel->updateTransaction(hash, status); + + // Balance and number of transactions might have changed + checkBalanceChanged(); + + int newNumTransactions = getNumTransactions(); + if(cachedNumTransactions != newNumTransactions) + { + cachedNumTransactions = newNumTransactions; + emit numTransactionsChanged(newNumTransactions); + } +} + +void WalletModel::updateAddressBook(const QString &address, const QString &label, bool isMine, int status) +{ + if(addressTableModel) + addressTableModel->updateEntry(address, label, isMine, status); +} + +bool WalletModel::validateAddress(const QString &address) +{ + CBitcoinAddress addressParsed(address.toStdString()); + return addressParsed.IsValid(); +} + +WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList &recipients, const CCoinControl *coinControl) +{ + qint64 total = 0; + QSet setAddress; + QString hex; + + if(recipients.empty()) + { + return OK; + } + + // Pre-check input data for validity + foreach(const SendCoinsRecipient &rcp, recipients) + { + if(!validateAddress(rcp.address)) + { + return InvalidAddress; + } + setAddress.insert(rcp.address); + + if(rcp.amount <= 0) + { + return InvalidAmount; + } + total += rcp.amount; + } + + if(recipients.size() > setAddress.size()) + { + return DuplicateAddress; + } + + int64 nBalance = getBalance(coinControl); + + if(total > nBalance) + { + return AmountExceedsBalance; + } + + if((total + nTransactionFee) > nBalance) + { + return SendCoinsReturn(AmountWithFeeExceedsBalance, nTransactionFee); + } + + { + LOCK2(cs_main, wallet->cs_wallet); + + // Sendmany + std::vector > vecSend; + foreach(const SendCoinsRecipient &rcp, recipients) + { + CScript scriptPubKey; + scriptPubKey.SetDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); + vecSend.push_back(make_pair(scriptPubKey, rcp.amount)); + } + + CWalletTx wtx; + CReserveKey keyChange(wallet); + int64 nFeeRequired = 0; + std::string strFailReason; + bool fCreated = wallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, strFailReason, coinControl); + + if(!fCreated) + { + if((total + nFeeRequired) > nBalance) + { + return SendCoinsReturn(AmountWithFeeExceedsBalance, nFeeRequired); + } + emit message(tr("Send Coins"), QString::fromStdString(strFailReason), + CClientUIInterface::MSG_ERROR); + return TransactionCreationFailed; + } + if(!uiInterface.ThreadSafeAskFee(nFeeRequired)) + { + return Aborted; + } + if(!wallet->CommitTransaction(wtx, keyChange)) + { + return TransactionCommitFailed; + } + hex = QString::fromStdString(wtx.GetHash().GetHex()); + } + + // Add addresses / update labels that we've sent to to the address book + foreach(const SendCoinsRecipient &rcp, recipients) + { + std::string strAddress = rcp.address.toStdString(); + CTxDestination dest = CBitcoinAddress(strAddress).Get(); + std::string strLabel = rcp.label.toStdString(); + { + LOCK(wallet->cs_wallet); + + std::map::iterator mi = wallet->mapAddressBook.find(dest); + + // Check if we have a new address or an updated label + if (mi == wallet->mapAddressBook.end() || mi->second != strLabel) + { + wallet->SetAddressBookName(dest, strLabel); + } + } + } + + return SendCoinsReturn(OK, 0, hex); +} + +OptionsModel *WalletModel::getOptionsModel() +{ + return optionsModel; +} + +AddressTableModel *WalletModel::getAddressTableModel() +{ + return addressTableModel; +} + +TransactionTableModel *WalletModel::getTransactionTableModel() +{ + return transactionTableModel; +} + +WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const +{ + if(!wallet->IsCrypted()) + { + return Unencrypted; + } + else if(wallet->IsLocked()) + { + return Locked; + } + else + { + return Unlocked; + } +} + +bool WalletModel::setWalletEncrypted(bool encrypted, const SecureString &passphrase) +{ + if(encrypted) + { + // Encrypt + return wallet->EncryptWallet(passphrase); + } + else + { + // Decrypt -- TODO; not supported yet + return false; + } +} + +bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase) +{ + if(locked) + { + // Lock + return wallet->Lock(); + } + else + { + // Unlock + return wallet->Unlock(passPhrase); + } +} + +bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureString &newPass) +{ + bool retval; + { + LOCK(wallet->cs_wallet); + wallet->Lock(); // Make sure wallet is locked before attempting pass change + retval = wallet->ChangeWalletPassphrase(oldPass, newPass); + } + return retval; +} + +bool WalletModel::backupWallet(const QString &filename) +{ + return BackupWallet(*wallet, filename.toLocal8Bit().data()); +} + +// Handlers for core signals +static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel, CCryptoKeyStore *wallet) +{ + OutputDebugStringF("NotifyKeyStoreStatusChanged\n"); + QMetaObject::invokeMethod(walletmodel, "updateStatus", Qt::QueuedConnection); +} + +static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet, const CTxDestination &address, const std::string &label, bool isMine, ChangeType status) +{ + OutputDebugStringF("NotifyAddressBookChanged %s %s isMine=%i status=%i\n", CBitcoinAddress(address).ToString().c_str(), label.c_str(), isMine, status); + QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection, + Q_ARG(QString, QString::fromStdString(CBitcoinAddress(address).ToString())), + Q_ARG(QString, QString::fromStdString(label)), + Q_ARG(bool, isMine), + Q_ARG(int, status)); +} + +static void NotifyTransactionChanged(WalletModel *walletmodel, CWallet *wallet, const uint256 &hash, ChangeType status) +{ + OutputDebugStringF("NotifyTransactionChanged %s status=%i\n", hash.GetHex().c_str(), status); + QMetaObject::invokeMethod(walletmodel, "updateTransaction", Qt::QueuedConnection, + Q_ARG(QString, QString::fromStdString(hash.GetHex())), + Q_ARG(int, status)); +} + +void WalletModel::subscribeToCoreSignals() +{ + // Connect signals to wallet + wallet->NotifyStatusChanged.connect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1)); + wallet->NotifyAddressBookChanged.connect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5)); + wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); +} + +void WalletModel::unsubscribeFromCoreSignals() +{ + // Disconnect signals from wallet + wallet->NotifyStatusChanged.disconnect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1)); + wallet->NotifyAddressBookChanged.disconnect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5)); + wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); +} + +// WalletModel::UnlockContext implementation +WalletModel::UnlockContext WalletModel::requestUnlock() +{ + bool was_locked = getEncryptionStatus() == Locked; + if(was_locked) + { + // Request UI to unlock wallet + emit requireUnlock(); + } + // If wallet is still locked, unlock was failed or cancelled, mark context as invalid + bool valid = getEncryptionStatus() != Locked; + + return UnlockContext(this, valid, was_locked); +} + +WalletModel::UnlockContext::UnlockContext(WalletModel *wallet, bool valid, bool relock): + wallet(wallet), + valid(valid), + relock(relock) +{ +} + +WalletModel::UnlockContext::~UnlockContext() +{ + if(valid && relock) + { + wallet->setWalletLocked(true); + } +} + +void WalletModel::UnlockContext::CopyFrom(const UnlockContext& rhs) +{ + // Transfer context; old object no longer relocks wallet + *this = rhs; + rhs.relock = false; +} + +bool WalletModel::getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const +{ + return wallet->GetPubKey(address, vchPubKeyOut); +} + +// returns a list of COutputs from COutPoints +void WalletModel::getOutputs(const std::vector& vOutpoints, std::vector& vOutputs) +{ + BOOST_FOREACH(const COutPoint& outpoint, vOutpoints) + { + if (!wallet->mapWallet.count(outpoint.hash)) continue; + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, wallet->mapWallet[outpoint.hash].GetDepthInMainChain()); + vOutputs.push_back(out); + } +} + +// AvailableCoins + LockedCoins grouped by wallet address (put change in one group with wallet address) +void WalletModel::listCoins(std::map >& mapCoins) const +{ + std::vector vCoins; + wallet->AvailableCoins(vCoins); + + std::vector vLockedCoins; + wallet->ListLockedCoins(vLockedCoins); + + // add locked coins + BOOST_FOREACH(const COutPoint& outpoint, vLockedCoins) + { + if (!wallet->mapWallet.count(outpoint.hash)) continue; + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, wallet->mapWallet[outpoint.hash].GetDepthInMainChain()); + vCoins.push_back(out); + } + + BOOST_FOREACH(const COutput& out, vCoins) + { + COutput cout = out; + + while (wallet->IsChange(cout.tx->vout[cout.i]) && cout.tx->vin.size() > 0 && wallet->IsMine(cout.tx->vin[0])) + { + if (!wallet->mapWallet.count(cout.tx->vin[0].prevout.hash)) break; + cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0); + } + + CTxDestination address; + if(!ExtractDestination(cout.tx->vout[cout.i].scriptPubKey, address)) continue; + mapCoins[CBitcoinAddress(address).ToString().c_str()].push_back(out); + } +} + +bool WalletModel::isLockedCoin(uint256 hash, unsigned int n) const +{ + return wallet->IsLockedCoin(hash, n); +} + +void WalletModel::lockCoin(COutPoint& output) +{ + wallet->LockCoin(output); +} + +void WalletModel::unlockCoin(COutPoint& output) +{ + wallet->UnlockCoin(output); +} + +void WalletModel::listLockedCoins(std::vector& vOutpts) +{ + wallet->ListLockedCoins(vOutpts); +} diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h new file mode 100644 index 0000000..4d61da9 --- /dev/null +++ b/src/qt/walletmodel.h @@ -0,0 +1,182 @@ +#ifndef WALLETMODEL_H +#define WALLETMODEL_H + +#include +#include +#include + +#include "allocators.h" /* for SecureString */ + +class OptionsModel; +class AddressTableModel; +class TransactionTableModel; +class CWallet; +class CKeyID; +class CPubKey; +class COutput; +class COutPoint; +class uint256; +class CCoinControl; + +QT_BEGIN_NAMESPACE +class QTimer; +QT_END_NAMESPACE + +class SendCoinsRecipient +{ +public: + QString address; + QString label; + qint64 amount; +}; + +/** Interface to Bitcoin wallet from Qt view code. */ +class WalletModel : public QObject +{ + Q_OBJECT + +public: + explicit WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent = 0); + ~WalletModel(); + + enum StatusCode // Returned by sendCoins + { + OK, + InvalidAmount, + InvalidAddress, + AmountExceedsBalance, + AmountWithFeeExceedsBalance, + DuplicateAddress, + TransactionCreationFailed, // Error returned when wallet is still locked + TransactionCommitFailed, + Aborted + }; + + enum EncryptionStatus + { + Unencrypted, // !wallet->IsCrypted() + Locked, // wallet->IsCrypted() && wallet->IsLocked() + Unlocked // wallet->IsCrypted() && !wallet->IsLocked() + }; + + OptionsModel *getOptionsModel(); + AddressTableModel *getAddressTableModel(); + TransactionTableModel *getTransactionTableModel(); + + qint64 getBalance(const CCoinControl *coinControl=NULL) const; + qint64 getUnconfirmedBalance() const; + qint64 getImmatureBalance() const; + int getNumTransactions() const; + EncryptionStatus getEncryptionStatus() const; + + // Check address for validity + bool validateAddress(const QString &address); + + // Return status record for SendCoins, contains error id + information + struct SendCoinsReturn + { + SendCoinsReturn(StatusCode status=Aborted, + qint64 fee=0, + QString hex=QString()): + status(status), fee(fee), hex(hex) {} + StatusCode status; + qint64 fee; // is used in case status is "AmountWithFeeExceedsBalance" + QString hex; // is filled with the transaction hash if status is "OK" + }; + + // Send coins to a list of recipients + SendCoinsReturn sendCoins(const QList &recipients, const CCoinControl *coinControl=NULL); + + // Wallet encryption + bool setWalletEncrypted(bool encrypted, const SecureString &passphrase); + // Passphrase only needed when unlocking + bool setWalletLocked(bool locked, const SecureString &passPhrase=SecureString()); + bool changePassphrase(const SecureString &oldPass, const SecureString &newPass); + // Wallet backup + bool backupWallet(const QString &filename); + + // RAI object for unlocking wallet, returned by requestUnlock() + class UnlockContext + { + public: + UnlockContext(WalletModel *wallet, bool valid, bool relock); + ~UnlockContext(); + + bool isValid() const { return valid; } + + // Copy operator and constructor transfer the context + UnlockContext(const UnlockContext& obj) { CopyFrom(obj); } + UnlockContext& operator=(const UnlockContext& rhs) { CopyFrom(rhs); return *this; } + private: + WalletModel *wallet; + bool valid; + mutable bool relock; // mutable, as it can be set to false by copying + + void CopyFrom(const UnlockContext& rhs); + }; + + UnlockContext requestUnlock(); + + bool getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; + void getOutputs(const std::vector& vOutpoints, std::vector& vOutputs); + void listCoins(std::map >& mapCoins) const; + + bool isLockedCoin(uint256 hash, unsigned int n) const; + void lockCoin(COutPoint& output); + void unlockCoin(COutPoint& output); + void listLockedCoins(std::vector& vOutpts); + +private: + CWallet *wallet; + + // Wallet has an options model for wallet-specific options + // (transaction fee, for example) + OptionsModel *optionsModel; + + AddressTableModel *addressTableModel; + TransactionTableModel *transactionTableModel; + + // Cache some values to be able to detect changes + qint64 cachedBalance; + qint64 cachedUnconfirmedBalance; + qint64 cachedImmatureBalance; + qint64 cachedNumTransactions; + EncryptionStatus cachedEncryptionStatus; + int cachedNumBlocks; + + QTimer *pollTimer; + + void subscribeToCoreSignals(); + void unsubscribeFromCoreSignals(); + void checkBalanceChanged(); + +signals: + // Signal that balance in wallet changed + void balanceChanged(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance); + + // Number of transactions in wallet changed + void numTransactionsChanged(int count); + + // Encryption status of wallet changed + void encryptionStatusChanged(int status); + + // Signal emitted when wallet needs to be unlocked + // It is valid behaviour for listeners to keep the wallet locked after this signal; + // this means that the unlocking failed or was cancelled. + void requireUnlock(); + + // Asynchronous message notification + void message(const QString &title, const QString &message, unsigned int style); + +public slots: + /* Wallet status might have changed */ + void updateStatus(); + /* New transaction, or transaction changed status */ + void updateTransaction(const QString &hash, int status); + /* New, updated or removed address book entry */ + void updateAddressBook(const QString &address, const QString &label, bool isMine, int status); + /* Current, immature or unconfirmed balance might have changed - emit 'balanceChanged' if so */ + void pollBalanceChanged(); +}; + +#endif // WALLETMODEL_H diff --git a/src/qt/walletstack.cpp b/src/qt/walletstack.cpp new file mode 100644 index 0000000..3576d55 --- /dev/null +++ b/src/qt/walletstack.cpp @@ -0,0 +1,161 @@ +/* + * Qt4 bitcoin GUI. + * + * W.J. van der Laan 2011-2012 + * The Bitcoin Developers 2011-2013 + */ +#include "walletstack.h" +#include "walletview.h" +#include "bitcoingui.h" + +#include +#include + +WalletStack::WalletStack(QWidget *parent) : + QStackedWidget(parent), + gui(0), + clientModel(0), + bOutOfSync(true) +{ + setContentsMargins(0,0,0,0); +} + +WalletStack::~WalletStack() +{ +} + +bool WalletStack::addWallet(const QString& name, WalletModel *walletModel) +{ + if (!gui || !clientModel || mapWalletViews.count(name) > 0) + return false; + + WalletView *walletView = new WalletView(this, gui); + walletView->setBitcoinGUI(gui); + walletView->setClientModel(clientModel); + walletView->setWalletModel(walletModel); + walletView->showOutOfSyncWarning(bOutOfSync); + addWidget(walletView); + mapWalletViews[name] = walletView; + + // Ensure a walletView is able to show the main window + connect(walletView, SIGNAL(showNormalIfMinimized()), gui, SLOT(showNormalIfMinimized())); + + return true; +} + +bool WalletStack::removeWallet(const QString& name) +{ + if (mapWalletViews.count(name) == 0) return false; + WalletView *walletView = mapWalletViews.take(name); + removeWidget(walletView); + return true; +} + +void WalletStack::removeAllWallets() +{ + QMap::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + removeWidget(i.value()); + mapWalletViews.clear(); +} + +bool WalletStack::handleURI(const QString &uri) +{ + WalletView *walletView = (WalletView*)currentWidget(); + if (!walletView) return false; + + return walletView->handleURI(uri); +} + +void WalletStack::showOutOfSyncWarning(bool fShow) +{ + bOutOfSync = fShow; + QMap::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->showOutOfSyncWarning(fShow); +} + +void WalletStack::gotoOverviewPage() +{ + QMap::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoOverviewPage(); +} + +void WalletStack::gotoHistoryPage() +{ + QMap::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoHistoryPage(); +} + +void WalletStack::gotoAddressBookPage() +{ + QMap::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoAddressBookPage(); +} + +void WalletStack::gotoReceiveCoinsPage() +{ + QMap::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoReceiveCoinsPage(); +} + +void WalletStack::gotoSendCoinsPage(QString addr) +{ + QMap::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoSendCoinsPage(addr); +} + +void WalletStack::gotoSignMessageTab(QString addr) +{ + WalletView *walletView = (WalletView*)currentWidget(); + if (walletView) walletView->gotoSignMessageTab(addr); +} + +void WalletStack::gotoVerifyMessageTab(QString addr) +{ + WalletView *walletView = (WalletView*)currentWidget(); + if (walletView) walletView->gotoVerifyMessageTab(addr); +} + +void WalletStack::encryptWallet(bool status) +{ + WalletView *walletView = (WalletView*)currentWidget(); + if (walletView) walletView->encryptWallet(status); +} + +void WalletStack::backupWallet() +{ + WalletView *walletView = (WalletView*)currentWidget(); + if (walletView) walletView->backupWallet(); +} + +void WalletStack::changePassphrase() +{ + WalletView *walletView = (WalletView*)currentWidget(); + if (walletView) walletView->changePassphrase(); +} + +void WalletStack::unlockWallet() +{ + WalletView *walletView = (WalletView*)currentWidget(); + if (walletView) walletView->unlockWallet(); +} + +void WalletStack::setEncryptionStatus() +{ + WalletView *walletView = (WalletView*)currentWidget(); + if (walletView) walletView->setEncryptionStatus(); +} + +void WalletStack::setCurrentWallet(const QString& name) +{ + if (mapWalletViews.count(name) == 0) return; + WalletView *walletView = mapWalletViews.value(name); + setCurrentWidget(walletView); + walletView->setEncryptionStatus(); +} diff --git a/src/qt/walletstack.h b/src/qt/walletstack.h new file mode 100644 index 0000000..506d595 --- /dev/null +++ b/src/qt/walletstack.h @@ -0,0 +1,103 @@ +/* + * Qt4 bitcoin GUI. + * + * W.J. van der Laan 2011-2012 + * The Bitcoin Developers 2011-2013 + */ +#ifndef WALLETSTACK_H +#define WALLETSTACK_H + +#include +#include +#include + +class BitcoinGUI; +class TransactionTableModel; +class ClientModel; +class WalletModel; +class WalletView; +class TransactionView; +class OverviewPage; +class AddressBookPage; +class SendCoinsDialog; +class SignVerifyMessageDialog; +class Notificator; +class RPCConsole; + +class CWalletManager; + +QT_BEGIN_NAMESPACE +class QLabel; +class QModelIndex; +QT_END_NAMESPACE + +/* + WalletStack class. This class is a container for WalletView instances. It takes the place of centralWidget. + It was added to support multiple wallet functionality. It communicates with both the client and the + wallet models to give the user an up-to-date view of the current core state. It manages all the wallet views + it contains and updates them accordingly. + */ +class WalletStack : public QStackedWidget +{ + Q_OBJECT + +public: + explicit WalletStack(QWidget *parent = 0); + ~WalletStack(); + + void setBitcoinGUI(BitcoinGUI *gui) { this->gui = gui; } + + void setClientModel(ClientModel *clientModel) { this->clientModel = clientModel; } + + bool addWallet(const QString& name, WalletModel *walletModel); + bool removeWallet(const QString& name); + + void removeAllWallets(); + + bool handleURI(const QString &uri); + + void showOutOfSyncWarning(bool fShow); + +private: + BitcoinGUI *gui; + ClientModel *clientModel; + QMap mapWalletViews; + + bool bOutOfSync; + +public slots: + void setCurrentWallet(const QString& name); + + /** Switch to overview (home) page */ + void gotoOverviewPage(); + /** Switch to history (transactions) page */ + void gotoHistoryPage(); + /** Switch to address book page */ + void gotoAddressBookPage(); + /** Switch to receive coins page */ + void gotoReceiveCoinsPage(); + /** Switch to send coins page */ + void gotoSendCoinsPage(QString addr = ""); + + /** Show Sign/Verify Message dialog and switch to sign message tab */ + void gotoSignMessageTab(QString addr = ""); + /** Show Sign/Verify Message dialog and switch to verify message tab */ + void gotoVerifyMessageTab(QString addr = ""); + + /** Encrypt the wallet */ + void encryptWallet(bool status); + /** Backup the wallet */ + void backupWallet(); + /** Change encrypted wallet passphrase */ + void changePassphrase(); + /** Ask for passphrase to unlock wallet temporarily */ + void unlockWallet(); + + /** Set the encryption status as shown in the UI. + @param[in] status current encryption status + @see WalletModel::EncryptionStatus + */ + void setEncryptionStatus(); +}; + +#endif // WALLETSTACK_H diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp new file mode 100644 index 0000000..dd2034c --- /dev/null +++ b/src/qt/walletview.cpp @@ -0,0 +1,275 @@ +/* + * Qt4 bitcoin GUI. + * + * W.J. van der Laan 2011-2012 + * The Bitcoin Developers 2011-2013 + */ +#include "walletview.h" +#include "bitcoingui.h" +#include "transactiontablemodel.h" +#include "addressbookpage.h" +#include "sendcoinsdialog.h" +#include "signverifymessagedialog.h" +#include "clientmodel.h" +#include "walletmodel.h" +#include "optionsmodel.h" +#include "transactionview.h" +#include "overviewpage.h" +#include "askpassphrasedialog.h" +#include "ui_interface.h" + +#include +#include +#include +#if QT_VERSION < 0x050000 +#include +#else +#include +#endif +#include +#include + +WalletView::WalletView(QWidget *parent, BitcoinGUI *_gui): + QStackedWidget(parent), + gui(_gui), + clientModel(0), + walletModel(0) +{ + // Create tabs + overviewPage = new OverviewPage(); + + transactionsPage = new QWidget(this); + QVBoxLayout *vbox = new QVBoxLayout(); + QHBoxLayout *hbox_buttons = new QHBoxLayout(); + transactionView = new TransactionView(this); + vbox->addWidget(transactionView); + QPushButton *exportButton = new QPushButton(tr("&Export"), this); + exportButton->setToolTip(tr("Export the data in the current tab to a file")); +#ifndef Q_OS_MAC // Icons on push buttons are very uncommon on Mac + exportButton->setIcon(QIcon(":/icons/export")); +#endif + hbox_buttons->addStretch(); + hbox_buttons->addWidget(exportButton); + vbox->addLayout(hbox_buttons); + transactionsPage->setLayout(vbox); + + addressBookPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab); + + receiveCoinsPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab); + + sendCoinsPage = new SendCoinsDialog(gui); + + signVerifyMessageDialog = new SignVerifyMessageDialog(gui); + + addWidget(overviewPage); + addWidget(transactionsPage); + addWidget(addressBookPage); + addWidget(receiveCoinsPage); + addWidget(sendCoinsPage); + + // Clicking on a transaction on the overview page simply sends you to transaction history page + connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), this, SLOT(gotoHistoryPage())); + connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), transactionView, SLOT(focusTransaction(QModelIndex))); + + // Double-clicking on a transaction on the transaction history page shows details + connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails())); + + // Clicking on "Send Coins" in the address book sends you to the send coins tab + connect(addressBookPage, SIGNAL(sendCoins(QString)), this, SLOT(gotoSendCoinsPage(QString))); + // Clicking on "Verify Message" in the address book opens the verify message tab in the Sign/Verify Message dialog + connect(addressBookPage, SIGNAL(verifyMessage(QString)), this, SLOT(gotoVerifyMessageTab(QString))); + // Clicking on "Sign Message" in the receive coins page opens the sign message tab in the Sign/Verify Message dialog + connect(receiveCoinsPage, SIGNAL(signMessage(QString)), this, SLOT(gotoSignMessageTab(QString))); + // Clicking on "Export" allows to export the transaction list + connect(exportButton, SIGNAL(clicked()), transactionView, SLOT(exportClicked())); + + gotoOverviewPage(); +} + +WalletView::~WalletView() +{ +} + +void WalletView::setBitcoinGUI(BitcoinGUI *gui) +{ + this->gui = gui; +} + +void WalletView::setClientModel(ClientModel *clientModel) +{ + this->clientModel = clientModel; + if (clientModel) + { + overviewPage->setClientModel(clientModel); + addressBookPage->setOptionsModel(clientModel->getOptionsModel()); + receiveCoinsPage->setOptionsModel(clientModel->getOptionsModel()); + } +} + +void WalletView::setWalletModel(WalletModel *walletModel) +{ + this->walletModel = walletModel; + if (walletModel) + { + // Receive and report messages from wallet thread + connect(walletModel, SIGNAL(message(QString,QString,unsigned int)), gui, SLOT(message(QString,QString,unsigned int))); + + // Put transaction list in tabs + transactionView->setModel(walletModel); + overviewPage->setWalletModel(walletModel); + addressBookPage->setModel(walletModel->getAddressTableModel()); + receiveCoinsPage->setModel(walletModel->getAddressTableModel()); + sendCoinsPage->setModel(walletModel); + signVerifyMessageDialog->setModel(walletModel); + + setEncryptionStatus(); + connect(walletModel, SIGNAL(encryptionStatusChanged(int)), gui, SLOT(setEncryptionStatus(int))); + + // Balloon pop-up for new transaction + connect(walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(incomingTransaction(QModelIndex,int,int))); + + // Ask for passphrase if needed + connect(walletModel, SIGNAL(requireUnlock()), this, SLOT(unlockWallet())); + } +} + +void WalletView::incomingTransaction(const QModelIndex& parent, int start, int /*end*/) +{ + // Prevent balloon-spam when initial block download is in progress + if(!walletModel || !clientModel || clientModel->inInitialBlockDownload()) + return; + + TransactionTableModel *ttm = walletModel->getTransactionTableModel(); + + QString date = ttm->index(start, TransactionTableModel::Date, parent).data().toString(); + qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent).data(Qt::EditRole).toULongLong(); + QString type = ttm->index(start, TransactionTableModel::Type, parent).data().toString(); + QString address = ttm->index(start, TransactionTableModel::ToAddress, parent).data().toString(); + + gui->incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address); +} + +void WalletView::gotoOverviewPage() +{ + gui->getOverviewAction()->setChecked(true); + setCurrentWidget(overviewPage); +} + +void WalletView::gotoHistoryPage() +{ + gui->getHistoryAction()->setChecked(true); + setCurrentWidget(transactionsPage); +} + +void WalletView::gotoAddressBookPage() +{ + gui->getAddressBookAction()->setChecked(true); + setCurrentWidget(addressBookPage); +} + +void WalletView::gotoReceiveCoinsPage() +{ + gui->getReceiveCoinsAction()->setChecked(true); + setCurrentWidget(receiveCoinsPage); +} + +void WalletView::gotoSendCoinsPage(QString addr) +{ + gui->getSendCoinsAction()->setChecked(true); + setCurrentWidget(sendCoinsPage); + + if (!addr.isEmpty()) + sendCoinsPage->setAddress(addr); +} + +void WalletView::gotoSignMessageTab(QString addr) +{ + // call show() in showTab_SM() + signVerifyMessageDialog->showTab_SM(true); + + if (!addr.isEmpty()) + signVerifyMessageDialog->setAddress_SM(addr); +} + +void WalletView::gotoVerifyMessageTab(QString addr) +{ + // call show() in showTab_VM() + signVerifyMessageDialog->showTab_VM(true); + + if (!addr.isEmpty()) + signVerifyMessageDialog->setAddress_VM(addr); +} + +bool WalletView::handleURI(const QString& strURI) +{ + // URI has to be valid + if (sendCoinsPage->handleURI(strURI)) + { + gotoSendCoinsPage(); + emit showNormalIfMinimized(); + return true; + } + else + return false; +} + +void WalletView::showOutOfSyncWarning(bool fShow) +{ + overviewPage->showOutOfSyncWarning(fShow); +} + +void WalletView::setEncryptionStatus() +{ + gui->setEncryptionStatus(walletModel->getEncryptionStatus()); +} + +void WalletView::encryptWallet(bool status) +{ + if(!walletModel) + return; + AskPassphraseDialog dlg(status ? AskPassphraseDialog::Encrypt : AskPassphraseDialog::Decrypt, this); + dlg.setModel(walletModel); + dlg.exec(); + + setEncryptionStatus(); +} + +void WalletView::backupWallet() +{ +#if QT_VERSION < 0x050000 + QString saveDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); +#else + QString saveDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); +#endif + QString filename = QFileDialog::getSaveFileName(this, tr("Backup Wallet"), saveDir, tr("Wallet Data (*.dat)")); + if (!filename.isEmpty()) { + if (!walletModel->backupWallet(filename)) { + gui->message(tr("Backup Failed"), tr("There was an error trying to save the wallet data to the new location."), + CClientUIInterface::MSG_ERROR); + } + else + gui->message(tr("Backup Successful"), tr("The wallet data was successfully saved to the new location."), + CClientUIInterface::MSG_INFORMATION); + } +} + +void WalletView::changePassphrase() +{ + AskPassphraseDialog dlg(AskPassphraseDialog::ChangePass, this); + dlg.setModel(walletModel); + dlg.exec(); +} + +void WalletView::unlockWallet() +{ + if(!walletModel) + return; + // Unlock wallet when requested by wallet model + if (walletModel->getEncryptionStatus() == WalletModel::Locked) + { + AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, this); + dlg.setModel(walletModel); + dlg.exec(); + } +} diff --git a/src/qt/walletview.h b/src/qt/walletview.h new file mode 100644 index 0000000..6ad5180 --- /dev/null +++ b/src/qt/walletview.h @@ -0,0 +1,108 @@ +/* + * Qt4 bitcoin GUI. + * + * W.J. van der Laan 2011-2012 + * The Bitcoin Developers 2011-2013 + */ +#ifndef WALLETVIEW_H +#define WALLETVIEW_H + +#include + +class BitcoinGUI; +class ClientModel; +class WalletModel; +class TransactionView; +class OverviewPage; +class AddressBookPage; +class SendCoinsDialog; +class SignVerifyMessageDialog; +class RPCConsole; + +QT_BEGIN_NAMESPACE +class QLabel; +class QModelIndex; +QT_END_NAMESPACE + +/* + WalletView class. This class represents the view to a single wallet. + It was added to support multiple wallet functionality. Each wallet gets its own WalletView instance. + It communicates with both the client and the wallet models to give the user an up-to-date view of the + current core state. +*/ +class WalletView : public QStackedWidget +{ + Q_OBJECT + +public: + explicit WalletView(QWidget *parent, BitcoinGUI *_gui); + ~WalletView(); + + void setBitcoinGUI(BitcoinGUI *gui); + /** Set the client model. + The client model represents the part of the core that communicates with the P2P network, and is wallet-agnostic. + */ + void setClientModel(ClientModel *clientModel); + /** Set the wallet model. + The wallet model represents a bitcoin wallet, and offers access to the list of transactions, address book and sending + functionality. + */ + void setWalletModel(WalletModel *walletModel); + + bool handleURI(const QString &uri); + + void showOutOfSyncWarning(bool fShow); + +private: + BitcoinGUI *gui; + ClientModel *clientModel; + WalletModel *walletModel; + + OverviewPage *overviewPage; + QWidget *transactionsPage; + AddressBookPage *addressBookPage; + AddressBookPage *receiveCoinsPage; + SendCoinsDialog *sendCoinsPage; + SignVerifyMessageDialog *signVerifyMessageDialog; + + TransactionView *transactionView; + +public slots: + /** Switch to overview (home) page */ + void gotoOverviewPage(); + /** Switch to history (transactions) page */ + void gotoHistoryPage(); + /** Switch to address book page */ + void gotoAddressBookPage(); + /** Switch to receive coins page */ + void gotoReceiveCoinsPage(); + /** Switch to send coins page */ + void gotoSendCoinsPage(QString addr = ""); + + /** Show Sign/Verify Message dialog and switch to sign message tab */ + void gotoSignMessageTab(QString addr = ""); + /** Show Sign/Verify Message dialog and switch to verify message tab */ + void gotoVerifyMessageTab(QString addr = ""); + + /** Show incoming transaction notification for new transactions. + + The new items are those between start and end inclusive, under the given parent item. + */ + void incomingTransaction(const QModelIndex& parent, int start, int /*end*/); + /** Encrypt the wallet */ + void encryptWallet(bool status); + /** Backup the wallet */ + void backupWallet(); + /** Change encrypted wallet passphrase */ + void changePassphrase(); + /** Ask for passphrase to unlock wallet temporarily */ + void unlockWallet(); + + void setEncryptionStatus(); + +signals: + /** Signal that we want to show the main window */ + void showNormalIfMinimized(); +}; + +#endif // WALLETVIEW_H diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp new file mode 100644 index 0000000..9d58133 --- /dev/null +++ b/src/rpcblockchain.cpp @@ -0,0 +1,270 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "main.h" +#include "bitcoinrpc.h" + +using namespace json_spirit; +using namespace std; + +void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out); + +double GetDifficulty(const CBlockIndex* blockindex) +{ + // Floating point number that is a multiple of the minimum difficulty, + // minimum difficulty = 1.0. + if (blockindex == NULL) + { + if (pindexBest == NULL) + return 1.0; + else + blockindex = pindexBest; + } + + int nShift = (blockindex->nBits >> 24) & 0xff; + + double dDiff = + (double)0x0000ffff / (double)(blockindex->nBits & 0x00ffffff); + + while (nShift < 29) + { + dDiff *= 256.0; + nShift++; + } + while (nShift > 29) + { + dDiff /= 256.0; + nShift--; + } + + return dDiff; +} + + +Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex) +{ + Object result; + result.push_back(Pair("hash", block.GetHash().GetHex())); + CMerkleTx txGen(block.vtx[0]); + txGen.SetMerkleBranch(&block); + result.push_back(Pair("confirmations", (int)txGen.GetDepthInMainChain())); + result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); + result.push_back(Pair("height", blockindex->nHeight)); + result.push_back(Pair("version", block.nVersion)); + result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); + Array txs; + BOOST_FOREACH(const CTransaction&tx, block.vtx) + txs.push_back(tx.GetHash().GetHex()); + result.push_back(Pair("tx", txs)); + result.push_back(Pair("time", (boost::int64_t)block.GetBlockTime())); + result.push_back(Pair("nonce", (boost::uint64_t)block.nNonce)); + result.push_back(Pair("bits", HexBits(block.nBits))); + result.push_back(Pair("difficulty", GetDifficulty(blockindex))); + + if (blockindex->pprev) + result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); + if (blockindex->pnext) + result.push_back(Pair("nextblockhash", blockindex->pnext->GetBlockHash().GetHex())); + return result; +} + + +Value getblockcount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getblockcount\n" + "Returns the number of blocks in the longest block chain."); + + return nBestHeight; +} + +Value getbestblockhash(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getbestblockhash\n" + "Returns the hash of the best (tip) block in the longest block chain."); + + return hashBestChain.GetHex(); +} + +Value getdifficulty(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getdifficulty\n" + "Returns the proof-of-work difficulty as a multiple of the minimum difficulty."); + + return GetDifficulty(); +} + + +Value settxfee(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 1) + throw runtime_error( + "settxfee \n" + " is a real and is rounded to the nearest 0.00000001"); + + // Amount + int64 nAmount = 0; + if (params[0].get_real() != 0.0) + nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts + + nTransactionFee = nAmount; + return true; +} + +Value getrawmempool(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getrawmempool\n" + "Returns all transaction ids in memory pool."); + + vector vtxid; + mempool.queryHashes(vtxid); + + Array a; + BOOST_FOREACH(const uint256& hash, vtxid) + a.push_back(hash.ToString()); + + return a; +} + +Value getblockhash(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getblockhash \n" + "Returns hash of block in best-block-chain at ."); + + int nHeight = params[0].get_int(); + if (nHeight < 0 || nHeight > nBestHeight) + throw runtime_error("Block number out of range."); + + CBlockIndex* pblockindex = FindBlockByHeight(nHeight); + return pblockindex->phashBlock->GetHex(); +} + +Value getblock(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getblock [verbose=true]\n" + "If verbose is false, returns a string that is serialized, hex-encoded data for block .\n" + "If verbose is true, returns an Object with information about block ." + ); + + std::string strHash = params[0].get_str(); + uint256 hash(strHash); + + bool fVerbose = true; + if (params.size() > 1) + fVerbose = params[1].get_bool(); + + if (mapBlockIndex.count(hash) == 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + + CBlock block; + CBlockIndex* pblockindex = mapBlockIndex[hash]; + block.ReadFromDisk(pblockindex); + + if (!fVerbose) + { + CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); + ssBlock << block; + std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); + return strHex; + } + + return blockToJSON(block, pblockindex); +} + +Value gettxoutsetinfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "gettxoutsetinfo\n" + "Returns statistics about the unspent transaction output set."); + + Object ret; + + CCoinsStats stats; + if (pcoinsTip->GetStats(stats)) { + ret.push_back(Pair("height", (boost::int64_t)stats.nHeight)); + ret.push_back(Pair("bestblock", stats.hashBlock.GetHex())); + ret.push_back(Pair("transactions", (boost::int64_t)stats.nTransactions)); + ret.push_back(Pair("txouts", (boost::int64_t)stats.nTransactionOutputs)); + ret.push_back(Pair("bytes_serialized", (boost::int64_t)stats.nSerializedSize)); + ret.push_back(Pair("hash_serialized", stats.hashSerialized.GetHex())); + ret.push_back(Pair("total_amount", ValueFromAmount(stats.nTotalAmount))); + } + return ret; +} + +Value gettxout(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 3) + throw runtime_error( + "gettxout [includemempool=true]\n" + "Returns details about an unspent transaction output."); + + Object ret; + + std::string strHash = params[0].get_str(); + uint256 hash(strHash); + int n = params[1].get_int(); + bool fMempool = true; + if (params.size() > 2) + fMempool = params[2].get_bool(); + + CCoins coins; + if (fMempool) { + LOCK(mempool.cs); + CCoinsViewMemPool view(*pcoinsTip, mempool); + if (!view.GetCoins(hash, coins)) + return Value::null; + mempool.pruneSpent(hash, coins); // TODO: this should be done by the CCoinsViewMemPool + } else { + if (!pcoinsTip->GetCoins(hash, coins)) + return Value::null; + } + if (n<0 || (unsigned int)n>=coins.vout.size() || coins.vout[n].IsNull()) + return Value::null; + + ret.push_back(Pair("bestblock", pcoinsTip->GetBestBlock()->GetBlockHash().GetHex())); + if ((unsigned int)coins.nHeight == MEMPOOL_HEIGHT) + ret.push_back(Pair("confirmations", 0)); + else + ret.push_back(Pair("confirmations", pcoinsTip->GetBestBlock()->nHeight - coins.nHeight + 1)); + ret.push_back(Pair("value", ValueFromAmount(coins.vout[n].nValue))); + Object o; + ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o); + ret.push_back(Pair("scriptPubKey", o)); + ret.push_back(Pair("version", coins.nVersion)); + ret.push_back(Pair("coinbase", coins.fCoinBase)); + + return ret; +} + +Value verifychain(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "verifychain [check level] [num blocks]\n" + "Verifies blockchain database."); + + int nCheckLevel = GetArg("-checklevel", 3); + int nCheckDepth = GetArg("-checkblocks", 288); + if (params.size() > 0) + nCheckLevel = params[0].get_int(); + if (params.size() > 1) + nCheckDepth = params[1].get_int(); + + return VerifyDB(nCheckLevel, nCheckDepth); +} + diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp new file mode 100644 index 0000000..3b8bc25 --- /dev/null +++ b/src/rpcdump.cpp @@ -0,0 +1,96 @@ +// Copyright (c) 2009-2012 Bitcoin Developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "init.h" // for pwalletMain +#include "bitcoinrpc.h" +#include "ui_interface.h" +#include "base58.h" + +#include + +#define printf OutputDebugStringF + +using namespace json_spirit; +using namespace std; + +class CTxDump +{ +public: + CBlockIndex *pindex; + int64 nValue; + bool fSpent; + CWalletTx* ptx; + int nOut; + CTxDump(CWalletTx* ptx = NULL, int nOut = -1) + { + pindex = NULL; + nValue = 0; + fSpent = false; + this->ptx = ptx; + this->nOut = nOut; + } +}; + +Value importprivkey(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 3) + throw runtime_error( + "importprivkey [label] [rescan=true]\n" + "Adds a private key (as returned by dumpprivkey) to your wallet."); + + string strSecret = params[0].get_str(); + string strLabel = ""; + if (params.size() > 1) + strLabel = params[1].get_str(); + + // Whether to perform rescan after import + bool fRescan = true; + if (params.size() > 2) + fRescan = params[2].get_bool(); + + CBitcoinSecret vchSecret; + bool fGood = vchSecret.SetString(strSecret); + + if (!fGood) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); + + CKey key = vchSecret.GetKey(); + CPubKey pubkey = key.GetPubKey(); + CKeyID vchAddress = pubkey.GetID(); + { + LOCK2(cs_main, pwalletMain->cs_wallet); + + pwalletMain->MarkDirty(); + pwalletMain->SetAddressBookName(vchAddress, strLabel); + + if (!pwalletMain->AddKeyPubKey(key, pubkey)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); + + if (fRescan) { + pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true); + pwalletMain->ReacceptWalletTransactions(); + } + } + + return Value::null; +} + +Value dumpprivkey(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "dumpprivkey \n" + "Reveals the private key corresponding to ."); + + string strAddress = params[0].get_str(); + CBitcoinAddress address; + if (!address.SetString(strAddress)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid CryptoLottoToken address"); + CKeyID keyID; + if (!address.GetKeyID(keyID)) + throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key"); + CKey vchSecret; + if (!pwalletMain->GetKey(keyID, vchSecret)) + throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); + return CBitcoinSecret(vchSecret).ToString(); +} diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp new file mode 100644 index 0000000..6be363b --- /dev/null +++ b/src/rpcmining.cpp @@ -0,0 +1,589 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "main.h" +#include "db.h" +#include "init.h" +#include "bitcoinrpc.h" + +using namespace json_spirit; +using namespace std; + +// Return average network hashes per second based on the last 'lookup' blocks, +// 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) { + CBlockIndex *pb = pindexBest; + + if (height >= 0 && height < nBestHeight) + pb = FindBlockByHeight(height); + + if (pb == NULL || !pb->nHeight) + return 0; + + // If lookup is -1, then use blocks since last difficulty change. + if (lookup <= 0) + lookup = pb->nHeight % 2016 + 1; + + // If lookup is larger than chain, then set it to chain length. + if (lookup > pb->nHeight) + lookup = pb->nHeight; + + CBlockIndex *pb0 = pb; + int64 minTime = pb0->GetBlockTime(); + int64 maxTime = minTime; + for (int i = 0; i < lookup; i++) { + pb0 = pb0->pprev; + int64 time = pb0->GetBlockTime(); + minTime = std::min(time, minTime); + maxTime = std::max(time, maxTime); + } + + // In case there's a situation where minTime == maxTime, we don't want a divide by zero exception. + if (minTime == maxTime) + return 0; + + uint256 workDiff = pb->nChainWork - pb0->nChainWork; + int64 timeDiff = maxTime - minTime; + + return (boost::int64_t)(workDiff.getdouble() / timeDiff); +} + +Value getnetworkhashps(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "getnetworkhashps [blocks] [height]\n" + "Returns the estimated network hashes per second based on the last 120 blocks.\n" + "Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n" + "Pass in [height] to estimate the network speed at the time when a certain block was found."); + + return GetNetworkHashPS(params.size() > 0 ? params[0].get_int() : 120, params.size() > 1 ? params[1].get_int() : -1); +} + + +// Key used by getwork/getblocktemplate miners. +// Allocated in InitRPCMining, free'd in ShutdownRPCMining +static CReserveKey* pMiningKey = NULL; + +void InitRPCMining() +{ + if (!pwalletMain) + return; + + // getwork/getblocktemplate mining rewards paid here: + pMiningKey = new CReserveKey(pwalletMain); +} + +void ShutdownRPCMining() +{ + if (!pMiningKey) + return; + + delete pMiningKey; pMiningKey = NULL; +} + +Value getgenerate(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getgenerate\n" + "Returns true or false."); + + if (!pMiningKey) + return false; + + return GetBoolArg("-gen"); +} + + +Value setgenerate(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2 || 1 == 1) + throw runtime_error( + "setgenerate [genproclimit]\n" + " is true or false to turn generation on or off.\n" + "Generation is limited to [genproclimit] processors, -1 is unlimited.\n" + "Currently DOES NOT WORK"); + + bool fGenerate = true; + if (params.size() > 0) + fGenerate = params[0].get_bool(); + + if (params.size() > 1) + { + int nGenProcLimit = params[1].get_int(); + mapArgs["-genproclimit"] = itostr(nGenProcLimit); + if (nGenProcLimit == 0) + fGenerate = false; + } + mapArgs["-gen"] = (fGenerate ? "1" : "0"); + + assert(pwalletMain != NULL); + GenerateBitcoins(fGenerate, pwalletMain); + return Value::null; +} + + +Value gethashespersec(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "gethashespersec\n" + "Returns a recent hashes per second performance measurement while generating."); + + if (GetTimeMillis() - nHPSTimerStart > 8000) + return (boost::int64_t)0; + return (boost::int64_t)dHashesPerSec; +} + + +Value getmininginfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getmininginfo\n" + "Returns an object containing mining-related information."); + + Object obj; + obj.push_back(Pair("blocks", (int)nBestHeight)); + obj.push_back(Pair("currentblocksize",(uint64_t)nLastBlockSize)); + obj.push_back(Pair("currentblocktx",(uint64_t)nLastBlockTx)); + obj.push_back(Pair("difficulty", (double)GetDifficulty())); + obj.push_back(Pair("errors", GetWarnings("statusbar"))); + obj.push_back(Pair("generate", GetBoolArg("-gen"))); + obj.push_back(Pair("genproclimit", (int)GetArg("-genproclimit", -1))); + obj.push_back(Pair("hashespersec", gethashespersec(params, false))); + obj.push_back(Pair("networkhashps", getnetworkhashps(params, false))); + obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); + obj.push_back(Pair("testnet", fTestNet)); + return obj; +} + + +Value getworkex(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "getworkex [data, coinbase]\n" + "If [data, coinbase] is not specified, returns extended work data.\n" + ); + + if (vNodes.empty()) + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "CryptoLottoToken is not connected!"); + + if (IsInitialBlockDownload()) + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "CryptoLottoToken is downloading blocks..."); + + typedef map > mapNewBlock_t; + static mapNewBlock_t mapNewBlock; // FIXME: thread safety + static vector vNewBlockTemplate; + static CReserveKey reservekey(pwalletMain); + + if (params.size() == 0) + { + // Update block + static unsigned int nTransactionsUpdatedLast; + static CBlockIndex* pindexPrev; + static int64 nStart; + static CBlockTemplate* pblocktemplate; + if (pindexPrev != pindexBest || + (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) + { + if (pindexPrev != pindexBest) + { + // Deallocate old blocks since they're obsolete now + mapNewBlock.clear(); + BOOST_FOREACH(CBlockTemplate* pblocktemplate, vNewBlockTemplate) + delete pblocktemplate; + vNewBlockTemplate.clear(); + } + + // Clear pindexPrev so future getworks make a new block, despite any failures from here on + pindexPrev = NULL; + + // Store the pindexBest used before CreateNewBlock, to avoid races + nTransactionsUpdatedLast = nTransactionsUpdated; + CBlockIndex* pindexPrevNew = pindexBest; + nStart = GetTime(); + + // Create new block + pblocktemplate = CreateNewBlockWithKey(*pMiningKey); + if (!pblocktemplate) + throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); + vNewBlockTemplate.push_back(pblocktemplate); + + // Need to update only after we know CreateNewBlock succeeded + pindexPrev = pindexPrevNew; + } + CBlock* pblock = &pblocktemplate->block; // pointer for convenience + + // Update nTime + pblock->UpdateTime(pindexPrev); + pblock->nNonce = 0; + + // Update nExtraNonce + static unsigned int nExtraNonce = 0; + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); + + // Save + mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig); + + // Pre-build hash buffers + char pmidstate[32]; + char pdata[128]; + char phash1[64]; + FormatHashBuffers(pblock, pmidstate, pdata, phash1); + + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + + CTransaction coinbaseTx = pblock->vtx[0]; + std::vector merkle = pblock->GetMerkleBranch(0); + + Object result; + result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata)))); + result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); + + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << coinbaseTx; + result.push_back(Pair("coinbase", HexStr(ssTx.begin(), ssTx.end()))); + + Array merkle_arr; + + BOOST_FOREACH(uint256 merkleh, merkle) { + printf("%s\n", merkleh.ToString().c_str()); + merkle_arr.push_back(HexStr(BEGIN(merkleh), END(merkleh))); + } + + result.push_back(Pair("merkle", merkle_arr)); + + return result; + } + else + { + // Parse parameters + vector vchData = ParseHex(params[0].get_str()); + vector coinbase; + + if(params.size() == 2) + coinbase = ParseHex(params[1].get_str()); + + if (vchData.size() != 128) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); + + CBlock* pdata = (CBlock*)&vchData[0]; + + // Byte reverse + for (int i = 0; i < 128/4; i++) + ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]); + + // Get saved block + if (!mapNewBlock.count(pdata->hashMerkleRoot)) + return false; + CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first; + + pblock->nTime = pdata->nTime; + pblock->nNonce = pdata->nNonce; + + if(coinbase.size() == 0) + pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second; + else + CDataStream(coinbase, SER_NETWORK, PROTOCOL_VERSION) >> pblock->vtx[0]; + + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + + return CheckWork(pblock, *pwalletMain, reservekey); + } +} + + +Value getwork(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getwork [data]\n" + "If [data] is not specified, returns formatted hash data to work on:\n" + " \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated + " \"data\" : block data\n" + " \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated + " \"target\" : little endian hash target\n" + "If [data] is specified, tries to solve the block and returns true if it was successful."); + + if (vNodes.empty()) + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "CryptoLottoToken is not connected!"); + + if (IsInitialBlockDownload()) + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "CryptoLottoToken is downloading blocks..."); + + typedef map > mapNewBlock_t; + static mapNewBlock_t mapNewBlock; // FIXME: thread safety + static vector vNewBlockTemplate; + + if (params.size() == 0) + { + // Update block + static unsigned int nTransactionsUpdatedLast; + static CBlockIndex* pindexPrev; + static int64 nStart; + static CBlockTemplate* pblocktemplate; + if (pindexPrev != pindexBest || + (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) + { + if (pindexPrev != pindexBest) + { + // Deallocate old blocks since they're obsolete now + mapNewBlock.clear(); + BOOST_FOREACH(CBlockTemplate* pblocktemplate, vNewBlockTemplate) + delete pblocktemplate; + vNewBlockTemplate.clear(); + } + + // Clear pindexPrev so future getworks make a new block, despite any failures from here on + pindexPrev = NULL; + + // Store the pindexBest used before CreateNewBlock, to avoid races + nTransactionsUpdatedLast = nTransactionsUpdated; + CBlockIndex* pindexPrevNew = pindexBest; + nStart = GetTime(); + + // Create new block + pblocktemplate = CreateNewBlockWithKey(*pMiningKey); + if (!pblocktemplate) + throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); + vNewBlockTemplate.push_back(pblocktemplate); + + // Need to update only after we know CreateNewBlock succeeded + pindexPrev = pindexPrevNew; + } + CBlock* pblock = &pblocktemplate->block; // pointer for convenience + + // Update nTime + pblock->UpdateTime(pindexPrev); + pblock->nNonce = 0; + + // Update nExtraNonce + static unsigned int nExtraNonce = 0; + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); + + // Save + mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig); + + // Pre-build hash buffers + char pmidstate[32]; + char pdata[128]; + char phash1[64]; + FormatHashBuffers(pblock, pmidstate, pdata, phash1); + + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + + Object result; + result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated + result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata)))); + result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); // deprecated + result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); + return result; + } + else + { + // Parse parameters + vector vchData = ParseHex(params[0].get_str()); + if (vchData.size() != 128) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); + CBlock* pdata = (CBlock*)&vchData[0]; + + // Byte reverse + for (int i = 0; i < 128/4; i++) + ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]); + + // Get saved block + if (!mapNewBlock.count(pdata->hashMerkleRoot)) + return false; + CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first; + + pblock->nTime = pdata->nTime; + pblock->nNonce = pdata->nNonce; + pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second; + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + + assert(pwalletMain != NULL); + return CheckWork(pblock, *pwalletMain, *pMiningKey); + } +} + + +Value getblocktemplate(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getblocktemplate [params]\n" + "Returns data needed to construct a block to work on:\n" + " \"version\" : block version\n" + " \"previousblockhash\" : hash of current highest block\n" + " \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n" + " \"coinbaseaux\" : data that should be included in coinbase\n" + " \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n" + " \"target\" : hash target\n" + " \"mintime\" : minimum timestamp appropriate for next block\n" + " \"curtime\" : current timestamp\n" + " \"mutable\" : list of ways the block template may be changed\n" + " \"noncerange\" : range of valid nonces\n" + " \"sigoplimit\" : limit of sigops in blocks\n" + " \"sizelimit\" : limit of block size\n" + " \"bits\" : compressed target of next block\n" + " \"height\" : height of the next block\n" + "See https://en.bitcoin.it/wiki/BIP_0022 for full specification."); + + std::string strMode = "template"; + if (params.size() > 0) + { + const Object& oparam = params[0].get_obj(); + const Value& modeval = find_value(oparam, "mode"); + if (modeval.type() == str_type) + strMode = modeval.get_str(); + else if (modeval.type() == null_type) + { + /* Do nothing */ + } + else + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); + } + + if (strMode != "template") + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); + + if (vNodes.empty()) + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "CryptoLottoToken is not connected!"); + + if (IsInitialBlockDownload()) + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "CryptoLottoToken is downloading blocks..."); + + // Update block + static unsigned int nTransactionsUpdatedLast; + static CBlockIndex* pindexPrev; + static int64 nStart; + static CBlockTemplate* pblocktemplate; + if (pindexPrev != pindexBest || + (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5)) + { + // Clear pindexPrev so future calls make a new block, despite any failures from here on + pindexPrev = NULL; + + // Store the pindexBest used before CreateNewBlock, to avoid races + nTransactionsUpdatedLast = nTransactionsUpdated; + CBlockIndex* pindexPrevNew = pindexBest; + nStart = GetTime(); + + // Create new block + if(pblocktemplate) + { + delete pblocktemplate; + pblocktemplate = NULL; + } + CScript scriptDummy = CScript() << OP_TRUE; + pblocktemplate = CreateNewBlock(scriptDummy); + if (!pblocktemplate) + throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); + + // Need to update only after we know CreateNewBlock succeeded + pindexPrev = pindexPrevNew; + } + CBlock* pblock = &pblocktemplate->block; // pointer for convenience + + // Update nTime + pblock->UpdateTime(pindexPrev); + pblock->nNonce = 0; + + Array transactions; + map setTxIndex; + int i = 0; + BOOST_FOREACH (CTransaction& tx, pblock->vtx) + { + uint256 txHash = tx.GetHash(); + setTxIndex[txHash] = i++; + + if (tx.IsCoinBase()) + continue; + + Object entry; + + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << tx; + entry.push_back(Pair("data", HexStr(ssTx.begin(), ssTx.end()))); + + entry.push_back(Pair("hash", txHash.GetHex())); + + Array deps; + BOOST_FOREACH (const CTxIn &in, tx.vin) + { + if (setTxIndex.count(in.prevout.hash)) + deps.push_back(setTxIndex[in.prevout.hash]); + } + entry.push_back(Pair("depends", deps)); + + int index_in_template = i - 1; + entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template])); + entry.push_back(Pair("sigops", pblocktemplate->vTxSigOps[index_in_template])); + + transactions.push_back(entry); + } + + Object aux; + aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()))); + + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + + static Array aMutable; + if (aMutable.empty()) + { + aMutable.push_back("time"); + aMutable.push_back("transactions"); + aMutable.push_back("prevblock"); + } + + Object result; + result.push_back(Pair("version", pblock->nVersion)); + result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex())); + result.push_back(Pair("transactions", transactions)); + result.push_back(Pair("coinbaseaux", aux)); + result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue)); + result.push_back(Pair("target", hashTarget.GetHex())); + result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1)); + result.push_back(Pair("mutable", aMutable)); + result.push_back(Pair("noncerange", "00000000ffffffff")); + result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS)); + result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE)); + result.push_back(Pair("curtime", (int64_t)pblock->nTime)); + result.push_back(Pair("bits", HexBits(pblock->nBits))); + result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1))); + + return result; +} + +Value submitblock(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "submitblock [optional-params-obj]\n" + "[optional-params-obj] parameter is currently ignored.\n" + "Attempts to submit new block to network.\n" + "See https://en.bitcoin.it/wiki/BIP_0022 for full specification."); + + vector blockData(ParseHex(params[0].get_str())); + CDataStream ssBlock(blockData, SER_NETWORK, PROTOCOL_VERSION); + CBlock pblock; + try { + ssBlock >> pblock; + } + catch (std::exception &e) { + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); + } + + CValidationState state; + bool fAccepted = ProcessBlock(state, NULL, &pblock); + if (!fAccepted) + return "rejected"; // TODO: report validation state + + return Value::null; +} diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp new file mode 100644 index 0000000..1228cb4 --- /dev/null +++ b/src/rpcnet.cpp @@ -0,0 +1,208 @@ +// Copyright (c) 2009-2012 Bitcoin Developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "net.h" +#include "bitcoinrpc.h" + +using namespace json_spirit; +using namespace std; + +Value getconnectioncount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getconnectioncount\n" + "Returns the number of connections to other nodes."); + + LOCK(cs_vNodes); + return (int)vNodes.size(); +} + +static void CopyNodeStats(std::vector& vstats) +{ + vstats.clear(); + + LOCK(cs_vNodes); + vstats.reserve(vNodes.size()); + BOOST_FOREACH(CNode* pnode, vNodes) { + CNodeStats stats; + pnode->copyStats(stats); + vstats.push_back(stats); + } +} + +Value getpeerinfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getpeerinfo\n" + "Returns data about each connected network node."); + + vector vstats; + CopyNodeStats(vstats); + + Array ret; + + BOOST_FOREACH(const CNodeStats& stats, vstats) { + Object obj; + + obj.push_back(Pair("addr", stats.addrName)); + obj.push_back(Pair("services", strprintf("%08"PRI64x, stats.nServices))); + obj.push_back(Pair("lastsend", (boost::int64_t)stats.nLastSend)); + obj.push_back(Pair("lastrecv", (boost::int64_t)stats.nLastRecv)); + obj.push_back(Pair("bytessent", (boost::int64_t)stats.nSendBytes)); + obj.push_back(Pair("bytesrecv", (boost::int64_t)stats.nRecvBytes)); + obj.push_back(Pair("blocksrequested", (boost::int64_t)stats.nBlocksRequested)); + obj.push_back(Pair("conntime", (boost::int64_t)stats.nTimeConnected)); + obj.push_back(Pair("version", stats.nVersion)); + // Use the sanitized form of subver here, to avoid tricksy remote peers from + // corrupting or modifiying the JSON output by putting special characters in + // their ver message. + obj.push_back(Pair("subver", stats.cleanSubVer)); + obj.push_back(Pair("inbound", stats.fInbound)); + obj.push_back(Pair("startingheight", stats.nStartingHeight)); + obj.push_back(Pair("banscore", stats.nMisbehavior)); + if (stats.fSyncNode) + obj.push_back(Pair("syncnode", true)); + + ret.push_back(obj); + } + + return ret; +} + +Value addnode(const Array& params, bool fHelp) +{ + string strCommand; + if (params.size() == 2) + strCommand = params[1].get_str(); + if (fHelp || params.size() != 2 || + (strCommand != "onetry" && strCommand != "add" && strCommand != "remove")) + throw runtime_error( + "addnode \n" + "Attempts add or remove from the addnode list or try a connection to once."); + + string strNode = params[0].get_str(); + + if (strCommand == "onetry") + { + CAddress addr; + ConnectNode(addr, strNode.c_str()); + return Value::null; + } + + LOCK(cs_vAddedNodes); + vector::iterator it = vAddedNodes.begin(); + for(; it != vAddedNodes.end(); it++) + if (strNode == *it) + break; + + if (strCommand == "add") + { + if (it != vAddedNodes.end()) + throw JSONRPCError(-23, "Error: Node already added"); + vAddedNodes.push_back(strNode); + } + else if(strCommand == "remove") + { + if (it == vAddedNodes.end()) + throw JSONRPCError(-24, "Error: Node has not been added."); + vAddedNodes.erase(it); + } + + return Value::null; +} + +Value getaddednodeinfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getaddednodeinfo [node]\n" + "Returns information about the given added node, or all added nodes\n" + "(note that onetry addnodes are not listed here)\n" + "If dns is false, only a list of added nodes will be provided,\n" + "otherwise connected information will also be available."); + + bool fDns = params[0].get_bool(); + + list laddedNodes(0); + if (params.size() == 1) + { + LOCK(cs_vAddedNodes); + BOOST_FOREACH(string& strAddNode, vAddedNodes) + laddedNodes.push_back(strAddNode); + } + else + { + string strNode = params[1].get_str(); + LOCK(cs_vAddedNodes); + BOOST_FOREACH(string& strAddNode, vAddedNodes) + if (strAddNode == strNode) + { + laddedNodes.push_back(strAddNode); + break; + } + if (laddedNodes.size() == 0) + throw JSONRPCError(-24, "Error: Node has not been added."); + } + + if (!fDns) + { + Object ret; + BOOST_FOREACH(string& strAddNode, laddedNodes) + ret.push_back(Pair("addednode", strAddNode)); + return ret; + } + + Array ret; + + list > > laddedAddreses(0); + BOOST_FOREACH(string& strAddNode, laddedNodes) + { + vector vservNode(0); + if(Lookup(strAddNode.c_str(), vservNode, GetDefaultPort(), fNameLookup, 0)) + laddedAddreses.push_back(make_pair(strAddNode, vservNode)); + else + { + Object obj; + obj.push_back(Pair("addednode", strAddNode)); + obj.push_back(Pair("connected", false)); + Array addresses; + obj.push_back(Pair("addresses", addresses)); + } + } + + LOCK(cs_vNodes); + for (list > >::iterator it = laddedAddreses.begin(); it != laddedAddreses.end(); it++) + { + Object obj; + obj.push_back(Pair("addednode", it->first)); + + Array addresses; + bool fConnected = false; + BOOST_FOREACH(CService& addrNode, it->second) + { + bool fFound = false; + Object node; + node.push_back(Pair("address", addrNode.ToString())); + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode->addr == addrNode) + { + fFound = true; + fConnected = true; + node.push_back(Pair("connected", pnode->fInbound ? "inbound" : "outbound")); + break; + } + if (!fFound) + node.push_back(Pair("connected", "false")); + addresses.push_back(node); + } + obj.push_back(Pair("connected", fConnected)); + obj.push_back(Pair("addresses", addresses)); + ret.push_back(obj); + } + + return ret; +} + diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp new file mode 100644 index 0000000..f02b40d --- /dev/null +++ b/src/rpcrawtransaction.cpp @@ -0,0 +1,574 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include "base58.h" +#include "bitcoinrpc.h" +#include "db.h" +#include "init.h" +#include "main.h" +#include "net.h" +#include "wallet.h" + +using namespace std; +using namespace boost; +using namespace boost::assign; +using namespace json_spirit; + +// +// Utilities: convert hex-encoded Values +// (throws error if not hex). +// +uint256 ParseHashV(const Value& v, string strName) +{ + string strHex; + if (v.type() == str_type) + strHex = v.get_str(); + if (!IsHex(strHex)) // Note: IsHex("") is false + throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); + uint256 result; + result.SetHex(strHex); + return result; +} +uint256 ParseHashO(const Object& o, string strKey) +{ + return ParseHashV(find_value(o, strKey), strKey); +} +vector ParseHexV(const Value& v, string strName) +{ + string strHex; + if (v.type() == str_type) + strHex = v.get_str(); + if (!IsHex(strHex)) + throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); + return ParseHex(strHex); +} +vector ParseHexO(const Object& o, string strKey) +{ + return ParseHexV(find_value(o, strKey), strKey); +} + +void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out) +{ + txnouttype type; + vector addresses; + int nRequired; + + out.push_back(Pair("asm", scriptPubKey.ToString())); + out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + + if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) + { + out.push_back(Pair("type", GetTxnOutputType(TX_NONSTANDARD))); + return; + } + + out.push_back(Pair("reqSigs", nRequired)); + out.push_back(Pair("type", GetTxnOutputType(type))); + + Array a; + 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) +{ + entry.push_back(Pair("txid", tx.GetHash().GetHex())); + entry.push_back(Pair("version", tx.nVersion)); + entry.push_back(Pair("locktime", (boost::int64_t)tx.nLockTime)); + Array vin; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + Object in; + 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", (boost::int64_t)txin.prevout.n)); + Object o; + 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)); + } + in.push_back(Pair("sequence", (boost::int64_t)txin.nSequence)); + vin.push_back(in); + } + entry.push_back(Pair("vin", vin)); + Array vout; + for (unsigned int i = 0; i < tx.vout.size(); i++) + { + const CTxOut& txout = tx.vout[i]; + Object out; + out.push_back(Pair("value", ValueFromAmount(txout.nValue))); + out.push_back(Pair("n", (boost::int64_t)i)); + Object o; + ScriptPubKeyToJSON(txout.scriptPubKey, o); + out.push_back(Pair("scriptPubKey", o)); + vout.push_back(out); + } + entry.push_back(Pair("vout", vout)); + + if (hashBlock != 0) + { + entry.push_back(Pair("blockhash", hashBlock.GetHex())); + map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end() && (*mi).second) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + { + entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight)); + entry.push_back(Pair("time", (boost::int64_t)pindex->nTime)); + entry.push_back(Pair("blocktime", (boost::int64_t)pindex->nTime)); + } + else + entry.push_back(Pair("confirmations", 0)); + } + } +} + +Value getrawtransaction(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getrawtransaction [verbose=0]\n" + "If verbose=0, returns a string that is\n" + "serialized, hex-encoded data for .\n" + "If verbose is non-zero, returns an Object\n" + "with information about ."); + + uint256 hash = ParseHashV(params[0], "parameter 1"); + + bool fVerbose = false; + if (params.size() > 1) + fVerbose = (params[1].get_int() != 0); + + CTransaction tx; + uint256 hashBlock = 0; + if (!GetTransaction(hash, tx, hashBlock, true)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); + + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << tx; + string strHex = HexStr(ssTx.begin(), ssTx.end()); + + if (!fVerbose) + return strHex; + + Object result; + result.push_back(Pair("hex", strHex)); + TxToJSON(tx, hashBlock, result); + return result; +} + +Value listunspent(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 3) + throw runtime_error( + "listunspent [minconf=1] [maxconf=9999999] [\"address\",...]\n" + "Returns array of unspent transaction outputs\n" + "with between minconf and maxconf (inclusive) confirmations.\n" + "Optionally filtered to only include txouts paid to specified addresses.\n" + "Results are an array of Objects, each of which has:\n" + "{txid, vout, scriptPubKey, amount, confirmations}"); + + RPCTypeCheck(params, list_of(int_type)(int_type)(array_type)); + + int nMinDepth = 1; + if (params.size() > 0) + nMinDepth = params[0].get_int(); + + int nMaxDepth = 9999999; + if (params.size() > 1) + nMaxDepth = params[1].get_int(); + + set setAddress; + if (params.size() > 2) + { + Array inputs = params[2].get_array(); + BOOST_FOREACH(Value& input, inputs) + { + CBitcoinAddress address(input.get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid CryptoLottoToken address: ")+input.get_str()); + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+input.get_str()); + setAddress.insert(address); + } + } + + Array results; + vector vecOutputs; + assert(pwalletMain != NULL); + pwalletMain->AvailableCoins(vecOutputs, false); + BOOST_FOREACH(const COutput& out, vecOutputs) + { + if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) + continue; + + if (setAddress.size()) + { + CTxDestination address; + if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) + continue; + + if (!setAddress.count(address)) + continue; + } + + int64 nValue = out.tx->vout[out.i].nValue; + const CScript& pk = out.tx->vout[out.i].scriptPubKey; + Object entry; + entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); + entry.push_back(Pair("vout", out.i)); + CTxDestination address; + if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) + { + entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); + if (pwalletMain->mapAddressBook.count(address)) + entry.push_back(Pair("account", pwalletMain->mapAddressBook[address])); + } + entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end()))); + if (pk.IsPayToScriptHash()) + { + CTxDestination address; + if (ExtractDestination(pk, address)) + { + const CScriptID& hash = boost::get(address); + CScript redeemScript; + if (pwalletMain->GetCScript(hash, redeemScript)) + entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); + } + } + entry.push_back(Pair("amount",ValueFromAmount(nValue))); + entry.push_back(Pair("confirmations",out.nDepth)); + results.push_back(entry); + } + + return results; +} + +Value createrawtransaction(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 2) + throw runtime_error( + "createrawtransaction [{\"txid\":txid,\"vout\":n},...] {address:amount,...}\n" + "Create a transaction spending given inputs\n" + "(array of objects containing transaction id and output number),\n" + "sending to given address(es).\n" + "Returns hex-encoded raw transaction.\n" + "Note that the transaction's inputs are not signed, and\n" + "it is not stored in the wallet or transmitted to the network."); + + RPCTypeCheck(params, list_of(array_type)(obj_type)); + + Array inputs = params[0].get_array(); + Object sendTo = params[1].get_obj(); + + CTransaction rawTx; + + BOOST_FOREACH(const Value& input, inputs) + { + const Object& o = input.get_obj(); + + uint256 txid = ParseHashO(o, "txid"); + + const Value& vout_v = find_value(o, "vout"); + if (vout_v.type() != int_type) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); + int nOutput = vout_v.get_int(); + if (nOutput < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); + + CTxIn in(COutPoint(txid, nOutput)); + rawTx.vin.push_back(in); + } + + set setAddress; + BOOST_FOREACH(const Pair& s, sendTo) + { + CBitcoinAddress address(s.name_); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid CryptoLottoToken address: ")+s.name_); + + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_); + setAddress.insert(address); + + CScript scriptPubKey; + scriptPubKey.SetDestination(address.Get()); + int64 nAmount = AmountFromValue(s.value_); + + CTxOut out(nAmount, scriptPubKey); + rawTx.vout.push_back(out); + } + + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << rawTx; + return HexStr(ss.begin(), ss.end()); +} + +Value decoderawtransaction(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "decoderawtransaction \n" + "Return a JSON object representing the serialized, hex-encoded transaction."); + + vector txData(ParseHexV(params[0], "argument")); + CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); + CTransaction tx; + try { + ssData >> tx; + } + catch (std::exception &e) { + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + } + + Object result; + TxToJSON(tx, 0, result); + + return result; +} + +Value signrawtransaction(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 4) + throw runtime_error( + "signrawtransaction [{\"txid\":txid,\"vout\":n,\"scriptPubKey\":hex,\"redeemScript\":hex},...] [,...] [sighashtype=\"ALL\"]\n" + "Sign inputs for raw transaction (serialized, hex-encoded).\n" + "Second optional argument (may be null) is an array of previous transaction outputs that\n" + "this transaction depends on but may not yet be in the block chain.\n" + "Third optional argument (may be null) is an array of base58-encoded private\n" + "keys that, if given, will be the only keys used to sign the transaction.\n" + "Fourth optional argument is a string that is one of six values; ALL, NONE, SINGLE or\n" + "ALL|ANYONECANPAY, NONE|ANYONECANPAY, SINGLE|ANYONECANPAY.\n" + "Returns json object with keys:\n" + " hex : raw transaction with signature(s) (hex-encoded string)\n" + " complete : 1 if transaction has a complete set of signature (0 if not)" + + HelpRequiringPassphrase()); + + RPCTypeCheck(params, list_of(str_type)(array_type)(array_type)(str_type), true); + + vector txData(ParseHexV(params[0], "argument 1")); + CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); + vector txVariants; + while (!ssData.empty()) + { + try { + CTransaction tx; + ssData >> tx; + txVariants.push_back(tx); + } + catch (std::exception &e) { + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + } + } + + if (txVariants.empty()) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction"); + + // mergedTx will end up with all the signatures; it + // starts as a clone of the rawtx: + CTransaction mergedTx(txVariants[0]); + bool fComplete = true; + + // Fetch previous transactions (inputs): + CCoinsView viewDummy; + CCoinsViewCache view(viewDummy); + { + LOCK(mempool.cs); + CCoinsViewCache &viewChain = *pcoinsTip; + CCoinsViewMemPool viewMempool(viewChain, mempool); + view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view + + BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) { + const uint256& prevHash = txin.prevout.hash; + CCoins coins; + view.GetCoins(prevHash, coins); // this is certainly allowed to fail + } + + view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long + } + + bool fGivenKeys = false; + CBasicKeyStore tempKeystore; + if (params.size() > 2 && params[2].type() != null_type) + { + fGivenKeys = true; + Array keys = params[2].get_array(); + BOOST_FOREACH(Value k, keys) + { + CBitcoinSecret vchSecret; + bool fGood = vchSecret.SetString(k.get_str()); + if (!fGood) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); + CKey key = vchSecret.GetKey(); + tempKeystore.AddKey(key); + } + } + else + EnsureWalletIsUnlocked(); + + // 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) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}"); + + Object prevOut = p.get_obj(); + + RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)); + + uint256 txid = ParseHashO(prevOut, "txid"); + + int nOut = find_value(prevOut, "vout").get_int(); + if (nOut < 0) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive"); + + vector pkData(ParseHexO(prevOut, "scriptPubKey")); + CScript scriptPubKey(pkData.begin(), pkData.end()); + + CCoins coins; + if (view.GetCoins(txid, coins)) { + if (coins.IsAvailable(nOut) && coins.vout[nOut].scriptPubKey != scriptPubKey) { + string err("Previous output scriptPubKey mismatch:\n"); + err = err + coins.vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+ + scriptPubKey.ToString(); + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); + } + // what todo if txid is known, but the actual output isn't? + } + if ((unsigned int)nOut >= coins.vout.size()) + coins.vout.resize(nOut+1); + coins.vout[nOut].scriptPubKey = scriptPubKey; + coins.vout[nOut].nValue = 0; // we don't know the actual output value + view.SetCoins(txid, coins); + + // 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, 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)) + { + vector rsData(ParseHexV(v, "redeemScript")); + CScript redeemScript(rsData.begin(), rsData.end()); + tempKeystore.AddCScript(redeemScript); + } + } + } + } + + const CKeyStore& keystore = ((fGivenKeys || !pwalletMain) ? tempKeystore : *pwalletMain); + + int nHashType = SIGHASH_ALL; + if (params.size() > 3 && params[3].type() != null_type) + { + static map mapSigHashValues = + boost::assign::map_list_of + (string("ALL"), int(SIGHASH_ALL)) + (string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)) + (string("NONE"), int(SIGHASH_NONE)) + (string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)) + (string("SINGLE"), int(SIGHASH_SINGLE)) + (string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)) + ; + string strHashType = params[3].get_str(); + if (mapSigHashValues.count(strHashType)) + nHashType = mapSigHashValues[strHashType]; + else + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param"); + } + + bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); + + // Sign what we can: + for (unsigned int i = 0; i < mergedTx.vin.size(); i++) + { + CTxIn& txin = mergedTx.vin[i]; + CCoins coins; + if (!view.GetCoins(txin.prevout.hash, coins) || !coins.IsAvailable(txin.prevout.n)) + { + fComplete = false; + continue; + } + const CScript& prevPubKey = coins.vout[txin.prevout.n].scriptPubKey; + + txin.scriptSig.clear(); + // Only sign SIGHASH_SINGLE if there's a corresponding output: + if (!fHashSingle || (i < mergedTx.vout.size())) + SignSignature(keystore, prevPubKey, mergedTx, i, nHashType); + + // ... and merge in other signatures: + BOOST_FOREACH(const CTransaction& txv, txVariants) + { + txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); + } + if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, 0)) + fComplete = false; + } + + Object result; + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << mergedTx; + result.push_back(Pair("hex", HexStr(ssTx.begin(), ssTx.end()))); + result.push_back(Pair("complete", fComplete)); + + return result; +} + +Value sendrawtransaction(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 1) + throw runtime_error( + "sendrawtransaction \n" + "Submits raw transaction (serialized, hex-encoded) to local node and network."); + + // parse hex string from parameter + vector txData(ParseHexV(params[0], "parameter")); + CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); + CTransaction tx; + + // deserialize binary data stream + try { + ssData >> tx; + } + catch (std::exception &e) { + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + } + uint256 hashTx = tx.GetHash(); + + bool fHave = false; + CCoinsViewCache &view = *pcoinsTip; + CCoins existingCoins; + { + fHave = view.GetCoins(hashTx, existingCoins); + if (!fHave) { + // push to local node + CValidationState state; + if (!tx.AcceptToMemoryPool(state, true, false)) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); // TODO: report validation state + } + } + if (fHave) { + if (existingCoins.nHeight < 1000000000) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "transaction already in block chain"); + // Not in block, but already in the memory pool; will drop + // through to re-relay it. + } else { + SyncWithWallets(hashTx, tx, NULL, true); + } + RelayTransaction(tx, hashTx); + + return hashTx.GetHex(); +} diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp new file mode 100644 index 0000000..2dce8d2 --- /dev/null +++ b/src/rpcwallet.cpp @@ -0,0 +1,1609 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include "wallet.h" +#include "walletdb.h" +#include "bitcoinrpc.h" +#include "init.h" +#include "base58.h" + +using namespace std; +using namespace boost; +using namespace boost::assign; +using namespace json_spirit; + +int64 nWalletUnlockTime; +static CCriticalSection cs_nWalletUnlockTime; + +std::string HelpRequiringPassphrase() +{ + return pwalletMain && pwalletMain->IsCrypted() + ? "\nrequires wallet passphrase to be set with walletpassphrase first" + : ""; +} + +void EnsureWalletIsUnlocked() +{ + if (pwalletMain->IsLocked()) + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); +} + +void WalletTxToJSON(const CWalletTx& wtx, Object& entry) +{ + int confirms = wtx.GetDepthInMainChain(); + entry.push_back(Pair("confirmations", confirms)); + if (wtx.IsCoinBase()) + entry.push_back(Pair("generated", true)); + if (confirms) + { + entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex())); + entry.push_back(Pair("blockindex", wtx.nIndex)); + entry.push_back(Pair("blocktime", (boost::int64_t)(mapBlockIndex[wtx.hashBlock]->nTime))); + } + entry.push_back(Pair("txid", wtx.GetHash().GetHex())); + entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime())); + entry.push_back(Pair("timereceived", (boost::int64_t)wtx.nTimeReceived)); + BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) + entry.push_back(Pair(item.first, item.second)); +} + +string AccountFromValue(const Value& value) +{ + string strAccount = value.get_str(); + if (strAccount == "*") + throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Invalid account name"); + return strAccount; +} + +Value getinfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getinfo\n" + "Returns an object containing various state info."); + + proxyType proxy; + GetProxy(NET_IPV4, proxy); + + Object obj; + obj.push_back(Pair("version", (int)CLIENT_VERSION)); + obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION)); + if (pwalletMain) { + obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); + obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); + } + obj.push_back(Pair("blocks", (int)nBestHeight)); + obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset())); + obj.push_back(Pair("connections", (int)vNodes.size())); + obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string()))); + obj.push_back(Pair("difficulty", (double)GetDifficulty())); + obj.push_back(Pair("testnet", fTestNet)); + if (pwalletMain) { + obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime())); + obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); + } + obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee))); + obj.push_back(Pair("mininput", ValueFromAmount(nMinimumInputValue))); + if (pwalletMain && pwalletMain->IsCrypted()) + obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime)); + obj.push_back(Pair("errors", GetWarnings("statusbar"))); + return obj; +} + + + +Value getnewaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getnewaddress [account]\n" + "Returns a new CryptoLottoToken address for receiving payments. " + "If [account] is specified (recommended), it is added to the address book " + "so payments received with the address will be credited to [account]."); + + // Parse the account first so we don't generate a key if there's an error + string strAccount; + if (params.size() > 0) + strAccount = AccountFromValue(params[0]); + + if (!pwalletMain->IsLocked()) + pwalletMain->TopUpKeyPool(); + + // Generate a new key that is added to wallet + CPubKey newKey; + if (!pwalletMain->GetKeyFromPool(newKey, false)) + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + CKeyID keyID = newKey.GetID(); + + pwalletMain->SetAddressBookName(keyID, strAccount); + + return CBitcoinAddress(keyID).ToString(); +} + + +CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false) +{ + CWalletDB walletdb(pwalletMain->strWalletFile); + + CAccount account; + walletdb.ReadAccount(strAccount, account); + + bool bKeyUsed = false; + + // Check if the current key has been used + if (account.vchPubKey.IsValid()) + { + CScript scriptPubKey; + scriptPubKey.SetDestination(account.vchPubKey.GetID()); + for (map::iterator it = pwalletMain->mapWallet.begin(); + it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid(); + ++it) + { + const CWalletTx& wtx = (*it).second; + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (txout.scriptPubKey == scriptPubKey) + bKeyUsed = true; + } + } + + // Generate a new key + if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed) + { + if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false)) + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + + pwalletMain->SetAddressBookName(account.vchPubKey.GetID(), strAccount); + walletdb.WriteAccount(strAccount, account); + } + + return CBitcoinAddress(account.vchPubKey.GetID()); +} + +Value getaccountaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaccountaddress \n" + "Returns the current CryptoLottoToken address for receiving payments to this account."); + + // Parse the account first so we don't generate a key if there's an error + string strAccount = AccountFromValue(params[0]); + + Value ret; + + ret = GetAccountAddress(strAccount).ToString(); + + return ret; +} + + + +Value setaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "setaccount \n" + "Sets the account associated with the given address."); + + CBitcoinAddress address(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid CryptoLottoToken address"); + + + string strAccount; + if (params.size() > 1) + strAccount = AccountFromValue(params[1]); + + // Detect when changing the account of an address that is the 'unused current key' of another account: + if (pwalletMain->mapAddressBook.count(address.Get())) + { + string strOldAccount = pwalletMain->mapAddressBook[address.Get()]; + if (address == GetAccountAddress(strOldAccount)) + GetAccountAddress(strOldAccount, true); + } + + pwalletMain->SetAddressBookName(address.Get(), strAccount); + + return Value::null; +} + + +Value getaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaccount \n" + "Returns the account associated with the given address."); + + CBitcoinAddress address(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid CryptoLottoToken address"); + + string strAccount; + map::iterator mi = pwalletMain->mapAddressBook.find(address.Get()); + if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty()) + strAccount = (*mi).second; + return strAccount; +} + + +Value getaddressesbyaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaddressesbyaccount \n" + "Returns the list of addresses for the given account."); + + string strAccount = AccountFromValue(params[0]); + + // Find all addresses that have the given account + Array ret; + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook) + { + const CBitcoinAddress& address = item.first; + const string& strName = item.second; + if (strName == strAccount) + ret.push_back(address.ToString()); + } + return ret; +} + + +Value setmininput(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 1) + throw runtime_error( + "setmininput \n" + " is a real and is rounded to the nearest 0.00000001"); + + // Amount + int64 nAmount = 0; + if (params[0].get_real() != 0.0) + nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts + + nMinimumInputValue = nAmount; + return true; +} + + +Value sendtoaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 4) + throw runtime_error( + "sendtoaddress [comment] [comment-to]\n" + " is a real and is rounded to the nearest 0.00000001" + + HelpRequiringPassphrase()); + + CBitcoinAddress address(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid CryptoLottoToken address"); + + // Amount + int64 nAmount = AmountFromValue(params[1]); + + // Wallet comments + CWalletTx wtx; + if (params.size() > 2 && params[2].type() != null_type && !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()) + wtx.mapValue["to"] = params[3].get_str(); + + if (pwalletMain->IsLocked()) + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); + + string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx); + if (strError != "") + throw JSONRPCError(RPC_WALLET_ERROR, strError); + + return wtx.GetHash().GetHex(); +} + +Value listaddressgroupings(const Array& params, bool fHelp) +{ + if (fHelp) + throw runtime_error( + "listaddressgroupings\n" + "Lists groups of addresses which have had their common ownership\n" + "made public by common use as inputs or as the resulting change\n" + "in past transactions"); + + Array jsonGroupings; + map balances = pwalletMain->GetAddressBalances(); + BOOST_FOREACH(set grouping, pwalletMain->GetAddressGroupings()) + { + Array jsonGrouping; + BOOST_FOREACH(CTxDestination address, grouping) + { + Array addressInfo; + addressInfo.push_back(CBitcoinAddress(address).ToString()); + addressInfo.push_back(ValueFromAmount(balances[address])); + { + LOCK(pwalletMain->cs_wallet); + if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end()) + addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second); + } + jsonGrouping.push_back(addressInfo); + } + jsonGroupings.push_back(jsonGrouping); + } + return jsonGroupings; +} + +Value signmessage(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 2) + throw runtime_error( + "signmessage \n" + "Sign a message with the private key of an address"); + + EnsureWalletIsUnlocked(); + + string strAddress = params[0].get_str(); + string strMessage = params[1].get_str(); + + CBitcoinAddress addr(strAddress); + if (!addr.IsValid()) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); + + CKeyID keyID; + if (!addr.GetKeyID(keyID)) + throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); + + CKey key; + if (!pwalletMain->GetKey(keyID, key)) + throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); + + CHashWriter ss(SER_GETHASH, 0); + ss << strMessageMagic; + ss << strMessage; + + vector vchSig; + if (!key.SignCompact(ss.GetHash(), vchSig)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); + + return EncodeBase64(&vchSig[0], vchSig.size()); +} + +Value verifymessage(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 3) + throw runtime_error( + "verifymessage \n" + "Verify a signed message"); + + string strAddress = params[0].get_str(); + string strSign = params[1].get_str(); + string strMessage = params[2].get_str(); + + CBitcoinAddress addr(strAddress); + if (!addr.IsValid()) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); + + CKeyID keyID; + if (!addr.GetKeyID(keyID)) + throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); + + bool fInvalid = false; + vector vchSig = DecodeBase64(strSign.c_str(), &fInvalid); + + if (fInvalid) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding"); + + CHashWriter ss(SER_GETHASH, 0); + ss << strMessageMagic; + ss << strMessage; + + CPubKey pubkey; + if (!pubkey.RecoverCompact(ss.GetHash(), vchSig)) + return false; + + return (pubkey.GetID() == keyID); +} + + +Value getreceivedbyaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getreceivedbyaddress [minconf=1]\n" + "Returns the total amount received by in transactions with at least [minconf] confirmations."); + + // Bitcoin address + CBitcoinAddress address = CBitcoinAddress(params[0].get_str()); + CScript scriptPubKey; + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid CryptoLottoToken address"); + scriptPubKey.SetDestination(address.Get()); + if (!IsMine(*pwalletMain,scriptPubKey)) + return (double)0.0; + + // Minimum confirmations + int nMinDepth = 1; + if (params.size() > 1) + nMinDepth = params[1].get_int(); + + // Tally + int64 nAmount = 0; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || !wtx.IsFinal()) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + if (txout.scriptPubKey == scriptPubKey) + if (wtx.GetDepthInMainChain() >= nMinDepth) + nAmount += txout.nValue; + } + + return ValueFromAmount(nAmount); +} + + +void GetAccountAddresses(string strAccount, set& setAddress) +{ + BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& item, pwalletMain->mapAddressBook) + { + const CTxDestination& address = item.first; + const string& strName = item.second; + if (strName == strAccount) + setAddress.insert(address); + } +} + +Value getreceivedbyaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getreceivedbyaccount [minconf=1]\n" + "Returns the total amount received by addresses with in transactions with at least [minconf] confirmations."); + + // Minimum confirmations + int nMinDepth = 1; + if (params.size() > 1) + nMinDepth = params[1].get_int(); + + // Get the set of pub keys assigned to account + string strAccount = AccountFromValue(params[0]); + set setAddress; + GetAccountAddresses(strAccount, setAddress); + + // Tally + int64 nAmount = 0; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || !wtx.IsFinal()) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + CTxDestination address; + if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address)) + if (wtx.GetDepthInMainChain() >= nMinDepth) + nAmount += txout.nValue; + } + } + + return (double)nAmount / (double)COIN; +} + + +int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth) +{ + int64 nBalance = 0; + + // Tally wallet transactions + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (!wtx.IsFinal()) + continue; + + int64 nReceived, nSent, nFee; + wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee); + + if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) + nBalance += nReceived; + nBalance -= nSent + nFee; + } + + // Tally internal accounting entries + nBalance += walletdb.GetAccountCreditDebit(strAccount); + + return nBalance; +} + +int64 GetAccountBalance(const string& strAccount, int nMinDepth) +{ + CWalletDB walletdb(pwalletMain->strWalletFile); + return GetAccountBalance(walletdb, strAccount, nMinDepth); +} + + +Value getbalance(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "getbalance [account] [minconf=1]\n" + "If [account] is not specified, returns the server's total available balance.\n" + "If [account] is specified, returns the balance in the account."); + + if (params.size() == 0) + return ValueFromAmount(pwalletMain->GetBalance()); + + int nMinDepth = 1; + if (params.size() > 1) + nMinDepth = params[1].get_int(); + + if (params[0].get_str() == "*") { + // Calculate total balance a different way from GetBalance() + // (GetBalance() sums up all unspent TxOuts) + // getbalance and getbalance '*' 0 should return the same number + int64 nBalance = 0; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (!wtx.IsConfirmed()) + continue; + + int64 allFee; + string strSentAccount; + list > listReceived; + list > listSent; + wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount); + if (wtx.GetDepthInMainChain() >= nMinDepth) + { + BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived) + nBalance += r.second; + } + BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listSent) + nBalance -= r.second; + nBalance -= allFee; + } + return ValueFromAmount(nBalance); + } + + string strAccount = AccountFromValue(params[0]); + + int64 nBalance = GetAccountBalance(strAccount, nMinDepth); + + return ValueFromAmount(nBalance); +} + + +Value movecmd(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 3 || params.size() > 5) + throw runtime_error( + "move [minconf=1] [comment]\n" + "Move from one account in your wallet to another."); + + string strFrom = AccountFromValue(params[0]); + string strTo = AccountFromValue(params[1]); + int64 nAmount = AmountFromValue(params[2]); + if (params.size() > 3) + // unused parameter, used to be nMinDepth, keep type-checking it though + (void)params[3].get_int(); + string strComment; + if (params.size() > 4) + strComment = params[4].get_str(); + + CWalletDB walletdb(pwalletMain->strWalletFile); + if (!walletdb.TxnBegin()) + throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); + + int64 nNow = GetAdjustedTime(); + + // Debit + CAccountingEntry debit; + debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); + debit.strAccount = strFrom; + debit.nCreditDebit = -nAmount; + debit.nTime = nNow; + debit.strOtherAccount = strTo; + debit.strComment = strComment; + walletdb.WriteAccountingEntry(debit); + + // Credit + CAccountingEntry credit; + credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); + credit.strAccount = strTo; + credit.nCreditDebit = nAmount; + credit.nTime = nNow; + credit.strOtherAccount = strFrom; + credit.strComment = strComment; + walletdb.WriteAccountingEntry(credit); + + if (!walletdb.TxnCommit()) + throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); + + return true; +} + + +Value sendfrom(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 3 || params.size() > 6) + throw runtime_error( + "sendfrom [minconf=1] [comment] [comment-to]\n" + " is a real and is rounded to the nearest 0.00000001" + + HelpRequiringPassphrase()); + + string strAccount = AccountFromValue(params[0]); + CBitcoinAddress address(params[1].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid CryptoLottoToken address"); + int64 nAmount = AmountFromValue(params[2]); + 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()) + wtx.mapValue["comment"] = params[4].get_str(); + if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty()) + wtx.mapValue["to"] = params[5].get_str(); + + EnsureWalletIsUnlocked(); + + // Check funds + int64 nBalance = GetAccountBalance(strAccount, nMinDepth); + if (nAmount > nBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); + + // Send + string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx); + if (strError != "") + throw JSONRPCError(RPC_WALLET_ERROR, strError); + + return wtx.GetHash().GetHex(); +} + + +Value sendmany(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 4) + throw runtime_error( + "sendmany {address:amount,...} [minconf=1] [comment]\n" + "amounts are double-precision floating point numbers" + + HelpRequiringPassphrase()); + + string strAccount = AccountFromValue(params[0]); + Object 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()) + wtx.mapValue["comment"] = params[3].get_str(); + + set setAddress; + vector > vecSend; + + int64 totalAmount = 0; + BOOST_FOREACH(const Pair& s, sendTo) + { + CBitcoinAddress address(s.name_); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid CryptoLottoToken address: ")+s.name_); + + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_); + setAddress.insert(address); + + CScript scriptPubKey; + scriptPubKey.SetDestination(address.Get()); + int64 nAmount = AmountFromValue(s.value_); + totalAmount += nAmount; + + vecSend.push_back(make_pair(scriptPubKey, nAmount)); + } + + EnsureWalletIsUnlocked(); + + // Check funds + int64 nBalance = GetAccountBalance(strAccount, nMinDepth); + if (totalAmount > nBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); + + // Send + CReserveKey keyChange(pwalletMain); + int64 nFeeRequired = 0; + string strFailReason; + bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, strFailReason); + if (!fCreated) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); + if (!pwalletMain->CommitTransaction(wtx, keyChange)) + throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed"); + + return wtx.GetHash().GetHex(); +} + +// +// Used by addmultisigaddress / createmultisig: +// +static CScript _createmultisig(const Array& params) +{ + int nRequired = params[0].get_int(); + const Array& keys = params[1].get_array(); + + // Gather public keys + if (nRequired < 1) + throw runtime_error("a multisignature address must require at least one key to redeem"); + if ((int)keys.size() < nRequired) + throw runtime_error( + strprintf("not enough keys supplied " + "(got %"PRIszu" keys, but need at least %d to redeem)", keys.size(), nRequired)); + std::vector pubkeys; + pubkeys.resize(keys.size()); + for (unsigned int i = 0; i < keys.size(); i++) + { + const std::string& ks = keys[i].get_str(); + + // Case 1: CryptoLottoToken address and we have full public key: + CBitcoinAddress address(ks); + if (pwalletMain && address.IsValid()) + { + CKeyID keyID; + if (!address.GetKeyID(keyID)) + throw runtime_error( + strprintf("%s does not refer to a key",ks.c_str())); + CPubKey vchPubKey; + if (!pwalletMain->GetPubKey(keyID, vchPubKey)) + throw runtime_error( + strprintf("no full public key for address %s",ks.c_str())); + if (!vchPubKey.IsFullyValid()) + throw runtime_error(" Invalid public key: "+ks); + pubkeys[i] = vchPubKey; + } + + // Case 2: hex public key + else if (IsHex(ks)) + { + CPubKey vchPubKey(ParseHex(ks)); + if (!vchPubKey.IsFullyValid()) + throw runtime_error(" Invalid public key: "+ks); + pubkeys[i] = vchPubKey; + } + else + { + throw runtime_error(" Invalid public key: "+ks); + } + } + CScript result; + result.SetMultisig(nRequired, pubkeys); + return result; +} + +Value addmultisigaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 3) + { + string msg = "addmultisigaddress <'[\"key\",\"key\"]'> [account]\n" + "Add a nrequired-to-sign multisignature address to the wallet\"\n" + "each key is a CryptoLottoToken address or hex-encoded public key\n" + "If [account] is specified, assign address to [account]."; + throw runtime_error(msg); + } + + string strAccount; + if (params.size() > 2) + strAccount = AccountFromValue(params[2]); + + // Construct using pay-to-script-hash: + CScript inner = _createmultisig(params); + CScriptID innerID = inner.GetID(); + pwalletMain->AddCScript(inner); + + pwalletMain->SetAddressBookName(innerID, strAccount); + return CBitcoinAddress(innerID).ToString(); +} + +Value createmultisig(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 2) + { + string msg = "createmultisig <'[\"key\",\"key\"]'>\n" + "Creates a multi-signature address and returns a json object\n" + "with keys:\n" + "address : CryptoLottoToken address\n" + "redeemScript : hex-encoded redemption script"; + throw runtime_error(msg); + } + + // Construct using pay-to-script-hash: + CScript inner = _createmultisig(params); + CScriptID innerID = inner.GetID(); + CBitcoinAddress address(innerID); + + Object result; + result.push_back(Pair("address", address.ToString())); + result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end()))); + + return result; +} + + +struct tallyitem +{ + int64 nAmount; + int nConf; + vector txids; + tallyitem() + { + nAmount = 0; + nConf = std::numeric_limits::max(); + } +}; + +Value ListReceived(const Array& params, bool fByAccounts) +{ + // Minimum confirmations + int nMinDepth = 1; + if (params.size() > 0) + nMinDepth = params[0].get_int(); + + // Whether to include empty accounts + bool fIncludeEmpty = false; + if (params.size() > 1) + fIncludeEmpty = params[1].get_bool(); + + // Tally + map mapTally; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + + if (wtx.IsCoinBase() || !wtx.IsFinal()) + continue; + + int nDepth = wtx.GetDepthInMainChain(); + if (nDepth < nMinDepth) + continue; + + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + CTxDestination address; + if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address)) + continue; + + tallyitem& item = mapTally[address]; + item.nAmount += txout.nValue; + item.nConf = min(item.nConf, nDepth); + item.txids.push_back(wtx.GetHash()); + } + } + + // Reply + Array ret; + map mapAccountTally; + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook) + { + const CBitcoinAddress& address = item.first; + const string& strAccount = item.second; + map::iterator it = mapTally.find(address); + if (it == mapTally.end() && !fIncludeEmpty) + continue; + + int64 nAmount = 0; + int nConf = std::numeric_limits::max(); + if (it != mapTally.end()) + { + nAmount = (*it).second.nAmount; + nConf = (*it).second.nConf; + } + + if (fByAccounts) + { + tallyitem& item = mapAccountTally[strAccount]; + item.nAmount += nAmount; + item.nConf = min(item.nConf, nConf); + } + else + { + Object obj; + 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::max() ? 0 : nConf))); + Array transactions; + if (it != mapTally.end()) + { + BOOST_FOREACH(const uint256& item, (*it).second.txids) + { + transactions.push_back(item.GetHex()); + } + } + obj.push_back(Pair("txids", transactions)); + ret.push_back(obj); + } + } + + if (fByAccounts) + { + for (map::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) + { + int64 nAmount = (*it).second.nAmount; + int nConf = (*it).second.nConf; + Object obj; + obj.push_back(Pair("account", (*it).first)); + obj.push_back(Pair("amount", ValueFromAmount(nAmount))); + obj.push_back(Pair("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf))); + ret.push_back(obj); + } + } + + return ret; +} + +Value listreceivedbyaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "listreceivedbyaddress [minconf=1] [includeempty=false]\n" + "[minconf] is the minimum number of confirmations before payments are included.\n" + "[includeempty] whether to include addresses that haven't received any payments.\n" + "Returns an array of objects containing:\n" + " \"address\" : receiving address\n" + " \"account\" : the account of the receiving address\n" + " \"amount\" : total amount received by the address\n" + " \"confirmations\" : number of confirmations of the most recent transaction included\n" + " \"txids\" : list of transactions with outputs to the address\n"); + + return ListReceived(params, false); +} + +Value listreceivedbyaccount(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "listreceivedbyaccount [minconf=1] [includeempty=false]\n" + "[minconf] is the minimum number of confirmations before payments are included.\n" + "[includeempty] whether to include accounts that haven't received any payments.\n" + "Returns an array of objects containing:\n" + " \"account\" : the account of the receiving addresses\n" + " \"amount\" : total amount received by addresses with this account\n" + " \"confirmations\" : number of confirmations of the most recent transaction included"); + + return ListReceived(params, true); +} + +void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret) +{ + int64 nFee; + string strSentAccount; + list > listReceived; + list > listSent; + + wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount); + + bool fAllAccounts = (strAccount == string("*")); + + // Sent + if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) + { + BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent) + { + Object entry; + entry.push_back(Pair("account", strSentAccount)); + entry.push_back(Pair("address", CBitcoinAddress(s.first).ToString())); + entry.push_back(Pair("category", "send")); + entry.push_back(Pair("amount", ValueFromAmount(-s.second))); + entry.push_back(Pair("fee", ValueFromAmount(-nFee))); + if (fLong) + WalletTxToJSON(wtx, entry); + ret.push_back(entry); + } + } + + // Received + if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) + { + BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived) + { + string account; + if (pwalletMain->mapAddressBook.count(r.first)) + account = pwalletMain->mapAddressBook[r.first]; + if (fAllAccounts || (account == strAccount)) + { + Object entry; + entry.push_back(Pair("account", account)); + entry.push_back(Pair("address", CBitcoinAddress(r.first).ToString())); + if (wtx.IsCoinBase()) + { + if (wtx.GetDepthInMainChain() < 1) + entry.push_back(Pair("category", "orphan")); + else if (wtx.GetBlocksToMaturity() > 0) + entry.push_back(Pair("category", "immature")); + else + entry.push_back(Pair("category", "generate")); + } + else + entry.push_back(Pair("category", "receive")); + entry.push_back(Pair("amount", ValueFromAmount(r.second))); + if (fLong) + WalletTxToJSON(wtx, entry); + ret.push_back(entry); + } + } + } +} + +void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret) +{ + bool fAllAccounts = (strAccount == string("*")); + + if (fAllAccounts || acentry.strAccount == strAccount) + { + Object entry; + entry.push_back(Pair("account", acentry.strAccount)); + entry.push_back(Pair("category", "move")); + entry.push_back(Pair("time", (boost::int64_t)acentry.nTime)); + entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit))); + entry.push_back(Pair("otheraccount", acentry.strOtherAccount)); + entry.push_back(Pair("comment", acentry.strComment)); + ret.push_back(entry); + } +} + +Value listtransactions(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 3) + throw runtime_error( + "listtransactions [account] [count=10] [from=0]\n" + "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account]."); + + string strAccount = "*"; + if (params.size() > 0) + strAccount = params[0].get_str(); + int nCount = 10; + if (params.size() > 1) + nCount = params[1].get_int(); + int nFrom = 0; + if (params.size() > 2) + nFrom = params[2].get_int(); + + if (nCount < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count"); + if (nFrom < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from"); + + Array ret; + + std::list acentries; + CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount); + + // iterate backwards until we have nCount items to return: + for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + if (pwtx != 0) + ListTransactions(*pwtx, strAccount, 0, true, ret); + CAccountingEntry *const pacentry = (*it).second.second; + if (pacentry != 0) + AcentryToJSON(*pacentry, strAccount, ret); + + if ((int)ret.size() >= (nCount+nFrom)) break; + } + // ret is newest to oldest + + if (nFrom > (int)ret.size()) + nFrom = ret.size(); + if ((nFrom + nCount) > (int)ret.size()) + nCount = ret.size() - nFrom; + Array::iterator first = ret.begin(); + std::advance(first, nFrom); + Array::iterator last = ret.begin(); + std::advance(last, nFrom+nCount); + + if (last != ret.end()) ret.erase(last, ret.end()); + if (first != ret.begin()) ret.erase(ret.begin(), first); + + std::reverse(ret.begin(), ret.end()); // Return oldest to newest + + return ret; +} + +Value listaccounts(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "listaccounts [minconf=1]\n" + "Returns Object that has account names as keys, account balances as values."); + + int nMinDepth = 1; + if (params.size() > 0) + nMinDepth = params[0].get_int(); + + map mapAccountBalances; + BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) { + if (IsMine(*pwalletMain, entry.first)) // This address belongs to me + mapAccountBalances[entry.second] = 0; + } + + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + int64 nFee; + string strSentAccount; + list > listReceived; + list > listSent; + wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount); + mapAccountBalances[strSentAccount] -= nFee; + BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent) + mapAccountBalances[strSentAccount] -= s.second; + if (wtx.GetDepthInMainChain() >= nMinDepth) + { + BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived) + if (pwalletMain->mapAddressBook.count(r.first)) + mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second; + else + mapAccountBalances[""] += r.second; + } + } + + list acentries; + CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries); + BOOST_FOREACH(const CAccountingEntry& entry, acentries) + mapAccountBalances[entry.strAccount] += entry.nCreditDebit; + + Object ret; + BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) { + ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); + } + return ret; +} + +Value listsinceblock(const Array& params, bool fHelp) +{ + if (fHelp) + throw runtime_error( + "listsinceblock [blockhash] [target-confirmations]\n" + "Get all transactions in blocks since block [blockhash], or all transactions if omitted"); + + CBlockIndex *pindex = NULL; + int target_confirms = 1; + + if (params.size() > 0) + { + uint256 blockId = 0; + + blockId.SetHex(params[0].get_str()); + pindex = CBlockLocator(blockId).GetBlockIndex(); + } + + if (params.size() > 1) + { + target_confirms = params[1].get_int(); + + if (target_confirms < 1) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); + } + + int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1; + + Array transactions; + + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++) + { + CWalletTx tx = (*it).second; + + if (depth == -1 || tx.GetDepthInMainChain() < depth) + ListTransactions(tx, "*", 0, true, transactions); + } + + uint256 lastblock; + + if (target_confirms == 1) + { + lastblock = hashBestChain; + } + else + { + int target_height = pindexBest->nHeight + 1 - target_confirms; + + CBlockIndex *block; + for (block = pindexBest; + block && block->nHeight > target_height; + block = block->pprev) { } + + lastblock = block ? block->GetBlockHash() : 0; + } + + Object ret; + ret.push_back(Pair("transactions", transactions)); + ret.push_back(Pair("lastblock", lastblock.GetHex())); + + return ret; +} + +Value gettransaction(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "gettransaction \n" + "Get detailed information about in-wallet transaction "); + + uint256 hash; + hash.SetHex(params[0].get_str()); + + Object entry; + if (!pwalletMain->mapWallet.count(hash)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); + const CWalletTx& wtx = pwalletMain->mapWallet[hash]; + + int64 nCredit = wtx.GetCredit(); + int64 nDebit = wtx.GetDebit(); + int64 nNet = nCredit - nDebit; + int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0); + + entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); + if (wtx.IsFromMe()) + entry.push_back(Pair("fee", ValueFromAmount(nFee))); + + WalletTxToJSON(wtx, entry); + + Array details; + ListTransactions(wtx, "*", 0, false, details); + entry.push_back(Pair("details", details)); + + return entry; +} + + +Value backupwallet(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "backupwallet \n" + "Safely copies wallet.dat to destination, which can be a directory or a path with filename."); + + string strDest = params[0].get_str(); + if (!BackupWallet(*pwalletMain, strDest)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!"); + + return Value::null; +} + + +Value keypoolrefill(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "keypoolrefill\n" + "Fills the keypool." + + HelpRequiringPassphrase()); + + EnsureWalletIsUnlocked(); + + pwalletMain->TopUpKeyPool(); + + if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool."); + + return Value::null; +} + + +void ThreadTopUpKeyPool(void* parg) +{ + // Make this thread recognisable as the key-topping-up thread + RenameThread("bitcoin-key-top"); + + pwalletMain->TopUpKeyPool(); +} + +void ThreadCleanWalletPassphrase(void* parg) +{ + // Make this thread recognisable as the wallet relocking thread + RenameThread("bitcoin-lock-wa"); + + int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000; + + ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime); + + if (nWalletUnlockTime == 0) + { + nWalletUnlockTime = nMyWakeTime; + + do + { + if (nWalletUnlockTime==0) + break; + int64 nToSleep = nWalletUnlockTime - GetTimeMillis(); + if (nToSleep <= 0) + break; + + LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime); + MilliSleep(nToSleep); + ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime); + + } while(1); + + if (nWalletUnlockTime) + { + nWalletUnlockTime = 0; + pwalletMain->Lock(); + } + } + else + { + if (nWalletUnlockTime < nMyWakeTime) + nWalletUnlockTime = nMyWakeTime; + } + + LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime); + + delete (int64*)parg; +} + +Value walletpassphrase(const Array& params, bool fHelp) +{ + if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) + throw runtime_error( + "walletpassphrase \n" + "Stores the wallet decryption key in memory for seconds."); + if (fHelp) + return true; + if (!pwalletMain->IsCrypted()) + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called."); + + if (!pwalletMain->IsLocked()) + throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked."); + + // Note that the walletpassphrase is stored in params[0] which is not mlock()ed + SecureString strWalletPass; + strWalletPass.reserve(100); + // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) + // Alternately, find a way to make params[0] mlock()'d to begin with. + strWalletPass = params[0].get_str().c_str(); + + if (strWalletPass.length() > 0) + { + if (!pwalletMain->Unlock(strWalletPass)) + throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); + } + else + throw runtime_error( + "walletpassphrase \n" + "Stores the wallet decryption key in memory for seconds."); + + NewThread(ThreadTopUpKeyPool, NULL); + int64* pnSleepTime = new int64(params[1].get_int64()); + NewThread(ThreadCleanWalletPassphrase, pnSleepTime); + + return Value::null; +} + + +Value walletpassphrasechange(const Array& params, bool fHelp) +{ + if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) + throw runtime_error( + "walletpassphrasechange \n" + "Changes the wallet passphrase from to ."); + if (fHelp) + return true; + if (!pwalletMain->IsCrypted()) + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called."); + + // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string) + // Alternately, find a way to make params[0] mlock()'d to begin with. + SecureString strOldWalletPass; + strOldWalletPass.reserve(100); + strOldWalletPass = params[0].get_str().c_str(); + + SecureString strNewWalletPass; + strNewWalletPass.reserve(100); + strNewWalletPass = params[1].get_str().c_str(); + + if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1) + throw runtime_error( + "walletpassphrasechange \n" + "Changes the wallet passphrase from to ."); + + if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) + throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); + + return Value::null; +} + + +Value walletlock(const Array& params, bool fHelp) +{ + if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0)) + throw runtime_error( + "walletlock\n" + "Removes the wallet encryption key from memory, locking the wallet.\n" + "After calling this method, you will need to call walletpassphrase again\n" + "before being able to call any methods which require the wallet to be unlocked."); + if (fHelp) + return true; + if (!pwalletMain->IsCrypted()) + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called."); + + { + LOCK(cs_nWalletUnlockTime); + pwalletMain->Lock(); + nWalletUnlockTime = 0; + } + + return Value::null; +} + + +Value encryptwallet(const Array& params, bool fHelp) +{ + if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1)) + throw runtime_error( + "encryptwallet \n" + "Encrypts the wallet with ."); + if (fHelp) + return true; + if (pwalletMain->IsCrypted()) + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called."); + + // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) + // Alternately, find a way to make params[0] mlock()'d to begin with. + SecureString strWalletPass; + strWalletPass.reserve(100); + strWalletPass = params[0].get_str().c_str(); + + if (strWalletPass.length() < 1) + throw runtime_error( + "encryptwallet \n" + "Encrypts the wallet with ."); + + if (!pwalletMain->EncryptWallet(strWalletPass)) + throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet."); + + // BDB seems to have a bad habit of writing old data into + // slack space in .dat files; that is bad if the old data is + // unencrypted private keys. So: + StartShutdown(); + return "wallet encrypted; CryptoLottoToken server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup."; +} + +class DescribeAddressVisitor : public boost::static_visitor +{ +public: + Object operator()(const CNoDestination &dest) const { return Object(); } + + Object operator()(const CKeyID &keyID) const { + Object obj; + CPubKey vchPubKey; + pwalletMain->GetPubKey(keyID, vchPubKey); + obj.push_back(Pair("isscript", false)); + obj.push_back(Pair("pubkey", HexStr(vchPubKey))); + obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); + return obj; + } + + Object operator()(const CScriptID &scriptID) const { + Object obj; + obj.push_back(Pair("isscript", true)); + CScript subscript; + pwalletMain->GetCScript(scriptID, subscript); + std::vector addresses; + txnouttype whichType; + int nRequired; + ExtractDestinations(subscript, whichType, addresses, nRequired); + obj.push_back(Pair("script", GetTxnOutputType(whichType))); + Array a; + BOOST_FOREACH(const CTxDestination& addr, addresses) + a.push_back(CBitcoinAddress(addr).ToString()); + obj.push_back(Pair("addresses", a)); + if (whichType == TX_MULTISIG) + obj.push_back(Pair("sigsrequired", nRequired)); + return obj; + } +}; + +Value validateaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "validateaddress \n" + "Return information about ."); + + CBitcoinAddress address(params[0].get_str()); + bool isValid = address.IsValid(); + + Object ret; + ret.push_back(Pair("isvalid", isValid)); + if (isValid) + { + CTxDestination dest = address.Get(); + string currentAddress = address.ToString(); + ret.push_back(Pair("address", currentAddress)); + bool fMine = pwalletMain ? IsMine(*pwalletMain, dest) : false; + ret.push_back(Pair("ismine", fMine)); + if (fMine) { + Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest); + ret.insert(ret.end(), detail.begin(), detail.end()); + } + if (pwalletMain && pwalletMain->mapAddressBook.count(dest)) + ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest])); + } + return ret; +} + +Value lockunspent(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "lockunspent unlock? [array-of-Objects]\n" + "Updates list of temporarily unspendable outputs."); + + if (params.size() == 1) + RPCTypeCheck(params, list_of(bool_type)); + else + RPCTypeCheck(params, list_of(bool_type)(array_type)); + + bool fUnlock = params[0].get_bool(); + + if (params.size() == 1) { + if (fUnlock) + pwalletMain->UnlockAllCoins(); + return true; + } + + Array outputs = params[1].get_array(); + BOOST_FOREACH(Value& output, outputs) + { + if (output.type() != obj_type) + throw JSONRPCError(-8, "Invalid parameter, expected object"); + const Object& o = output.get_obj(); + + RPCTypeCheck(o, map_list_of("txid", str_type)("vout", int_type)); + + string txid = find_value(o, "txid").get_str(); + if (!IsHex(txid)) + throw JSONRPCError(-8, "Invalid parameter, expected hex txid"); + + int nOutput = find_value(o, "vout").get_int(); + if (nOutput < 0) + throw JSONRPCError(-8, "Invalid parameter, vout must be positive"); + + COutPoint outpt(uint256(txid), nOutput); + + if (fUnlock) + pwalletMain->UnlockCoin(outpt); + else + pwalletMain->LockCoin(outpt); + } + + return true; +} + +Value listlockunspent(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "listlockunspent\n" + "Returns list of temporarily unspendable outputs."); + + vector vOutpts; + pwalletMain->ListLockedCoins(vOutpts); + + Array ret; + + BOOST_FOREACH(COutPoint &outpt, vOutpts) { + Object o; + + o.push_back(Pair("txid", outpt.hash.GetHex())); + o.push_back(Pair("vout", (int)outpt.n)); + ret.push_back(o); + } + + return ret; +} + diff --git a/src/script.cpp b/src/script.cpp new file mode 100644 index 0000000..b411666 --- /dev/null +++ b/src/script.cpp @@ -0,0 +1,1902 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include +#include + +using namespace std; +using namespace boost; + +#include "script.h" +#include "keystore.h" +#include "bignum.h" +#include "key.h" +#include "main.h" +#include "sync.h" +#include "util.h" + +bool CheckSig(vector vchSig, const vector &vchPubKey, const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, int flags); + + + +typedef vector valtype; +static const valtype vchFalse(0); +static const valtype vchZero(0); +static const valtype vchTrue(1, 1); +static const CBigNum bnZero(0); +static const CBigNum bnOne(1); +static const CBigNum bnFalse(0); +static const CBigNum bnTrue(1); +static const size_t nMaxNumSize = 4; + + +CBigNum CastToBigNum(const valtype& vch) +{ + if (vch.size() > nMaxNumSize) + throw runtime_error("CastToBigNum() : overflow"); + // Get rid of extra leading zeros + return CBigNum(CBigNum(vch).getvch()); +} + +bool CastToBool(const valtype& vch) +{ + for (unsigned int i = 0; i < vch.size(); i++) + { + if (vch[i] != 0) + { + // Can be negative zero + if (i == vch.size()-1 && vch[i] == 0x80) + return false; + return true; + } + } + return false; +} + + + +// +// Script is a stack machine (like Forth) that evaluates a predicate +// returning a bool indicating valid or not. There are no loops. +// +#define stacktop(i) (stack.at(stack.size()+(i))) +#define altstacktop(i) (altstack.at(altstack.size()+(i))) +static inline void popstack(vector& stack) +{ + if (stack.empty()) + throw runtime_error("popstack() : stack empty"); + stack.pop_back(); +} + + +const char* GetTxnOutputType(txnouttype t) +{ + switch (t) + { + case TX_NONSTANDARD: return "nonstandard"; + case TX_PUBKEY: return "pubkey"; + case TX_PUBKEYHASH: return "pubkeyhash"; + case TX_SCRIPTHASH: return "scripthash"; + case TX_MULTISIG: return "multisig"; + } + return NULL; +} + + +const char* GetOpName(opcodetype opcode) +{ + switch (opcode) + { + // push value + case OP_0 : return "0"; + case OP_PUSHDATA1 : return "OP_PUSHDATA1"; + case OP_PUSHDATA2 : return "OP_PUSHDATA2"; + case OP_PUSHDATA4 : return "OP_PUSHDATA4"; + case OP_1NEGATE : return "-1"; + case OP_RESERVED : return "OP_RESERVED"; + case OP_1 : return "1"; + case OP_2 : return "2"; + case OP_3 : return "3"; + case OP_4 : return "4"; + case OP_5 : return "5"; + case OP_6 : return "6"; + case OP_7 : return "7"; + case OP_8 : return "8"; + case OP_9 : return "9"; + case OP_10 : return "10"; + case OP_11 : return "11"; + case OP_12 : return "12"; + case OP_13 : return "13"; + case OP_14 : return "14"; + case OP_15 : return "15"; + case OP_16 : return "16"; + + // control + case OP_NOP : return "OP_NOP"; + case OP_VER : return "OP_VER"; + case OP_IF : return "OP_IF"; + case OP_NOTIF : return "OP_NOTIF"; + case OP_VERIF : return "OP_VERIF"; + case OP_VERNOTIF : return "OP_VERNOTIF"; + case OP_ELSE : return "OP_ELSE"; + case OP_ENDIF : return "OP_ENDIF"; + case OP_VERIFY : return "OP_VERIFY"; + case OP_RETURN : return "OP_RETURN"; + + // stack ops + case OP_TOALTSTACK : return "OP_TOALTSTACK"; + case OP_FROMALTSTACK : return "OP_FROMALTSTACK"; + case OP_2DROP : return "OP_2DROP"; + case OP_2DUP : return "OP_2DUP"; + case OP_3DUP : return "OP_3DUP"; + case OP_2OVER : return "OP_2OVER"; + case OP_2ROT : return "OP_2ROT"; + case OP_2SWAP : return "OP_2SWAP"; + case OP_IFDUP : return "OP_IFDUP"; + case OP_DEPTH : return "OP_DEPTH"; + case OP_DROP : return "OP_DROP"; + case OP_DUP : return "OP_DUP"; + case OP_NIP : return "OP_NIP"; + case OP_OVER : return "OP_OVER"; + case OP_PICK : return "OP_PICK"; + case OP_ROLL : return "OP_ROLL"; + case OP_ROT : return "OP_ROT"; + case OP_SWAP : return "OP_SWAP"; + case OP_TUCK : return "OP_TUCK"; + + // splice ops + case OP_CAT : return "OP_CAT"; + case OP_SUBSTR : return "OP_SUBSTR"; + case OP_LEFT : return "OP_LEFT"; + case OP_RIGHT : return "OP_RIGHT"; + case OP_SIZE : return "OP_SIZE"; + + // bit logic + case OP_INVERT : return "OP_INVERT"; + case OP_AND : return "OP_AND"; + case OP_OR : return "OP_OR"; + case OP_XOR : return "OP_XOR"; + case OP_EQUAL : return "OP_EQUAL"; + case OP_EQUALVERIFY : return "OP_EQUALVERIFY"; + case OP_RESERVED1 : return "OP_RESERVED1"; + case OP_RESERVED2 : return "OP_RESERVED2"; + + // numeric + case OP_1ADD : return "OP_1ADD"; + case OP_1SUB : return "OP_1SUB"; + case OP_2MUL : return "OP_2MUL"; + case OP_2DIV : return "OP_2DIV"; + case OP_NEGATE : return "OP_NEGATE"; + case OP_ABS : return "OP_ABS"; + case OP_NOT : return "OP_NOT"; + case OP_0NOTEQUAL : return "OP_0NOTEQUAL"; + case OP_ADD : return "OP_ADD"; + case OP_SUB : return "OP_SUB"; + case OP_MUL : return "OP_MUL"; + case OP_DIV : return "OP_DIV"; + case OP_MOD : return "OP_MOD"; + case OP_LSHIFT : return "OP_LSHIFT"; + case OP_RSHIFT : return "OP_RSHIFT"; + case OP_BOOLAND : return "OP_BOOLAND"; + case OP_BOOLOR : return "OP_BOOLOR"; + case OP_NUMEQUAL : return "OP_NUMEQUAL"; + case OP_NUMEQUALVERIFY : return "OP_NUMEQUALVERIFY"; + case OP_NUMNOTEQUAL : return "OP_NUMNOTEQUAL"; + case OP_LESSTHAN : return "OP_LESSTHAN"; + case OP_GREATERTHAN : return "OP_GREATERTHAN"; + case OP_LESSTHANOREQUAL : return "OP_LESSTHANOREQUAL"; + case OP_GREATERTHANOREQUAL : return "OP_GREATERTHANOREQUAL"; + case OP_MIN : return "OP_MIN"; + case OP_MAX : return "OP_MAX"; + case OP_WITHIN : return "OP_WITHIN"; + + // crypto + case OP_RIPEMD160 : return "OP_RIPEMD160"; + case OP_SHA1 : return "OP_SHA1"; + case OP_SHA256 : return "OP_SHA256"; + case OP_HASH160 : return "OP_HASH160"; + case OP_HASH256 : return "OP_HASH256"; + case OP_CODESEPARATOR : return "OP_CODESEPARATOR"; + case OP_CHECKSIG : return "OP_CHECKSIG"; + case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY"; + case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG"; + case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY"; + + // expanson + case OP_NOP1 : return "OP_NOP1"; + case OP_NOP2 : return "OP_NOP2"; + case OP_NOP3 : return "OP_NOP3"; + case OP_NOP4 : return "OP_NOP4"; + case OP_NOP5 : return "OP_NOP5"; + case OP_NOP6 : return "OP_NOP6"; + case OP_NOP7 : return "OP_NOP7"; + case OP_NOP8 : return "OP_NOP8"; + case OP_NOP9 : return "OP_NOP9"; + case OP_NOP10 : return "OP_NOP10"; + + + + // template matching params + case OP_PUBKEYHASH : return "OP_PUBKEYHASH"; + case OP_PUBKEY : return "OP_PUBKEY"; + + case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; + default: + return "OP_UNKNOWN"; + } +} + +bool IsCanonicalPubKey(const valtype &vchPubKey) { + if (vchPubKey.size() < 33) + return error("Non-canonical public key: too short"); + if (vchPubKey[0] == 0x04) { + if (vchPubKey.size() != 65) + return error("Non-canonical public key: invalid length for uncompressed key"); + } else if (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03) { + if (vchPubKey.size() != 33) + return error("Non-canonical public key: invalid length for compressed key"); + } else { + return error("Non-canonical public key: compressed nor uncompressed"); + } + return true; +} + +bool IsCanonicalSignature(const valtype &vchSig) { + // See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 + // A canonical signature exists of: <30> <02> <02> + // Where R and S are not negative (their first byte has its highest bit not set), and not + // excessively padded (do not start with a 0 byte, unless an otherwise negative number follows, + // in which case a single 0 byte is necessary and even required). + if (vchSig.size() < 9) + return error("Non-canonical signature: too short"); + if (vchSig.size() > 73) + return error("Non-canonical signature: too long"); + unsigned char nHashType = vchSig[vchSig.size() - 1] & (~(SIGHASH_ANYONECANPAY)); + if (nHashType < SIGHASH_ALL || nHashType > SIGHASH_SINGLE) + return error("Non-canonical signature: unknown hashtype byte"); + if (vchSig[0] != 0x30) + return error("Non-canonical signature: wrong type"); + if (vchSig[1] != vchSig.size()-3) + return error("Non-canonical signature: wrong length marker"); + unsigned int nLenR = vchSig[3]; + if (5 + nLenR >= vchSig.size()) + return error("Non-canonical signature: S length misplaced"); + unsigned int nLenS = vchSig[5+nLenR]; + if ((unsigned long)(nLenR+nLenS+7) != vchSig.size()) + return error("Non-canonical signature: R+S length mismatch"); + + const unsigned char *R = &vchSig[4]; + if (R[-2] != 0x02) + return error("Non-canonical signature: R value type mismatch"); + if (nLenR == 0) + return error("Non-canonical signature: R length is zero"); + if (R[0] & 0x80) + return error("Non-canonical signature: R value negative"); + if (nLenR > 1 && (R[0] == 0x00) && !(R[1] & 0x80)) + return error("Non-canonical signature: R value excessively padded"); + + const unsigned char *S = &vchSig[6+nLenR]; + if (S[-2] != 0x02) + return error("Non-canonical signature: S value type mismatch"); + if (nLenS == 0) + return error("Non-canonical signature: S length is zero"); + if (S[0] & 0x80) + return error("Non-canonical signature: S value negative"); + if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80)) + return error("Non-canonical signature: S value excessively padded"); + + return true; +} + +bool EvalScript(vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType) +{ + CAutoBN_CTX pctx; + CScript::const_iterator pc = script.begin(); + CScript::const_iterator pend = script.end(); + CScript::const_iterator pbegincodehash = script.begin(); + opcodetype opcode; + valtype vchPushValue; + vector vfExec; + vector altstack; + if (script.size() > 10000) + return false; + int nOpCount = 0; + bool fStrictEncodings = flags & SCRIPT_VERIFY_STRICTENC; + + try + { + while (pc < pend) + { + bool fExec = !count(vfExec.begin(), vfExec.end(), false); + + // + // Read instruction + // + if (!script.GetOp(pc, opcode, vchPushValue)) + return false; + if (vchPushValue.size() > MAX_SCRIPT_ELEMENT_SIZE) + return false; + if (opcode > OP_16 && ++nOpCount > 201) + return false; + + if (opcode == OP_CAT || + opcode == OP_SUBSTR || + opcode == OP_LEFT || + opcode == OP_RIGHT || + opcode == OP_INVERT || + opcode == OP_AND || + opcode == OP_OR || + opcode == OP_XOR || + opcode == OP_2MUL || + opcode == OP_2DIV || + opcode == OP_MUL || + opcode == OP_DIV || + opcode == OP_MOD || + opcode == OP_LSHIFT || + opcode == OP_RSHIFT) + return false; // Disabled opcodes. + + if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) + stack.push_back(vchPushValue); + else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF)) + switch (opcode) + { + // + // Push value + // + case OP_1NEGATE: + case OP_1: + case OP_2: + case OP_3: + case OP_4: + case OP_5: + case OP_6: + case OP_7: + case OP_8: + case OP_9: + case OP_10: + case OP_11: + case OP_12: + case OP_13: + case OP_14: + case OP_15: + case OP_16: + { + // ( -- value) + CBigNum bn((int)opcode - (int)(OP_1 - 1)); + stack.push_back(bn.getvch()); + } + break; + + + // + // Control + // + case OP_NOP: + case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5: + case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10: + break; + + case OP_IF: + case OP_NOTIF: + { + // if [statements] [else [statements]] endif + bool fValue = false; + if (fExec) + { + if (stack.size() < 1) + return false; + valtype& vch = stacktop(-1); + fValue = CastToBool(vch); + if (opcode == OP_NOTIF) + fValue = !fValue; + popstack(stack); + } + vfExec.push_back(fValue); + } + break; + + case OP_ELSE: + { + if (vfExec.empty()) + return false; + vfExec.back() = !vfExec.back(); + } + break; + + case OP_ENDIF: + { + if (vfExec.empty()) + return false; + vfExec.pop_back(); + } + break; + + case OP_VERIFY: + { + // (true -- ) or + // (false -- false) and return + if (stack.size() < 1) + return false; + bool fValue = CastToBool(stacktop(-1)); + if (fValue) + popstack(stack); + else + return false; + } + break; + + case OP_RETURN: + { + return false; + } + break; + + + // + // Stack ops + // + case OP_TOALTSTACK: + { + if (stack.size() < 1) + return false; + altstack.push_back(stacktop(-1)); + popstack(stack); + } + break; + + case OP_FROMALTSTACK: + { + if (altstack.size() < 1) + return false; + stack.push_back(altstacktop(-1)); + popstack(altstack); + } + break; + + case OP_2DROP: + { + // (x1 x2 -- ) + if (stack.size() < 2) + return false; + popstack(stack); + popstack(stack); + } + break; + + case OP_2DUP: + { + // (x1 x2 -- x1 x2 x1 x2) + if (stack.size() < 2) + return false; + valtype vch1 = stacktop(-2); + valtype vch2 = stacktop(-1); + stack.push_back(vch1); + stack.push_back(vch2); + } + break; + + case OP_3DUP: + { + // (x1 x2 x3 -- x1 x2 x3 x1 x2 x3) + if (stack.size() < 3) + return false; + valtype vch1 = stacktop(-3); + valtype vch2 = stacktop(-2); + valtype vch3 = stacktop(-1); + stack.push_back(vch1); + stack.push_back(vch2); + stack.push_back(vch3); + } + break; + + case OP_2OVER: + { + // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2) + if (stack.size() < 4) + return false; + valtype vch1 = stacktop(-4); + valtype vch2 = stacktop(-3); + stack.push_back(vch1); + stack.push_back(vch2); + } + break; + + case OP_2ROT: + { + // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2) + if (stack.size() < 6) + return false; + valtype vch1 = stacktop(-6); + valtype vch2 = stacktop(-5); + stack.erase(stack.end()-6, stack.end()-4); + stack.push_back(vch1); + stack.push_back(vch2); + } + break; + + case OP_2SWAP: + { + // (x1 x2 x3 x4 -- x3 x4 x1 x2) + if (stack.size() < 4) + return false; + swap(stacktop(-4), stacktop(-2)); + swap(stacktop(-3), stacktop(-1)); + } + break; + + case OP_IFDUP: + { + // (x - 0 | x x) + if (stack.size() < 1) + return false; + valtype vch = stacktop(-1); + if (CastToBool(vch)) + stack.push_back(vch); + } + break; + + case OP_DEPTH: + { + // -- stacksize + CBigNum bn(stack.size()); + stack.push_back(bn.getvch()); + } + break; + + case OP_DROP: + { + // (x -- ) + if (stack.size() < 1) + return false; + popstack(stack); + } + break; + + case OP_DUP: + { + // (x -- x x) + if (stack.size() < 1) + return false; + valtype vch = stacktop(-1); + stack.push_back(vch); + } + break; + + case OP_NIP: + { + // (x1 x2 -- x2) + if (stack.size() < 2) + return false; + stack.erase(stack.end() - 2); + } + break; + + case OP_OVER: + { + // (x1 x2 -- x1 x2 x1) + if (stack.size() < 2) + return false; + valtype vch = stacktop(-2); + stack.push_back(vch); + } + break; + + case OP_PICK: + case OP_ROLL: + { + // (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn) + // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn) + if (stack.size() < 2) + return false; + int n = CastToBigNum(stacktop(-1)).getint(); + popstack(stack); + if (n < 0 || n >= (int)stack.size()) + return false; + valtype vch = stacktop(-n-1); + if (opcode == OP_ROLL) + stack.erase(stack.end()-n-1); + stack.push_back(vch); + } + break; + + case OP_ROT: + { + // (x1 x2 x3 -- x2 x3 x1) + // x2 x1 x3 after first swap + // x2 x3 x1 after second swap + if (stack.size() < 3) + return false; + swap(stacktop(-3), stacktop(-2)); + swap(stacktop(-2), stacktop(-1)); + } + break; + + case OP_SWAP: + { + // (x1 x2 -- x2 x1) + if (stack.size() < 2) + return false; + swap(stacktop(-2), stacktop(-1)); + } + break; + + case OP_TUCK: + { + // (x1 x2 -- x2 x1 x2) + if (stack.size() < 2) + return false; + valtype vch = stacktop(-1); + stack.insert(stack.end()-2, vch); + } + break; + + + case OP_SIZE: + { + // (in -- in size) + if (stack.size() < 1) + return false; + CBigNum bn(stacktop(-1).size()); + stack.push_back(bn.getvch()); + } + break; + + + // + // Bitwise logic + // + case OP_EQUAL: + case OP_EQUALVERIFY: + //case OP_NOTEQUAL: // use OP_NUMNOTEQUAL + { + // (x1 x2 - bool) + if (stack.size() < 2) + return false; + valtype& vch1 = stacktop(-2); + valtype& vch2 = stacktop(-1); + bool fEqual = (vch1 == vch2); + // OP_NOTEQUAL is disabled because it would be too easy to say + // something like n != 1 and have some wiseguy pass in 1 with extra + // zero bytes after it (numerically, 0x01 == 0x0001 == 0x000001) + //if (opcode == OP_NOTEQUAL) + // fEqual = !fEqual; + popstack(stack); + popstack(stack); + stack.push_back(fEqual ? vchTrue : vchFalse); + if (opcode == OP_EQUALVERIFY) + { + if (fEqual) + popstack(stack); + else + return false; + } + } + break; + + + // + // Numeric + // + case OP_1ADD: + case OP_1SUB: + case OP_NEGATE: + case OP_ABS: + case OP_NOT: + case OP_0NOTEQUAL: + { + // (in -- out) + if (stack.size() < 1) + return false; + CBigNum bn = CastToBigNum(stacktop(-1)); + switch (opcode) + { + case OP_1ADD: bn += bnOne; break; + case OP_1SUB: bn -= bnOne; break; + case OP_NEGATE: bn = -bn; break; + case OP_ABS: if (bn < bnZero) bn = -bn; break; + case OP_NOT: bn = (bn == bnZero); break; + case OP_0NOTEQUAL: bn = (bn != bnZero); break; + default: assert(!"invalid opcode"); break; + } + popstack(stack); + stack.push_back(bn.getvch()); + } + break; + + case OP_ADD: + case OP_SUB: + case OP_BOOLAND: + case OP_BOOLOR: + case OP_NUMEQUAL: + case OP_NUMEQUALVERIFY: + case OP_NUMNOTEQUAL: + case OP_LESSTHAN: + case OP_GREATERTHAN: + case OP_LESSTHANOREQUAL: + case OP_GREATERTHANOREQUAL: + case OP_MIN: + case OP_MAX: + { + // (x1 x2 -- out) + if (stack.size() < 2) + return false; + CBigNum bn1 = CastToBigNum(stacktop(-2)); + CBigNum bn2 = CastToBigNum(stacktop(-1)); + CBigNum bn; + switch (opcode) + { + case OP_ADD: + bn = bn1 + bn2; + break; + + case OP_SUB: + bn = bn1 - bn2; + break; + + case OP_BOOLAND: bn = (bn1 != bnZero && bn2 != bnZero); break; + case OP_BOOLOR: bn = (bn1 != bnZero || bn2 != bnZero); break; + case OP_NUMEQUAL: bn = (bn1 == bn2); break; + case OP_NUMEQUALVERIFY: bn = (bn1 == bn2); break; + case OP_NUMNOTEQUAL: bn = (bn1 != bn2); break; + case OP_LESSTHAN: bn = (bn1 < bn2); break; + case OP_GREATERTHAN: bn = (bn1 > bn2); break; + case OP_LESSTHANOREQUAL: bn = (bn1 <= bn2); break; + case OP_GREATERTHANOREQUAL: bn = (bn1 >= bn2); break; + case OP_MIN: bn = (bn1 < bn2 ? bn1 : bn2); break; + case OP_MAX: bn = (bn1 > bn2 ? bn1 : bn2); break; + default: assert(!"invalid opcode"); break; + } + popstack(stack); + popstack(stack); + stack.push_back(bn.getvch()); + + if (opcode == OP_NUMEQUALVERIFY) + { + if (CastToBool(stacktop(-1))) + popstack(stack); + else + return false; + } + } + break; + + case OP_WITHIN: + { + // (x min max -- out) + if (stack.size() < 3) + return false; + CBigNum bn1 = CastToBigNum(stacktop(-3)); + CBigNum bn2 = CastToBigNum(stacktop(-2)); + CBigNum bn3 = CastToBigNum(stacktop(-1)); + bool fValue = (bn2 <= bn1 && bn1 < bn3); + popstack(stack); + popstack(stack); + popstack(stack); + stack.push_back(fValue ? vchTrue : vchFalse); + } + break; + + + // + // Crypto + // + case OP_RIPEMD160: + case OP_SHA1: + case OP_SHA256: + case OP_HASH160: + case OP_HASH256: + { + // (in -- hash) + if (stack.size() < 1) + return false; + valtype& vch = stacktop(-1); + valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32); + if (opcode == OP_RIPEMD160) + RIPEMD160(&vch[0], vch.size(), &vchHash[0]); + else if (opcode == OP_SHA1) + SHA1(&vch[0], vch.size(), &vchHash[0]); + else if (opcode == OP_SHA256) + SHA256(&vch[0], vch.size(), &vchHash[0]); + else if (opcode == OP_HASH160) + { + uint160 hash160 = Hash160(vch); + memcpy(&vchHash[0], &hash160, sizeof(hash160)); + } + else if (opcode == OP_HASH256) + { + uint256 hash = Hash(vch.begin(), vch.end()); + memcpy(&vchHash[0], &hash, sizeof(hash)); + } + popstack(stack); + stack.push_back(vchHash); + } + break; + + case OP_CODESEPARATOR: + { + // Hash starts after the code separator + pbegincodehash = pc; + } + break; + + case OP_CHECKSIG: + case OP_CHECKSIGVERIFY: + { + // (sig pubkey -- bool) + if (stack.size() < 2) + return false; + + valtype& vchSig = stacktop(-2); + valtype& vchPubKey = stacktop(-1); + + ////// debug print + //PrintHex(vchSig.begin(), vchSig.end(), "sig: %s\n"); + //PrintHex(vchPubKey.begin(), vchPubKey.end(), "pubkey: %s\n"); + + // Subset of script starting at the most recent codeseparator + CScript scriptCode(pbegincodehash, pend); + + // Drop the signature, since there's no way for a signature to sign itself + scriptCode.FindAndDelete(CScript(vchSig)); + + bool fSuccess = (!fStrictEncodings || (IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey))); + if (fSuccess) + fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags); + + popstack(stack); + popstack(stack); + stack.push_back(fSuccess ? vchTrue : vchFalse); + if (opcode == OP_CHECKSIGVERIFY) + { + if (fSuccess) + popstack(stack); + else + return false; + } + } + break; + + case OP_CHECKMULTISIG: + case OP_CHECKMULTISIGVERIFY: + { + // ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool) + + int i = 1; + if ((int)stack.size() < i) + return false; + + int nKeysCount = CastToBigNum(stacktop(-i)).getint(); + if (nKeysCount < 0 || nKeysCount > 20) + return false; + nOpCount += nKeysCount; + if (nOpCount > 201) + return false; + int ikey = ++i; + i += nKeysCount; + if ((int)stack.size() < i) + return false; + + int nSigsCount = CastToBigNum(stacktop(-i)).getint(); + if (nSigsCount < 0 || nSigsCount > nKeysCount) + return false; + int isig = ++i; + i += nSigsCount; + if ((int)stack.size() < i) + return false; + + // Subset of script starting at the most recent codeseparator + CScript scriptCode(pbegincodehash, pend); + + // Drop the signatures, since there's no way for a signature to sign itself + for (int k = 0; k < nSigsCount; k++) + { + valtype& vchSig = stacktop(-isig-k); + scriptCode.FindAndDelete(CScript(vchSig)); + } + + bool fSuccess = true; + while (fSuccess && nSigsCount > 0) + { + valtype& vchSig = stacktop(-isig); + valtype& vchPubKey = stacktop(-ikey); + + // Check signature + bool fOk = (!fStrictEncodings || (IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey))); + if (fOk) + fOk = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags); + + if (fOk) { + isig++; + nSigsCount--; + } + ikey++; + nKeysCount--; + + // If there are more signatures left than keys left, + // then too many signatures have failed + if (nSigsCount > nKeysCount) + fSuccess = false; + } + + while (i-- > 0) + popstack(stack); + stack.push_back(fSuccess ? vchTrue : vchFalse); + + if (opcode == OP_CHECKMULTISIGVERIFY) + { + if (fSuccess) + popstack(stack); + else + return false; + } + } + break; + + default: + return false; + } + + // Size limits + if (stack.size() + altstack.size() > 1000) + return false; + } + } + catch (...) + { + return false; + } + + + if (!vfExec.empty()) + return false; + + return true; +} + + + + + + + + + +uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) +{ + if (nIn >= txTo.vin.size()) + { + printf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); + return 1; + } + CTransaction txTmp(txTo); + + // In case concatenating two scripts ends up with two codeseparators, + // or an extra one at the end, this prevents all those possible incompatibilities. + scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR)); + + // Blank out other inputs' signatures + for (unsigned int i = 0; i < txTmp.vin.size(); i++) + txTmp.vin[i].scriptSig = CScript(); + txTmp.vin[nIn].scriptSig = scriptCode; + + // Blank out some of the outputs + if ((nHashType & 0x1f) == SIGHASH_NONE) + { + // Wildcard payee + txTmp.vout.clear(); + + // Let the others update at will + for (unsigned int i = 0; i < txTmp.vin.size(); i++) + if (i != nIn) + txTmp.vin[i].nSequence = 0; + } + else if ((nHashType & 0x1f) == SIGHASH_SINGLE) + { + // Only lock-in the txout payee at same index as txin + unsigned int nOut = nIn; + if (nOut >= txTmp.vout.size()) + { + printf("ERROR: SignatureHash() : nOut=%d out of range\n", nOut); + return 1; + } + txTmp.vout.resize(nOut+1); + for (unsigned int i = 0; i < nOut; i++) + txTmp.vout[i].SetNull(); + + // Let the others update at will + for (unsigned int i = 0; i < txTmp.vin.size(); i++) + if (i != nIn) + txTmp.vin[i].nSequence = 0; + } + + // Blank out other inputs completely, not recommended for open transactions + if (nHashType & SIGHASH_ANYONECANPAY) + { + txTmp.vin[0] = txTmp.vin[nIn]; + txTmp.vin.resize(1); + } + + // Serialize and hash + CHashWriter ss(SER_GETHASH, 0); + ss << txTmp << nHashType; + return ss.GetHash(); +} + + +// Valid signature cache, to avoid doing expensive ECDSA signature checking +// twice for every transaction (once when accepted into memory pool, and +// again when accepted into the block chain) + +class CSignatureCache +{ +private: + // sigdata_type is (signature hash, signature, public key): + typedef boost::tuple, CPubKey> sigdata_type; + std::set< sigdata_type> setValid; + boost::shared_mutex cs_sigcache; + +public: + bool + Get(const uint256 &hash, const std::vector& vchSig, const CPubKey& pubKey) + { + boost::shared_lock lock(cs_sigcache); + + sigdata_type k(hash, vchSig, pubKey); + std::set::iterator mi = setValid.find(k); + if (mi != setValid.end()) + return true; + return false; + } + + void Set(const uint256 &hash, const std::vector& vchSig, const CPubKey& pubKey) + { + // DoS prevention: limit cache size to less than 10MB + // (~200 bytes per cache entry times 50,000 entries) + // Since there are a maximum of 20,000 signature operations per block + // 50,000 is a reasonable default. + int64 nMaxCacheSize = GetArg("-maxsigcachesize", 50000); + if (nMaxCacheSize <= 0) return; + + boost::unique_lock lock(cs_sigcache); + + while (static_cast(setValid.size()) > nMaxCacheSize) + { + // Evict a random entry. Random because that helps + // foil would-be DoS attackers who might try to pre-generate + // and re-use a set of valid signatures just-slightly-greater + // than our cache size. + uint256 randomHash = GetRandHash(); + std::vector unused; + std::set::iterator it = + setValid.lower_bound(sigdata_type(randomHash, unused, unused)); + if (it == setValid.end()) + it = setValid.begin(); + setValid.erase(*it); + } + + sigdata_type k(hash, vchSig, pubKey); + setValid.insert(k); + } +}; + +bool CheckSig(vector vchSig, const vector &vchPubKey, const CScript &scriptCode, + const CTransaction& txTo, unsigned int nIn, int nHashType, int flags) +{ + static CSignatureCache signatureCache; + + CPubKey pubkey(vchPubKey); + if (!pubkey.IsValid()) + return false; + + // Hash type is one byte tacked on to the end of the signature + if (vchSig.empty()) + return false; + if (nHashType == 0) + nHashType = vchSig.back(); + else if (nHashType != vchSig.back()) + return false; + vchSig.pop_back(); + + uint256 sighash = SignatureHash(scriptCode, txTo, nIn, nHashType); + + if (signatureCache.Get(sighash, vchSig, pubkey)) + return true; + + if (!pubkey.Verify(sighash, vchSig)) + return false; + + if (!(flags & SCRIPT_VERIFY_NOCACHE)) + signatureCache.Set(sighash, vchSig, pubkey); + + return true; +} + + + + + + + + + +// +// Return public keys or hashes from scriptPubKey, for 'standard' transaction types. +// +bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector >& vSolutionsRet) +{ + // Templates + static map mTemplates; + if (mTemplates.empty()) + { + // Standard tx, sender provides pubkey, receiver adds signature + mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG)); + + // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey + mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG)); + + // Sender provides N pubkeys, receivers provides M signatures + mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG)); + } + + // Shortcut for pay-to-script-hash, which are more constrained than the other types: + // it is always OP_HASH160 20 [20 byte hash] OP_EQUAL + if (scriptPubKey.IsPayToScriptHash()) + { + typeRet = TX_SCRIPTHASH; + vector hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22); + vSolutionsRet.push_back(hashBytes); + return true; + } + + // Scan templates + const CScript& script1 = scriptPubKey; + BOOST_FOREACH(const PAIRTYPE(txnouttype, CScript)& tplate, mTemplates) + { + const CScript& script2 = tplate.second; + vSolutionsRet.clear(); + + opcodetype opcode1, opcode2; + vector vch1, vch2; + + // Compare + CScript::const_iterator pc1 = script1.begin(); + CScript::const_iterator pc2 = script2.begin(); + loop + { + if (pc1 == script1.end() && pc2 == script2.end()) + { + // Found a match + typeRet = tplate.first; + if (typeRet == TX_MULTISIG) + { + // Additional checks for TX_MULTISIG: + unsigned char m = vSolutionsRet.front()[0]; + unsigned char n = vSolutionsRet.back()[0]; + if (m < 1 || n < 1 || m > n || vSolutionsRet.size()-2 != n) + return false; + } + return true; + } + if (!script1.GetOp(pc1, opcode1, vch1)) + break; + if (!script2.GetOp(pc2, opcode2, vch2)) + break; + + // Template matching opcodes: + if (opcode2 == OP_PUBKEYS) + { + while (vch1.size() >= 33 && vch1.size() <= 120) + { + vSolutionsRet.push_back(vch1); + if (!script1.GetOp(pc1, opcode1, vch1)) + break; + } + if (!script2.GetOp(pc2, opcode2, vch2)) + break; + // Normal situation is to fall through + // to other if/else statements + } + + if (opcode2 == OP_PUBKEY) + { + if (vch1.size() < 33 || vch1.size() > 120) + break; + vSolutionsRet.push_back(vch1); + } + else if (opcode2 == OP_PUBKEYHASH) + { + if (vch1.size() != sizeof(uint160)) + break; + vSolutionsRet.push_back(vch1); + } + else if (opcode2 == OP_SMALLINTEGER) + { // Single-byte small integer pushed onto vSolutions + if (opcode1 == OP_0 || + (opcode1 >= OP_1 && opcode1 <= OP_16)) + { + char n = (char)CScript::DecodeOP_N(opcode1); + vSolutionsRet.push_back(valtype(1, n)); + } + else + break; + } + else if (opcode1 != opcode2 || vch1 != vch2) + { + // Others must match exactly + break; + } + } + } + + vSolutionsRet.clear(); + typeRet = TX_NONSTANDARD; + return false; +} + + +bool Sign1(const CKeyID& address, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet) +{ + CKey key; + if (!keystore.GetKey(address, key)) + return false; + + vector vchSig; + if (!key.Sign(hash, vchSig)) + return false; + vchSig.push_back((unsigned char)nHashType); + scriptSigRet << vchSig; + + return true; +} + +bool SignN(const vector& multisigdata, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet) +{ + int nSigned = 0; + int nRequired = multisigdata.front()[0]; + for (unsigned int i = 1; i < multisigdata.size()-1 && nSigned < nRequired; i++) + { + const valtype& pubkey = multisigdata[i]; + CKeyID keyID = CPubKey(pubkey).GetID(); + if (Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) + ++nSigned; + } + return nSigned==nRequired; +} + +// +// Sign scriptPubKey with private keys stored in keystore, given transaction hash and hash type. +// Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed), +// unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script. +// Returns false if scriptPubKey could not be completely satisfied. +// +bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, + CScript& scriptSigRet, txnouttype& whichTypeRet) +{ + scriptSigRet.clear(); + + vector vSolutions; + if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) + return false; + + CKeyID keyID; + switch (whichTypeRet) + { + case TX_NONSTANDARD: + return false; + case TX_PUBKEY: + keyID = CPubKey(vSolutions[0]).GetID(); + return Sign1(keyID, keystore, hash, nHashType, scriptSigRet); + case TX_PUBKEYHASH: + keyID = CKeyID(uint160(vSolutions[0])); + if (!Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) + return false; + else + { + CPubKey vch; + keystore.GetPubKey(keyID, vch); + scriptSigRet << vch; + } + return true; + case TX_SCRIPTHASH: + return keystore.GetCScript(uint160(vSolutions[0]), scriptSigRet); + + case TX_MULTISIG: + scriptSigRet << OP_0; // workaround CHECKMULTISIG bug + return (SignN(vSolutions, keystore, hash, nHashType, scriptSigRet)); + } + return false; +} + +int ScriptSigArgsExpected(txnouttype t, const std::vector >& vSolutions) +{ + switch (t) + { + case TX_NONSTANDARD: + return -1; + case TX_PUBKEY: + return 1; + case TX_PUBKEYHASH: + return 2; + case TX_MULTISIG: + if (vSolutions.size() < 1 || vSolutions[0].size() < 1) + return -1; + return vSolutions[0][0] + 1; + case TX_SCRIPTHASH: + return 1; // doesn't include args needed by the script + } + return -1; +} + +bool IsStandard(const CScript& scriptPubKey) +{ + vector vSolutions; + txnouttype whichType; + if (!Solver(scriptPubKey, whichType, vSolutions)) + return false; + + if (whichType == TX_MULTISIG) + { + unsigned char m = vSolutions.front()[0]; + unsigned char n = vSolutions.back()[0]; + // Support up to x-of-3 multisig txns as standard + if (n < 1 || n > 3) + return false; + if (m < 1 || m > n) + return false; + } + + return whichType != TX_NONSTANDARD; +} + + +unsigned int HaveKeys(const vector& pubkeys, const CKeyStore& keystore) +{ + unsigned int nResult = 0; + BOOST_FOREACH(const valtype& pubkey, pubkeys) + { + CKeyID keyID = CPubKey(pubkey).GetID(); + if (keystore.HaveKey(keyID)) + ++nResult; + } + return nResult; +} + + +class CKeyStoreIsMineVisitor : public boost::static_visitor +{ +private: + const CKeyStore *keystore; +public: + CKeyStoreIsMineVisitor(const CKeyStore *keystoreIn) : keystore(keystoreIn) { } + bool operator()(const CNoDestination &dest) const { return false; } + bool operator()(const CKeyID &keyID) const { return keystore->HaveKey(keyID); } + bool operator()(const CScriptID &scriptID) const { return keystore->HaveCScript(scriptID); } +}; + +bool IsMine(const CKeyStore &keystore, const CTxDestination &dest) +{ + return boost::apply_visitor(CKeyStoreIsMineVisitor(&keystore), dest); +} + +bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) +{ + vector vSolutions; + txnouttype whichType; + if (!Solver(scriptPubKey, whichType, vSolutions)) + return false; + + CKeyID keyID; + switch (whichType) + { + case TX_NONSTANDARD: + return false; + case TX_PUBKEY: + keyID = CPubKey(vSolutions[0]).GetID(); + return keystore.HaveKey(keyID); + case TX_PUBKEYHASH: + keyID = CKeyID(uint160(vSolutions[0])); + return keystore.HaveKey(keyID); + case TX_SCRIPTHASH: + { + CScript subscript; + if (!keystore.GetCScript(CScriptID(uint160(vSolutions[0])), subscript)) + return false; + return IsMine(keystore, subscript); + } + case TX_MULTISIG: + { + // Only consider transactions "mine" if we own ALL the + // keys involved. multi-signature transactions that are + // partially owned (somebody else has a key that can spend + // them) enable spend-out-from-under-you attacks, especially + // in shared-wallet situations. + vector keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); + return HaveKeys(keys, keystore) == keys.size(); + } + } + return false; +} + +bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) +{ + vector vSolutions; + txnouttype whichType; + if (!Solver(scriptPubKey, whichType, vSolutions)) + return false; + + if (whichType == TX_PUBKEY) + { + addressRet = CPubKey(vSolutions[0]).GetID(); + return true; + } + else if (whichType == TX_PUBKEYHASH) + { + addressRet = CKeyID(uint160(vSolutions[0])); + return true; + } + else if (whichType == TX_SCRIPTHASH) + { + addressRet = CScriptID(uint160(vSolutions[0])); + return true; + } + // Multisig txns have more than one address... + return false; +} + +bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vector& addressRet, int& nRequiredRet) +{ + addressRet.clear(); + typeRet = TX_NONSTANDARD; + vector vSolutions; + if (!Solver(scriptPubKey, typeRet, vSolutions)) + return false; + + if (typeRet == TX_MULTISIG) + { + nRequiredRet = vSolutions.front()[0]; + for (unsigned int i = 1; i < vSolutions.size()-1; i++) + { + CTxDestination address = CPubKey(vSolutions[i]).GetID(); + addressRet.push_back(address); + } + } + else + { + nRequiredRet = 1; + CTxDestination address; + if (!ExtractDestination(scriptPubKey, address)) + return false; + addressRet.push_back(address); + } + + return true; +} + +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, + unsigned int flags, int nHashType) +{ + vector > stack, stackCopy; + if (!EvalScript(stack, scriptSig, txTo, nIn, flags, nHashType)) + return false; + if (flags & SCRIPT_VERIFY_P2SH) + stackCopy = stack; + if (!EvalScript(stack, scriptPubKey, txTo, nIn, flags, nHashType)) + return false; + if (stack.empty()) + return false; + + if (CastToBool(stack.back()) == false) + return false; + + // Additional validation for spend-to-script-hash transactions: + if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash()) + { + if (!scriptSig.IsPushOnly()) // scriptSig must be literals-only + return false; // or validation fails + + // stackCopy cannot be empty here, because if it was the + // P2SH HASH <> EQUAL scriptPubKey would be evaluated with + // an empty stack and the EvalScript above would return false. + assert(!stackCopy.empty()); + + const valtype& pubKeySerialized = stackCopy.back(); + CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); + popstack(stackCopy); + + if (!EvalScript(stackCopy, pubKey2, txTo, nIn, flags, nHashType)) + return false; + if (stackCopy.empty()) + return false; + return CastToBool(stackCopy.back()); + } + + return true; +} + + +bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType) +{ + assert(nIn < txTo.vin.size()); + CTxIn& txin = txTo.vin[nIn]; + + // Leave out the signature from the hash, since a signature can't sign itself. + // The checksig op will also drop the signatures from its hash. + uint256 hash = SignatureHash(fromPubKey, txTo, nIn, nHashType); + + txnouttype whichType; + if (!Solver(keystore, fromPubKey, hash, nHashType, txin.scriptSig, whichType)) + return false; + + if (whichType == TX_SCRIPTHASH) + { + // Solver returns the subscript that need to be evaluated; + // the final scriptSig is the signatures from that + // and then the serialized subscript: + CScript subscript = txin.scriptSig; + + // Recompute txn hash using subscript in place of scriptPubKey: + uint256 hash2 = SignatureHash(subscript, txTo, nIn, nHashType); + + txnouttype subType; + bool fSolved = + Solver(keystore, subscript, hash2, nHashType, txin.scriptSig, subType) && subType != TX_SCRIPTHASH; + // Append serialized subscript whether or not it is completely signed: + txin.scriptSig << static_cast(subscript); + if (!fSolved) return false; + } + + // Test solution + return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, 0); +} + +bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType) +{ + assert(nIn < txTo.vin.size()); + CTxIn& txin = txTo.vin[nIn]; + assert(txin.prevout.n < txFrom.vout.size()); + const CTxOut& txout = txFrom.vout[txin.prevout.n]; + + return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType); +} + +static CScript PushAll(const vector& values) +{ + CScript result; + BOOST_FOREACH(const valtype& v, values) + result << v; + return result; +} + +static CScript CombineMultisig(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, + const vector& vSolutions, + vector& sigs1, vector& sigs2) +{ + // Combine all the signatures we've got: + set allsigs; + BOOST_FOREACH(const valtype& v, sigs1) + { + if (!v.empty()) + allsigs.insert(v); + } + BOOST_FOREACH(const valtype& v, sigs2) + { + if (!v.empty()) + allsigs.insert(v); + } + + // Build a map of pubkey -> signature by matching sigs to pubkeys: + assert(vSolutions.size() > 1); + unsigned int nSigsRequired = vSolutions.front()[0]; + unsigned int nPubKeys = vSolutions.size()-2; + map sigs; + BOOST_FOREACH(const valtype& sig, allsigs) + { + for (unsigned int i = 0; i < nPubKeys; i++) + { + const valtype& pubkey = vSolutions[i+1]; + if (sigs.count(pubkey)) + continue; // Already got a sig for this pubkey + + if (CheckSig(sig, pubkey, scriptPubKey, txTo, nIn, 0, 0)) + { + sigs[pubkey] = sig; + break; + } + } + } + // Now build a merged CScript: + unsigned int nSigsHave = 0; + CScript result; result << OP_0; // pop-one-too-many workaround + for (unsigned int i = 0; i < nPubKeys && nSigsHave < nSigsRequired; i++) + { + if (sigs.count(vSolutions[i+1])) + { + result << sigs[vSolutions[i+1]]; + ++nSigsHave; + } + } + // Fill any missing with OP_0: + for (unsigned int i = nSigsHave; i < nSigsRequired; i++) + result << OP_0; + + return result; +} + +static CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, + const txnouttype txType, const vector& vSolutions, + vector& sigs1, vector& sigs2) +{ + switch (txType) + { + case TX_NONSTANDARD: + // Don't know anything about this, assume bigger one is correct: + if (sigs1.size() >= sigs2.size()) + return PushAll(sigs1); + return PushAll(sigs2); + case TX_PUBKEY: + case TX_PUBKEYHASH: + // Signatures are bigger than placeholders or empty scripts: + if (sigs1.empty() || sigs1[0].empty()) + return PushAll(sigs2); + return PushAll(sigs1); + case TX_SCRIPTHASH: + if (sigs1.empty() || sigs1.back().empty()) + return PushAll(sigs2); + else if (sigs2.empty() || sigs2.back().empty()) + return PushAll(sigs1); + else + { + // Recur to combine: + valtype spk = sigs1.back(); + CScript pubKey2(spk.begin(), spk.end()); + + txnouttype txType2; + vector > vSolutions2; + Solver(pubKey2, txType2, vSolutions2); + sigs1.pop_back(); + sigs2.pop_back(); + CScript result = CombineSignatures(pubKey2, txTo, nIn, txType2, vSolutions2, sigs1, sigs2); + result << spk; + return result; + } + case TX_MULTISIG: + return CombineMultisig(scriptPubKey, txTo, nIn, vSolutions, sigs1, sigs2); + } + + return CScript(); +} + +CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, + const CScript& scriptSig1, const CScript& scriptSig2) +{ + txnouttype txType; + vector > vSolutions; + Solver(scriptPubKey, txType, vSolutions); + + vector stack1; + EvalScript(stack1, scriptSig1, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC, 0); + vector stack2; + EvalScript(stack2, scriptSig2, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC, 0); + + return CombineSignatures(scriptPubKey, txTo, nIn, txType, vSolutions, stack1, stack2); +} + +unsigned int CScript::GetSigOpCount(bool fAccurate) const +{ + unsigned int n = 0; + const_iterator pc = begin(); + opcodetype lastOpcode = OP_INVALIDOPCODE; + while (pc < end()) + { + opcodetype opcode; + if (!GetOp(pc, opcode)) + break; + if (opcode == OP_CHECKSIG || opcode == OP_CHECKSIGVERIFY) + n++; + else if (opcode == OP_CHECKMULTISIG || opcode == OP_CHECKMULTISIGVERIFY) + { + if (fAccurate && lastOpcode >= OP_1 && lastOpcode <= OP_16) + n += DecodeOP_N(lastOpcode); + else + n += 20; + } + lastOpcode = opcode; + } + return n; +} + +unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const +{ + if (!IsPayToScriptHash()) + return GetSigOpCount(true); + + // This is a pay-to-script-hash scriptPubKey; + // get the last item that the scriptSig + // pushes onto the stack: + const_iterator pc = scriptSig.begin(); + vector data; + while (pc < scriptSig.end()) + { + opcodetype opcode; + if (!scriptSig.GetOp(pc, opcode, data)) + return 0; + if (opcode > OP_16) + return 0; + } + + /// ... and return its opcount: + CScript subscript(data.begin(), data.end()); + return subscript.GetSigOpCount(true); +} + +bool CScript::IsPayToScriptHash() const +{ + // Extra-fast test for pay-to-script-hash CScripts: + return (this->size() == 23 && + this->at(0) == OP_HASH160 && + this->at(1) == 0x14 && + this->at(22) == OP_EQUAL); +} + +class CScriptVisitor : public boost::static_visitor +{ +private: + CScript *script; +public: + CScriptVisitor(CScript *scriptin) { script = scriptin; } + + bool operator()(const CNoDestination &dest) const { + script->clear(); + return false; + } + + bool operator()(const CKeyID &keyID) const { + script->clear(); + *script << OP_DUP << OP_HASH160 << keyID << OP_EQUALVERIFY << OP_CHECKSIG; + return true; + } + + bool operator()(const CScriptID &scriptID) const { + script->clear(); + *script << OP_HASH160 << scriptID << OP_EQUAL; + return true; + } +}; + +void CScript::SetDestination(const CTxDestination& dest) +{ + boost::apply_visitor(CScriptVisitor(this), dest); +} + +void CScript::SetMultisig(int nRequired, const std::vector& keys) +{ + this->clear(); + + *this << EncodeOP_N(nRequired); + BOOST_FOREACH(const CPubKey& key, keys) + *this << key; + *this << EncodeOP_N(keys.size()) << OP_CHECKMULTISIG; +} + +bool CScriptCompressor::IsToKeyID(CKeyID &hash) const +{ + if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 + && script[2] == 20 && script[23] == OP_EQUALVERIFY + && script[24] == OP_CHECKSIG) { + memcpy(&hash, &script[3], 20); + return true; + } + return false; +} + +bool CScriptCompressor::IsToScriptID(CScriptID &hash) const +{ + if (script.size() == 23 && script[0] == OP_HASH160 && script[1] == 20 + && script[22] == OP_EQUAL) { + memcpy(&hash, &script[2], 20); + return true; + } + return false; +} + +bool CScriptCompressor::IsToPubKey(CPubKey &pubkey) const +{ + if (script.size() == 35 && script[0] == 33 && script[34] == OP_CHECKSIG + && (script[1] == 0x02 || script[1] == 0x03)) { + pubkey.Set(&script[1], &script[34]); + return true; + } + if (script.size() == 67 && script[0] == 65 && script[66] == OP_CHECKSIG + && script[1] == 0x04) { + pubkey.Set(&script[1], &script[66]); + return pubkey.IsFullyValid(); // if not fully valid, a case that would not be compressible + } + return false; +} + +bool CScriptCompressor::Compress(std::vector &out) const +{ + CKeyID keyID; + if (IsToKeyID(keyID)) { + out.resize(21); + out[0] = 0x00; + memcpy(&out[1], &keyID, 20); + return true; + } + CScriptID scriptID; + if (IsToScriptID(scriptID)) { + out.resize(21); + out[0] = 0x01; + memcpy(&out[1], &scriptID, 20); + return true; + } + CPubKey pubkey; + if (IsToPubKey(pubkey)) { + out.resize(33); + memcpy(&out[1], &pubkey[1], 32); + if (pubkey[0] == 0x02 || pubkey[0] == 0x03) { + out[0] = pubkey[0]; + return true; + } else if (pubkey[0] == 0x04) { + out[0] = 0x04 | (pubkey[64] & 0x01); + return true; + } + } + return false; +} + +unsigned int CScriptCompressor::GetSpecialSize(unsigned int nSize) const +{ + if (nSize == 0 || nSize == 1) + return 20; + if (nSize == 2 || nSize == 3 || nSize == 4 || nSize == 5) + return 32; + return 0; +} + +bool CScriptCompressor::Decompress(unsigned int nSize, const std::vector &in) +{ + switch(nSize) { + case 0x00: + script.resize(25); + script[0] = OP_DUP; + script[1] = OP_HASH160; + script[2] = 20; + memcpy(&script[3], &in[0], 20); + script[23] = OP_EQUALVERIFY; + script[24] = OP_CHECKSIG; + return true; + case 0x01: + script.resize(23); + script[0] = OP_HASH160; + script[1] = 20; + memcpy(&script[2], &in[0], 20); + script[22] = OP_EQUAL; + return true; + case 0x02: + case 0x03: + script.resize(35); + script[0] = 33; + script[1] = nSize; + memcpy(&script[2], &in[0], 32); + script[34] = OP_CHECKSIG; + return true; + case 0x04: + case 0x05: + unsigned char vch[33] = {}; + vch[0] = nSize - 2; + memcpy(&vch[1], &in[0], 32); + CPubKey pubkey(&vch[0], &vch[33]); + if (!pubkey.Decompress()) + return false; + assert(pubkey.size() == 65); + script.resize(67); + script[0] = 65; + memcpy(&script[1], pubkey.begin(), 65); + script[66] = OP_CHECKSIG; + return true; + } + return false; +} diff --git a/src/script.h b/src/script.h new file mode 100644 index 0000000..3cbb2cf --- /dev/null +++ b/src/script.h @@ -0,0 +1,687 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef H_BITCOIN_SCRIPT +#define H_BITCOIN_SCRIPT + +#include +#include + +#include +#include + +#include "keystore.h" +#include "bignum.h" + +class CCoins; +class CTransaction; + +static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes + +/** Signature hash types/flags */ +enum +{ + SIGHASH_ALL = 1, + SIGHASH_NONE = 2, + SIGHASH_SINGLE = 3, + SIGHASH_ANYONECANPAY = 0x80, +}; + +/** Script verification flags */ +enum +{ + SCRIPT_VERIFY_NONE = 0, + SCRIPT_VERIFY_P2SH = (1U << 0), + SCRIPT_VERIFY_STRICTENC = (1U << 1), + SCRIPT_VERIFY_NOCACHE = (1U << 2), +}; + +enum txnouttype +{ + TX_NONSTANDARD, + // 'standard' transaction types: + TX_PUBKEY, + TX_PUBKEYHASH, + TX_SCRIPTHASH, + TX_MULTISIG, +}; + +class CNoDestination { +public: + friend bool operator==(const CNoDestination &a, const CNoDestination &b) { return true; } + friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; } +}; + +/** A txout script template with a specific destination. It is either: + * * CNoDestination: no destination set + * * CKeyID: TX_PUBKEYHASH destination + * * CScriptID: TX_SCRIPTHASH destination + * A CTxDestination is the internal data type encoded in a CBitcoinAddress + */ +typedef boost::variant CTxDestination; + +const char* GetTxnOutputType(txnouttype t); + +/** Script opcodes */ +enum opcodetype +{ + // push value + OP_0 = 0x00, + OP_FALSE = OP_0, + OP_PUSHDATA1 = 0x4c, + OP_PUSHDATA2 = 0x4d, + OP_PUSHDATA4 = 0x4e, + OP_1NEGATE = 0x4f, + OP_RESERVED = 0x50, + OP_1 = 0x51, + OP_TRUE=OP_1, + OP_2 = 0x52, + OP_3 = 0x53, + OP_4 = 0x54, + OP_5 = 0x55, + OP_6 = 0x56, + OP_7 = 0x57, + OP_8 = 0x58, + OP_9 = 0x59, + OP_10 = 0x5a, + OP_11 = 0x5b, + OP_12 = 0x5c, + OP_13 = 0x5d, + OP_14 = 0x5e, + OP_15 = 0x5f, + OP_16 = 0x60, + + // control + OP_NOP = 0x61, + OP_VER = 0x62, + OP_IF = 0x63, + OP_NOTIF = 0x64, + OP_VERIF = 0x65, + OP_VERNOTIF = 0x66, + OP_ELSE = 0x67, + OP_ENDIF = 0x68, + OP_VERIFY = 0x69, + OP_RETURN = 0x6a, + + // stack ops + OP_TOALTSTACK = 0x6b, + OP_FROMALTSTACK = 0x6c, + OP_2DROP = 0x6d, + OP_2DUP = 0x6e, + OP_3DUP = 0x6f, + OP_2OVER = 0x70, + OP_2ROT = 0x71, + OP_2SWAP = 0x72, + OP_IFDUP = 0x73, + OP_DEPTH = 0x74, + OP_DROP = 0x75, + OP_DUP = 0x76, + OP_NIP = 0x77, + OP_OVER = 0x78, + OP_PICK = 0x79, + OP_ROLL = 0x7a, + OP_ROT = 0x7b, + OP_SWAP = 0x7c, + OP_TUCK = 0x7d, + + // splice ops + OP_CAT = 0x7e, + OP_SUBSTR = 0x7f, + OP_LEFT = 0x80, + OP_RIGHT = 0x81, + OP_SIZE = 0x82, + + // bit logic + OP_INVERT = 0x83, + OP_AND = 0x84, + OP_OR = 0x85, + OP_XOR = 0x86, + OP_EQUAL = 0x87, + OP_EQUALVERIFY = 0x88, + OP_RESERVED1 = 0x89, + OP_RESERVED2 = 0x8a, + + // numeric + OP_1ADD = 0x8b, + OP_1SUB = 0x8c, + OP_2MUL = 0x8d, + OP_2DIV = 0x8e, + OP_NEGATE = 0x8f, + OP_ABS = 0x90, + OP_NOT = 0x91, + OP_0NOTEQUAL = 0x92, + + OP_ADD = 0x93, + OP_SUB = 0x94, + OP_MUL = 0x95, + OP_DIV = 0x96, + OP_MOD = 0x97, + OP_LSHIFT = 0x98, + OP_RSHIFT = 0x99, + + OP_BOOLAND = 0x9a, + OP_BOOLOR = 0x9b, + OP_NUMEQUAL = 0x9c, + OP_NUMEQUALVERIFY = 0x9d, + OP_NUMNOTEQUAL = 0x9e, + OP_LESSTHAN = 0x9f, + OP_GREATERTHAN = 0xa0, + OP_LESSTHANOREQUAL = 0xa1, + OP_GREATERTHANOREQUAL = 0xa2, + OP_MIN = 0xa3, + OP_MAX = 0xa4, + + OP_WITHIN = 0xa5, + + // crypto + OP_RIPEMD160 = 0xa6, + OP_SHA1 = 0xa7, + OP_SHA256 = 0xa8, + OP_HASH160 = 0xa9, + OP_HASH256 = 0xaa, + OP_CODESEPARATOR = 0xab, + OP_CHECKSIG = 0xac, + OP_CHECKSIGVERIFY = 0xad, + OP_CHECKMULTISIG = 0xae, + OP_CHECKMULTISIGVERIFY = 0xaf, + + // expansion + OP_NOP1 = 0xb0, + OP_NOP2 = 0xb1, + OP_NOP3 = 0xb2, + OP_NOP4 = 0xb3, + OP_NOP5 = 0xb4, + OP_NOP6 = 0xb5, + OP_NOP7 = 0xb6, + OP_NOP8 = 0xb7, + OP_NOP9 = 0xb8, + OP_NOP10 = 0xb9, + + + + // template matching params + OP_SMALLINTEGER = 0xfa, + OP_PUBKEYS = 0xfb, + OP_PUBKEYHASH = 0xfd, + OP_PUBKEY = 0xfe, + + OP_INVALIDOPCODE = 0xff, +}; + +const char* GetOpName(opcodetype opcode); + + + +inline std::string ValueString(const std::vector& vch) +{ + if (vch.size() <= 4) + return strprintf("%d", CBigNum(vch).getint()); + else + return HexStr(vch); +} + +inline std::string StackString(const std::vector >& vStack) +{ + std::string str; + BOOST_FOREACH(const std::vector& vch, vStack) + { + if (!str.empty()) + str += " "; + str += ValueString(vch); + } + return str; +} + + + + + + + + +/** Serialized script, used inside transaction inputs and outputs */ +class CScript : public std::vector +{ +protected: + CScript& push_int64(int64 n) + { + if (n == -1 || (n >= 1 && n <= 16)) + { + push_back(n + (OP_1 - 1)); + } + else + { + CBigNum bn(n); + *this << bn.getvch(); + } + return *this; + } + + CScript& push_uint64(uint64 n) + { + if (n >= 1 && n <= 16) + { + push_back(n + (OP_1 - 1)); + } + else + { + CBigNum bn(n); + *this << bn.getvch(); + } + return *this; + } + +public: + CScript() { } + CScript(const CScript& b) : std::vector(b.begin(), b.end()) { } + CScript(const_iterator pbegin, const_iterator pend) : std::vector(pbegin, pend) { } +#ifndef _MSC_VER + CScript(const unsigned char* pbegin, const unsigned char* pend) : std::vector(pbegin, pend) { } +#endif + + CScript& operator+=(const CScript& b) + { + insert(end(), b.begin(), b.end()); + return *this; + } + + friend CScript operator+(const CScript& a, const CScript& b) + { + CScript ret = a; + ret += b; + return ret; + } + + + //explicit CScript(char b) is not portable. Use 'signed char' or 'unsigned char'. + explicit CScript(signed char b) { operator<<(b); } + explicit CScript(short b) { operator<<(b); } + explicit CScript(int b) { operator<<(b); } + explicit CScript(long b) { operator<<(b); } + explicit CScript(int64 b) { operator<<(b); } + explicit CScript(unsigned char b) { operator<<(b); } + explicit CScript(unsigned int b) { operator<<(b); } + explicit CScript(unsigned short b) { operator<<(b); } + explicit CScript(unsigned long b) { operator<<(b); } + explicit CScript(uint64 b) { operator<<(b); } + + explicit CScript(opcodetype b) { operator<<(b); } + explicit CScript(const uint256& b) { operator<<(b); } + explicit CScript(const CBigNum& b) { operator<<(b); } + explicit CScript(const std::vector& b) { operator<<(b); } + + + //CScript& operator<<(char b) is not portable. Use 'signed char' or 'unsigned char'. + CScript& operator<<(signed char b) { return push_int64(b); } + CScript& operator<<(short b) { return push_int64(b); } + CScript& operator<<(int b) { return push_int64(b); } + CScript& operator<<(long b) { return push_int64(b); } + CScript& operator<<(int64 b) { return push_int64(b); } + CScript& operator<<(unsigned char b) { return push_uint64(b); } + CScript& operator<<(unsigned int b) { return push_uint64(b); } + CScript& operator<<(unsigned short b) { return push_uint64(b); } + CScript& operator<<(unsigned long b) { return push_uint64(b); } + CScript& operator<<(uint64 b) { return push_uint64(b); } + + CScript& operator<<(opcodetype opcode) + { + if (opcode < 0 || opcode > 0xff) + throw std::runtime_error("CScript::operator<<() : invalid opcode"); + insert(end(), (unsigned char)opcode); + return *this; + } + + CScript& operator<<(const uint160& b) + { + insert(end(), sizeof(b)); + insert(end(), (unsigned char*)&b, (unsigned char*)&b + sizeof(b)); + return *this; + } + + CScript& operator<<(const uint256& b) + { + insert(end(), sizeof(b)); + insert(end(), (unsigned char*)&b, (unsigned char*)&b + sizeof(b)); + return *this; + } + + CScript& operator<<(const CPubKey& key) + { + assert(key.size() < OP_PUSHDATA1); + insert(end(), (unsigned char)key.size()); + insert(end(), key.begin(), key.end()); + return *this; + } + + CScript& operator<<(const CBigNum& b) + { + *this << b.getvch(); + return *this; + } + + CScript& operator<<(const std::vector& b) + { + if (b.size() < OP_PUSHDATA1) + { + insert(end(), (unsigned char)b.size()); + } + else if (b.size() <= 0xff) + { + insert(end(), OP_PUSHDATA1); + insert(end(), (unsigned char)b.size()); + } + else if (b.size() <= 0xffff) + { + insert(end(), OP_PUSHDATA2); + unsigned short nSize = b.size(); + insert(end(), (unsigned char*)&nSize, (unsigned char*)&nSize + sizeof(nSize)); + } + else + { + insert(end(), OP_PUSHDATA4); + unsigned int nSize = b.size(); + insert(end(), (unsigned char*)&nSize, (unsigned char*)&nSize + sizeof(nSize)); + } + insert(end(), b.begin(), b.end()); + return *this; + } + + CScript& operator<<(const CScript& b) + { + // I'm not sure if this should push the script or concatenate scripts. + // If there's ever a use for pushing a script onto a script, delete this member fn + assert(!"Warning: Pushing a CScript onto a CScript with << is probably not intended, use + to concatenate!"); + return *this; + } + + + bool GetOp(iterator& pc, opcodetype& opcodeRet, std::vector& vchRet) + { + // Wrapper so it can be called with either iterator or const_iterator + const_iterator pc2 = pc; + bool fRet = GetOp2(pc2, opcodeRet, &vchRet); + pc = begin() + (pc2 - begin()); + return fRet; + } + + bool GetOp(iterator& pc, opcodetype& opcodeRet) + { + const_iterator pc2 = pc; + bool fRet = GetOp2(pc2, opcodeRet, NULL); + pc = begin() + (pc2 - begin()); + return fRet; + } + + bool GetOp(const_iterator& pc, opcodetype& opcodeRet, std::vector& vchRet) const + { + return GetOp2(pc, opcodeRet, &vchRet); + } + + bool GetOp(const_iterator& pc, opcodetype& opcodeRet) const + { + return GetOp2(pc, opcodeRet, NULL); + } + + bool GetOp2(const_iterator& pc, opcodetype& opcodeRet, std::vector* pvchRet) const + { + opcodeRet = OP_INVALIDOPCODE; + if (pvchRet) + pvchRet->clear(); + if (pc >= end()) + return false; + + // Read instruction + if (end() - pc < 1) + return false; + unsigned int opcode = *pc++; + + // Immediate operand + if (opcode <= OP_PUSHDATA4) + { + unsigned int nSize = 0; + if (opcode < OP_PUSHDATA1) + { + nSize = opcode; + } + else if (opcode == OP_PUSHDATA1) + { + if (end() - pc < 1) + return false; + nSize = *pc++; + } + else if (opcode == OP_PUSHDATA2) + { + if (end() - pc < 2) + return false; + nSize = 0; + memcpy(&nSize, &pc[0], 2); + pc += 2; + } + else if (opcode == OP_PUSHDATA4) + { + if (end() - pc < 4) + return false; + memcpy(&nSize, &pc[0], 4); + pc += 4; + } + if (end() - pc < 0 || (unsigned int)(end() - pc) < nSize) + return false; + if (pvchRet) + pvchRet->assign(pc, pc + nSize); + pc += nSize; + } + + opcodeRet = (opcodetype)opcode; + return true; + } + + // Encode/decode small integers: + static int DecodeOP_N(opcodetype opcode) + { + if (opcode == OP_0) + return 0; + assert(opcode >= OP_1 && opcode <= OP_16); + return (int)opcode - (int)(OP_1 - 1); + } + static opcodetype EncodeOP_N(int n) + { + assert(n >= 0 && n <= 16); + if (n == 0) + return OP_0; + return (opcodetype)(OP_1+n-1); + } + + int FindAndDelete(const CScript& b) + { + int nFound = 0; + if (b.empty()) + return nFound; + iterator pc = begin(); + opcodetype opcode; + do + { + while (end() - pc >= (long)b.size() && memcmp(&pc[0], &b[0], b.size()) == 0) + { + erase(pc, pc + b.size()); + ++nFound; + } + } + while (GetOp(pc, opcode)); + return nFound; + } + int Find(opcodetype op) const + { + int nFound = 0; + opcodetype opcode; + for (const_iterator pc = begin(); pc != end() && GetOp(pc, opcode);) + if (opcode == op) + ++nFound; + return nFound; + } + + // Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs + // as 20 sigops. With pay-to-script-hash, that changed: + // CHECKMULTISIGs serialized in scriptSigs are + // counted more accurately, assuming they are of the form + // ... OP_N CHECKMULTISIG ... + unsigned int GetSigOpCount(bool fAccurate) const; + + // Accurately count sigOps, including sigOps in + // pay-to-script-hash transactions: + unsigned int GetSigOpCount(const CScript& scriptSig) const; + + bool IsPayToScriptHash() const; + + // Called by CTransaction::IsStandard + bool IsPushOnly() const + { + const_iterator pc = begin(); + while (pc < end()) + { + opcodetype opcode; + if (!GetOp(pc, opcode)) + return false; + if (opcode > OP_16) + return false; + } + return true; + } + + + void SetDestination(const CTxDestination& address); + void SetMultisig(int nRequired, const std::vector& keys); + + + void PrintHex() const + { + printf("CScript(%s)\n", HexStr(begin(), end(), true).c_str()); + } + + std::string ToString() const + { + std::string str; + opcodetype opcode; + std::vector vch; + const_iterator pc = begin(); + while (pc < end()) + { + if (!str.empty()) + str += " "; + if (!GetOp(pc, opcode, vch)) + { + str += "[error]"; + return str; + } + if (0 <= opcode && opcode <= OP_PUSHDATA4) + str += ValueString(vch); + else + str += GetOpName(opcode); + } + return str; + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } + + CScriptID GetID() const + { + return CScriptID(Hash160(*this)); + } +}; + +/** Compact serializer for scripts. + * + * It detects common cases and encodes them much more efficiently. + * 3 special cases are defined: + * * Pay to pubkey hash (encoded as 21 bytes) + * * Pay to script hash (encoded as 21 bytes) + * * Pay to pubkey starting with 0x02, 0x03 or 0x04 (encoded as 33 bytes) + * + * Other scripts up to 121 bytes require 1 byte + script length. Above + * that, scripts up to 16505 bytes require 2 bytes + script length. + */ +class CScriptCompressor +{ +private: + // make this static for now (there are only 6 special scripts defined) + // this can potentially be extended together with a new nVersion for + // transactions, in which case this value becomes dependent on nVersion + // and nHeight of the enclosing transaction. + static const unsigned int nSpecialScripts = 6; + + CScript &script; +protected: + // These check for scripts for which a special case with a shorter encoding is defined. + // They are implemented separately from the CScript test, as these test for exact byte + // sequence correspondences, and are more strict. For example, IsToPubKey also verifies + // whether the public key is valid (as invalid ones cannot be represented in compressed + // form). + bool IsToKeyID(CKeyID &hash) const; + bool IsToScriptID(CScriptID &hash) const; + bool IsToPubKey(CPubKey &pubkey) const; + + bool Compress(std::vector &out) const; + unsigned int GetSpecialSize(unsigned int nSize) const; + bool Decompress(unsigned int nSize, const std::vector &out); +public: + CScriptCompressor(CScript &scriptIn) : script(scriptIn) { } + + unsigned int GetSerializeSize(int nType, int nVersion) const { + std::vector compr; + if (Compress(compr)) + return compr.size(); + unsigned int nSize = script.size() + nSpecialScripts; + return script.size() + VARINT(nSize).GetSerializeSize(nType, nVersion); + } + + template + void Serialize(Stream &s, int nType, int nVersion) const { + std::vector compr; + if (Compress(compr)) { + s << CFlatData(&compr[0], &compr[compr.size()]); + return; + } + unsigned int nSize = script.size() + nSpecialScripts; + s << VARINT(nSize); + s << CFlatData(&script[0], &script[script.size()]); + } + + template + void Unserialize(Stream &s, int nType, int nVersion) { + unsigned int nSize = 0; + s >> VARINT(nSize); + if (nSize < nSpecialScripts) { + std::vector vch(GetSpecialSize(nSize), 0x00); + s >> REF(CFlatData(&vch[0], &vch[vch.size()])); + Decompress(nSize, vch); + return; + } + nSize -= nSpecialScripts; + script.resize(nSize); + s >> REF(CFlatData(&script[0], &script[script.size()])); + } +}; + +bool IsCanonicalPubKey(const std::vector &vchPubKey); +bool IsCanonicalSignature(const std::vector &vchSig); + +bool EvalScript(std::vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); +bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet); +int ScriptSigArgsExpected(txnouttype t, const std::vector >& vSolutions); +bool IsStandard(const CScript& scriptPubKey); +bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); +bool IsMine(const CKeyStore& keystore, const CTxDestination &dest); +bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet); +bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector& addressRet, int& nRequiredRet); +bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); +bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); + +// Given two sets of signatures for scriptPubKey, possibly with OP_0 placeholders, +// combine them intelligently and return the result. +CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CScript& scriptSig1, const CScript& scriptSig2); + +#endif diff --git a/src/serialize.h b/src/serialize.h new file mode 100644 index 0000000..eac4e06 --- /dev/null +++ b/src/serialize.h @@ -0,0 +1,1361 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_SERIALIZE_H +#define BITCOIN_SERIALIZE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "allocators.h" +#include "version.h" + +typedef long long int64; +typedef unsigned long long uint64; + +class CScript; +class CDataStream; +class CAutoFile; +static const unsigned int MAX_SIZE = 0x02000000; + +// Used to bypass the rule against non-const reference to temporary +// where it makes sense with wrappers such as CFlatData or CTxDB +template +inline T& REF(const T& val) +{ + return const_cast(val); +} + +///////////////////////////////////////////////////////////////// +// +// Templates for serializing to anything that looks like a stream, +// i.e. anything that supports .read(char*, int) and .write(char*, int) +// + +enum +{ + // primary actions + SER_NETWORK = (1 << 0), + SER_DISK = (1 << 1), + SER_GETHASH = (1 << 2), +}; + +#define IMPLEMENT_SERIALIZE(statements) \ + unsigned int GetSerializeSize(int nType, int nVersion) const \ + { \ + CSerActionGetSerializeSize ser_action; \ + const bool fGetSize = true; \ + const bool fWrite = false; \ + const bool fRead = false; \ + unsigned int nSerSize = 0; \ + ser_streamplaceholder s; \ + assert(fGetSize||fWrite||fRead); /* suppress warning */ \ + s.nType = nType; \ + s.nVersion = nVersion; \ + {statements} \ + return nSerSize; \ + } \ + template \ + void Serialize(Stream& s, int nType, int nVersion) const \ + { \ + CSerActionSerialize ser_action; \ + const bool fGetSize = false; \ + const bool fWrite = true; \ + const bool fRead = false; \ + unsigned int nSerSize = 0; \ + assert(fGetSize||fWrite||fRead); /* suppress warning */ \ + {statements} \ + } \ + template \ + void Unserialize(Stream& s, int nType, int nVersion) \ + { \ + CSerActionUnserialize ser_action; \ + const bool fGetSize = false; \ + const bool fWrite = false; \ + const bool fRead = true; \ + unsigned int nSerSize = 0; \ + assert(fGetSize||fWrite||fRead); /* suppress warning */ \ + {statements} \ + } + +#define READWRITE(obj) (nSerSize += ::SerReadWrite(s, (obj), nType, nVersion, ser_action)) + + + + + + +// +// Basic types +// +#define WRITEDATA(s, obj) s.write((char*)&(obj), sizeof(obj)) +#define READDATA(s, obj) s.read((char*)&(obj), sizeof(obj)) + +inline unsigned int GetSerializeSize(char a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed char a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned char a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed short a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned short a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed int a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned int a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed long a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned long a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(int64 a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(uint64 a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(float a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(double a, int, int=0) { return sizeof(a); } + +template inline void Serialize(Stream& s, char a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, signed char a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, unsigned char a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, signed short a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, unsigned short a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, signed int a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, unsigned int a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, signed long a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, unsigned long a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, int64 a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, uint64 a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, float a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, double a, int, int=0) { WRITEDATA(s, a); } + +template inline void Unserialize(Stream& s, char& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, signed char& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, unsigned char& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, signed short& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, unsigned short& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, signed int& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, unsigned int& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, signed long& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, unsigned long& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, int64& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, uint64& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, float& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, double& a, int, int=0) { READDATA(s, a); } + +inline unsigned int GetSerializeSize(bool a, int, int=0) { return sizeof(char); } +template inline void Serialize(Stream& s, bool a, int, int=0) { char f=a; WRITEDATA(s, f); } +template inline void Unserialize(Stream& s, bool& a, int, int=0) { char f; READDATA(s, f); a=f; } + + + + + + +// +// Compact size +// size < 253 -- 1 byte +// size <= USHRT_MAX -- 3 bytes (253 + 2 bytes) +// size <= UINT_MAX -- 5 bytes (254 + 4 bytes) +// size > UINT_MAX -- 9 bytes (255 + 8 bytes) +// +inline unsigned int GetSizeOfCompactSize(uint64 nSize) +{ + if (nSize < 253) return sizeof(unsigned char); + else if (nSize <= std::numeric_limits::max()) return sizeof(unsigned char) + sizeof(unsigned short); + else if (nSize <= std::numeric_limits::max()) return sizeof(unsigned char) + sizeof(unsigned int); + else return sizeof(unsigned char) + sizeof(uint64); +} + +template +void WriteCompactSize(Stream& os, uint64 nSize) +{ + if (nSize < 253) + { + unsigned char chSize = nSize; + WRITEDATA(os, chSize); + } + else if (nSize <= std::numeric_limits::max()) + { + unsigned char chSize = 253; + unsigned short xSize = nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } + else if (nSize <= std::numeric_limits::max()) + { + unsigned char chSize = 254; + unsigned int xSize = nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } + else + { + unsigned char chSize = 255; + uint64 xSize = nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } + return; +} + +template +uint64 ReadCompactSize(Stream& is) +{ + unsigned char chSize; + READDATA(is, chSize); + uint64 nSizeRet = 0; + if (chSize < 253) + { + nSizeRet = chSize; + } + else if (chSize == 253) + { + unsigned short xSize; + READDATA(is, xSize); + nSizeRet = xSize; + } + else if (chSize == 254) + { + unsigned int xSize; + READDATA(is, xSize); + nSizeRet = xSize; + } + else + { + uint64 xSize; + READDATA(is, xSize); + nSizeRet = xSize; + } + if (nSizeRet > (uint64)MAX_SIZE) + throw std::ios_base::failure("ReadCompactSize() : size too large"); + return nSizeRet; +} + +// Variable-length integers: bytes are a MSB base-128 encoding of the number. +// The high bit in each byte signifies whether another digit follows. To make +// the encoding is one-to-one, one is subtracted from all but the last digit. +// Thus, the byte sequence a[] with length len, where all but the last byte +// has bit 128 set, encodes the number: +// +// (a[len-1] & 0x7F) + sum(i=1..len-1, 128^i*((a[len-i-1] & 0x7F)+1)) +// +// Properties: +// * Very small (0-127: 1 byte, 128-16511: 2 bytes, 16512-2113663: 3 bytes) +// * Every integer has exactly one encoding +// * Encoding does not depend on size of original integer type +// * No redundancy: every (infinite) byte sequence corresponds to a list +// of encoded integers. +// +// 0: [0x00] 256: [0x81 0x00] +// 1: [0x01] 16383: [0xFE 0x7F] +// 127: [0x7F] 16384: [0xFF 0x00] +// 128: [0x80 0x00] 16511: [0x80 0xFF 0x7F] +// 255: [0x80 0x7F] 65535: [0x82 0xFD 0x7F] +// 2^32: [0x8E 0xFE 0xFE 0xFF 0x00] + +template +inline unsigned int GetSizeOfVarInt(I n) +{ + int nRet = 0; + while(true) { + nRet++; + if (n <= 0x7F) + break; + n = (n >> 7) - 1; + } + return nRet; +} + +template +void WriteVarInt(Stream& os, I n) +{ + unsigned char tmp[(sizeof(n)*8+6)/7]; + int len=0; + while(true) { + tmp[len] = (n & 0x7F) | (len ? 0x80 : 0x00); + if (n <= 0x7F) + break; + n = (n >> 7) - 1; + len++; + } + do { + WRITEDATA(os, tmp[len]); + } while(len--); +} + +template +I ReadVarInt(Stream& is) +{ + I n = 0; + while(true) { + unsigned char chData; + READDATA(is, chData); + n = (n << 7) | (chData & 0x7F); + if (chData & 0x80) + n++; + else + return n; + } +} + +#define FLATDATA(obj) REF(CFlatData((char*)&(obj), (char*)&(obj) + sizeof(obj))) +#define VARINT(obj) REF(WrapVarInt(REF(obj))) + +/** Wrapper for serializing arrays and POD. + */ +class CFlatData +{ +protected: + char* pbegin; + char* pend; +public: + CFlatData(void* pbeginIn, void* pendIn) : pbegin((char*)pbeginIn), pend((char*)pendIn) { } + char* begin() { return pbegin; } + const char* begin() const { return pbegin; } + char* end() { return pend; } + const char* end() const { return pend; } + + unsigned int GetSerializeSize(int, int=0) const + { + return pend - pbegin; + } + + template + void Serialize(Stream& s, int, int=0) const + { + s.write(pbegin, pend - pbegin); + } + + template + void Unserialize(Stream& s, int, int=0) + { + s.read(pbegin, pend - pbegin); + } +}; + +template +class CVarInt +{ +protected: + I &n; +public: + CVarInt(I& nIn) : n(nIn) { } + + unsigned int GetSerializeSize(int, int) const { + return GetSizeOfVarInt(n); + } + + template + void Serialize(Stream &s, int, int) const { + WriteVarInt(s, n); + } + + template + void Unserialize(Stream& s, int, int) { + n = ReadVarInt(s); + } +}; + +template +CVarInt WrapVarInt(I& n) { return CVarInt(n); } + +// +// Forward declarations +// + +// string +template unsigned int GetSerializeSize(const std::basic_string& str, int, int=0); +template void Serialize(Stream& os, const std::basic_string& str, int, int=0); +template void Unserialize(Stream& is, std::basic_string& str, int, int=0); + +// vector +template unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::true_type&); +template unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::false_type&); +template inline unsigned int GetSerializeSize(const std::vector& v, int nType, int nVersion); +template void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::true_type&); +template void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::false_type&); +template inline void Serialize(Stream& os, const std::vector& v, int nType, int nVersion); +template void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::true_type&); +template void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::false_type&); +template inline void Unserialize(Stream& is, std::vector& v, int nType, int nVersion); + +// others derived from vector +extern inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion); +template void Serialize(Stream& os, const CScript& v, int nType, int nVersion); +template void Unserialize(Stream& is, CScript& v, int nType, int nVersion); + +// pair +template unsigned int GetSerializeSize(const std::pair& item, int nType, int nVersion); +template void Serialize(Stream& os, const std::pair& item, int nType, int nVersion); +template void Unserialize(Stream& is, std::pair& item, int nType, int nVersion); + +// 3 tuple +template unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion); +template void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion); +template void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion); + +// 4 tuple +template unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion); +template void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion); +template void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion); + +// map +template unsigned int GetSerializeSize(const std::map& m, int nType, int nVersion); +template void Serialize(Stream& os, const std::map& m, int nType, int nVersion); +template void Unserialize(Stream& is, std::map& m, int nType, int nVersion); + +// set +template unsigned int GetSerializeSize(const std::set& m, int nType, int nVersion); +template void Serialize(Stream& os, const std::set& m, int nType, int nVersion); +template void Unserialize(Stream& is, std::set& m, int nType, int nVersion); + + + + + +// +// If none of the specialized versions above matched, default to calling member function. +// "int nType" is changed to "long nType" to keep from getting an ambiguous overload error. +// The compiler will only cast int to long if none of the other templates matched. +// Thanks to Boost serialization for this idea. +// +template +inline unsigned int GetSerializeSize(const T& a, long nType, int nVersion) +{ + return a.GetSerializeSize((int)nType, nVersion); +} + +template +inline void Serialize(Stream& os, const T& a, long nType, int nVersion) +{ + a.Serialize(os, (int)nType, nVersion); +} + +template +inline void Unserialize(Stream& is, T& a, long nType, int nVersion) +{ + a.Unserialize(is, (int)nType, nVersion); +} + + + + + +// +// string +// +template +unsigned int GetSerializeSize(const std::basic_string& str, int, int) +{ + return GetSizeOfCompactSize(str.size()) + str.size() * sizeof(str[0]); +} + +template +void Serialize(Stream& os, const std::basic_string& str, int, int) +{ + WriteCompactSize(os, str.size()); + if (!str.empty()) + os.write((char*)&str[0], str.size() * sizeof(str[0])); +} + +template +void Unserialize(Stream& is, std::basic_string& str, int, int) +{ + unsigned int nSize = ReadCompactSize(is); + str.resize(nSize); + if (nSize != 0) + is.read((char*)&str[0], nSize * sizeof(str[0])); +} + + + +// +// vector +// +template +unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::true_type&) +{ + return (GetSizeOfCompactSize(v.size()) + v.size() * sizeof(T)); +} + +template +unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::false_type&) +{ + unsigned int nSize = GetSizeOfCompactSize(v.size()); + for (typename std::vector::const_iterator vi = v.begin(); vi != v.end(); ++vi) + nSize += GetSerializeSize((*vi), nType, nVersion); + return nSize; +} + +template +inline unsigned int GetSerializeSize(const std::vector& v, int nType, int nVersion) +{ + return GetSerializeSize_impl(v, nType, nVersion, boost::is_fundamental()); +} + + +template +void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::true_type&) +{ + WriteCompactSize(os, v.size()); + if (!v.empty()) + os.write((char*)&v[0], v.size() * sizeof(T)); +} + +template +void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::false_type&) +{ + WriteCompactSize(os, v.size()); + for (typename std::vector::const_iterator vi = v.begin(); vi != v.end(); ++vi) + ::Serialize(os, (*vi), nType, nVersion); +} + +template +inline void Serialize(Stream& os, const std::vector& v, int nType, int nVersion) +{ + Serialize_impl(os, v, nType, nVersion, boost::is_fundamental()); +} + + +template +void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::true_type&) +{ + // Limit size per read so bogus size value won't cause out of memory + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + while (i < nSize) + { + unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T))); + v.resize(i + blk); + is.read((char*)&v[i], blk * sizeof(T)); + i += blk; + } +} + +template +void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::false_type&) +{ + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + unsigned int nMid = 0; + while (nMid < nSize) + { + nMid += 5000000 / sizeof(T); + if (nMid > nSize) + nMid = nSize; + v.resize(nMid); + for (; i < nMid; i++) + Unserialize(is, v[i], nType, nVersion); + } +} + +template +inline void Unserialize(Stream& is, std::vector& v, int nType, int nVersion) +{ + Unserialize_impl(is, v, nType, nVersion, boost::is_fundamental()); +} + + + +// +// others derived from vector +// +inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion) +{ + return GetSerializeSize((const std::vector&)v, nType, nVersion); +} + +template +void Serialize(Stream& os, const CScript& v, int nType, int nVersion) +{ + Serialize(os, (const std::vector&)v, nType, nVersion); +} + +template +void Unserialize(Stream& is, CScript& v, int nType, int nVersion) +{ + Unserialize(is, (std::vector&)v, nType, nVersion); +} + + + +// +// pair +// +template +unsigned int GetSerializeSize(const std::pair& item, int nType, int nVersion) +{ + return GetSerializeSize(item.first, nType, nVersion) + GetSerializeSize(item.second, nType, nVersion); +} + +template +void Serialize(Stream& os, const std::pair& item, int nType, int nVersion) +{ + Serialize(os, item.first, nType, nVersion); + Serialize(os, item.second, nType, nVersion); +} + +template +void Unserialize(Stream& is, std::pair& item, int nType, int nVersion) +{ + Unserialize(is, item.first, nType, nVersion); + Unserialize(is, item.second, nType, nVersion); +} + + + +// +// 3 tuple +// +template +unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion) +{ + unsigned int nSize = 0; + nSize += GetSerializeSize(boost::get<0>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<1>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<2>(item), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion) +{ + Serialize(os, boost::get<0>(item), nType, nVersion); + Serialize(os, boost::get<1>(item), nType, nVersion); + Serialize(os, boost::get<2>(item), nType, nVersion); +} + +template +void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion) +{ + Unserialize(is, boost::get<0>(item), nType, nVersion); + Unserialize(is, boost::get<1>(item), nType, nVersion); + Unserialize(is, boost::get<2>(item), nType, nVersion); +} + + + +// +// 4 tuple +// +template +unsigned int GetSerializeSize(const boost::tuple& item, int nType, int nVersion) +{ + unsigned int nSize = 0; + nSize += GetSerializeSize(boost::get<0>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<1>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<2>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<3>(item), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream& os, const boost::tuple& item, int nType, int nVersion) +{ + Serialize(os, boost::get<0>(item), nType, nVersion); + Serialize(os, boost::get<1>(item), nType, nVersion); + Serialize(os, boost::get<2>(item), nType, nVersion); + Serialize(os, boost::get<3>(item), nType, nVersion); +} + +template +void Unserialize(Stream& is, boost::tuple& item, int nType, int nVersion) +{ + Unserialize(is, boost::get<0>(item), nType, nVersion); + Unserialize(is, boost::get<1>(item), nType, nVersion); + Unserialize(is, boost::get<2>(item), nType, nVersion); + Unserialize(is, boost::get<3>(item), nType, nVersion); +} + + + +// +// map +// +template +unsigned int GetSerializeSize(const std::map& m, int nType, int nVersion) +{ + unsigned int nSize = GetSizeOfCompactSize(m.size()); + for (typename std::map::const_iterator mi = m.begin(); mi != m.end(); ++mi) + nSize += GetSerializeSize((*mi), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream& os, const std::map& m, int nType, int nVersion) +{ + WriteCompactSize(os, m.size()); + for (typename std::map::const_iterator mi = m.begin(); mi != m.end(); ++mi) + Serialize(os, (*mi), nType, nVersion); +} + +template +void Unserialize(Stream& is, std::map& m, int nType, int nVersion) +{ + m.clear(); + unsigned int nSize = ReadCompactSize(is); + typename std::map::iterator mi = m.begin(); + for (unsigned int i = 0; i < nSize; i++) + { + std::pair item; + Unserialize(is, item, nType, nVersion); + mi = m.insert(mi, item); + } +} + + + +// +// set +// +template +unsigned int GetSerializeSize(const std::set& m, int nType, int nVersion) +{ + unsigned int nSize = GetSizeOfCompactSize(m.size()); + for (typename std::set::const_iterator it = m.begin(); it != m.end(); ++it) + nSize += GetSerializeSize((*it), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream& os, const std::set& m, int nType, int nVersion) +{ + WriteCompactSize(os, m.size()); + for (typename std::set::const_iterator it = m.begin(); it != m.end(); ++it) + Serialize(os, (*it), nType, nVersion); +} + +template +void Unserialize(Stream& is, std::set& m, int nType, int nVersion) +{ + m.clear(); + unsigned int nSize = ReadCompactSize(is); + typename std::set::iterator it = m.begin(); + for (unsigned int i = 0; i < nSize; i++) + { + K key; + Unserialize(is, key, nType, nVersion); + it = m.insert(it, key); + } +} + + + +// +// Support for IMPLEMENT_SERIALIZE and READWRITE macro +// +class CSerActionGetSerializeSize { }; +class CSerActionSerialize { }; +class CSerActionUnserialize { }; + +template +inline unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionGetSerializeSize ser_action) +{ + return ::GetSerializeSize(obj, nType, nVersion); +} + +template +inline unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionSerialize ser_action) +{ + ::Serialize(s, obj, nType, nVersion); + return 0; +} + +template +inline unsigned int SerReadWrite(Stream& s, T& obj, int nType, int nVersion, CSerActionUnserialize ser_action) +{ + ::Unserialize(s, obj, nType, nVersion); + return 0; +} + +struct ser_streamplaceholder +{ + int nType; + int nVersion; +}; + + + + + + + + + + + +typedef std::vector > CSerializeData; + +/** Double ended buffer combining vector and stream-like interfaces. + * + * >> and << read and write unformatted data using the above serialization templates. + * Fills with data in linear time; some stringstream implementations take N^2 time. + */ +class CDataStream +{ +protected: + typedef CSerializeData vector_type; + vector_type vch; + unsigned int nReadPos; + short state; + short exceptmask; +public: + int nType; + int nVersion; + + typedef vector_type::allocator_type allocator_type; + typedef vector_type::size_type size_type; + typedef vector_type::difference_type difference_type; + typedef vector_type::reference reference; + typedef vector_type::const_reference const_reference; + typedef vector_type::value_type value_type; + typedef vector_type::iterator iterator; + typedef vector_type::const_iterator const_iterator; + typedef vector_type::reverse_iterator reverse_iterator; + + explicit CDataStream(int nTypeIn, int nVersionIn) + { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const_iterator pbegin, const_iterator pend, int nTypeIn, int nVersionIn) : vch(pbegin, pend) + { + Init(nTypeIn, nVersionIn); + } + +#if !defined(_MSC_VER) || _MSC_VER >= 1300 + CDataStream(const char* pbegin, const char* pend, int nTypeIn, int nVersionIn) : vch(pbegin, pend) + { + Init(nTypeIn, nVersionIn); + } +#endif + + CDataStream(const vector_type& vchIn, int nTypeIn, int nVersionIn) : vch(vchIn.begin(), vchIn.end()) + { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const std::vector& vchIn, int nTypeIn, int nVersionIn) : vch(vchIn.begin(), vchIn.end()) + { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const std::vector& vchIn, int nTypeIn, int nVersionIn) : vch((char*)&vchIn.begin()[0], (char*)&vchIn.end()[0]) + { + Init(nTypeIn, nVersionIn); + } + + void Init(int nTypeIn, int nVersionIn) + { + nReadPos = 0; + nType = nTypeIn; + nVersion = nVersionIn; + state = 0; + exceptmask = std::ios::badbit | std::ios::failbit; + } + + CDataStream& operator+=(const CDataStream& b) + { + vch.insert(vch.end(), b.begin(), b.end()); + return *this; + } + + friend CDataStream operator+(const CDataStream& a, const CDataStream& b) + { + CDataStream ret = a; + ret += b; + return (ret); + } + + std::string str() const + { + return (std::string(begin(), end())); + } + + + // + // Vector subset + // + const_iterator begin() const { return vch.begin() + nReadPos; } + iterator begin() { return vch.begin() + nReadPos; } + const_iterator end() const { return vch.end(); } + iterator end() { return vch.end(); } + size_type size() const { return vch.size() - nReadPos; } + bool empty() const { return vch.size() == nReadPos; } + void resize(size_type n, value_type c=0) { vch.resize(n + nReadPos, c); } + void reserve(size_type n) { vch.reserve(n + nReadPos); } + const_reference operator[](size_type pos) const { return vch[pos + nReadPos]; } + reference operator[](size_type pos) { return vch[pos + nReadPos]; } + void clear() { vch.clear(); nReadPos = 0; } + iterator insert(iterator it, const char& x=char()) { return vch.insert(it, x); } + void insert(iterator it, size_type n, const char& x) { vch.insert(it, n, x); } + + void insert(iterator it, std::vector::const_iterator first, std::vector::const_iterator last) + { + assert(last - first >= 0); + if (it == vch.begin() + nReadPos && (unsigned int)(last - first) <= nReadPos) + { + // special case for inserting at the front when there's room + nReadPos -= (last - first); + memcpy(&vch[nReadPos], &first[0], last - first); + } + else + vch.insert(it, first, last); + } + +#if !defined(_MSC_VER) || _MSC_VER >= 1300 + void insert(iterator it, const char* first, const char* last) + { + assert(last - first >= 0); + if (it == vch.begin() + nReadPos && (unsigned int)(last - first) <= nReadPos) + { + // special case for inserting at the front when there's room + nReadPos -= (last - first); + memcpy(&vch[nReadPos], &first[0], last - first); + } + else + vch.insert(it, first, last); + } +#endif + + iterator erase(iterator it) + { + if (it == vch.begin() + nReadPos) + { + // special case for erasing from the front + if (++nReadPos >= vch.size()) + { + // whenever we reach the end, we take the opportunity to clear the buffer + nReadPos = 0; + return vch.erase(vch.begin(), vch.end()); + } + return vch.begin() + nReadPos; + } + else + return vch.erase(it); + } + + iterator erase(iterator first, iterator last) + { + if (first == vch.begin() + nReadPos) + { + // special case for erasing from the front + if (last == vch.end()) + { + nReadPos = 0; + return vch.erase(vch.begin(), vch.end()); + } + else + { + nReadPos = (last - vch.begin()); + return last; + } + } + else + return vch.erase(first, last); + } + + inline void Compact() + { + vch.erase(vch.begin(), vch.begin() + nReadPos); + nReadPos = 0; + } + + bool Rewind(size_type n) + { + // Rewind by n characters if the buffer hasn't been compacted yet + if (n > nReadPos) + return false; + nReadPos -= n; + return true; + } + + + // + // Stream subset + // + void setstate(short bits, const char* psz) + { + state |= bits; + if (state & exceptmask) + throw std::ios_base::failure(psz); + } + + bool eof() const { return size() == 0; } + bool fail() const { return state & (std::ios::badbit | std::ios::failbit); } + bool good() const { return !eof() && (state == 0); } + void clear(short n) { state = n; } // name conflict with vector clear() + short exceptions() { return exceptmask; } + short exceptions(short mask) { short prev = exceptmask; exceptmask = mask; setstate(0, "CDataStream"); return prev; } + CDataStream* rdbuf() { return this; } + int in_avail() { return size(); } + + void SetType(int n) { nType = n; } + int GetType() { return nType; } + void SetVersion(int n) { nVersion = n; } + int GetVersion() { return nVersion; } + void ReadVersion() { *this >> nVersion; } + void WriteVersion() { *this << nVersion; } + + CDataStream& read(char* pch, int nSize) + { + // Read from the beginning of the buffer + assert(nSize >= 0); + unsigned int nReadPosNext = nReadPos + nSize; + if (nReadPosNext >= vch.size()) + { + if (nReadPosNext > vch.size()) + { + setstate(std::ios::failbit, "CDataStream::read() : end of data"); + memset(pch, 0, nSize); + nSize = vch.size() - nReadPos; + } + memcpy(pch, &vch[nReadPos], nSize); + nReadPos = 0; + vch.clear(); + return (*this); + } + memcpy(pch, &vch[nReadPos], nSize); + nReadPos = nReadPosNext; + return (*this); + } + + CDataStream& ignore(int nSize) + { + // Ignore from the beginning of the buffer + assert(nSize >= 0); + unsigned int nReadPosNext = nReadPos + nSize; + if (nReadPosNext >= vch.size()) + { + if (nReadPosNext > vch.size()) + { + setstate(std::ios::failbit, "CDataStream::ignore() : end of data"); + nSize = vch.size() - nReadPos; + } + nReadPos = 0; + vch.clear(); + return (*this); + } + nReadPos = nReadPosNext; + return (*this); + } + + CDataStream& write(const char* pch, int nSize) + { + // Write to the end of the buffer + assert(nSize >= 0); + vch.insert(vch.end(), pch, pch + nSize); + return (*this); + } + + template + void Serialize(Stream& s, int nType, int nVersion) const + { + // Special case: stream << stream concatenates like stream += stream + if (!vch.empty()) + s.write((char*)&vch[0], vch.size() * sizeof(vch[0])); + } + + template + unsigned int GetSerializeSize(const T& obj) + { + // Tells the size of the object if serialized to this stream + return ::GetSerializeSize(obj, nType, nVersion); + } + + template + CDataStream& operator<<(const T& obj) + { + // Serialize to this stream + ::Serialize(*this, obj, nType, nVersion); + return (*this); + } + + template + CDataStream& operator>>(T& obj) + { + // Unserialize from this stream + ::Unserialize(*this, obj, nType, nVersion); + return (*this); + } + + void GetAndClear(CSerializeData &data) { + vch.swap(data); + CSerializeData().swap(vch); + } +}; + + + + + + + + + + +/** RAII wrapper for FILE*. + * + * Will automatically close the file when it goes out of scope if not null. + * If you're returning the file pointer, return file.release(). + * If you need to close the file early, use file.fclose() instead of fclose(file). + */ +class CAutoFile +{ +protected: + FILE* file; + short state; + short exceptmask; +public: + int nType; + int nVersion; + + CAutoFile(FILE* filenew, int nTypeIn, int nVersionIn) + { + file = filenew; + nType = nTypeIn; + nVersion = nVersionIn; + state = 0; + exceptmask = std::ios::badbit | std::ios::failbit; + } + + ~CAutoFile() + { + fclose(); + } + + void fclose() + { + if (file != NULL && file != stdin && file != stdout && file != stderr) + ::fclose(file); + file = NULL; + } + + FILE* release() { FILE* ret = file; file = NULL; return ret; } + operator FILE*() { return file; } + FILE* operator->() { return file; } + FILE& operator*() { return *file; } + FILE** operator&() { return &file; } + FILE* operator=(FILE* pnew) { return file = pnew; } + bool operator!() { return (file == NULL); } + + + // + // Stream subset + // + void setstate(short bits, const char* psz) + { + state |= bits; + if (state & exceptmask) + throw std::ios_base::failure(psz); + } + + bool fail() const { return state & (std::ios::badbit | std::ios::failbit); } + bool good() const { return state == 0; } + void clear(short n = 0) { state = n; } + short exceptions() { return exceptmask; } + short exceptions(short mask) { short prev = exceptmask; exceptmask = mask; setstate(0, "CAutoFile"); return prev; } + + void SetType(int n) { nType = n; } + int GetType() { return nType; } + void SetVersion(int n) { nVersion = n; } + int GetVersion() { return nVersion; } + void ReadVersion() { *this >> nVersion; } + void WriteVersion() { *this << nVersion; } + + CAutoFile& read(char* pch, size_t nSize) + { + if (!file) + throw std::ios_base::failure("CAutoFile::read : file handle is NULL"); + if (fread(pch, 1, nSize, file) != nSize) + setstate(std::ios::failbit, feof(file) ? "CAutoFile::read : end of file" : "CAutoFile::read : fread failed"); + return (*this); + } + + CAutoFile& write(const char* pch, size_t nSize) + { + if (!file) + throw std::ios_base::failure("CAutoFile::write : file handle is NULL"); + if (fwrite(pch, 1, nSize, file) != nSize) + setstate(std::ios::failbit, "CAutoFile::write : write failed"); + return (*this); + } + + template + unsigned int GetSerializeSize(const T& obj) + { + // Tells the size of the object if serialized to this stream + return ::GetSerializeSize(obj, nType, nVersion); + } + + template + CAutoFile& operator<<(const T& obj) + { + // Serialize to this stream + if (!file) + throw std::ios_base::failure("CAutoFile::operator<< : file handle is NULL"); + ::Serialize(*this, obj, nType, nVersion); + return (*this); + } + + template + CAutoFile& operator>>(T& obj) + { + // Unserialize from this stream + if (!file) + throw std::ios_base::failure("CAutoFile::operator>> : file handle is NULL"); + ::Unserialize(*this, obj, nType, nVersion); + return (*this); + } +}; + +/** Wrapper around a FILE* that implements a ring buffer to + * deserialize from. It guarantees the ability to rewind + * a given number of bytes. */ +class CBufferedFile +{ +private: + FILE *src; // source file + uint64 nSrcPos; // how many bytes have been read from source + uint64 nReadPos; // how many bytes have been read from this + uint64 nReadLimit; // up to which position we're allowed to read + uint64 nRewind; // how many bytes we guarantee to rewind + std::vector vchBuf; // the buffer + + short state; + short exceptmask; + +protected: + void setstate(short bits, const char *psz) { + state |= bits; + if (state & exceptmask) + throw std::ios_base::failure(psz); + } + + // read data from the source to fill the buffer + bool Fill() { + unsigned int pos = nSrcPos % vchBuf.size(); + unsigned int readNow = vchBuf.size() - pos; + unsigned int nAvail = vchBuf.size() - (nSrcPos - nReadPos) - nRewind; + if (nAvail < readNow) + readNow = nAvail; + if (readNow == 0) + return false; + size_t read = fread((void*)&vchBuf[pos], 1, readNow, src); + if (read == 0) { + setstate(std::ios_base::failbit, feof(src) ? "CBufferedFile::Fill : end of file" : "CBufferedFile::Fill : fread failed"); + return false; + } else { + nSrcPos += read; + return true; + } + } + +public: + int nType; + int nVersion; + + CBufferedFile(FILE *fileIn, uint64 nBufSize, uint64 nRewindIn, int nTypeIn, int nVersionIn) : + src(fileIn), nSrcPos(0), nReadPos(0), nReadLimit((uint64)(-1)), nRewind(nRewindIn), vchBuf(nBufSize, 0), + state(0), exceptmask(std::ios_base::badbit | std::ios_base::failbit), nType(nTypeIn), nVersion(nVersionIn) { + } + + // check whether no error occurred + bool good() const { + return state == 0; + } + + // check whether we're at the end of the source file + bool eof() const { + return nReadPos == nSrcPos && feof(src); + } + + // read a number of bytes + CBufferedFile& read(char *pch, size_t nSize) { + if (nSize + nReadPos > nReadLimit) + throw std::ios_base::failure("Read attempted past buffer limit"); + if (nSize + nRewind > vchBuf.size()) + throw std::ios_base::failure("Read larger than buffer size"); + while (nSize > 0) { + if (nReadPos == nSrcPos) + Fill(); + unsigned int pos = nReadPos % vchBuf.size(); + size_t nNow = nSize; + if (nNow + pos > vchBuf.size()) + nNow = vchBuf.size() - pos; + if (nNow + nReadPos > nSrcPos) + nNow = nSrcPos - nReadPos; + memcpy(pch, &vchBuf[pos], nNow); + nReadPos += nNow; + pch += nNow; + nSize -= nNow; + } + return (*this); + } + + // return the current reading position + uint64 GetPos() { + return nReadPos; + } + + // rewind to a given reading position + bool SetPos(uint64 nPos) { + nReadPos = nPos; + if (nReadPos + nRewind < nSrcPos) { + nReadPos = nSrcPos - nRewind; + return false; + } else if (nReadPos > nSrcPos) { + nReadPos = nSrcPos; + return false; + } else { + return true; + } + } + + bool Seek(uint64 nPos) { + long nLongPos = nPos; + if (nPos != (uint64)nLongPos) + return false; + if (fseek(src, nLongPos, SEEK_SET)) + return false; + nLongPos = ftell(src); + nSrcPos = nLongPos; + nReadPos = nLongPos; + state = 0; + return true; + } + + // prevent reading beyond a certain position + // no argument removes the limit + bool SetLimit(uint64 nPos = (uint64)(-1)) { + if (nPos < nReadPos) + return false; + nReadLimit = nPos; + return true; + } + + template + CBufferedFile& operator>>(T& obj) { + // Unserialize from this stream + ::Unserialize(*this, obj, nType, nVersion); + return (*this); + } + + // search for a given byte in the stream, and remain positioned on it + void FindByte(char ch) { + while (true) { + if (nReadPos == nSrcPos) + Fill(); + if (vchBuf[nReadPos % vchBuf.size()] == ch) + break; + nReadPos++; + } + } +}; + +#endif diff --git a/src/sync.cpp b/src/sync.cpp new file mode 100644 index 0000000..1ac4403 --- /dev/null +++ b/src/sync.cpp @@ -0,0 +1,128 @@ +// Copyright (c) 2011-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "sync.h" +#include "util.h" + +#include + +#ifdef DEBUG_LOCKCONTENTION +void PrintLockContention(const char* pszName, const char* pszFile, int nLine) +{ + printf("LOCKCONTENTION: %s\n", pszName); + printf("Locker: %s:%d\n", pszFile, nLine); +} +#endif /* DEBUG_LOCKCONTENTION */ + +#ifdef DEBUG_LOCKORDER +// +// Early deadlock detection. +// Problem being solved: +// Thread 1 locks A, then B, then C +// Thread 2 locks D, then C, then A +// --> may result in deadlock between the two threads, depending on when they run. +// Solution implemented here: +// Keep track of pairs of locks: (A before B), (A before C), etc. +// Complain if any thread tries to lock in a different order. +// + +struct CLockLocation +{ + CLockLocation(const char* pszName, const char* pszFile, int nLine) + { + mutexName = pszName; + sourceFile = pszFile; + sourceLine = nLine; + } + + std::string ToString() const + { + return mutexName+" "+sourceFile+":"+itostr(sourceLine); + } + +private: + std::string mutexName; + std::string sourceFile; + int sourceLine; +}; + +typedef std::vector< std::pair > LockStack; + +static boost::mutex dd_mutex; +static std::map, LockStack> lockorders; +static boost::thread_specific_ptr lockstack; + + +static void potential_deadlock_detected(const std::pair& mismatch, const LockStack& s1, const LockStack& s2) +{ + printf("POTENTIAL DEADLOCK DETECTED\n"); + printf("Previous lock order was:\n"); + BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, s2) + { + if (i.first == mismatch.first) printf(" (1)"); + if (i.first == mismatch.second) printf(" (2)"); + printf(" %s\n", i.second.ToString().c_str()); + } + printf("Current lock order is:\n"); + BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, s1) + { + if (i.first == mismatch.first) printf(" (1)"); + if (i.first == mismatch.second) printf(" (2)"); + printf(" %s\n", i.second.ToString().c_str()); + } +} + +static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) +{ + if (lockstack.get() == NULL) + lockstack.reset(new LockStack); + + if (fDebug) printf("Locking: %s\n", locklocation.ToString().c_str()); + dd_mutex.lock(); + + (*lockstack).push_back(std::make_pair(c, locklocation)); + + if (!fTry) { + BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, (*lockstack)) { + if (i.first == c) break; + + std::pair p1 = std::make_pair(i.first, c); + if (lockorders.count(p1)) + continue; + lockorders[p1] = (*lockstack); + + std::pair p2 = std::make_pair(c, i.first); + if (lockorders.count(p2)) + { + potential_deadlock_detected(p1, lockorders[p2], lockorders[p1]); + break; + } + } + } + dd_mutex.unlock(); +} + +static void pop_lock() +{ + if (fDebug) + { + const CLockLocation& locklocation = (*lockstack).rbegin()->second; + printf("Unlocked: %s\n", locklocation.ToString().c_str()); + } + dd_mutex.lock(); + (*lockstack).pop_back(); + dd_mutex.unlock(); +} + +void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry) +{ + push_lock(cs, CLockLocation(pszName, pszFile, nLine), fTry); +} + +void LeaveCritical() +{ + pop_lock(); +} + +#endif /* DEBUG_LOCKORDER */ diff --git a/src/sync.h b/src/sync.h new file mode 100644 index 0000000..930c9b2 --- /dev/null +++ b/src/sync.h @@ -0,0 +1,213 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_SYNC_H +#define BITCOIN_SYNC_H + +#include +#include +#include +#include +#include "threadsafety.h" + +// Template mixin that adds -Wthread-safety locking annotations to a +// subset of the mutex API. +template +class LOCKABLE AnnotatedMixin : public PARENT +{ +public: + void lock() EXCLUSIVE_LOCK_FUNCTION() + { + PARENT::lock(); + } + + void unlock() UNLOCK_FUNCTION() + { + PARENT::unlock(); + } + + bool try_lock() EXCLUSIVE_TRYLOCK_FUNCTION(true) + { + return PARENT::try_lock(); + } +}; + +/** Wrapped boost mutex: supports recursive locking, but no waiting */ +// TODO: We should move away from using the recursive lock by default. +typedef AnnotatedMixin CCriticalSection; + +/** Wrapped boost mutex: supports waiting but not recursive locking */ +typedef AnnotatedMixin CWaitableCriticalSection; + +#ifdef DEBUG_LOCKORDER +void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false); +void LeaveCritical(); +#else +void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {} +void static inline LeaveCritical() {} +#endif + +#ifdef DEBUG_LOCKCONTENTION +void PrintLockContention(const char* pszName, const char* pszFile, int nLine); +#endif + +/** Wrapper around boost::unique_lock */ +template +class CMutexLock +{ +private: + boost::unique_lock lock; + + void Enter(const char* pszName, const char* pszFile, int nLine) + { + EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex())); +#ifdef DEBUG_LOCKCONTENTION + if (!lock.try_lock()) + { + PrintLockContention(pszName, pszFile, nLine); +#endif + lock.lock(); +#ifdef DEBUG_LOCKCONTENTION + } +#endif + } + + bool TryEnter(const char* pszName, const char* pszFile, int nLine) + { + EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex()), true); + lock.try_lock(); + if (!lock.owns_lock()) + LeaveCritical(); + return lock.owns_lock(); + } + +public: + CMutexLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) : lock(mutexIn, boost::defer_lock) + { + if (fTry) + TryEnter(pszName, pszFile, nLine); + else + Enter(pszName, pszFile, nLine); + } + + ~CMutexLock() + { + if (lock.owns_lock()) + LeaveCritical(); + } + + operator bool() + { + return lock.owns_lock(); + } +}; + +typedef CMutexLock CCriticalBlock; + +#define LOCK(cs) CCriticalBlock criticalblock(cs, #cs, __FILE__, __LINE__) +#define LOCK2(cs1,cs2) CCriticalBlock criticalblock1(cs1, #cs1, __FILE__, __LINE__),criticalblock2(cs2, #cs2, __FILE__, __LINE__) +#define TRY_LOCK(cs,name) CCriticalBlock name(cs, #cs, __FILE__, __LINE__, true) + +#define ENTER_CRITICAL_SECTION(cs) \ + { \ + EnterCritical(#cs, __FILE__, __LINE__, (void*)(&cs)); \ + (cs).lock(); \ + } + +#define LEAVE_CRITICAL_SECTION(cs) \ + { \ + (cs).unlock(); \ + LeaveCritical(); \ + } + +class CSemaphore +{ +private: + boost::condition_variable condition; + boost::mutex mutex; + int value; + +public: + CSemaphore(int init) : value(init) {} + + void wait() { + boost::unique_lock lock(mutex); + while (value < 1) { + condition.wait(lock); + } + value--; + } + + bool try_wait() { + boost::unique_lock lock(mutex); + if (value < 1) + return false; + value--; + return true; + } + + void post() { + { + boost::unique_lock lock(mutex); + value++; + } + condition.notify_one(); + } +}; + +/** RAII-style semaphore lock */ +class CSemaphoreGrant +{ +private: + CSemaphore *sem; + bool fHaveGrant; + +public: + void Acquire() { + if (fHaveGrant) + return; + sem->wait(); + fHaveGrant = true; + } + + void Release() { + if (!fHaveGrant) + return; + sem->post(); + fHaveGrant = false; + } + + bool TryAcquire() { + if (!fHaveGrant && sem->try_wait()) + fHaveGrant = true; + return fHaveGrant; + } + + void MoveTo(CSemaphoreGrant &grant) { + grant.Release(); + grant.sem = sem; + grant.fHaveGrant = fHaveGrant; + sem = NULL; + fHaveGrant = false; + } + + CSemaphoreGrant() : sem(NULL), fHaveGrant(false) {} + + CSemaphoreGrant(CSemaphore &sema, bool fTry = false) : sem(&sema), fHaveGrant(false) { + if (fTry) + TryAcquire(); + else + Acquire(); + } + + ~CSemaphoreGrant() { + Release(); + } + + operator bool() { + return fHaveGrant; + } +}; +#endif + diff --git a/src/test/Checkpoints_tests.cpp b/src/test/Checkpoints_tests.cpp new file mode 100644 index 0000000..36a9873 --- /dev/null +++ b/src/test/Checkpoints_tests.cpp @@ -0,0 +1,34 @@ +// +// Unit tests for block-chain checkpoints +// +#include // for 'map_list_of()' +#include +#include + +#include "../checkpoints.h" +#include "../util.h" + +using namespace std; + +BOOST_AUTO_TEST_SUITE(Checkpoints_tests) + +BOOST_AUTO_TEST_CASE(sanity) +{ + uint256 p1500 = uint256("0x841a2965955dd288cfa707a755d05a54e45f8bd476835ec9af4402a2b59a2967"); + uint256 p120000 = uint256("0xbd9d26924f05f6daa7f0155f32828ec89e8e29cee9e7121b026a7a3552ac6131"); + BOOST_CHECK(Checkpoints::CheckBlock(1500, p1500)); + BOOST_CHECK(Checkpoints::CheckBlock(120000, p120000)); + + + // Wrong hashes at checkpoints should fail: + BOOST_CHECK(!Checkpoints::CheckBlock(1500, p120000)); + BOOST_CHECK(!Checkpoints::CheckBlock(120000, p1500)); + + // ... but any hash not at a checkpoint should succeed: + BOOST_CHECK(Checkpoints::CheckBlock(1500+1, p120000)); + BOOST_CHECK(Checkpoints::CheckBlock(120000+1, p1500)); + + BOOST_CHECK(Checkpoints::GetTotalBlocksEstimate() >= 120000); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp new file mode 100644 index 0000000..2ee6475 --- /dev/null +++ b/src/test/DoS_tests.cpp @@ -0,0 +1,303 @@ +// +// Unit tests for denial-of-service detection/prevention code +// +#include + +#include // for 'map_list_of()' +#include +#include +#include + +#include "main.h" +#include "wallet.h" +#include "net.h" +#include "util.h" + +#include + +// Tests this internal-to-main.cpp method: +extern bool AddOrphanTx(const CTransaction& tx); +extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans); +extern std::map mapOrphanTransactions; +extern std::map > mapOrphanTransactionsByPrev; + +CService ip(uint32_t i) +{ + struct in_addr s; + s.s_addr = i; + return CService(CNetAddr(s), GetDefaultPort()); +} + +BOOST_AUTO_TEST_SUITE(DoS_tests) + +BOOST_AUTO_TEST_CASE(DoS_banning) +{ + CNode::ClearBanned(); + CAddress addr1(ip(0xa0b0c001)); + CNode dummyNode1(INVALID_SOCKET, addr1, "", true); + dummyNode1.Misbehaving(100); // Should get banned + BOOST_CHECK(CNode::IsBanned(addr1)); + BOOST_CHECK(!CNode::IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned + + CAddress addr2(ip(0xa0b0c002)); + CNode dummyNode2(INVALID_SOCKET, addr2, "", true); + dummyNode2.Misbehaving(50); + BOOST_CHECK(!CNode::IsBanned(addr2)); // 2 not banned yet... + BOOST_CHECK(CNode::IsBanned(addr1)); // ... but 1 still should be + dummyNode2.Misbehaving(50); + BOOST_CHECK(CNode::IsBanned(addr2)); +} + +BOOST_AUTO_TEST_CASE(DoS_banscore) +{ + CNode::ClearBanned(); + mapArgs["-banscore"] = "111"; // because 11 is my favorite number + CAddress addr1(ip(0xa0b0c001)); + CNode dummyNode1(INVALID_SOCKET, addr1, "", true); + dummyNode1.Misbehaving(100); + BOOST_CHECK(!CNode::IsBanned(addr1)); + dummyNode1.Misbehaving(10); + BOOST_CHECK(!CNode::IsBanned(addr1)); + dummyNode1.Misbehaving(1); + BOOST_CHECK(CNode::IsBanned(addr1)); + mapArgs.erase("-banscore"); +} + +BOOST_AUTO_TEST_CASE(DoS_bantime) +{ + CNode::ClearBanned(); + int64 nStartTime = GetTime(); + SetMockTime(nStartTime); // Overrides future calls to GetTime() + + CAddress addr(ip(0xa0b0c001)); + CNode dummyNode(INVALID_SOCKET, addr, "", true); + + dummyNode.Misbehaving(100); + BOOST_CHECK(CNode::IsBanned(addr)); + + SetMockTime(nStartTime+60*60); + BOOST_CHECK(CNode::IsBanned(addr)); + + SetMockTime(nStartTime+60*60*24+1); + BOOST_CHECK(!CNode::IsBanned(addr)); +} + +static bool CheckNBits(unsigned int nbits1, int64 time1, unsigned int nbits2, int64 time2)\ +{ + if (time1 > time2) + return CheckNBits(nbits2, time2, nbits1, time1); + int64 deltaTime = time2-time1; + + CBigNum required; + required.SetCompact(ComputeMinWork(nbits1, deltaTime)); + CBigNum have; + have.SetCompact(nbits2); + return (have <= required); +} + +BOOST_AUTO_TEST_CASE(DoS_checknbits) +{ + using namespace boost::assign; // for 'map_list_of()' + + // Timestamps,nBits from the bitcoin block chain. + // These are the block-chain checkpoint blocks + typedef std::map BlockData; + BlockData chainData = + map_list_of(1239852051,486604799)(1262749024,486594666) + (1279305360,469854461)(1280200847,469830746)(1281678674,469809688) + (1296207707,453179945)(1302624061,453036989)(1309640330,437004818) + (1313172719,436789733); + + // Make sure CheckNBits considers every combination of block-chain-lock-in-points + // "sane": + BOOST_FOREACH(const BlockData::value_type& i, chainData) + { + BOOST_FOREACH(const BlockData::value_type& j, chainData) + { + BOOST_CHECK(CheckNBits(i.second, i.first, j.second, j.first)); + } + } + + // Test a couple of insane combinations: + BlockData::value_type firstcheck = *(chainData.begin()); + BlockData::value_type lastcheck = *(chainData.rbegin()); + + // First checkpoint difficulty at or a while after the last checkpoint time should fail when + // compared to last checkpoint + BOOST_CHECK(!CheckNBits(firstcheck.second, lastcheck.first+60*10, lastcheck.second, lastcheck.first)); + BOOST_CHECK(!CheckNBits(firstcheck.second, lastcheck.first+60*60*24*14, lastcheck.second, lastcheck.first)); + + // ... but OK if enough time passed for difficulty to adjust downward: + BOOST_CHECK(CheckNBits(firstcheck.second, lastcheck.first+60*60*24*365*4, lastcheck.second, lastcheck.first)); +} + +CTransaction RandomOrphan() +{ + std::map::iterator it; + it = mapOrphanTransactions.lower_bound(GetRandHash()); + if (it == mapOrphanTransactions.end()) + it = mapOrphanTransactions.begin(); + return it->second; +} + +BOOST_AUTO_TEST_CASE(DoS_mapOrphans) +{ + CKey key; + key.MakeNewKey(true); + CBasicKeyStore keystore; + keystore.AddKey(key); + + // 50 orphan transactions: + for (int i = 0; i < 50; i++) + { + CTransaction tx; + tx.vin.resize(1); + tx.vin[0].prevout.n = 0; + tx.vin[0].prevout.hash = GetRandHash(); + tx.vin[0].scriptSig << OP_1; + tx.vout.resize(1); + tx.vout[0].nValue = 1*CENT; + tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID()); + + AddOrphanTx(tx); + } + + // ... and 50 that depend on other orphans: + for (int i = 0; i < 50; i++) + { + CTransaction txPrev = RandomOrphan(); + + CTransaction tx; + tx.vin.resize(1); + tx.vin[0].prevout.n = 0; + tx.vin[0].prevout.hash = txPrev.GetHash(); + tx.vout.resize(1); + tx.vout[0].nValue = 1*CENT; + tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID()); + SignSignature(keystore, txPrev, tx, 0); + + AddOrphanTx(tx); + } + + // This really-big orphan should be ignored: + for (int i = 0; i < 10; i++) + { + CTransaction txPrev = RandomOrphan(); + + CTransaction tx; + tx.vout.resize(1); + tx.vout[0].nValue = 1*CENT; + tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID()); + tx.vin.resize(500); + for (unsigned int j = 0; j < tx.vin.size(); j++) + { + tx.vin[j].prevout.n = j; + tx.vin[j].prevout.hash = txPrev.GetHash(); + } + SignSignature(keystore, txPrev, tx, 0); + // Re-use same signature for other inputs + // (they don't have to be valid for this test) + for (unsigned int j = 1; j < tx.vin.size(); j++) + tx.vin[j].scriptSig = tx.vin[0].scriptSig; + + BOOST_CHECK(!AddOrphanTx(tx)); + } + + // Test LimitOrphanTxSize() function: + LimitOrphanTxSize(40); + BOOST_CHECK(mapOrphanTransactions.size() <= 40); + LimitOrphanTxSize(10); + BOOST_CHECK(mapOrphanTransactions.size() <= 10); + LimitOrphanTxSize(0); + BOOST_CHECK(mapOrphanTransactions.empty()); + BOOST_CHECK(mapOrphanTransactionsByPrev.empty()); +} + +BOOST_AUTO_TEST_CASE(DoS_checkSig) +{ + // Test signature caching code (see key.cpp Verify() methods) + + CKey key; + key.MakeNewKey(true); + CBasicKeyStore keystore; + keystore.AddKey(key); + unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC; + + // 100 orphan transactions: + static const int NPREV=100; + CTransaction orphans[NPREV]; + for (int i = 0; i < NPREV; i++) + { + CTransaction& tx = orphans[i]; + tx.vin.resize(1); + tx.vin[0].prevout.n = 0; + tx.vin[0].prevout.hash = GetRandHash(); + tx.vin[0].scriptSig << OP_1; + tx.vout.resize(1); + tx.vout[0].nValue = 1*CENT; + tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID()); + + AddOrphanTx(tx); + } + + // Create a transaction that depends on orphans: + CTransaction tx; + tx.vout.resize(1); + tx.vout[0].nValue = 1*CENT; + tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID()); + tx.vin.resize(NPREV); + for (unsigned int j = 0; j < tx.vin.size(); j++) + { + tx.vin[j].prevout.n = 0; + tx.vin[j].prevout.hash = orphans[j].GetHash(); + } + // Creating signatures primes the cache: + boost::posix_time::ptime mst1 = boost::posix_time::microsec_clock::local_time(); + for (unsigned int j = 0; j < tx.vin.size(); j++) + BOOST_CHECK(SignSignature(keystore, orphans[j], tx, j)); + boost::posix_time::ptime mst2 = boost::posix_time::microsec_clock::local_time(); + boost::posix_time::time_duration msdiff = mst2 - mst1; + long nOneValidate = msdiff.total_milliseconds(); + if (fDebug) printf("DoS_Checksig sign: %ld\n", nOneValidate); + + // ... now validating repeatedly should be quick: + // 2.8GHz machine, -g build: Sign takes ~760ms, + // uncached Verify takes ~250ms, cached Verify takes ~50ms + // (for 100 single-signature inputs) + mst1 = boost::posix_time::microsec_clock::local_time(); + for (unsigned int i = 0; i < 5; i++) + for (unsigned int j = 0; j < tx.vin.size(); j++) + BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, flags, SIGHASH_ALL)); + mst2 = boost::posix_time::microsec_clock::local_time(); + msdiff = mst2 - mst1; + long nManyValidate = msdiff.total_milliseconds(); + if (fDebug) printf("DoS_Checksig five: %ld\n", nManyValidate); + + BOOST_CHECK_MESSAGE(nManyValidate < nOneValidate, "Signature cache timing failed"); + + // Empty a signature, validation should fail: + CScript save = tx.vin[0].scriptSig; + tx.vin[0].scriptSig = CScript(); + BOOST_CHECK(!VerifySignature(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, flags, SIGHASH_ALL)); + tx.vin[0].scriptSig = save; + + // Swap signatures, validation should fail: + std::swap(tx.vin[0].scriptSig, tx.vin[1].scriptSig); + BOOST_CHECK(!VerifySignature(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, flags, SIGHASH_ALL)); + BOOST_CHECK(!VerifySignature(CCoins(orphans[1], MEMPOOL_HEIGHT), tx, 1, flags, SIGHASH_ALL)); + std::swap(tx.vin[0].scriptSig, tx.vin[1].scriptSig); + + // Exercise -maxsigcachesize code: + mapArgs["-maxsigcachesize"] = "10"; + // Generate a new, different signature for vin[0] to trigger cache clear: + CScript oldSig = tx.vin[0].scriptSig; + BOOST_CHECK(SignSignature(keystore, orphans[0], tx, 0)); + BOOST_CHECK(tx.vin[0].scriptSig != oldSig); + for (unsigned int j = 0; j < tx.vin.size(); j++) + BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, flags, SIGHASH_ALL)); + mapArgs.erase("-maxsigcachesize"); + + LimitOrphanTxSize(0); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/README b/src/test/README new file mode 100644 index 0000000..77f7faa --- /dev/null +++ b/src/test/README @@ -0,0 +1,21 @@ +The sources in this directory are unit test cases. Boost includes a +unit testing framework, and since bitcoin already uses boost, it makes +sense to simply use this framework rather than require developers to +configure some other framework (we want as few impediments to creating +unit tests as possible). + +The build system is setup to compile an executable called "test_bitcoin" +that runs all of the unit tests. The main source file is called +test_bitcoin.cpp, which simply includes other files that contain the +actual unit tests (outside of a couple required preprocessor +directives). The pattern is to create one test file for each class or +source file for which you want to create unit tests. The file naming +convention is "_tests.cpp" and such files should wrap +their tests in a test suite called "_tests". For an +examples of this pattern, examine uint160_tests.cpp and +uint256_tests.cpp. + +For further reading, I found the following website to be helpful in +explaining how the boost unit test framework works: + +http://www.alittlemadness.com/2009/03/31/c-unit-testing-with-boosttest/ diff --git a/src/test/accounting_tests.cpp b/src/test/accounting_tests.cpp new file mode 100644 index 0000000..8ac6572 --- /dev/null +++ b/src/test/accounting_tests.cpp @@ -0,0 +1,123 @@ +#include + +#include + +#include "init.h" +#include "wallet.h" +#include "walletdb.h" + +BOOST_AUTO_TEST_SUITE(accounting_tests) + +static void +GetResults(CWalletDB& walletdb, std::map& results) +{ + std::list aes; + + results.clear(); + BOOST_CHECK(walletdb.ReorderTransactions(pwalletMain) == DB_LOAD_OK); + walletdb.ListAccountCreditDebit("", aes); + BOOST_FOREACH(CAccountingEntry& ae, aes) + { + results[ae.nOrderPos] = ae; + } +} + +BOOST_AUTO_TEST_CASE(acc_orderupgrade) +{ + CWalletDB walletdb(pwalletMain->strWalletFile); + std::vector vpwtx; + CWalletTx wtx; + CAccountingEntry ae; + std::map results; + + ae.strAccount = ""; + ae.nCreditDebit = 1; + ae.nTime = 1333333333; + ae.strOtherAccount = "b"; + ae.strComment = ""; + walletdb.WriteAccountingEntry(ae); + + wtx.mapValue["comment"] = "z"; + pwalletMain->AddToWallet(wtx); + vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); + vpwtx[0]->nTimeReceived = (unsigned int)1333333335; + vpwtx[0]->nOrderPos = -1; + + ae.nTime = 1333333336; + ae.strOtherAccount = "c"; + walletdb.WriteAccountingEntry(ae); + + GetResults(walletdb, results); + + BOOST_CHECK(pwalletMain->nOrderPosNext == 3); + BOOST_CHECK(2 == results.size()); + BOOST_CHECK(results[0].nTime == 1333333333); + BOOST_CHECK(results[0].strComment.empty()); + BOOST_CHECK(1 == vpwtx[0]->nOrderPos); + BOOST_CHECK(results[2].nTime == 1333333336); + BOOST_CHECK(results[2].strOtherAccount == "c"); + + + ae.nTime = 1333333330; + ae.strOtherAccount = "d"; + ae.nOrderPos = pwalletMain->IncOrderPosNext(); + walletdb.WriteAccountingEntry(ae); + + GetResults(walletdb, results); + + BOOST_CHECK(results.size() == 3); + BOOST_CHECK(pwalletMain->nOrderPosNext == 4); + BOOST_CHECK(results[0].nTime == 1333333333); + BOOST_CHECK(1 == vpwtx[0]->nOrderPos); + BOOST_CHECK(results[2].nTime == 1333333336); + BOOST_CHECK(results[3].nTime == 1333333330); + BOOST_CHECK(results[3].strComment.empty()); + + + wtx.mapValue["comment"] = "y"; + --wtx.nLockTime; // Just to change the hash :) + pwalletMain->AddToWallet(wtx); + vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); + vpwtx[1]->nTimeReceived = (unsigned int)1333333336; + + wtx.mapValue["comment"] = "x"; + --wtx.nLockTime; // Just to change the hash :) + pwalletMain->AddToWallet(wtx); + vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); + vpwtx[2]->nTimeReceived = (unsigned int)1333333329; + vpwtx[2]->nOrderPos = -1; + + GetResults(walletdb, results); + + BOOST_CHECK(results.size() == 3); + BOOST_CHECK(pwalletMain->nOrderPosNext == 6); + BOOST_CHECK(0 == vpwtx[2]->nOrderPos); + BOOST_CHECK(results[1].nTime == 1333333333); + BOOST_CHECK(2 == vpwtx[0]->nOrderPos); + BOOST_CHECK(results[3].nTime == 1333333336); + BOOST_CHECK(results[4].nTime == 1333333330); + BOOST_CHECK(results[4].strComment.empty()); + BOOST_CHECK(5 == vpwtx[1]->nOrderPos); + + + ae.nTime = 1333333334; + ae.strOtherAccount = "e"; + ae.nOrderPos = -1; + walletdb.WriteAccountingEntry(ae); + + GetResults(walletdb, results); + + BOOST_CHECK(results.size() == 4); + BOOST_CHECK(pwalletMain->nOrderPosNext == 7); + BOOST_CHECK(0 == vpwtx[2]->nOrderPos); + BOOST_CHECK(results[1].nTime == 1333333333); + BOOST_CHECK(2 == vpwtx[0]->nOrderPos); + BOOST_CHECK(results[3].nTime == 1333333336); + BOOST_CHECK(results[3].strComment.empty()); + BOOST_CHECK(results[4].nTime == 1333333330); + BOOST_CHECK(results[4].strComment.empty()); + BOOST_CHECK(results[5].nTime == 1333333334); + BOOST_CHECK(6 == vpwtx[1]->nOrderPos); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/alert_tests.cpp b/src/test/alert_tests.cpp new file mode 100644 index 0000000..f9e2be7 --- /dev/null +++ b/src/test/alert_tests.cpp @@ -0,0 +1,182 @@ +// +// Unit tests for alert system +// + +#include +#include +#include + +#include "alert.h" +#include "serialize.h" +#include "util.h" + +#if 0 +// +// alertTests contains 7 alerts, generated with this code: +// (SignAndSave code not shown, alert signing key is secret) +// +{ + CAlert alert; + alert.nRelayUntil = 60; + alert.nExpiration = 24 * 60 * 60; + alert.nID = 1; + alert.nCancel = 0; // cancels previous messages up to this ID number + alert.nMinVer = 0; // These versions are protocol versions + alert.nMaxVer = 70001; + alert.nPriority = 1; + alert.strComment = "Alert comment"; + alert.strStatusBar = "Alert 1"; + + SignAndSave(alert, "test/alertTests"); + + alert.setSubVer.insert(std::string("/Satoshi:0.1.0/")); + alert.strStatusBar = "Alert 1 for Satoshi 0.1.0"; + SignAndSave(alert, "test/alertTests"); + + alert.setSubVer.insert(std::string("/Satoshi:0.2.0/")); + alert.strStatusBar = "Alert 1 for Satoshi 0.1.0, 0.2.0"; + SignAndSave(alert, "test/alertTests"); + + alert.setSubVer.clear(); + ++alert.nID; + alert.nCancel = 1; + alert.nPriority = 100; + alert.strStatusBar = "Alert 2, cancels 1"; + SignAndSave(alert, "test/alertTests"); + + alert.nExpiration += 60; + ++alert.nID; + SignAndSave(alert, "test/alertTests"); + + ++alert.nID; + alert.nMinVer = 11; + alert.nMaxVer = 22; + SignAndSave(alert, "test/alertTests"); + + ++alert.nID; + alert.strStatusBar = "Alert 2 for Satoshi 0.1.0"; + alert.setSubVer.insert(std::string("/Satoshi:0.1.0/")); + SignAndSave(alert, "test/alertTests"); + + ++alert.nID; + alert.nMinVer = 0; + alert.nMaxVer = 999999; + alert.strStatusBar = "Evil Alert'; /bin/ls; echo '"; + alert.setSubVer.clear(); + SignAndSave(alert, "test/alertTests"); +} +#endif + +struct ReadAlerts +{ + ReadAlerts() + { + std::string filename("alertTests"); + namespace fs = boost::filesystem; + fs::path testFile = fs::current_path() / "test" / "data" / filename; +#ifdef TEST_DATA_DIR + if (!fs::exists(testFile)) + { + testFile = fs::path(BOOST_PP_STRINGIZE(TEST_DATA_DIR)) / filename; + } +#endif + FILE* fp = fopen(testFile.string().c_str(), "rb"); + if (!fp) return; + + + CAutoFile filein = CAutoFile(fp, SER_DISK, CLIENT_VERSION); + if (!filein) return; + + try { + while (!feof(filein)) + { + CAlert alert; + filein >> alert; + alerts.push_back(alert); + } + } + catch (std::exception) { } + } + ~ReadAlerts() { } + + static std::vector read_lines(boost::filesystem::path filepath) + { + std::vector result; + + std::ifstream f(filepath.string().c_str()); + std::string line; + while (std::getline(f,line)) + result.push_back(line); + + return result; + } + + std::vector alerts; +}; + +BOOST_FIXTURE_TEST_SUITE(Alert_tests, ReadAlerts) + + +BOOST_AUTO_TEST_CASE(AlertApplies) +{ + SetMockTime(11); + + BOOST_FOREACH(const CAlert& alert, alerts) + { + BOOST_CHECK(alert.CheckSignature()); + } + // Matches: + BOOST_CHECK(alerts[0].AppliesTo(1, "")); + BOOST_CHECK(alerts[0].AppliesTo(70001, "")); + BOOST_CHECK(alerts[0].AppliesTo(1, "/Satoshi:11.11.11/")); + + BOOST_CHECK(alerts[1].AppliesTo(1, "/Satoshi:0.1.0/")); + BOOST_CHECK(alerts[1].AppliesTo(70001, "/Satoshi:0.1.0/")); + + BOOST_CHECK(alerts[2].AppliesTo(1, "/Satoshi:0.1.0/")); + BOOST_CHECK(alerts[2].AppliesTo(1, "/Satoshi:0.2.0/")); + + // Don't match: + BOOST_CHECK(!alerts[0].AppliesTo(-1, "")); + BOOST_CHECK(!alerts[0].AppliesTo(70002, "")); + + BOOST_CHECK(!alerts[1].AppliesTo(1, "")); + BOOST_CHECK(!alerts[1].AppliesTo(1, "Satoshi:0.1.0")); + BOOST_CHECK(!alerts[1].AppliesTo(1, "/Satoshi:0.1.0")); + BOOST_CHECK(!alerts[1].AppliesTo(1, "Satoshi:0.1.0/")); + BOOST_CHECK(!alerts[1].AppliesTo(-1, "/Satoshi:0.1.0/")); + BOOST_CHECK(!alerts[1].AppliesTo(70002, "/Satoshi:0.1.0/")); + BOOST_CHECK(!alerts[1].AppliesTo(1, "/Satoshi:0.2.0/")); + + BOOST_CHECK(!alerts[2].AppliesTo(1, "/Satoshi:0.3.0/")); + + SetMockTime(0); +} + + +// This uses sh 'echo' to test the -alertnotify function, writing to a +// /tmp file. So skip it on Windows: +#ifndef WIN32 +BOOST_AUTO_TEST_CASE(AlertNotify) +{ + SetMockTime(11); + + boost::filesystem::path temp = GetTempPath() / "alertnotify.txt"; + boost::filesystem::remove(temp); + + mapArgs["-alertnotify"] = std::string("echo %s >> ") + temp.string(); + + BOOST_FOREACH(CAlert alert, alerts) + alert.ProcessAlert(false); + + std::vector r = read_lines(temp); + BOOST_CHECK_EQUAL(r.size(), 1u); + BOOST_CHECK_EQUAL(r[0], "Evil Alert; /bin/ls; echo "); // single-quotes should be removed + + boost::filesystem::remove(temp); + + SetMockTime(0); +} +#endif + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/allocator_tests.cpp b/src/test/allocator_tests.cpp new file mode 100644 index 0000000..d5cb8e8 --- /dev/null +++ b/src/test/allocator_tests.cpp @@ -0,0 +1,115 @@ +#include + +#include "init.h" +#include "main.h" +#include "util.h" + +BOOST_AUTO_TEST_SUITE(allocator_tests) + +// Dummy memory page locker for platform independent tests +static const void *last_lock_addr, *last_unlock_addr; +static size_t last_lock_len, last_unlock_len; +class TestLocker +{ +public: + bool Lock(const void *addr, size_t len) + { + last_lock_addr = addr; + last_lock_len = len; + return true; + } + bool Unlock(const void *addr, size_t len) + { + last_unlock_addr = addr; + last_unlock_len = len; + return true; + } +}; + +BOOST_AUTO_TEST_CASE(test_LockedPageManagerBase) +{ + const size_t test_page_size = 4096; + LockedPageManagerBase lpm(test_page_size); + size_t addr; + last_lock_addr = last_unlock_addr = 0; + last_lock_len = last_unlock_len = 0; + + /* Try large number of small objects */ + addr = 0; + for(int i=0; i<1000; ++i) + { + lpm.LockRange(reinterpret_cast(addr), 33); + addr += 33; + } + /* Try small number of page-sized objects, straddling two pages */ + addr = test_page_size*100 + 53; + for(int i=0; i<100; ++i) + { + lpm.LockRange(reinterpret_cast(addr), test_page_size); + addr += test_page_size; + } + /* Try small number of page-sized objects aligned to exactly one page */ + addr = test_page_size*300; + for(int i=0; i<100; ++i) + { + lpm.LockRange(reinterpret_cast(addr), test_page_size); + addr += test_page_size; + } + /* one very large object, straddling pages */ + lpm.LockRange(reinterpret_cast(test_page_size*600+1), test_page_size*500); + BOOST_CHECK(last_lock_addr == reinterpret_cast(test_page_size*(600+500))); + /* one very large object, page aligned */ + lpm.LockRange(reinterpret_cast(test_page_size*1200), test_page_size*500-1); + BOOST_CHECK(last_lock_addr == reinterpret_cast(test_page_size*(1200+500-1))); + + BOOST_CHECK(lpm.GetLockedPageCount() == ( + (1000*33+test_page_size-1)/test_page_size + // small objects + 101 + 100 + // page-sized objects + 501 + 500)); // large objects + BOOST_CHECK((last_lock_len & (test_page_size-1)) == 0); // always lock entire pages + BOOST_CHECK(last_unlock_len == 0); // nothing unlocked yet + + /* And unlock again */ + addr = 0; + for(int i=0; i<1000; ++i) + { + lpm.UnlockRange(reinterpret_cast(addr), 33); + addr += 33; + } + addr = test_page_size*100 + 53; + for(int i=0; i<100; ++i) + { + lpm.UnlockRange(reinterpret_cast(addr), test_page_size); + addr += test_page_size; + } + addr = test_page_size*300; + for(int i=0; i<100; ++i) + { + lpm.UnlockRange(reinterpret_cast(addr), test_page_size); + addr += test_page_size; + } + lpm.UnlockRange(reinterpret_cast(test_page_size*600+1), test_page_size*500); + lpm.UnlockRange(reinterpret_cast(test_page_size*1200), test_page_size*500-1); + + /* Check that everything is released */ + BOOST_CHECK(lpm.GetLockedPageCount() == 0); + + /* A few and unlocks of size zero (should have no effect) */ + addr = 0; + for(int i=0; i<1000; ++i) + { + lpm.LockRange(reinterpret_cast(addr), 0); + addr += 1; + } + BOOST_CHECK(lpm.GetLockedPageCount() == 0); + addr = 0; + for(int i=0; i<1000; ++i) + { + lpm.UnlockRange(reinterpret_cast(addr), 0); + addr += 1; + } + BOOST_CHECK(lpm.GetLockedPageCount() == 0); + BOOST_CHECK((last_unlock_len & (test_page_size-1)) == 0); // always unlock entire pages +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/base32_tests.cpp b/src/test/base32_tests.cpp new file mode 100644 index 0000000..fdf3285 --- /dev/null +++ b/src/test/base32_tests.cpp @@ -0,0 +1,20 @@ +#include + +#include "util.h" + +BOOST_AUTO_TEST_SUITE(base32_tests) + +BOOST_AUTO_TEST_CASE(base32_testvectors) +{ + static const std::string vstrIn[] = {"","f","fo","foo","foob","fooba","foobar"}; + static const std::string vstrOut[] = {"","my======","mzxq====","mzxw6===","mzxw6yq=","mzxw6ytb","mzxw6ytboi======"}; + for (unsigned int i=0; i +#include "json/json_spirit_reader_template.h" +#include "json/json_spirit_writer_template.h" +#include "json/json_spirit_utils.h" + +#include "base58.h" +#include "util.h" + +using namespace json_spirit; +extern Array read_json(const std::string& filename); + +BOOST_AUTO_TEST_SUITE(base58_tests) + +// Goal: test low-level base58 encoding functionality +BOOST_AUTO_TEST_CASE(base58_EncodeBase58) +{ + Array tests = read_json("base58_encode_decode.json"); + + BOOST_FOREACH(Value& tv, tests) + { + Array test = tv.get_array(); + std::string strTest = write_string(tv, false); + if (test.size() < 2) // Allow for extra stuff (useful for comments) + { + BOOST_ERROR("Bad test: " << strTest); + continue; + } + std::vector sourcedata = ParseHex(test[0].get_str()); + std::string base58string = test[1].get_str(); + BOOST_CHECK_MESSAGE( + EncodeBase58(&sourcedata[0], &sourcedata[sourcedata.size()]) == base58string, + strTest); + } +} + +// Goal: test low-level base58 decoding functionality +BOOST_AUTO_TEST_CASE(base58_DecodeBase58) +{ + Array tests = read_json("base58_encode_decode.json"); + std::vector result; + + BOOST_FOREACH(Value& tv, tests) + { + Array test = tv.get_array(); + std::string strTest = write_string(tv, false); + if (test.size() < 2) // Allow for extra stuff (useful for comments) + { + BOOST_ERROR("Bad test: " << strTest); + continue; + } + std::vector expected = ParseHex(test[0].get_str()); + std::string base58string = test[1].get_str(); + BOOST_CHECK_MESSAGE(DecodeBase58(base58string, result), strTest); + BOOST_CHECK_MESSAGE(result.size() == expected.size() && std::equal(result.begin(), result.end(), expected.begin()), strTest); + } + + BOOST_CHECK(!DecodeBase58("invalid", result)); +} + +// Visitor to check address type +class TestAddrTypeVisitor : public boost::static_visitor +{ +private: + std::string exp_addrType; +public: + TestAddrTypeVisitor(const std::string &exp_addrType) : exp_addrType(exp_addrType) { } + bool operator()(const CKeyID &id) const + { + return (exp_addrType == "pubkey"); + } + bool operator()(const CScriptID &id) const + { + return (exp_addrType == "script"); + } + bool operator()(const CNoDestination &no) const + { + return (exp_addrType == "none"); + } +}; + +// Visitor to check address payload +class TestPayloadVisitor : public boost::static_visitor +{ +private: + std::vector exp_payload; +public: + TestPayloadVisitor(std::vector &exp_payload) : exp_payload(exp_payload) { } + bool operator()(const CKeyID &id) const + { + uint160 exp_key(exp_payload); + return exp_key == id; + } + bool operator()(const CScriptID &id) const + { + uint160 exp_key(exp_payload); + return exp_key == id; + } + bool operator()(const CNoDestination &no) const + { + return exp_payload.size() == 0; + } +}; + +// Goal: check that parsed keys match test payload +BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) +{ + Array tests = read_json("base58_keys_valid.json"); + std::vector result; + CBitcoinSecret secret; + CBitcoinAddress addr; + // Save global state + bool fTestNet_stored = fTestNet; + + BOOST_FOREACH(Value& tv, tests) + { + Array test = tv.get_array(); + std::string strTest = write_string(tv, false); + if (test.size() < 3) // Allow for extra stuff (useful for comments) + { + BOOST_ERROR("Bad test: " << strTest); + continue; + } + std::string exp_base58string = test[0].get_str(); + std::vector exp_payload = ParseHex(test[1].get_str()); + const Object &metadata = test[2].get_obj(); + bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); + bool isTestnet = find_value(metadata, "isTestnet").get_bool(); + fTestNet = isTestnet; // Override testnet flag + if(isPrivkey) + { + bool isCompressed = find_value(metadata, "isCompressed").get_bool(); + // Must be valid private key + // Note: CBitcoinSecret::SetString tests isValid, whereas CBitcoinAddress does not! + BOOST_CHECK_MESSAGE(secret.SetString(exp_base58string), "!SetString:"+ strTest); + BOOST_CHECK_MESSAGE(secret.IsValid(), "!IsValid:" + strTest); + CKey privkey = secret.GetKey(); + BOOST_CHECK_MESSAGE(privkey.IsCompressed() == isCompressed, "compressed mismatch:" + strTest); + BOOST_CHECK_MESSAGE(privkey.size() == exp_payload.size() && std::equal(privkey.begin(), privkey.end(), exp_payload.begin()), "key mismatch:" + strTest); + + // Private key must be invalid public key + addr.SetString(exp_base58string); + BOOST_CHECK_MESSAGE(!addr.IsValid(), "IsValid privkey as pubkey:" + strTest); + } + else + { + std::string exp_addrType = find_value(metadata, "addrType").get_str(); // "script" or "pubkey" + // Must be valid public key + BOOST_CHECK_MESSAGE(addr.SetString(exp_base58string), "SetString:" + strTest); + BOOST_CHECK_MESSAGE(addr.IsValid(), "!IsValid:" + strTest); + BOOST_CHECK_MESSAGE(addr.IsScript() == (exp_addrType == "script"), "isScript mismatch" + strTest); + CTxDestination dest = addr.Get(); + BOOST_CHECK_MESSAGE(boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), dest), "addrType mismatch" + strTest); + + // Public key must be invalid private key + secret.SetString(exp_base58string); + BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid pubkey as privkey:" + strTest); + } + } + // Restore global state + fTestNet = fTestNet_stored; +} + +// Goal: check that generated keys match test vectors +BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) +{ + Array tests = read_json("base58_keys_valid.json"); + std::vector result; + // Save global state + bool fTestNet_stored = fTestNet; + + BOOST_FOREACH(Value& tv, tests) + { + Array test = tv.get_array(); + std::string strTest = write_string(tv, false); + if (test.size() < 3) // Allow for extra stuff (useful for comments) + { + BOOST_ERROR("Bad test: " << strTest); + continue; + } + std::string exp_base58string = test[0].get_str(); + std::vector exp_payload = ParseHex(test[1].get_str()); + const Object &metadata = test[2].get_obj(); + bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); + bool isTestnet = find_value(metadata, "isTestnet").get_bool(); + fTestNet = isTestnet; // Override testnet flag + if(isPrivkey) + { + bool isCompressed = find_value(metadata, "isCompressed").get_bool(); + CKey key; + key.Set(exp_payload.begin(), exp_payload.end(), isCompressed); + assert(key.IsValid()); + CBitcoinSecret secret; + secret.SetKey(key); + BOOST_CHECK_MESSAGE(secret.ToString() == exp_base58string, "result mismatch: " + strTest); + } + else + { + std::string exp_addrType = find_value(metadata, "addrType").get_str(); + CTxDestination dest; + if(exp_addrType == "pubkey") + { + dest = CKeyID(uint160(exp_payload)); + } + else if(exp_addrType == "script") + { + dest = CScriptID(uint160(exp_payload)); + } + else if(exp_addrType == "none") + { + dest = CNoDestination(); + } + else + { + BOOST_ERROR("Bad addrtype: " << strTest); + continue; + } + CBitcoinAddress addrOut; + BOOST_CHECK_MESSAGE(boost::apply_visitor(CBitcoinAddressVisitor(&addrOut), dest), "encode dest: " + strTest); + BOOST_CHECK_MESSAGE(addrOut.ToString() == exp_base58string, "mismatch: " + strTest); + } + } + + // Visiting a CNoDestination must fail + CBitcoinAddress dummyAddr; + CTxDestination nodest = CNoDestination(); + BOOST_CHECK(!boost::apply_visitor(CBitcoinAddressVisitor(&dummyAddr), nodest)); + + // Restore global state + fTestNet = fTestNet_stored; +} + +// 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("base58_keys_invalid.json"); // Negative testcases + std::vector result; + CBitcoinSecret secret; + CBitcoinAddress addr; + + BOOST_FOREACH(Value& tv, tests) + { + Array test = tv.get_array(); + std::string strTest = write_string(tv, false); + if (test.size() < 1) // Allow for extra stuff (useful for comments) + { + BOOST_ERROR("Bad test: " << strTest); + continue; + } + std::string exp_base58string = test[0].get_str(); + + // must be invalid as public and as private key + addr.SetString(exp_base58string); + BOOST_CHECK_MESSAGE(!addr.IsValid(), "IsValid pubkey:" + strTest); + secret.SetString(exp_base58string); + BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey:" + strTest); + } +} + + +BOOST_AUTO_TEST_SUITE_END() + diff --git a/src/test/base64_tests.cpp b/src/test/base64_tests.cpp new file mode 100644 index 0000000..c5a053e --- /dev/null +++ b/src/test/base64_tests.cpp @@ -0,0 +1,22 @@ +#include + +#include "main.h" +#include "wallet.h" +#include "util.h" + +BOOST_AUTO_TEST_SUITE(base64_tests) + +BOOST_AUTO_TEST_CASE(base64_testvectors) +{ + static const std::string vstrIn[] = {"","f","fo","foo","foob","fooba","foobar"}; + static const std::string vstrOut[] = {"","Zg==","Zm8=","Zm9v","Zm9vYg==","Zm9vYmE=","Zm9vYmFy"}; + for (unsigned int i=0; i +#include + +#include "bignum.h" +#include "util.h" + +BOOST_AUTO_TEST_SUITE(bignum_tests) + +// Unfortunately there's no standard way of preventing a function from being +// inlined, so we define a macro for it. +// +// You should use it like this: +// NOINLINE void function() {...} +#if defined(__GNUC__) +// This also works and will be defined for any compiler implementing GCC +// extensions, such as Clang and ICC. +#define NOINLINE __attribute__((noinline)) +#elif defined(_MSC_VER) +#define NOINLINE __declspec(noinline) +#else +// We give out a warning because it impacts the correctness of one bignum test. +#warning You should define NOINLINE for your compiler. +#define NOINLINE +#endif + +// For the following test case, it is useful to use additional tools. +// +// The simplest one to use is the compiler flag -ftrapv, which detects integer +// overflows and similar errors. However, due to optimizations and compilers +// taking advantage of undefined behavior sometimes it may not actually detect +// anything. +// +// You can also use compiler-based stack protection to possibly detect possible +// stack buffer overruns. +// +// For more accurate diagnostics, you can use an undefined arithmetic operation +// detector such as the clang-based tool: +// +// "IOC: An Integer Overflow Checker for C/C++" +// +// Available at: http://embed.cs.utah.edu/ioc/ +// +// It might also be useful to use Google's AddressSanitizer to detect +// stack buffer overruns, which valgrind can't currently detect. + +// Let's force this code not to be inlined, in order to actually +// test a generic version of the function. This increases the chance +// that -ftrapv will detect overflows. +NOINLINE void mysetint64(CBigNum& num, int64 n) +{ + num.setint64(n); +} + +// For each number, we do 2 tests: one with inline code, then we reset the +// value to 0, then the second one with a non-inlined function. +BOOST_AUTO_TEST_CASE(bignum_setint64) +{ + int64 n; + + { + n = 0; + CBigNum num(n); + BOOST_CHECK(num.ToString() == "0"); + num.setulong(0); + BOOST_CHECK(num.ToString() == "0"); + mysetint64(num, n); + BOOST_CHECK(num.ToString() == "0"); + } + { + n = 1; + CBigNum num(n); + BOOST_CHECK(num.ToString() == "1"); + num.setulong(0); + BOOST_CHECK(num.ToString() == "0"); + mysetint64(num, n); + BOOST_CHECK(num.ToString() == "1"); + } + { + n = -1; + CBigNum num(n); + BOOST_CHECK(num.ToString() == "-1"); + num.setulong(0); + BOOST_CHECK(num.ToString() == "0"); + mysetint64(num, n); + BOOST_CHECK(num.ToString() == "-1"); + } + { + n = 5; + CBigNum num(n); + BOOST_CHECK(num.ToString() == "5"); + num.setulong(0); + BOOST_CHECK(num.ToString() == "0"); + mysetint64(num, n); + BOOST_CHECK(num.ToString() == "5"); + } + { + n = -5; + CBigNum num(n); + BOOST_CHECK(num.ToString() == "-5"); + num.setulong(0); + BOOST_CHECK(num.ToString() == "0"); + mysetint64(num, n); + BOOST_CHECK(num.ToString() == "-5"); + } + { + n = std::numeric_limits::min(); + CBigNum num(n); + BOOST_CHECK(num.ToString() == "-9223372036854775808"); + num.setulong(0); + BOOST_CHECK(num.ToString() == "0"); + mysetint64(num, n); + BOOST_CHECK(num.ToString() == "-9223372036854775808"); + } + { + n = std::numeric_limits::max(); + CBigNum num(n); + BOOST_CHECK(num.ToString() == "9223372036854775807"); + num.setulong(0); + BOOST_CHECK(num.ToString() == "0"); + mysetint64(num, n); + BOOST_CHECK(num.ToString() == "9223372036854775807"); + } +} + + +BOOST_AUTO_TEST_CASE(bignum_SetCompact) +{ + CBigNum num; + num.SetCompact(0); + BOOST_CHECK_EQUAL(num.GetHex(), "0"); + BOOST_CHECK_EQUAL(num.GetCompact(), 0U); + + num.SetCompact(0x00123456); + BOOST_CHECK_EQUAL(num.GetHex(), "0"); + BOOST_CHECK_EQUAL(num.GetCompact(), 0U); + + num.SetCompact(0x01123456); + BOOST_CHECK_EQUAL(num.GetHex(), "12"); + BOOST_CHECK_EQUAL(num.GetCompact(), 0x01120000U); + + // Make sure that we don't generate compacts with the 0x00800000 bit set + num = 0x80; + BOOST_CHECK_EQUAL(num.GetCompact(), 0x02008000U); + + num.SetCompact(0x01fedcba); + BOOST_CHECK_EQUAL(num.GetHex(), "-7e"); + BOOST_CHECK_EQUAL(num.GetCompact(), 0x01fe0000U); + + num.SetCompact(0x02123456); + BOOST_CHECK_EQUAL(num.GetHex(), "1234"); + BOOST_CHECK_EQUAL(num.GetCompact(), 0x02123400U); + + num.SetCompact(0x03123456); + BOOST_CHECK_EQUAL(num.GetHex(), "123456"); + BOOST_CHECK_EQUAL(num.GetCompact(), 0x03123456U); + + num.SetCompact(0x04123456); + BOOST_CHECK_EQUAL(num.GetHex(), "12345600"); + BOOST_CHECK_EQUAL(num.GetCompact(), 0x04123456U); + + num.SetCompact(0x04923456); + BOOST_CHECK_EQUAL(num.GetHex(), "-12345600"); + BOOST_CHECK_EQUAL(num.GetCompact(), 0x04923456U); + + num.SetCompact(0x05009234); + BOOST_CHECK_EQUAL(num.GetHex(), "92340000"); + BOOST_CHECK_EQUAL(num.GetCompact(), 0x05009234U); + + num.SetCompact(0x20123456); + BOOST_CHECK_EQUAL(num.GetHex(), "1234560000000000000000000000000000000000000000000000000000000000"); + BOOST_CHECK_EQUAL(num.GetCompact(), 0x20123456U); + + num.SetCompact(0xff123456); + BOOST_CHECK_EQUAL(num.GetHex(), "123456000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + BOOST_CHECK_EQUAL(num.GetCompact(), 0xff123456U); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp new file mode 100644 index 0000000..dc6efff --- /dev/null +++ b/src/test/bloom_tests.cpp @@ -0,0 +1,446 @@ +#include +#include + +#include "bloom.h" +#include "util.h" +#include "key.h" +#include "base58.h" +#include "main.h" + +using namespace std; +using namespace boost::tuples; + +BOOST_AUTO_TEST_SUITE(bloom_tests) + +BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize) +{ + CBloomFilter filter(3, 0.01, 0, BLOOM_UPDATE_ALL); + + filter.insert(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")); + BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter doesn't contain just-inserted object!"); + // One bit different in first byte + BOOST_CHECK_MESSAGE(!filter.contains(ParseHex("19108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter contains something it shouldn't!"); + + filter.insert(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")); + BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")), "BloomFilter doesn't contain just-inserted object (2)!"); + + filter.insert(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")); + BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")), "BloomFilter doesn't contain just-inserted object (3)!"); + + CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); + filter.Serialize(stream, SER_NETWORK, PROTOCOL_VERSION); + + vector vch = ParseHex("03614e9b050000000000000001"); + vector expected(vch.size()); + + for (unsigned int i = 0; i < vch.size(); i++) + expected[i] = (char)vch[i]; + + BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(), expected.begin(), expected.end()); +} + +BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize_with_tweak) +{ + // Same test as bloom_create_insert_serialize, but we add a nTweak of 100 + CBloomFilter filter(3, 0.01, 2147483649, BLOOM_UPDATE_ALL); + + filter.insert(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")); + BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter doesn't contain just-inserted object!"); + // One bit different in first byte + BOOST_CHECK_MESSAGE(!filter.contains(ParseHex("19108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter contains something it shouldn't!"); + + filter.insert(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")); + BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")), "BloomFilter doesn't contain just-inserted object (2)!"); + + filter.insert(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")); + BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")), "BloomFilter doesn't contain just-inserted object (3)!"); + + CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); + filter.Serialize(stream, SER_NETWORK, PROTOCOL_VERSION); + + vector vch = ParseHex("03ce4299050000000100008001"); + vector expected(vch.size()); + + for (unsigned int i = 0; i < vch.size(); i++) + expected[i] = (char)vch[i]; + + BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(), expected.begin(), expected.end()); +} + +BOOST_AUTO_TEST_CASE(bloom_create_insert_key) +{ + string strSecret = string("6v7hgtX6r7frdh7XDRJ3L4JRLMAn9chCgNgSccwqWdp69XfPdX6"); + CBitcoinSecret vchSecret; + BOOST_CHECK(vchSecret.SetString(strSecret)); + + CKey key = vchSecret.GetKey(); + CPubKey pubkey = key.GetPubKey(); + vector vchPubKey(pubkey.begin(), pubkey.end()); + + CBloomFilter filter(2, 0.001, 0, BLOOM_UPDATE_ALL); + filter.insert(vchPubKey); + uint160 hash = pubkey.GetID(); + filter.insert(vector(hash.begin(), hash.end())); + + CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); + filter.Serialize(stream, SER_NETWORK, PROTOCOL_VERSION); + + vector vch = ParseHex("0322ed23080000000000000001"); + vector expected(vch.size()); + + for (unsigned int i = 0; i < vch.size(); i++) + expected[i] = (char)vch[i]; + + BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(), expected.begin(), expected.end()); +} + +BOOST_AUTO_TEST_CASE(bloom_match) +{ + // Random real transaction (b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b) + CTransaction tx; + CDataStream stream(ParseHex("01000000010b26e9b7735eb6aabdf358bab62f9816a21ba9ebdb719d5299e88607d722c190000000008b4830450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a0141046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339ffffffff021bff3d11000000001976a91404943fdd508053c75000106d3bc6e2754dbcff1988ac2f15de00000000001976a914a266436d2965547608b9e15d9032a7b9d64fa43188ac00000000"), SER_DISK, CLIENT_VERSION); + stream >> tx; + + // and one which spends it (e2769b09e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436) + unsigned char ch[] = {0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0xff, 0x7f, 0xcd, 0x4f, 0x85, 0x65, 0xef, 0x40, 0x6d, 0xd5, 0xd6, 0x3d, 0x4f, 0xf9, 0x4f, 0x31, 0x8f, 0xe8, 0x20, 0x27, 0xfd, 0x4d, 0xc4, 0x51, 0xb0, 0x44, 0x74, 0x01, 0x9f, 0x74, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xda, 0x0d, 0xc6, 0xae, 0xce, 0xfe, 0x1e, 0x06, 0xef, 0xdf, 0x05, 0x77, 0x37, 0x57, 0xde, 0xb1, 0x68, 0x82, 0x09, 0x30, 0xe3, 0xb0, 0xd0, 0x3f, 0x46, 0xf5, 0xfc, 0xf1, 0x50, 0xbf, 0x99, 0x0c, 0x02, 0x21, 0x00, 0xd2, 0x5b, 0x5c, 0x87, 0x04, 0x00, 0x76, 0xe4, 0xf2, 0x53, 0xf8, 0x26, 0x2e, 0x76, 0x3e, 0x2d, 0xd5, 0x1e, 0x7f, 0xf0, 0xbe, 0x15, 0x77, 0x27, 0xc4, 0xbc, 0x42, 0x80, 0x7f, 0x17, 0xbd, 0x39, 0x01, 0x41, 0x04, 0xe6, 0xc2, 0x6e, 0xf6, 0x7d, 0xc6, 0x10, 0xd2, 0xcd, 0x19, 0x24, 0x84, 0x78, 0x9a, 0x6c, 0xf9, 0xae, 0xa9, 0x93, 0x0b, 0x94, 0x4b, 0x7e, 0x2d, 0xb5, 0x34, 0x2b, 0x9d, 0x9e, 0x5b, 0x9f, 0xf7, 0x9a, 0xff, 0x9a, 0x2e, 0xe1, 0x97, 0x8d, 0xd7, 0xfd, 0x01, 0xdf, 0xc5, 0x22, 0xee, 0x02, 0x28, 0x3d, 0x3b, 0x06, 0xa9, 0xd0, 0x3a, 0xcf, 0x80, 0x96, 0x96, 0x8d, 0x7d, 0xbb, 0x0f, 0x91, 0x78, 0xff, 0xff, 0xff, 0xff, 0x02, 0x8b, 0xa7, 0x94, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xba, 0xde, 0xec, 0xfd, 0xef, 0x05, 0x07, 0x24, 0x7f, 0xc8, 0xf7, 0x42, 0x41, 0xd7, 0x3b, 0xc0, 0x39, 0x97, 0x2d, 0x7b, 0x88, 0xac, 0x40, 0x94, 0xa8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xc1, 0x09, 0x32, 0x48, 0x3f, 0xec, 0x93, 0xed, 0x51, 0xf5, 0xfe, 0x95, 0xe7, 0x25, 0x59, 0xf2, 0xcc, 0x70, 0x43, 0xf9, 0x88, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00}; + vector vch(ch, ch + sizeof(ch) -1); + CDataStream spendStream(vch, SER_DISK, CLIENT_VERSION); + CTransaction spendingTx; + spendStream >> spendingTx; + + CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(uint256("0xb4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b")); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match tx hash"); + + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + // byte-reversed tx hash + filter.insert(ParseHex("6bff7fcd4f8565ef406dd5d63d4ff94f318fe82027fd4dc451b04474019f74b4")); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match manually serialized tx hash"); + + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(ParseHex("30450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a01")); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match input signature"); + + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(ParseHex("046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339")); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match input pub key"); + + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(ParseHex("04943fdd508053c75000106d3bc6e2754dbcff19")); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match output address"); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(spendingTx, spendingTx.GetHash()), "Simple Bloom filter didn't add output"); + + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(ParseHex("a266436d2965547608b9e15d9032a7b9d64fa431")); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match output address"); + + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(COutPoint(uint256("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0)); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match COutPoint"); + + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + COutPoint prevOutPoint(uint256("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0); + { + vector data(32 + sizeof(unsigned int)); + memcpy(&data[0], prevOutPoint.hash.begin(), 32); + memcpy(&data[32], &prevOutPoint.n, sizeof(unsigned int)); + filter.insert(data); + } + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match manually serialized COutPoint"); + + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(uint256("00000009e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436")); + BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter matched random tx hash"); + + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(ParseHex("0000006d2965547608b9e15d9032a7b9d64fa431")); + BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter matched random address"); + + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(COutPoint(uint256("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 1)); + BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter matched COutPoint for an output we didn't care about"); + + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(COutPoint(uint256("0x000000d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0)); + BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter matched COutPoint for an output we didn't care about"); +} + +BOOST_AUTO_TEST_CASE(merkle_block_1) +{ + // Random real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af) + // With 9 txes + CBlock block; + CDataStream stream(ParseHex("0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb6800000000000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558da2fdb261b4d4c86041b1ab1bf930901000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0146ffffffff0100f2052a01000000434104e18f7afbe4721580e81e8414fc8c24d7cfacf254bb5c7b949450c3e997c2dc1242487a8169507b631eb3771f2b425483fb13102c4eb5d858eef260fe70fbfae0ac00000000010000000196608ccbafa16abada902780da4dc35dafd7af05fa0da08cf833575f8cf9e836000000004a493046022100dab24889213caf43ae6adc41cf1c9396c08240c199f5225acf45416330fd7dbd022100fe37900e0644bf574493a07fc5edba06dbc07c311b947520c2d514bc5725dcb401ffffffff0100f2052a010000001976a914f15d1921f52e4007b146dfa60f369ed2fc393ce288ac000000000100000001fb766c1288458c2bafcfec81e48b24d98ec706de6b8af7c4e3c29419bfacb56d000000008c493046022100f268ba165ce0ad2e6d93f089cfcd3785de5c963bb5ea6b8c1b23f1ce3e517b9f022100da7c0f21adc6c401887f2bfd1922f11d76159cbc597fbd756a23dcbb00f4d7290141042b4e8625a96127826915a5b109852636ad0da753c9e1d5606a50480cd0c40f1f8b8d898235e571fe9357d9ec842bc4bba1827daaf4de06d71844d0057707966affffffff0280969800000000001976a9146963907531db72d0ed1a0cfb471ccb63923446f388ac80d6e34c000000001976a914f0688ba1c0d1ce182c7af6741e02658c7d4dfcd388ac000000000100000002c40297f730dd7b5a99567eb8d27b78758f607507c52292d02d4031895b52f2ff010000008b483045022100f7edfd4b0aac404e5bab4fd3889e0c6c41aa8d0e6fa122316f68eddd0a65013902205b09cc8b2d56e1cd1f7f2fafd60a129ed94504c4ac7bdc67b56fe67512658b3e014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffffca5065ff9617cbcba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefb000000008a473044022068010362a13c7f9919fa832b2dee4e788f61f6f5d344a7c2a0da6ae740605658022006d1af525b9a14a35c003b78b72bd59738cd676f845d1ff3fc25049e01003614014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffff01001ec4110200000043410469ab4181eceb28985b9b4e895c13fa5e68d85761b7eee311db5addef76fa8621865134a221bd01f28ec9999ee3e021e60766e9d1f3458c115fb28650605f11c9ac000000000100000001cdaf2f758e91c514655e2dc50633d1e4c84989f8aa90a0dbc883f0d23ed5c2fa010000008b48304502207ab51be6f12a1962ba0aaaf24a20e0b69b27a94fac5adf45aa7d2d18ffd9236102210086ae728b370e5329eead9accd880d0cb070aea0c96255fae6c4f1ddcce1fd56e014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff02404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac002d3101000000001976a9141befba0cdc1ad56529371864d9f6cb042faa06b588ac000000000100000001b4a47603e71b61bc3326efd90111bf02d2f549b067f4c4a8fa183b57a0f800cb010000008a4730440220177c37f9a505c3f1a1f0ce2da777c339bd8339ffa02c7cb41f0a5804f473c9230220585b25a2ee80eb59292e52b987dad92acb0c64eced92ed9ee105ad153cdb12d001410443bd44f683467e549dae7d20d1d79cbdb6df985c6e9c029c8d0c6cb46cc1a4d3cf7923c5021b27f7a0b562ada113bc85d5fda5a1b41e87fe6e8802817cf69996ffffffff0280651406000000001976a9145505614859643ab7b547cd7f1f5e7e2a12322d3788ac00aa0271000000001976a914ea4720a7a52fc166c55ff2298e07baf70ae67e1b88ac00000000010000000586c62cd602d219bb60edb14a3e204de0705176f9022fe49a538054fb14abb49e010000008c493046022100f2bc2aba2534becbdf062eb993853a42bbbc282083d0daf9b4b585bd401aa8c9022100b1d7fd7ee0b95600db8535bbf331b19eed8d961f7a8e54159c53675d5f69df8c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff03ad0e58ccdac3df9dc28a218bcf6f1997b0a93306faaa4b3a28ae83447b2179010000008b483045022100be12b2937179da88599e27bb31c3525097a07cdb52422d165b3ca2f2020ffcf702200971b51f853a53d644ebae9ec8f3512e442b1bcb6c315a5b491d119d10624c83014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff2acfcab629bbc8685792603762c921580030ba144af553d271716a95089e107b010000008b483045022100fa579a840ac258871365dd48cd7552f96c8eea69bd00d84f05b283a0dab311e102207e3c0ee9234814cfbb1b659b83671618f45abc1326b9edcc77d552a4f2a805c0014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffdcdc6023bbc9944a658ddc588e61eacb737ddf0a3cd24f113b5a8634c517fcd2000000008b4830450221008d6df731df5d32267954bd7d2dda2302b74c6c2a6aa5c0ca64ecbabc1af03c75022010e55c571d65da7701ae2da1956c442df81bbf076cdbac25133f99d98a9ed34c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffe15557cd5ce258f479dfd6dc6514edf6d7ed5b21fcfa4a038fd69f06b83ac76e010000008b483045022023b3e0ab071eb11de2eb1cc3a67261b866f86bf6867d4558165f7c8c8aca2d86022100dc6e1f53a91de3efe8f63512850811f26284b62f850c70ca73ed5de8771fb451014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff01404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000010000000166d7577163c932b4f9690ca6a80b6e4eb001f0a2fa9023df5595602aae96ed8d000000008a4730440220262b42546302dfb654a229cefc86432b89628ff259dc87edd1154535b16a67e102207b4634c020a97c3e7bbd0d4d19da6aa2269ad9dded4026e896b213d73ca4b63f014104979b82d02226b3a4597523845754d44f13639e3bf2df5e82c6aab2bdc79687368b01b1ab8b19875ae3c90d661a3d0a33161dab29934edeb36aa01976be3baf8affffffff02404b4c00000000001976a9144854e695a02af0aeacb823ccbc272134561e0a1688ac40420f00000000001976a914abee93376d6b37b5c2940655a6fcaf1c8e74237988ac0000000001000000014e3f8ef2e91349a9059cb4f01e54ab2597c1387161d3da89919f7ea6acdbb371010000008c49304602210081f3183471a5ca22307c0800226f3ef9c353069e0773ac76bb580654d56aa523022100d4c56465bdc069060846f4fbf2f6b20520b2a80b08b168b31e66ddb9c694e240014104976c79848e18251612f8940875b2b08d06e6dc73b9840e8860c066b7e87432c477e9a59a453e71e6d76d5fe34058b800a098fc1740ce3012e8fc8a00c96af966ffffffff02c0e1e400000000001976a9144134e75a6fcb6042034aab5e18570cf1f844f54788ac404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000"), SER_NETWORK, PROTOCOL_VERSION); + stream >> block; + + CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + // Match the last transaction + filter.insert(uint256("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20")); + + CMerkleBlock merkleBlock(block, filter); + BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash()); + + BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1); + pair pair = merkleBlock.vMatchedTxn[0]; + + BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20")); + BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 8); + + vector vMatched; + BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot); + BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size()); + for (unsigned int i = 0; i < vMatched.size(); i++) + BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second); + + // Also match the 8th transaction + filter.insert(uint256("0xdd1fd2a6fc16404faf339881a90adbde7f4f728691ac62e8f168809cdfae1053")); + merkleBlock = CMerkleBlock(block, filter); + BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash()); + + BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 2); + + BOOST_CHECK(merkleBlock.vMatchedTxn[1] == pair); + + BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256("0xdd1fd2a6fc16404faf339881a90adbde7f4f728691ac62e8f168809cdfae1053")); + BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 7); + + BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot); + BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size()); + for (unsigned int i = 0; i < vMatched.size(); i++) + BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second); +} + +BOOST_AUTO_TEST_CASE(merkle_block_2) +{ + // Random real block (000000005a4ded781e667e06ceefafb71410b511fe0d5adc3e5a27ecbec34ae6) + // With 4 txes + CBlock block; + CDataStream stream(ParseHex("0100000075616236cc2126035fadb38deb65b9102cc2c41c09cdf29fc051906800000000fe7d5e12ef0ff901f6050211249919b1c0653771832b3a80c66cea42847f0ae1d4d26e49ffff001d00f0a4410401000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d029105ffffffff0100f2052a010000004341046d8709a041d34357697dfcb30a9d05900a6294078012bf3bb09c6f9b525f1d16d5503d7905db1ada9501446ea00728668fc5719aa80be2fdfc8a858a4dbdd4fbac00000000010000000255605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d28350000000049483045022100aa46504baa86df8a33b1192b1b9367b4d729dc41e389f2c04f3e5c7f0559aae702205e82253a54bf5c4f65b7428551554b2045167d6d206dfe6a2e198127d3f7df1501ffffffff55605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d2835010000004847304402202329484c35fa9d6bb32a55a70c0982f606ce0e3634b69006138683bcd12cbb6602200c28feb1e2555c3210f1dddb299738b4ff8bbe9667b68cb8764b5ac17b7adf0001ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac0000000001000000025f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028000000004847304402205d6058484157235b06028c30736c15613a28bdb768ee628094ca8b0030d4d6eb0220328789c9a2ec27ddaec0ad5ef58efded42e6ea17c2e1ce838f3d6913f5e95db601ffffffff5f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028010000004a493046022100c45af050d3cea806cedd0ab22520c53ebe63b987b8954146cdca42487b84bdd6022100b9b027716a6b59e640da50a864d6dd8a0ef24c76ce62391fa3eabaf4d2886d2d01ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac000000000100000002e2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b0000000048473044022016e7a727a061ea2254a6c358376aaa617ac537eb836c77d646ebda4c748aac8b0220192ce28bf9f2c06a6467e6531e27648d2b3e2e2bae85159c9242939840295ba501ffffffffe2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b010000004a493046022100b7a1a755588d4190118936e15cd217d133b0e4a53c3c15924010d5648d8925c9022100aaef031874db2114f2d869ac2de4ae53908fbfea5b2b1862e181626bb9005c9f01ffffffff0200e1f505000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00000000"), SER_NETWORK, PROTOCOL_VERSION); + stream >> block; + + CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + // Match the first transaction + filter.insert(uint256("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70")); + + CMerkleBlock merkleBlock(block, filter); + BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash()); + + BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1); + pair pair = merkleBlock.vMatchedTxn[0]; + + BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70")); + BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0); + + vector vMatched; + BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot); + BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size()); + for (unsigned int i = 0; i < vMatched.size(); i++) + BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second); + + // Match an output from the second transaction (the pubkey for address 1DZTzaBHUDM7T3QvUKBz4qXMRpkg8jsfB5) + // This should match the third transaction because it spends the output matched + // It also matches the fourth transaction, which spends to the pubkey again + filter.insert(ParseHex("044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45af")); + + merkleBlock = CMerkleBlock(block, filter); + BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash()); + + BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 4); + + BOOST_CHECK(pair == merkleBlock.vMatchedTxn[0]); + + BOOST_CHECK(merkleBlock.vMatchedTxn[1].second == uint256("0x28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f")); + BOOST_CHECK(merkleBlock.vMatchedTxn[1].first == 1); + + BOOST_CHECK(merkleBlock.vMatchedTxn[2].second == uint256("0x6b0f8a73a56c04b519f1883e8aafda643ba61a30bd1439969df21bea5f4e27e2")); + BOOST_CHECK(merkleBlock.vMatchedTxn[2].first == 2); + + BOOST_CHECK(merkleBlock.vMatchedTxn[3].second == uint256("0x3c1d7e82342158e4109df2e0b6348b6e84e403d8b4046d7007663ace63cddb23")); + BOOST_CHECK(merkleBlock.vMatchedTxn[3].first == 3); + + BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot); + BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size()); + for (unsigned int i = 0; i < vMatched.size(); i++) + BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second); +} + +BOOST_AUTO_TEST_CASE(merkle_block_2_with_update_none) +{ + // Random real block (000000005a4ded781e667e06ceefafb71410b511fe0d5adc3e5a27ecbec34ae6) + // With 4 txes + CBlock block; + CDataStream stream(ParseHex("0100000075616236cc2126035fadb38deb65b9102cc2c41c09cdf29fc051906800000000fe7d5e12ef0ff901f6050211249919b1c0653771832b3a80c66cea42847f0ae1d4d26e49ffff001d00f0a4410401000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d029105ffffffff0100f2052a010000004341046d8709a041d34357697dfcb30a9d05900a6294078012bf3bb09c6f9b525f1d16d5503d7905db1ada9501446ea00728668fc5719aa80be2fdfc8a858a4dbdd4fbac00000000010000000255605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d28350000000049483045022100aa46504baa86df8a33b1192b1b9367b4d729dc41e389f2c04f3e5c7f0559aae702205e82253a54bf5c4f65b7428551554b2045167d6d206dfe6a2e198127d3f7df1501ffffffff55605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d2835010000004847304402202329484c35fa9d6bb32a55a70c0982f606ce0e3634b69006138683bcd12cbb6602200c28feb1e2555c3210f1dddb299738b4ff8bbe9667b68cb8764b5ac17b7adf0001ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac0000000001000000025f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028000000004847304402205d6058484157235b06028c30736c15613a28bdb768ee628094ca8b0030d4d6eb0220328789c9a2ec27ddaec0ad5ef58efded42e6ea17c2e1ce838f3d6913f5e95db601ffffffff5f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028010000004a493046022100c45af050d3cea806cedd0ab22520c53ebe63b987b8954146cdca42487b84bdd6022100b9b027716a6b59e640da50a864d6dd8a0ef24c76ce62391fa3eabaf4d2886d2d01ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac000000000100000002e2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b0000000048473044022016e7a727a061ea2254a6c358376aaa617ac537eb836c77d646ebda4c748aac8b0220192ce28bf9f2c06a6467e6531e27648d2b3e2e2bae85159c9242939840295ba501ffffffffe2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b010000004a493046022100b7a1a755588d4190118936e15cd217d133b0e4a53c3c15924010d5648d8925c9022100aaef031874db2114f2d869ac2de4ae53908fbfea5b2b1862e181626bb9005c9f01ffffffff0200e1f505000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00000000"), SER_NETWORK, PROTOCOL_VERSION); + stream >> block; + + CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_NONE); + // Match the first transaction + filter.insert(uint256("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70")); + + CMerkleBlock merkleBlock(block, filter); + BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash()); + + BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1); + pair pair = merkleBlock.vMatchedTxn[0]; + + BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70")); + BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0); + + vector vMatched; + BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot); + BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size()); + for (unsigned int i = 0; i < vMatched.size(); i++) + BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second); + + // Match an output from the second transaction (the pubkey for address 1DZTzaBHUDM7T3QvUKBz4qXMRpkg8jsfB5) + // This should not match the third transaction though it spends the output matched + // It will match the fourth transaction, which has another pay-to-pubkey output to the same address + filter.insert(ParseHex("044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45af")); + + merkleBlock = CMerkleBlock(block, filter); + BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash()); + + BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 3); + + BOOST_CHECK(pair == merkleBlock.vMatchedTxn[0]); + + BOOST_CHECK(merkleBlock.vMatchedTxn[1].second == uint256("0x28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f")); + BOOST_CHECK(merkleBlock.vMatchedTxn[1].first == 1); + + BOOST_CHECK(merkleBlock.vMatchedTxn[2].second == uint256("0x3c1d7e82342158e4109df2e0b6348b6e84e403d8b4046d7007663ace63cddb23")); + BOOST_CHECK(merkleBlock.vMatchedTxn[2].first == 3); + + BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot); + BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size()); + for (unsigned int i = 0; i < vMatched.size(); i++) + BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second); +} + +BOOST_AUTO_TEST_CASE(merkle_block_3_and_serialize) +{ + // Random real block (000000000000dab0130bbcc991d3d7ae6b81aa6f50a798888dfe62337458dc45) + // With one tx + CBlock block; + CDataStream stream(ParseHex("0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b4b8b0000000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f196367291b4d4c86041b8fa45d630101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff08044c86041b020a02ffffffff0100f2052a01000000434104ecd3229b0571c3be876feaac0442a9f13c5a572742927af1dc623353ecf8c202225f64868137a18cdd85cbbb4c74fbccfd4f49639cf1bdc94a5672bb15ad5d4cac00000000"), SER_NETWORK, PROTOCOL_VERSION); + stream >> block; + + CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + // Match the only transaction + filter.insert(uint256("0x63194f18be0af63f2c6bc9dc0f777cbefed3d9415c4af83f3ee3a3d669c00cb5")); + + CMerkleBlock merkleBlock(block, filter); + BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash()); + + BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1); + + BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256("0x63194f18be0af63f2c6bc9dc0f777cbefed3d9415c4af83f3ee3a3d669c00cb5")); + BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0); + + vector vMatched; + BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot); + BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size()); + for (unsigned int i = 0; i < vMatched.size(); i++) + BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second); + + CDataStream merkleStream(SER_NETWORK, PROTOCOL_VERSION); + merkleStream << merkleBlock; + + vector vch = ParseHex("0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b4b8b0000000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f196367291b4d4c86041b8fa45d630100000001b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f19630101"); + vector expected(vch.size()); + + for (unsigned int i = 0; i < vch.size(); i++) + expected[i] = (char)vch[i]; + + BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), merkleStream.begin(), merkleStream.end()); +} + +BOOST_AUTO_TEST_CASE(merkle_block_4) +{ + // Random real block (000000000000b731f2eef9e8c63173adfb07e41bd53eb0ef0a6b720d6cb6dea4) + // With 7 txes + CBlock block; + CDataStream stream(ParseHex("0100000082bb869cf3a793432a66e826e05a6fc37469f8efb7421dc880670100000000007f16c5962e8bd963659c793ce370d95f093bc7e367117b3c30c1f8fdd0d9728776381b4d4c86041b554b85290701000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0136ffffffff0100f2052a01000000434104eaafc2314def4ca98ac970241bcab022b9c1e1f4ea423a20f134c876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad1357231a2252247d97a46a91ac000000000100000001bcad20a6a29827d1424f08989255120bf7f3e9e3cdaaa6bb31b0737fe048724300000000494830450220356e834b046cadc0f8ebb5a8a017b02de59c86305403dad52cd77b55af062ea10221009253cd6c119d4729b77c978e1e2aa19f5ea6e0e52b3f16e32fa608cd5bab753901ffffffff02008d380c010000001976a9142b4b8072ecbba129b6453c63e129e643207249ca88ac0065cd1d000000001976a9141b8dd13b994bcfc787b32aeadf58ccb3615cbd5488ac000000000100000003fdacf9b3eb077412e7a968d2e4f11b9a9dee312d666187ed77ee7d26af16cb0b000000008c493046022100ea1608e70911ca0de5af51ba57ad23b9a51db8d28f82c53563c56a05c20f5a87022100a8bdc8b4a8acc8634c6b420410150775eb7f2474f5615f7fccd65af30f310fbf01410465fdf49e29b06b9a1582287b6279014f834edc317695d125ef623c1cc3aaece245bd69fcad7508666e9c74a49dc9056d5fc14338ef38118dc4afae5fe2c585caffffffff309e1913634ecb50f3c4f83e96e70b2df071b497b8973a3e75429df397b5af83000000004948304502202bdb79c596a9ffc24e96f4386199aba386e9bc7b6071516e2b51dda942b3a1ed022100c53a857e76b724fc14d45311eac5019650d415c3abb5428f3aae16d8e69bec2301ffffffff2089e33491695080c9edc18a428f7d834db5b6d372df13ce2b1b0e0cbcb1e6c10000000049483045022100d4ce67c5896ee251c810ac1ff9ceccd328b497c8f553ab6e08431e7d40bad6b5022033119c0c2b7d792d31f1187779c7bd95aefd93d90a715586d73801d9b47471c601ffffffff0100714460030000001976a914c7b55141d097ea5df7a0ed330cf794376e53ec8d88ac0000000001000000045bf0e214aa4069a3e792ecee1e1bf0c1d397cde8dd08138f4b72a00681743447000000008b48304502200c45de8c4f3e2c1821f2fc878cba97b1e6f8807d94930713aa1c86a67b9bf1e40221008581abfef2e30f957815fc89978423746b2086375ca8ecf359c85c2a5b7c88ad01410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffffd669f7d7958d40fc59d2253d88e0f248e29b599c80bbcec344a83dda5f9aa72c000000008a473044022078124c8beeaa825f9e0b30bff96e564dd859432f2d0cb3b72d3d5d93d38d7e930220691d233b6c0f995be5acb03d70a7f7a65b6bc9bdd426260f38a1346669507a3601410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95fffffffff878af0d93f5229a68166cf051fd372bb7a537232946e0a46f53636b4dafdaa4000000008c493046022100c717d1714551663f69c3c5759bdbb3a0fcd3fab023abc0e522fe6440de35d8290221008d9cbe25bffc44af2b18e81c58eb37293fd7fe1c2e7b46fc37ee8c96c50ab1e201410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffff27f2b668859cd7f2f894aa0fd2d9e60963bcd07c88973f425f999b8cbfd7a1e2000000008c493046022100e00847147cbf517bcc2f502f3ddc6d284358d102ed20d47a8aa788a62f0db780022100d17b2d6fa84dcaf1c95d88d7e7c30385aecf415588d749afd3ec81f6022cecd701410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffff0100c817a8040000001976a914b6efd80d99179f4f4ff6f4dd0a007d018c385d2188ac000000000100000001834537b2f1ce8ef9373a258e10545ce5a50b758df616cd4356e0032554ebd3c4000000008b483045022100e68f422dd7c34fdce11eeb4509ddae38201773dd62f284e8aa9d96f85099d0b002202243bd399ff96b649a0fad05fa759d6a882f0af8c90cf7632c2840c29070aec20141045e58067e815c2f464c6a2a15f987758374203895710c2d452442e28496ff38ba8f5fd901dc20e29e88477167fe4fc299bf818fd0d9e1632d467b2a3d9503b1aaffffffff0280d7e636030000001976a914f34c3e10eb387efe872acb614c89e78bfca7815d88ac404b4c00000000001976a914a84e272933aaf87e1715d7786c51dfaeb5b65a6f88ac00000000010000000143ac81c8e6f6ef307dfe17f3d906d999e23e0189fda838c5510d850927e03ae7000000008c4930460221009c87c344760a64cb8ae6685a3eec2c1ac1bed5b88c87de51acd0e124f266c16602210082d07c037359c3a257b5c63ebd90f5a5edf97b2ac1c434b08ca998839f346dd40141040ba7e521fa7946d12edbb1d1e95a15c34bd4398195e86433c92b431cd315f455fe30032ede69cad9d1e1ed6c3c4ec0dbfced53438c625462afb792dcb098544bffffffff0240420f00000000001976a9144676d1b820d63ec272f1900d59d43bc6463d96f888ac40420f00000000001976a914648d04341d00d7968b3405c034adc38d4d8fb9bd88ac00000000010000000248cc917501ea5c55f4a8d2009c0567c40cfe037c2e71af017d0a452ff705e3f1000000008b483045022100bf5fdc86dc5f08a5d5c8e43a8c9d5b1ed8c65562e280007b52b133021acd9acc02205e325d613e555f772802bf413d36ba807892ed1a690a77811d3033b3de226e0a01410429fa713b124484cb2bd7b5557b2c0b9df7b2b1fee61825eadc5ae6c37a9920d38bfccdc7dc3cb0c47d7b173dbc9db8d37db0a33ae487982c59c6f8606e9d1791ffffffff41ed70551dd7e841883ab8f0b16bf04176b7d1480e4f0af9f3d4c3595768d068000000008b4830450221008513ad65187b903aed1102d1d0c47688127658c51106753fed0151ce9c16b80902201432b9ebcb87bd04ceb2de66035fbbaf4bf8b00d1cfe41f1a1f7338f9ad79d210141049d4cf80125bf50be1709f718c07ad15d0fc612b7da1f5570dddc35f2a352f0f27c978b06820edca9ef982c35fda2d255afba340068c5035552368bc7200c1488ffffffff0100093d00000000001976a9148edb68822f1ad580b043c7b3df2e400f8699eb4888ac00000000"), SER_NETWORK, PROTOCOL_VERSION); + stream >> block; + + CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + // Match the last transaction + filter.insert(uint256("0x0a2a92f0bda4727d0a13eaddf4dd9ac6b5c61a1429e6b2b818f19b15df0ac154")); + + CMerkleBlock merkleBlock(block, filter); + BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash()); + + BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1); + pair pair = merkleBlock.vMatchedTxn[0]; + + BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256("0x0a2a92f0bda4727d0a13eaddf4dd9ac6b5c61a1429e6b2b818f19b15df0ac154")); + BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 6); + + vector vMatched; + BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot); + BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size()); + for (unsigned int i = 0; i < vMatched.size(); i++) + BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second); + + // Also match the 4th transaction + filter.insert(uint256("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041")); + merkleBlock = CMerkleBlock(block, filter); + BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash()); + + BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 2); + + BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041")); + BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 3); + + BOOST_CHECK(merkleBlock.vMatchedTxn[1] == pair); + + BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot); + BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size()); + for (unsigned int i = 0; i < vMatched.size(); i++) + BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second); +} + +BOOST_AUTO_TEST_CASE(merkle_block_4_test_p2pubkey_only) +{ + // Random real block (000000000000b731f2eef9e8c63173adfb07e41bd53eb0ef0a6b720d6cb6dea4) + // With 7 txes + CBlock block; + CDataStream stream(ParseHex("0100000082bb869cf3a793432a66e826e05a6fc37469f8efb7421dc880670100000000007f16c5962e8bd963659c793ce370d95f093bc7e367117b3c30c1f8fdd0d9728776381b4d4c86041b554b85290701000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0136ffffffff0100f2052a01000000434104eaafc2314def4ca98ac970241bcab022b9c1e1f4ea423a20f134c876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad1357231a2252247d97a46a91ac000000000100000001bcad20a6a29827d1424f08989255120bf7f3e9e3cdaaa6bb31b0737fe048724300000000494830450220356e834b046cadc0f8ebb5a8a017b02de59c86305403dad52cd77b55af062ea10221009253cd6c119d4729b77c978e1e2aa19f5ea6e0e52b3f16e32fa608cd5bab753901ffffffff02008d380c010000001976a9142b4b8072ecbba129b6453c63e129e643207249ca88ac0065cd1d000000001976a9141b8dd13b994bcfc787b32aeadf58ccb3615cbd5488ac000000000100000003fdacf9b3eb077412e7a968d2e4f11b9a9dee312d666187ed77ee7d26af16cb0b000000008c493046022100ea1608e70911ca0de5af51ba57ad23b9a51db8d28f82c53563c56a05c20f5a87022100a8bdc8b4a8acc8634c6b420410150775eb7f2474f5615f7fccd65af30f310fbf01410465fdf49e29b06b9a1582287b6279014f834edc317695d125ef623c1cc3aaece245bd69fcad7508666e9c74a49dc9056d5fc14338ef38118dc4afae5fe2c585caffffffff309e1913634ecb50f3c4f83e96e70b2df071b497b8973a3e75429df397b5af83000000004948304502202bdb79c596a9ffc24e96f4386199aba386e9bc7b6071516e2b51dda942b3a1ed022100c53a857e76b724fc14d45311eac5019650d415c3abb5428f3aae16d8e69bec2301ffffffff2089e33491695080c9edc18a428f7d834db5b6d372df13ce2b1b0e0cbcb1e6c10000000049483045022100d4ce67c5896ee251c810ac1ff9ceccd328b497c8f553ab6e08431e7d40bad6b5022033119c0c2b7d792d31f1187779c7bd95aefd93d90a715586d73801d9b47471c601ffffffff0100714460030000001976a914c7b55141d097ea5df7a0ed330cf794376e53ec8d88ac0000000001000000045bf0e214aa4069a3e792ecee1e1bf0c1d397cde8dd08138f4b72a00681743447000000008b48304502200c45de8c4f3e2c1821f2fc878cba97b1e6f8807d94930713aa1c86a67b9bf1e40221008581abfef2e30f957815fc89978423746b2086375ca8ecf359c85c2a5b7c88ad01410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffffd669f7d7958d40fc59d2253d88e0f248e29b599c80bbcec344a83dda5f9aa72c000000008a473044022078124c8beeaa825f9e0b30bff96e564dd859432f2d0cb3b72d3d5d93d38d7e930220691d233b6c0f995be5acb03d70a7f7a65b6bc9bdd426260f38a1346669507a3601410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95fffffffff878af0d93f5229a68166cf051fd372bb7a537232946e0a46f53636b4dafdaa4000000008c493046022100c717d1714551663f69c3c5759bdbb3a0fcd3fab023abc0e522fe6440de35d8290221008d9cbe25bffc44af2b18e81c58eb37293fd7fe1c2e7b46fc37ee8c96c50ab1e201410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffff27f2b668859cd7f2f894aa0fd2d9e60963bcd07c88973f425f999b8cbfd7a1e2000000008c493046022100e00847147cbf517bcc2f502f3ddc6d284358d102ed20d47a8aa788a62f0db780022100d17b2d6fa84dcaf1c95d88d7e7c30385aecf415588d749afd3ec81f6022cecd701410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffff0100c817a8040000001976a914b6efd80d99179f4f4ff6f4dd0a007d018c385d2188ac000000000100000001834537b2f1ce8ef9373a258e10545ce5a50b758df616cd4356e0032554ebd3c4000000008b483045022100e68f422dd7c34fdce11eeb4509ddae38201773dd62f284e8aa9d96f85099d0b002202243bd399ff96b649a0fad05fa759d6a882f0af8c90cf7632c2840c29070aec20141045e58067e815c2f464c6a2a15f987758374203895710c2d452442e28496ff38ba8f5fd901dc20e29e88477167fe4fc299bf818fd0d9e1632d467b2a3d9503b1aaffffffff0280d7e636030000001976a914f34c3e10eb387efe872acb614c89e78bfca7815d88ac404b4c00000000001976a914a84e272933aaf87e1715d7786c51dfaeb5b65a6f88ac00000000010000000143ac81c8e6f6ef307dfe17f3d906d999e23e0189fda838c5510d850927e03ae7000000008c4930460221009c87c344760a64cb8ae6685a3eec2c1ac1bed5b88c87de51acd0e124f266c16602210082d07c037359c3a257b5c63ebd90f5a5edf97b2ac1c434b08ca998839f346dd40141040ba7e521fa7946d12edbb1d1e95a15c34bd4398195e86433c92b431cd315f455fe30032ede69cad9d1e1ed6c3c4ec0dbfced53438c625462afb792dcb098544bffffffff0240420f00000000001976a9144676d1b820d63ec272f1900d59d43bc6463d96f888ac40420f00000000001976a914648d04341d00d7968b3405c034adc38d4d8fb9bd88ac00000000010000000248cc917501ea5c55f4a8d2009c0567c40cfe037c2e71af017d0a452ff705e3f1000000008b483045022100bf5fdc86dc5f08a5d5c8e43a8c9d5b1ed8c65562e280007b52b133021acd9acc02205e325d613e555f772802bf413d36ba807892ed1a690a77811d3033b3de226e0a01410429fa713b124484cb2bd7b5557b2c0b9df7b2b1fee61825eadc5ae6c37a9920d38bfccdc7dc3cb0c47d7b173dbc9db8d37db0a33ae487982c59c6f8606e9d1791ffffffff41ed70551dd7e841883ab8f0b16bf04176b7d1480e4f0af9f3d4c3595768d068000000008b4830450221008513ad65187b903aed1102d1d0c47688127658c51106753fed0151ce9c16b80902201432b9ebcb87bd04ceb2de66035fbbaf4bf8b00d1cfe41f1a1f7338f9ad79d210141049d4cf80125bf50be1709f718c07ad15d0fc612b7da1f5570dddc35f2a352f0f27c978b06820edca9ef982c35fda2d255afba340068c5035552368bc7200c1488ffffffff0100093d00000000001976a9148edb68822f1ad580b043c7b3df2e400f8699eb4888ac00000000"), SER_NETWORK, PROTOCOL_VERSION); + stream >> block; + + CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_P2PUBKEY_ONLY); + // Match the generation pubkey + filter.insert(ParseHex("04eaafc2314def4ca98ac970241bcab022b9c1e1f4ea423a20f134c876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad1357231a2252247d97a46a91")); + // ...and the output address of the 4th transaction + filter.insert(ParseHex("b6efd80d99179f4f4ff6f4dd0a007d018c385d21")); + + CMerkleBlock merkleBlock(block, filter); + BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash()); + + // We should match the generation outpoint + BOOST_CHECK(filter.contains(COutPoint(uint256("0x147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b"), 0))); + // ... but not the 4th transaction's output (its not pay-2-pubkey) + BOOST_CHECK(!filter.contains(COutPoint(uint256("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"), 0))); +} + +BOOST_AUTO_TEST_CASE(merkle_block_4_test_update_none) +{ + // Random real block (000000000000b731f2eef9e8c63173adfb07e41bd53eb0ef0a6b720d6cb6dea4) + // With 7 txes + CBlock block; + CDataStream stream(ParseHex("0100000082bb869cf3a793432a66e826e05a6fc37469f8efb7421dc880670100000000007f16c5962e8bd963659c793ce370d95f093bc7e367117b3c30c1f8fdd0d9728776381b4d4c86041b554b85290701000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0136ffffffff0100f2052a01000000434104eaafc2314def4ca98ac970241bcab022b9c1e1f4ea423a20f134c876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad1357231a2252247d97a46a91ac000000000100000001bcad20a6a29827d1424f08989255120bf7f3e9e3cdaaa6bb31b0737fe048724300000000494830450220356e834b046cadc0f8ebb5a8a017b02de59c86305403dad52cd77b55af062ea10221009253cd6c119d4729b77c978e1e2aa19f5ea6e0e52b3f16e32fa608cd5bab753901ffffffff02008d380c010000001976a9142b4b8072ecbba129b6453c63e129e643207249ca88ac0065cd1d000000001976a9141b8dd13b994bcfc787b32aeadf58ccb3615cbd5488ac000000000100000003fdacf9b3eb077412e7a968d2e4f11b9a9dee312d666187ed77ee7d26af16cb0b000000008c493046022100ea1608e70911ca0de5af51ba57ad23b9a51db8d28f82c53563c56a05c20f5a87022100a8bdc8b4a8acc8634c6b420410150775eb7f2474f5615f7fccd65af30f310fbf01410465fdf49e29b06b9a1582287b6279014f834edc317695d125ef623c1cc3aaece245bd69fcad7508666e9c74a49dc9056d5fc14338ef38118dc4afae5fe2c585caffffffff309e1913634ecb50f3c4f83e96e70b2df071b497b8973a3e75429df397b5af83000000004948304502202bdb79c596a9ffc24e96f4386199aba386e9bc7b6071516e2b51dda942b3a1ed022100c53a857e76b724fc14d45311eac5019650d415c3abb5428f3aae16d8e69bec2301ffffffff2089e33491695080c9edc18a428f7d834db5b6d372df13ce2b1b0e0cbcb1e6c10000000049483045022100d4ce67c5896ee251c810ac1ff9ceccd328b497c8f553ab6e08431e7d40bad6b5022033119c0c2b7d792d31f1187779c7bd95aefd93d90a715586d73801d9b47471c601ffffffff0100714460030000001976a914c7b55141d097ea5df7a0ed330cf794376e53ec8d88ac0000000001000000045bf0e214aa4069a3e792ecee1e1bf0c1d397cde8dd08138f4b72a00681743447000000008b48304502200c45de8c4f3e2c1821f2fc878cba97b1e6f8807d94930713aa1c86a67b9bf1e40221008581abfef2e30f957815fc89978423746b2086375ca8ecf359c85c2a5b7c88ad01410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffffd669f7d7958d40fc59d2253d88e0f248e29b599c80bbcec344a83dda5f9aa72c000000008a473044022078124c8beeaa825f9e0b30bff96e564dd859432f2d0cb3b72d3d5d93d38d7e930220691d233b6c0f995be5acb03d70a7f7a65b6bc9bdd426260f38a1346669507a3601410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95fffffffff878af0d93f5229a68166cf051fd372bb7a537232946e0a46f53636b4dafdaa4000000008c493046022100c717d1714551663f69c3c5759bdbb3a0fcd3fab023abc0e522fe6440de35d8290221008d9cbe25bffc44af2b18e81c58eb37293fd7fe1c2e7b46fc37ee8c96c50ab1e201410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffff27f2b668859cd7f2f894aa0fd2d9e60963bcd07c88973f425f999b8cbfd7a1e2000000008c493046022100e00847147cbf517bcc2f502f3ddc6d284358d102ed20d47a8aa788a62f0db780022100d17b2d6fa84dcaf1c95d88d7e7c30385aecf415588d749afd3ec81f6022cecd701410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffff0100c817a8040000001976a914b6efd80d99179f4f4ff6f4dd0a007d018c385d2188ac000000000100000001834537b2f1ce8ef9373a258e10545ce5a50b758df616cd4356e0032554ebd3c4000000008b483045022100e68f422dd7c34fdce11eeb4509ddae38201773dd62f284e8aa9d96f85099d0b002202243bd399ff96b649a0fad05fa759d6a882f0af8c90cf7632c2840c29070aec20141045e58067e815c2f464c6a2a15f987758374203895710c2d452442e28496ff38ba8f5fd901dc20e29e88477167fe4fc299bf818fd0d9e1632d467b2a3d9503b1aaffffffff0280d7e636030000001976a914f34c3e10eb387efe872acb614c89e78bfca7815d88ac404b4c00000000001976a914a84e272933aaf87e1715d7786c51dfaeb5b65a6f88ac00000000010000000143ac81c8e6f6ef307dfe17f3d906d999e23e0189fda838c5510d850927e03ae7000000008c4930460221009c87c344760a64cb8ae6685a3eec2c1ac1bed5b88c87de51acd0e124f266c16602210082d07c037359c3a257b5c63ebd90f5a5edf97b2ac1c434b08ca998839f346dd40141040ba7e521fa7946d12edbb1d1e95a15c34bd4398195e86433c92b431cd315f455fe30032ede69cad9d1e1ed6c3c4ec0dbfced53438c625462afb792dcb098544bffffffff0240420f00000000001976a9144676d1b820d63ec272f1900d59d43bc6463d96f888ac40420f00000000001976a914648d04341d00d7968b3405c034adc38d4d8fb9bd88ac00000000010000000248cc917501ea5c55f4a8d2009c0567c40cfe037c2e71af017d0a452ff705e3f1000000008b483045022100bf5fdc86dc5f08a5d5c8e43a8c9d5b1ed8c65562e280007b52b133021acd9acc02205e325d613e555f772802bf413d36ba807892ed1a690a77811d3033b3de226e0a01410429fa713b124484cb2bd7b5557b2c0b9df7b2b1fee61825eadc5ae6c37a9920d38bfccdc7dc3cb0c47d7b173dbc9db8d37db0a33ae487982c59c6f8606e9d1791ffffffff41ed70551dd7e841883ab8f0b16bf04176b7d1480e4f0af9f3d4c3595768d068000000008b4830450221008513ad65187b903aed1102d1d0c47688127658c51106753fed0151ce9c16b80902201432b9ebcb87bd04ceb2de66035fbbaf4bf8b00d1cfe41f1a1f7338f9ad79d210141049d4cf80125bf50be1709f718c07ad15d0fc612b7da1f5570dddc35f2a352f0f27c978b06820edca9ef982c35fda2d255afba340068c5035552368bc7200c1488ffffffff0100093d00000000001976a9148edb68822f1ad580b043c7b3df2e400f8699eb4888ac00000000"), SER_NETWORK, PROTOCOL_VERSION); + stream >> block; + + CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_NONE); + // Match the generation pubkey + filter.insert(ParseHex("04eaafc2314def4ca98ac970241bcab022b9c1e1f4ea423a20f134c876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad1357231a2252247d97a46a91")); + // ...and the output address of the 4th transaction + filter.insert(ParseHex("b6efd80d99179f4f4ff6f4dd0a007d018c385d21")); + + CMerkleBlock merkleBlock(block, filter); + BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash()); + + // We shouldn't match any outpoints (UPDATE_NONE) + BOOST_CHECK(!filter.contains(COutPoint(uint256("0x147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b"), 0))); + BOOST_CHECK(!filter.contains(COutPoint(uint256("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"), 0))); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/canonical_tests.cpp b/src/test/canonical_tests.cpp new file mode 100644 index 0000000..42d21f8 --- /dev/null +++ b/src/test/canonical_tests.cpp @@ -0,0 +1,87 @@ +// +// Unit tests for canonical signatures + +#include "json/json_spirit_writer_template.h" +#include +#include + +#include "key.h" +#include "script.h" +#include "util.h" + +using namespace std; +using namespace json_spirit; + + +// In script_tests.cpp +extern Array read_json(const std::string& filename); + +BOOST_AUTO_TEST_SUITE(canonical_tests) + +// OpenSSL-based test for canonical signature (without test for hashtype byte) +bool static IsCanonicalSignature_OpenSSL_inner(const std::vector& vchSig) +{ + if (vchSig.size() == 0) + return false; + const unsigned char *input = &vchSig[0]; + ECDSA_SIG *psig = NULL; + d2i_ECDSA_SIG(&psig, &input, vchSig.size()); + if (psig == NULL) + return false; + unsigned char buf[256]; + unsigned char *pbuf = buf; + unsigned int nLen = i2d_ECDSA_SIG(psig, NULL); + if (nLen != vchSig.size()) { + ECDSA_SIG_free(psig); + return false; + } + nLen = i2d_ECDSA_SIG(psig, &pbuf); + ECDSA_SIG_free(psig); + return (memcmp(&vchSig[0], &buf[0], nLen) == 0); +} + +// OpenSSL-based test for canonical signature +bool static IsCanonicalSignature_OpenSSL(const std::vector &vchSignature) { + if (vchSignature.size() < 1) + return false; + if (vchSignature.size() > 127) + return false; + if (vchSignature[vchSignature.size() - 1] & 0x7C) + return false; + + std::vector vchSig(vchSignature); + vchSig.pop_back(); + if (!IsCanonicalSignature_OpenSSL_inner(vchSig)) + return false; + return true; +} + +BOOST_AUTO_TEST_CASE(script_canon) +{ + Array tests = read_json("sig_canonical.json"); + + BOOST_FOREACH(Value &tv, tests) { + string test = tv.get_str(); + if (IsHex(test)) { + std::vector sig = ParseHex(test); + BOOST_CHECK_MESSAGE(IsCanonicalSignature(sig), test); + BOOST_CHECK_MESSAGE(IsCanonicalSignature_OpenSSL(sig), test); + } + } +} + +BOOST_AUTO_TEST_CASE(script_noncanon) +{ + Array tests = read_json("sig_noncanonical.json"); + + BOOST_FOREACH(Value &tv, tests) { + string test = tv.get_str(); + if (IsHex(test)) { + std::vector sig = ParseHex(test); + BOOST_CHECK_MESSAGE(!IsCanonicalSignature(sig), test); + BOOST_CHECK_MESSAGE(!IsCanonicalSignature_OpenSSL(sig), test); + } + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/checkblock_tests.cpp b/src/test/checkblock_tests.cpp new file mode 100644 index 0000000..3cfb6db --- /dev/null +++ b/src/test/checkblock_tests.cpp @@ -0,0 +1,66 @@ +// +// Unit tests for block.CheckBlock() +// +#include + +#include // for 'map_list_of()' +#include +#include +#include + +#include "main.h" +#include "wallet.h" +#include "net.h" +#include "util.h" + +BOOST_AUTO_TEST_SUITE(CheckBlock_tests) + +bool +read_block(const std::string& filename, CBlock& block) +{ + namespace fs = boost::filesystem; + fs::path testFile = fs::current_path() / "test" / "data" / filename; +#ifdef TEST_DATA_DIR + if (!fs::exists(testFile)) + { + testFile = fs::path(BOOST_PP_STRINGIZE(TEST_DATA_DIR)) / filename; + } +#endif + FILE* fp = fopen(testFile.string().c_str(), "rb"); + if (!fp) return false; + + fseek(fp, 8, SEEK_SET); // skip msgheader/size + + CAutoFile filein = CAutoFile(fp, SER_DISK, CLIENT_VERSION); + if (!filein) return false; + + filein >> block; + + return true; +} + +BOOST_AUTO_TEST_CASE(May15) +{ + // Putting a 1MB binary file in the git repository is not a great + // idea, so this test is only run if you manually download + // test/data/Mar12Fork.dat from + // http://sourceforge.net/projects/bitcoin/files/Bitcoin/blockchain/Mar12Fork.dat/download + unsigned int tMay15 = 1368576000; + SetMockTime(tMay15); // Test as if it was right at May 15 + + CBlock forkingBlock; + if (read_block("Mar12Fork.dat", forkingBlock)) + { + CValidationState state; + forkingBlock.nTime = tMay15-1; // Invalidates PoW + BOOST_CHECK(!forkingBlock.CheckBlock(state, false, false)); + + // After May 15'th, big blocks are OK: + forkingBlock.nTime = tMay15; // Invalidates PoW + BOOST_CHECK(forkingBlock.CheckBlock(state, false, false)); + } + + SetMockTime(0); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/compress_tests.cpp b/src/test/compress_tests.cpp new file mode 100644 index 0000000..71b86bc --- /dev/null +++ b/src/test/compress_tests.cpp @@ -0,0 +1,62 @@ +#include + +#include +#include + +#include "main.h" + +// amounts 0.00000001 .. 0.00100000 +#define NUM_MULTIPLES_UNIT 100000 + +// amounts 0.01 .. 100.00 +#define NUM_MULTIPLES_CENT 10000 + +// amounts 1 .. 10000 +#define NUM_MULTIPLES_1BTC 10000 + +// amounts 50 .. 21000000 +#define NUM_MULTIPLES_50BTC 420000 + +using namespace std; + +BOOST_AUTO_TEST_SUITE(compress_tests) + +bool static TestEncode(uint64 in) { + return in == CTxOutCompressor::DecompressAmount(CTxOutCompressor::CompressAmount(in)); +} + +bool static TestDecode(uint64 in) { + return in == CTxOutCompressor::CompressAmount(CTxOutCompressor::DecompressAmount(in)); +} + +bool static TestPair(uint64 dec, uint64 enc) { + return CTxOutCompressor::CompressAmount(dec) == enc && + CTxOutCompressor::DecompressAmount(enc) == dec; +} + +BOOST_AUTO_TEST_CASE(compress_amounts) +{ + BOOST_CHECK(TestPair( 0, 0x0)); + BOOST_CHECK(TestPair( 1, 0x1)); + BOOST_CHECK(TestPair( CENT, 0x7)); + BOOST_CHECK(TestPair( COIN, 0x9)); + BOOST_CHECK(TestPair( 50*COIN, 0x32)); + BOOST_CHECK(TestPair(21000000*COIN, 0x1406f40)); + + for (uint64 i = 1; i <= NUM_MULTIPLES_UNIT; i++) + BOOST_CHECK(TestEncode(i)); + + for (uint64 i = 1; i <= NUM_MULTIPLES_CENT; i++) + BOOST_CHECK(TestEncode(i * CENT)); + + for (uint64 i = 1; i <= NUM_MULTIPLES_1BTC; i++) + BOOST_CHECK(TestEncode(i * COIN)); + + for (uint64 i = 1; i <= NUM_MULTIPLES_50BTC; i++) + BOOST_CHECK(TestEncode(i * 50 * COIN)); + + for (uint64 i = 0; i < 100000; i++) + BOOST_CHECK(TestDecode(i)); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/data/alertTests b/src/test/data/alertTests new file mode 100644 index 0000000..24e906c Binary files /dev/null and b/src/test/data/alertTests differ diff --git a/src/test/data/base58_encode_decode.json b/src/test/data/base58_encode_decode.json new file mode 100644 index 0000000..9448f25 --- /dev/null +++ b/src/test/data/base58_encode_decode.json @@ -0,0 +1,14 @@ +[ +["", ""], +["61", "2g"], +["626262", "a3gV"], +["636363", "aPEr"], +["73696d706c792061206c6f6e6720737472696e67", "2cFupjhnEsSn59qHXstmK2ffpLv2"], +["00eb15231dfceb60925886b67d065299925915aeb172c06647", "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L"], +["516b6fcd0f", "ABnLTmg"], +["bf4f89001e670274dd", "3SEo3LWLoPntC"], +["572e4794", "3EFU7m"], +["ecac89cad93923c02321", "EJDM8drfXA6uyA"], +["10c8511e", "Rt5zm"], +["00000000000000000000", "1111111111"] +] diff --git a/src/test/data/base58_keys_invalid.json b/src/test/data/base58_keys_invalid.json new file mode 100644 index 0000000..a088620 --- /dev/null +++ b/src/test/data/base58_keys_invalid.json @@ -0,0 +1,152 @@ +[ + [ + "" + ], + [ + "x" + ], + [ + "37qgekLpCCHrQuSjvX3fs496FWTGsHFHizjJAs6NPcR47aefnnCWECAhHV6E3g4YN7u7Yuwod5Y" + ], + [ + "dzb7VV1Ui55BARxv7ATxAtCUeJsANKovDGWFVgpTbhq9gvPqP3yv" + ], + [ + "MuNu7ZAEDFiHthiunm7dPjwKqrVNCM3mAz6rP9zFveQu14YA8CxExSJTHcVP9DErn6u84E6Ej7S" + ], + [ + "rPpQpYknyNQ5AEHuY6H8ijJJrYc2nDKKk9jjmKEXsWzyAQcFGpDLU2Zvsmoi8JLR7hAwoy3RQWf" + ], + [ + "4Uc3FmN6NQ6zLBK5QQBXRBUREaaHwCZYsGCueHauuDmJpZKn6jkEskMB2Zi2CNgtb5r6epWEFfUJq" + ], + [ + "7aQgR5DFQ25vyXmqZAWmnVCjL3PkBcdVkBUpjrjMTcghHx3E8wb" + ], + [ + "17QpPprjeg69fW1DV8DcYYCKvWjYhXvWkov6MJ1iTTvMFj6weAqW7wybZeH57WTNxXVCRH4veVs" + ], + [ + "KxuACDviz8Xvpn1xAh9MfopySZNuyajYMZWz16Dv2mHHryznWUp3" + ], + [ + "7nK3GSmqdXJQtdohvGfJ7KsSmn3TmGqExug49583bDAL91pVSGq5xS9SHoAYL3Wv3ijKTit65th" + ], + [ + "cTivdBmq7bay3RFGEBBuNfMh2P1pDCgRYN2Wbxmgwr4ki3jNUL2va" + ], + [ + "gjMV4vjNjyMrna4fsAr8bWxAbwtmMUBXJS3zL4NJt5qjozpbQLmAfK1uA3CquSqsZQMpoD1g2nk" + ], + [ + "emXm1naBMoVzPjbk7xpeTVMFy4oDEe25UmoyGgKEB1gGWsK8kRGs" + ], + [ + "7VThQnNRj1o3Zyvc7XHPRrjDf8j2oivPTeDXnRPYWeYGE4pXeRJDZgf28ppti5hsHWXS2GSobdqyo" + ], + [ + "1G9u6oCVCPh2o8m3t55ACiYvG1y5BHewUkDSdiQarDcYXXhFHYdzMdYfUAhfxn5vNZBwpgUNpso" + ], + [ + "31QQ7ZMLkScDiB4VyZjuptr7AEc9j1SjstF7pRoLhHTGkW4Q2y9XELobQmhhWxeRvqcukGd1XCq" + ], + [ + "DHqKSnpxa8ZdQyH8keAhvLTrfkyBMQxqngcQA5N8LQ9KVt25kmGN" + ], + [ + "2LUHcJPbwLCy9GLH1qXmfmAwvadWw4bp4PCpDfduLqV17s6iDcy1imUwhQJhAoNoN1XNmweiJP4i" + ], + [ + "7USRzBXAnmck8fX9HmW7RAb4qt92VFX6soCnts9s74wxm4gguVhtG5of8fZGbNPJA83irHVY6bCos" + ], + [ + "1DGezo7BfVebZxAbNT3XGujdeHyNNBF3vnficYoTSp4PfK2QaML9bHzAMxke3wdKdHYWmsMTJVu" + ], + [ + "2D12DqDZKwCxxkzs1ZATJWvgJGhQ4cFi3WrizQ5zLAyhN5HxuAJ1yMYaJp8GuYsTLLxTAz6otCfb" + ], + [ + "8AFJzuTujXjw1Z6M3fWhQ1ujDW7zsV4ePeVjVo7D1egERqSW9nZ" + ], + [ + "163Q17qLbTCue8YY3AvjpUhotuaodLm2uqMhpYirsKjVqnxJRWTEoywMVY3NbBAHuhAJ2cF9GAZ" + ], + [ + "2MnmgiRH4eGLyLc9eAqStzk7dFgBjFtUCtu" + ], + [ + "461QQ2sYWxU7H2PV4oBwJGNch8XVTYYbZxU" + ], + [ + "2UCtv53VttmQYkVU4VMtXB31REvQg4ABzs41AEKZ8UcB7DAfVzdkV9JDErwGwyj5AUHLkmgZeobs" + ], + [ + "cSNjAsnhgtiFMi6MtfvgscMB2Cbhn2v1FUYfviJ1CdjfidvmeW6mn" + ], + [ + "gmsow2Y6EWAFDFE1CE4Hd3Tpu2BvfmBfG1SXsuRARbnt1WjkZnFh1qGTiptWWbjsq2Q6qvpgJVj" + ], + [ + "nksUKSkzS76v8EsSgozXGMoQFiCoCHzCVajFKAXqzK5on9ZJYVHMD5CKwgmX3S3c7M1U3xabUny" + ], + [ + "L3favK1UzFGgdzYBF2oBT5tbayCo4vtVBLJhg2iYuMeePxWG8SQc" + ], + [ + "7VxLxGGtYT6N99GdEfi6xz56xdQ8nP2dG1CavuXx7Rf2PrvNMTBNevjkfgs9JmkcGm6EXpj8ipyPZ" + ], + [ + "2mbZwFXF6cxShaCo2czTRB62WTx9LxhTtpP" + ], + [ + "dB7cwYdcPSgiyAwKWL3JwCVwSk6epU2txw" + ], + [ + "HPhFUhUAh8ZQQisH8QQWafAxtQYju3SFTX" + ], + [ + "4ctAH6AkHzq5ioiM1m9T3E2hiYEev5mTsB" + ], + [ + "Hn1uFi4dNexWrqARpjMqgT6cX1UsNPuV3cHdGg9ExyXw8HTKadbktRDtdeVmY3M1BxJStiL4vjJ" + ], + [ + "Sq3fDbvutABmnAHHExJDgPLQn44KnNC7UsXuT7KZecpaYDMU9Txs" + ], + [ + "6TqWyrqdgUEYDQU1aChMuFMMEimHX44qHFzCUgGfqxGgZNMUVWJ" + ], + [ + "giqJo7oWqFxNKWyrgcBxAVHXnjJ1t6cGoEffce5Y1y7u649Noj5wJ4mmiUAKEVVrYAGg2KPB3Y4" + ], + [ + "cNzHY5e8vcmM3QVJUcjCyiKMYfeYvyueq5qCMV3kqcySoLyGLYUK" + ], + [ + "37uTe568EYc9WLoHEd9jXEvUiWbq5LFLscNyqvAzLU5vBArUJA6eydkLmnMwJDjkL5kXc2VK7ig" + ], + [ + "EsYbG4tWWWY45G31nox838qNdzksbPySWc" + ], + [ + "nbuzhfwMoNzA3PaFnyLcRxE9bTJPDkjZ6Rf6Y6o2ckXZfzZzXBT" + ], + [ + "cQN9PoxZeCWK1x56xnz6QYAsvR11XAce3Ehp3gMUdfSQ53Y2mPzx" + ], + [ + "1Gm3N3rkef6iMbx4voBzaxtXcmmiMTqZPhcuAepRzYUJQW4qRpEnHvMojzof42hjFRf8PE2jPde" + ], + [ + "2TAq2tuN6x6m233bpT7yqdYQPELdTDJn1eU" + ], + [ + "ntEtnnGhqPii4joABvBtSEJG6BxjT2tUZqE8PcVYgk3RHpgxgHDCQxNbLJf7ardf1dDk2oCQ7Cf" + ], + [ + "Ky1YjoZNgQ196HJV3HpdkecfhRBmRZdMJk89Hi5KGfpfPwS2bUbfd" + ], + [ + "2A1q1YsMZowabbvta7kTy2Fd6qN4r5ZCeG3qLpvZBMzCixMUdkN2Y4dHB1wPsZAeVXUGD83MfRED" + ] +] diff --git a/src/test/data/base58_keys_valid.json b/src/test/data/base58_keys_valid.json new file mode 100644 index 0000000..9409afc --- /dev/null +++ b/src/test/data/base58_keys_valid.json @@ -0,0 +1,452 @@ +[ + [ + "Lf8Th7S4LDxFUZegQgn5z5se7BahrJ9DeV", + "da589613a4c031bafa9fa5490fdaea491e81e687", + { + "addrType": "pubkey", + "isPrivkey": false, + "isTestnet": false + } + ], + [ + "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou", + "74f209f6ea907e2ea48f74fae05782ae8a665257", + { + "addrType": "script", + "isPrivkey": false, + "isTestnet": false + } + ], + [ + "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", + "53c0307d6851aa0ce7825ba883c6bd9ad242b486", + { + "addrType": "pubkey", + "isPrivkey": false, + "isTestnet": true + } + ], + [ + "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", + "6349a418fc4578d10a372b54b45c280cc8c4382f", + { + "addrType": "script", + "isPrivkey": false, + "isTestnet": true + } + ], + [ + "6uoCwsNo3oV9rsYwHY7TeGtZpwLbWJyVcKCAQyz91Ah4XbZYxw3", + "58fb1bfc04bd9293a916d0688cba48ac143921a400e2086f43e16a02e572f2de", + { + "isCompressed": false, + "isPrivkey": true, + "isTestnet": false + } + ], + [ + "T9wxCLuZLZ2uSs44HUzGW5ZC2hNtnZqsbZGKtWC1MhJjHbRNCVYP", + "cdc32fb8fab8b80ecf7e7501d93173f4c10bc45bd42c030ceaa7d947090bb441", + { + "isCompressed": true, + "isPrivkey": true, + "isTestnet": false + } + ], + [ + "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko", + "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2", + { + "isCompressed": false, + "isPrivkey": true, + "isTestnet": true + } + ], + [ + "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH", + "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3", + { + "isCompressed": true, + "isPrivkey": true, + "isTestnet": true + } + ], + [ + "LP7Ls95NmnuWi5o9bKZrezLiyCPy1B5P1q", + "2aa075715a16b3f8fe726ae9c5c4aa044bbca054", + { + "addrType": "pubkey", + "isPrivkey": false, + "isTestnet": false + } + ], + [ + "3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy", + "fcc5460dd6e2487c7d75b1963625da0e8f4c5975", + { + "addrType": "script", + "isPrivkey": false, + "isTestnet": false + } + ], + [ + "n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ", + "f1d470f9b02370fdec2e6b708b08ac431bf7a5f7", + { + "addrType": "pubkey", + "isPrivkey": false, + "isTestnet": true + } + ], + [ + "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n", + "c579342c2c4c9220205e2cdc285617040c924a0a", + { + "addrType": "script", + "isPrivkey": false, + "isTestnet": true + } + ], + [ + "6vs3xk3YxdBcHAA12Y63qwALButouE5SBzZUnBzCq5N8wvMjnZ9", + "e5686121b71c57364887d32d7b90885af98ebd96972bd1c00a6bd2a8f1f35b45", + { + "isCompressed": false, + "isPrivkey": true, + "isTestnet": false + } + ], + [ + "T4rLdfaJrCLN3uemQQoi1nAhXSAFwepDX4Ahjw8EjVP27Avmjzo4", + "35b0165377348a7ba55184f5a30834e7b5ba20dcb3ed4c7bb330e1dc631b050f", + { + "isCompressed": true, + "isPrivkey": true, + "isTestnet": false + } + ], + [ + "93DVKyFYwSN6wEo3E2fCrFPUp17FtrtNi2Lf7n4G3garFb16CRj", + "d6bca256b5abc5602ec2e1c121a08b0da2556587430bcf7e1898af2224885203", + { + "isCompressed": false, + "isPrivkey": true, + "isTestnet": true + } + ], + [ + "cTDVKtMGVYWTHCb1AFjmVbEbWjvKpKqKgMaR3QJxToMSQAhmCeTN", + "a81ca4e8f90181ec4b61b6a7eb998af17b2cb04de8a03b504b9e34c4c61db7d9", + { + "isCompressed": true, + "isPrivkey": true, + "isTestnet": true + } + ], + [ + "LdaHb6WXgyf38F96JweN9XNWKJS91iCLNF", + "c94ab72be12aa1a05f1af875f7250333e5d27aa2", + { + "addrType": "pubkey", + "isPrivkey": false, + "isTestnet": false + } + ], + [ + "3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks", + "63bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb", + { + "addrType": "script", + "isPrivkey": false, + "isTestnet": false + } + ], + [ + "n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk", + "ef66444b5b17f14e8fae6e7e19b045a78c54fd79", + { + "addrType": "pubkey", + "isPrivkey": false, + "isTestnet": true + } + ], + [ + "2NB72XtkjpnATMggui83aEtPawyyKvnbX2o", + "c3e55fceceaa4391ed2a9677f4a4d34eacd021a0", + { + "addrType": "script", + "isPrivkey": false, + "isTestnet": true + } + ], + [ + "6uQT9bdxJhE83M8RjCQfo7U9oaAo5ncAP5skuzemx5DeuA6hB9o", + "2550053cc3dfc32104fd3d0b1bce0e6f6e65a0564d36018f1544d9aa4764ad39", + { + "isCompressed": false, + "isPrivkey": true, + "isTestnet": false + } + ], + [ + "T4cpoFeaPJhTCTdLfmbNmA2f2PyjJgpCWU2pcdzBwExj5F9panJ4", + "2ebc4b6d730064cf6023b006f2b4aff89a53ac04740f21d84efa6ef33d8855cd", + { + "isCompressed": true, + "isPrivkey": true, + "isTestnet": false + } + ], + [ + "927CnUkUbasYtDwYwVn2j8GdTuACNnKkjZ1rpZd2yBB1CLcnXpo", + "44c4f6a096eac5238291a94cc24c01e3b19b8d8cef72874a079e00a242237a52", + { + "isCompressed": false, + "isPrivkey": true, + "isTestnet": true + } + ], + [ + "cUcfCMRjiQf85YMzzQEk9d1s5A4K7xL5SmBCLrezqXFuTVefyhY7", + "d1de707020a9059d6d3abaf85e17967c6555151143db13dbb06db78df0f15c69", + { + "isCompressed": true, + "isPrivkey": true, + "isTestnet": true + } + ], + [ + "LX5YLxmAJ3YKHaRc2eR2A2s1cnw6fPA4SE", + "820a3743686bf0fd46005de06cfd382bbcbd70c5", + { + "addrType": "pubkey", + "isPrivkey": false, + "isTestnet": false + } + ], + [ + "33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk", + "188f91a931947eddd7432d6e614387e32b244709", + { + "addrType": "script", + "isPrivkey": false, + "isTestnet": false + } + ], + [ + "mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H", + "1694f5bc1a7295b600f40018a618a6ea48eeb498", + { + "addrType": "pubkey", + "isPrivkey": false, + "isTestnet": true + } + ], + [ + "2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN", + "3b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f3", + { + "addrType": "script", + "isPrivkey": false, + "isTestnet": true + } + ], + [ + "6usgadpSJcXjfNFqn2Cz1kgKEWYhtzANKvhBs5pAKpsoCCRRv6H", + "63252cd47897bd0380152475453e275cf504226ee82253bbc980a926b2a71689", + { + "isCompressed": false, + "isPrivkey": true, + "isTestnet": false + } + ], + [ + "T8wbMCiccPDTa3hUFT23LUXeWU1AiNpVy8MgsQMFAbPjDjpWPVod", + "afbd7a0468460c25eef3576503578ce5ff985a598e30960018ad559282b3ef3c", + { + "isCompressed": true, + "isPrivkey": true, + "isTestnet": false + } + ], + [ + "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq", + "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856", + { + "isCompressed": false, + "isPrivkey": true, + "isTestnet": true + } + ], + [ + "cVM65tdYu1YK37tNoAyGoJTR13VBYFva1vg9FLuPAsJijGvG6NEA", + "e7b230133f1b5489843260236b06edca25f66adb1be455fbd38d4010d48faeef", + { + "isCompressed": true, + "isPrivkey": true, + "isTestnet": true + } + ], + [ + "LZa2tVcdFafKLMVeEKijaJ45jvruvZSRFK", + "9d5df1006a8a175f342dd0610337331fceb45830", + { + "addrType": "pubkey", + "isPrivkey": false, + "isTestnet": false + } + ], + [ + "3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y", + "f6fe69bcb548a829cce4c57bf6fff8af3a5981f9", + { + "addrType": "script", + "isPrivkey": false, + "isTestnet": false + } + ], + [ + "mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6", + "261f83568a098a8638844bd7aeca039d5f2352c0", + { + "addrType": "pubkey", + "isPrivkey": false, + "isTestnet": true + } + ], + [ + "2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda", + "e930e1834a4d234702773951d627cce82fbb5d2e", + { + "addrType": "script", + "isPrivkey": false, + "isTestnet": true + } + ], + [ + "6vUB9QijBguaXPMoLXK5ZwLJCZTjKKAeQxgWfZLfJvForhka5D8", + "b176f3e34ff474dea2fb3cafb19be519e6e8f34fd39166f5d5ce657154eb05d6", + { + "isCompressed": false, + "isPrivkey": true, + "isTestnet": false + } + ], + [ + "T5MCn7wUVMmffkTpoRNdW1MhWaRoSgNESC5tzeTSHie92tMBxjEZ", + "448970a2a25f412fd9fc48d9df5dee7a01ad039b824efc28663f31728b92cce2", + { + "isCompressed": true, + "isPrivkey": true, + "isTestnet": false + } + ], + [ + "91cTVUcgydqyZLgaANpf1fvL55FH53QMm4BsnCADVNYuWuqdVys", + "037f4192c630f399d9271e26c575269b1d15be553ea1a7217f0cb8513cef41cb", + { + "isCompressed": false, + "isPrivkey": true, + "isTestnet": true + } + ], + [ + "cQspfSzsgLeiJGB2u8vrAiWpCU4MxUT6JseWo2SjXy4Qbzn2fwDw", + "6251e205e8ad508bab5596bee086ef16cd4b239e0cc0c5d7c4e6035441e7d5de", + { + "isCompressed": true, + "isPrivkey": true, + "isTestnet": true + } + ], + [ + "LPJfbhKmpiDNczUF1xwL8mBpwjg22kCRiw", + "2cc4ac26bb08dfad7a40aa48b88046cfd2c930fd", + { + "addrType": "pubkey", + "isPrivkey": false, + "isTestnet": false + } + ], + [ + "37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3", + "3f210e7277c899c3a155cc1c90f4106cbddeec6e", + { + "addrType": "script", + "isPrivkey": false, + "isTestnet": false + } + ], + [ + "myoqcgYiehufrsnnkqdqbp69dddVDMopJu", + "c8a3c2a09a298592c3e180f02487cd91ba3400b5", + { + "addrType": "pubkey", + "isPrivkey": false, + "isTestnet": true + } + ], + [ + "2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C", + "99b31df7c9068d1481b596578ddbb4d3bd90baeb", + { + "addrType": "script", + "isPrivkey": false, + "isTestnet": true + } + ], + [ + "6vM32FdMdhMF7qS9TLDH2sTLC6wALamAk9ebRFESaUpq9yf2mbE", + "a1409598b6d7490548b3d5f3255b2c6adf487d82306a920d41362b98350b18a1", + { + "isCompressed": false, + "isPrivkey": true, + "isTestnet": false + } + ], + [ + "TA8YZ69RUn4NjmCJ9gaoDmxesD9bb9xzkXKHDcymJApWVKTPbYTm", + "d3362922cebe534a1cef42928fc7208c9cfa1c4413422b2af741110fe5b0c8de", + { + "isCompressed": true, + "isPrivkey": true, + "isTestnet": false + } + ], + [ + "93N87D6uxSBzwXvpokpzg8FFmfQPmvX4xHoWQe3pLdYpbiwT5YV", + "ea577acfb5d1d14d3b7b195c321566f12f87d2b77ea3a53f68df7ebf8604a801", + { + "isCompressed": false, + "isPrivkey": true, + "isTestnet": true + } + ], + [ + "cMxXusSihaX58wpJ3tNuuUcZEQGt6DKJ1wEpxys88FFaQCYjku9h", + "0b3b34f0958d8a268193a9814da92c3e8b58b4a4378a542863e34ac289cd830c", + { + "isCompressed": true, + "isPrivkey": true, + "isTestnet": true + } + ], + [ + "LWzPZSe3opyEdEmRAiULVLR3zBZS3oY9Wu", + "8110cd88bfb26bfb08e0878ddb34af03fc03c8b7", + { + "addrType": "pubkey", + "isPrivkey": false, + "isTestnet": false + } + ], + [ + "3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G", + "5ece0cadddc415b1980f001785947120acdb36fc", + { + "addrType": "script", + "isPrivkey": false, + "isTestnet": false + } + ] +] diff --git a/src/test/data/script_invalid.json b/src/test/data/script_invalid.json new file mode 100644 index 0000000..9566422 --- /dev/null +++ b/src/test/data/script_invalid.json @@ -0,0 +1,314 @@ +[ +["", ""], +["", "NOP"], +["NOP", ""], +["NOP","NOP"], + +["0x4c01","0x01 NOP", "PUSHDATA1 with not enough bytes"], +["0x4d0200ff","0x01 NOP", "PUSHDATA2 with not enough bytes"], +["0x4e03000000ffff","0x01 NOP", "PUSHDATA4 with not enough bytes"], + +["1", "IF 0x50 ENDIF 1", "0x50 is reserved"], +["0x52", "0x5f ADD 0x60 EQUAL", "0x51 through 0x60 push 1 through 16 onto stack"], +["0","NOP"], +["1", "IF VER ELSE 1 ENDIF", "VER non-functional"], +["0", "IF VERIF ELSE 1 ENDIF", "VERIF illegal everywhere"], +["0", "IF VERNOTIF ELSE 1 ENDIF", "VERNOT illegal everywhere"], + +["1 IF", "1 ENDIF", "IF/ENDIF can't span scriptSig/scriptPubKey"], +["1 IF 0 ENDIF", "1 ENDIF"], +["1 ELSE 0 ENDIF", "1"], +["0 NOTIF", "123"], + +["0", "DUP IF ENDIF"], +["0", "IF 1 ENDIF"], +["0", "DUP IF ELSE ENDIF"], +["0", "IF 1 ELSE ENDIF"], +["0", "NOTIF ELSE 1 ENDIF"], + +["0 1", "IF IF 1 ELSE 0 ENDIF ENDIF"], +["0 0", "IF IF 1 ELSE 0 ENDIF ENDIF"], +["1 0", "IF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"], +["0 1", "IF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"], + +["0 0", "NOTIF IF 1 ELSE 0 ENDIF ENDIF"], +["0 1", "NOTIF IF 1 ELSE 0 ENDIF ENDIF"], +["1 1", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"], +["0 0", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"], + +["1", "RETURN"], +["1", "DUP IF RETURN ENDIF"], + +["1", "RETURN 'data'", "canonical prunable txout format"], +["0 IF", "RETURN ENDIF 1", "still prunable because IF/ENDIF can't span scriptSig/scriptPubKey"], + +["0", "VERIFY 1"], +["1", "VERIFY"], +["1", "VERIFY 0"], + +["1 TOALTSTACK", "FROMALTSTACK 1", "alt stack not shared between sig/pubkey"], + +["IFDUP", "DEPTH 0 EQUAL"], +["DROP", "DEPTH 0 EQUAL"], +["DUP", "DEPTH 0 EQUAL"], +["1", "DUP 1 ADD 2 EQUALVERIFY 0 EQUAL"], +["NOP", "NIP"], +["NOP", "1 NIP"], +["NOP", "1 0 NIP"], +["NOP", "OVER 1"], +["1", "OVER"], +["0 1", "OVER DEPTH 3 EQUALVERIFY"], +["19 20 21", "PICK 19 EQUALVERIFY DEPTH 2 EQUAL"], +["NOP", "0 PICK"], +["1", "-1 PICK"], +["19 20 21", "0 PICK 20 EQUALVERIFY DEPTH 3 EQUAL"], +["19 20 21", "1 PICK 21 EQUALVERIFY DEPTH 3 EQUAL"], +["19 20 21", "2 PICK 22 EQUALVERIFY DEPTH 3 EQUAL"], +["NOP", "0 ROLL"], +["1", "-1 ROLL"], +["19 20 21", "0 ROLL 20 EQUALVERIFY DEPTH 2 EQUAL"], +["19 20 21", "1 ROLL 21 EQUALVERIFY DEPTH 2 EQUAL"], +["19 20 21", "2 ROLL 22 EQUALVERIFY DEPTH 2 EQUAL"], +["NOP", "ROT 1"], +["NOP", "1 ROT 1"], +["NOP", "1 2 ROT 1"], +["NOP", "0 1 2 ROT"], +["NOP", "SWAP 1"], +["1", "SWAP 1"], +["0 1", "SWAP 1 EQUALVERIFY"], +["NOP", "TUCK 1"], +["1", "TUCK 1"], +["1 0", "TUCK DEPTH 3 EQUALVERIFY SWAP 2DROP"], +["NOP", "2DUP 1"], +["1", "2DUP 1"], +["NOP", "3DUP 1"], +["1", "3DUP 1"], +["1 2", "3DUP 1"], +["NOP", "2OVER 1"], +["1", "2 3 2OVER 1"], +["NOP", "2SWAP 1"], +["1", "2 3 2SWAP 1"], + +["'a' 'b'", "CAT", "CAT disabled"], +["'a' 'b' 0", "IF CAT ELSE 1 ENDIF", "CAT disabled"], +["'abc' 1 1", "SUBSTR", "SUBSTR disabled"], +["'abc' 1 1 0", "IF SUBSTR ELSE 1 ENDIF", "SUBSTR disabled"], +["'abc' 2 0", "IF LEFT ELSE 1 ENDIF", "LEFT disabled"], +["'abc' 2 0", "IF RIGHT ELSE 1 ENDIF", "RIGHT disabled"], + +["NOP", "SIZE 1"], + +["'abc'", "IF INVERT ELSE 1 ENDIF", "INVERT disabled"], +["1 2 0 IF AND ELSE 1 ENDIF", "NOP", "AND disabled"], +["1 2 0 IF OR ELSE 1 ENDIF", "NOP", "OR disabled"], +["1 2 0 IF XOR ELSE 1 ENDIF", "NOP", "XOR disabled"], +["2 0 IF 2MUL ELSE 1 ENDIF", "NOP", "2MUL disabled"], +["2 0 IF 2DIV ELSE 1 ENDIF", "NOP", "2DIV disabled"], +["2 2 0 IF MUL ELSE 1 ENDIF", "NOP", "MUL disabled"], +["2 2 0 IF DIV ELSE 1 ENDIF", "NOP", "DIV disabled"], +["2 2 0 IF MOD ELSE 1 ENDIF", "NOP", "MOD disabled"], +["2 2 0 IF LSHIFT ELSE 1 ENDIF", "NOP", "LSHIFT disabled"], +["2 2 0 IF RSHIFT ELSE 1 ENDIF", "NOP", "RSHIFT disabled"], + +["0 1","EQUAL"], +["1 1 ADD", "0 EQUAL"], +["11 1 ADD 12 SUB", "11 EQUAL"], + +["2147483648 0 ADD", "NOP", "arithmetic operands must be in range [-2^31...2^31] "], +["-2147483648 0 ADD", "NOP", "arithmetic operands must be in range [-2^31...2^31] "], +["2147483647 DUP ADD", "4294967294 NUMEQUAL", "NUMEQUAL must be in numeric range"], +["'abcdef' NOT", "0 EQUAL", "NOT is an arithmetic operand"], + +["2 DUP MUL", "4 EQUAL", "disabled"], +["2 DUP DIV", "1 EQUAL", "disabled"], +["2 2MUL", "4 EQUAL", "disabled"], +["2 2DIV", "1 EQUAL", "disabled"], +["7 3 MOD", "1 EQUAL", "disabled"], +["2 2 LSHIFT", "8 EQUAL", "disabled"], +["2 1 RSHIFT", "1 EQUAL", "disabled"], + +["1","NOP1 NOP2 NOP3 NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10 2 EQUAL"], +["'NOP_1_to_10' NOP1 NOP2 NOP3 NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10","'NOP_1_to_11' EQUAL"], + +["0x50","1", "opcode 0x50 is reserved"], +["1", "IF 0xba ELSE 1 ENDIF", "opcodes above NOP10 invalid if executed"], +["1", "IF 0xbb ELSE 1 ENDIF"], +["1", "IF 0xbc ELSE 1 ENDIF"], +["1", "IF 0xbd ELSE 1 ENDIF"], +["1", "IF 0xbe ELSE 1 ENDIF"], +["1", "IF 0xbf ELSE 1 ENDIF"], +["1", "IF 0xc0 ELSE 1 ENDIF"], +["1", "IF 0xc1 ELSE 1 ENDIF"], +["1", "IF 0xc2 ELSE 1 ENDIF"], +["1", "IF 0xc3 ELSE 1 ENDIF"], +["1", "IF 0xc4 ELSE 1 ENDIF"], +["1", "IF 0xc5 ELSE 1 ENDIF"], +["1", "IF 0xc6 ELSE 1 ENDIF"], +["1", "IF 0xc7 ELSE 1 ENDIF"], +["1", "IF 0xc8 ELSE 1 ENDIF"], +["1", "IF 0xc9 ELSE 1 ENDIF"], +["1", "IF 0xca ELSE 1 ENDIF"], +["1", "IF 0xcb ELSE 1 ENDIF"], +["1", "IF 0xcc ELSE 1 ENDIF"], +["1", "IF 0xcd ELSE 1 ENDIF"], +["1", "IF 0xce ELSE 1 ENDIF"], +["1", "IF 0xcf ELSE 1 ENDIF"], +["1", "IF 0xd0 ELSE 1 ENDIF"], +["1", "IF 0xd1 ELSE 1 ENDIF"], +["1", "IF 0xd2 ELSE 1 ENDIF"], +["1", "IF 0xd3 ELSE 1 ENDIF"], +["1", "IF 0xd4 ELSE 1 ENDIF"], +["1", "IF 0xd5 ELSE 1 ENDIF"], +["1", "IF 0xd6 ELSE 1 ENDIF"], +["1", "IF 0xd7 ELSE 1 ENDIF"], +["1", "IF 0xd8 ELSE 1 ENDIF"], +["1", "IF 0xd9 ELSE 1 ENDIF"], +["1", "IF 0xda ELSE 1 ENDIF"], +["1", "IF 0xdb ELSE 1 ENDIF"], +["1", "IF 0xdc ELSE 1 ENDIF"], +["1", "IF 0xdd ELSE 1 ENDIF"], +["1", "IF 0xde ELSE 1 ENDIF"], +["1", "IF 0xdf ELSE 1 ENDIF"], +["1", "IF 0xe0 ELSE 1 ENDIF"], +["1", "IF 0xe1 ELSE 1 ENDIF"], +["1", "IF 0xe2 ELSE 1 ENDIF"], +["1", "IF 0xe3 ELSE 1 ENDIF"], +["1", "IF 0xe4 ELSE 1 ENDIF"], +["1", "IF 0xe5 ELSE 1 ENDIF"], +["1", "IF 0xe6 ELSE 1 ENDIF"], +["1", "IF 0xe7 ELSE 1 ENDIF"], +["1", "IF 0xe8 ELSE 1 ENDIF"], +["1", "IF 0xe9 ELSE 1 ENDIF"], +["1", "IF 0xea ELSE 1 ENDIF"], +["1", "IF 0xeb ELSE 1 ENDIF"], +["1", "IF 0xec ELSE 1 ENDIF"], +["1", "IF 0xed ELSE 1 ENDIF"], +["1", "IF 0xee ELSE 1 ENDIF"], +["1", "IF 0xef ELSE 1 ENDIF"], +["1", "IF 0xf0 ELSE 1 ENDIF"], +["1", "IF 0xf1 ELSE 1 ENDIF"], +["1", "IF 0xf2 ELSE 1 ENDIF"], +["1", "IF 0xf3 ELSE 1 ENDIF"], +["1", "IF 0xf4 ELSE 1 ENDIF"], +["1", "IF 0xf5 ELSE 1 ENDIF"], +["1", "IF 0xf6 ELSE 1 ENDIF"], +["1", "IF 0xf7 ELSE 1 ENDIF"], +["1", "IF 0xf8 ELSE 1 ENDIF"], +["1", "IF 0xf9 ELSE 1 ENDIF"], +["1", "IF 0xfa ELSE 1 ENDIF"], +["1", "IF 0xfb ELSE 1 ENDIF"], +["1", "IF 0xfc ELSE 1 ENDIF"], +["1", "IF 0xfd ELSE 1 ENDIF"], +["1", "IF 0xfe ELSE 1 ENDIF"], +["1", "IF 0xff ELSE 1 ENDIF"], + +["1 IF 1 ELSE", "0xff ENDIF", "invalid because scriptSig and scriptPubKey are processed separately"], + +["NOP", "RIPEMD160"], +["NOP", "SHA1"], +["NOP", "SHA256"], +["NOP", "HASH160"], +["NOP", "HASH256"], + +["NOP", +"'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'", +">520 byte push"], +["0", +"IF 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' ENDIF 1", +">520 byte push in non-executed IF branch"], +["1", +"0x61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161", +">201 opcodes executed. 0x61 is NOP"], +["0", +"IF 0x6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161 ENDIF 1", +">201 opcodes including non-executed IF branch. 0x61 is NOP"], +["1 2 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"1 2 3 4 5 6 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +">1,000 stack size (0x6f is 3DUP)"], +["1 2 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"1 TOALTSTACK 2 TOALTSTACK 3 4 5 6 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +">1,000 stack+altstack size"], +["NOP", +"0 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f 2DUP 0x616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161", +"10,001-byte scriptPubKey"], + +["NOP1","NOP10"], + +["1","VER", "OP_VER is reserved"], +["1","VERIF", "OP_VERIF is reserved"], +["1","VERNOTIF", "OP_VERNOTIF is reserved"], +["1","RESERVED1", "OP_RESERVED1 is reserved"], +["1","RESERVED2", "OP_RESERVED2 is reserved"], +["1","0xba", "0xba == OP_NOP10 + 1"], + +["2147483648", "1ADD 1", "We cannot do math on 5-byte integers"], +["-2147483648", "1ADD 1", "Because we use a sign bit, -2147483648 is also 5 bytes"], + +["1", "1 ENDIF", "ENDIF without IF"], +["1", "IF 1", "IF without ENDIF"], +["1 IF 1", "ENDIF", "IFs don't carry over"], + +["NOP", "IF 1 ENDIF", "The following tests check the if(stack.size() < N) tests in each opcode"], +["NOP", "NOTIF 1 ENDIF", "They are here to catch copy-and-paste errors"], +["NOP", "VERIFY 1", "Most of them are duplicated elsewhere,"], + +["NOP", "TOALTSTACK 1", "but, hey, more is always better, right?"], +["1", "FROMALTSTACK"], +["1", "2DROP 1"], +["1", "2DUP"], +["1 1", "3DUP"], +["1 1 1", "2OVER"], +["1 1 1 1 1", "2ROT"], +["1 1 1", "2SWAP"], +["NOP", "IFDUP 1"], +["NOP", "DROP 1"], +["NOP", "DUP 1"], +["1", "NIP"], +["1", "OVER"], +["1 1 1 3", "PICK"], +["0", "PICK 1"], +["1 1 1 3", "ROLL"], +["0", "ROLL 1"], +["1 1", "ROT"], +["1", "SWAP"], +["1", "TUCK"], + +["NOP", "SIZE 1"], + +["1", "EQUAL 1"], +["1", "EQUALVERIFY 1"], + +["NOP", "1ADD 1"], +["NOP", "1SUB 1"], +["NOP", "NEGATE 1"], +["NOP", "ABS 1"], +["NOP", "NOT 1"], +["NOP", "0NOTEQUAL 1"], + +["1", "ADD"], +["1", "SUB"], +["1", "BOOLAND"], +["1", "BOOLOR"], +["1", "NUMEQUAL"], +["1", "NUMEQUALVERIFY 1"], +["1", "NUMNOTEQUAL"], +["1", "LESSTHAN"], +["1", "GREATERTHAN"], +["1", "LESSTHANOREQUAL"], +["1", "GREATERTHANOREQUAL"], +["1", "MIN"], +["1", "MAX"], +["1 1", "WITHIN"], + +["NOP", "RIPEMD160 1"], +["NOP", "SHA1 1"], +["NOP", "SHA256 1"], +["NOP", "HASH160 1"], +["NOP", "HASH256 1"], + +["NOP 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL", "Tests for Script.IsPushOnly()"], +["NOP1 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL"], + +["0 0x01 0x50", "HASH160 0x14 0xece424a6bb6ddf4db592c0faed60685047a361b1 EQUAL", "OP_RESERVED in P2SH should fail"], +["0 0x01 VER", "HASH160 0x14 0x0f4d7845db968f2a81b530b6f3c1d6246d4c7e01 EQUAL", "OP_VER in P2SH should fail"] +] diff --git a/src/test/data/script_valid.json b/src/test/data/script_valid.json new file mode 100644 index 0000000..58682d3 --- /dev/null +++ b/src/test/data/script_valid.json @@ -0,0 +1,379 @@ +[ +["0x01 0x0b", "11 EQUAL", "push 1 byte"], +["0x02 0x417a", "'Az' EQUAL"], +["0x4b 0x417a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a", + "'Azzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz' EQUAL", "push 75 bytes"], + +["0x4c 0x01 0x07","7 EQUAL", "0x4c is OP_PUSHDATA1"], +["0x4d 0x0100 0x08","8 EQUAL", "0x4d is OP_PUSHDATA2"], +["0x4e 0x01000000 0x09","9 EQUAL", "0x4e is OP_PUSHDATA4"], + +["0x4c 0x00","0 EQUAL"], +["0x4d 0x0000","0 EQUAL"], +["0x4e 0x00000000","0 EQUAL"], +["0x4f 1000 ADD","999 EQUAL"], +["0", "IF 0x50 ENDIF 1", "0x50 is reserved (ok if not executed)"], +["0x51", "0x5f ADD 0x60 EQUAL", "0x51 through 0x60 push 1 through 16 onto stack"], +["1","NOP"], +["0", "IF VER ELSE 1 ENDIF", "VER non-functional (ok if not executed)"], +["0", "IF RESERVED1 RESERVED2 ELSE 1 ENDIF", "RESERVED ok in un-executed IF"], + +["1", "DUP IF ENDIF"], +["1", "IF 1 ENDIF"], +["1", "DUP IF ELSE ENDIF"], +["1", "IF 1 ELSE ENDIF"], +["0", "IF ELSE 1 ENDIF"], + +["1 1", "IF IF 1 ELSE 0 ENDIF ENDIF"], +["1 0", "IF IF 1 ELSE 0 ENDIF ENDIF"], +["1 1", "IF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"], +["0 0", "IF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"], + +["1 0", "NOTIF IF 1 ELSE 0 ENDIF ENDIF"], +["1 1", "NOTIF IF 1 ELSE 0 ENDIF ENDIF"], +["1 0", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"], +["0 1", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"], + +["0", "IF RETURN ENDIF 1", "RETURN only works if executed"], + +["1 1", "VERIFY"], + +["10 0 11 TOALTSTACK DROP FROMALTSTACK", "ADD 21 EQUAL"], +["'gavin_was_here' TOALTSTACK 11 FROMALTSTACK", "'gavin_was_here' EQUALVERIFY 11 EQUAL"], + +["0 IFDUP", "DEPTH 1 EQUALVERIFY 0 EQUAL"], +["1 IFDUP", "DEPTH 2 EQUALVERIFY 1 EQUALVERIFY 1 EQUAL"], +["0 DROP", "DEPTH 0 EQUAL"], +["0", "DUP 1 ADD 1 EQUALVERIFY 0 EQUAL"], +["0 1", "NIP"], +["1 0", "OVER DEPTH 3 EQUALVERIFY"], +["22 21 20", "0 PICK 20 EQUALVERIFY DEPTH 3 EQUAL"], +["22 21 20", "1 PICK 21 EQUALVERIFY DEPTH 3 EQUAL"], +["22 21 20", "2 PICK 22 EQUALVERIFY DEPTH 3 EQUAL"], +["22 21 20", "0 ROLL 20 EQUALVERIFY DEPTH 2 EQUAL"], +["22 21 20", "1 ROLL 21 EQUALVERIFY DEPTH 2 EQUAL"], +["22 21 20", "2 ROLL 22 EQUALVERIFY DEPTH 2 EQUAL"], +["22 21 20", "ROT 22 EQUAL"], +["22 21 20", "ROT DROP 20 EQUAL"], +["22 21 20", "ROT DROP DROP 21 EQUAL"], +["22 21 20", "ROT ROT 21 EQUAL"], +["22 21 20", "ROT ROT ROT 20 EQUAL"], +["25 24 23 22 21 20", "2ROT 24 EQUAL"], +["25 24 23 22 21 20", "2ROT DROP 25 EQUAL"], +["25 24 23 22 21 20", "2ROT 2DROP 20 EQUAL"], +["25 24 23 22 21 20", "2ROT 2DROP DROP 21 EQUAL"], +["25 24 23 22 21 20", "2ROT 2DROP 2DROP 22 EQUAL"], +["25 24 23 22 21 20", "2ROT 2DROP 2DROP DROP 23 EQUAL"], +["25 24 23 22 21 20", "2ROT 2ROT 22 EQUAL"], +["25 24 23 22 21 20", "2ROT 2ROT 2ROT 20 EQUAL"], +["1 0", "SWAP 1 EQUALVERIFY 0 EQUAL"], +["0 1", "TUCK DEPTH 3 EQUALVERIFY SWAP 2DROP"], +["13 14", "2DUP ROT EQUALVERIFY EQUAL"], +["-1 0 1 2", "3DUP DEPTH 7 EQUALVERIFY ADD ADD 3 EQUALVERIFY 2DROP 0 EQUALVERIFY"], +["1 2 3 5", "2OVER ADD ADD 8 EQUALVERIFY ADD ADD 6 EQUAL"], +["1 3 5 7", "2SWAP ADD 4 EQUALVERIFY ADD 12 EQUAL"], +["0", "SIZE 0 EQUAL"], +["1", "SIZE 1 EQUAL"], +["127", "SIZE 1 EQUAL"], +["128", "SIZE 2 EQUAL"], +["32767", "SIZE 2 EQUAL"], +["32768", "SIZE 3 EQUAL"], +["8388607", "SIZE 3 EQUAL"], +["8388608", "SIZE 4 EQUAL"], +["2147483647", "SIZE 4 EQUAL"], +["2147483648", "SIZE 5 EQUAL"], +["-1", "SIZE 1 EQUAL"], +["-127", "SIZE 1 EQUAL"], +["-128", "SIZE 2 EQUAL"], +["-32767", "SIZE 2 EQUAL"], +["-32768", "SIZE 3 EQUAL"], +["-8388607", "SIZE 3 EQUAL"], +["-8388608", "SIZE 4 EQUAL"], +["-2147483647", "SIZE 4 EQUAL"], +["-2147483648", "SIZE 5 EQUAL"], +["'abcdefghijklmnopqrstuvwxyz'", "SIZE 26 EQUAL"], + + +["2 -2 ADD", "0 EQUAL"], +["2147483647 -2147483647 ADD", "0 EQUAL"], +["-1 -1 ADD", "-2 EQUAL"], + +["0 0","EQUAL"], +["1 1 ADD", "2 EQUAL"], +["1 1ADD", "2 EQUAL"], +["111 1SUB", "110 EQUAL"], +["111 1 ADD 12 SUB", "100 EQUAL"], +["0 ABS", "0 EQUAL"], +["16 ABS", "16 EQUAL"], +["-16 ABS", "-16 NEGATE EQUAL"], +["0 NOT", "NOP"], +["1 NOT", "0 EQUAL"], +["11 NOT", "0 EQUAL"], +["0 0NOTEQUAL", "0 EQUAL"], +["1 0NOTEQUAL", "1 EQUAL"], +["111 0NOTEQUAL", "1 EQUAL"], +["-111 0NOTEQUAL", "1 EQUAL"], +["1 1 BOOLAND", "NOP"], +["1 0 BOOLAND", "NOT"], +["0 1 BOOLAND", "NOT"], +["0 0 BOOLAND", "NOT"], +["16 17 BOOLAND", "NOP"], +["1 1 BOOLOR", "NOP"], +["1 0 BOOLOR", "NOP"], +["0 1 BOOLOR", "NOP"], +["0 0 BOOLOR", "NOT"], +["16 17 BOOLOR", "NOP"], +["11 10 1 ADD", "NUMEQUAL"], +["11 10 1 ADD", "NUMEQUALVERIFY 1"], +["11 10 1 ADD", "NUMNOTEQUAL NOT"], +["111 10 1 ADD", "NUMNOTEQUAL"], +["11 10", "LESSTHAN NOT"], +["4 4", "LESSTHAN NOT"], +["10 11", "LESSTHAN"], +["-11 11", "LESSTHAN"], +["-11 -10", "LESSTHAN"], +["11 10", "GREATERTHAN"], +["4 4", "GREATERTHAN NOT"], +["10 11", "GREATERTHAN NOT"], +["-11 11", "GREATERTHAN NOT"], +["-11 -10", "GREATERTHAN NOT"], +["11 10", "LESSTHANOREQUAL NOT"], +["4 4", "LESSTHANOREQUAL"], +["10 11", "LESSTHANOREQUAL"], +["-11 11", "LESSTHANOREQUAL"], +["-11 -10", "LESSTHANOREQUAL"], +["11 10", "GREATERTHANOREQUAL"], +["4 4", "GREATERTHANOREQUAL"], +["10 11", "GREATERTHANOREQUAL NOT"], +["-11 11", "GREATERTHANOREQUAL NOT"], +["-11 -10", "GREATERTHANOREQUAL NOT"], +["1 0 MIN", "0 NUMEQUAL"], +["0 1 MIN", "0 NUMEQUAL"], +["-1 0 MIN", "-1 NUMEQUAL"], +["0 -2147483647 MIN", "-2147483647 NUMEQUAL"], +["2147483647 0 MAX", "2147483647 NUMEQUAL"], +["0 100 MAX", "100 NUMEQUAL"], +["-100 0 MAX", "0 NUMEQUAL"], +["0 -2147483647 MAX", "0 NUMEQUAL"], +["0 0 1", "WITHIN"], +["1 0 1", "WITHIN NOT"], +["0 -2147483647 2147483647", "WITHIN"], +["-1 -100 100", "WITHIN"], +["11 -100 100", "WITHIN"], +["-2147483647 -100 100", "WITHIN NOT"], +["2147483647 -100 100", "WITHIN NOT"], + +["2147483647 2147483647 SUB", "0 EQUAL"], +["2147483647 DUP ADD", "4294967294 EQUAL", ">32 bit EQUAL is valid"], +["2147483647 NEGATE DUP ADD", "-4294967294 EQUAL"], + +["''", "RIPEMD160 0x14 0x9c1185a5c5e9fc54612808977ee8f548b2258d31 EQUAL"], +["'a'", "RIPEMD160 0x14 0x0bdc9d2d256b3ee9daae347be6f4dc835a467ffe EQUAL"], +["'abcdefghijklmnopqrstuvwxyz'", "RIPEMD160 0x14 0xf71c27109c692c1b56bbdceb5b9d2865b3708dbc EQUAL"], +["''", "SHA1 0x14 0xda39a3ee5e6b4b0d3255bfef95601890afd80709 EQUAL"], +["'a'", "SHA1 0x14 0x86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 EQUAL"], +["'abcdefghijklmnopqrstuvwxyz'", "SHA1 0x14 0x32d10c7b8cf96570ca04ce37f2a19d84240d3a89 EQUAL"], +["''", "SHA256 0x20 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 EQUAL"], +["'a'", "SHA256 0x20 0xca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb EQUAL"], +["'abcdefghijklmnopqrstuvwxyz'", "SHA256 0x20 0x71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73 EQUAL"], +["''", "DUP HASH160 SWAP SHA256 RIPEMD160 EQUAL"], +["''", "DUP HASH256 SWAP SHA256 SHA256 EQUAL"], +["''", "NOP HASH160 0x14 0xb472a266d0bd89c13706a4132ccfb16f7c3b9fcb EQUAL"], +["'a'", "HASH160 NOP 0x14 0x994355199e516ff76c4fa4aab39337b9d84cf12b EQUAL"], +["'abcdefghijklmnopqrstuvwxyz'", "HASH160 0x4c 0x14 0xc286a1af0947f58d1ad787385b1c2c4a976f9e71 EQUAL"], +["''", "HASH256 0x20 0x5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456 EQUAL"], +["'a'", "HASH256 0x20 0xbf5d3affb73efd2ec6c36ad3112dd933efed63c4e1cbffcfa88e2759c144f2d8 EQUAL"], +["'abcdefghijklmnopqrstuvwxyz'", "HASH256 0x4c 0x20 0xca139bc10c2f660da42666f72e89a225936fc60f193c161124a672050c434671 EQUAL"], + + +["1","NOP1 NOP2 NOP3 NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10 1 EQUAL"], +["'NOP_1_to_10' NOP1 NOP2 NOP3 NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10","'NOP_1_to_10' EQUAL"], + +["0", "IF 0xba ELSE 1 ENDIF", "opcodes above NOP10 invalid if executed"], +["0", "IF 0xbb ELSE 1 ENDIF"], +["0", "IF 0xbc ELSE 1 ENDIF"], +["0", "IF 0xbd ELSE 1 ENDIF"], +["0", "IF 0xbe ELSE 1 ENDIF"], +["0", "IF 0xbf ELSE 1 ENDIF"], +["0", "IF 0xc0 ELSE 1 ENDIF"], +["0", "IF 0xc1 ELSE 1 ENDIF"], +["0", "IF 0xc2 ELSE 1 ENDIF"], +["0", "IF 0xc3 ELSE 1 ENDIF"], +["0", "IF 0xc4 ELSE 1 ENDIF"], +["0", "IF 0xc5 ELSE 1 ENDIF"], +["0", "IF 0xc6 ELSE 1 ENDIF"], +["0", "IF 0xc7 ELSE 1 ENDIF"], +["0", "IF 0xc8 ELSE 1 ENDIF"], +["0", "IF 0xc9 ELSE 1 ENDIF"], +["0", "IF 0xca ELSE 1 ENDIF"], +["0", "IF 0xcb ELSE 1 ENDIF"], +["0", "IF 0xcc ELSE 1 ENDIF"], +["0", "IF 0xcd ELSE 1 ENDIF"], +["0", "IF 0xce ELSE 1 ENDIF"], +["0", "IF 0xcf ELSE 1 ENDIF"], +["0", "IF 0xd0 ELSE 1 ENDIF"], +["0", "IF 0xd1 ELSE 1 ENDIF"], +["0", "IF 0xd2 ELSE 1 ENDIF"], +["0", "IF 0xd3 ELSE 1 ENDIF"], +["0", "IF 0xd4 ELSE 1 ENDIF"], +["0", "IF 0xd5 ELSE 1 ENDIF"], +["0", "IF 0xd6 ELSE 1 ENDIF"], +["0", "IF 0xd7 ELSE 1 ENDIF"], +["0", "IF 0xd8 ELSE 1 ENDIF"], +["0", "IF 0xd9 ELSE 1 ENDIF"], +["0", "IF 0xda ELSE 1 ENDIF"], +["0", "IF 0xdb ELSE 1 ENDIF"], +["0", "IF 0xdc ELSE 1 ENDIF"], +["0", "IF 0xdd ELSE 1 ENDIF"], +["0", "IF 0xde ELSE 1 ENDIF"], +["0", "IF 0xdf ELSE 1 ENDIF"], +["0", "IF 0xe0 ELSE 1 ENDIF"], +["0", "IF 0xe1 ELSE 1 ENDIF"], +["0", "IF 0xe2 ELSE 1 ENDIF"], +["0", "IF 0xe3 ELSE 1 ENDIF"], +["0", "IF 0xe4 ELSE 1 ENDIF"], +["0", "IF 0xe5 ELSE 1 ENDIF"], +["0", "IF 0xe6 ELSE 1 ENDIF"], +["0", "IF 0xe7 ELSE 1 ENDIF"], +["0", "IF 0xe8 ELSE 1 ENDIF"], +["0", "IF 0xe9 ELSE 1 ENDIF"], +["0", "IF 0xea ELSE 1 ENDIF"], +["0", "IF 0xeb ELSE 1 ENDIF"], +["0", "IF 0xec ELSE 1 ENDIF"], +["0", "IF 0xed ELSE 1 ENDIF"], +["0", "IF 0xee ELSE 1 ENDIF"], +["0", "IF 0xef ELSE 1 ENDIF"], +["0", "IF 0xf0 ELSE 1 ENDIF"], +["0", "IF 0xf1 ELSE 1 ENDIF"], +["0", "IF 0xf2 ELSE 1 ENDIF"], +["0", "IF 0xf3 ELSE 1 ENDIF"], +["0", "IF 0xf4 ELSE 1 ENDIF"], +["0", "IF 0xf5 ELSE 1 ENDIF"], +["0", "IF 0xf6 ELSE 1 ENDIF"], +["0", "IF 0xf7 ELSE 1 ENDIF"], +["0", "IF 0xf8 ELSE 1 ENDIF"], +["0", "IF 0xf9 ELSE 1 ENDIF"], +["0", "IF 0xfa ELSE 1 ENDIF"], +["0", "IF 0xfb ELSE 1 ENDIF"], +["0", "IF 0xfc ELSE 1 ENDIF"], +["0", "IF 0xfd ELSE 1 ENDIF"], +["0", "IF 0xfe ELSE 1 ENDIF"], +["0", "IF 0xff ELSE 1 ENDIF"], + +["NOP", +"'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'", +"520 byte push"], +["1", +"0x616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161", +"201 opcodes executed. 0x61 is NOP"], +["1 2 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"1 2 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"1,000 stack size (0x6f is 3DUP)"], +["1 TOALTSTACK 2 TOALTSTACK 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"1 2 3 4 5 6 7 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"1,000 stack size (altstack cleared between scriptSig/scriptPubKey)"], +["'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f 2DUP 0x616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161", +"Max-size (10,000-byte), max-push(520 bytes), max-opcodes(201), max stack size(1,000 items). 0x6f is 3DUP, 0x61 is NOP"], + +["NOP","1"], + +["1", "0x01 0x01 EQUAL", "The following is useful for checking implementations of BN_bn2mpi"], +["127", "0x01 0x7F EQUAL"], +["128", "0x02 0x8000 EQUAL", "Leave room for the sign bit"], +["32767", "0x02 0xFF7F EQUAL"], +["32768", "0x03 0x008000 EQUAL"], +["8388607", "0x03 0xFFFF7F EQUAL"], +["8388608", "0x04 0x00008000 EQUAL"], +["2147483647", "0x04 0xFFFFFF7F EQUAL"], +["2147483648", "0x05 0x0000008000 EQUAL"], +["-1", "0x01 0x81 EQUAL", "Numbers are little-endian with the MSB being a sign bit"], +["-127", "0x01 0xFF EQUAL"], +["-128", "0x02 0x8080 EQUAL"], +["-32767", "0x02 0xFFFF EQUAL"], +["-32768", "0x03 0x008080 EQUAL"], +["-8388607", "0x03 0xFFFFFF EQUAL"], +["-8388608", "0x04 0x00008080 EQUAL"], +["-2147483647", "0x04 0xFFFFFFFF EQUAL"], +["-2147483648", "0x05 0x0000008080 EQUAL"], + +["2147483647", "1ADD 2147483648 EQUAL", "We can do math on 4-byte integers, and compare 5-byte ones"], +["2147483647", "1ADD 1"], +["-2147483647", "1ADD 1"], + + +["NOP", "NOP 1", "The following tests check the if(stack.size() < N) tests in each opcode"], +["1", "IF 1 ENDIF", "They are here to catch copy-and-paste errors"], +["0", "NOTIF 1 ENDIF", "Most of them are duplicated elsewhere,"], +["1", "VERIFY 1", "but, hey, more is always better, right?"], + +["0", "TOALTSTACK 1"], +["1", "TOALTSTACK FROMALTSTACK"], +["0 0", "2DROP 1"], +["0 1", "2DUP"], +["0 0 1", "3DUP"], +["0 1 0 0", "2OVER"], +["0 1 0 0 0 0", "2ROT"], +["0 1 0 0", "2SWAP"], +["1", "IFDUP"], +["NOP", "DEPTH 1"], +["0", "DROP 1"], +["1", "DUP"], +["0 1", "NIP"], +["1 0", "OVER"], +["1 0 0 0 3", "PICK"], +["1 0", "PICK"], +["1 0 0 0 3", "ROLL"], +["1 0", "ROLL"], +["1 0 0", "ROT"], +["1 0", "SWAP"], +["0 1", "TUCK"], + +["1", "SIZE"], + +["0 0", "EQUAL"], +["0 0", "EQUALVERIFY 1"], + +["0", "1ADD"], +["2", "1SUB"], +["-1", "NEGATE"], +["-1", "ABS"], +["0", "NOT"], +["-1", "0NOTEQUAL"], + +["1 0", "ADD"], +["1 0", "SUB"], +["-1 -1", "BOOLAND"], +["-1 0", "BOOLOR"], +["0 0", "NUMEQUAL"], +["0 0", "NUMEQUALVERIFY 1"], +["-1 0", "NUMNOTEQUAL"], +["-1 0", "LESSTHAN"], +["1 0", "GREATERTHAN"], +["0 0", "LESSTHANOREQUAL"], +["0 0", "GREATERTHANOREQUAL"], +["-1 0", "MIN"], +["1 0", "MAX"], +["-1 -1 0", "WITHIN"], + +["0", "RIPEMD160"], +["0", "SHA1"], +["0", "SHA256"], +["0", "HASH160"], +["0", "HASH256"], +["NOP", "CODESEPARATOR 1"], + +["NOP", "NOP1 1"], +["NOP", "NOP2 1"], +["NOP", "NOP3 1"], +["NOP", "NOP4 1"], +["NOP", "NOP5 1"], +["NOP", "NOP6 1"], +["NOP", "NOP7 1"], +["NOP", "NOP8 1"], +["NOP", "NOP9 1"], +["NOP", "NOP10 1"], + +["0 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL", "Very basic P2SH"], +["0x4c 0 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL"] +] diff --git a/src/test/data/sig_canonical.json b/src/test/data/sig_canonical.json new file mode 100644 index 0000000..e43a086 --- /dev/null +++ b/src/test/data/sig_canonical.json @@ -0,0 +1,7 @@ +[ + "300602010002010001", + "3008020200ff020200ff01", + "304402203932c892e2e550f3af8ee4ce9c215a87f9bb831dcac87b2838e2c2eaa891df0c022030b61dd36543125d56b9f9f3a1f9353189e5af33cdda8d77a5209aec03978fa001", + "30450220076045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40f90a0221008fffd599910eefe00bc803c688c2eca1d2ba7f6b180620eaa03488e6585db6ba01", + "3046022100876045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40f90a0221008fffd599910eefe00bc803c688c2eca1d2ba7f6b180620eaa03488e6585db6ba01" +] diff --git a/src/test/data/sig_noncanonical.json b/src/test/data/sig_noncanonical.json new file mode 100644 index 0000000..d9a6c1c --- /dev/null +++ b/src/test/data/sig_noncanonical.json @@ -0,0 +1,22 @@ +[ + "non-hex strings are ignored", + + "too short:", "30050201FF020001", + "too long:", "30470221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105022200002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01", + "hashtype:", "304402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed11", + "type:", "314402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01", + "total length:", "304502205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01", + "S len oob:", "301F01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb101", + "R+S:", "304502205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed0001", + + "R type:", "304401205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01", + "R len = 0:", "3024020002202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01", + "R<0:", "304402208990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01", + "R padded:", "30450221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01", + + + "S type:", "304402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610501202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01", + "S len = 0:", "302402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105020001", + "S<0:", "304402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61050220fd5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01", + "S padded:", "304502205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61050221002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01" +] diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json new file mode 100644 index 0000000..81e77b7 --- /dev/null +++ b/src/test/data/tx_invalid.json @@ -0,0 +1,69 @@ +[ +["The following are deserialized transactions which are invalid."], +["They are in the form"], +["[[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], +["serializedTransaction, enforceP2SH]"], +["Objects that are only a single string (like this one) are ignored"], + +["0e1b5688cf179cd9f7cbda1fac0090f6e684bbf8cd946660120197c3f3681809 but with extra junk appended to the end of the scriptPubKey"], +[[["6ca7ec7b1847f6bdbd737176050e6a08d66ccd55bb94ad24f4018024107a5827", 0, "0x41 0x043b640e983c9690a14c039a2037ecc3467b27a0dcd58f19d76c7bc118d09fec45adc5370a1c5bf8067ca9f5557a4cf885fdb0fe0dcc9c3a7137226106fbc779a5 CHECKSIG VERIFY 1"]], +"010000000127587a10248001f424ad94bb55cd6cd6086a0e05767173bdbdf647187beca76c000000004948304502201b822ad10d6adc1a341ae8835be3f70a25201bbff31f59cbb9c5353a5f0eca18022100ea7b2f7074e9aa9cf70aa8d0ffee13e6b45dddabf1ab961bda378bcdb778fa4701ffffffff0100f2052a010000001976a914fc50c5907d86fed474ba5ce8b12a66e0a4c139d888ac00000000", true], + +["This is the nearly-standard transaction with CHECKSIGVERIFY 1 instead of CHECKSIG from tx_valid.json"], +["but with the signature duplicated in the scriptPubKey with a non-standard pushdata prefix"], +["See FindAndDelete, which will only remove if it uses the same pushdata prefix as is standard"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x4c 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", true], + +["Same as above, but with the sig in the scriptSig also pushed with the same non-standard OP_PUSHDATA"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x4c 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006b4c473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", true], + +["An invalid P2SH Transaction"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", true], + +["Tests for CTransaction::CheckTransaction()"], +["No inputs"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]], +"0100000000010000000000000000015100000000", true], + +["No outputs"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x05ab9e14d983742513f0f451e105ffb4198d1dd4 EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022100f16703104aab4e4088317c862daec83440242411b039d14280e03dd33b487ab802201318a7be236672c5c56083eb7a5a195bc57a40af7923ff8545016cd3b571e2a601232103c40e5d339df3f30bf753e7e04450ae4ef76c9e45587d1d993bdc4cd06f0651c7acffffffff0000000000", true], + +["Negative output"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xae609aca8061d77c5e111f6bb62501a6bbe2bfdb EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d4830450220063222cbb128731fc09de0d7323746539166544d6c1df84d867ccea84bcc8903022100bf568e8552844de664cd41648a031554327aa8844af34b4f27397c65b92c04de0123210243ec37dee0e2e053a9c976f43147e79bc7d9dc606ea51010af1ac80db6b069e1acffffffff01ffffffffffffffff015100000000", true], + +["MAX_MONEY + 1 output"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x32afac281462b822adbec5094b8d4d337dd5bd6a EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010140075af0750700015100000000", true], + +["MAX_MONEY output + 1 output"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xb558cbf4930954aa6a344363a15668d7477ae716 EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022027deccc14aa6668e78a8c9da3484fbcd4f9dcc9bb7d1b85146314b21b9ae4d86022100d0b43dece8cfb07348de0ca8bc5b86276fa88f7f2138381128b7c36ab2e42264012321029bb13463ddd5d2cc05da6e84e37536cb9525703cfd8f43afdb414988987a92f6acffffffff020040075af075070001510001000000000000015100000000", true], + +["Duplicate inputs"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x236d0639db62b0773fd8ac34dc85ae19e9aba80a EQUAL"]], +"01000000020001000000000000000000000000000000000000000000000000000000000000000000006c47304402204bb1197053d0d7799bf1b30cd503c44b58d6240cccbdc85b6fe76d087980208f02204beeed78200178ffc6c74237bb74b3f276bbb4098b5605d814304fe128bf1431012321039e8815e15952a7c3fada1905f8cf55419837133bd7756c0ef14fc8dfe50c0deaacffffffff0001000000000000000000000000000000000000000000000000000000000000000000006c47304402202306489afef52a6f62e90bf750bbcdf40c06f5c6b138286e6b6b86176bb9341802200dba98486ea68380f47ebb19a7df173b99e6bc9c681d6ccf3bde31465d1f16b3012321039e8815e15952a7c3fada1905f8cf55419837133bd7756c0ef14fc8dfe50c0deaacffffffff010000000000000000015100000000", true], + +["Coinbase of size 1"], +["Note the input is just required to make the tester happy"], +[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], +"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0151ffffffff010000000000000000015100000000", true], + +["Coinbase of size 101"], +["Note the input is just required to make the tester happy"], +[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], +"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff655151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151ffffffff010000000000000000015100000000", true], + +["Null txin"], +[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "HASH160 0x14 0x02dae7dbbda56097959cba59b1989dd3e47937bf EQUAL"]], +"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff6e49304602210086f39e028e46dafa8e1e3be63906465f4cf038fbe5ed6403dc3e74ae876e6431022100c4625c675cfc5c7e3a0e0d7eaec92ac24da20c73a88eb40d09253e51ac6def5201232103a183ddc41e84753aca47723c965d1b5c8b0e2b537963518355e6dd6cf8415e50acffffffff010000000000000000015100000000", true], + +["Same as the transactions in valid with one input SIGHASH_ALL and one SIGHASH_ANYONECANPAY, but we set the _ANYONECANPAY sequence number, invalidating the SIGHASH_ALL signature"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"], + ["0000000000000000000000000000000000000000000000000000000000000200", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"]], + "01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df10101000000000200000000000000000000000000000000000000000000000000000000000000000000484730440220201dc2d030e380e8f9cfb41b442d930fa5a685bb2c8db5906671f865507d0670022018d9e7a8d4c8d86a73c2a724ee38ef983ec249827e0e464841735955c707ece98101000000010100000000000000015100000000", true] +] diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json new file mode 100644 index 0000000..c609924 --- /dev/null +++ b/src/test/data/tx_valid.json @@ -0,0 +1,81 @@ +[ +["The following are deserialized transactions which are valid."], +["They are in the form"], +["[[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], +["serializedTransaction, enforceP2SH]"], +["Objects that are only a single string (like this one) are ignored"], + +["The following is 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], +["It is of particular interest because it contains an invalidly-encoded signature which OpenSSL accepts"], +["See http://r6.ca/blog/20111119T211504Z.html"], +["It is also the first OP_CHECKMULTISIG transaction in standard form"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", true], + +["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], +["It has an arbitrary extra byte stuffed into the signature at pos length - 2"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004A0048304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2bab01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", true], + +["The following is c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73"], +["It is of interest because it contains a 0-sequence as well as a signature of SIGHASH type 0 (which is not a real type)"], +[[["406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602", 0, "DUP HASH160 0x14 0xdc44b1164188067c3a32d4780f5996fa14a4f2d9 EQUALVERIFY CHECKSIG"]], +"01000000010276b76b07f4935c70acf54fbf1f438a4c397a9fb7e633873c4dd3bc062b6b40000000008c493046022100d23459d03ed7e9511a47d13292d3430a04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b2effc514b76279476550ba3663fdcaff94c38420e9d5000000000100093d00000000001976a9149a7b0f3b80c6baaeedce0a0842553800f832ba1f88ac00000000", true], + +["A nearly-standard transaction with CHECKSIGVERIFY 1 instead of CHECKSIG"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", true], + +["Same as above, but with the signature duplicated in the scriptPubKey with the proper pushdata prefix"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", true], + +["The following is f7fdd091fa6d8f5e7a8c2458f5c38faffff2d3f1406b6e4fe2c99dcc0d2d1cbb"], +["It caught a bug in the workaround for 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63 in an overly simple implementation"], +[[["b464e85df2a238416f8bdae11d120add610380ea07f4ef19c5f9dfd472f96c3d", 0, "DUP HASH160 0x14 0xbef80ecf3a44500fda1bc92176e442891662aed2 EQUALVERIFY CHECKSIG"], +["b7978cc96e59a8b13e0865d3f95657561a7f725be952438637475920bac9eb21", 1, "DUP HASH160 0x14 0xbef80ecf3a44500fda1bc92176e442891662aed2 EQUALVERIFY CHECKSIG"]], +"01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a4730440220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f81e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df32949d4646dfa10a92458cfaa88ac00000000", true], + +["The following tests for the presence of a bug in the handling of SIGHASH_SINGLE"], +["It results in signing the constant 1, instead of something generated based on the transaction,"], +["when the input doing the signing has an index greater than the maximum output index"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0xe52b482f2faa8ecbf0db344f93c84ac908557f33 EQUALVERIFY CHECKSIG"], ["0000000000000000000000000000000000000000000000000000000000000200", 0, "1"]], +"01000000020002000000000000000000000000000000000000000000000000000000000000000000000151ffffffff0001000000000000000000000000000000000000000000000000000000000000000000006b483045022100c9cdd08798a28af9d1baf44a6c77bcc7e279f47dc487c8c899911bc48feaffcc0220503c5c50ae3998a733263c5c0f7061b483e2b56c4c41b456e7d2f5a78a74c077032102d5c25adb51b61339d2b05315791e21bbe80ea470a49db0135720983c905aace0ffffffff010000000000000000015100000000", true], + +["An invalid P2SH Transaction"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", false], + +["A valid P2SH Transaction using the standard transaction type put forth in BIP 16"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x8febbed40483661de6958d957412f82deed8e2f7 EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100c66c9cdf4c43609586d15424c54707156e316d88b0a1534c9e6b0d4f311406310221009c0fe51dbc9c4ab7cc25d3fdbeccf6679fe6827f08edf2b4a9f16ee3eb0e438a0123210338e8034509af564c62644c07691942e0c056752008a173c89f60ab2a88ac2ebfacffffffff010000000000000000015100000000", true], + +["Tests for CTransaction::CheckTransaction()"], +["MAX_MONEY output"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x32afac281462b822adbec5094b8d4d337dd5bd6a EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010040075af0750700015100000000", true], + +["MAX_MONEY output + 0 output"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xb558cbf4930954aa6a344363a15668d7477ae716 EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022027deccc14aa6668e78a8c9da3484fbcd4f9dcc9bb7d1b85146314b21b9ae4d86022100d0b43dece8cfb07348de0ca8bc5b86276fa88f7f2138381128b7c36ab2e42264012321029bb13463ddd5d2cc05da6e84e37536cb9525703cfd8f43afdb414988987a92f6acffffffff020040075af075070001510000000000000000015100000000", true], + +["Coinbase of size 2"], +["Note the input is just required to make the tester happy"], +[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], +"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff025151ffffffff010000000000000000015100000000", true], + +["Coinbase of size 100"], +["Note the input is just required to make the tester happy"], +[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], +"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff6451515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151ffffffff010000000000000000015100000000", true], + +["Simple transaction with first input is signed with SIGHASH_ALL, second with SIGHASH_ANYONECANPAY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"], + ["0000000000000000000000000000000000000000000000000000000000000200", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"]], + "010000000200010000000000000000000000000000000000000000000000000000000000000000000049483045022100d180fd2eb9140aeb4210c9204d3f358766eb53842b2a9473db687fa24b12a3cc022079781799cd4f038b85135bbe49ec2b57f306b2bb17101b17f71f000fcab2b6fb01ffffffff0002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000", true], + +["Same as above, but we change the sequence number of the first input to check that SIGHASH_ANYONECANPAY is being followed"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"], + ["0000000000000000000000000000000000000000000000000000000000000200", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"]], + "01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df101010000000002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000", true] +] diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp new file mode 100644 index 0000000..78953d2 --- /dev/null +++ b/src/test/getarg_tests.cpp @@ -0,0 +1,167 @@ +#include +#include +#include + +#include "util.h" + +BOOST_AUTO_TEST_SUITE(getarg_tests) + +static void +ResetArgs(const std::string& strArg) +{ + std::vector vecArg; + boost::split(vecArg, strArg, boost::is_space(), boost::token_compress_on); + + // Insert dummy executable name: + vecArg.insert(vecArg.begin(), "testbitcoin"); + + // Convert to char*: + std::vector vecChar; + BOOST_FOREACH(std::string& s, vecArg) + vecChar.push_back(s.c_str()); + + ParseParameters(vecChar.size(), &vecChar[0]); +} + +BOOST_AUTO_TEST_CASE(boolarg) +{ + ResetArgs("-foo"); + BOOST_CHECK(GetBoolArg("-foo")); + BOOST_CHECK(GetBoolArg("-foo", false)); + BOOST_CHECK(GetBoolArg("-foo", true)); + + BOOST_CHECK(!GetBoolArg("-fo")); + BOOST_CHECK(!GetBoolArg("-fo", false)); + BOOST_CHECK(GetBoolArg("-fo", true)); + + BOOST_CHECK(!GetBoolArg("-fooo")); + BOOST_CHECK(!GetBoolArg("-fooo", false)); + BOOST_CHECK(GetBoolArg("-fooo", true)); + + ResetArgs("-foo=0"); + BOOST_CHECK(!GetBoolArg("-foo")); + BOOST_CHECK(!GetBoolArg("-foo", false)); + BOOST_CHECK(!GetBoolArg("-foo", true)); + + ResetArgs("-foo=1"); + BOOST_CHECK(GetBoolArg("-foo")); + BOOST_CHECK(GetBoolArg("-foo", false)); + BOOST_CHECK(GetBoolArg("-foo", true)); + + // New 0.6 feature: auto-map -nosomething to !-something: + ResetArgs("-nofoo"); + BOOST_CHECK(!GetBoolArg("-foo")); + BOOST_CHECK(!GetBoolArg("-foo", false)); + BOOST_CHECK(!GetBoolArg("-foo", true)); + + ResetArgs("-nofoo=1"); + BOOST_CHECK(!GetBoolArg("-foo")); + BOOST_CHECK(!GetBoolArg("-foo", false)); + BOOST_CHECK(!GetBoolArg("-foo", true)); + + ResetArgs("-foo -nofoo"); // -foo should win + BOOST_CHECK(GetBoolArg("-foo")); + BOOST_CHECK(GetBoolArg("-foo", false)); + BOOST_CHECK(GetBoolArg("-foo", true)); + + ResetArgs("-foo=1 -nofoo=1"); // -foo should win + BOOST_CHECK(GetBoolArg("-foo")); + BOOST_CHECK(GetBoolArg("-foo", false)); + BOOST_CHECK(GetBoolArg("-foo", true)); + + ResetArgs("-foo=0 -nofoo=0"); // -foo should win + BOOST_CHECK(!GetBoolArg("-foo")); + BOOST_CHECK(!GetBoolArg("-foo", false)); + BOOST_CHECK(!GetBoolArg("-foo", true)); + + // New 0.6 feature: treat -- same as -: + ResetArgs("--foo=1"); + BOOST_CHECK(GetBoolArg("-foo")); + BOOST_CHECK(GetBoolArg("-foo", false)); + BOOST_CHECK(GetBoolArg("-foo", true)); + + ResetArgs("--nofoo=1"); + BOOST_CHECK(!GetBoolArg("-foo")); + BOOST_CHECK(!GetBoolArg("-foo", false)); + BOOST_CHECK(!GetBoolArg("-foo", true)); + +} + +BOOST_AUTO_TEST_CASE(stringarg) +{ + ResetArgs(""); + BOOST_CHECK_EQUAL(GetArg("-foo", ""), ""); + BOOST_CHECK_EQUAL(GetArg("-foo", "eleven"), "eleven"); + + ResetArgs("-foo -bar"); + BOOST_CHECK_EQUAL(GetArg("-foo", ""), ""); + BOOST_CHECK_EQUAL(GetArg("-foo", "eleven"), ""); + + ResetArgs("-foo="); + BOOST_CHECK_EQUAL(GetArg("-foo", ""), ""); + BOOST_CHECK_EQUAL(GetArg("-foo", "eleven"), ""); + + ResetArgs("-foo=11"); + BOOST_CHECK_EQUAL(GetArg("-foo", ""), "11"); + BOOST_CHECK_EQUAL(GetArg("-foo", "eleven"), "11"); + + ResetArgs("-foo=eleven"); + BOOST_CHECK_EQUAL(GetArg("-foo", ""), "eleven"); + BOOST_CHECK_EQUAL(GetArg("-foo", "eleven"), "eleven"); + +} + +BOOST_AUTO_TEST_CASE(intarg) +{ + ResetArgs(""); + BOOST_CHECK_EQUAL(GetArg("-foo", 11), 11); + BOOST_CHECK_EQUAL(GetArg("-foo", 0), 0); + + ResetArgs("-foo -bar"); + BOOST_CHECK_EQUAL(GetArg("-foo", 11), 0); + BOOST_CHECK_EQUAL(GetArg("-bar", 11), 0); + + ResetArgs("-foo=11 -bar=12"); + BOOST_CHECK_EQUAL(GetArg("-foo", 0), 11); + BOOST_CHECK_EQUAL(GetArg("-bar", 11), 12); + + ResetArgs("-foo=NaN -bar=NotANumber"); + BOOST_CHECK_EQUAL(GetArg("-foo", 1), 0); + BOOST_CHECK_EQUAL(GetArg("-bar", 11), 0); +} + +BOOST_AUTO_TEST_CASE(doubledash) +{ + ResetArgs("--foo"); + BOOST_CHECK_EQUAL(GetBoolArg("-foo"), true); + + ResetArgs("--foo=verbose --bar=1"); + BOOST_CHECK_EQUAL(GetArg("-foo", ""), "verbose"); + BOOST_CHECK_EQUAL(GetArg("-bar", 0), 1); +} + +BOOST_AUTO_TEST_CASE(boolargno) +{ + ResetArgs("-nofoo"); + BOOST_CHECK(!GetBoolArg("-foo")); + BOOST_CHECK(!GetBoolArg("-foo", true)); + BOOST_CHECK(!GetBoolArg("-foo", false)); + + ResetArgs("-nofoo=1"); + BOOST_CHECK(!GetBoolArg("-foo")); + BOOST_CHECK(!GetBoolArg("-foo", true)); + BOOST_CHECK(!GetBoolArg("-foo", false)); + + ResetArgs("-nofoo=0"); + BOOST_CHECK(GetBoolArg("-foo")); + BOOST_CHECK(GetBoolArg("-foo", true)); + BOOST_CHECK(GetBoolArg("-foo", false)); + + ResetArgs("-foo --nofoo"); + BOOST_CHECK(GetBoolArg("-foo")); + + ResetArgs("-nofoo -foo"); // foo always wins: + BOOST_CHECK(GetBoolArg("-foo")); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp new file mode 100644 index 0000000..f94f915 --- /dev/null +++ b/src/test/key_tests.cpp @@ -0,0 +1,141 @@ +#include + +#include +#include + +#include "key.h" +#include "base58.h" +#include "uint256.h" +#include "util.h" + +using namespace std; + +static const string strSecret1 ("6uu5bsZLA2Lm6yCxgwxDxHyZmhYeqBMLQT83Fyq738YhYucQPQf"); +static const string strSecret2 ("6vZDRwYgTNidWzmKs9x8QzQGeWCqbdUtNRpEKZMaP67ZSn8XMjb"); +static const string strSecret1C ("T6UsJv9hYpvDfM5noKYkB3vfeHxhyegkeWJ4y7qKeQJuyXMK11XX"); +static const string strSecret2C ("T9PBs5kq9QrkBPxeGNWKitMi4XuFVr25jaXTnuopLVZxCUAJbixA"); +static const CBitcoinAddress addr1 ("LWaFezDtucfCA4xcVEfs3R3xfgGWjSwcZr"); +static const CBitcoinAddress addr2 ("LXwHM6mRd432EzLJYwuKQMPhTzrgr7ur9K"); +static const CBitcoinAddress addr1C("LZWK8h7C166niP6GmpUmiGrvn4oxPqQgFV"); +static const CBitcoinAddress addr2C("Lgb6tdqmdW3n5E12johSuEAqRMt4kAr7yu"); + + +static const string strAddressBad("LRjyUS2uuieEPkhZNdQz8hE5YycxVEqSXA"); + + +#ifdef KEY_TESTS_DUMPINFO +void dumpKeyInfo(uint256 privkey) +{ + CKey key; + key.resize(32); + memcpy(&secret[0], &privkey, 32); + vector sec; + sec.resize(32); + memcpy(&sec[0], &secret[0], 32); + printf(" * secret (hex): %s\n", HexStr(sec).c_str()); + + for (int nCompressed=0; nCompressed<2; nCompressed++) + { + bool fCompressed = nCompressed == 1; + printf(" * %s:\n", fCompressed ? "compressed" : "uncompressed"); + CBitcoinSecret bsecret; + bsecret.SetSecret(secret, fCompressed); + printf(" * secret (base58): %s\n", bsecret.ToString().c_str()); + CKey key; + key.SetSecret(secret, fCompressed); + vector vchPubKey = key.GetPubKey(); + printf(" * pubkey (hex): %s\n", HexStr(vchPubKey).c_str()); + printf(" * address (base58): %s\n", CBitcoinAddress(vchPubKey).ToString().c_str()); + } +} +#endif + + +BOOST_AUTO_TEST_SUITE(key_tests) + +BOOST_AUTO_TEST_CASE(key_test1) +{ + CBitcoinSecret bsecret1, bsecret2, bsecret1C, bsecret2C, baddress1; + BOOST_CHECK( bsecret1.SetString (strSecret1)); + BOOST_CHECK( bsecret2.SetString (strSecret2)); + BOOST_CHECK( bsecret1C.SetString(strSecret1C)); + BOOST_CHECK( bsecret2C.SetString(strSecret2C)); + BOOST_CHECK(!baddress1.SetString(strAddressBad)); + + CKey key1 = bsecret1.GetKey(); + BOOST_CHECK(key1.IsCompressed() == false); + CKey key2 = bsecret2.GetKey(); + BOOST_CHECK(key2.IsCompressed() == false); + CKey key1C = bsecret1C.GetKey(); + BOOST_CHECK(key1C.IsCompressed() == true); + CKey key2C = bsecret2C.GetKey(); + BOOST_CHECK(key1C.IsCompressed() == true); + + CPubKey pubkey1 = key1. GetPubKey(); + CPubKey pubkey2 = key2. GetPubKey(); + CPubKey pubkey1C = key1C.GetPubKey(); + CPubKey pubkey2C = key2C.GetPubKey(); + + BOOST_CHECK(addr1.Get() == CTxDestination(pubkey1.GetID())); + BOOST_CHECK(addr2.Get() == CTxDestination(pubkey2.GetID())); + BOOST_CHECK(addr1C.Get() == CTxDestination(pubkey1C.GetID())); + BOOST_CHECK(addr2C.Get() == CTxDestination(pubkey2C.GetID())); + + for (int n=0; n<16; n++) + { + string strMsg = strprintf("Very secret message %i: 11", n); + uint256 hashMsg = Hash(strMsg.begin(), strMsg.end()); + + // normal signatures + + vector sign1, sign2, sign1C, sign2C; + + BOOST_CHECK(key1.Sign (hashMsg, sign1)); + BOOST_CHECK(key2.Sign (hashMsg, sign2)); + BOOST_CHECK(key1C.Sign(hashMsg, sign1C)); + BOOST_CHECK(key2C.Sign(hashMsg, sign2C)); + + BOOST_CHECK( pubkey1.Verify(hashMsg, sign1)); + BOOST_CHECK(!pubkey1.Verify(hashMsg, sign2)); + BOOST_CHECK( pubkey1.Verify(hashMsg, sign1C)); + BOOST_CHECK(!pubkey1.Verify(hashMsg, sign2C)); + + BOOST_CHECK(!pubkey2.Verify(hashMsg, sign1)); + BOOST_CHECK( pubkey2.Verify(hashMsg, sign2)); + BOOST_CHECK(!pubkey2.Verify(hashMsg, sign1C)); + BOOST_CHECK( pubkey2.Verify(hashMsg, sign2C)); + + BOOST_CHECK( pubkey1C.Verify(hashMsg, sign1)); + BOOST_CHECK(!pubkey1C.Verify(hashMsg, sign2)); + BOOST_CHECK( pubkey1C.Verify(hashMsg, sign1C)); + BOOST_CHECK(!pubkey1C.Verify(hashMsg, sign2C)); + + BOOST_CHECK(!pubkey2C.Verify(hashMsg, sign1)); + BOOST_CHECK( pubkey2C.Verify(hashMsg, sign2)); + BOOST_CHECK(!pubkey2C.Verify(hashMsg, sign1C)); + BOOST_CHECK( pubkey2C.Verify(hashMsg, sign2C)); + + // compact signatures (with key recovery) + + vector csign1, csign2, csign1C, csign2C; + + BOOST_CHECK(key1.SignCompact (hashMsg, csign1)); + BOOST_CHECK(key2.SignCompact (hashMsg, csign2)); + BOOST_CHECK(key1C.SignCompact(hashMsg, csign1C)); + BOOST_CHECK(key2C.SignCompact(hashMsg, csign2C)); + + CPubKey rkey1, rkey2, rkey1C, rkey2C; + + BOOST_CHECK(rkey1.RecoverCompact (hashMsg, csign1)); + BOOST_CHECK(rkey2.RecoverCompact (hashMsg, csign2)); + BOOST_CHECK(rkey1C.RecoverCompact(hashMsg, csign1C)); + BOOST_CHECK(rkey2C.RecoverCompact(hashMsg, csign2C)); + + BOOST_CHECK(rkey1 == pubkey1); + BOOST_CHECK(rkey2 == pubkey2); + BOOST_CHECK(rkey1C == pubkey1C); + BOOST_CHECK(rkey2C == pubkey2C); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp new file mode 100644 index 0000000..b5f78d0 --- /dev/null +++ b/src/test/miner_tests.cpp @@ -0,0 +1,230 @@ +#include + +#include "init.h" +#include "main.h" +#include "uint256.h" +#include "util.h" +#include "wallet.h" + +extern void SHA256Transform(void* pstate, void* pinput, const void* pinit); + +BOOST_AUTO_TEST_SUITE(miner_tests) + +static +struct { + unsigned char extranonce; + unsigned int nonce; +} blockinfo[] = { + {4, 0xa4ad9f65}, {2, 0x15cf2b27}, {1, 0x037620ac}, {1, 0x700d9c54}, + {2, 0xce79f74f}, {2, 0x52d9c194}, {1, 0x77bc3efc}, {2, 0xbb62c5e8}, + {2, 0x83ff997a}, {1, 0x48b984ee}, {1, 0xef925da0}, {2, 0x680d2979}, + {2, 0x08953af7}, {1, 0x087dd553}, {2, 0x210e2818}, {2, 0xdfffcdef}, + {1, 0xeea1b209}, {2, 0xba4a8943}, {1, 0xa7333e77}, {1, 0x344f3e2a}, + {3, 0xd651f08e}, {2, 0xeca3957f}, {2, 0xca35aa49}, {1, 0x6bb2065d}, + {2, 0x0170ee44}, {1, 0x6e12f4aa}, {2, 0x43f4f4db}, {2, 0x279c1c44}, + {2, 0xb5a50f10}, {2, 0xb3902841}, {2, 0xd198647e}, {2, 0x6bc40d88}, + {1, 0x633a9a1c}, {2, 0x9a722ed8}, {2, 0x55580d10}, {1, 0xd65022a1}, + {2, 0xa12ffcc8}, {1, 0x75a6a9c7}, {2, 0xfb7c80b7}, {1, 0xe8403e6c}, + {1, 0xe34017a0}, {3, 0x659e177b}, {2, 0xba5c40bf}, {5, 0x022f11ef}, + {1, 0xa9ab516a}, {5, 0xd0999ed4}, {1, 0x37277cb3}, {1, 0x830f735f}, + {1, 0xc6e3d947}, {2, 0x824a0c1b}, {1, 0x99962416}, {1, 0x75336f63}, + {1, 0xaacf0fea}, {1, 0xd6531aec}, {5, 0x7afcf541}, {5, 0x9d6fac0d}, + {1, 0x4cf5c4df}, {1, 0xabe0f2a0}, {6, 0x4a3dac18}, {2, 0xf265febe}, + {2, 0x1bc9f23f}, {1, 0xad49ab71}, {1, 0x9f2d8923}, {1, 0x15acb65d}, + {2, 0xd1cecb52}, {2, 0xf856808b}, {1, 0x0fa96e29}, {1, 0xe063ecbc}, + {1, 0x78d926c6}, {5, 0x3e38ad35}, {5, 0x73901915}, {1, 0x63424be0}, + {1, 0x6d6b0a1d}, {2, 0x888ba681}, {2, 0xe96b0714}, {1, 0xb7fcaa55}, + {2, 0x19c106eb}, {1, 0x5aa13484}, {2, 0x5bf4c2f3}, {2, 0x94d401dd}, + {1, 0xa9bc23d9}, {1, 0x3a69c375}, {1, 0x56ed2006}, {5, 0x85ba6dbd}, + {1, 0xfd9b2000}, {1, 0x2b2be19a}, {1, 0xba724468}, {1, 0x717eb6e5}, + {1, 0x70de86d9}, {1, 0x74e23a42}, {1, 0x49e92832}, {2, 0x6926dbb9}, + {0, 0x64452497}, {1, 0x54306d6f}, {2, 0x97ebf052}, {2, 0x55198b70}, + {2, 0x03fe61f0}, {1, 0x98f9e67f}, {1, 0xc0842a09}, {1, 0xdfed39c5}, + {1, 0x3144223e}, {1, 0xb3d12f84}, {1, 0x7366ceb7}, {5, 0x6240691b}, + {2, 0xd3529b57}, {1, 0xf4cae3b1}, {1, 0x5b1df222}, {1, 0xa16a5c70}, + {2, 0xbbccedc6}, {2, 0xfe38d0ef}, +}; + +// NOTE: These tests rely on CreateNewBlock doing its own self-validation! +BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) +{ + CReserveKey reservekey(pwalletMain); + CBlockTemplate *pblocktemplate; + CTransaction tx; + CScript script; + uint256 hash; + + // Simple block creation, nothing special yet: + BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + + // We can't make transactions until we have inputs + // Therefore, load 100 blocks :) + std::vectortxFirst; + for (unsigned int i = 0; i < sizeof(blockinfo)/sizeof(*blockinfo); ++i) + { + CBlock *pblock = &pblocktemplate->block; // pointer for convenience + pblock->nVersion = 1; + pblock->nTime = pindexBest->GetMedianTimePast()+1; + pblock->vtx[0].vin[0].scriptSig = CScript(); + pblock->vtx[0].vin[0].scriptSig.push_back(blockinfo[i].extranonce); + pblock->vtx[0].vin[0].scriptSig.push_back(pindexBest->nHeight); + pblock->vtx[0].vout[0].scriptPubKey = CScript(); + if (txFirst.size() < 2) + txFirst.push_back(new CTransaction(pblock->vtx[0])); + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + pblock->nNonce = blockinfo[i].nonce; + CValidationState state; + BOOST_CHECK(ProcessBlock(state, NULL, pblock)); + BOOST_CHECK(state.IsValid()); + pblock->hashPrevBlock = pblock->GetHash(); + } + delete pblocktemplate; + + // Just to make sure we can still make simple blocks + BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + + // block sigops > limit: 1000 CHECKMULTISIG + 1 + tx.vin.resize(1); + // NOTE: OP_NOP is used to force 20 SigOps for the CHECKMULTISIG + tx.vin[0].scriptSig = CScript() << OP_0 << OP_0 << OP_0 << OP_NOP << OP_CHECKMULTISIG << OP_1; + tx.vin[0].prevout.hash = txFirst[0]->GetHash(); + tx.vin[0].prevout.n = 0; + tx.vout.resize(1); + tx.vout[0].nValue = 5000000000LL; + for (unsigned int i = 0; i < 1001; ++i) + { + tx.vout[0].nValue -= 1000000; + hash = tx.GetHash(); + mempool.addUnchecked(hash, tx); + tx.vin[0].prevout.hash = hash; + } + BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + delete pblocktemplate; + mempool.clear(); + + // block size > limit + tx.vin[0].scriptSig = CScript(); + // 18 * (520char + DROP) + OP_1 = 9433 bytes + std::vector vchData(520); + for (unsigned int i = 0; i < 18; ++i) + tx.vin[0].scriptSig << vchData << OP_DROP; + tx.vin[0].scriptSig << OP_1; + tx.vin[0].prevout.hash = txFirst[0]->GetHash(); + tx.vout[0].nValue = 5000000000LL; + for (unsigned int i = 0; i < 128; ++i) + { + tx.vout[0].nValue -= 10000000; + hash = tx.GetHash(); + mempool.addUnchecked(hash, tx); + tx.vin[0].prevout.hash = hash; + } + BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + delete pblocktemplate; + mempool.clear(); + + // orphan in mempool + hash = tx.GetHash(); + mempool.addUnchecked(hash, tx); + BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + delete pblocktemplate; + mempool.clear(); + + // child with higher priority than parent + tx.vin[0].scriptSig = CScript() << OP_1; + tx.vin[0].prevout.hash = txFirst[1]->GetHash(); + tx.vout[0].nValue = 4900000000LL; + hash = tx.GetHash(); + mempool.addUnchecked(hash, tx); + tx.vin[0].prevout.hash = hash; + tx.vin.resize(2); + tx.vin[1].scriptSig = CScript() << OP_1; + tx.vin[1].prevout.hash = txFirst[0]->GetHash(); + tx.vin[1].prevout.n = 0; + tx.vout[0].nValue = 5900000000LL; + hash = tx.GetHash(); + mempool.addUnchecked(hash, tx); + BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + delete pblocktemplate; + mempool.clear(); + + // coinbase in mempool + tx.vin.resize(1); + tx.vin[0].prevout.SetNull(); + tx.vin[0].scriptSig = CScript() << OP_0 << OP_1; + tx.vout[0].nValue = 0; + hash = tx.GetHash(); + mempool.addUnchecked(hash, tx); + BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + delete pblocktemplate; + mempool.clear(); + + // invalid (pre-p2sh) txn in mempool + tx.vin[0].prevout.hash = txFirst[0]->GetHash(); + tx.vin[0].prevout.n = 0; + tx.vin[0].scriptSig = CScript() << OP_1; + tx.vout[0].nValue = 4900000000LL; + script = CScript() << OP_0; + tx.vout[0].scriptPubKey.SetDestination(script.GetID()); + hash = tx.GetHash(); + mempool.addUnchecked(hash, tx); + tx.vin[0].prevout.hash = hash; + tx.vin[0].scriptSig = CScript() << (std::vector)script; + tx.vout[0].nValue -= 1000000; + hash = tx.GetHash(); + mempool.addUnchecked(hash,tx); + BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + delete pblocktemplate; + mempool.clear(); + + // double spend txn pair in mempool + tx.vin[0].prevout.hash = txFirst[0]->GetHash(); + tx.vin[0].scriptSig = CScript() << OP_1; + tx.vout[0].nValue = 4900000000LL; + tx.vout[0].scriptPubKey = CScript() << OP_1; + hash = tx.GetHash(); + mempool.addUnchecked(hash, tx); + tx.vout[0].scriptPubKey = CScript() << OP_2; + hash = tx.GetHash(); + mempool.addUnchecked(hash, tx); + BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + delete pblocktemplate; + mempool.clear(); + + // subsidy changing + int nHeight = pindexBest->nHeight; + pindexBest->nHeight = 209999; + BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + delete pblocktemplate; + pindexBest->nHeight = 210000; + BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + delete pblocktemplate; + pindexBest->nHeight = nHeight; +} + +BOOST_AUTO_TEST_CASE(sha256transform_equality) +{ + unsigned int pSHA256InitState[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; + + + // unsigned char pstate[32]; + unsigned char pinput[64]; + + int i; + + for (i = 0; i < 32; i++) { + pinput[i] = i; + pinput[i+32] = 0; + } + + uint256 hash; + + SHA256Transform(&hash, pinput, pSHA256InitState); + + BOOST_TEST_MESSAGE(hash.GetHex()); + + uint256 hash_reference("0x2df5e1c65ef9f8cde240d23cae2ec036d31a15ec64bc68f64be242b1da6631f3"); + + BOOST_CHECK(hash == hash_reference); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/mruset_tests.cpp b/src/test/mruset_tests.cpp new file mode 100644 index 0000000..64a6678 --- /dev/null +++ b/src/test/mruset_tests.cpp @@ -0,0 +1,90 @@ +#include + +using namespace std; + +#include "mruset.h" +#include "util.h" + +#define NUM_TESTS 16 +#define MAX_SIZE 100 + +class mrutester +{ +private: + mruset mru; + std::set set; + +public: + mrutester() { mru.max_size(MAX_SIZE); } + int size() const { return set.size(); } + + void insert(int n) + { + mru.insert(n); + set.insert(n); + BOOST_CHECK(mru == set); + } +}; + +BOOST_AUTO_TEST_SUITE(mruset_tests) + +// Test that an mruset behaves like a set, as long as no more than MAX_SIZE elements are in it +BOOST_AUTO_TEST_CASE(mruset_like_set) +{ + + for (int nTest=0; nTest mru(MAX_SIZE); + for (int nAction=0; nAction<3*MAX_SIZE; nAction++) + { + int n = GetRandInt(2 * MAX_SIZE); + mru.insert(n); + BOOST_CHECK(mru.size() <= MAX_SIZE); + } + } +} + +// 16-bit permutation function +int static permute(int n) +{ + // hexadecimals of pi; verified to be linearly independent + static const int table[16] = {0x243F, 0x6A88, 0x85A3, 0x08D3, 0x1319, 0x8A2E, 0x0370, 0x7344, + 0xA409, 0x3822, 0x299F, 0x31D0, 0x082E, 0xFA98, 0xEC4E, 0x6C89}; + + int ret = 0; + for (int bit=0; bit<16; bit++) + if (n & (1< mru(MAX_SIZE); + for (int n=0; n<10*MAX_SIZE; n++) + { + mru.insert(permute(n)); + + set tester; + for (int m=max(0,n-MAX_SIZE+1); m<=n; m++) + tester.insert(permute(m)); + + BOOST_CHECK(mru == tester); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp new file mode 100644 index 0000000..9ef932b --- /dev/null +++ b/src/test/multisig_tests.cpp @@ -0,0 +1,296 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "keystore.h" +#include "main.h" +#include "script.h" +#include "wallet.h" + +using namespace std; +using namespace boost::assign; + +typedef vector valtype; + +extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); + +BOOST_AUTO_TEST_SUITE(multisig_tests) + +CScript +sign_multisig(CScript scriptPubKey, vector keys, CTransaction transaction, int whichIn) +{ + uint256 hash = SignatureHash(scriptPubKey, transaction, whichIn, SIGHASH_ALL); + + CScript result; + result << OP_0; // CHECKMULTISIG bug workaround + BOOST_FOREACH(const CKey &key, keys) + { + vector vchSig; + BOOST_CHECK(key.Sign(hash, vchSig)); + vchSig.push_back((unsigned char)SIGHASH_ALL); + result << vchSig; + } + return result; +} + +BOOST_AUTO_TEST_CASE(multisig_verify) +{ + unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC; + + CKey key[4]; + for (int i = 0; i < 4; i++) + key[i].MakeNewKey(true); + + CScript a_and_b; + a_and_b << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; + + CScript a_or_b; + a_or_b << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; + + CScript escrow; + escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; + + CTransaction txFrom; // Funding transaction + txFrom.vout.resize(3); + txFrom.vout[0].scriptPubKey = a_and_b; + txFrom.vout[1].scriptPubKey = a_or_b; + txFrom.vout[2].scriptPubKey = escrow; + + CTransaction txTo[3]; // Spending transaction + for (int i = 0; i < 3; i++) + { + txTo[i].vin.resize(1); + txTo[i].vout.resize(1); + txTo[i].vin[0].prevout.n = i; + txTo[i].vin[0].prevout.hash = txFrom.GetHash(); + txTo[i].vout[0].nValue = 1; + } + + vector keys; + CScript s; + + // Test a AND b: + keys.clear(); + keys += key[0],key[1]; // magic operator+= from boost.assign + s = sign_multisig(a_and_b, keys, txTo[0], 0); + BOOST_CHECK(VerifyScript(s, a_and_b, txTo[0], 0, flags, 0)); + + for (int i = 0; i < 4; i++) + { + keys.clear(); + keys += key[i]; + s = sign_multisig(a_and_b, keys, txTo[0], 0); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, flags, 0), strprintf("a&b 1: %d", i)); + + keys.clear(); + keys += key[1],key[i]; + s = sign_multisig(a_and_b, keys, txTo[0], 0); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, flags, 0), strprintf("a&b 2: %d", i)); + } + + // Test a OR b: + for (int i = 0; i < 4; i++) + { + keys.clear(); + keys += key[i]; + s = sign_multisig(a_or_b, keys, txTo[1], 0); + if (i == 0 || i == 1) + BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, txTo[1], 0, flags, 0), strprintf("a|b: %d", i)); + else + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, txTo[1], 0, flags, 0), strprintf("a|b: %d", i)); + } + s.clear(); + s << OP_0 << OP_0; + BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, flags, 0)); + s.clear(); + s << OP_0 << OP_1; + BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, flags, 0)); + + + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + { + keys.clear(); + keys += key[i],key[j]; + s = sign_multisig(escrow, keys, txTo[2], 0); + if (i < j && i < 3 && j < 3) + BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, txTo[2], 0, flags, 0), strprintf("escrow 1: %d %d", i, j)); + else + BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, txTo[2], 0, flags, 0), strprintf("escrow 2: %d %d", i, j)); + } +} + +BOOST_AUTO_TEST_CASE(multisig_IsStandard) +{ + CKey key[4]; + for (int i = 0; i < 4; i++) + key[i].MakeNewKey(true); + + CScript a_and_b; + a_and_b << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; + BOOST_CHECK(::IsStandard(a_and_b)); + + CScript a_or_b; + a_or_b << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; + BOOST_CHECK(::IsStandard(a_or_b)); + + CScript escrow; + escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; + BOOST_CHECK(::IsStandard(escrow)); + + CScript one_of_four; + one_of_four << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << key[3].GetPubKey() << OP_4 << OP_CHECKMULTISIG; + BOOST_CHECK(!::IsStandard(one_of_four)); + + CScript malformed[6]; + malformed[0] << OP_3 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; + malformed[1] << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_3 << OP_CHECKMULTISIG; + malformed[2] << OP_0 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; + malformed[3] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_0 << OP_CHECKMULTISIG; + malformed[4] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_CHECKMULTISIG; + malformed[5] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey(); + + for (int i = 0; i < 6; i++) + BOOST_CHECK(!::IsStandard(malformed[i])); +} + +BOOST_AUTO_TEST_CASE(multisig_Solver1) +{ + // Tests Solver() that returns lists of keys that are + // required to satisfy a ScriptPubKey + // + // Also tests IsMine() and ExtractAddress() + // + // Note: ExtractAddress for the multisignature transactions + // always returns false for this release, even if you have + // one key that would satisfy an (a|b) or 2-of-3 keys needed + // to spend an escrow transaction. + // + CBasicKeyStore keystore, emptykeystore, partialkeystore; + CKey key[3]; + CTxDestination keyaddr[3]; + for (int i = 0; i < 3; i++) + { + key[i].MakeNewKey(true); + keystore.AddKey(key[i]); + keyaddr[i] = key[i].GetPubKey().GetID(); + } + partialkeystore.AddKey(key[0]); + + { + vector solutions; + txnouttype whichType; + CScript s; + s << key[0].GetPubKey() << OP_CHECKSIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK(solutions.size() == 1); + CTxDestination addr; + BOOST_CHECK(ExtractDestination(s, addr)); + BOOST_CHECK(addr == keyaddr[0]); + BOOST_CHECK(IsMine(keystore, s)); + BOOST_CHECK(!IsMine(emptykeystore, s)); + } + { + vector solutions; + txnouttype whichType; + CScript s; + s << OP_DUP << OP_HASH160 << key[0].GetPubKey().GetID() << OP_EQUALVERIFY << OP_CHECKSIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK(solutions.size() == 1); + CTxDestination addr; + BOOST_CHECK(ExtractDestination(s, addr)); + BOOST_CHECK(addr == keyaddr[0]); + BOOST_CHECK(IsMine(keystore, s)); + BOOST_CHECK(!IsMine(emptykeystore, s)); + } + { + vector solutions; + txnouttype whichType; + CScript s; + s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(solutions.size(), 4U); + CTxDestination addr; + BOOST_CHECK(!ExtractDestination(s, addr)); + BOOST_CHECK(IsMine(keystore, s)); + BOOST_CHECK(!IsMine(emptykeystore, s)); + BOOST_CHECK(!IsMine(partialkeystore, s)); + } + { + vector solutions; + txnouttype whichType; + CScript s; + s << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(solutions.size(), 4U); + vector addrs; + int nRequired; + BOOST_CHECK(ExtractDestinations(s, whichType, addrs, nRequired)); + BOOST_CHECK(addrs[0] == keyaddr[0]); + BOOST_CHECK(addrs[1] == keyaddr[1]); + BOOST_CHECK(nRequired == 1); + BOOST_CHECK(IsMine(keystore, s)); + BOOST_CHECK(!IsMine(emptykeystore, s)); + BOOST_CHECK(!IsMine(partialkeystore, s)); + } + { + vector solutions; + txnouttype whichType; + CScript s; + s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK(solutions.size() == 5); + } +} + +BOOST_AUTO_TEST_CASE(multisig_Sign) +{ + // Test SignSignature() (and therefore the version of Solver() that signs transactions) + CBasicKeyStore keystore; + CKey key[4]; + for (int i = 0; i < 4; i++) + { + key[i].MakeNewKey(true); + keystore.AddKey(key[i]); + } + + CScript a_and_b; + a_and_b << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; + + CScript a_or_b; + a_or_b << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; + + CScript escrow; + escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; + + CTransaction txFrom; // Funding transaction + txFrom.vout.resize(3); + txFrom.vout[0].scriptPubKey = a_and_b; + txFrom.vout[1].scriptPubKey = a_or_b; + txFrom.vout[2].scriptPubKey = escrow; + + CTransaction txTo[3]; // Spending transaction + for (int i = 0; i < 3; i++) + { + txTo[i].vin.resize(1); + txTo[i].vout.resize(1); + txTo[i].vin[0].prevout.n = i; + txTo[i].vin[0].prevout.hash = txFrom.GetHash(); + txTo[i].vout[0].nValue = 1; + } + + for (int i = 0; i < 3; i++) + { + BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i)); + } +} + + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp new file mode 100644 index 0000000..6ec1744 --- /dev/null +++ b/src/test/netbase_tests.cpp @@ -0,0 +1,102 @@ +#include + +#include +#include + +#include "netbase.h" + +using namespace std; + +BOOST_AUTO_TEST_SUITE(netbase_tests) + +BOOST_AUTO_TEST_CASE(netbase_networks) +{ + BOOST_CHECK(CNetAddr("127.0.0.1").GetNetwork() == NET_UNROUTABLE); + BOOST_CHECK(CNetAddr("::1").GetNetwork() == NET_UNROUTABLE); + BOOST_CHECK(CNetAddr("8.8.8.8").GetNetwork() == NET_IPV4); + BOOST_CHECK(CNetAddr("2001::8888").GetNetwork() == NET_IPV6); + BOOST_CHECK(CNetAddr("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetNetwork() == NET_TOR); +} + +BOOST_AUTO_TEST_CASE(netbase_properties) +{ + BOOST_CHECK(CNetAddr("127.0.0.1").IsIPv4()); + BOOST_CHECK(CNetAddr("::FFFF:192.168.1.1").IsIPv4()); + BOOST_CHECK(CNetAddr("::1").IsIPv6()); + BOOST_CHECK(CNetAddr("10.0.0.1").IsRFC1918()); + BOOST_CHECK(CNetAddr("192.168.1.1").IsRFC1918()); + BOOST_CHECK(CNetAddr("172.31.255.255").IsRFC1918()); + BOOST_CHECK(CNetAddr("2001:0DB8::").IsRFC3849()); + BOOST_CHECK(CNetAddr("169.254.1.1").IsRFC3927()); + BOOST_CHECK(CNetAddr("2002::1").IsRFC3964()); + BOOST_CHECK(CNetAddr("FC00::").IsRFC4193()); + BOOST_CHECK(CNetAddr("2001::2").IsRFC4380()); + BOOST_CHECK(CNetAddr("2001:10::").IsRFC4843()); + BOOST_CHECK(CNetAddr("FE80::").IsRFC4862()); + BOOST_CHECK(CNetAddr("64:FF9B::").IsRFC6052()); + BOOST_CHECK(CNetAddr("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").IsTor()); + BOOST_CHECK(CNetAddr("127.0.0.1").IsLocal()); + BOOST_CHECK(CNetAddr("::1").IsLocal()); + BOOST_CHECK(CNetAddr("8.8.8.8").IsRoutable()); + BOOST_CHECK(CNetAddr("2001::1").IsRoutable()); + BOOST_CHECK(CNetAddr("127.0.0.1").IsValid()); +} + +bool static TestSplitHost(string test, string host, int port) +{ + string hostOut; + int portOut = -1; + SplitHostPort(test, portOut, hostOut); + return hostOut == host && port == portOut; +} + +BOOST_AUTO_TEST_CASE(netbase_splithost) +{ + BOOST_CHECK(TestSplitHost("www.bitcoin.org", "www.bitcoin.org", -1)); + BOOST_CHECK(TestSplitHost("[www.bitcoin.org]", "www.bitcoin.org", -1)); + BOOST_CHECK(TestSplitHost("www.bitcoin.org:80", "www.bitcoin.org", 80)); + BOOST_CHECK(TestSplitHost("[www.bitcoin.org]:80", "www.bitcoin.org", 80)); + BOOST_CHECK(TestSplitHost("127.0.0.1", "127.0.0.1", -1)); + BOOST_CHECK(TestSplitHost("127.0.0.1:9333", "127.0.0.1", 9333)); + BOOST_CHECK(TestSplitHost("[127.0.0.1]", "127.0.0.1", -1)); + BOOST_CHECK(TestSplitHost("[127.0.0.1]:9333", "127.0.0.1", 9333)); + BOOST_CHECK(TestSplitHost("::ffff:127.0.0.1", "::ffff:127.0.0.1", -1)); + BOOST_CHECK(TestSplitHost("[::ffff:127.0.0.1]:9333", "::ffff:127.0.0.1", 9333)); + BOOST_CHECK(TestSplitHost("[::]:9333", "::", 9333)); + BOOST_CHECK(TestSplitHost("::9333", "::9333", -1)); + BOOST_CHECK(TestSplitHost(":9333", "", 9333)); + BOOST_CHECK(TestSplitHost("[]:9333", "", 9333)); + BOOST_CHECK(TestSplitHost("", "", -1)); +} + +bool static TestParse(string src, string canon) +{ + CService addr; + if (!LookupNumeric(src.c_str(), addr, 65535)) + return canon == ""; + return canon == addr.ToString(); +} + +BOOST_AUTO_TEST_CASE(netbase_lookupnumeric) +{ + BOOST_CHECK(TestParse("127.0.0.1", "127.0.0.1:65535")); + BOOST_CHECK(TestParse("127.0.0.1:9333", "127.0.0.1:9333")); + BOOST_CHECK(TestParse("::ffff:127.0.0.1", "127.0.0.1:65535")); + BOOST_CHECK(TestParse("::", "[::]:65535")); + BOOST_CHECK(TestParse("[::]:9333", "[::]:9333")); + BOOST_CHECK(TestParse("[127.0.0.1]", "127.0.0.1:65535")); + BOOST_CHECK(TestParse(":::", "")); +} + +BOOST_AUTO_TEST_CASE(onioncat_test) +{ + // values from http://www.cypherpunk.at/onioncat/wiki/OnionCat + CNetAddr addr1("5wyqrzbvrdsumnok.onion"); + CNetAddr addr2("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca"); + BOOST_CHECK(addr1 == addr2); + BOOST_CHECK(addr1.IsTor()); + BOOST_CHECK(addr1.ToStringIP() == "5wyqrzbvrdsumnok.onion"); + BOOST_CHECK(addr1.IsRoutable()); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp new file mode 100644 index 0000000..cf09421 --- /dev/null +++ b/src/test/pmt_tests.cpp @@ -0,0 +1,98 @@ +#include + +#include "uint256.h" +#include "main.h" + +using namespace std; + +class CPartialMerkleTreeTester : public CPartialMerkleTree +{ +public: + // flip one bit in one of the hashes - this should break the authentication + void Damage() { + unsigned int n = rand() % vHash.size(); + int bit = rand() % 256; + uint256 &hash = vHash[n]; + hash ^= ((uint256)1 << bit); + } +}; + +BOOST_AUTO_TEST_SUITE(pmt_tests) + +BOOST_AUTO_TEST_CASE(pmt_test1) +{ + static const unsigned int nTxCounts[] = {1, 4, 7, 17, 56, 100, 127, 256, 312, 513, 1000, 4095}; + + for (int n = 0; n < 12; n++) { + unsigned int nTx = nTxCounts[n]; + + // build a block with some dummy transactions + CBlock block; + for (unsigned int j=0; j vTxid(nTx, 0); + for (unsigned int j=0; j 1) { + nTx_ = (nTx_+1)/2; + nHeight++; + } + + // check with random subsets with inclusion chances 1, 1/2, 1/4, ..., 1/128 + for (int att = 1; att < 15; att++) { + // build random subset of txid's + std::vector vMatch(nTx, false); + std::vector vMatchTxid1; + for (unsigned int j=0; j(nTx, 1 + vMatchTxid1.size()*nHeight); + BOOST_CHECK(ss.size() <= 10 + (258*n+7)/8); + + // deserialize into a tester copy + CPartialMerkleTreeTester pmt2; + ss >> pmt2; + + // extract merkle root and matched txids from copy + std::vector vMatchTxid2; + uint256 merkleRoot2 = pmt2.ExtractMatches(vMatchTxid2); + + // check that it has the same merkle root as the original, and a valid one + BOOST_CHECK(merkleRoot1 == merkleRoot2); + BOOST_CHECK(merkleRoot2 != 0); + + // check that it contains the matched transactions (in the same order!) + BOOST_CHECK(vMatchTxid1 == vMatchTxid2); + + // check that random bit flips break the authentication + for (int j=0; j<4; j++) { + CPartialMerkleTreeTester pmt3(pmt2); + pmt3.Damage(); + std::vector vMatchTxid3; + uint256 merkleRoot3 = pmt3.ExtractMatches(vMatchTxid3); + BOOST_CHECK(merkleRoot3 != merkleRoot1); + } + } + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp new file mode 100644 index 0000000..baeea42 --- /dev/null +++ b/src/test/rpc_tests.cpp @@ -0,0 +1,171 @@ +#include +#include +#include + +#include "base58.h" +#include "util.h" +#include "bitcoinrpc.h" + +using namespace std; +using namespace json_spirit; + +BOOST_AUTO_TEST_SUITE(rpc_tests) + +static Array +createArgs(int nRequired, const char* address1=NULL, const char* address2=NULL) +{ + Array result; + result.push_back(nRequired); + Array addresses; + if (address1) addresses.push_back(address1); + if (address2) addresses.push_back(address2); + result.push_back(addresses); + return result; +} + +BOOST_AUTO_TEST_CASE(rpc_addmultisig) +{ + rpcfn_type addmultisig = tableRPC["addmultisigaddress"]->actor; + + // old, 65-byte-long: + const char address1Hex[] = "0434e3e09f49ea168c5bbf53f877ff4206923858aab7c7e1df25bc263978107c95e35065a27ef6f1b27222db0ec97e0e895eaca603d3ee0d4c060ce3d8a00286c8"; + // new, compressed: + const char address2Hex[] = "0388c2037017c62240b6b72ac1a2a5f94da790596ebd06177c8572752922165cb4"; + + Value v; + CBitcoinAddress address; + BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(1, address1Hex), false)); + address.SetString(v.get_str()); + BOOST_CHECK(address.IsValid() && address.IsScript()); + + BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(1, address1Hex, address2Hex), false)); + address.SetString(v.get_str()); + BOOST_CHECK(address.IsValid() && address.IsScript()); + + BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(2, address1Hex, address2Hex), false)); + address.SetString(v.get_str()); + BOOST_CHECK(address.IsValid() && address.IsScript()); + + BOOST_CHECK_THROW(addmultisig(createArgs(0), false), runtime_error); + BOOST_CHECK_THROW(addmultisig(createArgs(1), false), runtime_error); + BOOST_CHECK_THROW(addmultisig(createArgs(2, address1Hex), false), runtime_error); + + BOOST_CHECK_THROW(addmultisig(createArgs(1, ""), false), runtime_error); + BOOST_CHECK_THROW(addmultisig(createArgs(1, "NotAValidPubkey"), false), runtime_error); + + string short1(address1Hex, address1Hex+sizeof(address1Hex)-2); // last byte missing + BOOST_CHECK_THROW(addmultisig(createArgs(2, short1.c_str()), false), runtime_error); + + string short2(address1Hex+1, address1Hex+sizeof(address1Hex)); // first byte missing + BOOST_CHECK_THROW(addmultisig(createArgs(2, short2.c_str()), false), runtime_error); +} + +static Value CallRPC(string args) +{ + vector vArgs; + boost::split(vArgs, args, boost::is_any_of(" \t")); + string strMethod = vArgs[0]; + vArgs.erase(vArgs.begin()); + Array params = RPCConvertValues(strMethod, vArgs); + + rpcfn_type method = tableRPC[strMethod]->actor; + try { + Value result = (*method)(params, false); + return result; + } + catch (Object& objError) + { + throw runtime_error(find_value(objError, "message").get_str()); + } +} + +BOOST_AUTO_TEST_CASE(rpc_wallet) +{ + // Test RPC calls for various wallet statistics + Value r; + + BOOST_CHECK_NO_THROW(CallRPC("listunspent")); + BOOST_CHECK_THROW(CallRPC("listunspent string"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listunspent 0 string"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listunspent 0 1 not_array"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listunspent 0 1 [] extra"), runtime_error); + BOOST_CHECK_NO_THROW(r=CallRPC("listunspent 0 1 []")); + BOOST_CHECK(r.get_array().empty()); + + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress")); + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress 0")); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress not_int"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress 0 not_bool"), runtime_error); + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress 0 true")); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress 0 true extra"), runtime_error); + + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount")); + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount 0")); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount not_int"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount 0 not_bool"), runtime_error); + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount 0 true")); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount 0 true extra"), runtime_error); +} + + +BOOST_AUTO_TEST_CASE(rpc_rawparams) +{ + // Test raw transaction API argument handling + Value r; + + BOOST_CHECK_THROW(CallRPC("getrawtransaction"), runtime_error); + BOOST_CHECK_THROW(CallRPC("getrawtransaction not_hex"), runtime_error); + BOOST_CHECK_THROW(CallRPC("getrawtransaction a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed not_int"), runtime_error); + + BOOST_CHECK_THROW(CallRPC("createrawtransaction"), runtime_error); + BOOST_CHECK_THROW(CallRPC("createrawtransaction null null"), runtime_error); + BOOST_CHECK_THROW(CallRPC("createrawtransaction not_array"), runtime_error); + BOOST_CHECK_THROW(CallRPC("createrawtransaction [] []"), runtime_error); + BOOST_CHECK_THROW(CallRPC("createrawtransaction {} {}"), runtime_error); + BOOST_CHECK_NO_THROW(CallRPC("createrawtransaction [] {}")); + BOOST_CHECK_THROW(CallRPC("createrawtransaction [] {} extra"), runtime_error); + + BOOST_CHECK_THROW(CallRPC("decoderawtransaction"), runtime_error); + BOOST_CHECK_THROW(CallRPC("decoderawtransaction null"), runtime_error); + BOOST_CHECK_THROW(CallRPC("decoderawtransaction DEADBEEF"), runtime_error); + string rawtx = "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000"; + BOOST_CHECK_NO_THROW(r = CallRPC(string("decoderawtransaction ")+rawtx)); + BOOST_CHECK_EQUAL(find_value(r.get_obj(), "version").get_int(), 1); + BOOST_CHECK_EQUAL(find_value(r.get_obj(), "locktime").get_int(), 0); + BOOST_CHECK_THROW(r = CallRPC(string("decoderawtransaction ")+rawtx+" extra"), runtime_error); + + BOOST_CHECK_THROW(CallRPC("signrawtransaction"), runtime_error); + BOOST_CHECK_THROW(CallRPC("signrawtransaction null"), runtime_error); + BOOST_CHECK_THROW(CallRPC("signrawtransaction ff00"), runtime_error); + BOOST_CHECK_NO_THROW(CallRPC(string("signrawtransaction ")+rawtx)); + BOOST_CHECK_NO_THROW(CallRPC(string("signrawtransaction ")+rawtx+" null null NONE|ANYONECANPAY")); + BOOST_CHECK_NO_THROW(CallRPC(string("signrawtransaction ")+rawtx+" [] [] NONE|ANYONECANPAY")); + BOOST_CHECK_THROW(CallRPC(string("signrawtransaction ")+rawtx+" null null badenum"), runtime_error); + + // Only check failure cases for sendrawtransaction, there's no network to send to... + BOOST_CHECK_THROW(CallRPC("sendrawtransaction"), runtime_error); + BOOST_CHECK_THROW(CallRPC("sendrawtransaction null"), runtime_error); + BOOST_CHECK_THROW(CallRPC("sendrawtransaction DEADBEEF"), runtime_error); + BOOST_CHECK_THROW(CallRPC(string("sendrawtransaction ")+rawtx+" extra"), runtime_error); +} + +BOOST_AUTO_TEST_CASE(rpc_rawsign) +{ + Value r; + // input is a 1-of-2 multisig (so is output): + string prevout = + "[{\"txid\":\"b4cc287e58f87cdae59417329f710f3ecd75a4ee1d2872b7248f50977c8493f3\"," + "\"vout\":1,\"scriptPubKey\":\"a914b10c9df5f7edf436c697f02f1efdba4cf399615187\"," + "\"redeemScript\":\"512103debedc17b3df2badbcdd86d5feb4562b86fe182e5998abd8bcd4f122c6155b1b21027e940bb73ab8732bfdf7f9216ecefca5b94d6df834e77e108f68e66f126044c052ae\"}]"; + r = CallRPC(string("createrawtransaction ")+prevout+" "+ + "{\"3HqAe9LtNBjnsfM4CyYaWTnvCaUYT7v4oZ\":11}"); + string notsigned = r.get_str(); + string privkey1 = "\"T6hoRM7L8u4f9vHd4eGMAmwV6AMCE11PvYi7YjrdegG223kw64r1\""; + string privkey2 = "\"T5Xu6pe5iqQYqXGxhcY2QEFr7NNoVQ5R6A4abpswunCTF9w85g8V\""; + r = CallRPC(string("signrawtransaction ")+notsigned+" "+prevout+" "+"[]"); + BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == false); + r = CallRPC(string("signrawtransaction ")+notsigned+" "+prevout+" "+"["+privkey1+","+privkey2+"]"); + BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == true); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp new file mode 100644 index 0000000..65f0ad0 --- /dev/null +++ b/src/test/script_P2SH_tests.cpp @@ -0,0 +1,339 @@ +#include +#include +#include +#include +#include +#include + +#include "../main.h" +#include "../script.h" +#include "../wallet.h" + +using namespace std; + +// Test routines internal to script.cpp: +extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); + +// Helpers: +static std::vector +Serialize(const CScript& s) +{ + std::vector sSerialized(s); + return sSerialized; +} + +static bool +Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict) +{ + // Create dummy to/from transactions: + CTransaction txFrom; + txFrom.vout.resize(1); + txFrom.vout[0].scriptPubKey = scriptPubKey; + + CTransaction txTo; + txTo.vin.resize(1); + txTo.vout.resize(1); + txTo.vin[0].prevout.n = 0; + txTo.vin[0].prevout.hash = txFrom.GetHash(); + txTo.vin[0].scriptSig = scriptSig; + txTo.vout[0].nValue = 1; + + return VerifyScript(scriptSig, scriptPubKey, txTo, 0, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, 0); +} + + +BOOST_AUTO_TEST_SUITE(script_P2SH_tests) + +BOOST_AUTO_TEST_CASE(sign) +{ + // Pay-to-script-hash looks like this: + // scriptSig: + // scriptPubKey: HASH160 EQUAL + + // Test SignSignature() (and therefore the version of Solver() that signs transactions) + CBasicKeyStore keystore; + CKey key[4]; + for (int i = 0; i < 4; i++) + { + key[i].MakeNewKey(true); + keystore.AddKey(key[i]); + } + + // 8 Scripts: checking all combinations of + // different keys, straight/P2SH, pubkey/pubkeyhash + CScript standardScripts[4]; + standardScripts[0] << key[0].GetPubKey() << OP_CHECKSIG; + standardScripts[1].SetDestination(key[1].GetPubKey().GetID()); + standardScripts[2] << key[1].GetPubKey() << OP_CHECKSIG; + standardScripts[3].SetDestination(key[2].GetPubKey().GetID()); + CScript evalScripts[4]; + for (int i = 0; i < 4; i++) + { + keystore.AddCScript(standardScripts[i]); + evalScripts[i].SetDestination(standardScripts[i].GetID()); + } + + CTransaction txFrom; // Funding transaction: + txFrom.vout.resize(8); + for (int i = 0; i < 4; i++) + { + txFrom.vout[i].scriptPubKey = evalScripts[i]; + txFrom.vout[i].nValue = COIN; + txFrom.vout[i+4].scriptPubKey = standardScripts[i]; + txFrom.vout[i+4].nValue = COIN; + } + BOOST_CHECK(txFrom.IsStandard()); + + CTransaction txTo[8]; // Spending transactions + for (int i = 0; i < 8; i++) + { + txTo[i].vin.resize(1); + txTo[i].vout.resize(1); + txTo[i].vin[0].prevout.n = i; + txTo[i].vin[0].prevout.hash = txFrom.GetHash(); + txTo[i].vout[0].nValue = 1; + BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", i)); + } + for (int i = 0; i < 8; i++) + { + BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i)); + } + // All of the above should be OK, and the txTos have valid signatures + // Check to make sure signature verification fails if we use the wrong ScriptSig: + for (int i = 0; i < 8; i++) + for (int j = 0; j < 8; j++) + { + CScript sigSave = txTo[i].vin[0].scriptSig; + txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig; + bool sigOK = VerifySignature(CCoins(txFrom, 0), txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, 0); + if (i == j) + BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j)); + else + BOOST_CHECK_MESSAGE(!sigOK, strprintf("VerifySignature %d %d", i, j)); + txTo[i].vin[0].scriptSig = sigSave; + } +} + +BOOST_AUTO_TEST_CASE(norecurse) +{ + // Make sure only the outer pay-to-script-hash does the + // extra-validation thing: + CScript invalidAsScript; + invalidAsScript << OP_INVALIDOPCODE << OP_INVALIDOPCODE; + + CScript p2sh; + p2sh.SetDestination(invalidAsScript.GetID()); + + CScript scriptSig; + scriptSig << Serialize(invalidAsScript); + + // Should not verify, because it will try to execute OP_INVALIDOPCODE + BOOST_CHECK(!Verify(scriptSig, p2sh, true)); + + // Try to recur, and verification should succeed because + // the inner HASH160 <> EQUAL should only check the hash: + CScript p2sh2; + p2sh2.SetDestination(p2sh.GetID()); + CScript scriptSig2; + scriptSig2 << Serialize(invalidAsScript) << Serialize(p2sh); + + BOOST_CHECK(Verify(scriptSig2, p2sh2, true)); +} + +BOOST_AUTO_TEST_CASE(set) +{ + // Test the CScript::Set* methods + CBasicKeyStore keystore; + CKey key[4]; + std::vector keys; + for (int i = 0; i < 4; i++) + { + key[i].MakeNewKey(true); + keystore.AddKey(key[i]); + keys.push_back(key[i].GetPubKey()); + } + + CScript inner[4]; + inner[0].SetDestination(key[0].GetPubKey().GetID()); + inner[1].SetMultisig(2, std::vector(keys.begin(), keys.begin()+2)); + inner[2].SetMultisig(1, std::vector(keys.begin(), keys.begin()+2)); + inner[3].SetMultisig(2, std::vector(keys.begin(), keys.begin()+3)); + + CScript outer[4]; + for (int i = 0; i < 4; i++) + { + outer[i].SetDestination(inner[i].GetID()); + keystore.AddCScript(inner[i]); + } + + CTransaction txFrom; // Funding transaction: + txFrom.vout.resize(4); + for (int i = 0; i < 4; i++) + { + txFrom.vout[i].scriptPubKey = outer[i]; + txFrom.vout[i].nValue = CENT; + } + BOOST_CHECK(txFrom.IsStandard()); + + CTransaction txTo[4]; // Spending transactions + for (int i = 0; i < 4; i++) + { + txTo[i].vin.resize(1); + txTo[i].vout.resize(1); + txTo[i].vin[0].prevout.n = i; + txTo[i].vin[0].prevout.hash = txFrom.GetHash(); + txTo[i].vout[0].nValue = 1*CENT; + txTo[i].vout[0].scriptPubKey = inner[i]; + BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", i)); + } + for (int i = 0; i < 4; i++) + { + BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i)); + BOOST_CHECK_MESSAGE(txTo[i].IsStandard(), strprintf("txTo[%d].IsStandard", i)); + } +} + +BOOST_AUTO_TEST_CASE(is) +{ + // Test CScript::IsPayToScriptHash() + uint160 dummy; + CScript p2sh; + p2sh << OP_HASH160 << dummy << OP_EQUAL; + BOOST_CHECK(p2sh.IsPayToScriptHash()); + + // Not considered pay-to-script-hash if using one of the OP_PUSHDATA opcodes: + static const unsigned char direct[] = { OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUAL }; + BOOST_CHECK(CScript(direct, direct+sizeof(direct)).IsPayToScriptHash()); + static const unsigned char pushdata1[] = { OP_HASH160, OP_PUSHDATA1, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUAL }; + BOOST_CHECK(!CScript(pushdata1, pushdata1+sizeof(pushdata1)).IsPayToScriptHash()); + static const unsigned char pushdata2[] = { OP_HASH160, OP_PUSHDATA2, 20,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUAL }; + BOOST_CHECK(!CScript(pushdata2, pushdata2+sizeof(pushdata2)).IsPayToScriptHash()); + static const unsigned char pushdata4[] = { OP_HASH160, OP_PUSHDATA4, 20,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUAL }; + BOOST_CHECK(!CScript(pushdata4, pushdata4+sizeof(pushdata4)).IsPayToScriptHash()); + + CScript not_p2sh; + BOOST_CHECK(!not_p2sh.IsPayToScriptHash()); + + not_p2sh.clear(); not_p2sh << OP_HASH160 << dummy << dummy << OP_EQUAL; + BOOST_CHECK(!not_p2sh.IsPayToScriptHash()); + + not_p2sh.clear(); not_p2sh << OP_NOP << dummy << OP_EQUAL; + BOOST_CHECK(!not_p2sh.IsPayToScriptHash()); + + not_p2sh.clear(); not_p2sh << OP_HASH160 << dummy << OP_CHECKSIG; + BOOST_CHECK(!not_p2sh.IsPayToScriptHash()); +} + +BOOST_AUTO_TEST_CASE(switchover) +{ + // Test switch over code + CScript notValid; + notValid << OP_11 << OP_12 << OP_EQUALVERIFY; + CScript scriptSig; + scriptSig << Serialize(notValid); + + CScript fund; + fund.SetDestination(notValid.GetID()); + + + // Validation should succeed under old rules (hash is correct): + BOOST_CHECK(Verify(scriptSig, fund, false)); + // Fail under new: + BOOST_CHECK(!Verify(scriptSig, fund, true)); +} + +BOOST_AUTO_TEST_CASE(AreInputsStandard) +{ + CCoinsView coinsDummy; + CCoinsViewCache coins(coinsDummy); + CBasicKeyStore keystore; + CKey key[3]; + vector keys; + for (int i = 0; i < 3; i++) + { + key[i].MakeNewKey(true); + keystore.AddKey(key[i]); + keys.push_back(key[i].GetPubKey()); + } + + CTransaction txFrom; + txFrom.vout.resize(6); + + // First three are standard: + CScript pay1; pay1.SetDestination(key[0].GetPubKey().GetID()); + keystore.AddCScript(pay1); + CScript payScriptHash1; payScriptHash1.SetDestination(pay1.GetID()); + CScript pay1of3; pay1of3.SetMultisig(1, keys); + + txFrom.vout[0].scriptPubKey = payScriptHash1; + txFrom.vout[0].nValue = 1000; + txFrom.vout[1].scriptPubKey = pay1; + txFrom.vout[1].nValue = 2000; + txFrom.vout[2].scriptPubKey = pay1of3; + txFrom.vout[2].nValue = 3000; + + // Last three non-standard: + CScript empty; + keystore.AddCScript(empty); + txFrom.vout[3].scriptPubKey = empty; + txFrom.vout[3].nValue = 4000; + // Can't use SetPayToScriptHash, it checks for the empty Script. So: + txFrom.vout[4].scriptPubKey << OP_HASH160 << Hash160(empty) << OP_EQUAL; + txFrom.vout[4].nValue = 5000; + CScript oneOfEleven; + oneOfEleven << OP_1; + for (int i = 0; i < 11; i++) + oneOfEleven << key[0].GetPubKey(); + oneOfEleven << OP_11 << OP_CHECKMULTISIG; + txFrom.vout[5].scriptPubKey.SetDestination(oneOfEleven.GetID()); + txFrom.vout[5].nValue = 6000; + + coins.SetCoins(txFrom.GetHash(), CCoins(txFrom, 0)); + + CTransaction txTo; + txTo.vout.resize(1); + txTo.vout[0].scriptPubKey.SetDestination(key[1].GetPubKey().GetID()); + + txTo.vin.resize(3); + txTo.vin[0].prevout.n = 0; + txTo.vin[0].prevout.hash = txFrom.GetHash(); + BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 0)); + txTo.vin[1].prevout.n = 1; + txTo.vin[1].prevout.hash = txFrom.GetHash(); + BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 1)); + txTo.vin[2].prevout.n = 2; + txTo.vin[2].prevout.hash = txFrom.GetHash(); + BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 2)); + + BOOST_CHECK(txTo.AreInputsStandard(coins)); + BOOST_CHECK_EQUAL(txTo.GetP2SHSigOpCount(coins), 1U); + + // Make sure adding crap to the scriptSigs makes them non-standard: + for (int i = 0; i < 3; i++) + { + CScript t = txTo.vin[i].scriptSig; + txTo.vin[i].scriptSig = (CScript() << 11) + t; + BOOST_CHECK(!txTo.AreInputsStandard(coins)); + txTo.vin[i].scriptSig = t; + } + + CTransaction txToNonStd; + txToNonStd.vout.resize(1); + txToNonStd.vout[0].scriptPubKey.SetDestination(key[1].GetPubKey().GetID()); + txToNonStd.vout[0].nValue = 1000; + txToNonStd.vin.resize(2); + txToNonStd.vin[0].prevout.n = 4; + txToNonStd.vin[0].prevout.hash = txFrom.GetHash(); + txToNonStd.vin[0].scriptSig << Serialize(empty); + txToNonStd.vin[1].prevout.n = 5; + txToNonStd.vin[1].prevout.hash = txFrom.GetHash(); + txToNonStd.vin[1].scriptSig << OP_0 << Serialize(oneOfEleven); + + BOOST_CHECK(!txToNonStd.AreInputsStandard(coins)); + BOOST_CHECK_EQUAL(txToNonStd.GetP2SHSigOpCount(coins), 11U); + + txToNonStd.vin[0].scriptSig.clear(); + BOOST_CHECK(!txToNonStd.AreInputsStandard(coins)); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp new file mode 100644 index 0000000..e7ad526 --- /dev/null +++ b/src/test/script_tests.cpp @@ -0,0 +1,447 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "json/json_spirit_reader_template.h" +#include "json/json_spirit_writer_template.h" +#include "json/json_spirit_utils.h" + +#include "main.h" +#include "wallet.h" + +using namespace std; +using namespace json_spirit; +using namespace boost::algorithm; + +extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); + +static const unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC; + +CScript +ParseScript(string s) +{ + CScript result; + + static map mapOpNames; + + if (mapOpNames.size() == 0) + { + for (int op = OP_NOP; op <= OP_NOP10; op++) + { + const char* name = GetOpName((opcodetype)op); + if (strcmp(name, "OP_UNKNOWN") == 0) + continue; + string strName(name); + mapOpNames[strName] = (opcodetype)op; + // Convenience: OP_ADD and just ADD are both recognized: + replace_first(strName, "OP_", ""); + mapOpNames[strName] = (opcodetype)op; + } + } + + vector words; + split(words, s, is_any_of(" \t\n"), token_compress_on); + + BOOST_FOREACH(string w, words) + { + if (all(w, is_digit()) || + (starts_with(w, "-") && all(string(w.begin()+1, w.end()), is_digit()))) + { + // Number + int64 n = atoi64(w); + result << n; + } + else if (starts_with(w, "0x") && IsHex(string(w.begin()+2, w.end()))) + { + // Raw hex data, inserted NOT pushed onto stack: + std::vector raw = ParseHex(string(w.begin()+2, w.end())); + result.insert(result.end(), raw.begin(), raw.end()); + } + else if (w.size() >= 2 && starts_with(w, "'") && ends_with(w, "'")) + { + // Single-quoted string, pushed as data. NOTE: this is poor-man's + // parsing, spaces/tabs/newlines in single-quoted strings won't work. + std::vector value(w.begin()+1, w.end()-1); + result << value; + } + else if (mapOpNames.count(w)) + { + // opcode, e.g. OP_ADD or OP_1: + result << mapOpNames[w]; + } + else + { + BOOST_ERROR("Parse error: " << s); + return CScript(); + } + } + + return result; +} + +Array +read_json(const std::string& filename) +{ + namespace fs = boost::filesystem; + fs::path testFile = fs::current_path() / "test" / "data" / filename; + +#ifdef TEST_DATA_DIR + if (!fs::exists(testFile)) + { + testFile = fs::path(BOOST_PP_STRINGIZE(TEST_DATA_DIR)) / filename; + } +#endif + + ifstream ifs(testFile.string().c_str(), ifstream::in); + Value v; + if (!read_stream(ifs, v)) + { + if (ifs.fail()) + BOOST_ERROR("Cound not find/open " << filename); + else + BOOST_ERROR("JSON syntax error in " << filename); + return Array(); + } + if (v.type() != array_type) + { + BOOST_ERROR(filename << " does not contain a json array"); + return Array(); + } + + return v.get_array(); +} + +BOOST_AUTO_TEST_SUITE(script_tests) + +BOOST_AUTO_TEST_CASE(script_valid) +{ + // Read tests from test/data/script_valid.json + // Format is an array of arrays + // Inner arrays are [ "scriptSig", "scriptPubKey" ] + // ... where scriptSig and scriptPubKey are stringified + // scripts. + Array tests = read_json("script_valid.json"); + + BOOST_FOREACH(Value& tv, tests) + { + Array test = tv.get_array(); + string strTest = write_string(tv, false); + if (test.size() < 2) // Allow size > 2; extra stuff ignored (useful for comments) + { + BOOST_ERROR("Bad test: " << strTest); + continue; + } + string scriptSigString = test[0].get_str(); + CScript scriptSig = ParseScript(scriptSigString); + string scriptPubKeyString = test[1].get_str(); + CScript scriptPubKey = ParseScript(scriptPubKeyString); + + CTransaction tx; + BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, tx, 0, flags, SIGHASH_NONE), strTest); + } +} + +BOOST_AUTO_TEST_CASE(script_invalid) +{ + // Scripts that should evaluate as invalid + Array tests = read_json("script_invalid.json"); + + BOOST_FOREACH(Value& tv, tests) + { + Array test = tv.get_array(); + string strTest = write_string(tv, false); + if (test.size() < 2) // Allow size > 2; extra stuff ignored (useful for comments) + { + BOOST_ERROR("Bad test: " << strTest); + continue; + } + string scriptSigString = test[0].get_str(); + CScript scriptSig = ParseScript(scriptSigString); + string scriptPubKeyString = test[1].get_str(); + CScript scriptPubKey = ParseScript(scriptPubKeyString); + + CTransaction tx; + BOOST_CHECK_MESSAGE(!VerifyScript(scriptSig, scriptPubKey, tx, 0, flags, SIGHASH_NONE), strTest); + } +} + +BOOST_AUTO_TEST_CASE(script_PushData) +{ + // Check that PUSHDATA1, PUSHDATA2, and PUSHDATA4 create the same value on + // the stack as the 1-75 opcodes do. + static const unsigned char direct[] = { 1, 0x5a }; + static const unsigned char pushdata1[] = { OP_PUSHDATA1, 1, 0x5a }; + static const unsigned char pushdata2[] = { OP_PUSHDATA2, 1, 0, 0x5a }; + static const unsigned char pushdata4[] = { OP_PUSHDATA4, 1, 0, 0, 0, 0x5a }; + + vector > directStack; + BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), CTransaction(), 0, true, 0)); + + vector > pushdata1Stack; + BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), CTransaction(), 0, true, 0)); + BOOST_CHECK(pushdata1Stack == directStack); + + vector > pushdata2Stack; + BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), CTransaction(), 0, true, 0)); + BOOST_CHECK(pushdata2Stack == directStack); + + vector > pushdata4Stack; + BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), CTransaction(), 0, true, 0)); + BOOST_CHECK(pushdata4Stack == directStack); +} + +CScript +sign_multisig(CScript scriptPubKey, std::vector keys, CTransaction transaction) +{ + uint256 hash = SignatureHash(scriptPubKey, transaction, 0, SIGHASH_ALL); + + CScript result; + // + // NOTE: CHECKMULTISIG has an unfortunate bug; it requires + // one extra item on the stack, before the signatures. + // Putting OP_0 on the stack is the workaround; + // fixing the bug would mean splitting the block chain (old + // clients would not accept new CHECKMULTISIG transactions, + // and vice-versa) + // + result << OP_0; + BOOST_FOREACH(const CKey &key, keys) + { + vector vchSig; + BOOST_CHECK(key.Sign(hash, vchSig)); + vchSig.push_back((unsigned char)SIGHASH_ALL); + result << vchSig; + } + return result; +} +CScript +sign_multisig(CScript scriptPubKey, const CKey &key, CTransaction transaction) +{ + std::vector keys; + keys.push_back(key); + return sign_multisig(scriptPubKey, keys, transaction); +} + +BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12) +{ + CKey key1, key2, key3; + key1.MakeNewKey(true); + key2.MakeNewKey(false); + key3.MakeNewKey(true); + + CScript scriptPubKey12; + scriptPubKey12 << OP_1 << key1.GetPubKey() << key2.GetPubKey() << OP_2 << OP_CHECKMULTISIG; + + CTransaction txFrom12; + txFrom12.vout.resize(1); + txFrom12.vout[0].scriptPubKey = scriptPubKey12; + + CTransaction txTo12; + txTo12.vin.resize(1); + txTo12.vout.resize(1); + txTo12.vin[0].prevout.n = 0; + txTo12.vin[0].prevout.hash = txFrom12.GetHash(); + txTo12.vout[0].nValue = 1; + + CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, flags, 0)); + txTo12.vout[0].nValue = 2; + BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, flags, 0)); + + CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, flags, 0)); + + CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, flags, 0)); +} + +BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) +{ + CKey key1, key2, key3, key4; + key1.MakeNewKey(true); + key2.MakeNewKey(false); + key3.MakeNewKey(true); + key4.MakeNewKey(false); + + CScript scriptPubKey23; + scriptPubKey23 << OP_2 << key1.GetPubKey() << key2.GetPubKey() << key3.GetPubKey() << OP_3 << OP_CHECKMULTISIG; + + CTransaction txFrom23; + txFrom23.vout.resize(1); + txFrom23.vout[0].scriptPubKey = scriptPubKey23; + + CTransaction txTo23; + txTo23.vin.resize(1); + txTo23.vout.resize(1); + txTo23.vin[0].prevout.n = 0; + txTo23.vin[0].prevout.hash = txFrom23.GetHash(); + txTo23.vout[0].nValue = 1; + + std::vector keys; + keys.push_back(key1); keys.push_back(key2); + CScript goodsig1 = sign_multisig(scriptPubKey23, keys, txTo23); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, txTo23, 0, flags, 0)); + + keys.clear(); + keys.push_back(key1); keys.push_back(key3); + CScript goodsig2 = sign_multisig(scriptPubKey23, keys, txTo23); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, txTo23, 0, flags, 0)); + + keys.clear(); + keys.push_back(key2); keys.push_back(key3); + CScript goodsig3 = sign_multisig(scriptPubKey23, keys, txTo23); + BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, txTo23, 0, flags, 0)); + + keys.clear(); + keys.push_back(key2); keys.push_back(key2); // Can't re-use sig + CScript badsig1 = sign_multisig(scriptPubKey23, keys, txTo23); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, txTo23, 0, flags, 0)); + + keys.clear(); + keys.push_back(key2); keys.push_back(key1); // sigs must be in correct order + CScript badsig2 = sign_multisig(scriptPubKey23, keys, txTo23); + BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, txTo23, 0, flags, 0)); + + keys.clear(); + keys.push_back(key3); keys.push_back(key2); // sigs must be in correct order + CScript badsig3 = sign_multisig(scriptPubKey23, keys, txTo23); + BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, txTo23, 0, flags, 0)); + + keys.clear(); + keys.push_back(key4); keys.push_back(key2); // sigs must match pubkeys + CScript badsig4 = sign_multisig(scriptPubKey23, keys, txTo23); + BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, txTo23, 0, flags, 0)); + + keys.clear(); + keys.push_back(key1); keys.push_back(key4); // sigs must match pubkeys + CScript badsig5 = sign_multisig(scriptPubKey23, keys, txTo23); + BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, txTo23, 0, flags, 0)); + + keys.clear(); // Must have signatures + CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23); + BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, txTo23, 0, flags, 0)); +} + +BOOST_AUTO_TEST_CASE(script_combineSigs) +{ + // Test the CombineSignatures function + CBasicKeyStore keystore; + vector keys; + vector pubkeys; + for (int i = 0; i < 3; i++) + { + CKey key; + key.MakeNewKey(i%2 == 1); + keys.push_back(key); + pubkeys.push_back(key.GetPubKey()); + keystore.AddKey(key); + } + + CTransaction txFrom; + txFrom.vout.resize(1); + txFrom.vout[0].scriptPubKey.SetDestination(keys[0].GetPubKey().GetID()); + CScript& scriptPubKey = txFrom.vout[0].scriptPubKey; + CTransaction txTo; + txTo.vin.resize(1); + txTo.vout.resize(1); + txTo.vin[0].prevout.n = 0; + txTo.vin[0].prevout.hash = txFrom.GetHash(); + CScript& scriptSig = txTo.vin[0].scriptSig; + txTo.vout[0].nValue = 1; + + CScript empty; + CScript combined = CombineSignatures(scriptPubKey, txTo, 0, empty, empty); + BOOST_CHECK(combined.empty()); + + // Single signature case: + SignSignature(keystore, txFrom, txTo, 0); // changes scriptSig + combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSig, empty); + BOOST_CHECK(combined == scriptSig); + combined = CombineSignatures(scriptPubKey, txTo, 0, empty, scriptSig); + BOOST_CHECK(combined == scriptSig); + CScript scriptSigCopy = scriptSig; + // Signing again will give a different, valid signature: + SignSignature(keystore, txFrom, txTo, 0); + combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSigCopy, scriptSig); + BOOST_CHECK(combined == scriptSigCopy || combined == scriptSig); + + // P2SH, single-signature case: + CScript pkSingle; pkSingle << keys[0].GetPubKey() << OP_CHECKSIG; + keystore.AddCScript(pkSingle); + scriptPubKey.SetDestination(pkSingle.GetID()); + SignSignature(keystore, txFrom, txTo, 0); + combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSig, empty); + BOOST_CHECK(combined == scriptSig); + combined = CombineSignatures(scriptPubKey, txTo, 0, empty, scriptSig); + BOOST_CHECK(combined == scriptSig); + scriptSigCopy = scriptSig; + SignSignature(keystore, txFrom, txTo, 0); + combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSigCopy, scriptSig); + BOOST_CHECK(combined == scriptSigCopy || combined == scriptSig); + // dummy scriptSigCopy with placeholder, should always choose non-placeholder: + scriptSigCopy = CScript() << OP_0 << static_cast >(pkSingle); + combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSigCopy, scriptSig); + BOOST_CHECK(combined == scriptSig); + combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSig, scriptSigCopy); + BOOST_CHECK(combined == scriptSig); + + // Hardest case: Multisig 2-of-3 + scriptPubKey.SetMultisig(2, pubkeys); + keystore.AddCScript(scriptPubKey); + SignSignature(keystore, txFrom, txTo, 0); + combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSig, empty); + BOOST_CHECK(combined == scriptSig); + combined = CombineSignatures(scriptPubKey, txTo, 0, empty, scriptSig); + BOOST_CHECK(combined == scriptSig); + + // A couple of partially-signed versions: + vector sig1; + uint256 hash1 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_ALL); + BOOST_CHECK(keys[0].Sign(hash1, sig1)); + sig1.push_back(SIGHASH_ALL); + vector sig2; + uint256 hash2 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_NONE); + BOOST_CHECK(keys[1].Sign(hash2, sig2)); + sig2.push_back(SIGHASH_NONE); + vector sig3; + uint256 hash3 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_SINGLE); + BOOST_CHECK(keys[2].Sign(hash3, sig3)); + sig3.push_back(SIGHASH_SINGLE); + + // Not fussy about order (or even existence) of placeholders or signatures: + CScript partial1a = CScript() << OP_0 << sig1 << OP_0; + CScript partial1b = CScript() << OP_0 << OP_0 << sig1; + CScript partial2a = CScript() << OP_0 << sig2; + CScript partial2b = CScript() << sig2 << OP_0; + CScript partial3a = CScript() << sig3; + CScript partial3b = CScript() << OP_0 << OP_0 << sig3; + CScript partial3c = CScript() << OP_0 << sig3 << OP_0; + CScript complete12 = CScript() << OP_0 << sig1 << sig2; + CScript complete13 = CScript() << OP_0 << sig1 << sig3; + CScript complete23 = CScript() << OP_0 << sig2 << sig3; + + combined = CombineSignatures(scriptPubKey, txTo, 0, partial1a, partial1b); + BOOST_CHECK(combined == partial1a); + combined = CombineSignatures(scriptPubKey, txTo, 0, partial1a, partial2a); + BOOST_CHECK(combined == complete12); + combined = CombineSignatures(scriptPubKey, txTo, 0, partial2a, partial1a); + BOOST_CHECK(combined == complete12); + combined = CombineSignatures(scriptPubKey, txTo, 0, partial1b, partial2b); + BOOST_CHECK(combined == complete12); + combined = CombineSignatures(scriptPubKey, txTo, 0, partial3b, partial1b); + BOOST_CHECK(combined == complete13); + combined = CombineSignatures(scriptPubKey, txTo, 0, partial2a, partial3a); + BOOST_CHECK(combined == complete23); + combined = CombineSignatures(scriptPubKey, txTo, 0, partial3b, partial2b); + BOOST_CHECK(combined == complete23); + combined = CombineSignatures(scriptPubKey, txTo, 0, partial3b, partial3a); + BOOST_CHECK(combined == partial3c); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/scrypt_tests.cpp b/src/test/scrypt_tests.cpp new file mode 100644 index 0000000..c73de11 --- /dev/null +++ b/src/test/scrypt_tests.cpp @@ -0,0 +1,29 @@ +#include + +#include "util.h" +#include "scrypt.h" + +BOOST_AUTO_TEST_SUITE(scrypt_tests) + +BOOST_AUTO_TEST_CASE(scrypt_hashtest) +{ + // Test Scrypt hash with known inputs against expected outputs + #define HASHCOUNT 5 + const char* inputhex[HASHCOUNT] = { "020000004c1271c211717198227392b029a64a7971931d351b387bb80db027f270411e398a07046f7d4a08dd815412a8712f874a7ebf0507e3878bd24e20a3b73fd750a667d2f451eac7471b00de6659", "0200000011503ee6a855e900c00cfdd98f5f55fffeaee9b6bf55bea9b852d9de2ce35828e204eef76acfd36949ae56d1fbe81c1ac9c0209e6331ad56414f9072506a77f8c6faf551eac7471b00389d01", "02000000a72c8a177f523946f42f22c3e86b8023221b4105e8007e59e81f6beb013e29aaf635295cb9ac966213fb56e046dc71df5b3f7f67ceaeab24038e743f883aff1aaafaf551eac7471b0166249b", "010000007824bc3a8a1b4628485eee3024abd8626721f7f870f8ad4d2f33a27155167f6a4009d1285049603888fe85a84b6c803a53305a8d497965a5e896e1a00568359589faf551eac7471b0065434e", "0200000050bfd4e4a307a8cb6ef4aef69abc5c0f2d579648bd80d7733e1ccc3fbc90ed664a7f74006cb11bde87785f229ecd366c2d4e44432832580e0608c579e4cb76f383f7f551eac7471b00c36982" }; + const char* expected[HASHCOUNT] = { "00000000002bef4107f882f6115e0b01f348d21195dacd3582aa2dabd7985806" , "00000000003a0d11bdd5eb634e08b7feddcfbbf228ed35d250daf19f1c88fc94", "00000000000b40f895f288e13244728a6c2d9d59d8aff29c65f8dd5114a8ca81", "00000000003007005891cd4923031e99d8e8d72f6e8e7edc6a86181897e105fe", "000000000018f0b426a4afc7130ccb47fa02af730d345b4fe7c7724d3800ec8c" }; + uint256 scrypthash; + std::vector inputbytes; + char scratchpad[SCRYPT_SCRATCHPAD_SIZE]; + for (int i = 0; i < HASHCOUNT; i++) { + inputbytes = ParseHex(inputhex[i]); +#if defined(USE_SSE2) + // Test SSE2 scrypt + scrypt_1024_1_1_256_sp_sse2((const char*)&inputbytes[0], BEGIN(scrypthash), scratchpad); +#endif + // Test generic scrypt + scrypt_1024_1_1_256_sp_generic((const char*)&inputbytes[0], BEGIN(scrypthash), scratchpad); + BOOST_CHECK_EQUAL(scrypthash.ToString().c_str(), expected[i]); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp new file mode 100644 index 0000000..19ffdca --- /dev/null +++ b/src/test/serialize_tests.cpp @@ -0,0 +1,45 @@ +#include + +#include +#include + +#include "serialize.h" + +using namespace std; + +BOOST_AUTO_TEST_SUITE(serialize_tests) + +BOOST_AUTO_TEST_CASE(varints) +{ + // encode + + CDataStream ss(SER_DISK, 0); + CDataStream::size_type size = 0; + for (int i = 0; i < 100000; i++) { + ss << VARINT(i); + size += ::GetSerializeSize(VARINT(i), 0, 0); + BOOST_CHECK(size == ss.size()); + } + + for (uint64 i = 0; i < 100000000000ULL; i += 999999937) { + ss << VARINT(i); + size += ::GetSerializeSize(VARINT(i), 0, 0); + BOOST_CHECK(size == ss.size()); + } + + // decode + for (int i = 0; i < 100000; i++) { + int j = -1; + ss >> VARINT(j); + BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); + } + + for (uint64 i = 0; i < 100000000000ULL; i += 999999937) { + uint64 j = -1; + ss >> VARINT(j); + BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); + } + +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp new file mode 100644 index 0000000..5a87f17 --- /dev/null +++ b/src/test/sigopcount_tests.cpp @@ -0,0 +1,60 @@ +#include +#include +#include + +#include "script.h" +#include "key.h" + +using namespace std; + +// Helpers: +static std::vector +Serialize(const CScript& s) +{ + std::vector sSerialized(s); + return sSerialized; +} + +BOOST_AUTO_TEST_SUITE(sigopcount_tests) + +BOOST_AUTO_TEST_CASE(GetSigOpCount) +{ + // Test CScript::GetSigOpCount() + CScript s1; + BOOST_CHECK_EQUAL(s1.GetSigOpCount(false), 0U); + BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 0U); + + uint160 dummy; + s1 << OP_1 << dummy << dummy << OP_2 << OP_CHECKMULTISIG; + BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 2U); + s1 << OP_IF << OP_CHECKSIG << OP_ENDIF; + BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 3U); + BOOST_CHECK_EQUAL(s1.GetSigOpCount(false), 21U); + + CScript p2sh; + p2sh.SetDestination(s1.GetID()); + CScript scriptSig; + scriptSig << OP_0 << Serialize(s1); + BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(scriptSig), 3U); + + std::vector keys; + for (int i = 0; i < 3; i++) + { + CKey k; + k.MakeNewKey(true); + keys.push_back(k.GetPubKey()); + } + CScript s2; + s2.SetMultisig(1, keys); + BOOST_CHECK_EQUAL(s2.GetSigOpCount(true), 3U); + BOOST_CHECK_EQUAL(s2.GetSigOpCount(false), 20U); + + p2sh.SetDestination(s2.GetID()); + BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(true), 0U); + BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(false), 0U); + CScript scriptSig2; + scriptSig2 << OP_1 << dummy << dummy << Serialize(s2); + BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(scriptSig2), 3U); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp new file mode 100644 index 0000000..4ada524 --- /dev/null +++ b/src/test/test_bitcoin.cpp @@ -0,0 +1,66 @@ +#define BOOST_TEST_MODULE CryptoLottoToken Test Suite +#include +#include + +#include "db.h" +#include "txdb.h" +#include "main.h" +#include "wallet.h" +#include "util.h" + +CWallet* pwalletMain; +CClientUIInterface uiInterface; + +extern bool fPrintToConsole; +extern void noui_connect(); + +struct TestingSetup { + CCoinsViewDB *pcoinsdbview; + boost::filesystem::path pathTemp; + boost::thread_group threadGroup; + + TestingSetup() { + fPrintToDebugger = true; // don't want to write to debug.log file + noui_connect(); + bitdb.MakeMock(); + pathTemp = GetTempPath() / strprintf("test_CryptoLottoToken_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000))); + boost::filesystem::create_directories(pathTemp); + mapArgs["-datadir"] = pathTemp.string(); + pblocktree = new CBlockTreeDB(1 << 20, true); + pcoinsdbview = new CCoinsViewDB(1 << 23, true); + pcoinsTip = new CCoinsViewCache(*pcoinsdbview); + InitBlockIndex(); + bool fFirstRun; + pwalletMain = new CWallet("wallet.dat"); + pwalletMain->LoadWallet(fFirstRun); + RegisterWallet(pwalletMain); + nScriptCheckThreads = 3; + for (int i=0; i < nScriptCheckThreads-1; i++) + threadGroup.create_thread(&ThreadScriptCheck); + } + ~TestingSetup() + { + threadGroup.interrupt_all(); + threadGroup.join_all(); + delete pwalletMain; + pwalletMain = NULL; + delete pcoinsTip; + delete pcoinsdbview; + delete pblocktree; + bitdb.Flush(true); + boost::filesystem::remove_all(pathTemp); + } +}; + +BOOST_GLOBAL_FIXTURE(TestingSetup); + +void Shutdown(void* parg) +{ + exit(0); +} + +void StartShutdown() +{ + exit(0); +} + diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp new file mode 100644 index 0000000..d356e60 --- /dev/null +++ b/src/test/transaction_tests.cpp @@ -0,0 +1,276 @@ +#include +#include +#include +#include "json/json_spirit_writer_template.h" + +#include "main.h" +#include "wallet.h" + +using namespace std; +using namespace json_spirit; + +// In script_tests.cpp +extern Array read_json(const std::string& filename); +extern CScript ParseScript(string s); + +BOOST_AUTO_TEST_SUITE(transaction_tests) + +BOOST_AUTO_TEST_CASE(tx_valid) +{ + // Read tests from test/data/tx_valid.json + // Format is an array of arrays + // Inner arrays are either [ "comment" ] + // or [[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], serializedTransaction, enforceP2SH + // ... where all scripts are stringified scripts. + Array tests = read_json("tx_valid.json"); + + BOOST_FOREACH(Value& tv, tests) + { + Array test = tv.get_array(); + string strTest = write_string(tv, false); + if (test[0].type() == array_type) + { + if (test.size() != 3 || test[1].type() != str_type || test[2].type() != bool_type) + { + BOOST_ERROR("Bad test: " << strTest); + continue; + } + + map mapprevOutScriptPubKeys; + Array inputs = test[0].get_array(); + bool fValid = true; + BOOST_FOREACH(Value& input, inputs) + { + if (input.type() != array_type) + { + fValid = false; + break; + } + Array vinput = input.get_array(); + if (vinput.size() != 3) + { + fValid = false; + break; + } + + mapprevOutScriptPubKeys[COutPoint(uint256(vinput[0].get_str()), vinput[1].get_int())] = ParseScript(vinput[2].get_str()); + } + if (!fValid) + { + BOOST_ERROR("Bad test: " << strTest); + continue; + } + + string transaction = test[1].get_str(); + CDataStream stream(ParseHex(transaction), SER_NETWORK, PROTOCOL_VERSION); + CTransaction tx; + stream >> tx; + + CValidationState state; + BOOST_CHECK_MESSAGE(tx.CheckTransaction(state), strTest); + BOOST_CHECK(state.IsValid()); + + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout)) + { + BOOST_ERROR("Bad test: " << strTest); + break; + } + + BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], tx, i, test[2].get_bool() ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, 0), strTest); + } + } + } +} + +BOOST_AUTO_TEST_CASE(tx_invalid) +{ + // Read tests from test/data/tx_invalid.json + // Format is an array of arrays + // Inner arrays are either [ "comment" ] + // or [[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], serializedTransaction, enforceP2SH + // ... where all scripts are stringified scripts. + Array tests = read_json("tx_invalid.json"); + + BOOST_FOREACH(Value& tv, tests) + { + Array test = tv.get_array(); + string strTest = write_string(tv, false); + if (test[0].type() == array_type) + { + if (test.size() != 3 || test[1].type() != str_type || test[2].type() != bool_type) + { + BOOST_ERROR("Bad test: " << strTest); + continue; + } + + map mapprevOutScriptPubKeys; + Array inputs = test[0].get_array(); + bool fValid = true; + BOOST_FOREACH(Value& input, inputs) + { + if (input.type() != array_type) + { + fValid = false; + break; + } + Array vinput = input.get_array(); + if (vinput.size() != 3) + { + fValid = false; + break; + } + + mapprevOutScriptPubKeys[COutPoint(uint256(vinput[0].get_str()), vinput[1].get_int())] = ParseScript(vinput[2].get_str()); + } + if (!fValid) + { + BOOST_ERROR("Bad test: " << strTest); + continue; + } + + string transaction = test[1].get_str(); + CDataStream stream(ParseHex(transaction), SER_NETWORK, PROTOCOL_VERSION); + CTransaction tx; + stream >> tx; + + CValidationState state; + fValid = tx.CheckTransaction(state) && state.IsValid(); + + for (unsigned int i = 0; i < tx.vin.size() && fValid; i++) + { + if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout)) + { + BOOST_ERROR("Bad test: " << strTest); + break; + } + + fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], tx, i, test[2].get_bool() ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, 0); + } + + BOOST_CHECK_MESSAGE(!fValid, strTest); + } + } +} + +BOOST_AUTO_TEST_CASE(basic_transaction_tests) +{ + // Random real transaction (e2769b09e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436) + unsigned char ch[] = {0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0xff, 0x7f, 0xcd, 0x4f, 0x85, 0x65, 0xef, 0x40, 0x6d, 0xd5, 0xd6, 0x3d, 0x4f, 0xf9, 0x4f, 0x31, 0x8f, 0xe8, 0x20, 0x27, 0xfd, 0x4d, 0xc4, 0x51, 0xb0, 0x44, 0x74, 0x01, 0x9f, 0x74, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xda, 0x0d, 0xc6, 0xae, 0xce, 0xfe, 0x1e, 0x06, 0xef, 0xdf, 0x05, 0x77, 0x37, 0x57, 0xde, 0xb1, 0x68, 0x82, 0x09, 0x30, 0xe3, 0xb0, 0xd0, 0x3f, 0x46, 0xf5, 0xfc, 0xf1, 0x50, 0xbf, 0x99, 0x0c, 0x02, 0x21, 0x00, 0xd2, 0x5b, 0x5c, 0x87, 0x04, 0x00, 0x76, 0xe4, 0xf2, 0x53, 0xf8, 0x26, 0x2e, 0x76, 0x3e, 0x2d, 0xd5, 0x1e, 0x7f, 0xf0, 0xbe, 0x15, 0x77, 0x27, 0xc4, 0xbc, 0x42, 0x80, 0x7f, 0x17, 0xbd, 0x39, 0x01, 0x41, 0x04, 0xe6, 0xc2, 0x6e, 0xf6, 0x7d, 0xc6, 0x10, 0xd2, 0xcd, 0x19, 0x24, 0x84, 0x78, 0x9a, 0x6c, 0xf9, 0xae, 0xa9, 0x93, 0x0b, 0x94, 0x4b, 0x7e, 0x2d, 0xb5, 0x34, 0x2b, 0x9d, 0x9e, 0x5b, 0x9f, 0xf7, 0x9a, 0xff, 0x9a, 0x2e, 0xe1, 0x97, 0x8d, 0xd7, 0xfd, 0x01, 0xdf, 0xc5, 0x22, 0xee, 0x02, 0x28, 0x3d, 0x3b, 0x06, 0xa9, 0xd0, 0x3a, 0xcf, 0x80, 0x96, 0x96, 0x8d, 0x7d, 0xbb, 0x0f, 0x91, 0x78, 0xff, 0xff, 0xff, 0xff, 0x02, 0x8b, 0xa7, 0x94, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xba, 0xde, 0xec, 0xfd, 0xef, 0x05, 0x07, 0x24, 0x7f, 0xc8, 0xf7, 0x42, 0x41, 0xd7, 0x3b, 0xc0, 0x39, 0x97, 0x2d, 0x7b, 0x88, 0xac, 0x40, 0x94, 0xa8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xc1, 0x09, 0x32, 0x48, 0x3f, 0xec, 0x93, 0xed, 0x51, 0xf5, 0xfe, 0x95, 0xe7, 0x25, 0x59, 0xf2, 0xcc, 0x70, 0x43, 0xf9, 0x88, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00}; + vector vch(ch, ch + sizeof(ch) -1); + CDataStream stream(vch, SER_DISK, CLIENT_VERSION); + CTransaction tx; + stream >> tx; + CValidationState state; + BOOST_CHECK_MESSAGE(tx.CheckTransaction(state) && state.IsValid(), "Simple deserialized transaction should be valid."); + + // Check that duplicate txins fail + tx.vin.push_back(tx.vin[0]); + BOOST_CHECK_MESSAGE(!tx.CheckTransaction(state) || !state.IsValid(), "Transaction with duplicate txins should be invalid."); +} + +// +// Helper: create two dummy transactions, each with +// two outputs. The first has 11 and 50 CENT outputs +// paid to a TX_PUBKEY, the second 21 and 22 CENT outputs +// paid to a TX_PUBKEYHASH. +// +static std::vector +SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsView & coinsRet) +{ + std::vector dummyTransactions; + dummyTransactions.resize(2); + + // Add some keys to the keystore: + CKey key[4]; + for (int i = 0; i < 4; i++) + { + key[i].MakeNewKey(i % 2); + keystoreRet.AddKey(key[i]); + } + + // Create some dummy input transactions + dummyTransactions[0].vout.resize(2); + dummyTransactions[0].vout[0].nValue = 11*CENT; + dummyTransactions[0].vout[0].scriptPubKey << key[0].GetPubKey() << OP_CHECKSIG; + dummyTransactions[0].vout[1].nValue = 50*CENT; + dummyTransactions[0].vout[1].scriptPubKey << key[1].GetPubKey() << OP_CHECKSIG; + coinsRet.SetCoins(dummyTransactions[0].GetHash(), CCoins(dummyTransactions[0], 0)); + + dummyTransactions[1].vout.resize(2); + dummyTransactions[1].vout[0].nValue = 21*CENT; + dummyTransactions[1].vout[0].scriptPubKey.SetDestination(key[2].GetPubKey().GetID()); + dummyTransactions[1].vout[1].nValue = 22*CENT; + dummyTransactions[1].vout[1].scriptPubKey.SetDestination(key[3].GetPubKey().GetID()); + coinsRet.SetCoins(dummyTransactions[1].GetHash(), CCoins(dummyTransactions[1], 0)); + + return dummyTransactions; +} + +BOOST_AUTO_TEST_CASE(test_Get) +{ + CBasicKeyStore keystore; + CCoinsView coinsDummy; + CCoinsViewCache coins(coinsDummy); + std::vector dummyTransactions = SetupDummyInputs(keystore, coins); + + CTransaction t1; + t1.vin.resize(3); + t1.vin[0].prevout.hash = dummyTransactions[0].GetHash(); + t1.vin[0].prevout.n = 1; + t1.vin[0].scriptSig << std::vector(65, 0); + t1.vin[1].prevout.hash = dummyTransactions[1].GetHash(); + t1.vin[1].prevout.n = 0; + t1.vin[1].scriptSig << std::vector(65, 0) << std::vector(33, 4); + t1.vin[2].prevout.hash = dummyTransactions[1].GetHash(); + t1.vin[2].prevout.n = 1; + t1.vin[2].scriptSig << std::vector(65, 0) << std::vector(33, 4); + t1.vout.resize(2); + t1.vout[0].nValue = 90*CENT; + t1.vout[0].scriptPubKey << OP_1; + + BOOST_CHECK(t1.AreInputsStandard(coins)); + BOOST_CHECK_EQUAL(t1.GetValueIn(coins), (50+21+22)*CENT); + + // Adding extra junk to the scriptSig should make it non-standard: + t1.vin[0].scriptSig << OP_11; + BOOST_CHECK(!t1.AreInputsStandard(coins)); + + // ... as should not having enough: + t1.vin[0].scriptSig = CScript(); + BOOST_CHECK(!t1.AreInputsStandard(coins)); +} + +BOOST_AUTO_TEST_CASE(test_IsStandard) +{ + CBasicKeyStore keystore; + CCoinsView coinsDummy; + CCoinsViewCache coins(coinsDummy); + std::vector dummyTransactions = SetupDummyInputs(keystore, coins); + + CTransaction t; + t.vin.resize(1); + t.vin[0].prevout.hash = dummyTransactions[0].GetHash(); + t.vin[0].prevout.n = 1; + t.vin[0].scriptSig << std::vector(65, 0); + t.vout.resize(1); + t.vout[0].nValue = 90*CENT; + CKey key; + key.MakeNewKey(true); + t.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID()); + + BOOST_CHECK(t.IsStandard()); + + t.vout[0].nValue = 5011; // dust + // CryptoLottoToken does not enforce isDust(). Per dust fees are considered sufficient as deterrant. + // BOOST_CHECK(!t.IsStandard()); + + t.vout[0].nValue = 6011; // not dust + BOOST_CHECK(t.IsStandard()); + + t.vout[0].scriptPubKey = CScript() << OP_1; + BOOST_CHECK(!t.IsStandard()); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/uint160_tests.cpp b/src/test/uint160_tests.cpp new file mode 100644 index 0000000..35cb35b --- /dev/null +++ b/src/test/uint160_tests.cpp @@ -0,0 +1,18 @@ +#include + +#include "uint256.h" + +BOOST_AUTO_TEST_SUITE(uint160_tests) + +BOOST_AUTO_TEST_CASE(uint160_equality) +{ + uint160 num1 = 10; + uint160 num2 = 11; + BOOST_CHECK(num1+1 == num2); + + uint64 num3 = 10; + BOOST_CHECK(num1 == num3); + BOOST_CHECK(num1+num2 == num3+num2); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp new file mode 100644 index 0000000..efdc8a6 --- /dev/null +++ b/src/test/uint256_tests.cpp @@ -0,0 +1,18 @@ +#include + +#include "uint256.h" + +BOOST_AUTO_TEST_SUITE(uint256_tests) + +BOOST_AUTO_TEST_CASE(uint256_equality) +{ + uint256 num1 = 10; + uint256 num2 = 11; + BOOST_CHECK(num1+1 == num2); + + uint64 num3 = 10; + BOOST_CHECK(num1 == num3); + BOOST_CHECK(num1+num2 == num3+num2); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp new file mode 100644 index 0000000..e224776 --- /dev/null +++ b/src/test/util_tests.cpp @@ -0,0 +1,305 @@ +#include +#include +#include + +#include "main.h" +#include "wallet.h" +#include "util.h" + +using namespace std; + +BOOST_AUTO_TEST_SUITE(util_tests) + +BOOST_AUTO_TEST_CASE(util_criticalsection) +{ + CCriticalSection cs; + + do { + LOCK(cs); + break; + + BOOST_ERROR("break was swallowed!"); + } while(0); + + do { + TRY_LOCK(cs, lockTest); + if (lockTest) + break; + + BOOST_ERROR("break was swallowed!"); + } while(0); +} + +BOOST_AUTO_TEST_CASE(util_MedianFilter) +{ + CMedianFilter filter(5, 15); + + BOOST_CHECK_EQUAL(filter.median(), 15); + + filter.input(20); // [15 20] + BOOST_CHECK_EQUAL(filter.median(), 17); + + filter.input(30); // [15 20 30] + BOOST_CHECK_EQUAL(filter.median(), 20); + + filter.input(3); // [3 15 20 30] + BOOST_CHECK_EQUAL(filter.median(), 17); + + filter.input(7); // [3 7 15 20 30] + BOOST_CHECK_EQUAL(filter.median(), 15); + + filter.input(18); // [3 7 18 20 30] + BOOST_CHECK_EQUAL(filter.median(), 18); + + filter.input(0); // [0 3 7 18 30] + BOOST_CHECK_EQUAL(filter.median(), 7); +} + +static const unsigned char ParseHex_expected[65] = { + 0x04, 0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, 0x48, 0x27, 0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, 0xb7, + 0x10, 0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, 0x09, 0xa6, 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, 0xde, + 0xb6, 0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, 0x38, 0xc4, 0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, 0x12, + 0xde, 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, 0x8d, 0x57, 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d, + 0x5f +}; +BOOST_AUTO_TEST_CASE(util_ParseHex) +{ + std::vector result; + std::vector expected(ParseHex_expected, ParseHex_expected + sizeof(ParseHex_expected)); + // Basic test vector + result = ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"); + BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); + + // Spaces between bytes must be supported + result = ParseHex("12 34 56 78"); + BOOST_CHECK(result.size() == 4 && result[0] == 0x12 && result[1] == 0x34 && result[2] == 0x56 && result[3] == 0x78); + + // Stop parsing at invalid value + result = ParseHex("1234 invalid 1234"); + BOOST_CHECK(result.size() == 2 && result[0] == 0x12 && result[1] == 0x34); +} + +BOOST_AUTO_TEST_CASE(util_HexStr) +{ + BOOST_CHECK_EQUAL( + HexStr(ParseHex_expected, ParseHex_expected + sizeof(ParseHex_expected)), + "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"); + + BOOST_CHECK_EQUAL( + HexStr(ParseHex_expected, ParseHex_expected + 5, true), + "04 67 8a fd b0"); + + BOOST_CHECK_EQUAL( + HexStr(ParseHex_expected, ParseHex_expected, true), + ""); + + std::vector ParseHex_vec(ParseHex_expected, ParseHex_expected + 5); + + BOOST_CHECK_EQUAL( + HexStr(ParseHex_vec, true), + "04 67 8a fd b0"); +} + + +BOOST_AUTO_TEST_CASE(util_DateTimeStrFormat) +{ +/*These are platform-dependant and thus removed to avoid useless test failures + BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 0), "1970-01-01 00:00:00"); + BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 0x7FFFFFFF), "2038-01-19 03:14:07"); + // Formats used within Bitcoin + BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 1317425777), "2011-09-30 23:36:17"); + BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M", 1317425777), "2011-09-30 23:36"); +*/ +} + +BOOST_AUTO_TEST_CASE(util_ParseParameters) +{ + const char *argv_test[] = {"-ignored", "-a", "-b", "-ccc=argument", "-ccc=multiple", "f", "-d=e"}; + + ParseParameters(0, (char**)argv_test); + BOOST_CHECK(mapArgs.empty() && mapMultiArgs.empty()); + + ParseParameters(1, (char**)argv_test); + BOOST_CHECK(mapArgs.empty() && mapMultiArgs.empty()); + + ParseParameters(5, (char**)argv_test); + // expectation: -ignored is ignored (program name argument), + // -a, -b and -ccc end up in map, -d ignored because it is after + // a non-option argument (non-GNU option parsing) + BOOST_CHECK(mapArgs.size() == 3 && mapMultiArgs.size() == 3); + BOOST_CHECK(mapArgs.count("-a") && mapArgs.count("-b") && mapArgs.count("-ccc") + && !mapArgs.count("f") && !mapArgs.count("-d")); + BOOST_CHECK(mapMultiArgs.count("-a") && mapMultiArgs.count("-b") && mapMultiArgs.count("-ccc") + && !mapMultiArgs.count("f") && !mapMultiArgs.count("-d")); + + BOOST_CHECK(mapArgs["-a"] == "" && mapArgs["-ccc"] == "multiple"); + BOOST_CHECK(mapMultiArgs["-ccc"].size() == 2); +} + +BOOST_AUTO_TEST_CASE(util_GetArg) +{ + mapArgs.clear(); + mapArgs["strtest1"] = "string..."; + // strtest2 undefined on purpose + mapArgs["inttest1"] = "12345"; + mapArgs["inttest2"] = "81985529216486895"; + // inttest3 undefined on purpose + mapArgs["booltest1"] = ""; + // booltest2 undefined on purpose + mapArgs["booltest3"] = "0"; + mapArgs["booltest4"] = "1"; + + BOOST_CHECK_EQUAL(GetArg("strtest1", "default"), "string..."); + BOOST_CHECK_EQUAL(GetArg("strtest2", "default"), "default"); + BOOST_CHECK_EQUAL(GetArg("inttest1", -1), 12345); + BOOST_CHECK_EQUAL(GetArg("inttest2", -1), 81985529216486895LL); + BOOST_CHECK_EQUAL(GetArg("inttest3", -1), -1); + BOOST_CHECK_EQUAL(GetBoolArg("booltest1"), true); + BOOST_CHECK_EQUAL(GetBoolArg("booltest2"), false); + BOOST_CHECK_EQUAL(GetBoolArg("booltest3"), false); + BOOST_CHECK_EQUAL(GetBoolArg("booltest4"), true); +} + +BOOST_AUTO_TEST_CASE(util_WildcardMatch) +{ + BOOST_CHECK(WildcardMatch("127.0.0.1", "*")); + BOOST_CHECK(WildcardMatch("127.0.0.1", "127.*")); + BOOST_CHECK(WildcardMatch("abcdef", "a?cde?")); + BOOST_CHECK(!WildcardMatch("abcdef", "a?cde??")); + BOOST_CHECK(WildcardMatch("abcdef", "a*f")); + BOOST_CHECK(!WildcardMatch("abcdef", "a*x")); + BOOST_CHECK(WildcardMatch("", "*")); +} + +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_AUTO_TEST_CASE(util_ParseMoney) +{ + int64 ret = 0; + BOOST_CHECK(ParseMoney("0.0", ret)); + BOOST_CHECK_EQUAL(ret, 0); + + BOOST_CHECK(ParseMoney("12345.6789", ret)); + BOOST_CHECK_EQUAL(ret, (COIN/10000)*123456789); + + BOOST_CHECK(ParseMoney("100000000.00", ret)); + BOOST_CHECK_EQUAL(ret, COIN*100000000); + BOOST_CHECK(ParseMoney("10000000.00", ret)); + BOOST_CHECK_EQUAL(ret, COIN*10000000); + BOOST_CHECK(ParseMoney("1000000.00", ret)); + BOOST_CHECK_EQUAL(ret, COIN*1000000); + BOOST_CHECK(ParseMoney("100000.00", ret)); + BOOST_CHECK_EQUAL(ret, COIN*100000); + BOOST_CHECK(ParseMoney("10000.00", ret)); + BOOST_CHECK_EQUAL(ret, COIN*10000); + BOOST_CHECK(ParseMoney("1000.00", ret)); + BOOST_CHECK_EQUAL(ret, COIN*1000); + BOOST_CHECK(ParseMoney("100.00", ret)); + BOOST_CHECK_EQUAL(ret, COIN*100); + BOOST_CHECK(ParseMoney("10.00", ret)); + BOOST_CHECK_EQUAL(ret, COIN*10); + BOOST_CHECK(ParseMoney("1.00", ret)); + BOOST_CHECK_EQUAL(ret, COIN); + BOOST_CHECK(ParseMoney("0.1", ret)); + BOOST_CHECK_EQUAL(ret, COIN/10); + BOOST_CHECK(ParseMoney("0.01", ret)); + BOOST_CHECK_EQUAL(ret, COIN/100); + BOOST_CHECK(ParseMoney("0.001", ret)); + BOOST_CHECK_EQUAL(ret, COIN/1000); + BOOST_CHECK(ParseMoney("0.0001", ret)); + BOOST_CHECK_EQUAL(ret, COIN/10000); + BOOST_CHECK(ParseMoney("0.00001", ret)); + BOOST_CHECK_EQUAL(ret, COIN/100000); + BOOST_CHECK(ParseMoney("0.000001", ret)); + BOOST_CHECK_EQUAL(ret, COIN/1000000); + BOOST_CHECK(ParseMoney("0.0000001", ret)); + BOOST_CHECK_EQUAL(ret, COIN/10000000); + BOOST_CHECK(ParseMoney("0.00000001", ret)); + BOOST_CHECK_EQUAL(ret, COIN/100000000); + + // Attempted 63 bit overflow should fail + BOOST_CHECK(!ParseMoney("92233720368.54775808", ret)); +} + +BOOST_AUTO_TEST_CASE(util_IsHex) +{ + BOOST_CHECK(IsHex("00")); + BOOST_CHECK(IsHex("00112233445566778899aabbccddeeffAABBCCDDEEFF")); + BOOST_CHECK(IsHex("ff")); + BOOST_CHECK(IsHex("FF")); + + BOOST_CHECK(!IsHex("")); + BOOST_CHECK(!IsHex("0")); + BOOST_CHECK(!IsHex("a")); + BOOST_CHECK(!IsHex("eleven")); + BOOST_CHECK(!IsHex("00xx00")); + BOOST_CHECK(!IsHex("0x0000")); +} + +BOOST_AUTO_TEST_CASE(util_seed_insecure_rand) +{ + int i; + int count=0; + + seed_insecure_rand(true); + + for (int mod=2;mod<11;mod++) + { + int mask = 1; + // Really rough binomal confidence approximation. + int err = 30*10000./mod*sqrt((1./mod*(1-1./mod))/10000.); + //mask is 2^ceil(log2(mod))-1 + while(mask=(uint32_t)mod); + count += rval==0; + } + BOOST_CHECK(count<=10000/mod+err); + BOOST_CHECK(count>=10000/mod-err); + } +} + +BOOST_AUTO_TEST_CASE(util_TimingResistantEqual) +{ + BOOST_CHECK(TimingResistantEqual(std::string(""), std::string(""))); + BOOST_CHECK(!TimingResistantEqual(std::string("abc"), std::string(""))); + BOOST_CHECK(!TimingResistantEqual(std::string(""), std::string("abc"))); + BOOST_CHECK(!TimingResistantEqual(std::string("a"), std::string("aa"))); + BOOST_CHECK(!TimingResistantEqual(std::string("aa"), std::string("a"))); + BOOST_CHECK(TimingResistantEqual(std::string("abc"), std::string("abc"))); + BOOST_CHECK(!TimingResistantEqual(std::string("abc"), std::string("aba"))); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/wallet_tests.cpp b/src/test/wallet_tests.cpp new file mode 100644 index 0000000..a14f6b2 --- /dev/null +++ b/src/test/wallet_tests.cpp @@ -0,0 +1,294 @@ +#include + +#include "main.h" +#include "wallet.h" + +// how many times to run all the tests to have a chance to catch errors that only show up with particular random shuffles +#define RUN_TESTS 100 + +// some tests fail 1% of the time due to bad luck. +// we repeat those tests this many times and only complain if all iterations of the test fail +#define RANDOM_REPEATS 5 + +using namespace std; + +typedef set > CoinSet; + +BOOST_AUTO_TEST_SUITE(wallet_tests) + +static CWallet wallet; +static vector vCoins; + +static void add_coin(int64 nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0) +{ + static int nextLockTime = 0; + CTransaction tx; + tx.nLockTime = nextLockTime++; // so all transactions get different hashes + tx.vout.resize(nInput+1); + tx.vout[nInput].nValue = nValue; + CWalletTx* wtx = new CWalletTx(&wallet, tx); + if (fIsFromMe) + { + // IsFromMe() returns (GetDebit() > 0), and GetDebit() is 0 if vin.empty(), + // so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe() + wtx->vin.resize(1); + wtx->fDebitCached = true; + wtx->nDebitCached = 1; + } + COutput output(wtx, nInput, nAge); + vCoins.push_back(output); +} + +static void empty_wallet(void) +{ + BOOST_FOREACH(COutput output, vCoins) + delete output.tx; + vCoins.clear(); +} + +static bool equal_sets(CoinSet a, CoinSet b) +{ + pair ret = mismatch(a.begin(), a.end(), b.begin()); + return ret.first == a.end() && ret.second == b.end(); +} + +BOOST_AUTO_TEST_CASE(coin_selection_tests) +{ + CoinSet setCoinsRet, setCoinsRet2; + int64 nValueRet; + + // test multiple times to allow for differences in the shuffle order + for (int i = 0; i < RUN_TESTS; i++) + { + empty_wallet(); + + // with an empty wallet we can't even pay one cent + BOOST_CHECK(!wallet.SelectCoinsMinConf( 1 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet)); + + add_coin(1*CENT, 4); // add a new 1 cent coin + + // with a new 1 cent coin, we still can't find a mature 1 cent + BOOST_CHECK(!wallet.SelectCoinsMinConf( 1 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet)); + + // but we can find a new 1 cent + BOOST_CHECK( wallet.SelectCoinsMinConf( 1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); + + add_coin(2*CENT); // add a mature 2 cent coin + + // we can't make 3 cents of mature coins + BOOST_CHECK(!wallet.SelectCoinsMinConf( 3 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet)); + + // we can make 3 cents of new coins + BOOST_CHECK( wallet.SelectCoinsMinConf( 3 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 3 * CENT); + + add_coin(5*CENT); // add a mature 5 cent coin, + add_coin(10*CENT, 3, true); // a new 10 cent coin sent from one of our own addresses + add_coin(20*CENT); // and a mature 20 cent coin + + // now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27. total = 38 + + // we can't make 38 cents only if we disallow new coins: + BOOST_CHECK(!wallet.SelectCoinsMinConf(38 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet)); + // we can't even make 37 cents if we don't allow new coins even if they're from us + BOOST_CHECK(!wallet.SelectCoinsMinConf(38 * CENT, 6, 6, vCoins, setCoinsRet, nValueRet)); + // but we can make 37 cents if we accept new coins from ourself + BOOST_CHECK( wallet.SelectCoinsMinConf(37 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 37 * CENT); + // and we can make 38 cents if we accept all new coins + BOOST_CHECK( wallet.SelectCoinsMinConf(38 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 38 * CENT); + + // try making 34 cents from 1,2,5,10,20 - we can't do it exactly + BOOST_CHECK( wallet.SelectCoinsMinConf(34 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_GT(nValueRet, 34 * CENT); // but should get more than 34 cents + BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // the best should be 20+10+5. it's incredibly unlikely the 1 or 2 got included (but possible) + + // when we try making 7 cents, the smaller coins (1,2,5) are enough. We should see just 2+5 + BOOST_CHECK( wallet.SelectCoinsMinConf( 7 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 7 * CENT); + BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); + + // when we try making 8 cents, the smaller coins (1,2,5) are exactly enough. + BOOST_CHECK( wallet.SelectCoinsMinConf( 8 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK(nValueRet == 8 * CENT); + BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); + + // when we try making 9 cents, no subset of smaller coins is enough, and we get the next bigger coin (10) + BOOST_CHECK( wallet.SelectCoinsMinConf( 9 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 10 * CENT); + BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); + + // now clear out the wallet and start again to test choosing between subsets of smaller coins and the next biggest coin + empty_wallet(); + + add_coin( 6*CENT); + add_coin( 7*CENT); + add_coin( 8*CENT); + add_coin(20*CENT); + add_coin(30*CENT); // now we have 6+7+8+20+30 = 71 cents total + + // check that we have 71 and not 72 + BOOST_CHECK( wallet.SelectCoinsMinConf(71 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK(!wallet.SelectCoinsMinConf(72 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + + // now try making 16 cents. the best smaller coins can do is 6+7+8 = 21; not as good at the next biggest coin, 20 + BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 20 * CENT); // we should get 20 in one coin + BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); + + add_coin( 5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total + + // now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, better than the next biggest coin, 20 + BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 3 coins + BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); + + add_coin( 18*CENT); // now we have 5+6+7+8+18+20+30 + + // and now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, the same as the next biggest coin, 18 + BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 1 coin + BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // because in the event of a tie, the biggest coin wins + + // now try making 11 cents. we should get 5+6 + BOOST_CHECK( wallet.SelectCoinsMinConf(11 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 11 * CENT); + BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); + + // check that the smallest bigger coin is used + add_coin( 1*COIN); + add_coin( 2*COIN); + add_coin( 3*COIN); + add_coin( 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents + BOOST_CHECK( wallet.SelectCoinsMinConf(95 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 1 * COIN); // we should get 1 BTC in 1 coin + BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); + + BOOST_CHECK( wallet.SelectCoinsMinConf(195 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 2 * COIN); // we should get 2 BTC in 1 coin + BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); + + // empty the wallet and start again, now with fractions of a cent, to test sub-cent change avoidance + empty_wallet(); + add_coin(0.1*CENT); + add_coin(0.2*CENT); + add_coin(0.3*CENT); + add_coin(0.4*CENT); + add_coin(0.5*CENT); + + // try making 1 cent from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 = 1.5 cents + // we'll get sub-cent change whatever happens, so can expect 1.0 exactly + BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); + + // but if we add a bigger coin, making it possible to avoid sub-cent change, things change: + add_coin(1111*CENT); + + // try making 1 cent from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5 cents + BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); // we should get the exact amount + + // if we add more sub-cent coins: + add_coin(0.6*CENT); + add_coin(0.7*CENT); + + // and try again to make 1.0 cents, we can still make 1.0 cents + BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); // we should get the exact amount + + // run the 'mtgox' test (see http://blockexplorer.com/tx/29a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf) + // they tried to consolidate 10 50k coins into one 500k coin, and ended up with 50k in change + empty_wallet(); + for (int i = 0; i < 20; i++) + add_coin(50000 * COIN); + + BOOST_CHECK( wallet.SelectCoinsMinConf(500000 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 500000 * COIN); // we should get the exact amount + BOOST_CHECK_EQUAL(setCoinsRet.size(), 10U); // in ten coins + + // if there's not enough in the smaller coins to make at least 1 cent change (0.5+0.6+0.7 < 1.0+1.0), + // we need to try finding an exact subset anyway + + // sometimes it will fail, and so we use the next biggest coin: + empty_wallet(); + add_coin(0.5 * CENT); + add_coin(0.6 * CENT); + add_coin(0.7 * CENT); + add_coin(1111 * CENT); + BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 1111 * CENT); // we get the bigger coin + BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); + + // but sometimes it's possible, and we use an exact subset (0.4 + 0.6 = 1.0) + empty_wallet(); + add_coin(0.4 * CENT); + add_coin(0.6 * CENT); + add_coin(0.8 * CENT); + add_coin(1111 * CENT); + BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); // we should get the exact amount + BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // in two coins 0.4+0.6 + + // test avoiding sub-cent change + empty_wallet(); + add_coin(0.0005 * COIN); + add_coin(0.01 * COIN); + add_coin(1 * COIN); + + // trying to make 1.0001 from these three coins + BOOST_CHECK( wallet.SelectCoinsMinConf(1.0001 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 1.0105 * COIN); // we should get all coins + BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); + + // but if we try to make 0.999, we should take the bigger of the two small coins to avoid sub-cent change + BOOST_CHECK( wallet.SelectCoinsMinConf(0.999 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 1.01 * COIN); // we should get 1 + 0.01 + BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); + + // test randomness + { + empty_wallet(); + for (int i2 = 0; i2 < 100; i2++) + add_coin(COIN); + + // picking 50 from 100 coins doesn't depend on the shuffle, + // but does depend on randomness in the stochastic approximation code + BOOST_CHECK(wallet.SelectCoinsMinConf(50 * COIN, 1, 6, vCoins, setCoinsRet , nValueRet)); + BOOST_CHECK(wallet.SelectCoinsMinConf(50 * COIN, 1, 6, vCoins, setCoinsRet2, nValueRet)); + BOOST_CHECK(!equal_sets(setCoinsRet, setCoinsRet2)); + + int fails = 0; + for (int i = 0; i < RANDOM_REPEATS; i++) + { + // selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time + // run the test RANDOM_REPEATS times and only complain if all of them fail + BOOST_CHECK(wallet.SelectCoinsMinConf(COIN, 1, 6, vCoins, setCoinsRet , nValueRet)); + BOOST_CHECK(wallet.SelectCoinsMinConf(COIN, 1, 6, vCoins, setCoinsRet2, nValueRet)); + if (equal_sets(setCoinsRet, setCoinsRet2)) + fails++; + } + BOOST_CHECK_NE(fails, RANDOM_REPEATS); + + // add 75 cents in small change. not enough to make 90 cents, + // then try making 90 cents. there are multiple competing "smallest bigger" coins, + // one of which should be picked at random + add_coin( 5*CENT); add_coin(10*CENT); add_coin(15*CENT); add_coin(20*CENT); add_coin(25*CENT); + + fails = 0; + for (int i = 0; i < RANDOM_REPEATS; i++) + { + // selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time + // run the test RANDOM_REPEATS times and only complain if all of them fail + BOOST_CHECK(wallet.SelectCoinsMinConf(90*CENT, 1, 6, vCoins, setCoinsRet , nValueRet)); + BOOST_CHECK(wallet.SelectCoinsMinConf(90*CENT, 1, 6, vCoins, setCoinsRet2, nValueRet)); + if (equal_sets(setCoinsRet, setCoinsRet2)) + fails++; + } + BOOST_CHECK_NE(fails, RANDOM_REPEATS); + } + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/threadsafety.h b/src/threadsafety.h new file mode 100644 index 0000000..3d3d526 --- /dev/null +++ b/src/threadsafety.h @@ -0,0 +1,53 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_THREADSAFETY_H +#define BITCOIN_THREADSAFETY_H + +#ifdef __clang__ +// TL;DR Add GUARDED_BY(mutex) to member variables. The others are +// rarely necessary. Ex: int nFoo GUARDED_BY(cs_foo); +// +// See http://clang.llvm.org/docs/LanguageExtensions.html#threadsafety +// for documentation. The clang compiler can do advanced static analysis +// of locking when given the -Wthread-safety option. +#define LOCKABLE __attribute__ ((lockable)) +#define SCOPED_LOCKABLE __attribute__ ((scoped_lockable)) +#define GUARDED_BY(x) __attribute__ ((guarded_by(x))) +#define GUARDED_VAR __attribute__ ((guarded_var)) +#define PT_GUARDED_BY(x) __attribute__ ((pt_guarded_by(x))) +#define PT_GUARDED_VAR __attribute__ ((pt_guarded_var)) +#define ACQUIRED_AFTER(...) __attribute__ ((acquired_after(__VA_ARGS__))) +#define ACQUIRED_BEFORE(...) __attribute__ ((acquired_before(__VA_ARGS__))) +#define EXCLUSIVE_LOCK_FUNCTION(...) __attribute__ ((exclusive_lock_function(__VA_ARGS__))) +#define SHARED_LOCK_FUNCTION(...) __attribute__ ((shared_lock_function(__VA_ARGS__))) +#define EXCLUSIVE_TRYLOCK_FUNCTION(...) __attribute__ ((exclusive_trylock_function(__VA_ARGS__))) +#define SHARED_TRYLOCK_FUNCTION(...) __attribute__ ((shared_trylock_function(__VA_ARGS__))) +#define UNLOCK_FUNCTION(...) __attribute__ ((unlock_function(__VA_ARGS__))) +#define LOCK_RETURNED(x) __attribute__ ((lock_returned(x))) +#define LOCKS_EXCLUDED(...) __attribute__ ((locks_excluded(__VA_ARGS__))) +#define EXCLUSIVE_LOCKS_REQUIRED(...) __attribute__ ((exclusive_locks_required(__VA_ARGS__))) +#define SHARED_LOCKS_REQUIRED(...) __attribute__ ((shared_locks_required(__VA_ARGS__))) +#define NO_THREAD_SAFETY_ANALYSIS __attribute__ ((no_thread_safety_analysis)) +#else +#define LOCKABLE +#define SCOPED_LOCKABLE +#define GUARDED_BY(x) +#define GUARDED_VAR +#define PT_GUARDED_BY(x) +#define PT_GUARDED_VAR +#define ACQUIRED_AFTER(...) +#define ACQUIRED_BEFORE(...) +#define EXCLUSIVE_LOCK_FUNCTION(...) +#define SHARED_LOCK_FUNCTION(...) +#define EXCLUSIVE_TRYLOCK_FUNCTION(...) +#define SHARED_TRYLOCK_FUNCTION(...) +#define UNLOCK_FUNCTION(...) +#define LOCK_RETURNED(x) +#define LOCKS_EXCLUDED(...) +#define EXCLUSIVE_LOCKS_REQUIRED(...) +#define SHARED_LOCKS_REQUIRED(...) +#define NO_THREAD_SAFETY_ANALYSIS +#endif // __GNUC__ +#endif // BITCOIN_THREADSAFETY_H diff --git a/src/txdb.cpp b/src/txdb.cpp new file mode 100644 index 0000000..3d34710 --- /dev/null +++ b/src/txdb.cpp @@ -0,0 +1,243 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "txdb.h" +#include "main.h" +#include "hash.h" + +using namespace std; + +void static BatchWriteCoins(CLevelDBBatch &batch, const uint256 &hash, const CCoins &coins) { + if (coins.IsPruned()) + batch.Erase(make_pair('c', hash)); + else + batch.Write(make_pair('c', hash), coins); +} + +void static BatchWriteHashBestChain(CLevelDBBatch &batch, const uint256 &hash) { + batch.Write('B', hash); +} + +CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe) { +} + +bool CCoinsViewDB::GetCoins(const uint256 &txid, CCoins &coins) { + return db.Read(make_pair('c', txid), coins); +} + +bool CCoinsViewDB::SetCoins(const uint256 &txid, const CCoins &coins) { + CLevelDBBatch batch; + BatchWriteCoins(batch, txid, coins); + return db.WriteBatch(batch); +} + +bool CCoinsViewDB::HaveCoins(const uint256 &txid) { + return db.Exists(make_pair('c', txid)); +} + +CBlockIndex *CCoinsViewDB::GetBestBlock() { + uint256 hashBestChain; + if (!db.Read('B', hashBestChain)) + return NULL; + std::map::iterator it = mapBlockIndex.find(hashBestChain); + if (it == mapBlockIndex.end()) + return NULL; + return it->second; +} + +bool CCoinsViewDB::SetBestBlock(CBlockIndex *pindex) { + CLevelDBBatch batch; + BatchWriteHashBestChain(batch, pindex->GetBlockHash()); + return db.WriteBatch(batch); +} + +bool CCoinsViewDB::BatchWrite(const std::map &mapCoins, CBlockIndex *pindex) { + printf("Committing %u changed transactions to coin database...\n", (unsigned int)mapCoins.size()); + + CLevelDBBatch batch; + for (std::map::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) + BatchWriteCoins(batch, it->first, it->second); + if (pindex) + BatchWriteHashBestChain(batch, pindex->GetBlockHash()); + + return db.WriteBatch(batch); +} + +CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDB(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) { +} + +bool CBlockTreeDB::WriteBlockIndex(const CDiskBlockIndex& blockindex) +{ + return Write(make_pair('b', blockindex.GetBlockHash()), blockindex); +} + +bool CBlockTreeDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork) +{ + return Read('I', bnBestInvalidWork); +} + +bool CBlockTreeDB::WriteBestInvalidWork(const CBigNum& bnBestInvalidWork) +{ + return Write('I', bnBestInvalidWork); +} + +bool CBlockTreeDB::WriteBlockFileInfo(int nFile, const CBlockFileInfo &info) { + return Write(make_pair('f', nFile), info); +} + +bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) { + return Read(make_pair('f', nFile), info); +} + +bool CBlockTreeDB::WriteLastBlockFile(int nFile) { + return Write('l', nFile); +} + +bool CBlockTreeDB::WriteReindexing(bool fReindexing) { + if (fReindexing) + return Write('R', '1'); + else + return Erase('R'); +} + +bool CBlockTreeDB::ReadReindexing(bool &fReindexing) { + fReindexing = Exists('R'); + return true; +} + +bool CBlockTreeDB::ReadLastBlockFile(int &nFile) { + return Read('l', nFile); +} + +bool CCoinsViewDB::GetStats(CCoinsStats &stats) { + leveldb::Iterator *pcursor = db.NewIterator(); + pcursor->SeekToFirst(); + + CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); + stats.hashBlock = GetBestBlock()->GetBlockHash(); + ss << stats.hashBlock; + int64 nTotalAmount = 0; + while (pcursor->Valid()) { + boost::this_thread::interruption_point(); + try { + leveldb::Slice slKey = pcursor->key(); + CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); + char chType; + ssKey >> chType; + if (chType == 'c') { + leveldb::Slice slValue = pcursor->value(); + CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); + CCoins coins; + ssValue >> coins; + uint256 txhash; + ssKey >> txhash; + ss << txhash; + ss << VARINT(coins.nVersion); + ss << (coins.fCoinBase ? 'c' : 'n'); + ss << VARINT(coins.nHeight); + stats.nTransactions++; + for (unsigned int i=0; iNext(); + } catch (std::exception &e) { + return error("%s() : deserialize error", __PRETTY_FUNCTION__); + } + } + delete pcursor; + stats.nHeight = GetBestBlock()->nHeight; + stats.hashSerialized = ss.GetHash(); + stats.nTotalAmount = nTotalAmount; + return true; +} + +bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) { + return Read(make_pair('t', txid), pos); +} + +bool CBlockTreeDB::WriteTxIndex(const std::vector >&vect) { + CLevelDBBatch batch; + for (std::vector >::const_iterator it=vect.begin(); it!=vect.end(); it++) + batch.Write(make_pair('t', it->first), it->second); + return WriteBatch(batch); +} + +bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) { + return Write(std::make_pair('F', name), fValue ? '1' : '0'); +} + +bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) { + char ch; + if (!Read(std::make_pair('F', name), ch)) + return false; + fValue = ch == '1'; + return true; +} + +bool CBlockTreeDB::LoadBlockIndexGuts() +{ + leveldb::Iterator *pcursor = NewIterator(); + + CDataStream ssKeySet(SER_DISK, CLIENT_VERSION); + ssKeySet << make_pair('b', uint256(0)); + pcursor->Seek(ssKeySet.str()); + + // Load mapBlockIndex + while (pcursor->Valid()) { + boost::this_thread::interruption_point(); + try { + leveldb::Slice slKey = pcursor->key(); + CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); + char chType; + ssKey >> chType; + if (chType == 'b') { + leveldb::Slice slValue = pcursor->value(); + CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); + CDiskBlockIndex diskindex; + ssValue >> diskindex; + + // Construct block index object + CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash()); + pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); + pindexNew->nHeight = diskindex.nHeight; + pindexNew->nFile = diskindex.nFile; + pindexNew->nDataPos = diskindex.nDataPos; + pindexNew->nUndoPos = diskindex.nUndoPos; + pindexNew->nVersion = diskindex.nVersion; + pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; + pindexNew->nTime = diskindex.nTime; + pindexNew->nBits = diskindex.nBits; + pindexNew->nNonce = diskindex.nNonce; + pindexNew->nStatus = diskindex.nStatus; + pindexNew->nTx = diskindex.nTx; + + // Watch for genesis block + if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock) + pindexGenesisBlock = pindexNew; + + if (!pindexNew->CheckIndex()) + return error("LoadBlockIndex() : CheckIndex failed: %s", pindexNew->ToString().c_str()); + + pcursor->Next(); + } else { + break; // if shutdown requested or finished loading block index + } + } catch (std::exception &e) { + return error("%s() : deserialize error", __PRETTY_FUNCTION__); + } + } + delete pcursor; + + return true; +} diff --git a/src/txdb.h b/src/txdb.h new file mode 100644 index 0000000..f59fc5d --- /dev/null +++ b/src/txdb.h @@ -0,0 +1,53 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_TXDB_LEVELDB_H +#define BITCOIN_TXDB_LEVELDB_H + +#include "main.h" +#include "leveldb.h" + +/** CCoinsView backed by the LevelDB coin database (chainstate/) */ +class CCoinsViewDB : public CCoinsView +{ +protected: + CLevelDB db; +public: + CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); + + bool GetCoins(const uint256 &txid, CCoins &coins); + bool SetCoins(const uint256 &txid, const CCoins &coins); + bool HaveCoins(const uint256 &txid); + CBlockIndex *GetBestBlock(); + bool SetBestBlock(CBlockIndex *pindex); + bool BatchWrite(const std::map &mapCoins, CBlockIndex *pindex); + bool GetStats(CCoinsStats &stats); +}; + +/** Access to the block database (blocks/index/) */ +class CBlockTreeDB : public CLevelDB +{ +public: + CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); +private: + CBlockTreeDB(const CBlockTreeDB&); + void operator=(const CBlockTreeDB&); +public: + bool WriteBlockIndex(const CDiskBlockIndex& blockindex); + bool ReadBestInvalidWork(CBigNum& bnBestInvalidWork); + bool WriteBestInvalidWork(const CBigNum& bnBestInvalidWork); + bool ReadBlockFileInfo(int nFile, CBlockFileInfo &fileinfo); + bool WriteBlockFileInfo(int nFile, const CBlockFileInfo &fileinfo); + bool ReadLastBlockFile(int &nFile); + bool WriteLastBlockFile(int nFile); + bool WriteReindexing(bool fReindex); + bool ReadReindexing(bool &fReindex); + bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos); + bool WriteTxIndex(const std::vector > &list); + bool WriteFlag(const std::string &name, bool fValue); + bool ReadFlag(const std::string &name, bool &fValue); + bool LoadBlockIndexGuts(); +}; + +#endif // BITCOIN_TXDB_LEVELDB_H diff --git a/src/ui_interface.h b/src/ui_interface.h new file mode 100644 index 0000000..5b0555c --- /dev/null +++ b/src/ui_interface.h @@ -0,0 +1,110 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_UI_INTERFACE_H +#define BITCOIN_UI_INTERFACE_H + +#include +#include "util.h" // for int64 +#include +#include + +class CBasicKeyStore; +class CWallet; +class uint256; + +/** General change type (added, updated, removed). */ +enum ChangeType +{ + CT_NEW, + CT_UPDATED, + CT_DELETED +}; + +/** Signals for UI communication. */ +class CClientUIInterface +{ +public: + /** Flags for CClientUIInterface::ThreadSafeMessageBox */ + enum MessageBoxFlags + { + ICON_INFORMATION = 0, + ICON_WARNING = (1U << 0), + ICON_ERROR = (1U << 1), + /** + * Mask of all available icons in CClientUIInterface::MessageBoxFlags + * This needs to be updated, when icons are changed there! + */ + ICON_MASK = (ICON_INFORMATION | ICON_WARNING | ICON_ERROR), + + /** These values are taken from qmessagebox.h "enum StandardButton" to be directly usable */ + BTN_OK = 0x00000400U, // QMessageBox::Ok + BTN_YES = 0x00004000U, // QMessageBox::Yes + BTN_NO = 0x00010000U, // QMessageBox::No + BTN_ABORT = 0x00040000U, // QMessageBox::Abort + BTN_RETRY = 0x00080000U, // QMessageBox::Retry + BTN_IGNORE = 0x00100000U, // QMessageBox::Ignore + BTN_CLOSE = 0x00200000U, // QMessageBox::Close + BTN_CANCEL = 0x00400000U, // QMessageBox::Cancel + BTN_DISCARD = 0x00800000U, // QMessageBox::Discard + BTN_HELP = 0x01000000U, // QMessageBox::Help + BTN_APPLY = 0x02000000U, // QMessageBox::Apply + BTN_RESET = 0x04000000U, // QMessageBox::Reset + /** + * Mask of all available buttons in CClientUIInterface::MessageBoxFlags + * This needs to be updated, when buttons are changed there! + */ + BTN_MASK = (BTN_OK | BTN_YES | BTN_NO | BTN_ABORT | BTN_RETRY | BTN_IGNORE | + BTN_CLOSE | BTN_CANCEL | BTN_DISCARD | BTN_HELP | BTN_APPLY | BTN_RESET), + + /** Force blocking, modal message box dialog (not just OS notification) */ + MODAL = 0x10000000U, + + /** Predefined combinations for certain default usage cases */ + MSG_INFORMATION = ICON_INFORMATION, + MSG_WARNING = (ICON_WARNING | BTN_OK | MODAL), + MSG_ERROR = (ICON_ERROR | BTN_OK | MODAL) + }; + + /** Show message box. */ + boost::signals2::signal > ThreadSafeMessageBox; + + /** Ask the user whether they want to pay a fee or not. */ + boost::signals2::signal > ThreadSafeAskFee; + + /** Handle a URL passed at the command line. */ + boost::signals2::signal ThreadSafeHandleURI; + + /** Progress message during initialization. */ + boost::signals2::signal InitMessage; + + /** Translate a message to the native language of the user. */ + boost::signals2::signal Translate; + + /** Block chain changed. */ + boost::signals2::signal NotifyBlocksChanged; + + /** Number of network connections changed. */ + boost::signals2::signal NotifyNumConnectionsChanged; + + /** + * New, updated or cancelled alert. + * @note called with lock cs_mapAlerts held. + */ + boost::signals2::signal NotifyAlertChanged; +}; + +extern CClientUIInterface uiInterface; + +/** + * Translation function: Call Translate signal on UI interface, which returns a boost::optional result. + * If no translation slot is registered, nothing is returned, and simply return the input. + */ +inline std::string _(const char* psz) +{ + boost::optional rv = uiInterface.Translate(psz); + return rv ? (*rv) : psz; +} + +#endif diff --git a/src/uint256.h b/src/uint256.h new file mode 100644 index 0000000..2a252c9 --- /dev/null +++ b/src/uint256.h @@ -0,0 +1,784 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_UINT256_H +#define BITCOIN_UINT256_H + +#include +#include +#include +#include +#include +#include + +typedef long long int64; +typedef unsigned long long uint64; + + +inline int Testuint256AdHoc(std::vector vArg); + + + +/** Base class without constructors for uint256 and uint160. + * This makes the compiler let you use it in a union. + */ +template +class base_uint +{ +protected: + enum { WIDTH=BITS/32 }; + uint32_t pn[WIDTH]; +public: + + bool operator!() const + { + for (int i = 0; i < WIDTH; i++) + if (pn[i] != 0) + return false; + return true; + } + + const base_uint operator~() const + { + base_uint ret; + for (int i = 0; i < WIDTH; i++) + ret.pn[i] = ~pn[i]; + return ret; + } + + const base_uint operator-() const + { + base_uint ret; + for (int i = 0; i < WIDTH; i++) + ret.pn[i] = ~pn[i]; + ret++; + return ret; + } + + double getdouble() const + { + double ret = 0.0; + double fact = 1.0; + for (int i = 0; i < WIDTH; i++) { + ret += fact * pn[i]; + fact *= 4294967296.0; + } + return ret; + } + + base_uint& operator=(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + base_uint& operator^=(const base_uint& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] ^= b.pn[i]; + return *this; + } + + base_uint& operator&=(const base_uint& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] &= b.pn[i]; + return *this; + } + + base_uint& operator|=(const base_uint& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] |= b.pn[i]; + return *this; + } + + base_uint& operator^=(uint64 b) + { + pn[0] ^= (unsigned int)b; + pn[1] ^= (unsigned int)(b >> 32); + return *this; + } + + base_uint& operator|=(uint64 b) + { + pn[0] |= (unsigned int)b; + pn[1] |= (unsigned int)(b >> 32); + return *this; + } + + base_uint& operator<<=(unsigned int shift) + { + base_uint a(*this); + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) + { + if (i+k+1 < WIDTH && shift != 0) + pn[i+k+1] |= (a.pn[i] >> (32-shift)); + if (i+k < WIDTH) + pn[i+k] |= (a.pn[i] << shift); + } + return *this; + } + + base_uint& operator>>=(unsigned int shift) + { + base_uint a(*this); + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) + { + if (i-k-1 >= 0 && shift != 0) + pn[i-k-1] |= (a.pn[i] << (32-shift)); + if (i-k >= 0) + pn[i-k] |= (a.pn[i] >> shift); + } + return *this; + } + + base_uint& operator+=(const base_uint& b) + { + uint64 carry = 0; + for (int i = 0; i < WIDTH; i++) + { + uint64 n = carry + pn[i] + b.pn[i]; + pn[i] = n & 0xffffffff; + carry = n >> 32; + } + return *this; + } + + base_uint& operator-=(const base_uint& b) + { + *this += -b; + return *this; + } + + base_uint& operator+=(uint64 b64) + { + base_uint b; + b = b64; + *this += b; + return *this; + } + + base_uint& operator-=(uint64 b64) + { + base_uint b; + b = b64; + *this += -b; + return *this; + } + + + base_uint& operator++() + { + // prefix operator + int i = 0; + while (++pn[i] == 0 && i < WIDTH-1) + i++; + return *this; + } + + const base_uint operator++(int) + { + // postfix operator + const base_uint ret = *this; + ++(*this); + return ret; + } + + base_uint& operator--() + { + // prefix operator + int i = 0; + while (--pn[i] == -1 && i < WIDTH-1) + i++; + return *this; + } + + const base_uint operator--(int) + { + // postfix operator + const base_uint ret = *this; + --(*this); + return ret; + } + + + friend inline bool operator<(const base_uint& a, const base_uint& b) + { + for (int i = base_uint::WIDTH-1; i >= 0; i--) + { + if (a.pn[i] < b.pn[i]) + return true; + else if (a.pn[i] > b.pn[i]) + return false; + } + return false; + } + + friend inline bool operator<=(const base_uint& a, const base_uint& b) + { + for (int i = base_uint::WIDTH-1; i >= 0; i--) + { + if (a.pn[i] < b.pn[i]) + return true; + else if (a.pn[i] > b.pn[i]) + return false; + } + return true; + } + + friend inline bool operator>(const base_uint& a, const base_uint& b) + { + for (int i = base_uint::WIDTH-1; i >= 0; i--) + { + if (a.pn[i] > b.pn[i]) + return true; + else if (a.pn[i] < b.pn[i]) + return false; + } + return false; + } + + friend inline bool operator>=(const base_uint& a, const base_uint& b) + { + for (int i = base_uint::WIDTH-1; i >= 0; i--) + { + if (a.pn[i] > b.pn[i]) + return true; + else if (a.pn[i] < b.pn[i]) + return false; + } + return true; + } + + friend inline bool operator==(const base_uint& a, const base_uint& b) + { + for (int i = 0; i < base_uint::WIDTH; i++) + if (a.pn[i] != b.pn[i]) + return false; + return true; + } + + friend inline bool operator==(const base_uint& a, uint64 b) + { + if (a.pn[0] != (unsigned int)b) + return false; + if (a.pn[1] != (unsigned int)(b >> 32)) + return false; + for (int i = 2; i < base_uint::WIDTH; i++) + if (a.pn[i] != 0) + return false; + return true; + } + + friend inline bool operator!=(const base_uint& a, const base_uint& b) + { + return (!(a == b)); + } + + friend inline bool operator!=(const base_uint& a, uint64 b) + { + return (!(a == b)); + } + + + + std::string GetHex() const + { + char psz[sizeof(pn)*2 + 1]; + for (unsigned int i = 0; i < sizeof(pn); i++) + sprintf(psz + i*2, "%02x", ((unsigned char*)pn)[sizeof(pn) - i - 1]); + return std::string(psz, psz + sizeof(pn)*2); + } + + void SetHex(const char* psz) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + + // skip leading spaces + while (isspace(*psz)) + psz++; + + // skip 0x + if (psz[0] == '0' && tolower(psz[1]) == 'x') + psz += 2; + + // hex string to uint + static const unsigned char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 }; + const char* pbegin = psz; + while (phexdigit[(unsigned char)*psz] || *psz == '0') + psz++; + psz--; + unsigned char* p1 = (unsigned char*)pn; + unsigned char* pend = p1 + WIDTH * 4; + while (psz >= pbegin && p1 < pend) + { + *p1 = phexdigit[(unsigned char)*psz--]; + if (psz >= pbegin) + { + *p1 |= (phexdigit[(unsigned char)*psz--] << 4); + p1++; + } + } + } + + void SetHex(const std::string& str) + { + SetHex(str.c_str()); + } + + std::string ToString() const + { + return (GetHex()); + } + + unsigned char* begin() + { + return (unsigned char*)&pn[0]; + } + + unsigned char* end() + { + return (unsigned char*)&pn[WIDTH]; + } + + const unsigned char* begin() const + { + return (unsigned char*)&pn[0]; + } + + const unsigned char* end() const + { + return (unsigned char*)&pn[WIDTH]; + } + + unsigned int size() const + { + return sizeof(pn); + } + + uint64 Get64(int n=0) const + { + return pn[2*n] | (uint64)pn[2*n+1] << 32; + } + +// unsigned int GetSerializeSize(int nType=0, int nVersion=PROTOCOL_VERSION) const + unsigned int GetSerializeSize(int nType, int nVersion) const + { + return sizeof(pn); + } + + template +// void Serialize(Stream& s, int nType=0, int nVersion=PROTOCOL_VERSION) const + void Serialize(Stream& s, int nType, int nVersion) const + { + s.write((char*)pn, sizeof(pn)); + } + + template +// void Unserialize(Stream& s, int nType=0, int nVersion=PROTOCOL_VERSION) + void Unserialize(Stream& s, int nType, int nVersion) + { + s.read((char*)pn, sizeof(pn)); + } + + + friend class uint160; + friend class uint256; + friend inline int Testuint256AdHoc(std::vector vArg); +}; + +typedef base_uint<160> base_uint160; +typedef base_uint<256> base_uint256; + + + +// +// uint160 and uint256 could be implemented as templates, but to keep +// compile errors and debugging cleaner, they're copy and pasted. +// + + + +////////////////////////////////////////////////////////////////////////////// +// +// uint160 +// + +/** 160-bit unsigned integer */ +class uint160 : public base_uint160 +{ +public: + typedef base_uint160 basetype; + + uint160() + { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + } + + uint160(const basetype& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + } + + uint160& operator=(const basetype& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + return *this; + } + + uint160(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + } + + uint160& operator=(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + explicit uint160(const std::string& str) + { + SetHex(str); + } + + explicit uint160(const std::vector& vch) + { + if (vch.size() == sizeof(pn)) + memcpy(pn, &vch[0], sizeof(pn)); + else + *this = 0; + } +}; + +inline bool operator==(const uint160& a, uint64 b) { return (base_uint160)a == b; } +inline bool operator!=(const uint160& a, uint64 b) { return (base_uint160)a != b; } +inline const uint160 operator<<(const base_uint160& a, unsigned int shift) { return uint160(a) <<= shift; } +inline const uint160 operator>>(const base_uint160& a, unsigned int shift) { return uint160(a) >>= shift; } +inline const uint160 operator<<(const uint160& a, unsigned int shift) { return uint160(a) <<= shift; } +inline const uint160 operator>>(const uint160& a, unsigned int shift) { return uint160(a) >>= shift; } + +inline const uint160 operator^(const base_uint160& a, const base_uint160& b) { return uint160(a) ^= b; } +inline const uint160 operator&(const base_uint160& a, const base_uint160& b) { return uint160(a) &= b; } +inline const uint160 operator|(const base_uint160& a, const base_uint160& b) { return uint160(a) |= b; } +inline const uint160 operator+(const base_uint160& a, const base_uint160& b) { return uint160(a) += b; } +inline const uint160 operator-(const base_uint160& a, const base_uint160& b) { return uint160(a) -= b; } + +inline bool operator<(const base_uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; } +inline bool operator<=(const base_uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; } +inline bool operator>(const base_uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; } +inline bool operator>=(const base_uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; } +inline bool operator==(const base_uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; } +inline bool operator!=(const base_uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; } +inline const uint160 operator^(const base_uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; } +inline const uint160 operator&(const base_uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; } +inline const uint160 operator|(const base_uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; } +inline const uint160 operator+(const base_uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; } +inline const uint160 operator-(const base_uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; } + +inline bool operator<(const uint160& a, const base_uint160& b) { return (base_uint160)a < (base_uint160)b; } +inline bool operator<=(const uint160& a, const base_uint160& b) { return (base_uint160)a <= (base_uint160)b; } +inline bool operator>(const uint160& a, const base_uint160& b) { return (base_uint160)a > (base_uint160)b; } +inline bool operator>=(const uint160& a, const base_uint160& b) { return (base_uint160)a >= (base_uint160)b; } +inline bool operator==(const uint160& a, const base_uint160& b) { return (base_uint160)a == (base_uint160)b; } +inline bool operator!=(const uint160& a, const base_uint160& b) { return (base_uint160)a != (base_uint160)b; } +inline const uint160 operator^(const uint160& a, const base_uint160& b) { return (base_uint160)a ^ (base_uint160)b; } +inline const uint160 operator&(const uint160& a, const base_uint160& b) { return (base_uint160)a & (base_uint160)b; } +inline const uint160 operator|(const uint160& a, const base_uint160& b) { return (base_uint160)a | (base_uint160)b; } +inline const uint160 operator+(const uint160& a, const base_uint160& b) { return (base_uint160)a + (base_uint160)b; } +inline const uint160 operator-(const uint160& a, const base_uint160& b) { return (base_uint160)a - (base_uint160)b; } + +inline bool operator<(const uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; } +inline bool operator<=(const uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; } +inline bool operator>(const uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; } +inline bool operator>=(const uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; } +inline bool operator==(const uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; } +inline bool operator!=(const uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; } +inline const uint160 operator^(const uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; } +inline const uint160 operator&(const uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; } +inline const uint160 operator|(const uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; } +inline const uint160 operator+(const uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; } +inline const uint160 operator-(const uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; } + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// uint256 +// + +/** 256-bit unsigned integer */ +class uint256 : public base_uint256 +{ +public: + typedef base_uint256 basetype; + + uint256() + { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + } + + uint256(const basetype& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + } + + uint256& operator=(const basetype& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + return *this; + } + + uint256(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + } + + uint256& operator=(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + explicit uint256(const std::string& str) + { + SetHex(str); + } + + explicit uint256(const std::vector& vch) + { + if (vch.size() == sizeof(pn)) + memcpy(pn, &vch[0], sizeof(pn)); + else + *this = 0; + } +}; + +inline bool operator==(const uint256& a, uint64 b) { return (base_uint256)a == b; } +inline bool operator!=(const uint256& a, uint64 b) { return (base_uint256)a != b; } +inline const uint256 operator<<(const base_uint256& a, unsigned int shift) { return uint256(a) <<= shift; } +inline const uint256 operator>>(const base_uint256& a, unsigned int shift) { return uint256(a) >>= shift; } +inline const uint256 operator<<(const uint256& a, unsigned int shift) { return uint256(a) <<= shift; } +inline const uint256 operator>>(const uint256& a, unsigned int shift) { return uint256(a) >>= shift; } + +inline const uint256 operator^(const base_uint256& a, const base_uint256& b) { return uint256(a) ^= b; } +inline const uint256 operator&(const base_uint256& a, const base_uint256& b) { return uint256(a) &= b; } +inline const uint256 operator|(const base_uint256& a, const base_uint256& b) { return uint256(a) |= b; } +inline const uint256 operator+(const base_uint256& a, const base_uint256& b) { return uint256(a) += b; } +inline const uint256 operator-(const base_uint256& a, const base_uint256& b) { return uint256(a) -= b; } + +inline bool operator<(const base_uint256& a, const uint256& b) { return (base_uint256)a < (base_uint256)b; } +inline bool operator<=(const base_uint256& a, const uint256& b) { return (base_uint256)a <= (base_uint256)b; } +inline bool operator>(const base_uint256& a, const uint256& b) { return (base_uint256)a > (base_uint256)b; } +inline bool operator>=(const base_uint256& a, const uint256& b) { return (base_uint256)a >= (base_uint256)b; } +inline bool operator==(const base_uint256& a, const uint256& b) { return (base_uint256)a == (base_uint256)b; } +inline bool operator!=(const base_uint256& a, const uint256& b) { return (base_uint256)a != (base_uint256)b; } +inline const uint256 operator^(const base_uint256& a, const uint256& b) { return (base_uint256)a ^ (base_uint256)b; } +inline const uint256 operator&(const base_uint256& a, const uint256& b) { return (base_uint256)a & (base_uint256)b; } +inline const uint256 operator|(const base_uint256& a, const uint256& b) { return (base_uint256)a | (base_uint256)b; } +inline const uint256 operator+(const base_uint256& a, const uint256& b) { return (base_uint256)a + (base_uint256)b; } +inline const uint256 operator-(const base_uint256& a, const uint256& b) { return (base_uint256)a - (base_uint256)b; } + +inline bool operator<(const uint256& a, const base_uint256& b) { return (base_uint256)a < (base_uint256)b; } +inline bool operator<=(const uint256& a, const base_uint256& b) { return (base_uint256)a <= (base_uint256)b; } +inline bool operator>(const uint256& a, const base_uint256& b) { return (base_uint256)a > (base_uint256)b; } +inline bool operator>=(const uint256& a, const base_uint256& b) { return (base_uint256)a >= (base_uint256)b; } +inline bool operator==(const uint256& a, const base_uint256& b) { return (base_uint256)a == (base_uint256)b; } +inline bool operator!=(const uint256& a, const base_uint256& b) { return (base_uint256)a != (base_uint256)b; } +inline const uint256 operator^(const uint256& a, const base_uint256& b) { return (base_uint256)a ^ (base_uint256)b; } +inline const uint256 operator&(const uint256& a, const base_uint256& b) { return (base_uint256)a & (base_uint256)b; } +inline const uint256 operator|(const uint256& a, const base_uint256& b) { return (base_uint256)a | (base_uint256)b; } +inline const uint256 operator+(const uint256& a, const base_uint256& b) { return (base_uint256)a + (base_uint256)b; } +inline const uint256 operator-(const uint256& a, const base_uint256& b) { return (base_uint256)a - (base_uint256)b; } + +inline bool operator<(const uint256& a, const uint256& b) { return (base_uint256)a < (base_uint256)b; } +inline bool operator<=(const uint256& a, const uint256& b) { return (base_uint256)a <= (base_uint256)b; } +inline bool operator>(const uint256& a, const uint256& b) { return (base_uint256)a > (base_uint256)b; } +inline bool operator>=(const uint256& a, const uint256& b) { return (base_uint256)a >= (base_uint256)b; } +inline bool operator==(const uint256& a, const uint256& b) { return (base_uint256)a == (base_uint256)b; } +inline bool operator!=(const uint256& a, const uint256& b) { return (base_uint256)a != (base_uint256)b; } +inline const uint256 operator^(const uint256& a, const uint256& b) { return (base_uint256)a ^ (base_uint256)b; } +inline const uint256 operator&(const uint256& a, const uint256& b) { return (base_uint256)a & (base_uint256)b; } +inline const uint256 operator|(const uint256& a, const uint256& b) { return (base_uint256)a | (base_uint256)b; } +inline const uint256 operator+(const uint256& a, const uint256& b) { return (base_uint256)a + (base_uint256)b; } +inline const uint256 operator-(const uint256& a, const uint256& b) { return (base_uint256)a - (base_uint256)b; } + + + + + + + + + + +#ifdef TEST_UINT256 + +inline int Testuint256AdHoc(std::vector vArg) +{ + uint256 g(0); + + + printf("%s\n", g.ToString().c_str()); + g--; printf("g--\n"); + printf("%s\n", g.ToString().c_str()); + g--; printf("g--\n"); + printf("%s\n", g.ToString().c_str()); + g++; printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + g++; printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + g++; printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + g++; printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + + + + uint256 a(7); + printf("a=7\n"); + printf("%s\n", a.ToString().c_str()); + + uint256 b; + printf("b undefined\n"); + printf("%s\n", b.ToString().c_str()); + int c = 3; + + a = c; + a.pn[3] = 15; + printf("%s\n", a.ToString().c_str()); + uint256 k(c); + + a = 5; + a.pn[3] = 15; + printf("%s\n", a.ToString().c_str()); + b = 1; + b <<= 52; + + a |= b; + + a ^= 0x500; + + printf("a %s\n", a.ToString().c_str()); + + a = a | b | (uint256)0x1000; + + + printf("a %s\n", a.ToString().c_str()); + printf("b %s\n", b.ToString().c_str()); + + a = 0xfffffffe; + a.pn[4] = 9; + + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + + a--; + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + uint256 d = a--; + printf("%s\n", d.ToString().c_str()); + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + + d = a; + + printf("%s\n", d.ToString().c_str()); + for (int i = uint256::WIDTH-1; i >= 0; i--) printf("%08x", d.pn[i]); printf("\n"); + + uint256 neg = d; + neg = ~neg; + printf("%s\n", neg.ToString().c_str()); + + + uint256 e = uint256("0xABCDEF123abcdef12345678909832180000011111111"); + printf("\n"); + printf("%s\n", e.ToString().c_str()); + + + printf("\n"); + uint256 x1 = uint256("0xABCDEF123abcdef12345678909832180000011111111"); + uint256 x2; + printf("%s\n", x1.ToString().c_str()); + for (int i = 0; i < 270; i += 4) + { + x2 = x1 << i; + printf("%s\n", x2.ToString().c_str()); + } + + printf("\n"); + printf("%s\n", x1.ToString().c_str()); + for (int i = 0; i < 270; i += 4) + { + x2 = x1; + x2 >>= i; + printf("%s\n", x2.ToString().c_str()); + } + + + for (int i = 0; i < 100; i++) + { + uint256 k = (~uint256(0) >> i); + printf("%s\n", k.ToString().c_str()); + } + + for (int i = 0; i < 100; i++) + { + uint256 k = (~uint256(0) << i); + printf("%s\n", k.ToString().c_str()); + } + + return (0); +} + +#endif + +#endif diff --git a/src/util.cpp b/src/util.cpp new file mode 100644 index 0000000..39585a4 --- /dev/null +++ b/src/util.cpp @@ -0,0 +1,1564 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef WIN32 +// for posix_fallocate +#ifdef __linux__ +#define _POSIX_C_SOURCE 200112L +#endif +#include +#include +#include +#endif + +// added stuff for gravity update + +#include "util.h" +#include "sync.h" +#include "version.h" +#include "ui_interface.h" +#include +#include // for to_lower() +#include // for startswith() and endswith() + +// Work around clang compilation problem in Boost 1.46: +// /usr/include/boost/program_options/detail/config_file.hpp:163:17: error: call to function 'to_internal' that is neither visible in the template definition nor found by argument-dependent lookup +// See also: http://stackoverflow.com/questions/10020179/compilation-fail-in-boost-librairies-program-options +// http://clang.debian.net/status.php?version=3.0&key=CANNOT_FIND_FUNCTION +namespace boost { + namespace program_options { + std::string to_internal(const std::string&); + } +} + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +#ifdef _MSC_VER +#pragma warning(disable:4786) +#pragma warning(disable:4804) +#pragma warning(disable:4805) +#pragma warning(disable:4717) +#endif +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0501 +#ifdef _WIN32_IE +#undef _WIN32_IE +#endif +#define _WIN32_IE 0x0501 +#define WIN32_LEAN_AND_MEAN 1 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include /* for _commit */ +#include "shlobj.h" +#elif defined(__linux__) +# include +#endif + +using namespace std; + +map mapArgs; +map > mapMultiArgs; +bool fDebug = false; +bool fDebugNet = false; +bool fPrintToConsole = false; +bool fPrintToDebugger = false; +bool fDaemon = false; +bool fServer = false; +bool fCommandLine = false; +string strMiscWarning; +bool fTestNet = false; +bool fBloomFilters = true; +bool fNoListen = false; +bool fLogTimestamps = false; +CMedianFilter vTimeOffsets(200,0); +volatile bool fReopenDebugLog = false; +bool fCachedPath[2] = {false, false}; + +// Init OpenSSL library multithreading support +static CCriticalSection** ppmutexOpenSSL; +void locking_callback(int mode, int i, const char* file, int line) +{ + if (mode & CRYPTO_LOCK) { + ENTER_CRITICAL_SECTION(*ppmutexOpenSSL[i]); + } else { + LEAVE_CRITICAL_SECTION(*ppmutexOpenSSL[i]); + } +} + +LockedPageManager LockedPageManager::instance; + +// Init +class CInit +{ +public: + CInit() + { + // Init OpenSSL library multithreading support + ppmutexOpenSSL = (CCriticalSection**)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(CCriticalSection*)); + for (int i = 0; i < CRYPTO_num_locks(); i++) + ppmutexOpenSSL[i] = new CCriticalSection(); + CRYPTO_set_locking_callback(locking_callback); + +#ifdef WIN32 + // Seed random number generator with screen scrape and other hardware sources + RAND_screen(); +#endif + + // Seed random number generator with performance counter + RandAddSeed(); + } + ~CInit() + { + // Shutdown OpenSSL library multithreading support + CRYPTO_set_locking_callback(NULL); + for (int i = 0; i < CRYPTO_num_locks(); i++) + delete ppmutexOpenSSL[i]; + OPENSSL_free(ppmutexOpenSSL); + } +} +instance_of_cinit; + + + + + + + + +void RandAddSeed() +{ + // Seed with CPU performance counter + int64 nCounter = GetPerformanceCounter(); + RAND_add(&nCounter, sizeof(nCounter), 1.5); + memset(&nCounter, 0, sizeof(nCounter)); +} + +void RandAddSeedPerfmon() +{ + RandAddSeed(); + + // This can take up to 2 seconds, so only do it every 10 minutes + static int64 nLastPerfmon; + if (GetTime() < nLastPerfmon + 10 * 60) + return; + nLastPerfmon = GetTime(); + +#ifdef WIN32 + // Don't need this on Linux, OpenSSL automatically uses /dev/urandom + // Seed with the entire set of perfmon data + unsigned char pdata[250000]; + memset(pdata, 0, sizeof(pdata)); + unsigned long nSize = sizeof(pdata); + long ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, pdata, &nSize); + RegCloseKey(HKEY_PERFORMANCE_DATA); + if (ret == ERROR_SUCCESS) + { + RAND_add(pdata, nSize, nSize/100.0); + OPENSSL_cleanse(pdata, nSize); + printf("RandAddSeed() %lu bytes\n", nSize); + } +#endif +} + +uint64 GetRand(uint64 nMax) +{ + if (nMax == 0) + return 0; + + // The range of the random source must be a multiple of the modulus + // to give every possible output value an equal possibility + uint64 nRange = (std::numeric_limits::max() / nMax) * nMax; + uint64 nRand = 0; + do + RAND_bytes((unsigned char*)&nRand, sizeof(nRand)); + while (nRand >= nRange); + return (nRand % nMax); +} + +int GetRandInt(int nMax) +{ + return GetRand(nMax); +} + +uint256 GetRandHash() +{ + uint256 hash; + RAND_bytes((unsigned char*)&hash, sizeof(hash)); + return hash; +} + + + + + + + +// +// OutputDebugStringF (aka printf -- there is a #define that we really +// should get rid of one day) has been broken a couple of times now +// by well-meaning people adding mutexes in the most straightforward way. +// It breaks because it may be called by global destructors during shutdown. +// Since the order of destruction of static/global objects is undefined, +// defining a mutex as a global object doesn't work (the mutex gets +// destroyed, and then some later destructor calls OutputDebugStringF, +// maybe indirectly, and you get a core dump at shutdown trying to lock +// the mutex). + +static boost::once_flag debugPrintInitFlag = BOOST_ONCE_INIT; +// We use boost::call_once() to make sure these are initialized in +// in a thread-safe manner the first time it is called: +static FILE* fileout = NULL; +static boost::mutex* mutexDebugLog = NULL; + +static void DebugPrintInit() +{ + assert(fileout == NULL); + assert(mutexDebugLog == NULL); + + boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; + fileout = fopen(pathDebug.string().c_str(), "a"); + if (fileout) setbuf(fileout, NULL); // unbuffered + + mutexDebugLog = new boost::mutex(); +} + +int OutputDebugStringF(const char* pszFormat, ...) +{ + int ret = 0; // Returns total number of characters written + if (fPrintToConsole) + { + // print to console + va_list arg_ptr; + va_start(arg_ptr, pszFormat); + ret += vprintf(pszFormat, arg_ptr); + va_end(arg_ptr); + } + else if (!fPrintToDebugger) + { + static bool fStartedNewLine = true; + boost::call_once(&DebugPrintInit, debugPrintInitFlag); + + if (fileout == NULL) + return ret; + + boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); + + // reopen the log file, if requested + if (fReopenDebugLog) { + fReopenDebugLog = false; + boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; + if (freopen(pathDebug.string().c_str(),"a",fileout) != NULL) + setbuf(fileout, NULL); // unbuffered + } + + // Debug print useful for profiling + if (fLogTimestamps && fStartedNewLine) + ret += fprintf(fileout, "%s ", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()).c_str()); + if (pszFormat[strlen(pszFormat) - 1] == '\n') + fStartedNewLine = true; + else + fStartedNewLine = false; + + va_list arg_ptr; + va_start(arg_ptr, pszFormat); + ret += vfprintf(fileout, pszFormat, arg_ptr); + va_end(arg_ptr); + } + +#ifdef WIN32 + if (fPrintToDebugger) + { + static CCriticalSection cs_OutputDebugStringF; + + // accumulate and output a line at a time + { + LOCK(cs_OutputDebugStringF); + static std::string buffer; + + va_list arg_ptr; + va_start(arg_ptr, pszFormat); + buffer += vstrprintf(pszFormat, arg_ptr); + va_end(arg_ptr); + + int line_start = 0, line_end; + while((line_end = buffer.find('\n', line_start)) != -1) + { + OutputDebugStringA(buffer.substr(line_start, line_end - line_start).c_str()); + line_start = line_end + 1; + ret += line_end-line_start; + } + buffer.erase(0, line_start); + } + } +#endif + return ret; +} + +string vstrprintf(const char *format, va_list ap) +{ + char buffer[50000]; + char* p = buffer; + int limit = sizeof(buffer); + int ret; + loop + { + va_list arg_ptr; + va_copy(arg_ptr, ap); +#ifdef WIN32 + ret = _vsnprintf(p, limit, format, arg_ptr); +#else + ret = vsnprintf(p, limit, format, arg_ptr); +#endif + va_end(arg_ptr); + if (ret >= 0 && ret < limit) + break; + if (p != buffer) + delete[] p; + limit *= 2; + p = new char[limit]; + if (p == NULL) + throw std::bad_alloc(); + } + string str(p, p+ret); + if (p != buffer) + delete[] p; + return str; +} + +string real_strprintf(const char *format, int dummy, ...) +{ + va_list arg_ptr; + va_start(arg_ptr, dummy); + string str = vstrprintf(format, arg_ptr); + va_end(arg_ptr); + return str; +} + +string real_strprintf(const std::string &format, int dummy, ...) +{ + va_list arg_ptr; + va_start(arg_ptr, dummy); + string str = vstrprintf(format.c_str(), arg_ptr); + va_end(arg_ptr); + return str; +} + +bool error(const char *format, ...) +{ + va_list arg_ptr; + va_start(arg_ptr, format); + std::string str = vstrprintf(format, arg_ptr); + va_end(arg_ptr); + printf("ERROR: %s\n", str.c_str()); + return false; +} + + +void ParseString(const string& str, char c, vector& v) +{ + if (str.empty()) + return; + string::size_type i1 = 0; + string::size_type i2; + loop + { + i2 = str.find(c, i1); + if (i2 == str.npos) + { + v.push_back(str.substr(i1)); + return; + } + v.push_back(str.substr(i1, i2-i1)); + i1 = i2+1; + } +} + + +string FormatMoney(int64 n, bool fPlus) +{ + // Note: not using straight sprintf here because we do NOT want + // localized number formatting. + int64 n_abs = (n > 0 ? n : -n); + int64 quotient = n_abs/COIN; + int64 remainder = n_abs%COIN; + string str = strprintf("%"PRI64d".%08"PRI64d, quotient, remainder); + + // Right-trim excess zeros before the decimal point: + int nTrim = 0; + for (int i = str.size()-1; (str[i] == '0' && isdigit(str[i-2])); --i) + ++nTrim; + if (nTrim) + str.erase(str.size()-nTrim, nTrim); + + if (n < 0) + str.insert((unsigned int)0, 1, '-'); + else if (fPlus && n > 0) + str.insert((unsigned int)0, 1, '+'); + return str; +} + + +bool ParseMoney(const string& str, int64& nRet) +{ + return ParseMoney(str.c_str(), nRet); +} + +bool ParseMoney(const char* pszIn, int64& nRet) +{ + string strWhole; + int64 nUnits = 0; + const char* p = pszIn; + while (isspace(*p)) + p++; + for (; *p; p++) + { + if (*p == '.') + { + p++; + int64 nMult = CENT*10; + while (isdigit(*p) && (nMult > 0)) + { + nUnits += nMult * (*p++ - '0'); + nMult /= 10; + } + break; + } + if (isspace(*p)) + break; + if (!isdigit(*p)) + return false; + strWhole.insert(strWhole.end(), *p); + } + for (; *p; p++) + if (!isspace(*p)) + return false; + if (strWhole.size() > 10) // guard against 63 bit overflow + return false; + if (nUnits < 0 || nUnits > COIN) + return false; + int64 nWhole = atoi64(strWhole); + int64 nValue = nWhole*COIN + nUnits; + + nRet = nValue; + return true; +} + +// safeChars chosen to allow simple messages/URLs/email addresses, but avoid anything +// even possibly remotely dangerous like & or > +static string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 .,;_/:?@"); +string SanitizeString(const string& str) +{ + string strResult; + for (std::string::size_type i = 0; i < str.size(); i++) + { + if (safeChars.find(str[i]) != std::string::npos) + strResult.push_back(str[i]); + } + return strResult; +} + +static const signed char phexdigit[256] = +{ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1, + -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, }; + +bool IsHex(const string& str) +{ + BOOST_FOREACH(unsigned char c, str) + { + if (phexdigit[c] < 0) + return false; + } + return (str.size() > 0) && (str.size()%2 == 0); +} + +vector ParseHex(const char* psz) +{ + // convert hex dump to vector + vector vch; + loop + { + while (isspace(*psz)) + psz++; + signed char c = phexdigit[(unsigned char)*psz++]; + if (c == (signed char)-1) + break; + unsigned char n = (c << 4); + c = phexdigit[(unsigned char)*psz++]; + if (c == (signed char)-1) + break; + n |= c; + vch.push_back(n); + } + return vch; +} + +vector ParseHex(const string& str) +{ + return ParseHex(str.c_str()); +} + +static void InterpretNegativeSetting(string name, map& mapSettingsRet) +{ + // interpret -nofoo as -foo=0 (and -nofoo=0 as -foo=1) as long as -foo not set + if (name.find("-no") == 0) + { + std::string positive("-"); + positive.append(name.begin()+3, name.end()); + if (mapSettingsRet.count(positive) == 0) + { + bool value = !GetBoolArg(name); + mapSettingsRet[positive] = (value ? "1" : "0"); + } + } +} + +void ParseParameters(int argc, const char* const argv[]) +{ + mapArgs.clear(); + mapMultiArgs.clear(); + for (int i = 1; i < argc; i++) + { + std::string str(argv[i]); + std::string strValue; + size_t is_index = str.find('='); + if (is_index != std::string::npos) + { + strValue = str.substr(is_index+1); + str = str.substr(0, is_index); + } +#ifdef WIN32 + boost::to_lower(str); + if (boost::algorithm::starts_with(str, "/")) + str = "-" + str.substr(1); +#endif + if (str[0] != '-') + break; + + mapArgs[str] = strValue; + mapMultiArgs[str].push_back(strValue); + } + + // New 0.6 features: + BOOST_FOREACH(const PAIRTYPE(string,string)& entry, mapArgs) + { + string name = entry.first; + + // interpret --foo as -foo (as long as both are not set) + if (name.find("--") == 0) + { + std::string singleDash(name.begin()+1, name.end()); + if (mapArgs.count(singleDash) == 0) + mapArgs[singleDash] = entry.second; + name = singleDash; + } + + // interpret -nofoo as -foo=0 (and -nofoo=0 as -foo=1) as long as -foo not set + InterpretNegativeSetting(name, mapArgs); + } +} + +std::string GetArg(const std::string& strArg, const std::string& strDefault) +{ + if (mapArgs.count(strArg)) + return mapArgs[strArg]; + return strDefault; +} + +int64 GetArg(const std::string& strArg, int64 nDefault) +{ + if (mapArgs.count(strArg)) + return atoi64(mapArgs[strArg]); + return nDefault; +} + +bool GetBoolArg(const std::string& strArg, bool fDefault) +{ + if (mapArgs.count(strArg)) + { + if (mapArgs[strArg].empty()) + return true; + return (atoi(mapArgs[strArg]) != 0); + } + return fDefault; +} + +bool SoftSetArg(const std::string& strArg, const std::string& strValue) +{ + if (mapArgs.count(strArg)) + return false; + mapArgs[strArg] = strValue; + return true; +} + +bool SoftSetBoolArg(const std::string& strArg, bool fValue) +{ + if (fValue) + return SoftSetArg(strArg, std::string("1")); + else + return SoftSetArg(strArg, std::string("0")); +} + + +string EncodeBase64(const unsigned char* pch, size_t len) +{ + static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + string strRet=""; + strRet.reserve((len+2)/3*4); + + int mode=0, left=0; + const unsigned char *pchEnd = pch+len; + + while (pch> 2]; + left = (enc & 3) << 4; + mode = 1; + break; + + case 1: // we have two bits + strRet += pbase64[left | (enc >> 4)]; + left = (enc & 15) << 2; + mode = 2; + break; + + case 2: // we have four bits + strRet += pbase64[left | (enc >> 6)]; + strRet += pbase64[enc & 63]; + mode = 0; + break; + } + } + + if (mode) + { + strRet += pbase64[left]; + strRet += '='; + if (mode == 1) + strRet += '='; + } + + return strRet; +} + +string EncodeBase64(const string& str) +{ + return EncodeBase64((const unsigned char*)str.c_str(), str.size()); +} + +vector DecodeBase64(const char* p, bool* pfInvalid) +{ + static const int decode64_table[256] = + { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, + -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + + if (pfInvalid) + *pfInvalid = false; + + vector vchRet; + vchRet.reserve(strlen(p)*3/4); + + int mode = 0; + int left = 0; + + while (1) + { + int dec = decode64_table[(unsigned char)*p]; + if (dec == -1) break; + p++; + switch (mode) + { + case 0: // we have no bits and get 6 + left = dec; + mode = 1; + break; + + case 1: // we have 6 bits and keep 4 + vchRet.push_back((left<<2) | (dec>>4)); + left = dec & 15; + mode = 2; + break; + + case 2: // we have 4 bits and get 6, we keep 2 + vchRet.push_back((left<<4) | (dec>>2)); + left = dec & 3; + mode = 3; + break; + + case 3: // we have 2 bits and get 6 + vchRet.push_back((left<<6) | dec); + mode = 0; + break; + } + } + + if (pfInvalid) + switch (mode) + { + case 0: // 4n base64 characters processed: ok + break; + + case 1: // 4n+1 base64 character processed: impossible + *pfInvalid = true; + break; + + case 2: // 4n+2 base64 characters processed: require '==' + if (left || p[0] != '=' || p[1] != '=' || decode64_table[(unsigned char)p[2]] != -1) + *pfInvalid = true; + break; + + case 3: // 4n+3 base64 characters processed: require '=' + if (left || p[0] != '=' || decode64_table[(unsigned char)p[1]] != -1) + *pfInvalid = true; + break; + } + + return vchRet; +} + +string DecodeBase64(const string& str) +{ + vector vchRet = DecodeBase64(str.c_str()); + return string((const char*)&vchRet[0], vchRet.size()); +} + +string EncodeBase32(const unsigned char* pch, size_t len) +{ + static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567"; + + string strRet=""; + strRet.reserve((len+4)/5*8); + + int mode=0, left=0; + const unsigned char *pchEnd = pch+len; + + while (pch> 3]; + left = (enc & 7) << 2; + mode = 1; + break; + + case 1: // we have three bits + strRet += pbase32[left | (enc >> 6)]; + strRet += pbase32[(enc >> 1) & 31]; + left = (enc & 1) << 4; + mode = 2; + break; + + case 2: // we have one bit + strRet += pbase32[left | (enc >> 4)]; + left = (enc & 15) << 1; + mode = 3; + break; + + case 3: // we have four bits + strRet += pbase32[left | (enc >> 7)]; + strRet += pbase32[(enc >> 2) & 31]; + left = (enc & 3) << 3; + mode = 4; + break; + + case 4: // we have two bits + strRet += pbase32[left | (enc >> 5)]; + strRet += pbase32[enc & 31]; + mode = 0; + } + } + + static const int nPadding[5] = {0, 6, 4, 3, 1}; + if (mode) + { + strRet += pbase32[left]; + for (int n=0; n DecodeBase32(const char* p, bool* pfInvalid) +{ + static const int decode32_table[256] = + { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + + if (pfInvalid) + *pfInvalid = false; + + vector vchRet; + vchRet.reserve((strlen(p))*5/8); + + int mode = 0; + int left = 0; + + while (1) + { + int dec = decode32_table[(unsigned char)*p]; + if (dec == -1) break; + p++; + switch (mode) + { + case 0: // we have no bits and get 5 + left = dec; + mode = 1; + break; + + case 1: // we have 5 bits and keep 2 + vchRet.push_back((left<<3) | (dec>>2)); + left = dec & 3; + mode = 2; + break; + + case 2: // we have 2 bits and keep 7 + left = left << 5 | dec; + mode = 3; + break; + + case 3: // we have 7 bits and keep 4 + vchRet.push_back((left<<1) | (dec>>4)); + left = dec & 15; + mode = 4; + break; + + case 4: // we have 4 bits, and keep 1 + vchRet.push_back((left<<4) | (dec>>1)); + left = dec & 1; + mode = 5; + break; + + case 5: // we have 1 bit, and keep 6 + left = left << 5 | dec; + mode = 6; + break; + + case 6: // we have 6 bits, and keep 3 + vchRet.push_back((left<<2) | (dec>>3)); + left = dec & 7; + mode = 7; + break; + + case 7: // we have 3 bits, and keep 0 + vchRet.push_back((left<<5) | dec); + mode = 0; + break; + } + } + + if (pfInvalid) + switch (mode) + { + case 0: // 8n base32 characters processed: ok + break; + + case 1: // 8n+1 base32 characters processed: impossible + case 3: // +3 + case 6: // +6 + *pfInvalid = true; + break; + + case 2: // 8n+2 base32 characters processed: require '======' + if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || p[4] != '=' || p[5] != '=' || decode32_table[(unsigned char)p[6]] != -1) + *pfInvalid = true; + break; + + case 4: // 8n+4 base32 characters processed: require '====' + if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || decode32_table[(unsigned char)p[4]] != -1) + *pfInvalid = true; + break; + + case 5: // 8n+5 base32 characters processed: require '===' + if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || decode32_table[(unsigned char)p[3]] != -1) + *pfInvalid = true; + break; + + case 7: // 8n+7 base32 characters processed: require '=' + if (left || p[0] != '=' || decode32_table[(unsigned char)p[1]] != -1) + *pfInvalid = true; + break; + } + + return vchRet; +} + +string DecodeBase32(const string& str) +{ + vector vchRet = DecodeBase32(str.c_str()); + return string((const char*)&vchRet[0], vchRet.size()); +} + + +bool WildcardMatch(const char* psz, const char* mask) +{ + loop + { + switch (*mask) + { + case '\0': + return (*psz == '\0'); + case '*': + return WildcardMatch(psz, mask+1) || (*psz && WildcardMatch(psz+1, mask)); + case '?': + if (*psz == '\0') + return false; + break; + default: + if (*psz != *mask) + return false; + break; + } + psz++; + mask++; + } +} + +bool WildcardMatch(const string& str, const string& mask) +{ + return WildcardMatch(str.c_str(), mask.c_str()); +} + + + + + + + + +static std::string FormatException(std::exception* pex, const char* pszThread) +{ +#ifdef WIN32 + char pszModule[MAX_PATH] = ""; + GetModuleFileNameA(NULL, pszModule, sizeof(pszModule)); +#else + const char* pszModule = "CryptoLottoToken"; +#endif + if (pex) + return strprintf( + "EXCEPTION: %s \n%s \n%s in %s \n", typeid(*pex).name(), pex->what(), pszModule, pszThread); + else + return strprintf( + "UNKNOWN EXCEPTION \n%s in %s \n", pszModule, pszThread); +} + +void LogException(std::exception* pex, const char* pszThread) +{ + std::string message = FormatException(pex, pszThread); + printf("\n%s", message.c_str()); +} + +void PrintException(std::exception* pex, const char* pszThread) +{ + std::string message = FormatException(pex, pszThread); + printf("\n\n************************\n%s\n", message.c_str()); + fprintf(stderr, "\n\n************************\n%s\n", message.c_str()); + strMiscWarning = message; + throw; +} + +void PrintExceptionContinue(std::exception* pex, const char* pszThread) +{ + std::string message = FormatException(pex, pszThread); + printf("\n\n************************\n%s\n", message.c_str()); + fprintf(stderr, "\n\n************************\n%s\n", message.c_str()); + strMiscWarning = message; +} + +boost::filesystem::path GetDefaultDataDir() +{ + namespace fs = boost::filesystem; + // Windows < Vista: C:\Documents and Settings\Username\Application Data\Bitcoin + // Windows >= Vista: C:\Users\Username\AppData\Roaming\Bitcoin + // Mac: ~/Library/Application Support/Bitcoin + // Unix: ~/.bitcoin +#ifdef WIN32 + // Windows + return GetSpecialFolderPath(CSIDL_APPDATA) / "CryptoLottoToken"; +#else + fs::path pathRet; + char* pszHome = getenv("HOME"); + if (pszHome == NULL || strlen(pszHome) == 0) + pathRet = fs::path("/"); + else + pathRet = fs::path(pszHome); +#ifdef MAC_OSX + // Mac + pathRet /= "Library/Application Support"; + fs::create_directory(pathRet); + return pathRet / "CryptoLottoToken"; +#else + // Unix + return pathRet / ".CryptoLottoToken"; +#endif +#endif +} + +const boost::filesystem::path &GetDataDir(bool fNetSpecific) +{ + namespace fs = boost::filesystem; + + static fs::path pathCached[2]; + static CCriticalSection csPathCached; + + fs::path &path = pathCached[fNetSpecific]; + + // This can be called during exceptions by printf, so we cache the + // value so we don't have to do memory allocations after that. + if (fCachedPath[fNetSpecific]) + return path; + + LOCK(csPathCached); + + if (mapArgs.count("-datadir")) { + path = fs::system_complete(mapArgs["-datadir"]); + if (!fs::is_directory(path)) { + path = ""; + return path; + } + } else { + path = GetDefaultDataDir(); + } + if (fNetSpecific && GetBoolArg("-testnet", false)) + path /= "testnet3"; + + fs::create_directories(path); + + fCachedPath[fNetSpecific] = true; + return path; +} + +boost::filesystem::path GetConfigFile() +{ + boost::filesystem::path pathConfigFile(GetArg("-conf", "CryptoLottoToken.conf")); + if (!pathConfigFile.is_complete()) pathConfigFile = GetDataDir(false) / pathConfigFile; + return pathConfigFile; +} + +void ReadConfigFile(map& mapSettingsRet, + map >& mapMultiSettingsRet) +{ + boost::filesystem::ifstream streamConfig(GetConfigFile()); + if (!streamConfig.good()) + return; // No bitcoin.conf file is OK + + // clear path cache after loading config file + fCachedPath[0] = fCachedPath[1] = false; + + set setOptions; + setOptions.insert("*"); + + for (boost::program_options::detail::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it) + { + // Don't overwrite existing settings so command line settings override bitcoin.conf + string strKey = string("-") + it->string_key; + if (mapSettingsRet.count(strKey) == 0) + { + mapSettingsRet[strKey] = it->value[0]; + // interpret nofoo=1 as foo=0 (and nofoo=0 as foo=1) as long as foo not set) + InterpretNegativeSetting(strKey, mapSettingsRet); + } + mapMultiSettingsRet[strKey].push_back(it->value[0]); + } +} + +boost::filesystem::path GetPidFile() +{ + boost::filesystem::path pathPidFile(GetArg("-pid", "CryptoLottoTokend.pid")); + if (!pathPidFile.is_complete()) pathPidFile = GetDataDir() / pathPidFile; + return pathPidFile; +} + +#ifndef WIN32 +void CreatePidFile(const boost::filesystem::path &path, pid_t pid) +{ + FILE* file = fopen(path.string().c_str(), "w"); + if (file) + { + fprintf(file, "%d\n", pid); + fclose(file); + } +} +#endif + +bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest) +{ +#ifdef WIN32 + return MoveFileExA(src.string().c_str(), dest.string().c_str(), + MOVEFILE_REPLACE_EXISTING); +#else + int rc = std::rename(src.string().c_str(), dest.string().c_str()); + return (rc == 0); +#endif /* WIN32 */ +} + +void FileCommit(FILE *fileout) +{ + fflush(fileout); // harmless if redundantly called +#ifdef WIN32 + _commit(_fileno(fileout)); +#else + #if defined(__linux__) || defined(__NetBSD__) + fdatasync(fileno(fileout)); + #elif defined(__APPLE__) && defined(F_FULLFSYNC) + fcntl(fileno(fileout), F_FULLFSYNC, 0); + #else + fsync(fileno(fileout)); + #endif +#endif +} + +int GetFilesize(FILE* file) +{ + int nSavePos = ftell(file); + int nFilesize = -1; + if (fseek(file, 0, SEEK_END) == 0) + nFilesize = ftell(file); + fseek(file, nSavePos, SEEK_SET); + return nFilesize; +} + +bool TruncateFile(FILE *file, unsigned int length) { +#if defined(WIN32) + return _chsize(_fileno(file), length) == 0; +#else + return ftruncate(fileno(file), length) == 0; +#endif +} + + +// this function tries to raise the file descriptor limit to the requested number. +// It returns the actual file descriptor limit (which may be more or less than nMinFD) +int RaiseFileDescriptorLimit(int nMinFD) { +#if defined(WIN32) + return 2048; +#else + struct rlimit limitFD; + if (getrlimit(RLIMIT_NOFILE, &limitFD) != -1) { + if (limitFD.rlim_cur < (rlim_t)nMinFD) { + limitFD.rlim_cur = nMinFD; + if (limitFD.rlim_cur > limitFD.rlim_max) + limitFD.rlim_cur = limitFD.rlim_max; + setrlimit(RLIMIT_NOFILE, &limitFD); + getrlimit(RLIMIT_NOFILE, &limitFD); + } + return limitFD.rlim_cur; + } + return nMinFD; // getrlimit failed, assume it's fine +#endif +} + +// this function tries to make a particular range of a file allocated (corresponding to disk space) +// it is advisory, and the range specified in the arguments will never contain live data +void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) { +#if defined(WIN32) + // Windows-specific version + HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file)); + LARGE_INTEGER nFileSize; + int64 nEndPos = (int64)offset + length; + nFileSize.u.LowPart = nEndPos & 0xFFFFFFFF; + nFileSize.u.HighPart = nEndPos >> 32; + SetFilePointerEx(hFile, nFileSize, 0, FILE_BEGIN); + SetEndOfFile(hFile); +#elif defined(MAC_OSX) + // OSX specific version + fstore_t fst; + fst.fst_flags = F_ALLOCATECONTIG; + fst.fst_posmode = F_PEOFPOSMODE; + fst.fst_offset = 0; + fst.fst_length = (off_t)offset + length; + fst.fst_bytesalloc = 0; + if (fcntl(fileno(file), F_PREALLOCATE, &fst) == -1) { + fst.fst_flags = F_ALLOCATEALL; + fcntl(fileno(file), F_PREALLOCATE, &fst); + } + ftruncate(fileno(file), fst.fst_length); +#elif defined(__linux__) + // Version using posix_fallocate + off_t nEndPos = (off_t)offset + length; + posix_fallocate(fileno(file), 0, nEndPos); +#else + // Fallback version + // TODO: just write one byte per block + static const char buf[65536] = {}; + fseek(file, offset, SEEK_SET); + while (length > 0) { + unsigned int now = 65536; + if (length < now) + now = length; + fwrite(buf, 1, now, file); // allowed to fail; this function is advisory anyway + length -= now; + } +#endif +} + +void ShrinkDebugFile() +{ + // Scroll debug.log if it's getting too big + boost::filesystem::path pathLog = GetDataDir() / "debug.log"; + FILE* file = fopen(pathLog.string().c_str(), "r"); + if (file && GetFilesize(file) > 10 * 1000000) + { + // Restart the file with some of the end + char pch[200000]; + fseek(file, -sizeof(pch), SEEK_END); + int nBytes = fread(pch, 1, sizeof(pch), file); + fclose(file); + + file = fopen(pathLog.string().c_str(), "w"); + if (file) + { + fwrite(pch, 1, nBytes, file); + fclose(file); + } + } + else if(file != NULL) + fclose(file); +} + + + + + + + + +// +// "Never go to sea with two chronometers; take one or three." +// Our three time sources are: +// - System clock +// - Median of other nodes clocks +// - The user (asking the user to fix the system clock if the first two disagree) +// +static int64 nMockTime = 0; // For unit testing + +int64 GetTime() +{ + if (nMockTime) return nMockTime; + + return time(NULL); +} + +void SetMockTime(int64 nMockTimeIn) +{ + nMockTime = nMockTimeIn; +} + +static int64 nTimeOffset = 0; + +int64 GetTimeOffset() +{ + return nTimeOffset; +} + +int64 GetAdjustedTime() +{ + return GetTime() + GetTimeOffset(); +} + +void AddTimeData(const CNetAddr& ip, int64 nTime) +{ + int64 nOffsetSample = nTime - GetTime(); + + // Ignore duplicates + static set setKnown; + if (!setKnown.insert(ip).second) + return; + + // Add data + vTimeOffsets.input(nOffsetSample); + printf("Added time data, samples %d, offset %+"PRI64d" (%+"PRI64d" minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60); + if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1) + { + int64 nMedian = vTimeOffsets.median(); + std::vector vSorted = vTimeOffsets.sorted(); + // Only let other nodes change our time by so much + if (abs64(nMedian) < 15 * 60) // CryptoLottoToken: changed maximum adjust to 15 mins to avoid letting peers change our time too much in case of an attack. + { + nTimeOffset = nMedian; + } + else + { + nTimeOffset = 0; + + static bool fDone; + if (!fDone) + { + // If nobody has a time different than ours but within 5 minutes of ours, give a warning + bool fMatch = false; + BOOST_FOREACH(int64 nOffset, vSorted) + if (nOffset != 0 && abs64(nOffset) < 5 * 60) + fMatch = true; + + if (!fMatch) + { + fDone = true; + string strMessage = _("Warning: Please check that your computer's date and time are correct! If your clock is wrong CryptoLottoToken will not work properly."); + strMiscWarning = strMessage; + printf("*** %s\n", strMessage.c_str()); + uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING); + } + } + } + if (fDebug) { + BOOST_FOREACH(int64 n, vSorted) + printf("%+"PRI64d" ", n); + printf("| "); + } + printf("nTimeOffset = %+"PRI64d" (%+"PRI64d" minutes)\n", nTimeOffset, nTimeOffset/60); + } +} + +uint32_t insecure_rand_Rz = 11; +uint32_t insecure_rand_Rw = 11; +void seed_insecure_rand(bool fDeterministic) +{ + //The seed values have some unlikely fixed points which we avoid. + if(fDeterministic) + { + insecure_rand_Rz = insecure_rand_Rw = 11; + } else { + uint32_t tmp; + do { + RAND_bytes((unsigned char*)&tmp, 4); + } while(tmp == 0 || tmp == 0x9068ffffU); + insecure_rand_Rz = tmp; + do { + RAND_bytes((unsigned char*)&tmp, 4); + } while(tmp == 0 || tmp == 0x464fffffU); + insecure_rand_Rw = tmp; + } +} + +static const long hextable[] = +{ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10-19 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 30-39 + -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, // 50-59 + -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, + 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 70-79 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, // 90-99 + 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 110-109 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 130-139 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 150-159 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 170-179 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 190-199 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 210-219 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 230-239 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1 +}; + +long hex2long(const char* hexString) +{ + long ret = 0; + + while (*hexString && ret >= 0) + { + ret = (ret << 4) | hextable[*hexString++]; + } + + return ret; +} + +string FormatVersion(int nVersion) +{ + if (nVersion%100 == 0) + return strprintf("%d.%d.%d", nVersion/1000000, (nVersion/10000)%100, (nVersion/100)%100); + else + return strprintf("%d.%d.%d.%d", nVersion/1000000, (nVersion/10000)%100, (nVersion/100)%100, nVersion%100); +} + +string FormatFullVersion() +{ + return CLIENT_BUILD; +} + +// Format the subversion field according to BIP 14 spec (https://en.bitcoin.it/wiki/BIP_0014) +std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector& comments) +{ + std::ostringstream ss; + ss << "/"; + ss << name << ":" << FormatVersion(nClientVersion); + if (!comments.empty()) + ss << "(" << boost::algorithm::join(comments, "; ") << ")"; + ss << "/"; + return ss.str(); +} + +#ifdef WIN32 +boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate) +{ + namespace fs = boost::filesystem; + + char pszPath[MAX_PATH] = ""; + + if(SHGetSpecialFolderPathA(NULL, pszPath, nFolder, fCreate)) + { + return fs::path(pszPath); + } + + printf("SHGetSpecialFolderPathA() failed, could not obtain requested path.\n"); + return fs::path(""); +} +#endif + +boost::filesystem::path GetTempPath() { +#if BOOST_FILESYSTEM_VERSION == 3 + return boost::filesystem::temp_directory_path(); +#else + // TODO: remove when we don't support filesystem v2 anymore + boost::filesystem::path path; +#ifdef WIN32 + char pszPath[MAX_PATH] = ""; + + if (GetTempPathA(MAX_PATH, pszPath)) + path = boost::filesystem::path(pszPath); +#else + path = boost::filesystem::path("/tmp"); +#endif + if (path.empty() || !boost::filesystem::is_directory(path)) { + printf("GetTempPath(): failed to find temp path\n"); + return boost::filesystem::path(""); + } + return path; +#endif +} + +void runCommand(std::string strCommand) +{ + int nErr = ::system(strCommand.c_str()); + if (nErr) + printf("runCommand error: system(%s) returned %d\n", strCommand.c_str(), nErr); +} + +void RenameThread(const char* name) +{ +#if defined(PR_SET_NAME) + // Only the first 15 characters are used (16 - NUL terminator) + ::prctl(PR_SET_NAME, name, 0, 0, 0); +#elif 0 && (defined(__FreeBSD__) || defined(__OpenBSD__)) + // TODO: This is currently disabled because it needs to be verified to work + // on FreeBSD or OpenBSD first. When verified the '0 &&' part can be + // removed. + pthread_set_name_np(pthread_self(), name); + +#elif defined(MAC_OSX) && defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + +// pthread_setname_np is XCode 10.6-and-later +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 + pthread_setname_np(name); +#endif + +#else + // Prevent warnings for unused parameters... + (void)name; +#endif +} + +bool NewThread(void(*pfn)(void*), void* parg) +{ + try + { + boost::thread(pfn, parg); // thread detaches when out of scope + } catch(boost::thread_resource_error &e) { + printf("Error creating thread: %s\n", e.what()); + return false; + } + return true; +} +// for the difficulty update in GetNextWorkRequired +void DoubleToNumeratorDenominator(double inDouble, long long *outNumerator, long long *outDenominator) +{ + double fPart; + int expo; // exponent + long long lExpo; + int i; + + fPart = frexp(inDouble, &expo); + for (i=0; i<300 && fPart != floor(fPart) ; i++) { fPart *= 2.0; expo--; } + + *outNumerator = (long long) fPart; + lExpo = 1LL << labs((long) expo); + if (expo > 0) { + *outNumerator *= lExpo; + *outDenominator = 1; + } + else { *outDenominator = lExpo; } +} \ No newline at end of file diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..c068c96 --- /dev/null +++ b/src/util.h @@ -0,0 +1,611 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_UTIL_H +#define BITCOIN_UTIL_H + +#include "uint256.h" + +#include +// added stuff for gravity update +#ifndef WIN32 +#include +#include +#include +#endif +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "netbase.h" // for AddTimeData + +typedef long long int64; +typedef unsigned long long uint64; + +static const int64 COIN = 100000000; +static const int64 CENT = 1000000; + +#define loop for (;;) +#define BEGIN(a) ((char*)&(a)) +#define END(a) ((char*)&((&(a))[1])) +#define UBEGIN(a) ((unsigned char*)&(a)) +#define UEND(a) ((unsigned char*)&((&(a))[1])) +#define ARRAYLEN(array) (sizeof(array)/sizeof((array)[0])) + +#ifndef PRI64d +#if defined(_MSC_VER) || defined(__MSVCRT__) +#define PRI64d "I64d" +#define PRI64u "I64u" +#define PRI64x "I64x" +#else +#define PRI64d "lld" +#define PRI64u "llu" +#define PRI64x "llx" +#endif +#endif + +/* Format characters for (s)size_t and ptrdiff_t */ +#if defined(_MSC_VER) || defined(__MSVCRT__) + /* (s)size_t and ptrdiff_t have the same size specifier in MSVC: + http://msdn.microsoft.com/en-us/library/tcxf1dw6%28v=vs.100%29.aspx + */ + #define PRIszx "Ix" + #define PRIszu "Iu" + #define PRIszd "Id" + #define PRIpdx "Ix" + #define PRIpdu "Iu" + #define PRIpdd "Id" +#else /* C99 standard */ + #define PRIszx "zx" + #define PRIszu "zu" + #define PRIszd "zd" + #define PRIpdx "tx" + #define PRIpdu "tu" + #define PRIpdd "td" +#endif + +// This is needed because the foreach macro can't get over the comma in pair +#define PAIRTYPE(t1, t2) std::pair + +// Align by increasing pointer, must have extra space at end of buffer +template +T* alignup(T* p) +{ + union + { + T* ptr; + size_t n; + } u; + u.ptr = p; + u.n = (u.n + (nBytes-1)) & ~(nBytes-1); + return u.ptr; +} + +#ifdef WIN32 +#define MSG_NOSIGNAL 0 +#define MSG_DONTWAIT 0 + +#ifndef S_IRUSR +#define S_IRUSR 0400 +#define S_IWUSR 0200 +#endif +#else +#define MAX_PATH 1024 +#endif + +inline void MilliSleep(int64 n) +{ +// Boost's sleep_for was uninterruptable when backed by nanosleep from 1.50 +// until fixed in 1.52. Use the deprecated sleep method for the broken case. +// See: https://svn.boost.org/trac/boost/ticket/7238 + +#if BOOST_VERSION >= 105000 && (!defined(BOOST_HAS_NANOSLEEP) || BOOST_VERSION >= 105200) + boost::this_thread::sleep_for(boost::chrono::milliseconds(n)); +#else + boost::this_thread::sleep(boost::posix_time::milliseconds(n)); +#endif +} + +/* This GNU C extension enables the compiler to check the format string against the parameters provided. + * X is the number of the "format string" parameter, and Y is the number of the first variadic parameter. + * Parameters count from 1. + */ +#ifdef __GNUC__ +#define ATTR_WARN_PRINTF(X,Y) __attribute__((format(printf,X,Y))) +#else +#define ATTR_WARN_PRINTF(X,Y) +#endif + + + + + + + + +extern std::map mapArgs; +extern std::map > mapMultiArgs; +extern bool fDebug; +extern bool fDebugNet; +extern bool fPrintToConsole; +extern bool fPrintToDebugger; +extern bool fDaemon; +extern bool fServer; +extern bool fCommandLine; +extern std::string strMiscWarning; +extern bool fTestNet; +extern bool fBloomFilters; +extern bool fNoListen; +extern bool fLogTimestamps; +extern volatile bool fReopenDebugLog; + +void RandAddSeed(); +void RandAddSeedPerfmon(); +int ATTR_WARN_PRINTF(1,2) OutputDebugStringF(const char* pszFormat, ...); + +/* + Rationale for the real_strprintf / strprintf construction: + It is not allowed to use va_start with a pass-by-reference argument. + (C++ standard, 18.7, paragraph 3). Use a dummy argument to work around this, and use a + macro to keep similar semantics. +*/ + +/** Overload strprintf for char*, so that GCC format type warnings can be given */ +std::string ATTR_WARN_PRINTF(1,3) real_strprintf(const char *format, int dummy, ...); +/** Overload strprintf for std::string, to be able to use it with _ (translation). + * This will not support GCC format type warnings (-Wformat) so be careful. + */ +std::string real_strprintf(const std::string &format, int dummy, ...); +#define strprintf(format, ...) real_strprintf(format, 0, __VA_ARGS__) +std::string vstrprintf(const char *format, va_list ap); + +bool ATTR_WARN_PRINTF(1,2) error(const char *format, ...); + +/* Redefine printf so that it directs output to debug.log + * + * Do this *after* defining the other printf-like functions, because otherwise the + * __attribute__((format(printf,X,Y))) gets expanded to __attribute__((format(OutputDebugStringF,X,Y))) + * which confuses gcc. + */ +#define printf OutputDebugStringF + +void LogException(std::exception* pex, const char* pszThread); +void PrintException(std::exception* pex, const char* pszThread); +void PrintExceptionContinue(std::exception* pex, const char* pszThread); +void ParseString(const std::string& str, char c, std::vector& v); +std::string FormatMoney(int64 n, bool fPlus=false); +bool ParseMoney(const std::string& str, int64& nRet); +bool ParseMoney(const char* pszIn, int64& nRet); +std::string SanitizeString(const std::string& str); +std::vector ParseHex(const char* psz); +std::vector ParseHex(const std::string& str); +bool IsHex(const std::string& str); +std::vector DecodeBase64(const char* p, bool* pfInvalid = NULL); +std::string DecodeBase64(const std::string& str); +std::string EncodeBase64(const unsigned char* pch, size_t len); +std::string EncodeBase64(const std::string& str); +std::vector DecodeBase32(const char* p, bool* pfInvalid = NULL); +std::string DecodeBase32(const std::string& str); +std::string EncodeBase32(const unsigned char* pch, size_t len); +std::string EncodeBase32(const std::string& str); +void ParseParameters(int argc, const char*const argv[]); +bool WildcardMatch(const char* psz, const char* mask); +bool WildcardMatch(const std::string& str, const std::string& mask); +void FileCommit(FILE *fileout); +int GetFilesize(FILE* file); +bool TruncateFile(FILE *file, unsigned int length); +int RaiseFileDescriptorLimit(int nMinFD); +void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length); +bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest); +boost::filesystem::path GetDefaultDataDir(); +const boost::filesystem::path &GetDataDir(bool fNetSpecific = true); +boost::filesystem::path GetConfigFile(); +boost::filesystem::path GetPidFile(); +#ifndef WIN32 +void CreatePidFile(const boost::filesystem::path &path, pid_t pid); +#endif +void ReadConfigFile(std::map& mapSettingsRet, std::map >& mapMultiSettingsRet); +#ifdef WIN32 +boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true); +#endif +boost::filesystem::path GetTempPath(); +void ShrinkDebugFile(); +int GetRandInt(int nMax); +uint64 GetRand(uint64 nMax); +uint256 GetRandHash(); +int64 GetTime(); +void SetMockTime(int64 nMockTimeIn); +int64 GetAdjustedTime(); +int64 GetTimeOffset(); +long hex2long(const char* hexString); +std::string FormatFullVersion(); +std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector& comments); +void AddTimeData(const CNetAddr& ip, int64 nTime); +void runCommand(std::string strCommand); +void DoubleToNumeratorDenominator(double inDouble, long long *outNumerator, long long *outDenominator); // For the difficulty update in GetNextWorkRequired + + + + + + + + +inline std::string i64tostr(int64 n) +{ + return strprintf("%"PRI64d, n); +} + +inline std::string itostr(int n) +{ + return strprintf("%d", n); +} + +inline int64 atoi64(const char* psz) +{ +#ifdef _MSC_VER + return _atoi64(psz); +#else + return strtoll(psz, NULL, 10); +#endif +} + +inline int64 atoi64(const std::string& str) +{ +#ifdef _MSC_VER + return _atoi64(str.c_str()); +#else + return strtoll(str.c_str(), NULL, 10); +#endif +} + +inline int atoi(const std::string& str) +{ + return atoi(str.c_str()); +} + +inline int roundint(double d) +{ + return (int)(d > 0 ? d + 0.5 : d - 0.5); +} + +inline int64 roundint64(double d) +{ + return (int64)(d > 0 ? d + 0.5 : d - 0.5); +} + +inline int64 abs64(int64 n) +{ + return (n >= 0 ? n : -n); +} + +template +std::string HexStr(const T itbegin, const T itend, bool fSpaces=false) +{ + std::string rv; + static const char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + rv.reserve((itend-itbegin)*3); + for(T it = itbegin; it < itend; ++it) + { + unsigned char val = (unsigned char)(*it); + if(fSpaces && it != itbegin) + rv.push_back(' '); + rv.push_back(hexmap[val>>4]); + rv.push_back(hexmap[val&15]); + } + + return rv; +} + +template +inline std::string HexStr(const T& vch, bool fSpaces=false) +{ + return HexStr(vch.begin(), vch.end(), fSpaces); +} + +template +void PrintHex(const T pbegin, const T pend, const char* pszFormat="%s", bool fSpaces=true) +{ + printf(pszFormat, HexStr(pbegin, pend, fSpaces).c_str()); +} + +inline void PrintHex(const std::vector& vch, const char* pszFormat="%s", bool fSpaces=true) +{ + printf(pszFormat, HexStr(vch, fSpaces).c_str()); +} + +inline int64 GetPerformanceCounter() +{ + int64 nCounter = 0; +#ifdef WIN32 + QueryPerformanceCounter((LARGE_INTEGER*)&nCounter); +#else + timeval t; + gettimeofday(&t, NULL); + nCounter = (int64) t.tv_sec * 1000000 + t.tv_usec; +#endif + return nCounter; +} + +inline int64 GetTimeMillis() +{ + return (boost::posix_time::ptime(boost::posix_time::microsec_clock::universal_time()) - + boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_milliseconds(); +} + +inline int64 GetTimeMicros() +{ + return (boost::posix_time::ptime(boost::posix_time::microsec_clock::universal_time()) - + boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_microseconds(); +} + +inline std::string DateTimeStrFormat(const char* pszFormat, int64 nTime) +{ + time_t n = nTime; + struct tm* ptmTime = gmtime(&n); + char pszTime[200]; + strftime(pszTime, sizeof(pszTime), pszFormat, ptmTime); + return pszTime; +} + +template +void skipspaces(T& it) +{ + while (isspace(*it)) + ++it; +} + +inline bool IsSwitchChar(char c) +{ +#ifdef WIN32 + return c == '-' || c == '/'; +#else + return c == '-'; +#endif +} + +/** + * Return string argument or default value + * + * @param strArg Argument to get (e.g. "-foo") + * @param default (e.g. "1") + * @return command-line argument or default value + */ +std::string GetArg(const std::string& strArg, const std::string& strDefault); + +/** + * Return integer argument or default value + * + * @param strArg Argument to get (e.g. "-foo") + * @param default (e.g. 1) + * @return command-line argument (0 if invalid number) or default value + */ +int64 GetArg(const std::string& strArg, int64 nDefault); + +/** + * Return boolean argument or default value + * + * @param strArg Argument to get (e.g. "-foo") + * @param default (true or false) + * @return command-line argument or default value + */ +bool GetBoolArg(const std::string& strArg, bool fDefault=false); + +/** + * Set an argument if it doesn't already have a value + * + * @param strArg Argument to set (e.g. "-foo") + * @param strValue Value (e.g. "1") + * @return true if argument gets set, false if it already had a value + */ +bool SoftSetArg(const std::string& strArg, const std::string& strValue); + +/** + * Set a boolean argument if it doesn't already have a value + * + * @param strArg Argument to set (e.g. "-foo") + * @param fValue Value (e.g. false) + * @return true if argument gets set, false if it already had a value + */ +bool SoftSetBoolArg(const std::string& strArg, bool fValue); + +/** + * MWC RNG of George Marsaglia + * This is intended to be fast. It has a period of 2^59.3, though the + * least significant 16 bits only have a period of about 2^30.1. + * + * @return random value + */ +extern uint32_t insecure_rand_Rz; +extern uint32_t insecure_rand_Rw; +static inline uint32_t insecure_rand(void) +{ + insecure_rand_Rz = 36969 * (insecure_rand_Rz & 65535) + (insecure_rand_Rz >> 16); + insecure_rand_Rw = 18000 * (insecure_rand_Rw & 65535) + (insecure_rand_Rw >> 16); + return (insecure_rand_Rw << 16) + insecure_rand_Rz; +} + +/** + * Seed insecure_rand using the random pool. + * @param Deterministic Use a determinstic seed + */ +void seed_insecure_rand(bool fDeterministic=false); + +/** + * Timing-attack-resistant comparison. + * Takes time proportional to length + * of first argument. + */ +template +bool TimingResistantEqual(const T& a, const T& b) +{ + if (b.size() == 0) return a.size() == 0; + size_t accumulator = a.size() ^ b.size(); + for (size_t i = 0; i < a.size(); i++) + accumulator |= a[i] ^ b[i%b.size()]; + return accumulator == 0; +} + +/** Median filter over a stream of values. + * Returns the median of the last N numbers + */ +template class CMedianFilter +{ +private: + std::vector vValues; + std::vector vSorted; + unsigned int nSize; +public: + CMedianFilter(unsigned int size, T initial_value): + nSize(size) + { + vValues.reserve(size); + vValues.push_back(initial_value); + vSorted = vValues; + } + + void input(T value) + { + if(vValues.size() == nSize) + { + vValues.erase(vValues.begin()); + } + vValues.push_back(value); + + vSorted.resize(vValues.size()); + std::copy(vValues.begin(), vValues.end(), vSorted.begin()); + std::sort(vSorted.begin(), vSorted.end()); + } + + T median() const + { + int size = vSorted.size(); + assert(size>0); + if(size & 1) // Odd number of elements + { + return vSorted[size/2]; + } + else // Even number of elements + { + return (vSorted[size/2-1] + vSorted[size/2]) / 2; + } + } + + int size() const + { + return vValues.size(); + } + + std::vector sorted () const + { + return vSorted; + } +}; + +bool NewThread(void(*pfn)(void*), void* parg); + +#ifdef WIN32 +inline void SetThreadPriority(int nPriority) +{ + SetThreadPriority(GetCurrentThread(), nPriority); +} +#else + +#define THREAD_PRIORITY_LOWEST PRIO_MAX +#define THREAD_PRIORITY_BELOW_NORMAL 2 +#define THREAD_PRIORITY_NORMAL 0 +#define THREAD_PRIORITY_ABOVE_NORMAL 0 + +inline void SetThreadPriority(int nPriority) +{ + // It's unclear if it's even possible to change thread priorities on Linux, + // but we really and truly need it for the generation threads. +#ifdef PRIO_THREAD + setpriority(PRIO_THREAD, 0, nPriority); +#else + setpriority(PRIO_PROCESS, 0, nPriority); +#endif +} + +inline void ExitThread(size_t nExitCode) +{ + pthread_exit((void*)nExitCode); +} +#endif + +void RenameThread(const char* name); + +inline uint32_t ByteReverse(uint32_t value) +{ + value = ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8); + return (value<<16) | (value>>16); +} + +// 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, "dumpaddr", &DumpAddresses, 900000)); +// or maybe: +// boost::function f = boost::bind(&FunctionWithArg, argument); +// threadGroup.create_thread(boost::bind(&LoopForever >, "nothing", f, milliseconds)); +template void LoopForever(const char* name, Callable func, int64 msecs) +{ + std::string s = strprintf("bitcoin-%s", name); + RenameThread(s.c_str()); + printf("%s thread start\n", name); + try + { + while (1) + { + MilliSleep(msecs); + func(); + } + } + catch (boost::thread_interrupted) + { + printf("%s thread stop\n", name); + throw; + } + catch (std::exception& e) { + PrintException(&e, name); + } + catch (...) { + PrintException(NULL, name); + } +} +// .. and a wrapper that just calls func once +template void TraceThread(const char* name, Callable func) +{ + std::string s = strprintf("bitcoin-%s", name); + RenameThread(s.c_str()); + try + { + printf("%s thread start\n", name); + func(); + printf("%s thread exit\n", name); + } + catch (boost::thread_interrupted) + { + printf("%s thread interrupt\n", name); + throw; + } + catch (std::exception& e) { + PrintException(&e, name); + } + catch (...) { + PrintException(NULL, name); + } +} + +#endif diff --git a/src/version.cpp b/src/version.cpp new file mode 100644 index 0000000..7f676c4 --- /dev/null +++ b/src/version.cpp @@ -0,0 +1,67 @@ +// Copyright (c) 2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include + +#include "version.h" + +// Name of client reported in the 'version' message. Report the same name +// for both bitcoind and bitcoin-qt, to make it harder for attackers to +// target servers or GUI users specifically. +const std::string CLIENT_NAME("Satoshi"); + +// Client version number +// added stuff for gravity update +#define CLIENT_VERSION_SUFFIX "" + + +// The following part of the code determines the CLIENT_BUILD variable. +// Several mechanisms are used for this: +// * first, if HAVE_BUILD_INFO is defined, include build.h, a file that is +// generated by the build environment, possibly containing the output +// of git-describe in a macro called BUILD_DESC +// * secondly, if this is an exported version of the code, GIT_ARCHIVE will +// be defined (automatically using the export-subst git attribute), and +// GIT_COMMIT will contain the commit id. +// * then, three options exist for determining CLIENT_BUILD: +// * if BUILD_DESC is defined, use that literally (output of git-describe) +// * if not, but GIT_COMMIT is defined, use v[maj].[min].[rev].[build]-g[commit] +// * otherwise, use v[maj].[min].[rev].[build]-unk +// finally CLIENT_VERSION_SUFFIX is added + +// First, include build.h if requested +#ifdef HAVE_BUILD_INFO +# include "build.h" +#endif + +// git will put "#define GIT_ARCHIVE 1" on the next line inside archives. +#define GIT_ARCHIVE 1 +#ifdef GIT_ARCHIVE +# define GIT_COMMIT_ID "9999999" +# define GIT_COMMIT_DATE "Sat, 08 Feb 2014 13:32:49 +0100" +#endif + +#define BUILD_DESC_FROM_COMMIT(maj,min,rev,build,commit) \ + "v" DO_STRINGIZE(maj) "." DO_STRINGIZE(min) "." DO_STRINGIZE(rev) "." DO_STRINGIZE(build) "" + +#define BUILD_DESC_FROM_UNKNOWN(maj,min,rev,build) \ + "v" DO_STRINGIZE(maj) "." DO_STRINGIZE(min) "." DO_STRINGIZE(rev) "." DO_STRINGIZE(build) "-unk" + +#ifndef BUILD_DESC +# ifdef GIT_COMMIT_ID +# define BUILD_DESC BUILD_DESC_FROM_COMMIT(CLIENT_VERSION_MAJOR, CLIENT_VERSION_MINOR, CLIENT_VERSION_REVISION, CLIENT_VERSION_BUILD, GIT_COMMIT_ID) +# else +# define BUILD_DESC BUILD_DESC_FROM_UNKNOWN(CLIENT_VERSION_MAJOR, CLIENT_VERSION_MINOR, CLIENT_VERSION_REVISION, CLIENT_VERSION_BUILD) +# endif +#endif + +#ifndef BUILD_DATE +# ifdef GIT_COMMIT_DATE +# define BUILD_DATE GIT_COMMIT_DATE +# else +# define BUILD_DATE __DATE__ ", " __TIME__ +# endif +#endif + +const std::string CLIENT_BUILD(BUILD_DESC CLIENT_VERSION_SUFFIX); +const std::string CLIENT_DATE(BUILD_DATE); diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..57368c5 --- /dev/null +++ b/src/version.h @@ -0,0 +1,55 @@ +// Copyright (c) 2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_VERSION_H +#define BITCOIN_VERSION_H + +#include "clientversion.h" +#include + +// +// client versioning +// +// added stuff for gravity update ( changed proto version to 80005, 80003 unsupported ) + + +static const int CLIENT_VERSION = + 1000000 * CLIENT_VERSION_MAJOR + + 10000 * CLIENT_VERSION_MINOR + + 100 * CLIENT_VERSION_REVISION + + 1 * CLIENT_VERSION_BUILD; + +extern const std::string CLIENT_NAME; +extern const std::string CLIENT_BUILD; +extern const std::string CLIENT_DATE; + +// +// network protocol versioning +// + + +static const int PROTOCOL_VERSION_SHORT = 7; + +static const int PROTOCOL_VERSION = 80007; + +// intial proto version, to be increased after version/verack negotiation +static const int INIT_PROTO_VERSION = 209; + +// disconnect from peers older than this proto version +static const int MIN_PEER_PROTO_VERSION = 80004; + +// nTime field added to CAddress, starting with this version; +// if possible, avoid requesting addresses nodes older than this +static const int CADDR_TIME_VERSION = 31402; + +// only request blocks from nodes outside this range of versions +static const int NOBLKS_VERSION_START = 80000; +static const int NOBLKS_VERSION_END = 80002; + +// BIP 0031, pong message, is enabled for all versions AFTER this one +static const int BIP0031_VERSION = 60000; + +// "mempool" command, enhanced "getdata" behavior starts with this version: +static const int MEMPOOL_GD_VERSION = 60002; + +#endif diff --git a/src/wallet.cpp b/src/wallet.cpp new file mode 100644 index 0000000..ea09190 --- /dev/null +++ b/src/wallet.cpp @@ -0,0 +1,1911 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "wallet.h" +#include "walletdb.h" +#include "crypter.h" +#include "ui_interface.h" +#include "base58.h" +#include "coincontrol.h" +#include + +using namespace std; + + +////////////////////////////////////////////////////////////////////////////// +// +// mapWallet +// + +struct CompareValueOnly +{ + bool operator()(const pair >& t1, + const pair >& t2) const + { + return t1.first < t2.first; + } +}; + +CPubKey CWallet::GenerateNewKey() +{ + bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets + + RandAddSeedPerfmon(); + CKey secret; + secret.MakeNewKey(fCompressed); + + // Compressed public keys were introduced in version 0.6.0 + if (fCompressed) + SetMinVersion(FEATURE_COMPRPUBKEY); + + CPubKey pubkey = secret.GetPubKey(); + if (!AddKeyPubKey(secret, pubkey)) + throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed"); + return pubkey; +} + +bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) +{ + if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) + return false; + if (!fFileBacked) + return true; + if (!IsCrypted()) { + return CWalletDB(strWalletFile).WriteKey(pubkey, secret.GetPrivKey()); + } + return true; +} + +bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const vector &vchCryptedSecret) +{ + if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) + return false; + if (!fFileBacked) + return true; + { + LOCK(cs_wallet); + if (pwalletdbEncryption) + return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret); + else + return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret); + } + return false; +} + +bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret) +{ + return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); +} + +bool CWallet::AddCScript(const CScript& redeemScript) +{ + if (!CCryptoKeyStore::AddCScript(redeemScript)) + return false; + if (!fFileBacked) + return true; + return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript); +} + +bool CWallet::Unlock(const SecureString& strWalletPassphrase) +{ + if (!IsLocked()) + return false; + + CCrypter crypter; + CKeyingMaterial vMasterKey; + + { + LOCK(cs_wallet); + BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys) + { + if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) + return false; + if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) + return false; + if (CCryptoKeyStore::Unlock(vMasterKey)) + return true; + } + } + return false; +} + +bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase) +{ + bool fWasLocked = IsLocked(); + + { + LOCK(cs_wallet); + Lock(); + + CCrypter crypter; + CKeyingMaterial vMasterKey; + BOOST_FOREACH(MasterKeyMap::value_type& pMasterKey, mapMasterKeys) + { + if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) + return false; + if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) + return false; + if (CCryptoKeyStore::Unlock(vMasterKey)) + { + int64 nStartTime = GetTimeMillis(); + crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); + pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime))); + + nStartTime = GetTimeMillis(); + crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); + pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; + + if (pMasterKey.second.nDeriveIterations < 25000) + pMasterKey.second.nDeriveIterations = 25000; + + printf("Wallet passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations); + + if (!crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) + return false; + if (!crypter.Encrypt(vMasterKey, pMasterKey.second.vchCryptedKey)) + return false; + CWalletDB(strWalletFile).WriteMasterKey(pMasterKey.first, pMasterKey.second); + if (fWasLocked) + Lock(); + return true; + } + } + } + + return false; +} + +void CWallet::SetBestChain(const CBlockLocator& loc) +{ + CWalletDB walletdb(strWalletFile); + walletdb.WriteBestBlock(loc); +} + +// This class implements an addrIncoming entry that causes pre-0.4 +// clients to crash on startup if reading a private-key-encrypted wallet. +class CCorruptAddress +{ +public: + IMPLEMENT_SERIALIZE + ( + if (nType & SER_DISK) + READWRITE(nVersion); + ) +}; + +bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit) +{ + if (nWalletVersion >= nVersion) + return true; + + // when doing an explicit upgrade, if we pass the max version permitted, upgrade all the way + if (fExplicit && nVersion > nWalletMaxVersion) + nVersion = FEATURE_LATEST; + + nWalletVersion = nVersion; + + if (nVersion > nWalletMaxVersion) + nWalletMaxVersion = nVersion; + + if (fFileBacked) + { + CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile); + if (nWalletVersion >= 40000) + { + // Versions prior to 0.4.0 did not support the "minversion" record. + // Use a CCorruptAddress to make them crash instead. + CCorruptAddress corruptAddress; + pwalletdb->WriteSetting("addrIncoming", corruptAddress); + } + if (nWalletVersion > 40000) + pwalletdb->WriteMinVersion(nWalletVersion); + if (!pwalletdbIn) + delete pwalletdb; + } + + return true; +} + +bool CWallet::SetMaxVersion(int nVersion) +{ + // cannot downgrade below current version + if (nWalletVersion > nVersion) + return false; + + nWalletMaxVersion = nVersion; + + return true; +} + +bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) +{ + if (IsCrypted()) + return false; + + CKeyingMaterial vMasterKey; + RandAddSeedPerfmon(); + + vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE); + RAND_bytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE); + + CMasterKey kMasterKey; + + RandAddSeedPerfmon(); + kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE); + RAND_bytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE); + + CCrypter crypter; + int64 nStartTime = GetTimeMillis(); + crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod); + kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime)); + + nStartTime = GetTimeMillis(); + crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod); + kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; + + if (kMasterKey.nDeriveIterations < 25000) + kMasterKey.nDeriveIterations = 25000; + + printf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations); + + if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod)) + return false; + if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey)) + return false; + + { + LOCK(cs_wallet); + mapMasterKeys[++nMasterKeyMaxID] = kMasterKey; + if (fFileBacked) + { + pwalletdbEncryption = new CWalletDB(strWalletFile); + if (!pwalletdbEncryption->TxnBegin()) + return false; + pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey); + } + + if (!EncryptKeys(vMasterKey)) + { + if (fFileBacked) + pwalletdbEncryption->TxnAbort(); + exit(1); //We now probably have half of our keys encrypted in memory, and half not...die and let the user reload their unencrypted wallet. + } + + // Encryption was introduced in version 0.4.0 + SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true); + + if (fFileBacked) + { + if (!pwalletdbEncryption->TxnCommit()) + exit(1); //We now have keys encrypted in memory, but no on disk...die to avoid confusion and let the user reload their unencrypted wallet. + + delete pwalletdbEncryption; + pwalletdbEncryption = NULL; + } + + Lock(); + Unlock(strWalletPassphrase); + NewKeyPool(); + Lock(); + + // Need to completely rewrite the wallet file; if we don't, bdb might keep + // bits of the unencrypted private key in slack space in the database file. + CDB::Rewrite(strWalletFile); + + } + NotifyStatusChanged(this); + + return true; +} + +int64 CWallet::IncOrderPosNext(CWalletDB *pwalletdb) +{ + int64 nRet = nOrderPosNext++; + if (pwalletdb) { + pwalletdb->WriteOrderPosNext(nOrderPosNext); + } else { + CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext); + } + return nRet; +} + +CWallet::TxItems CWallet::OrderedTxItems(std::list& acentries, std::string strAccount) +{ + CWalletDB walletdb(strWalletFile); + + // First: get all CWalletTx and CAccountingEntry into a sorted-by-order multimap. + TxItems txOrdered; + + // Note: maintaining indices in the database of (account,time) --> txid and (account, time) --> acentry + // would make this much faster for applications that do this a lot. + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + CWalletTx* wtx = &((*it).second); + txOrdered.insert(make_pair(wtx->nOrderPos, TxPair(wtx, (CAccountingEntry*)0))); + } + acentries.clear(); + walletdb.ListAccountCreditDebit(strAccount, acentries); + BOOST_FOREACH(CAccountingEntry& entry, acentries) + { + txOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry))); + } + + return txOrdered; +} + +void CWallet::WalletUpdateSpent(const CTransaction &tx) +{ + // Anytime a signature is successfully verified, it's proof the outpoint is spent. + // Update the wallet spent flag if it doesn't know due to wallet.dat being + // restored from backup or the user making copies of wallet.dat. + { + LOCK(cs_wallet); + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + map::iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) + { + CWalletTx& wtx = (*mi).second; + if (txin.prevout.n >= wtx.vout.size()) + printf("WalletUpdateSpent: bad wtx %s\n", wtx.GetHash().ToString().c_str()); + else if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n])) + { + printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); + wtx.MarkSpent(txin.prevout.n); + wtx.WriteToDisk(); + NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED); + } + } + } + } +} + +void CWallet::MarkDirty() +{ + { + LOCK(cs_wallet); + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + item.second.MarkDirty(); + } +} + +bool CWallet::AddToWallet(const CWalletTx& wtxIn) +{ + uint256 hash = wtxIn.GetHash(); + { + LOCK(cs_wallet); + // Inserts only if not already there, returns tx inserted or tx found + pair::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn)); + CWalletTx& wtx = (*ret.first).second; + wtx.BindWallet(this); + bool fInsertedNew = ret.second; + if (fInsertedNew) + { + wtx.nTimeReceived = GetAdjustedTime(); + wtx.nOrderPos = IncOrderPosNext(); + + wtx.nTimeSmart = wtx.nTimeReceived; + if (wtxIn.hashBlock != 0) + { + if (mapBlockIndex.count(wtxIn.hashBlock)) + { + unsigned int latestNow = wtx.nTimeReceived; + unsigned int latestEntry = 0; + { + // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future + int64 latestTolerated = latestNow + 300; + std::list acentries; + TxItems txOrdered = OrderedTxItems(acentries); + for (TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + if (pwtx == &wtx) + continue; + CAccountingEntry *const pacentry = (*it).second.second; + int64 nSmartTime; + if (pwtx) + { + nSmartTime = pwtx->nTimeSmart; + if (!nSmartTime) + nSmartTime = pwtx->nTimeReceived; + } + else + nSmartTime = pacentry->nTime; + if (nSmartTime <= latestTolerated) + { + latestEntry = nSmartTime; + if (nSmartTime > latestNow) + latestNow = nSmartTime; + break; + } + } + } + + unsigned int& blocktime = mapBlockIndex[wtxIn.hashBlock]->nTime; + wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow)); + } + else + printf("AddToWallet() : found %s in block %s not in index\n", + wtxIn.GetHash().ToString().c_str(), + wtxIn.hashBlock.ToString().c_str()); + } + } + + bool fUpdated = false; + if (!fInsertedNew) + { + // Merge + if (wtxIn.hashBlock != 0 && wtxIn.hashBlock != wtx.hashBlock) + { + wtx.hashBlock = wtxIn.hashBlock; + fUpdated = true; + } + if (wtxIn.nIndex != -1 && (wtxIn.vMerkleBranch != wtx.vMerkleBranch || wtxIn.nIndex != wtx.nIndex)) + { + wtx.vMerkleBranch = wtxIn.vMerkleBranch; + wtx.nIndex = wtxIn.nIndex; + fUpdated = true; + } + if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) + { + wtx.fFromMe = wtxIn.fFromMe; + fUpdated = true; + } + fUpdated |= wtx.UpdateSpent(wtxIn.vfSpent); + } + + //// debug print + printf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString().c_str(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); + + // Write to disk + if (fInsertedNew || fUpdated) + if (!wtx.WriteToDisk()) + return false; +#ifndef QT_GUI + // If default receiving address gets used, replace it with a new one + if (vchDefaultKey.IsValid()) { + CScript scriptDefaultKey; + scriptDefaultKey.SetDestination(vchDefaultKey.GetID()); + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + if (txout.scriptPubKey == scriptDefaultKey) + { + CPubKey newDefaultKey; + if (GetKeyFromPool(newDefaultKey, false)) + { + SetDefaultKey(newDefaultKey); + SetAddressBookName(vchDefaultKey.GetID(), ""); + } + } + } + } +#endif + // since AddToWallet is called directly for self-originating transactions, check for consumption of own coins + WalletUpdateSpent(wtx); + + // Notify UI of new or updated transaction + NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); + + // notify an external script when a wallet transaction comes in or is updated + std::string strCmd = GetArg("-walletnotify", ""); + + if ( !strCmd.empty()) + { + boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } + + } + return true; +} + +// Add a transaction to the wallet, or update it. +// pblock is optional, but should be provided if the transaction is known to be in a block. +// If fUpdate is true, existing transactions will be updated. +bool CWallet::AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fFindBlock) +{ + { + LOCK(cs_wallet); + bool fExisted = mapWallet.count(hash); + if (fExisted && !fUpdate) return false; + if (fExisted || IsMine(tx) || IsFromMe(tx)) + { + CWalletTx wtx(this,tx); + // Get merkle branch if transaction was found in a block + if (pblock) + wtx.SetMerkleBranch(pblock); + return AddToWallet(wtx); + } + else + WalletUpdateSpent(tx); + } + return false; +} + +bool CWallet::EraseFromWallet(uint256 hash) +{ + if (!fFileBacked) + return false; + { + LOCK(cs_wallet); + if (mapWallet.erase(hash)) + CWalletDB(strWalletFile).EraseTx(hash); + } + return true; +} + + +bool CWallet::IsMine(const CTxIn &txin) const +{ + { + LOCK(cs_wallet); + map::const_iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (txin.prevout.n < prev.vout.size()) + if (IsMine(prev.vout[txin.prevout.n])) + return true; + } + } + return false; +} + +int64 CWallet::GetDebit(const CTxIn &txin) const +{ + { + LOCK(cs_wallet); + map::const_iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (txin.prevout.n < prev.vout.size()) + if (IsMine(prev.vout[txin.prevout.n])) + return prev.vout[txin.prevout.n].nValue; + } + } + return 0; +} + +bool CWallet::IsChange(const CTxOut& txout) const +{ + CTxDestination address; + + // TODO: fix handling of 'change' outputs. The assumption is that any + // payment to a TX_PUBKEYHASH that is mine but isn't in the address book + // is change. That assumption is likely to break when we implement multisignature + // wallets that return change back into a multi-signature-protected address; + // a better way of identifying which outputs are 'the send' and which are + // 'the change' will need to be implemented (maybe extend CWalletTx to remember + // which output, if any, was change). + if (ExtractDestination(txout.scriptPubKey, address) && ::IsMine(*this, address)) + { + LOCK(cs_wallet); + if (!mapAddressBook.count(address)) + return true; + } + return false; +} + +int64 CWalletTx::GetTxTime() const +{ + int64 n = nTimeSmart; + return n ? n : nTimeReceived; +} + +int CWalletTx::GetRequestCount() const +{ + // Returns -1 if it wasn't being tracked + int nRequests = -1; + { + LOCK(pwallet->cs_wallet); + if (IsCoinBase()) + { + // Generated block + if (hashBlock != 0) + { + map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); + if (mi != pwallet->mapRequestCount.end()) + nRequests = (*mi).second; + } + } + else + { + // Did anyone request this transaction? + map::const_iterator mi = pwallet->mapRequestCount.find(GetHash()); + if (mi != pwallet->mapRequestCount.end()) + { + nRequests = (*mi).second; + + // How about the block it's in? + if (nRequests == 0 && hashBlock != 0) + { + map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); + if (mi != pwallet->mapRequestCount.end()) + nRequests = (*mi).second; + else + nRequests = 1; // If it's in someone else's block it must have got out + } + } + } + } + return nRequests; +} + +void CWalletTx::GetAmounts(list >& listReceived, + list >& listSent, int64& nFee, string& strSentAccount) const +{ + nFee = 0; + listReceived.clear(); + listSent.clear(); + strSentAccount = strFromAccount; + + // Compute fee: + int64 nDebit = GetDebit(); + if (nDebit > 0) // debit>0 means we signed/sent this transaction + { + int64 nValueOut = GetValueOut(); + nFee = nDebit - nValueOut; + } + + // Sent/received. + BOOST_FOREACH(const CTxOut& txout, vout) + { + CTxDestination address; + vector vchPubKey; + if (!ExtractDestination(txout.scriptPubKey, address)) + { + printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", + this->GetHash().ToString().c_str()); + } + + // Don't report 'change' txouts + if (nDebit > 0 && pwallet->IsChange(txout)) + continue; + + if (nDebit > 0) + listSent.push_back(make_pair(address, txout.nValue)); + + if (pwallet->IsMine(txout)) + listReceived.push_back(make_pair(address, txout.nValue)); + } + +} + +void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nReceived, + int64& nSent, int64& nFee) const +{ + nReceived = nSent = nFee = 0; + + int64 allFee; + string strSentAccount; + list > listReceived; + list > listSent; + GetAmounts(listReceived, listSent, allFee, strSentAccount); + + if (strAccount == strSentAccount) + { + BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& s, listSent) + nSent += s.second; + nFee = allFee; + } + { + LOCK(pwallet->cs_wallet); + BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived) + { + if (pwallet->mapAddressBook.count(r.first)) + { + map::const_iterator mi = pwallet->mapAddressBook.find(r.first); + if (mi != pwallet->mapAddressBook.end() && (*mi).second == strAccount) + nReceived += r.second; + } + else if (strAccount.empty()) + { + nReceived += r.second; + } + } + } +} + +void CWalletTx::AddSupportingTransactions() +{ + vtxPrev.clear(); + + const int COPY_DEPTH = 3; + if (SetMerkleBranch() < COPY_DEPTH) + { + vector vWorkQueue; + BOOST_FOREACH(const CTxIn& txin, vin) + vWorkQueue.push_back(txin.prevout.hash); + + { + LOCK(pwallet->cs_wallet); + map mapWalletPrev; + set setAlreadyDone; + for (unsigned int i = 0; i < vWorkQueue.size(); i++) + { + uint256 hash = vWorkQueue[i]; + if (setAlreadyDone.count(hash)) + continue; + setAlreadyDone.insert(hash); + + CMerkleTx tx; + map::const_iterator mi = pwallet->mapWallet.find(hash); + if (mi != pwallet->mapWallet.end()) + { + tx = (*mi).second; + BOOST_FOREACH(const CMerkleTx& txWalletPrev, (*mi).second.vtxPrev) + mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev; + } + else if (mapWalletPrev.count(hash)) + { + tx = *mapWalletPrev[hash]; + } + else + { + continue; + } + + int nDepth = tx.SetMerkleBranch(); + vtxPrev.push_back(tx); + + if (nDepth < COPY_DEPTH) + { + BOOST_FOREACH(const CTxIn& txin, tx.vin) + vWorkQueue.push_back(txin.prevout.hash); + } + } + } + } + + reverse(vtxPrev.begin(), vtxPrev.end()); +} + +bool CWalletTx::WriteToDisk() +{ + return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this); +} + +// Scan the block chain (starting in pindexStart) for transactions +// from or to us. If fUpdate is true, found transactions that already +// exist in the wallet will be updated. +int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) +{ + int ret = 0; + + CBlockIndex* pindex = pindexStart; + { + LOCK(cs_wallet); + while (pindex) + { + CBlock block; + block.ReadFromDisk(pindex); + BOOST_FOREACH(CTransaction& tx, block.vtx) + { + if (AddToWalletIfInvolvingMe(tx.GetHash(), tx, &block, fUpdate)) + ret++; + } + pindex = pindex->pnext; + } + } + return ret; +} + +void CWallet::ReacceptWalletTransactions() +{ + bool fRepeat = true; + while (fRepeat) + { + LOCK(cs_wallet); + fRepeat = false; + bool fMissing = false; + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + { + CWalletTx& wtx = item.second; + if (wtx.IsCoinBase() && wtx.IsSpent(0)) + continue; + + CCoins coins; + bool fUpdated = false; + bool fFound = pcoinsTip->GetCoins(wtx.GetHash(), coins); + if (fFound || wtx.GetDepthInMainChain() > 0) + { + // Update fSpent if a tx got spent somewhere else by a copy of wallet.dat + for (unsigned int i = 0; i < wtx.vout.size(); i++) + { + if (wtx.IsSpent(i)) + continue; + if ((i >= coins.vout.size() || coins.vout[i].IsNull()) && IsMine(wtx.vout[i])) + { + wtx.MarkSpent(i); + fUpdated = true; + fMissing = true; + } + } + if (fUpdated) + { + printf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); + wtx.MarkDirty(); + wtx.WriteToDisk(); + } + } + else + { + // Re-accept any txes of ours that aren't already in a block + if (!wtx.IsCoinBase()) + wtx.AcceptWalletTransaction(false); + } + } + if (fMissing) + { + // TODO: optimize this to scan just part of the block chain? + if (ScanForWalletTransactions(pindexGenesisBlock)) + fRepeat = true; // Found missing transactions: re-do re-accept. + } + } +} + +void CWalletTx::RelayWalletTransaction() +{ + BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) + { + // Important: versions of bitcoin before 0.8.6 had a bug that inserted + // empty transactions into the vtxPrev, which will cause the node to be + // banned when retransmitted, hence the check for !tx.vin.empty() + if (!tx.IsCoinBase() && !tx.vin.empty()) + if (tx.GetDepthInMainChain() == 0) + RelayTransaction((CTransaction)tx, tx.GetHash()); + } + if (!IsCoinBase()) + { + if (GetDepthInMainChain() == 0) { + uint256 hash = GetHash(); + printf("Relaying wtx %s\n", hash.ToString().c_str()); + RelayTransaction((CTransaction)*this, hash); + } + } +} + +void CWallet::ResendWalletTransactions() +{ + // Do this infrequently and randomly to avoid giving away + // that these are our transactions. + static int64 nNextTime; + if (GetTime() < nNextTime) + return; + bool fFirst = (nNextTime == 0); + nNextTime = GetTime() + GetRand(30 * 60); + if (fFirst) + return; + + // Only do it if there's been a new block since last time + static int64 nLastTime; + if (nTimeBestReceived < nLastTime) + return; + nLastTime = GetTime(); + + // Rebroadcast any of our txes that aren't in a block yet + printf("ResendWalletTransactions()\n"); + { + LOCK(cs_wallet); + // Sort them in chronological order + multimap mapSorted; + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + { + CWalletTx& wtx = item.second; + // Don't rebroadcast until it's had plenty of time that + // it should have gotten in already by now. + if (nTimeBestReceived - (int64)wtx.nTimeReceived > 5 * 60) + mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); + } + BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) + { + CWalletTx& wtx = *item.second; + wtx.RelayWalletTransaction(); + } + } +} + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// Actions +// + + +int64 CWallet::GetBalance() const +{ + int64 nTotal = 0; + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsConfirmed()) + nTotal += pcoin->GetAvailableCredit(); + } + } + + return nTotal; +} + +int64 CWallet::GetUnconfirmedBalance() const +{ + int64 nTotal = 0; + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) + nTotal += pcoin->GetAvailableCredit(); + } + } + return nTotal; +} + +int64 CWallet::GetImmatureBalance() const +{ + int64 nTotal = 0; + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + nTotal += pcoin->GetImmatureCredit(); + } + } + return nTotal; +} + +// populate vCoins with vector of spendable COutputs +void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl) const +{ + vCoins.clear(); + + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + + if (!pcoin->IsFinal()) + continue; + + if (fOnlyConfirmed && !pcoin->IsConfirmed()) + continue; + + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; + + for (unsigned int i = 0; i < pcoin->vout.size(); i++) { + if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && + !IsLockedCoin((*it).first, i) && pcoin->vout[i].nValue >= nMinimumInputValue && + (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i))) + vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain())); + } + } + } +} + +static void ApproximateBestSubset(vector > >vValue, int64 nTotalLower, int64 nTargetValue, + vector& vfBest, int64& nBest, int iterations = 1000) +{ + vector vfIncluded; + + vfBest.assign(vValue.size(), true); + nBest = nTotalLower; + + seed_insecure_rand(); + + for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++) + { + vfIncluded.assign(vValue.size(), false); + int64 nTotal = 0; + bool fReachedTarget = false; + for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) + { + for (unsigned int i = 0; i < vValue.size(); i++) + { + //The solver here uses a randomized algorithm, + //the randomness serves no real security purpose but is just + //needed to prevent degenerate behavior and it is important + //that the rng fast. We do not use a constant random sequence, + //because there may be some privacy improvement by making + //the selection random. + if (nPass == 0 ? insecure_rand()&1 : !vfIncluded[i]) + { + nTotal += vValue[i].first; + vfIncluded[i] = true; + if (nTotal >= nTargetValue) + { + fReachedTarget = true; + if (nTotal < nBest) + { + nBest = nTotal; + vfBest = vfIncluded; + } + nTotal -= vValue[i].first; + vfIncluded[i] = false; + } + } + } + } + } +} + +bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, vector vCoins, + set >& setCoinsRet, int64& nValueRet) const +{ + setCoinsRet.clear(); + nValueRet = 0; + + // List of values less than target + pair > coinLowestLarger; + coinLowestLarger.first = std::numeric_limits::max(); + coinLowestLarger.second.first = NULL; + vector > > vValue; + int64 nTotalLower = 0; + + random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); + + BOOST_FOREACH(COutput output, vCoins) + { + const CWalletTx *pcoin = output.tx; + + if (output.nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs)) + continue; + + int i = output.i; + int64 n = pcoin->vout[i].nValue; + + pair > coin = make_pair(n,make_pair(pcoin, i)); + + if (n == nTargetValue) + { + setCoinsRet.insert(coin.second); + nValueRet += coin.first; + return true; + } + else if (n < nTargetValue + CENT) + { + vValue.push_back(coin); + nTotalLower += n; + } + else if (n < coinLowestLarger.first) + { + coinLowestLarger = coin; + } + } + + if (nTotalLower == nTargetValue) + { + for (unsigned int i = 0; i < vValue.size(); ++i) + { + setCoinsRet.insert(vValue[i].second); + nValueRet += vValue[i].first; + } + return true; + } + + if (nTotalLower < nTargetValue) + { + if (coinLowestLarger.second.first == NULL) + return false; + setCoinsRet.insert(coinLowestLarger.second); + nValueRet += coinLowestLarger.first; + return true; + } + + // Solve subset sum by stochastic approximation + sort(vValue.rbegin(), vValue.rend(), CompareValueOnly()); + vector vfBest; + int64 nBest; + + ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest, 1000); + if (nBest != nTargetValue && nTotalLower >= nTargetValue + CENT) + ApproximateBestSubset(vValue, nTotalLower, nTargetValue + CENT, vfBest, nBest, 1000); + + // If we have a bigger coin and (either the stochastic approximation didn't find a good solution, + // or the next bigger coin is closer), return the bigger coin + if (coinLowestLarger.second.first && + ((nBest != nTargetValue && nBest < nTargetValue + CENT) || coinLowestLarger.first <= nBest)) + { + setCoinsRet.insert(coinLowestLarger.second); + nValueRet += coinLowestLarger.first; + } + else { + for (unsigned int i = 0; i < vValue.size(); i++) + if (vfBest[i]) + { + setCoinsRet.insert(vValue[i].second); + nValueRet += vValue[i].first; + } + + //// debug print + printf("SelectCoins() best subset: "); + for (unsigned int i = 0; i < vValue.size(); i++) + if (vfBest[i]) + printf("%s ", FormatMoney(vValue[i].first).c_str()); + printf("total %s\n", FormatMoney(nBest).c_str()); + } + + return true; +} + +bool CWallet::SelectCoins(int64 nTargetValue, set >& setCoinsRet, int64& nValueRet, const CCoinControl* coinControl) const +{ + vector vCoins; + AvailableCoins(vCoins, true, coinControl); + + // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) + if (coinControl && coinControl->HasSelected()) + { + BOOST_FOREACH(const COutput& out, vCoins) + { + nValueRet += out.tx->vout[out.i].nValue; + setCoinsRet.insert(make_pair(out.tx, out.i)); + } + return (nValueRet >= nTargetValue); + } + + return (SelectCoinsMinConf(nTargetValue, 1, 6, vCoins, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue, 1, 1, vCoins, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet)); +} + + + + +bool CWallet::CreateTransaction(const vector >& vecSend, + CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl) +{ + int64 nValue = 0; + BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) + { + if (nValue < 0) + { + strFailReason = _("Transaction amounts must be positive"); + return false; + } + nValue += s.second; + } + if (vecSend.empty() || nValue < 0) + { + strFailReason = _("Transaction amounts must be positive"); + return false; + } + + wtxNew.BindWallet(this); + + { + LOCK2(cs_main, cs_wallet); + { + nFeeRet = nTransactionFee; + loop + { + wtxNew.vin.clear(); + wtxNew.vout.clear(); + wtxNew.fFromMe = true; + + int64 nTotalValue = nValue + nFeeRet; + double dPriority = 0; + // vouts to the payees + BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) + { + CTxOut txout(s.second, s.first); + if (txout.IsDust()) + { + strFailReason = _("Transaction amount too small"); + return false; + } + wtxNew.vout.push_back(txout); + } + + // Choose coins to use + set > setCoins; + int64 nValueIn = 0; + if (!SelectCoins(nTotalValue, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) + { + int64 nCredit = pcoin.first->vout[pcoin.second].nValue; + //The priority after the next block (depth+1) is used instead of the current, + //reflecting an assumption the user would accept a bit more delay for + //a chance at a free transaction. + dPriority += (double)nCredit * (pcoin.first->GetDepthInMainChain()+1); + } + + int64 nChange = nValueIn - nValue - nFeeRet; + // if sub-cent change is required, the fee must be raised to at least nMinTxFee + // or until nChange becomes zero + // NOTE: this depends on the exact behaviour of GetMinFee + if (nFeeRet < CTransaction::nMinTxFee && nChange > 0 && nChange < COIN) + { + int64 nMoveToFee = min(nChange, CTransaction::nMinTxFee - nFeeRet); + nChange -= nMoveToFee; + nFeeRet += nMoveToFee; + } + + if (nChange > 0) + { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange.SetDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked + + scriptChange.SetDestination(vchPubKey.GetID()); + } + + CTxOut newTxOut(nChange, scriptChange); + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust()) + { + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + // Insert change txn at random position: + vector::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()+1); + wtxNew.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) + wtxNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second)); + + // Sign + int nIn = 0; + BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) + if (!SignSignature(*this, *coin.first, wtxNew, nIn++)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + + // Limit size + unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION); + if (nBytes >= MAX_STANDARD_TX_SIZE) + { + strFailReason = _("Transaction too large"); + return false; + } + dPriority /= nBytes; + + // Check that enough fee is included + int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000); + bool fAllowFree = CTransaction::AllowFree(dPriority); + int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree, GMF_SEND); + if (nFeeRet < max(nPayFee, nMinFee)) + { + nFeeRet = max(nPayFee, nMinFee); + continue; + } + + // Fill vtxPrev by copying from previous transactions vtxPrev + wtxNew.AddSupportingTransactions(); + wtxNew.fTimeReceivedIsTxTime = true; + + break; + } + } + } + return true; +} + +bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, + CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl) +{ + vector< pair > vecSend; + vecSend.push_back(make_pair(scriptPubKey, nValue)); + return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet, strFailReason, coinControl); +} + +// Call after CreateTransaction unless you want to abort +bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) +{ + { + LOCK2(cs_main, cs_wallet); + printf("CommitTransaction:\n%s", wtxNew.ToString().c_str()); + { + // This is only to keep the database open to defeat the auto-flush for the + // duration of this scope. This is the only place where this optimization + // maybe makes sense; please don't do it anywhere else. + CWalletDB* pwalletdb = fFileBacked ? new CWalletDB(strWalletFile,"r") : NULL; + + // Take key pair from key pool so it won't be used again + reservekey.KeepKey(); + + // Add tx to wallet, because if it has change it's also ours, + // otherwise just for transaction history. + AddToWallet(wtxNew); + + // Mark old coins as spent + set setCoins; + BOOST_FOREACH(const CTxIn& txin, wtxNew.vin) + { + CWalletTx &coin = mapWallet[txin.prevout.hash]; + coin.BindWallet(this); + coin.MarkSpent(txin.prevout.n); + coin.WriteToDisk(); + NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED); + } + + if (fFileBacked) + delete pwalletdb; + } + + // Track how many getdata requests our transaction gets + mapRequestCount[wtxNew.GetHash()] = 0; + + // Broadcast + if (!wtxNew.AcceptToMemoryPool(true, false)) + { + // This must not fail. The transaction has already been signed and recorded. + printf("CommitTransaction() : Error: Transaction not valid"); + return false; + } + wtxNew.RelayWalletTransaction(); + } + return true; +} + + + + +string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee) +{ + CReserveKey reservekey(this); + int64 nFeeRequired; + + if (IsLocked()) + { + string strError = _("Error: Wallet locked, unable to create transaction!"); + printf("SendMoney() : %s", strError.c_str()); + return strError; + } + string strError; + if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired, strError)) + { + if (nValue + nFeeRequired > GetBalance()) + strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!"), FormatMoney(nFeeRequired).c_str()); + printf("SendMoney() : %s\n", strError.c_str()); + return strError; + } + + if (fAskFee && !uiInterface.ThreadSafeAskFee(nFeeRequired)) + return "ABORTED"; + + if (!CommitTransaction(wtxNew, reservekey)) + return _("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."); + + return ""; +} + + + +string CWallet::SendMoneyToDestination(const CTxDestination& address, int64 nValue, CWalletTx& wtxNew, bool fAskFee) +{ + // Check amount + if (nValue <= 0) + return _("Invalid amount"); + if (nValue + nTransactionFee > GetBalance()) + return _("Insufficient funds"); + + // Parse Bitcoin address + CScript scriptPubKey; + scriptPubKey.SetDestination(address); + + return SendMoney(scriptPubKey, nValue, wtxNew, fAskFee); +} + + + + +DBErrors CWallet::LoadWallet(bool& fFirstRunRet) +{ + if (!fFileBacked) + return DB_LOAD_OK; + fFirstRunRet = false; + DBErrors nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this); + if (nLoadWalletRet == DB_NEED_REWRITE) + { + if (CDB::Rewrite(strWalletFile, "\x04pool")) + { + setKeyPool.clear(); + // Note: can't top-up keypool here, because wallet is locked. + // User will be prompted to unlock wallet the next operation + // the requires a new key. + } + } + + if (nLoadWalletRet != DB_LOAD_OK) + return nLoadWalletRet; + fFirstRunRet = !vchDefaultKey.IsValid(); + + return DB_LOAD_OK; +} + + +bool CWallet::SetAddressBookName(const CTxDestination& address, const string& strName) +{ + std::map::iterator mi = mapAddressBook.find(address); + mapAddressBook[address] = strName; + NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address), (mi == mapAddressBook.end()) ? CT_NEW : CT_UPDATED); + if (!fFileBacked) + return false; + return CWalletDB(strWalletFile).WriteName(CBitcoinAddress(address).ToString(), strName); +} + +bool CWallet::DelAddressBookName(const CTxDestination& address) +{ + mapAddressBook.erase(address); + NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address), CT_DELETED); + if (!fFileBacked) + return false; + return CWalletDB(strWalletFile).EraseName(CBitcoinAddress(address).ToString()); +} + + +void CWallet::PrintWallet(const CBlock& block) +{ + { + LOCK(cs_wallet); + if (mapWallet.count(block.vtx[0].GetHash())) + { + CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()]; + printf(" mine: %d %d %"PRI64d"", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit()); + } + } + printf("\n"); +} + +bool CWallet::GetTransaction(const uint256 &hashTx, CWalletTx& wtx) +{ + { + LOCK(cs_wallet); + map::iterator mi = mapWallet.find(hashTx); + if (mi != mapWallet.end()) + { + wtx = (*mi).second; + return true; + } + } + return false; +} + +bool CWallet::SetDefaultKey(const CPubKey &vchPubKey) +{ + if (fFileBacked) + { + if (!CWalletDB(strWalletFile).WriteDefaultKey(vchPubKey)) + return false; + } + vchDefaultKey = vchPubKey; + return true; +} + +bool GetWalletFile(CWallet* pwallet, string &strWalletFileOut) +{ + if (!pwallet->fFileBacked) + return false; + strWalletFileOut = pwallet->strWalletFile; + return true; +} + +// +// Mark old keypool keys as used, +// and generate all new keys +// +bool CWallet::NewKeyPool() +{ + { + LOCK(cs_wallet); + CWalletDB walletdb(strWalletFile); + BOOST_FOREACH(int64 nIndex, setKeyPool) + walletdb.ErasePool(nIndex); + setKeyPool.clear(); + + if (IsLocked()) + return false; + + int64 nKeys = max(GetArg("-keypool", 100), (int64)0); + for (int i = 0; i < nKeys; i++) + { + int64 nIndex = i+1; + walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey())); + setKeyPool.insert(nIndex); + } + printf("CWallet::NewKeyPool wrote %"PRI64d" new keys\n", nKeys); + } + return true; +} + +bool CWallet::TopUpKeyPool() +{ + { + LOCK(cs_wallet); + + if (IsLocked()) + return false; + + CWalletDB walletdb(strWalletFile); + + // Top up key pool + unsigned int nTargetSize = max(GetArg("-keypool", 100), 0LL); + while (setKeyPool.size() < (nTargetSize + 1)) + { + int64 nEnd = 1; + if (!setKeyPool.empty()) + nEnd = *(--setKeyPool.end()) + 1; + if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey()))) + throw runtime_error("TopUpKeyPool() : writing generated key failed"); + setKeyPool.insert(nEnd); + printf("keypool added key %"PRI64d", size=%"PRIszu"\n", nEnd, setKeyPool.size()); + } + } + return true; +} + +void CWallet::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) +{ + nIndex = -1; + keypool.vchPubKey = CPubKey(); + { + LOCK(cs_wallet); + + if (!IsLocked()) + TopUpKeyPool(); + + // Get the oldest key + if(setKeyPool.empty()) + return; + + CWalletDB walletdb(strWalletFile); + + nIndex = *(setKeyPool.begin()); + setKeyPool.erase(setKeyPool.begin()); + if (!walletdb.ReadPool(nIndex, keypool)) + throw runtime_error("ReserveKeyFromKeyPool() : read failed"); + if (!HaveKey(keypool.vchPubKey.GetID())) + throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool"); + assert(keypool.vchPubKey.IsValid()); + printf("keypool reserve %"PRI64d"\n", nIndex); + } +} + +int64 CWallet::AddReserveKey(const CKeyPool& keypool) +{ + { + LOCK2(cs_main, cs_wallet); + CWalletDB walletdb(strWalletFile); + + int64 nIndex = 1 + *(--setKeyPool.end()); + if (!walletdb.WritePool(nIndex, keypool)) + throw runtime_error("AddReserveKey() : writing added key failed"); + setKeyPool.insert(nIndex); + return nIndex; + } + return -1; +} + +void CWallet::KeepKey(int64 nIndex) +{ + // Remove from key pool + if (fFileBacked) + { + CWalletDB walletdb(strWalletFile); + walletdb.ErasePool(nIndex); + } + printf("keypool keep %"PRI64d"\n", nIndex); +} + +void CWallet::ReturnKey(int64 nIndex) +{ + // Return to key pool + { + LOCK(cs_wallet); + setKeyPool.insert(nIndex); + } + printf("keypool return %"PRI64d"\n", nIndex); +} + +bool CWallet::GetKeyFromPool(CPubKey& result, bool fAllowReuse) +{ + int64 nIndex = 0; + CKeyPool keypool; + { + LOCK(cs_wallet); + ReserveKeyFromKeyPool(nIndex, keypool); + if (nIndex == -1) + { + if (fAllowReuse && vchDefaultKey.IsValid()) + { + result = vchDefaultKey; + return true; + } + if (IsLocked()) return false; + result = GenerateNewKey(); + return true; + } + KeepKey(nIndex); + result = keypool.vchPubKey; + } + return true; +} + +int64 CWallet::GetOldestKeyPoolTime() +{ + int64 nIndex = 0; + CKeyPool keypool; + ReserveKeyFromKeyPool(nIndex, keypool); + if (nIndex == -1) + return GetTime(); + ReturnKey(nIndex); + return keypool.nTime; +} + +std::map CWallet::GetAddressBalances() +{ + map balances; + + { + LOCK(cs_wallet); + BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet) + { + CWalletTx *pcoin = &walletEntry.second; + + if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) + continue; + + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; + + int nDepth = pcoin->GetDepthInMainChain(); + if (nDepth < (pcoin->IsFromMe() ? 0 : 1)) + continue; + + for (unsigned int i = 0; i < pcoin->vout.size(); i++) + { + CTxDestination addr; + if (!IsMine(pcoin->vout[i])) + continue; + if(!ExtractDestination(pcoin->vout[i].scriptPubKey, addr)) + continue; + + int64 n = pcoin->IsSpent(i) ? 0 : pcoin->vout[i].nValue; + + if (!balances.count(addr)) + balances[addr] = 0; + balances[addr] += n; + } + } + } + + return balances; +} + +set< set > CWallet::GetAddressGroupings() +{ + set< set > groupings; + set grouping; + + BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet) + { + CWalletTx *pcoin = &walletEntry.second; + + if (pcoin->vin.size() > 0) + { + bool any_mine = false; + // group all input addresses with each other + BOOST_FOREACH(CTxIn txin, pcoin->vin) + { + CTxDestination address; + if(!IsMine(txin)) /* If this input isn't mine, ignore it */ + continue; + if(!ExtractDestination(mapWallet[txin.prevout.hash].vout[txin.prevout.n].scriptPubKey, address)) + continue; + grouping.insert(address); + any_mine = true; + } + + // group change with input addresses + if (any_mine) + { + BOOST_FOREACH(CTxOut txout, pcoin->vout) + if (IsChange(txout)) + { + CTxDestination txoutAddr; + if(!ExtractDestination(txout.scriptPubKey, txoutAddr)) + continue; + grouping.insert(txoutAddr); + } + } + if (grouping.size() > 0) + { + groupings.insert(grouping); + grouping.clear(); + } + } + + // group lone addrs by themselves + for (unsigned int i = 0; i < pcoin->vout.size(); i++) + if (IsMine(pcoin->vout[i])) + { + CTxDestination address; + if(!ExtractDestination(pcoin->vout[i].scriptPubKey, address)) + continue; + grouping.insert(address); + groupings.insert(grouping); + grouping.clear(); + } + } + + set< set* > uniqueGroupings; // a set of pointers to groups of addresses + map< CTxDestination, set* > setmap; // map addresses to the unique group containing it + BOOST_FOREACH(set grouping, groupings) + { + // make a set of all the groups hit by this new group + set< set* > hits; + map< CTxDestination, set* >::iterator it; + BOOST_FOREACH(CTxDestination address, grouping) + if ((it = setmap.find(address)) != setmap.end()) + hits.insert((*it).second); + + // merge all hit groups into a new single group and delete old groups + set* merged = new set(grouping); + BOOST_FOREACH(set* hit, hits) + { + merged->insert(hit->begin(), hit->end()); + uniqueGroupings.erase(hit); + delete hit; + } + uniqueGroupings.insert(merged); + + // update setmap + BOOST_FOREACH(CTxDestination element, *merged) + setmap[element] = merged; + } + + set< set > ret; + BOOST_FOREACH(set* uniqueGrouping, uniqueGroupings) + { + ret.insert(*uniqueGrouping); + delete uniqueGrouping; + } + + return ret; +} + +bool CReserveKey::GetReservedKey(CPubKey& pubkey) +{ + if (nIndex == -1) + { + CKeyPool keypool; + pwallet->ReserveKeyFromKeyPool(nIndex, keypool); + if (nIndex != -1) + vchPubKey = keypool.vchPubKey; + else { + if (pwallet->vchDefaultKey.IsValid()) { + printf("CReserveKey::GetReservedKey(): Warning: Using default key instead of a new key, top up your keypool!"); + vchPubKey = pwallet->vchDefaultKey; + } else + return false; + } + } + assert(vchPubKey.IsValid()); + pubkey = vchPubKey; + return true; +} + +void CReserveKey::KeepKey() +{ + if (nIndex != -1) + pwallet->KeepKey(nIndex); + nIndex = -1; + vchPubKey = CPubKey(); +} + +void CReserveKey::ReturnKey() +{ + if (nIndex != -1) + pwallet->ReturnKey(nIndex); + nIndex = -1; + vchPubKey = CPubKey(); +} + +void CWallet::GetAllReserveKeys(set& setAddress) +{ + setAddress.clear(); + + CWalletDB walletdb(strWalletFile); + + LOCK2(cs_main, cs_wallet); + BOOST_FOREACH(const int64& id, setKeyPool) + { + CKeyPool keypool; + if (!walletdb.ReadPool(id, keypool)) + throw runtime_error("GetAllReserveKeyHashes() : read failed"); + assert(keypool.vchPubKey.IsValid()); + CKeyID keyID = keypool.vchPubKey.GetID(); + if (!HaveKey(keyID)) + throw runtime_error("GetAllReserveKeyHashes() : unknown key in key pool"); + setAddress.insert(keyID); + } +} + +void CWallet::UpdatedTransaction(const uint256 &hashTx) +{ + { + LOCK(cs_wallet); + // Only notify UI if this transaction is in this wallet + map::const_iterator mi = mapWallet.find(hashTx); + if (mi != mapWallet.end()) + NotifyTransactionChanged(this, hashTx, CT_UPDATED); + } +} + +void CWallet::LockCoin(COutPoint& output) +{ + setLockedCoins.insert(output); +} + +void CWallet::UnlockCoin(COutPoint& output) +{ + setLockedCoins.erase(output); +} + +void CWallet::UnlockAllCoins() +{ + setLockedCoins.clear(); +} + +bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const +{ + COutPoint outpt(hash, n); + + return (setLockedCoins.count(outpt) > 0); +} + +void CWallet::ListLockedCoins(std::vector& vOutpts) +{ + for (std::set::iterator it = setLockedCoins.begin(); + it != setLockedCoins.end(); it++) { + COutPoint outpt = (*it); + vOutpts.push_back(outpt); + } +} + diff --git a/src/wallet.h b/src/wallet.h new file mode 100644 index 0000000..cd1fb34 --- /dev/null +++ b/src/wallet.h @@ -0,0 +1,863 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_WALLET_H +#define BITCOIN_WALLET_H + +#include +#include + +#include + +#include "main.h" +#include "key.h" +#include "keystore.h" +#include "script.h" +#include "ui_interface.h" +#include "util.h" +#include "walletdb.h" + +class CAccountingEntry; +class CWalletTx; +class CReserveKey; +class COutput; +class CCoinControl; + +/** (client) version numbers for particular wallet features */ +enum WalletFeature +{ + FEATURE_BASE = 10500, // the earliest version new wallets supports (only useful for getinfo's clientversion output) + + FEATURE_WALLETCRYPT = 40000, // wallet encryption + FEATURE_COMPRPUBKEY = 60000, // compressed public keys + + FEATURE_LATEST = 60000 +}; + + +/** A key pool entry */ +class CKeyPool +{ +public: + int64 nTime; + CPubKey vchPubKey; + + CKeyPool() + { + nTime = GetTime(); + } + + CKeyPool(const CPubKey& vchPubKeyIn) + { + nTime = GetTime(); + vchPubKey = vchPubKeyIn; + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(nTime); + READWRITE(vchPubKey); + ) +}; + +/** A CWallet is an extension of a keystore, which also maintains a set of transactions and balances, + * and provides the ability to create new transactions. + */ +class CWallet : public CCryptoKeyStore +{ +private: + bool SelectCoins(int64 nTargetValue, std::set >& setCoinsRet, int64& nValueRet, const CCoinControl *coinControl=NULL) const; + + CWalletDB *pwalletdbEncryption; + + // the current wallet version: clients below this version are not able to load the wallet + int nWalletVersion; + + // the maximum wallet format version: memory-only variable that specifies to what version this wallet may be upgraded + int nWalletMaxVersion; + +public: + mutable CCriticalSection cs_wallet; + + bool fFileBacked; + std::string strWalletFile; + + std::set setKeyPool; + + + typedef std::map MasterKeyMap; + MasterKeyMap mapMasterKeys; + unsigned int nMasterKeyMaxID; + + CWallet() + { + nWalletVersion = FEATURE_BASE; + nWalletMaxVersion = FEATURE_BASE; + fFileBacked = false; + nMasterKeyMaxID = 0; + pwalletdbEncryption = NULL; + nOrderPosNext = 0; + } + CWallet(std::string strWalletFileIn) + { + nWalletVersion = FEATURE_BASE; + nWalletMaxVersion = FEATURE_BASE; + strWalletFile = strWalletFileIn; + fFileBacked = true; + nMasterKeyMaxID = 0; + pwalletdbEncryption = NULL; + nOrderPosNext = 0; + } + + std::map mapWallet; + int64 nOrderPosNext; + std::map mapRequestCount; + + std::map mapAddressBook; + + CPubKey vchDefaultKey; + + std::set setLockedCoins; + + // check whether we are allowed to upgrade (or already support) to the named feature + bool CanSupportFeature(enum WalletFeature wf) { return nWalletMaxVersion >= wf; } + + void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl=NULL) const; + bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, std::vector vCoins, std::set >& setCoinsRet, int64& nValueRet) const; + bool IsLockedCoin(uint256 hash, unsigned int n) const; + void LockCoin(COutPoint& output); + void UnlockCoin(COutPoint& output); + void UnlockAllCoins(); + void ListLockedCoins(std::vector& vOutpts); + + // keystore implementation + // Generate a new key + CPubKey GenerateNewKey(); + // Adds a key to the store, and saves it to disk. + bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); + // Adds a key to the store, without saving it to disk (used by LoadWallet) + bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); } + + bool LoadMinVersion(int nVersion) { nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; } + + // Adds an encrypted key to the store, and saves it to disk. + bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret); + // Adds an encrypted key to the store, without saving it to disk (used by LoadWallet) + bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret); + bool AddCScript(const CScript& redeemScript); + bool LoadCScript(const CScript& redeemScript) { return CCryptoKeyStore::AddCScript(redeemScript); } + + bool Unlock(const SecureString& strWalletPassphrase); + bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); + bool EncryptWallet(const SecureString& strWalletPassphrase); + + /** Increment the next transaction order id + @return next transaction order id + */ + int64 IncOrderPosNext(CWalletDB *pwalletdb = NULL); + + typedef std::pair TxPair; + typedef std::multimap TxItems; + + /** Get the wallet's activity log + @return multimap of ordered transactions and accounting entries + @warning Returned pointers are *only* valid within the scope of passed acentries + */ + TxItems OrderedTxItems(std::list& acentries, std::string strAccount = ""); + + void MarkDirty(); + bool AddToWallet(const CWalletTx& wtxIn); + bool AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate = false, bool fFindBlock = false); + bool EraseFromWallet(uint256 hash); + void WalletUpdateSpent(const CTransaction& prevout); + int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); + void ReacceptWalletTransactions(); + void ResendWalletTransactions(); + int64 GetBalance() const; + int64 GetUnconfirmedBalance() const; + int64 GetImmatureBalance() const; + bool CreateTransaction(const std::vector >& vecSend, + CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl=NULL); + bool CreateTransaction(CScript scriptPubKey, int64 nValue, + CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl=NULL); + bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); + std::string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); + std::string SendMoneyToDestination(const CTxDestination &address, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); + + bool NewKeyPool(); + bool TopUpKeyPool(); + int64 AddReserveKey(const CKeyPool& keypool); + void ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool); + void KeepKey(int64 nIndex); + void ReturnKey(int64 nIndex); + bool GetKeyFromPool(CPubKey &key, bool fAllowReuse=true); + int64 GetOldestKeyPoolTime(); + void GetAllReserveKeys(std::set& setAddress); + + std::set< std::set > GetAddressGroupings(); + std::map GetAddressBalances(); + + bool IsMine(const CTxIn& txin) const; + int64 GetDebit(const CTxIn& txin) const; + bool IsMine(const CTxOut& txout) const + { + return ::IsMine(*this, txout.scriptPubKey); + } + int64 GetCredit(const CTxOut& txout) const + { + if (!MoneyRange(txout.nValue)) + throw std::runtime_error("CWallet::GetCredit() : value out of range"); + return (IsMine(txout) ? txout.nValue : 0); + } + bool IsChange(const CTxOut& txout) const; + int64 GetChange(const CTxOut& txout) const + { + if (!MoneyRange(txout.nValue)) + throw std::runtime_error("CWallet::GetChange() : value out of range"); + return (IsChange(txout) ? txout.nValue : 0); + } + bool IsMine(const CTransaction& tx) const + { + BOOST_FOREACH(const CTxOut& txout, tx.vout) + if (IsMine(txout) && txout.nValue >= nMinimumInputValue) + return true; + return false; + } + bool IsFromMe(const CTransaction& tx) const + { + return (GetDebit(tx) > 0); + } + int64 GetDebit(const CTransaction& tx) const + { + int64 nDebit = 0; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + nDebit += GetDebit(txin); + if (!MoneyRange(nDebit)) + throw std::runtime_error("CWallet::GetDebit() : value out of range"); + } + return nDebit; + } + int64 GetCredit(const CTransaction& tx) const + { + int64 nCredit = 0; + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + nCredit += GetCredit(txout); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWallet::GetCredit() : value out of range"); + } + return nCredit; + } + int64 GetChange(const CTransaction& tx) const + { + int64 nChange = 0; + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + nChange += GetChange(txout); + if (!MoneyRange(nChange)) + throw std::runtime_error("CWallet::GetChange() : value out of range"); + } + return nChange; + } + void SetBestChain(const CBlockLocator& loc); + + DBErrors LoadWallet(bool& fFirstRunRet); + + bool SetAddressBookName(const CTxDestination& address, const std::string& strName); + + bool DelAddressBookName(const CTxDestination& address); + + void UpdatedTransaction(const uint256 &hashTx); + + void PrintWallet(const CBlock& block); + + void Inventory(const uint256 &hash) + { + { + LOCK(cs_wallet); + std::map::iterator mi = mapRequestCount.find(hash); + if (mi != mapRequestCount.end()) + (*mi).second++; + } + } + + int GetKeyPoolSize() + { + return setKeyPool.size(); + } + + bool GetTransaction(const uint256 &hashTx, CWalletTx& wtx); + + bool SetDefaultKey(const CPubKey &vchPubKey); + + // signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower + bool SetMinVersion(enum WalletFeature, CWalletDB* pwalletdbIn = NULL, bool fExplicit = false); + + // change which version we're allowed to upgrade to (note that this does not immediately imply upgrading to that format) + bool SetMaxVersion(int nVersion); + + // get the current wallet format (the oldest client version guaranteed to understand this wallet) + int GetVersion() { return nWalletVersion; } + + /** Address book entry changed. + * @note called with lock cs_wallet held. + */ + boost::signals2::signal NotifyAddressBookChanged; + + /** Wallet transaction added, removed or updated. + * @note called with lock cs_wallet held. + */ + boost::signals2::signal NotifyTransactionChanged; +}; + +/** A key allocated from the key pool. */ +class CReserveKey +{ +protected: + CWallet* pwallet; + int64 nIndex; + CPubKey vchPubKey; +public: + CReserveKey(CWallet* pwalletIn) + { + nIndex = -1; + pwallet = pwalletIn; + } + + ~CReserveKey() + { + ReturnKey(); + } + + void ReturnKey(); + bool GetReservedKey(CPubKey &pubkey); + void KeepKey(); +}; + + +typedef std::map mapValue_t; + + +static void ReadOrderPos(int64& nOrderPos, mapValue_t& mapValue) +{ + if (!mapValue.count("n")) + { + nOrderPos = -1; // TODO: calculate elsewhere + return; + } + nOrderPos = atoi64(mapValue["n"].c_str()); +} + + +static void WriteOrderPos(const int64& nOrderPos, mapValue_t& mapValue) +{ + if (nOrderPos == -1) + return; + mapValue["n"] = i64tostr(nOrderPos); +} + + +/** A transaction with a bunch of additional info that only the owner cares about. + * It includes any unrecorded transactions needed to link it back to the block chain. + */ +class CWalletTx : public CMerkleTx +{ +private: + const CWallet* pwallet; + +public: + std::vector vtxPrev; + mapValue_t mapValue; + std::vector > vOrderForm; + unsigned int fTimeReceivedIsTxTime; + unsigned int nTimeReceived; // time received by this node + unsigned int nTimeSmart; + char fFromMe; + std::string strFromAccount; + std::vector vfSpent; // which outputs are already spent + int64 nOrderPos; // position in ordered transaction list + + // memory only + mutable bool fDebitCached; + mutable bool fCreditCached; + mutable bool fImmatureCreditCached; + mutable bool fAvailableCreditCached; + mutable bool fChangeCached; + mutable int64 nDebitCached; + mutable int64 nCreditCached; + mutable int64 nImmatureCreditCached; + mutable int64 nAvailableCreditCached; + mutable int64 nChangeCached; + + CWalletTx() + { + Init(NULL); + } + + CWalletTx(const CWallet* pwalletIn) + { + Init(pwalletIn); + } + + CWalletTx(const CWallet* pwalletIn, const CMerkleTx& txIn) : CMerkleTx(txIn) + { + Init(pwalletIn); + } + + CWalletTx(const CWallet* pwalletIn, const CTransaction& txIn) : CMerkleTx(txIn) + { + Init(pwalletIn); + } + + void Init(const CWallet* pwalletIn) + { + pwallet = pwalletIn; + vtxPrev.clear(); + mapValue.clear(); + vOrderForm.clear(); + fTimeReceivedIsTxTime = false; + nTimeReceived = 0; + nTimeSmart = 0; + fFromMe = false; + strFromAccount.clear(); + vfSpent.clear(); + fDebitCached = false; + fCreditCached = false; + fImmatureCreditCached = false; + fAvailableCreditCached = false; + fChangeCached = false; + nDebitCached = 0; + nCreditCached = 0; + nImmatureCreditCached = 0; + nAvailableCreditCached = 0; + nChangeCached = 0; + nOrderPos = -1; + } + + IMPLEMENT_SERIALIZE + ( + CWalletTx* pthis = const_cast(this); + if (fRead) + pthis->Init(NULL); + char fSpent = false; + + if (!fRead) + { + pthis->mapValue["fromaccount"] = pthis->strFromAccount; + + std::string str; + BOOST_FOREACH(char f, vfSpent) + { + str += (f ? '1' : '0'); + if (f) + fSpent = true; + } + pthis->mapValue["spent"] = str; + + WriteOrderPos(pthis->nOrderPos, pthis->mapValue); + + if (nTimeSmart) + pthis->mapValue["timesmart"] = strprintf("%u", nTimeSmart); + } + + nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion,ser_action); + READWRITE(vtxPrev); + READWRITE(mapValue); + READWRITE(vOrderForm); + READWRITE(fTimeReceivedIsTxTime); + READWRITE(nTimeReceived); + READWRITE(fFromMe); + READWRITE(fSpent); + + if (fRead) + { + pthis->strFromAccount = pthis->mapValue["fromaccount"]; + + if (mapValue.count("spent")) + BOOST_FOREACH(char c, pthis->mapValue["spent"]) + pthis->vfSpent.push_back(c != '0'); + else + pthis->vfSpent.assign(vout.size(), fSpent); + + ReadOrderPos(pthis->nOrderPos, pthis->mapValue); + + pthis->nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(pthis->mapValue["timesmart"]) : 0; + } + + pthis->mapValue.erase("fromaccount"); + pthis->mapValue.erase("version"); + pthis->mapValue.erase("spent"); + pthis->mapValue.erase("n"); + pthis->mapValue.erase("timesmart"); + ) + + // marks certain txout's as spent + // returns true if any update took place + bool UpdateSpent(const std::vector& vfNewSpent) + { + bool fReturn = false; + for (unsigned int i = 0; i < vfNewSpent.size(); i++) + { + if (i == vfSpent.size()) + break; + + if (vfNewSpent[i] && !vfSpent[i]) + { + vfSpent[i] = true; + fReturn = true; + fAvailableCreditCached = false; + } + } + return fReturn; + } + + // make sure balances are recalculated + void MarkDirty() + { + fCreditCached = false; + fAvailableCreditCached = false; + fDebitCached = false; + fChangeCached = false; + } + + void BindWallet(CWallet *pwalletIn) + { + pwallet = pwalletIn; + MarkDirty(); + } + + void MarkSpent(unsigned int nOut) + { + if (nOut >= vout.size()) + throw std::runtime_error("CWalletTx::MarkSpent() : nOut out of range"); + vfSpent.resize(vout.size()); + if (!vfSpent[nOut]) + { + vfSpent[nOut] = true; + fAvailableCreditCached = false; + } + } + + bool IsSpent(unsigned int nOut) const + { + if (nOut >= vout.size()) + throw std::runtime_error("CWalletTx::IsSpent() : nOut out of range"); + if (nOut >= vfSpent.size()) + return false; + return (!!vfSpent[nOut]); + } + + int64 GetDebit() const + { + if (vin.empty()) + return 0; + if (fDebitCached) + return nDebitCached; + nDebitCached = pwallet->GetDebit(*this); + fDebitCached = true; + return nDebitCached; + } + + int64 GetCredit(bool fUseCache=true) const + { + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + // GetBalance can assume transactions in mapWallet won't change + if (fUseCache && fCreditCached) + return nCreditCached; + nCreditCached = pwallet->GetCredit(*this); + fCreditCached = true; + return nCreditCached; + } + + int64 GetImmatureCredit(bool fUseCache=true) const + { + if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) + { + if (fUseCache && fImmatureCreditCached) + return nImmatureCreditCached; + nImmatureCreditCached = pwallet->GetCredit(*this); + fImmatureCreditCached = true; + return nImmatureCreditCached; + } + + return 0; + } + + int64 GetAvailableCredit(bool fUseCache=true) const + { + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + if (fUseCache && fAvailableCreditCached) + return nAvailableCreditCached; + + int64 nCredit = 0; + for (unsigned int i = 0; i < vout.size(); i++) + { + if (!IsSpent(i)) + { + const CTxOut &txout = vout[i]; + nCredit += pwallet->GetCredit(txout); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + } + } + + nAvailableCreditCached = nCredit; + fAvailableCreditCached = true; + return nCredit; + } + + + int64 GetChange() const + { + if (fChangeCached) + return nChangeCached; + nChangeCached = pwallet->GetChange(*this); + fChangeCached = true; + return nChangeCached; + } + + void GetAmounts(std::list >& listReceived, + std::list >& listSent, int64& nFee, std::string& strSentAccount) const; + + void GetAccountAmounts(const std::string& strAccount, int64& nReceived, + int64& nSent, int64& nFee) const; + + bool IsFromMe() const + { + return (GetDebit() > 0); + } + + bool IsConfirmed() const + { + // Quick answer in most cases + if (!IsFinal()) + return false; + if (GetDepthInMainChain() >= 1) + return true; + if (!IsFromMe()) // using wtx's cached debit + return false; + + // If no confirmations but it's from us, we can still + // consider it confirmed if all dependencies are confirmed + std::map mapPrev; + std::vector vWorkQueue; + vWorkQueue.reserve(vtxPrev.size()+1); + vWorkQueue.push_back(this); + for (unsigned int i = 0; i < vWorkQueue.size(); i++) + { + const CMerkleTx* ptx = vWorkQueue[i]; + + if (!ptx->IsFinal()) + return false; + if (ptx->GetDepthInMainChain() >= 1) + continue; + if (!pwallet->IsFromMe(*ptx)) + return false; + + if (mapPrev.empty()) + { + BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) + mapPrev[tx.GetHash()] = &tx; + } + + BOOST_FOREACH(const CTxIn& txin, ptx->vin) + { + if (!mapPrev.count(txin.prevout.hash)) + return false; + vWorkQueue.push_back(mapPrev[txin.prevout.hash]); + } + } + return true; + } + + bool WriteToDisk(); + + int64 GetTxTime() const; + int GetRequestCount() const; + + void AddSupportingTransactions(); + bool AcceptWalletTransaction(bool fCheckInputs=true); + void RelayWalletTransaction(); +}; + + + + +class COutput +{ +public: + const CWalletTx *tx; + int i; + int nDepth; + + COutput(const CWalletTx *txIn, int iIn, int nDepthIn) + { + tx = txIn; i = iIn; nDepth = nDepthIn; + } + + std::string ToString() const + { + return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString().c_str(), i, nDepth, FormatMoney(tx->vout[i].nValue).c_str()); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + +/** Private key that includes an expiration date in case it never gets used. */ +class CWalletKey +{ +public: + CPrivKey vchPrivKey; + int64 nTimeCreated; + int64 nTimeExpires; + std::string strComment; + //// todo: add something to note what created it (user, getnewaddress, change) + //// maybe should have a map property map + + CWalletKey(int64 nExpires=0) + { + nTimeCreated = (nExpires ? GetTime() : 0); + nTimeExpires = nExpires; + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vchPrivKey); + READWRITE(nTimeCreated); + READWRITE(nTimeExpires); + READWRITE(strComment); + ) +}; + + + + + + +/** Account information. + * Stored in wallet with key "acc"+string account name. + */ +class CAccount +{ +public: + CPubKey vchPubKey; + + CAccount() + { + SetNull(); + } + + void SetNull() + { + vchPubKey = CPubKey(); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vchPubKey); + ) +}; + + + +/** Internal transfers. + * Database key is acentry. + */ +class CAccountingEntry +{ +public: + std::string strAccount; + int64 nCreditDebit; + int64 nTime; + std::string strOtherAccount; + std::string strComment; + mapValue_t mapValue; + int64 nOrderPos; // position in ordered transaction list + uint64 nEntryNo; + + CAccountingEntry() + { + SetNull(); + } + + void SetNull() + { + nCreditDebit = 0; + nTime = 0; + strAccount.clear(); + strOtherAccount.clear(); + strComment.clear(); + nOrderPos = -1; + } + + IMPLEMENT_SERIALIZE + ( + CAccountingEntry& me = *const_cast(this); + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + // Note: strAccount is serialized as part of the key, not here. + READWRITE(nCreditDebit); + READWRITE(nTime); + READWRITE(strOtherAccount); + + if (!fRead) + { + WriteOrderPos(nOrderPos, me.mapValue); + + if (!(mapValue.empty() && _ssExtra.empty())) + { + CDataStream ss(nType, nVersion); + ss.insert(ss.begin(), '\0'); + ss << mapValue; + ss.insert(ss.end(), _ssExtra.begin(), _ssExtra.end()); + me.strComment.append(ss.str()); + } + } + + READWRITE(strComment); + + size_t nSepPos = strComment.find("\0", 0, 1); + if (fRead) + { + me.mapValue.clear(); + if (std::string::npos != nSepPos) + { + CDataStream ss(std::vector(strComment.begin() + nSepPos + 1, strComment.end()), nType, nVersion); + ss >> me.mapValue; + me._ssExtra = std::vector(ss.begin(), ss.end()); + } + ReadOrderPos(me.nOrderPos, me.mapValue); + } + if (std::string::npos != nSepPos) + me.strComment.erase(nSepPos); + + me.mapValue.erase("n"); + ) + +private: + std::vector _ssExtra; +}; + +bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut); + +#endif diff --git a/src/walletdb.cpp b/src/walletdb.cpp new file mode 100644 index 0000000..cdc692f --- /dev/null +++ b/src/walletdb.cpp @@ -0,0 +1,660 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "walletdb.h" +#include "wallet.h" +#include +#include + +using namespace std; +using namespace boost; + + +static uint64 nAccountingEntryNumber = 0; + +// +// CWalletDB +// + +bool CWalletDB::WriteName(const string& strAddress, const string& strName) +{ + nWalletDBUpdated++; + return Write(make_pair(string("name"), strAddress), strName); +} + +bool CWalletDB::EraseName(const string& strAddress) +{ + // This should only be used for sending addresses, never for receiving addresses, + // receiving addresses must always have an address book entry if they're not change return. + nWalletDBUpdated++; + return Erase(make_pair(string("name"), strAddress)); +} + +bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account) +{ + account.SetNull(); + return Read(make_pair(string("acc"), strAccount), account); +} + +bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account) +{ + return Write(make_pair(string("acc"), strAccount), account); +} + +bool CWalletDB::WriteAccountingEntry(const uint64 nAccEntryNum, const CAccountingEntry& acentry) +{ + return Write(boost::make_tuple(string("acentry"), acentry.strAccount, nAccEntryNum), acentry); +} + +bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry) +{ + return WriteAccountingEntry(++nAccountingEntryNumber, acentry); +} + +int64 CWalletDB::GetAccountCreditDebit(const string& strAccount) +{ + list entries; + ListAccountCreditDebit(strAccount, entries); + + int64 nCreditDebit = 0; + BOOST_FOREACH (const CAccountingEntry& entry, entries) + nCreditDebit += entry.nCreditDebit; + + return nCreditDebit; +} + +void CWalletDB::ListAccountCreditDebit(const string& strAccount, list& entries) +{ + bool fAllAccounts = (strAccount == "*"); + + Dbc* pcursor = GetCursor(); + if (!pcursor) + throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor"); + unsigned int fFlags = DB_SET_RANGE; + loop + { + // Read next record + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + if (fFlags == DB_SET_RANGE) + ssKey << boost::make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64(0)); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); + fFlags = DB_NEXT; + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + { + pcursor->close(); + throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB"); + } + + // Unserialize + string strType; + ssKey >> strType; + if (strType != "acentry") + break; + CAccountingEntry acentry; + ssKey >> acentry.strAccount; + if (!fAllAccounts && acentry.strAccount != strAccount) + break; + + ssValue >> acentry; + ssKey >> acentry.nEntryNo; + entries.push_back(acentry); + } + + pcursor->close(); +} + + +DBErrors +CWalletDB::ReorderTransactions(CWallet* pwallet) +{ + LOCK(pwallet->cs_wallet); + // Old wallets didn't have any defined order for transactions + // Probably a bad idea to change the output of this + + // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap. + typedef pair TxPair; + typedef multimap TxItems; + TxItems txByTime; + + for (map::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it) + { + CWalletTx* wtx = &((*it).second); + txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0))); + } + list acentries; + ListAccountCreditDebit("", acentries); + BOOST_FOREACH(CAccountingEntry& entry, acentries) + { + txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); + } + + int64& nOrderPosNext = pwallet->nOrderPosNext; + nOrderPosNext = 0; + std::vector nOrderPosOffsets; + for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + CAccountingEntry *const pacentry = (*it).second.second; + int64& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos; + + if (nOrderPos == -1) + { + nOrderPos = nOrderPosNext++; + nOrderPosOffsets.push_back(nOrderPos); + + if (pacentry) + // Have to write accounting regardless, since we don't keep it in memory + if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) + return DB_LOAD_FAIL; + } + else + { + int64 nOrderPosOff = 0; + BOOST_FOREACH(const int64& nOffsetStart, nOrderPosOffsets) + { + if (nOrderPos >= nOffsetStart) + ++nOrderPosOff; + } + nOrderPos += nOrderPosOff; + nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1); + + if (!nOrderPosOff) + continue; + + // Since we're changing the order, write it back + if (pwtx) + { + if (!WriteTx(pwtx->GetHash(), *pwtx)) + return DB_LOAD_FAIL; + } + else + if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) + return DB_LOAD_FAIL; + } + } + + return DB_LOAD_OK; +} + + +bool +ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, + int& nFileVersion, vector& vWalletUpgrade, + bool& fIsEncrypted, bool& fAnyUnordered, string& strType, string& strErr) +{ + try { + // Unserialize + // Taking advantage of the fact that pair serialization + // is just the two items serialized one after the other + ssKey >> strType; + if (strType == "name") + { + string strAddress; + ssKey >> strAddress; + ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()]; + } + else if (strType == "tx") + { + uint256 hash; + ssKey >> hash; + CWalletTx& wtx = pwallet->mapWallet[hash]; + ssValue >> wtx; + CValidationState state; + if (wtx.CheckTransaction(state) && (wtx.GetHash() == hash) && state.IsValid()) + wtx.BindWallet(pwallet); + else + { + pwallet->mapWallet.erase(hash); + return false; + } + + // Undo serialize changes in 31600 + if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) + { + if (!ssValue.empty()) + { + char fTmp; + char fUnused; + ssValue >> fTmp >> fUnused >> wtx.strFromAccount; + strErr = strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s", + wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str()); + wtx.fTimeReceivedIsTxTime = fTmp; + } + else + { + strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str()); + wtx.fTimeReceivedIsTxTime = 0; + } + vWalletUpgrade.push_back(hash); + } + + if (wtx.nOrderPos == -1) + fAnyUnordered = true; + + //// debug print + //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str()); + //printf(" %12"PRI64d" %s %s %s\n", + // wtx.vout[0].nValue, + // DateTimeStrFormat("%Y-%m-%d %H:%M:%S", wtx.GetBlockTime()).c_str(), + // wtx.hashBlock.ToString().c_str(), + // wtx.mapValue["message"].c_str()); + } + else if (strType == "acentry") + { + string strAccount; + ssKey >> strAccount; + uint64 nNumber; + ssKey >> nNumber; + if (nNumber > nAccountingEntryNumber) + nAccountingEntryNumber = nNumber; + + if (!fAnyUnordered) + { + CAccountingEntry acentry; + ssValue >> acentry; + if (acentry.nOrderPos == -1) + fAnyUnordered = true; + } + } + else if (strType == "key" || strType == "wkey") + { + CPubKey vchPubKey; + ssKey >> vchPubKey; + if (!vchPubKey.IsValid()) + { + strErr = "Error reading wallet database: CPubKey corrupt"; + return false; + } + CKey key; + CPrivKey pkey; + if (strType == "key") + ssValue >> pkey; + else { + CWalletKey wkey; + ssValue >> wkey; + pkey = wkey.vchPrivKey; + } + if (!key.SetPrivKey(pkey, vchPubKey.IsCompressed())) + { + strErr = "Error reading wallet database: CPrivKey corrupt"; + return false; + } + if (key.GetPubKey() != vchPubKey) + { + strErr = "Error reading wallet database: CPrivKey pubkey inconsistency"; + return false; + } + if (!pwallet->LoadKey(key, vchPubKey)) + { + strErr = "Error reading wallet database: LoadKey failed"; + return false; + } + } + else if (strType == "mkey") + { + unsigned int nID; + ssKey >> nID; + CMasterKey kMasterKey; + ssValue >> kMasterKey; + if(pwallet->mapMasterKeys.count(nID) != 0) + { + strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID); + return false; + } + pwallet->mapMasterKeys[nID] = kMasterKey; + if (pwallet->nMasterKeyMaxID < nID) + pwallet->nMasterKeyMaxID = nID; + } + else if (strType == "ckey") + { + vector vchPubKey; + ssKey >> vchPubKey; + vector vchPrivKey; + ssValue >> vchPrivKey; + if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) + { + strErr = "Error reading wallet database: LoadCryptedKey failed"; + return false; + } + fIsEncrypted = true; + } + else if (strType == "defaultkey") + { + ssValue >> pwallet->vchDefaultKey; + } + else if (strType == "pool") + { + int64 nIndex; + ssKey >> nIndex; + pwallet->setKeyPool.insert(nIndex); + } + else if (strType == "version") + { + ssValue >> nFileVersion; + if (nFileVersion == 10300) + nFileVersion = 300; + } + else if (strType == "cscript") + { + uint160 hash; + ssKey >> hash; + CScript script; + ssValue >> script; + if (!pwallet->LoadCScript(script)) + { + strErr = "Error reading wallet database: LoadCScript failed"; + return false; + } + } + else if (strType == "orderposnext") + { + ssValue >> pwallet->nOrderPosNext; + } + } catch (...) + { + return false; + } + return true; +} + +static bool IsKeyType(string strType) +{ + return (strType== "key" || strType == "wkey" || + strType == "mkey" || strType == "ckey"); +} + +DBErrors CWalletDB::LoadWallet(CWallet* pwallet) +{ + pwallet->vchDefaultKey = CPubKey(); + int nFileVersion = 0; + vector vWalletUpgrade; + bool fIsEncrypted = false; + bool fAnyUnordered = false; + bool fNoncriticalErrors = false; + DBErrors result = DB_LOAD_OK; + + try { + LOCK(pwallet->cs_wallet); + int nMinVersion = 0; + if (Read((string)"minversion", nMinVersion)) + { + if (nMinVersion > CLIENT_VERSION) + return DB_TOO_NEW; + pwallet->LoadMinVersion(nMinVersion); + } + + // Get cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) + { + printf("Error getting wallet database cursor\n"); + return DB_CORRUPT; + } + + loop + { + // Read next record + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + int ret = ReadAtCursor(pcursor, ssKey, ssValue); + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + { + printf("Error reading next record from wallet database\n"); + return DB_CORRUPT; + } + + // Try to be tolerant of single corrupt records: + string strType, strErr; + if (!ReadKeyValue(pwallet, ssKey, ssValue, nFileVersion, + vWalletUpgrade, fIsEncrypted, fAnyUnordered, strType, strErr)) + { + // losing keys is considered a catastrophic error, anything else + // we assume the user can live with: + if (IsKeyType(strType)) + result = DB_CORRUPT; + else + { + // Leave other errors alone, if we try to fix them we might make things worse. + fNoncriticalErrors = true; // ... but do warn the user there is something wrong. + if (strType == "tx") + // Rescan if there is a bad transaction record: + SoftSetBoolArg("-rescan", true); + } + } + if (!strErr.empty()) + printf("%s\n", strErr.c_str()); + } + pcursor->close(); + } + catch (boost::thread_interrupted) { + throw; + } + catch (...) { + result = DB_CORRUPT; + } + + if (fNoncriticalErrors && result == DB_LOAD_OK) + result = DB_NONCRITICAL_ERROR; + + // Any wallet corruption at all: skip any rewriting or + // upgrading, we don't want to make it worse. + if (result != DB_LOAD_OK) + return result; + + printf("nFileVersion = %d\n", nFileVersion); + + BOOST_FOREACH(uint256 hash, vWalletUpgrade) + WriteTx(hash, pwallet->mapWallet[hash]); + + // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: + if (fIsEncrypted && (nFileVersion == 40000 || nFileVersion == 50000)) + return DB_NEED_REWRITE; + + if (nFileVersion < CLIENT_VERSION) // Update + WriteVersion(CLIENT_VERSION); + + if (fAnyUnordered) + result = ReorderTransactions(pwallet); + + return result; +} + +void ThreadFlushWalletDB(const string& strFile) +{ + // Make this thread recognisable as the wallet flushing thread + RenameThread("bitcoin-wallet"); + + static bool fOneThread; + if (fOneThread) + return; + fOneThread = true; + if (!GetBoolArg("-flushwallet", true)) + return; + + unsigned int nLastSeen = nWalletDBUpdated; + unsigned int nLastFlushed = nWalletDBUpdated; + int64 nLastWalletUpdate = GetTime(); + while (true) + { + MilliSleep(500); + + if (nLastSeen != nWalletDBUpdated) + { + nLastSeen = nWalletDBUpdated; + nLastWalletUpdate = GetTime(); + } + + if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2) + { + TRY_LOCK(bitdb.cs_db,lockDb); + if (lockDb) + { + // Don't do this if any databases are in use + int nRefCount = 0; + map::iterator mi = bitdb.mapFileUseCount.begin(); + while (mi != bitdb.mapFileUseCount.end()) + { + nRefCount += (*mi).second; + mi++; + } + + if (nRefCount == 0) + { + boost::this_thread::interruption_point(); + map::iterator mi = bitdb.mapFileUseCount.find(strFile); + if (mi != bitdb.mapFileUseCount.end()) + { + printf("Flushing wallet.dat\n"); + nLastFlushed = nWalletDBUpdated; + int64 nStart = GetTimeMillis(); + + // Flush wallet.dat so it's self contained + bitdb.CloseDb(strFile); + bitdb.CheckpointLSN(strFile); + + bitdb.mapFileUseCount.erase(mi++); + printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart); + } + } + } + } + } +} + +bool BackupWallet(const CWallet& wallet, const string& strDest) +{ + if (!wallet.fFileBacked) + return false; + while (true) + { + { + LOCK(bitdb.cs_db); + if (!bitdb.mapFileUseCount.count(wallet.strWalletFile) || bitdb.mapFileUseCount[wallet.strWalletFile] == 0) + { + // Flush log data to the dat file + bitdb.CloseDb(wallet.strWalletFile); + bitdb.CheckpointLSN(wallet.strWalletFile); + bitdb.mapFileUseCount.erase(wallet.strWalletFile); + + // Copy wallet.dat + filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile; + filesystem::path pathDest(strDest); + if (filesystem::is_directory(pathDest)) + pathDest /= wallet.strWalletFile; + + try { +#if BOOST_VERSION >= 104000 + filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists); +#else + filesystem::copy_file(pathSrc, pathDest); +#endif + printf("copied wallet.dat to %s\n", pathDest.string().c_str()); + return true; + } catch(const filesystem::filesystem_error &e) { + printf("error copying wallet.dat to %s - %s\n", pathDest.string().c_str(), e.what()); + return false; + } + } + } + MilliSleep(100); + } + return false; +} + +// +// Try to (very carefully!) recover wallet.dat if there is a problem. +// +bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) +{ + // Recovery procedure: + // move wallet.dat to wallet.timestamp.bak + // Call Salvage with fAggressive=true to + // get as much data as possible. + // Rewrite salvaged data to wallet.dat + // Set -rescan so any missing transactions will be + // found. + int64 now = GetTime(); + std::string newFilename = strprintf("wallet.%"PRI64d".bak", now); + + int result = dbenv.dbenv.dbrename(NULL, filename.c_str(), NULL, + newFilename.c_str(), DB_AUTO_COMMIT); + if (result == 0) + printf("Renamed %s to %s\n", filename.c_str(), newFilename.c_str()); + else + { + printf("Failed to rename %s to %s\n", filename.c_str(), newFilename.c_str()); + return false; + } + + std::vector salvagedData; + bool allOK = dbenv.Salvage(newFilename, true, salvagedData); + if (salvagedData.empty()) + { + printf("Salvage(aggressive) found no records in %s.\n", newFilename.c_str()); + return false; + } + printf("Salvage(aggressive) found %"PRIszu" records\n", salvagedData.size()); + + bool fSuccess = allOK; + Db* pdbCopy = new Db(&dbenv.dbenv, 0); + int ret = pdbCopy->open(NULL, // Txn pointer + filename.c_str(), // Filename + "main", // Logical db name + DB_BTREE, // Database type + DB_CREATE, // Flags + 0); + if (ret > 0) + { + printf("Cannot create database file %s\n", filename.c_str()); + return false; + } + CWallet dummyWallet; + int nFileVersion = 0; + vector vWalletUpgrade; + bool fIsEncrypted = false; + bool fAnyUnordered = false; + + DbTxn* ptxn = dbenv.TxnBegin(); + BOOST_FOREACH(CDBEnv::KeyValPair& row, salvagedData) + { + if (fOnlyKeys) + { + CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION); + CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION); + string strType, strErr; + bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, + nFileVersion, vWalletUpgrade, + fIsEncrypted, fAnyUnordered, + strType, strErr); + if (!IsKeyType(strType)) + continue; + if (!fReadOK) + { + printf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType.c_str(), strErr.c_str()); + continue; + } + } + Dbt datKey(&row.first[0], row.first.size()); + Dbt datValue(&row.second[0], row.second.size()); + int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE); + if (ret2 > 0) + fSuccess = false; + } + ptxn->commit(0); + pdbCopy->close(0); + delete pdbCopy; + + return fSuccess; +} + +bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename) +{ + return CWalletDB::Recover(dbenv, filename, false); +} diff --git a/src/walletdb.h b/src/walletdb.h new file mode 100644 index 0000000..8ae6c3f --- /dev/null +++ b/src/walletdb.h @@ -0,0 +1,163 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_WALLETDB_H +#define BITCOIN_WALLETDB_H + +#include "db.h" +#include "base58.h" + +class CKeyPool; +class CAccount; +class CAccountingEntry; + +/** Error statuses for the wallet database */ +enum DBErrors +{ + DB_LOAD_OK, + DB_CORRUPT, + DB_NONCRITICAL_ERROR, + DB_TOO_NEW, + DB_LOAD_FAIL, + DB_NEED_REWRITE +}; + +/** Access to the wallet database (wallet.dat) */ +class CWalletDB : public CDB +{ +public: + CWalletDB(std::string strFilename, const char* pszMode="r+") : CDB(strFilename.c_str(), pszMode) + { + } +private: + CWalletDB(const CWalletDB&); + void operator=(const CWalletDB&); +public: + bool WriteName(const std::string& strAddress, const std::string& strName); + + bool EraseName(const std::string& strAddress); + + bool WriteTx(uint256 hash, const CWalletTx& wtx) + { + nWalletDBUpdated++; + return Write(std::make_pair(std::string("tx"), hash), wtx); + } + + bool EraseTx(uint256 hash) + { + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("tx"), hash)); + } + + bool WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey) + { + nWalletDBUpdated++; + return Write(std::make_pair(std::string("key"), vchPubKey), vchPrivKey, false); + } + + bool WriteCryptedKey(const CPubKey& vchPubKey, const std::vector& vchCryptedSecret, bool fEraseUnencryptedKey = true) + { + nWalletDBUpdated++; + if (!Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false)) + return false; + if (fEraseUnencryptedKey) + { + Erase(std::make_pair(std::string("key"), vchPubKey)); + Erase(std::make_pair(std::string("wkey"), vchPubKey)); + } + return true; + } + + bool WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) + { + nWalletDBUpdated++; + return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true); + } + + bool WriteCScript(const uint160& hash, const CScript& redeemScript) + { + nWalletDBUpdated++; + return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false); + } + + bool WriteBestBlock(const CBlockLocator& locator) + { + nWalletDBUpdated++; + return Write(std::string("bestblock"), locator); + } + + bool ReadBestBlock(CBlockLocator& locator) + { + return Read(std::string("bestblock"), locator); + } + + bool WriteOrderPosNext(int64 nOrderPosNext) + { + nWalletDBUpdated++; + return Write(std::string("orderposnext"), nOrderPosNext); + } + + bool WriteDefaultKey(const CPubKey& vchPubKey) + { + nWalletDBUpdated++; + return Write(std::string("defaultkey"), vchPubKey); + } + + bool ReadPool(int64 nPool, CKeyPool& keypool) + { + return Read(std::make_pair(std::string("pool"), nPool), keypool); + } + + bool WritePool(int64 nPool, const CKeyPool& keypool) + { + nWalletDBUpdated++; + return Write(std::make_pair(std::string("pool"), nPool), keypool); + } + + bool ErasePool(int64 nPool) + { + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("pool"), nPool)); + } + + // Settings are no longer stored in wallet.dat; these are + // used only for backwards compatibility: + template + bool ReadSetting(const std::string& strKey, T& value) + { + return Read(std::make_pair(std::string("setting"), strKey), value); + } + template + bool WriteSetting(const std::string& strKey, const T& value) + { + nWalletDBUpdated++; + return Write(std::make_pair(std::string("setting"), strKey), value); + } + bool EraseSetting(const std::string& strKey) + { + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("setting"), strKey)); + } + + bool WriteMinVersion(int nVersion) + { + return Write(std::string("minversion"), nVersion); + } + + bool ReadAccount(const std::string& strAccount, CAccount& account); + bool WriteAccount(const std::string& strAccount, const CAccount& account); +private: + bool WriteAccountingEntry(const uint64 nAccEntryNum, const CAccountingEntry& acentry); +public: + bool WriteAccountingEntry(const CAccountingEntry& acentry); + int64 GetAccountCreditDebit(const std::string& strAccount); + void ListAccountCreditDebit(const std::string& strAccount, std::list& acentries); + + DBErrors ReorderTransactions(CWallet*); + DBErrors LoadWallet(CWallet* pwallet); + static bool Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys); + static bool Recover(CDBEnv& dbenv, std::string filename); +}; + +#endif // BITCOIN_WALLETDB_H