From: Jeff Garzik Date: Wed, 13 Jul 2011 02:34:30 +0000 (-0700) Subject: Merge pull request #381 from TheBlueMatt/nminversion X-Git-Tag: v0.4.0-unstable~227^2~96 X-Git-Url: https://git.novaco.in/?a=commitdiff_plain;h=0fa89d8e816807a621419495d7bdc6366979a0f0;hp=7ec552676c66488fe00fb503d02ec4a389a715b7;p=novacoin.git Merge pull request #381 from TheBlueMatt/nminversion Add minversion to wallet. --- diff --git a/contrib/gitian-win32.yml b/contrib/gitian-win32.yml index 0c804af..07d31c7 100644 --- a/contrib/gitian-win32.yml +++ b/contrib/gitian-win32.yml @@ -74,7 +74,8 @@ script: | cd src sed 's/$(DEBUGFLAGS)//' < makefile.linux-mingw > makefile.linux-mingw.2 && mv makefile.linux-mingw.2 makefile.linux-mingw sed 's|//#include |#include |' < net.cpp > net.cpp.2 && mv net.cpp.2 net.cpp - make -f makefile.linux-mingw $MAKEOPTS DEPSDIR=$HOME/build bitcoin.exe bitcoind.exe + make -f makefile.linux-mingw $MAKEOPTS DEPSDIR=$HOME/build bitcoin.exe USE_UPNP=1 + make -f makefile.linux-mingw $MAKEOPTS DEPSDIR=$HOME/build bitcoind.exe USE_UPNP=0 i586-mingw32msvc-strip bitcoin.exe i586-mingw32msvc-strip bitcoind.exe makensis ../share/setup.nsi diff --git a/contrib/gitian.yml b/contrib/gitian.yml index cff4cce..90be893 100644 --- a/contrib/gitian.yml +++ b/contrib/gitian.yml @@ -6,7 +6,7 @@ architectures: - "i386" - "amd64" packages: -- "libdb4.7++-dev +- "libdb4.7++-dev" - "libxxf86vm-dev" - "libgtk2.0-dev" - "libboost-all-dev" @@ -51,6 +51,7 @@ script: | cp $OUTDIR/src/COPYING $OUTDIR cd src sed 's/$(DEBUGFLAGS)//' < makefile.unix > makefile.unix.2 && mv makefile.unix.2 makefile.unix - PATH=$INSTDIR/bin:$PATH make -f makefile.unix CXX="g++ -I$INSTDIR/include -L$INSTDIR/lib" $MAKEOPTS bitcoin bitcoind + PATH=$INSTDIR/bin:$PATH make -f makefile.unix CXX="g++ -I$INSTDIR/include -L$INSTDIR/lib" $MAKEOPTS bitcoin USE_UPNP=1 + PATH=$INSTDIR/bin:$PATH make -f makefile.unix CXX="g++ -I$INSTDIR/include -L$INSTDIR/lib" $MAKEOPTS bitcoind USE_UPNP=0 mkdir -p $OUTDIR/bin/$GBUILD_BITS install -s bitcoin bitcoind $OUTDIR/bin/$GBUILD_BITS diff --git a/doc/README b/doc/README index a4df4c2..421c83c 100644 --- a/doc/README +++ b/doc/README @@ -24,6 +24,44 @@ Unpack the files into a directory and run: bin/64/bitcoin (GUI, 64-bit) bin/64/bitcoind (headless, 64-bit) + +Wallet Encryption +----------------- +Bitcoin supports native wallet encryption so that people who steal your wallet +file don't automatically get access to all of your Bitcoins. In order to enable +this feature, chose "Encrypt Wallet" from the Options menu. You will be prompted +to enter a passphrase, which will be used as the key to encrypt your wallet and +will be needed every time you wish to send Bitcoins. If you lose this passphrase, +you will lose access to spend all of the bitcoins in your wallet, no one, not even +the Bitcoin developers can recover your Bitcoins. This means you are responsible +for your own security, store your password in a secure location and do not forget +it. + +Remember that the encryption built into bitcoin only encrypts the actual keys +which are required to send your bitcoins, not the full wallet. This means that +someone who steals your wallet file will be able to see all the addresses which +belong to you, as well as the relevant transactions, you are only protected from +someone spending your coins. + +It is recommended that you backup your wallet file before you encrypt your wallet. +To do this, close the Bitcoin client and copy the wallet.dat file from ~/.bitcoin/ +on Linux, /Users/(user name)/Application Support/Bitcoin/ on Mac OSX, and +%APPDATA%/Bitcoin/ on Windows (that is /Users/(user name)/AppData/Roaming/Bitcoin on +Windows Vista and 7 and /Documents and Settings/(user name)/Application Data/Bitcoin +on Windows XP). Once you have copied that file to a safe location, reopen the +Bitcoin client and Encrypt your wallet. If everything goes fine, delete the backup +and enjoy your encrypted wallet. Note that once you encrypt your wallet, you will +never be able to go back to a version of the Bitcoin client older than 0.4. + +Keep in mind that you are always responsible for you own security. All it takes is a +slightly more advanced wallet-stealing trojan which installs a keylogger to steal +your wallet passphrase as you enter it in addition to your wallet file and you have +lost all your Bitcoins. Wallet encryption cannot keep you safe if you do not practice +good security, such as running up-to-date antivirus software, only entering your +wallet passphrase in the Bitcoin client and using the same passphrase only as your +wallet passphrase. + + See the documentation at the bitcoin wiki: https://en.bitcoin.it/wiki/Main_Page diff --git a/locale/sv/LC_MESSAGES/bitcoin.mo b/locale/sv/LC_MESSAGES/bitcoin.mo new file mode 100644 index 0000000..b61914c Binary files /dev/null and b/locale/sv/LC_MESSAGES/bitcoin.mo differ diff --git a/locale/sv/LC_MESSAGES/bitcoin.po b/locale/sv/LC_MESSAGES/bitcoin.po index fb46e15..dc635f3 100644 --- a/locale/sv/LC_MESSAGES/bitcoin.po +++ b/locale/sv/LC_MESSAGES/bitcoin.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-06-11 21:58+0100\n" -"PO-Revision-Date: 2011-06-14 09:01+0100\n" +"POT-Creation-Date: 2011-07-03 10:40+0100\n" +"PO-Revision-Date: 2011-07-03 15:13+0100\n" "Last-Translator: Codler \n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -11,130 +11,133 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-KeywordsList: _;gettext;gettext_noop\n" "X-Poedit-Basepath: .\n" -"X-Poedit-Bookmarks: 35,145,-1,-1,-1,-1,-1,-1,-1,-1\n" "X-Poedit-SearchPath-0: ../../..\n" -#: ../../../init.cpp:141 +#: ../../../init.cpp:162 msgid "Bitcoin version" msgstr "Bitcoin version" -#: ../../../init.cpp:142 +#: ../../../init.cpp:163 msgid "Usage:" msgstr "Användning:" -#: ../../../init.cpp:144 +#: ../../../init.cpp:165 msgid "Send command to -server or bitcoind\n" msgstr "Skicka kommando till -server eller bitcoind\n" -#: ../../../init.cpp:145 +#: ../../../init.cpp:166 msgid "List commands\n" msgstr "Lista kommandon\n" -#: ../../../init.cpp:146 +#: ../../../init.cpp:167 msgid "Get help for a command\n" msgstr "Få hjälp om kommandon\n" -#: ../../../init.cpp:147 +#: ../../../init.cpp:168 msgid "Options:\n" msgstr "Val:\n" -#: ../../../init.cpp:148 +#: ../../../init.cpp:169 msgid "Specify configuration file (default: bitcoin.conf)\n" msgstr "Ange konfigurationsfil (standard: bitcoin.conf)\n" -#: ../../../init.cpp:149 +#: ../../../init.cpp:170 msgid "Specify pid file (default: bitcoind.pid)\n" msgstr "Ange pid-fil (standard: bitcoind.pid)\n" -#: ../../../init.cpp:150 +#: ../../../init.cpp:171 msgid "Generate coins\n" msgstr "Generera mynt\n" -#: ../../../init.cpp:151 +#: ../../../init.cpp:172 msgid "Don't generate coins\n" msgstr "Generera inte mynt\n" -#: ../../../init.cpp:152 +#: ../../../init.cpp:173 msgid "Start minimized\n" msgstr "Starta minimerad\n" -#: ../../../init.cpp:153 +#: ../../../init.cpp:174 msgid "Specify data directory\n" msgstr "Ange data mappen\n" -#: ../../../init.cpp:154 +#: ../../../init.cpp:175 +msgid "Specify connection timeout (in milliseconds)\n" +msgstr "Ange timeout för anslutning (i millisekunder)\n" + +#: ../../../init.cpp:176 msgid "Connect through socks4 proxy\n" msgstr "Anslut via socks4 proxy\n" -#: ../../../init.cpp:155 +#: ../../../init.cpp:177 msgid "Allow DNS lookups for addnode and connect\n" msgstr "Tillåt DNS uppslagningar för addnode och connect\n" -#: ../../../init.cpp:156 +#: ../../../init.cpp:178 msgid "Add a node to connect to\n" msgstr "Lägg till en nod att ansluta till\n" -#: ../../../init.cpp:157 +#: ../../../init.cpp:179 msgid "Connect only to the specified node\n" msgstr "Anslut endast till specifik nod\n" -#: ../../../init.cpp:158 +#: ../../../init.cpp:180 msgid "Don't accept connections from outside\n" msgstr "Acceptera ej anslutningar från utsidan\n" -#: ../../../init.cpp:161 +#: ../../../init.cpp:183 msgid "Don't attempt to use UPnP to map the listening port\n" msgstr "Försöker inte använda UPnP till avslyssningsport\n" -#: ../../../init.cpp:163 +#: ../../../init.cpp:185 msgid "Attempt to use UPnP to map the listening port\n" msgstr "Försöker använda UPnP till avlyssningsport\n" -#: ../../../init.cpp:166 +#: ../../../init.cpp:188 msgid "Fee per KB to add to transactions you send\n" msgstr "Avgift per KB som läggs på transaktionen när du sänder\n" -#: ../../../init.cpp:168 +#: ../../../init.cpp:190 msgid "Accept command line and JSON-RPC commands\n" msgstr "Accepterar kommandorad och JSON-RPC kommando\n" -#: ../../../init.cpp:171 +#: ../../../init.cpp:193 msgid "Run in the background as a daemon and accept commands\n" msgstr "Kör i bakgrund som daemon och accepterar kommando\n" -#: ../../../init.cpp:173 +#: ../../../init.cpp:195 msgid "Use the test network\n" msgstr "Använd test nätverk\n" -#: ../../../init.cpp:174 +#: ../../../init.cpp:196 msgid "Username for JSON-RPC connections\n" msgstr "Användarnamn till JSON-RPC anslutning\n" -#: ../../../init.cpp:175 +#: ../../../init.cpp:197 msgid "Password for JSON-RPC connections\n" msgstr "Lösenord till JSON-RPC anslutning\n" -#: ../../../init.cpp:176 +#: ../../../init.cpp:198 msgid "Listen for JSON-RPC connections on (default: 8332)\n" msgstr "Lyssnar på JSON-RPC anslutningar på (standard: 8332)\n" -#: ../../../init.cpp:177 +#: ../../../init.cpp:199 msgid "Allow JSON-RPC connections from specified IP address\n" msgstr "Tillåt JSON-RPC anslutningar från specifik IP-adress\n" -#: ../../../init.cpp:178 +#: ../../../init.cpp:200 msgid "Send commands to node running on (default: 127.0.0.1)\n" msgstr "Skicka kommando till noden som körs på (standard: 127.0.0.1)\n" -#: ../../../init.cpp:179 +#: ../../../init.cpp:201 msgid "Set key pool size to (default: 100)\n" msgstr "Sätt nyckel pool storlek till (standard: 100)\n" -#: ../../../init.cpp:180 +#: ../../../init.cpp:202 msgid "Rescan the block chain for missing wallet transactions\n" msgstr "Scanna om block kedja efter saknade plånbokstransaktioner\n" -#: ../../../init.cpp:184 +#: ../../../init.cpp:206 msgid "" "\n" "SSL options: (see the Bitcoin Wiki for SSL setup instructions)\n" @@ -142,106 +145,71 @@ msgstr "" "\n" "SSL val: (Se på Bitcoin Wiki för SSL installation instruktioner)\n" -#: ../../../init.cpp:185 +#: ../../../init.cpp:207 msgid "Use OpenSSL (https) for JSON-RPC connections\n" msgstr "Använd OpenSSL (https) till JSON-RPC anslutningar\n" -#: ../../../init.cpp:186 +#: ../../../init.cpp:208 msgid "Server certificate file (default: server.cert)\n" msgstr "Server certifikatfil (standard: server.cert)\n" -#: ../../../init.cpp:187 +#: ../../../init.cpp:209 msgid "Server private key (default: server.pem)\n" msgstr "Server privat nyckel (standard: server.pem)\n" -#: ../../../init.cpp:188 +#: ../../../init.cpp:210 msgid "Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)\n" msgstr "Acceptabla krypteringar (standard: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)\n" -#: ../../../init.cpp:192 +#: ../../../init.cpp:214 msgid "This help message\n" msgstr "Detta hjälpmeddelande\n" -#: ../../../init.cpp:329 +#: ../../../init.cpp:351 #, c-format msgid "Cannot obtain a lock on data directory %s. Bitcoin is probably already running." msgstr "Kan inte sätta lås på data mappen %s. Bitcoin körs troligen redan." -#: ../../../init.cpp:355 +#: ../../../init.cpp:377 msgid "Error loading addr.dat \n" msgstr "Fel vid laddning av addr.dat \n" -#: ../../../init.cpp:361 +#: ../../../init.cpp:383 msgid "Error loading blkindex.dat \n" msgstr "Fel vid laddning av blkindex.dat \n" -#: ../../../init.cpp:368 +#: ../../../init.cpp:391 msgid "Error loading wallet.dat \n" msgstr "Fel vid laddning av wallet.dat \n" -#: ../../../init.cpp:448 +#: ../../../init.cpp:481 msgid "Invalid -proxy address" msgstr "Ogiltig -proxy adress" -#: ../../../init.cpp:471 +#: ../../../init.cpp:506 msgid "Invalid amount for -paytxfee=" msgstr "Ogiltig belopp på -paytxfee=" -#: ../../../init.cpp:475 +#: ../../../init.cpp:510 msgid "Warning: -paytxfee is set very high. This is the transaction fee you will pay if you send a transaction." msgstr "Varning: -paytxfee är satt väldigt högt. Denna är transaktionsavgiften som du kommer att betala om du skickar en transaktion." -#: ../../../main.cpp:1866 +#: ../../../main.cpp:1430 msgid "Warning: Disk space is low " msgstr "Varning: Diskutrymme är låg " -#: ../../../main.cpp:3999 -#, c-format -msgid "Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds " -msgstr "Fel: Denna transaktion kräver en transaktionsavgift minst %s för dess belopp, komplexitet, eller användning av nyligen mottagna pengar " - -#: ../../../main.cpp:4001 -msgid "Error: Transaction creation failed " -msgstr "Fel: Misslyckades att skapa transaktion " - -#: ../../../main.cpp:4006 -#: ../../../ui.cpp:1951 -#: ../../../ui.cpp:1956 -#: ../../../ui.cpp:2102 -#: ../../../ui.cpp:2255 -msgid "Sending..." -msgstr "Skickar..." - -#: ../../../main.cpp:4010 -msgid "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." -msgstr "Fel: Transaktionen blev nekad. Detta kan hända om några av dina mynt i din plånbok var redan spenderad. Till exempel om du använde en kopia av wallet.dat och mynten var redan spenderad i kopian, men som inte var markerad som spenderad här." - -#: ../../../main.cpp:4023 -msgid "Invalid amount" -msgstr "Ogiltig belopp" - -#: ../../../main.cpp:4025 -#: ../../../ui.cpp:2169 -#: ../../../ui.cpp:2240 -msgid "Insufficient funds" -msgstr "Otillräckligt med pengar" - -#: ../../../main.cpp:4030 -msgid "Invalid bitcoin address" -msgstr "Ogiltig bitcoin adress" - -#: ../../../net.cpp:1505 +#: ../../../net.cpp:1598 #, c-format msgid "Unable to bind to port %d on this computer. Bitcoin is probably already running." msgstr "Det gick inte att binda till port %d på denna dator. Bitcoin körs troligen redan." -#: ../../../rpc.cpp:1816 -#: ../../../rpc.cpp:1818 +#: ../../../rpc.cpp:1828 +#: ../../../rpc.cpp:1830 #, c-format msgid "To use the %s option" msgstr "För att använda %s val" -#: ../../../rpc.cpp:1820 +#: ../../../rpc.cpp:1832 #, c-format msgid "" "Warning: %s, you must set rpcpassword=\n" @@ -252,7 +220,7 @@ msgstr "" "i konfigurationsfilen: %s\n" "Om filen inte existerar, skapa med ägare med filbehörigheten -readable-only.\n" -#: ../../../rpc.cpp:1988 +#: ../../../rpc.cpp:2005 #, c-format msgid "" "You must set rpcpassword= in the configuration file:\n" @@ -263,347 +231,360 @@ msgstr "" "%s\n" "Om filen inte existerar, skapa med ägare med filbehörighete -readable-only." -#: ../../../ui.cpp:211 +#: ../../../ui.cpp:216 #, c-format msgid "This transaction is over the size limit. You can still send it for a fee of %s, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee?" msgstr "Denna transaktion är över storleksbegränsningen. Du kan fortfarande skicka mot en avgift på %s, som går till noderna som bearbetar din transaktion och hjälper att stödja nätverket. Vill du fortfarande betala avgiften?" -#: ../../../ui.cpp:311 +#: ../../../ui.cpp:316 msgid "Status" msgstr "Status" -#: ../../../ui.cpp:312 +#: ../../../ui.cpp:317 msgid "Date" msgstr "Datum" -#: ../../../ui.cpp:313 +#: ../../../ui.cpp:318 msgid "Description" msgstr "Beskrivning" -#: ../../../ui.cpp:314 +#: ../../../ui.cpp:319 msgid "Debit" -msgstr "Debitera" +msgstr "Debet" -#: ../../../ui.cpp:315 +#: ../../../ui.cpp:320 msgid "Credit" -msgstr "Kreditera" +msgstr "Kredit" -#: ../../../ui.cpp:521 +#: ../../../ui.cpp:526 #, c-format msgid "Open for %d blocks" -msgstr "Öppna för %d blocks" +msgstr "Öppen för %d block" -#: ../../../ui.cpp:523 +#: ../../../ui.cpp:528 #, c-format msgid "Open until %s" msgstr "Öppen tills %s" -#: ../../../ui.cpp:529 +#: ../../../ui.cpp:534 #, c-format msgid "%d/offline?" msgstr "%d/offline?" -#: ../../../ui.cpp:531 +#: ../../../ui.cpp:536 #, c-format msgid "%d/unconfirmed" msgstr "%d/obekräftade" -#: ../../../ui.cpp:533 +#: ../../../ui.cpp:538 #, c-format msgid "%d confirmations" msgstr "%d bekräftelser" -#: ../../../ui.cpp:618 +#: ../../../ui.cpp:623 msgid "Generated" msgstr "Genererad" -#: ../../../ui.cpp:626 -#, fuzzy, c-format +#: ../../../ui.cpp:631 +#, c-format msgid "Generated (%s matures in %d more blocks)" -msgstr "Genererad (%s mogna i %d fler block)" +msgstr "Genererad (%s mognar om %d block)" -#: ../../../ui.cpp:630 +#: ../../../ui.cpp:635 msgid "Generated - Warning: This block was not received by any other nodes and will probably not be accepted!" msgstr "Genererad - Varning: Denna block har inte blivit mottagen av någon annan nod och kommer troligtvis inte bli accepterad" -#: ../../../ui.cpp:634 +#: ../../../ui.cpp:639 msgid "Generated (not accepted)" msgstr "Genererad (ej accepterad)" -#: ../../../ui.cpp:644 +#: ../../../ui.cpp:649 msgid "From: " msgstr "Från: " -#: ../../../ui.cpp:668 +#: ../../../ui.cpp:673 msgid "Received with: " msgstr "Mottagen med: " -#: ../../../ui.cpp:714 +#: ../../../ui.cpp:719 msgid "Payment to yourself" msgstr "Betalning till dig själv" -#: ../../../ui.cpp:748 +#: ../../../ui.cpp:753 msgid "To: " msgstr "Till: " -#: ../../../ui.cpp:1063 +#: ../../../ui.cpp:1068 msgid " Generating" msgstr " Generering" -#: ../../../ui.cpp:1065 +#: ../../../ui.cpp:1070 msgid "(not connected)" msgstr "(inte ansluten)" -#: ../../../ui.cpp:1068 +#: ../../../ui.cpp:1073 #, c-format msgid " %d connections %d blocks %d transactions" msgstr " %d anslutningar %d block %d transaktioner" -#: ../../../ui.cpp:1173 -#: ../../../ui.cpp:2566 +#: ../../../ui.cpp:1178 +#: ../../../ui.cpp:2577 msgid "New Receiving Address" msgstr "Ny Mottagningsadress" -#: ../../../ui.cpp:1174 -#: ../../../ui.cpp:2567 +#: ../../../ui.cpp:1179 +#: ../../../ui.cpp:2578 msgid "" "You should use a new address for each payment you receive.\n" "\n" "Label" msgstr "" -"Du borde använda en ny adress för varje betalning som du mottar.\n" +"Du borde använda en ny adress för varje betalning som du mottagit.\n" "\n" "Etikett" -#: ../../../ui.cpp:1246 +#: ../../../ui.cpp:1252 msgid "Status: " msgstr "Status: " -#: ../../../ui.cpp:1251 +#: ../../../ui.cpp:1257 msgid ", has not been successfully broadcast yet" -msgstr ", has not been successfully broadcast yet" +msgstr ", har inte lyckats broadcast än" -#: ../../../ui.cpp:1253 +#: ../../../ui.cpp:1259 #, c-format msgid ", broadcast through %d node" msgstr ", broadcast genom %d nod" -#: ../../../ui.cpp:1255 +#: ../../../ui.cpp:1261 #, c-format msgid ", broadcast through %d nodes" msgstr ", broadcast genom %d noder" -#: ../../../ui.cpp:1259 +#: ../../../ui.cpp:1265 msgid "Date: " msgstr "Datum: " -#: ../../../ui.cpp:1267 +#: ../../../ui.cpp:1273 msgid "Source: Generated
" msgstr "Källa: Genererad
" -#: ../../../ui.cpp:1273 -#: ../../../ui.cpp:1291 +#: ../../../ui.cpp:1279 +#: ../../../ui.cpp:1297 msgid "From: " msgstr "Från: " -#: ../../../ui.cpp:1291 +#: ../../../ui.cpp:1297 msgid "unknown" msgstr "okänd" -#: ../../../ui.cpp:1292 -#: ../../../ui.cpp:1316 -#: ../../../ui.cpp:1375 +#: ../../../ui.cpp:1298 +#: ../../../ui.cpp:1322 +#: ../../../ui.cpp:1381 msgid "To: " msgstr "Till: " -#: ../../../ui.cpp:1295 +#: ../../../ui.cpp:1301 msgid " (yours, label: " msgstr " (din, etikett: " -#: ../../../ui.cpp:1297 +#: ../../../ui.cpp:1303 msgid " (yours)" msgstr " (ditt)" -#: ../../../ui.cpp:1334 -#: ../../../ui.cpp:1346 -#: ../../../ui.cpp:1392 -#: ../../../ui.cpp:1409 +#: ../../../ui.cpp:1340 +#: ../../../ui.cpp:1352 +#: ../../../ui.cpp:1398 +#: ../../../ui.cpp:1415 msgid "Credit: " msgstr "Kredit: " -#: ../../../ui.cpp:1336 -#, fuzzy, c-format +#: ../../../ui.cpp:1342 +#, c-format msgid "(%s matures in %d more blocks)" -msgstr "(%s matures in %d more blocks)" +msgstr "(%s mognar om %d block)" -#: ../../../ui.cpp:1338 +#: ../../../ui.cpp:1344 msgid "(not accepted)" msgstr "(ej accepterad)" -#: ../../../ui.cpp:1383 -#: ../../../ui.cpp:1391 -#: ../../../ui.cpp:1406 +#: ../../../ui.cpp:1389 +#: ../../../ui.cpp:1397 +#: ../../../ui.cpp:1412 msgid "Debit: " -msgstr "Debit: " +msgstr "Debet: " -#: ../../../ui.cpp:1397 +#: ../../../ui.cpp:1403 msgid "Transaction fee: " msgstr "Transaktionsavgift: " -#: ../../../ui.cpp:1413 -#, fuzzy +#: ../../../ui.cpp:1419 msgid "Net amount: " msgstr "Nät belopp: " -#: ../../../ui.cpp:1420 +#: ../../../ui.cpp:1426 msgid "Message:" msgstr "Meddelande:" -#: ../../../ui.cpp:1422 +#: ../../../ui.cpp:1428 msgid "Comment:" msgstr "Kommentar:" -#: ../../../ui.cpp:1425 +#: ../../../ui.cpp:1431 msgid "Generated coins must wait 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, it will change to \"not accepted\" and not be spendable. This may occasionally happen if another node generates a block within a few seconds of yours." msgstr "Genererad mynt måste vänta 120 block innan dem kan användas.När du genererat denna block, skickades en broadcast till nätet och lades till block kedjan. Om den misslyckas att lägga till kedjan så kommer det ändras till \"not accepted\" och är icke brukbar. Detta kan hända om en annan nod genererat en block några sekunder efter din." -#: ../../../ui.cpp:1605 +#: ../../../ui.cpp:1611 msgid "Cannot write autostart/bitcoin.desktop file" msgstr "Kan inte skriva autostart/bitcoin.desktop fil" -#: ../../../ui.cpp:1641 +#: ../../../ui.cpp:1647 msgid "Main" -msgstr "Huvud" +msgstr "Allmänt" -#: ../../../ui.cpp:1651 +#: ../../../ui.cpp:1657 msgid "&Start Bitcoin on window system startup" -msgstr "&Starta Bitcoin vid uppstart i windows" +msgstr "&Starta Bitcoin vid system uppstart" -#: ../../../ui.cpp:1658 +#: ../../../ui.cpp:1664 msgid "&Minimize on close" msgstr "&Minimera vid stängning" -#: ../../../ui.cpp:1800 +#: ../../../ui.cpp:1806 #, c-format msgid "version %s" msgstr "version %s" -#: ../../../ui.cpp:1923 +#: ../../../ui.cpp:1929 msgid "Error in amount " msgstr "Fel i belopp " -#: ../../../ui.cpp:1923 -#: ../../../ui.cpp:1928 -#: ../../../ui.cpp:1933 -#: ../../../ui.cpp:1968 +#: ../../../ui.cpp:1929 +#: ../../../ui.cpp:1934 +#: ../../../ui.cpp:1939 +#: ../../../ui.cpp:1974 #: ../../../uibase.cpp:55 msgid "Send Coins" msgstr "Skicka mynt" -#: ../../../ui.cpp:1928 +#: ../../../ui.cpp:1934 msgid "Amount exceeds your balance " msgstr "Belopp överskrider din balans " -#: ../../../ui.cpp:1933 +#: ../../../ui.cpp:1939 msgid "Total exceeds your balance when the " -msgstr "Totals överskrider din balans när " +msgstr "Totalt överskrider din balans när " -#: ../../../ui.cpp:1933 +#: ../../../ui.cpp:1939 msgid " transaction fee is included " msgstr " transaktionsavgift är inkluderad " -#: ../../../ui.cpp:1951 +#: ../../../ui.cpp:1957 msgid "Payment sent " msgstr "Betalning skickad " -#: ../../../ui.cpp:1968 +#: ../../../ui.cpp:1957 +#: ../../../ui.cpp:1962 +#: ../../../ui.cpp:2108 +#: ../../../ui.cpp:2261 +#: ../../../wallet.cpp:924 +msgid "Sending..." +msgstr "Skickar..." + +#: ../../../ui.cpp:1974 msgid "Invalid address " msgstr "Ogiltig adress " -#: ../../../ui.cpp:2023 +#: ../../../ui.cpp:2029 #, c-format msgid "Sending %s to %s" msgstr "Skickar %s till %s" -#: ../../../ui.cpp:2096 -#: ../../../ui.cpp:2129 +#: ../../../ui.cpp:2102 +#: ../../../ui.cpp:2135 msgid "CANCELLED" msgstr "AVBRUTEN" -#: ../../../ui.cpp:2100 +#: ../../../ui.cpp:2106 msgid "Cancelled" msgstr "Avbruten" -#: ../../../ui.cpp:2102 +#: ../../../ui.cpp:2108 msgid "Transfer cancelled " msgstr "Överföring avbruten " -#: ../../../ui.cpp:2155 +#: ../../../ui.cpp:2161 msgid "Error: " msgstr "Fel: " -#: ../../../ui.cpp:2174 +#: ../../../ui.cpp:2175 +#: ../../../ui.cpp:2246 +#: ../../../wallet.cpp:943 +msgid "Insufficient funds" +msgstr "Otillräckligt med pengar" + +#: ../../../ui.cpp:2180 msgid "Connecting..." msgstr "Ansluter..." -#: ../../../ui.cpp:2179 +#: ../../../ui.cpp:2185 msgid "Unable to connect" msgstr "Det gick inte att ansluta" -#: ../../../ui.cpp:2184 +#: ../../../ui.cpp:2190 msgid "Requesting public key..." msgstr "Efterfrågar publik nyckel..." -#: ../../../ui.cpp:2196 +#: ../../../ui.cpp:2202 msgid "Received public key..." msgstr "Mottagen publik nyckel..." -#: ../../../ui.cpp:2210 +#: ../../../ui.cpp:2216 msgid "Recipient is not accepting transactions sent by IP address" msgstr "Mottagaren accepterar inte transaktioner skickat från IP adress" -#: ../../../ui.cpp:2212 +#: ../../../ui.cpp:2218 msgid "Transfer was not accepted" msgstr "Överföringen var inte accepterad" -#: ../../../ui.cpp:2221 +#: ../../../ui.cpp:2227 msgid "Invalid response received" msgstr "Ogiltig respons mottagen" -#: ../../../ui.cpp:2236 +#: ../../../ui.cpp:2242 msgid "Creating transaction..." msgstr "Skapar transation..." -#: ../../../ui.cpp:2248 +#: ../../../ui.cpp:2254 #, c-format msgid "This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds" msgstr "Denna transaktion kräver en transaktionsavgift minst %s för dess belopp, komplexitet, eller användning av nyligen mottagna pengar" -#: ../../../ui.cpp:2250 +#: ../../../ui.cpp:2256 msgid "Transaction creation failed" msgstr "Misslyckades skapa transaktion" -#: ../../../ui.cpp:2257 +#: ../../../ui.cpp:2263 msgid "Transaction aborted" msgstr "Transaktion avbruten" -#: ../../../ui.cpp:2265 +#: ../../../ui.cpp:2271 msgid "Lost connection, transaction cancelled" msgstr "Förlorad anslutning, transaktionen avbruten" -#: ../../../ui.cpp:2281 +#: ../../../ui.cpp:2287 msgid "Sending payment..." msgstr "Skickar betalning..." -#: ../../../ui.cpp:2287 +#: ../../../ui.cpp:2293 msgid "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." msgstr "Transaktionen blev nekad. Detta kan hända om några av dina mynt i din plånbok var redan spenderad. Till exempel om du använde en kopia av wallet.dat och mynten var redan spenderad i kopian, men som inte var markerad som spenderad här." -#: ../../../ui.cpp:2296 +#: ../../../ui.cpp:2302 msgid "Waiting for confirmation..." msgstr "Väntar på bekräftelse..." -#: ../../../ui.cpp:2314 +#: ../../../ui.cpp:2320 msgid "" "The payment was sent, but the recipient was unable to verify it.\n" "The transaction is recorded and will credit to the recipient,\n" @@ -613,84 +594,84 @@ msgstr "" "Transaktionen är registrerad och kommer att kreditera mottagaren,\n" "men kommentar informationen kommer att vara tom." -#: ../../../ui.cpp:2323 +#: ../../../ui.cpp:2329 msgid "Payment was sent, but an invalid response was received" msgstr "Betalning var skickad, men en ogiltig respons var mottagen" -#: ../../../ui.cpp:2329 +#: ../../../ui.cpp:2335 msgid "Payment completed" msgstr "Betalning slutförd" -#: ../../../ui.cpp:2371 -#: ../../../ui.cpp:2517 -#: ../../../ui.cpp:2554 +#: ../../../ui.cpp:2377 +#: ../../../ui.cpp:2525 +#: ../../../ui.cpp:2565 msgid "Name" msgstr "Namn" -#: ../../../ui.cpp:2372 -#: ../../../ui.cpp:2517 -#: ../../../ui.cpp:2554 +#: ../../../ui.cpp:2378 +#: ../../../ui.cpp:2525 +#: ../../../ui.cpp:2565 msgid "Address" msgstr "Adress" -#: ../../../ui.cpp:2374 -#: ../../../ui.cpp:2529 +#: ../../../ui.cpp:2380 +#: ../../../ui.cpp:2537 msgid "Label" msgstr "Etikett" -#: ../../../ui.cpp:2375 +#: ../../../ui.cpp:2381 #: ../../../uibase.cpp:837 msgid "Bitcoin Address" msgstr "Bitcoin Adress" -#: ../../../ui.cpp:2499 +#: ../../../ui.cpp:2507 msgid "This is one of your own addresses for receiving payments and cannot be entered in the address book. " msgstr "Denna är en av dina egna adresser för mottagning av betalningar och kan inte slås in i adressboken. " -#: ../../../ui.cpp:2517 -#: ../../../ui.cpp:2523 +#: ../../../ui.cpp:2525 +#: ../../../ui.cpp:2531 msgid "Edit Address" msgstr "Ändra Adress" -#: ../../../ui.cpp:2529 +#: ../../../ui.cpp:2537 msgid "Edit Address Label" msgstr "Ändra Address Etikett" -#: ../../../ui.cpp:2554 -#: ../../../ui.cpp:2560 +#: ../../../ui.cpp:2565 +#: ../../../ui.cpp:2571 msgid "Add Address" msgstr "Lägg till Adress" -#: ../../../ui.cpp:2637 +#: ../../../ui.cpp:2649 msgid "Bitcoin" msgstr "Bitcoin" -#: ../../../ui.cpp:2639 +#: ../../../ui.cpp:2651 msgid "Bitcoin - Generating" msgstr "Bitcoin - Genererar" -#: ../../../ui.cpp:2641 +#: ../../../ui.cpp:2653 msgid "Bitcoin - (not connected)" msgstr "Bitcoin - (ej ansluten)" -#: ../../../ui.cpp:2720 +#: ../../../ui.cpp:2732 msgid "&Open Bitcoin" msgstr "&Öppna Bitcoin" -#: ../../../ui.cpp:2721 +#: ../../../ui.cpp:2733 msgid "&Send Bitcoins" msgstr "&Skicka Bitcoins" -#: ../../../ui.cpp:2722 +#: ../../../ui.cpp:2734 msgid "O&ptions..." msgstr "&Inställningar..." -#: ../../../ui.cpp:2725 +#: ../../../ui.cpp:2737 #: ../../../uibase.cpp:25 msgid "E&xit" msgstr "&Avsluta" -#: ../../../ui.cpp:2951 +#: ../../../ui.cpp:2963 msgid "Program has crashed and will terminate. " msgstr "Programmet har krachat och kommer att avslutas. " @@ -791,7 +772,6 @@ msgid "&Minimize to the tray instead of the taskbar" msgstr "&Minimerar till systemfältet istället för aktivitetsfält" #: ../../../uibase.cpp:351 -#, fuzzy msgid "Map port using &UPnP" msgstr "Mapp port använder &UPnP" @@ -812,9 +792,8 @@ msgid " &Port:" msgstr " &Port:" #: ../../../uibase.cpp:392 -#, fuzzy msgid "Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended." -msgstr "Frivillig transaktionsavgift per KB som garanterar att din transaktion bearbetas snabbt. Flesta transaktioner är 1KB. Most transactions are 1KB. Avgift 0.01 rekommenderas." +msgstr "Frivillig transaktionsavgift per KB som garanterar att din transaktion bearbetas snabbt. Flesta transaktioner är 1KB. Avgift 0.01 rekommenderas." #: ../../../uibase.cpp:399 msgid "Pay transaction fee:" @@ -850,7 +829,6 @@ msgid "version" msgstr "version" #: ../../../uibase.cpp:525 -#, fuzzy msgid "" "Copyright (c) 2009-2011 Bitcoin Developers\n" "\n" @@ -865,14 +843,16 @@ msgid "" msgstr "" "Copyright (c) 2009-2011 Bitcoin Developers\n" "\n" -"This is experimental software.\n" +"Detta är en experimentell mjukvara.\n" "\n" -"Distributed under the MIT/X11 software license, see the accompanying file \n" -"license.txt or http://www.opensource.org/licenses/mit-license.php.\n" +"Distribuerad under MIT/X11 mjukvarulicens, se medföljande fil \n" +"license.txt eller http://www.opensource.org/licenses/mit-license.php.\n" "\n" -"This product includes software developed by the OpenSSL Project for use in the \n" -"OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by \n" -"Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard." +"Denna produkt inkluderar mjukvara utvecklad av OpenSSL Project för användning i \n" +"OpenSSL Toolkit (http://www.openssl.org/) och kryptografisk mjukvara skriven av \n" +"Eric Young (eay@cryptsoft.com) och UPnP mjukvara skriven av Thomas Bernard.\n" +"\n" +"Översatt av Han Lin Yap." #: ../../../uibase.cpp:581 msgid "Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)" @@ -895,9 +875,8 @@ msgid "&Amount:" msgstr "&Belopp:" #: ../../../uibase.cpp:630 -#, fuzzy msgid "T&ransfer:" -msgstr "T&ransfer:" +msgstr "&Överföring:" #: ../../../uibase.cpp:636 msgid " Standard" @@ -918,9 +897,8 @@ msgstr "" "Ansluter..." #: ../../../uibase.cpp:761 -#, fuzzy msgid "These are your Bitcoin 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. The highlighted address is displayed in the main window." -msgstr "Dessa är dina Bitcoin adresser för mottagning av betalningar. Du rekommenderas att ge olika till varje avsändare så du kan ha koll på vem som betalar dig. Den markerade adressen är visar i huvudfönstret." +msgstr "Dessa är dina Bitcoin adresser för mottagning av betalningar. Du rekommenderas att ge olika till varje avsändare så du kan ha koll på vem som betalar dig. Den markerade adressen visas i huvudfönstret." #: ../../../uibase.cpp:774 #: ../../../uibase.cpp:886 @@ -937,9 +915,8 @@ msgid "Sending" msgstr "Skickar" #: ../../../uibase.cpp:857 -#, fuzzy msgid "These are your Bitcoin addresses for receiving payments. You can give a different one to each sender to keep track of who is paying you. The highlighted address will be displayed in the main window." -msgstr "Dessa är dina Bitcoin adresser för mottagning av betalningar. Du kan ge olika till varje avsändare så du kan ha koll på vem som betalar dig. Den markerade adressen är visar i huvudfönstret." +msgstr "Dessa är dina Bitcoin adresser för mottagning av betalningar. Du kan ge olika till varje avsändare så du kan ha koll på vem som betalar dig. Den markerade adressen visas i huvudfönstret." #: ../../../uibase.cpp:870 msgid "Receiving" @@ -949,15 +926,35 @@ msgstr "Mottagning" msgid "&Delete" msgstr "&Ta bort" -#: ../../../util.cpp:866 -#, fuzzy +#: ../../../util.cpp:874 msgid "Warning: Please check that your computer's date and time are correct. If your clock is wrong Bitcoin will not work properly." -msgstr "Varning: Var god och kolla att din dator har rätt datum och klockslag. Om din klocka är fel så kommer Bitcoin inte att fungera." +msgstr "Varning: Var god och kolla att din dator har rätt datum och klockslag. Om din klocka är fel så kommer Bitcoin inte att fungera ordentligt." -#: ../../../util.cpp:899 +#: ../../../util.cpp:908 msgid "beta" msgstr "beta" +#: ../../../wallet.cpp:917 +#, c-format +msgid "Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds " +msgstr "Fel: Denna transaktion kräver en transaktionsavgift minst %s för dess belopp, komplexitet, eller användning av nyligen mottagna pengar " + +#: ../../../wallet.cpp:919 +msgid "Error: Transaction creation failed " +msgstr "Fel: Misslyckades att skapa transaktion " + +#: ../../../wallet.cpp:928 +msgid "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." +msgstr "Fel: Transaktionen blev nekad. Detta kan hända om några av dina mynt i din plånbok var redan spenderad. Till exempel om du använde en kopia av wallet.dat och mynten var redan spenderad i kopian, men som inte var markerad som spenderad här." + +#: ../../../wallet.cpp:941 +msgid "Invalid amount" +msgstr "Ogiltig belopp" + +#: ../../../wallet.cpp:948 +msgid "Invalid bitcoin address" +msgstr "Ogiltig bitcoin adress" + #: ../../../uibase.h:147 msgid "Transaction Details" msgstr "Transaktion detaljer" @@ -974,5 +971,3 @@ msgstr "Om Bitcoin" msgid "Your Bitcoin Addresses" msgstr "Dina Bitcoin Adresser" -#~ msgid " beta" -#~ msgstr " beta" diff --git a/share/pixmaps/bitcoin.ico b/share/pixmaps/bitcoin.ico index cdec9a0..734d217 100644 Binary files a/share/pixmaps/bitcoin.ico and b/share/pixmaps/bitcoin.ico differ diff --git a/share/pixmaps/nsis-header.bmp b/share/pixmaps/nsis-header.bmp new file mode 100644 index 0000000..9ab0ce2 Binary files /dev/null and b/share/pixmaps/nsis-header.bmp differ diff --git a/share/pixmaps/nsis-wizard.bmp b/share/pixmaps/nsis-wizard.bmp new file mode 100644 index 0000000..71255c6 Binary files /dev/null and b/share/pixmaps/nsis-wizard.bmp differ diff --git a/share/setup.nsi b/share/setup.nsi index 8677d33..888c8ea 100644 --- a/share/setup.nsi +++ b/share/setup.nsi @@ -1,6 +1,7 @@ Name Bitcoin RequestExecutionLevel highest +SetCompressor /SOLID lzma # General Symbol Definitions !define REGKEY "SOFTWARE\$(^Name)" @@ -10,6 +11,10 @@ RequestExecutionLevel highest # 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} @@ -17,6 +22,7 @@ RequestExecutionLevel highest !define MUI_STARTMENUPAGE_DEFAULTFOLDER Bitcoin !define MUI_FINISHPAGE_RUN $INSTDIR\bitcoin.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 @@ -43,6 +49,7 @@ OutFile bitcoin-0.3.24-win32-setup.exe InstallDir $PROGRAMFILES\Bitcoin CRCCheck on XPStyle on +BrandingText " " ShowInstDetails show VIProductVersion 0.3.24.0 VIAddVersionKey ProductName Bitcoin diff --git a/share/uiproject.fbp b/share/uiproject.fbp index d9d4638..d5e6b1e 100644 --- a/share/uiproject.fbp +++ b/share/uiproject.fbp @@ -167,6 +167,36 @@ 0 1 + wxID_ANY + wxITEM_NORMAL + &Encrypt Wallet... + m_menuOptionsEncryptWallet + public + + + OnMenuOptionsEncryptWallet + + + + + 0 + 1 + + wxID_ANY + wxITEM_NORMAL + &Change Wallet Encryption Passphrase... + m_menuOptionsChangeWalletPassphrase + public + + + OnMenuOptionsChangeWalletPassphrase + + + + + 0 + 1 + wxID_PREFERENCES wxITEM_NORMAL &Options... diff --git a/src/crypter.cpp b/src/crypter.cpp new file mode 100644 index 0000000..9a8e6ca --- /dev/null +++ b/src/crypter.cpp @@ -0,0 +1,132 @@ +// 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. + +#include +#include +#include +#include +#include "headers.h" +#ifdef __WXMSW__ +#include +#endif + +#include "crypter.h" +#include "main.h" +#include "util.h" + +bool CCrypter::SetKeyFromPassphrase(const std::string& strKeyData, const std::vector& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod) +{ + if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE) + return false; + + // Try to keep the keydata 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. + mlock(&chKey[0], sizeof chKey); + mlock(&chIV[0], sizeof chIV); + + 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 != WALLET_CRYPTO_KEY_SIZE) + { + memset(&chKey, 0, sizeof chKey); + memset(&chIV, 0, 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; + + // Try to keep the keydata 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. + mlock(&chKey[0], sizeof chKey); + mlock(&chIV[0], sizeof chIV); + + 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; + + EVP_CIPHER_CTX_init(&ctx); + EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV); + + EVP_EncryptUpdate(&ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen); + EVP_EncryptFinal_ex(&ctx, (&vchCiphertext[0])+nCLen, &nFLen); + + EVP_CIPHER_CTX_cleanup(&ctx); + + 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; + + EVP_CIPHER_CTX_init(&ctx); + EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV); + + EVP_DecryptUpdate(&ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen); + EVP_DecryptFinal_ex(&ctx, (&vchPlaintext[0])+nPLen, &nFLen); + + EVP_CIPHER_CTX_cleanup(&ctx); + + vchPlaintext.resize(nPLen + nFLen); + return true; +} + + +bool EncryptSecret(CKeyingMaterial& vMasterKey, const CSecret &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((CKeyingMaterial)vchPlaintext, vchCiphertext); +} + +bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector& vchCiphertext, const uint256& nIV, CSecret& 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..5b95ea4 --- /dev/null +++ b/src/crypter.h @@ -0,0 +1,96 @@ +// 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. +#ifndef __CRYPTER_H__ +#define __CRYPTER_H__ + +#include "key.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 is 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 private key as the IV, and the +master key's key as the encryption key. +*/ + +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; + +class CCrypter +{ +private: + unsigned char chKey[WALLET_CRYPTO_KEY_SIZE]; + unsigned char chIV[WALLET_CRYPTO_KEY_SIZE]; + bool fKeySet; + +public: + bool SetKeyFromPassphrase(const std::string &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() + { + memset(&chKey, 0, sizeof chKey); + memset(&chIV, 0, sizeof chIV); + munlock(&chKey, sizeof chKey); + munlock(&chIV, sizeof chIV); + fKeySet = false; + } + + CCrypter() + { + fKeySet = false; + } + + ~CCrypter() + { + CleanKey(); + } +}; + +bool EncryptSecret(CKeyingMaterial& vMasterKey, const CSecret &vchPlaintext, const uint256& nIV, std::vector &vchCiphertext); +bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector &vchCiphertext, const uint256& nIV, CSecret &vchPlaintext); + +#endif diff --git a/src/db.cpp b/src/db.cpp index 9e13727..1dea92e 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -685,7 +685,7 @@ int CWalletDB::LoadWallet(CWallet* pwallet) //// todo: shouldn't we catch exceptions and try to recover and continue? CRITICAL_BLOCK(pwallet->cs_mapWallet) - CRITICAL_BLOCK(pwallet->cs_mapKeys) + CRITICAL_BLOCK(pwallet->cs_KeyStore) { // Get cursor Dbc* pcursor = GetCursor(); @@ -765,14 +765,42 @@ int CWalletDB::LoadWallet(CWallet* pwallet) { vector vchPubKey; ssKey >> vchPubKey; - CWalletKey wkey; + CKey key; if (strType == "key") - ssValue >> wkey.vchPrivKey; + { + CPrivKey pkey; + ssValue >> pkey; + key.SetPrivKey(pkey); + } else + { + CWalletKey wkey; ssValue >> wkey; - - pwallet->mapKeys[vchPubKey] = wkey.vchPrivKey; - mapPubKeys[Hash160(vchPubKey)] = vchPubKey; + key.SetPrivKey(wkey.vchPrivKey); + } + if (!pwallet->LoadKey(key)) + return false; + } + else if (strType == "mkey") + { + unsigned int nID; + ssKey >> nID; + CMasterKey kMasterKey; + ssValue >> kMasterKey; + if(pwallet->mapMasterKeys.count(nID) != 0) + 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)) + return false; } else if (strType == "defaultkey") { @@ -800,7 +828,6 @@ int CWalletDB::LoadWallet(CWallet* pwallet) if (strKey == "fGenerateBitcoins") ssValue >> fGenerateBitcoins; #endif if (strKey == "nTransactionFee") ssValue >> nTransactionFee; - if (strKey == "addrIncoming") ssValue >> addrIncoming; if (strKey == "fLimitProcessors") ssValue >> fLimitProcessors; if (strKey == "nLimitProcessors") ssValue >> nLimitProcessors; if (strKey == "fMinimizeToTray") ssValue >> fMinimizeToTray; @@ -826,7 +853,6 @@ int CWalletDB::LoadWallet(CWallet* pwallet) printf("nFileVersion = %d\n", nFileVersion); printf("fGenerateBitcoins = %d\n", fGenerateBitcoins); printf("nTransactionFee = %"PRI64d"\n", nTransactionFee); - printf("addrIncoming = %s\n", addrIncoming.ToString().c_str()); printf("fMinimizeToTray = %d\n", fMinimizeToTray); printf("fMinimizeOnClose = %d\n", fMinimizeOnClose); printf("fUseProxy = %d\n", fUseProxy); diff --git a/src/db.h b/src/db.h index b910111..a86c110 100644 --- a/src/db.h +++ b/src/db.h @@ -398,6 +398,25 @@ public: return Write(std::make_pair(std::string("key"), vchPubKey), vchPrivKey, false); } + bool WriteCryptedKey(const std::vector& 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 WriteBestBlock(const CBlockLocator& locator) { nWalletDBUpdated++; diff --git a/src/init.cpp b/src/init.cpp index beaad68..fd1d8d3 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -424,7 +424,6 @@ bool AppInit2(int argc, char* argv[]) //// debug print printf("mapBlockIndex.size() = %d\n", mapBlockIndex.size()); printf("nBestHeight = %d\n", nBestHeight); - printf("mapKeys.size() = %d\n", pwalletMain->mapKeys.size()); printf("setKeyPool.size() = %d\n", pwalletMain->setKeyPool.size()); printf("mapPubKeys.size() = %d\n", mapPubKeys.size()); printf("mapWallet.size() = %d\n", pwalletMain->mapWallet.size()); diff --git a/src/key.h b/src/key.h index c973d6e..c43e4ee 100644 --- a/src/key.h +++ b/src/key.h @@ -31,6 +31,41 @@ // see www.keylength.com // script supports up to 75 for single byte push +int static inline 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); +} class key_error : public std::runtime_error @@ -42,8 +77,7 @@ public: // secure_allocator is defined in serialize.h typedef std::vector > CPrivKey; - - +typedef std::vector > CSecret; class CKey { @@ -102,6 +136,38 @@ public: return true; } + bool SetSecret(const CSecret& vchSecret) + { + EC_KEY_free(pkey); + pkey = EC_KEY_new_by_curve_name(NID_secp256k1); + if (pkey == NULL) + throw key_error("CKey::SetSecret() : EC_KEY_new_by_curve_name failed"); + if (vchSecret.size() != 32) + throw key_error("CKey::SetSecret() : secret must be 32 bytes"); + BIGNUM *bn = BN_bin2bn(&vchSecret[0],32,BN_new()); + if (bn == NULL) + throw key_error("CKey::SetSecret() : BN_bin2bn failed"); + if (!EC_KEY_regenerate_key(pkey,bn)) + throw key_error("CKey::SetSecret() : EC_KEY_regenerate_key failed"); + BN_clear_free(bn); + fSet = true; + return true; + } + + CSecret GetSecret() const + { + CSecret vchRet; + vchRet.resize(32); + const BIGNUM *bn = EC_KEY_get0_private_key(pkey); + int nBytes = BN_num_bytes(bn); + if (bn == NULL) + throw key_error("CKey::GetSecret() : EC_KEY_get0_private_key failed"); + int n=BN_bn2bin(bn,&vchRet[32 - nBytes]); + if (n != nBytes) + throw key_error("CKey::GetSecret(): BN_bn2bin failed"); + return vchRet; + } + CPrivKey GetPrivKey() const { unsigned int nSize = i2d_ECPrivateKey(pkey, NULL); @@ -154,22 +220,6 @@ public: return false; return true; } - - static bool Sign(const CPrivKey& vchPrivKey, uint256 hash, std::vector& vchSig) - { - CKey key; - if (!key.SetPrivKey(vchPrivKey)) - return false; - return key.Sign(hash, vchSig); - } - - static bool Verify(const std::vector& vchPubKey, uint256 hash, const std::vector& vchSig) - { - CKey key; - if (!key.SetPubKey(vchPubKey)) - return false; - return key.Verify(hash, vchSig); - } }; #endif diff --git a/src/keystore.cpp b/src/keystore.cpp index 7dd045f..de13958 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -4,13 +4,7 @@ #include "headers.h" #include "db.h" - - - -////////////////////////////////////////////////////////////////////////////// -// -// mapKeys -// +#include "crypter.h" std::vector CKeyStore::GenerateNewKey() { @@ -18,16 +12,136 @@ std::vector CKeyStore::GenerateNewKey() CKey key; key.MakeNewKey(); if (!AddKey(key)) - throw std::runtime_error("GenerateNewKey() : AddKey failed"); + throw std::runtime_error("CKeyStore::GenerateNewKey() : AddKey failed"); return key.GetPubKey(); } -bool CKeyStore::AddKey(const CKey& key) +bool CBasicKeyStore::AddKey(const CKey& key) { - CRITICAL_BLOCK(cs_mapKeys) + CRITICAL_BLOCK(cs_mapPubKeys) + CRITICAL_BLOCK(cs_KeyStore) { mapKeys[key.GetPubKey()] = key.GetPrivKey(); mapPubKeys[Hash160(key.GetPubKey())] = key.GetPubKey(); } + return true; +} + +std::vector CCryptoKeyStore::GenerateNewKey() +{ + RandAddSeedPerfmon(); + CKey key; + key.MakeNewKey(); + if (!AddKey(key)) + throw std::runtime_error("CCryptoKeyStore::GenerateNewKey() : AddKey failed"); + return key.GetPubKey(); +} + +bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) +{ + CRITICAL_BLOCK(cs_vMasterKey) + { + if (!SetCrypted()) + return false; + + std::map, std::vector >::const_iterator mi = mapCryptedKeys.begin(); + for (; mi != mapCryptedKeys.end(); ++mi) + { + const std::vector &vchPubKey = (*mi).first; + const std::vector &vchCryptedSecret = (*mi).second; + CSecret vchSecret; + if(!DecryptSecret(vMasterKeyIn, vchCryptedSecret, Hash(vchPubKey.begin(), vchPubKey.end()), vchSecret)) + return false; + CKey key; + key.SetSecret(vchSecret); + if (key.GetPubKey() == vchPubKey) + break; + return false; + } + vMasterKey = vMasterKeyIn; + } + return true; } +bool CCryptoKeyStore::AddKey(const CKey& key) +{ + CRITICAL_BLOCK(cs_KeyStore) + CRITICAL_BLOCK(cs_vMasterKey) + { + if (!IsCrypted()) + return CBasicKeyStore::AddKey(key); + + if (IsLocked()) + return false; + + std::vector vchCryptedSecret; + std::vector vchPubKey = key.GetPubKey(); + if (!EncryptSecret(vMasterKey, key.GetSecret(), Hash(vchPubKey.begin(), vchPubKey.end()), vchCryptedSecret)) + return false; + + if (!AddCryptedKey(key.GetPubKey(), vchCryptedSecret)) + return false; + } + return true; +} + + +bool CCryptoKeyStore::AddCryptedKey(const std::vector &vchPubKey, const std::vector &vchCryptedSecret) +{ + CRITICAL_BLOCK(cs_mapPubKeys) + CRITICAL_BLOCK(cs_KeyStore) + { + if (!SetCrypted()) + return false; + + mapCryptedKeys[vchPubKey] = vchCryptedSecret; + mapPubKeys[Hash160(vchPubKey)] = vchPubKey; + } + return true; +} + +bool CCryptoKeyStore::GetPrivKey(const std::vector &vchPubKey, CKey& keyOut) const +{ + CRITICAL_BLOCK(cs_vMasterKey) + { + if (!IsCrypted()) + return CBasicKeyStore::GetPrivKey(vchPubKey, keyOut); + + std::map, std::vector >::const_iterator mi = mapCryptedKeys.find(vchPubKey); + if (mi != mapCryptedKeys.end()) + { + const std::vector &vchCryptedSecret = (*mi).second; + CSecret vchSecret; + if (!DecryptSecret(vMasterKey, (*mi).second, Hash((*mi).first.begin(), (*mi).first.end()), vchSecret)) + return false; + keyOut.SetSecret(vchSecret); + return true; + } + } + return false; +} + +bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn) +{ + CRITICAL_BLOCK(cs_KeyStore) + CRITICAL_BLOCK(cs_vMasterKey) + { + if (!mapCryptedKeys.empty() || IsCrypted()) + return false; + + fUseCrypto = true; + CKey key; + BOOST_FOREACH(KeyMap::value_type& mKey, mapKeys) + { + if (!key.SetPrivKey(mKey.second)) + return false; + std::vector vchCryptedSecret; + if (!EncryptSecret(vMasterKeyIn, key.GetSecret(), Hash(mKey.first.begin(), mKey.first.end()), vchCryptedSecret)) + return false; + if (!AddCryptedKey(mKey.first, vchCryptedSecret)) + return false; + } + mapKeys.clear(); + } + return true; +} diff --git a/src/keystore.h b/src/keystore.h index 6080d7d..0dc09f0 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -4,27 +4,112 @@ #ifndef BITCOIN_KEYSTORE_H #define BITCOIN_KEYSTORE_H +#include "crypter.h" + class CKeyStore { public: - std::map, CPrivKey> mapKeys; - mutable CCriticalSection cs_mapKeys; - virtual bool AddKey(const CKey& key); + mutable CCriticalSection cs_KeyStore; + + virtual bool AddKey(const CKey& key) =0; + virtual bool HaveKey(const std::vector &vchPubKey) const =0; + virtual bool GetPrivKey(const std::vector &vchPubKey, CKey& keyOut) const =0; + virtual std::vector GenerateNewKey(); +}; + +typedef std::map, CPrivKey> KeyMap; + +class CBasicKeyStore : public CKeyStore +{ +protected: + KeyMap mapKeys; + +public: + bool AddKey(const CKey& key); bool HaveKey(const std::vector &vchPubKey) const { return (mapKeys.count(vchPubKey) > 0); } - bool GetPrivKey(const std::vector &vchPubKey, CPrivKey& keyOut) const + bool GetPrivKey(const std::vector &vchPubKey, CKey& keyOut) const { std::map, CPrivKey>::const_iterator mi = mapKeys.find(vchPubKey); if (mi != mapKeys.end()) { - keyOut = (*mi).second; + keyOut.SetPrivKey((*mi).second); return true; } return false; } +}; + +class CCryptoKeyStore : public CBasicKeyStore +{ +private: + std::map, std::vector > mapCryptedKeys; + + CKeyingMaterial vMasterKey; + + // if fUseCrypto is true, mapKeys must be empty + // if fUseCrypto is false, vMasterKey must be empty + bool fUseCrypto; + +protected: + bool SetCrypted() + { + if (fUseCrypto) + return true; + if (!mapKeys.empty()) + return false; + fUseCrypto = true; + return true; + } + + // will encrypt previously unencrypted keys + bool EncryptKeys(CKeyingMaterial& vMasterKeyIn); + + bool Unlock(const CKeyingMaterial& vMasterKeyIn); + +public: + mutable CCriticalSection cs_vMasterKey; //No guarantees master key wont get locked before you can use it, so lock this first + + CCryptoKeyStore() : fUseCrypto(false) + { + } + + bool IsCrypted() const + { + return fUseCrypto; + } + + bool IsLocked() const + { + if (!IsCrypted()) + return false; + return vMasterKey.empty(); + } + + bool Lock() + { + CRITICAL_BLOCK(cs_vMasterKey) + { + if (!SetCrypted()) + return false; + + vMasterKey.clear(); + } + return true; + } + + virtual bool AddCryptedKey(const std::vector &vchPubKey, const std::vector &vchCryptedSecret); std::vector GenerateNewKey(); + bool AddKey(const CKey& key); + bool HaveKey(const std::vector &vchPubKey) const + { + if (!IsCrypted()) + return CBasicKeyStore::HaveKey(vchPubKey); + return mapCryptedKeys.count(vchPubKey) > 0; + } + bool GetPrivKey(const std::vector &vchPubKey, CKey& keyOut) const; }; #endif diff --git a/src/main.cpp b/src/main.cpp index 594f1d3..6e97349 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -55,7 +55,6 @@ int64 nHPSTimerStart; // Settings int fGenerateBitcoins = false; int64 nTransactionFee = 0; -CAddress addrIncoming; int fLimitProcessors = false; int nLimitProcessors = 1; int fMinimizeToTray = true; @@ -2213,7 +2212,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // Keep giving the same key to the same ip until they use it if (!mapReuseKey.count(pfrom->addr.ip)) - mapReuseKey[pfrom->addr.ip] = pwalletMain->GetKeyFromKeyPool(); + mapReuseKey[pfrom->addr.ip] = pwalletMain->GetOrReuseKeyFromPool(); // Send back approval of order and pubkey to use CScript scriptPubKey; diff --git a/src/main.h b/src/main.h index aa74ac5..d34f68f 100644 --- a/src/main.h +++ b/src/main.h @@ -37,6 +37,8 @@ static const int64 MIN_RELAY_TX_FEE = 10000; static const int64 MAX_MONEY = 21000000 * COIN; inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } static const int COINBASE_MATURITY = 100; +// Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp. +static const int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC #ifdef USE_UPNP static const int fHaveUPnP = true; #else @@ -68,7 +70,6 @@ extern std::set setpwalletRegistered; // Settings extern int fGenerateBitcoins; extern int64 nTransactionFee; -extern CAddress addrIncoming; extern int fLimitProcessors; extern int nLimitProcessors; extern int fMinimizeToTray; @@ -441,7 +442,7 @@ public: nBlockHeight = nBestHeight; if (nBlockTime == 0) nBlockTime = GetAdjustedTime(); - if ((int64)nLockTime < (nLockTime < 500000000 ? (int64)nBlockHeight : nBlockTime)) + if ((int64)nLockTime < (nLockTime < LOCKTIME_THRESHOLD ? (int64)nBlockHeight : nBlockTime)) return true; BOOST_FOREACH(const CTxIn& txin, vin) if (!txin.IsFinal()) diff --git a/src/makefile.mingw b/src/makefile.mingw index 994f47c..16a0540 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw @@ -2,6 +2,8 @@ # Distributed under the MIT/X11 software license, see the accompanying # file license.txt or http://www.opensource.org/licenses/mit-license.php. +USE_UPNP:=0 + INCLUDEPATHS= \ -I"C:\boost-1.43.0-mgw" \ -I"C:\db-4.7.25.NC-mgw\build_unix" \ @@ -31,24 +33,15 @@ DEFS=-DWIN32 -D__WXMSW__ -D_WINDOWS -DNOPCH -DUSE_SSL DEBUGFLAGS=-g -D__WXDEBUG__ CFLAGS=-mthreads -O2 -w -Wno-invalid-offsetof -Wformat $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) HEADERS=headers.h strlcpy.h serialize.h uint256.h util.h key.h bignum.h base58.h \ - script.h db.h net.h irc.h keystore.h main.h wallet.h rpc.h uibase.h ui.h noui.h init.h - - -bitcoin.exe: USE_UPNP:=1 - ifdef USE_UPNP - INCLUDEPATHS += -I"C:\upnpc-exe-win32-20110215" - LIBPATHS += -L"C:\upnpc-exe-win32-20110215" - LIBS += -l miniupnpc -l iphlpapi - DEFS += -DSTATICLIB -DUSE_UPNP=$(USE_UPNP) - endif + script.h db.h net.h irc.h keystore.h main.h wallet.h rpc.h uibase.h ui.h noui.h \ + init.h crypter.h -bitcoind.exe: USE_UPNP:=0 - ifdef USE_UPNP - INCLUDEPATHS += -I"C:\upnpc-exe-win32-20110215" - LIBPATHS += -L"C:\upnpc-exe-win32-20110215" - LIBS += -l miniupnpc -l iphlpapi - DEFS += -DSTATICLIB -DUSE_UPNP=$(USE_UPNP) - endif +ifdef USE_UPNP + INCLUDEPATHS += -I"C:\upnpc-exe-win32-20110215" + LIBPATHS += -L"C:\upnpc-exe-win32-20110215" + LIBS += -l miniupnpc -l iphlpapi + DEFS += -DSTATICLIB -DUSE_UPNP=$(USE_UPNP) +endif LIBS += -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 shlwapi @@ -63,6 +56,7 @@ OBJS= \ obj/wallet.o \ obj/rpc.o \ obj/init.o \ + obj/crypter.o \ cryptopp/obj/sha.o \ cryptopp/obj/cpu.o diff --git a/src/makefile.osx b/src/makefile.osx index 36890f9..8978856 100644 --- a/src/makefile.osx +++ b/src/makefile.osx @@ -16,6 +16,8 @@ LIBPATHS= \ WXLIBS=$(shell $(DEPSDIR)/bin/wx-config --libs --static) +USE_UPNP:=0 + LIBS= -dead_strip \ $(DEPSDIR)/lib/libdb_cxx-4.8.a \ $(DEPSDIR)/lib/libboost_system.a \ @@ -31,7 +33,8 @@ DEBUGFLAGS=-g -DwxDEBUG_LEVEL=0 # ppc doesn't work because we don't support big-endian CFLAGS=-mmacosx-version-min=10.5 -arch i386 -arch x86_64 -O3 -Wno-invalid-offsetof -Wformat $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) HEADERS=headers.h strlcpy.h serialize.h uint256.h util.h key.h bignum.h base58.h \ - script.h db.h net.h irc.h keystore.h main.h wallet.h rpc.h uibase.h ui.h noui.h init.h + script.h db.h net.h irc.h keystore.h main.h wallet.h rpc.h uibase.h ui.h noui.h \ + init.h crypter.h OBJS= \ obj/util.o \ @@ -44,20 +47,14 @@ OBJS= \ obj/wallet.o \ obj/rpc.o \ obj/init.o \ + obj/crypter.o \ cryptopp/obj/sha.o \ cryptopp/obj/cpu.o -bitcoin: USE_UPNP:=1 - ifdef USE_UPNP - LIBS += $(DEPSDIR)/lib/libminiupnpc.a - DEFS += -DUSE_UPNP=$(USE_UPNP) - endif - -bitcoind: USE_UPNP:=0 - ifdef USE_UPNP - LIBS += $(DEPSDIR)/lib/libminiupnpc.a - DEFS += -DUSE_UPNP=$(USE_UPNP) - endif +ifdef USE_UPNP + LIBS += $(DEPSDIR)/lib/libminiupnpc.a + DEFS += -DUSE_UPNP=$(USE_UPNP) +endif all: bitcoin diff --git a/src/makefile.unix b/src/makefile.unix index 82d2a9f..0567f8b 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -8,6 +8,8 @@ WXINCLUDEPATHS=$(shell wx-config --cxxflags) WXLIBS=$(shell wx-config --libs) +USE_UPNP:=0 + DEFS=-DNOPCH -DFOURWAYSSE2 -DUSE_SSL # for boost 1.37, add -mt to the boost libraries @@ -21,17 +23,10 @@ LIBS= \ -l ssl \ -l crypto -bitcoin: USE_UPNP:=1 - ifdef USE_UPNP - LIBS += -l miniupnpc - DEFS += -DUSE_UPNP=$(USE_UPNP) - endif - -bitcoind: USE_UPNP:=0 - ifdef USE_UPNP - LIBS += -l miniupnpc - DEFS += -DUSE_UPNP=$(USE_UPNP) - endif +ifdef USE_UPNP + LIBS += -l miniupnpc + DEFS += -DUSE_UPNP=$(USE_UPNP) +endif LIBS+= \ -Wl,-Bdynamic \ @@ -44,7 +39,8 @@ LIBS+= \ DEBUGFLAGS=-g -D__WXDEBUG__ CXXFLAGS=-O2 -Wno-invalid-offsetof -Wformat $(DEBUGFLAGS) $(DEFS) HEADERS=headers.h strlcpy.h serialize.h uint256.h util.h key.h bignum.h base58.h \ - script.h db.h net.h irc.h keystore.h main.h wallet.h rpc.h uibase.h ui.h noui.h init.h + script.h db.h net.h irc.h keystore.h main.h wallet.h rpc.h uibase.h ui.h noui.h \ + init.h crypter.h OBJS= \ obj/util.o \ @@ -57,6 +53,7 @@ OBJS= \ obj/wallet.o \ obj/rpc.o \ obj/init.o \ + obj/crypter.o \ cryptopp/obj/sha.o \ cryptopp/obj/cpu.o diff --git a/src/rpc.cpp b/src/rpc.cpp index 6f951b7..fbed626 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -36,6 +36,9 @@ void ThreadRPCServer2(void* parg); typedef Value(*rpcfn_type)(const Array& params, bool fHelp); extern map mapCallTable; +static int64 nWalletUnlockTime; +static CCriticalSection cs_nWalletUnlockTime; + Object JSONRPCError(int code, const string& message) { @@ -309,7 +312,10 @@ Value getinfo(const Array& params, bool fHelp) obj.push_back(Pair("hashespersec", gethashespersec(params, false))); obj.push_back(Pair("testnet", fTestNet)); obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime())); + obj.push_back(Pair("keypoolsize", pwalletMain->GetKeyPoolSize())); obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee))); + if (pwalletMain->IsCrypted()) + obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime)); obj.push_back(Pair("errors", GetWarnings("statusbar"))); return obj; } @@ -324,13 +330,19 @@ Value getnewaddress(const Array& params, bool fHelp) "If [account] is specified (recommended), it is added to the address book " "so payments received with the address will be credited to [account]."); + if (!pwalletMain->IsLocked()) + pwalletMain->TopUpKeyPool(); + + if (pwalletMain->GetKeyPoolSize() < 1) + throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first"); + // 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]); // Generate a new key that is added to wallet - string strAddress = PubKeyToAddress(pwalletMain->GetKeyFromKeyPool()); + string strAddress = PubKeyToAddress(pwalletMain->GetOrReuseKeyFromPool()); // This could be done in the same main CS as GetKeyFromKeyPool. CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) @@ -346,37 +358,48 @@ string GetAccountAddress(string strAccount, bool bForceNew=false) string strAddress; CWalletDB walletdb(pwalletMain->strWalletFile); - walletdb.TxnBegin(); CAccount account; - walletdb.ReadAccount(strAccount, account); - - // Check if the current key has been used - if (!account.vchPubKey.empty()) + CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) { - CScript scriptPubKey; - scriptPubKey.SetBitcoinAddress(account.vchPubKey); - for (map::iterator it = pwalletMain->mapWallet.begin(); - it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty(); - ++it) + walletdb.ReadAccount(strAccount, account); + + bool bKeyUsed = false; + + // Check if the current key has been used + if (!account.vchPubKey.empty()) { - const CWalletTx& wtx = (*it).second; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - if (txout.scriptPubKey == scriptPubKey) - account.vchPubKey.clear(); + CScript scriptPubKey; + scriptPubKey.SetBitcoinAddress(account.vchPubKey); + for (map::iterator it = pwalletMain->mapWallet.begin(); + it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty(); + ++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.empty() || bForceNew) - { - account.vchPubKey = pwalletMain->GetKeyFromKeyPool(); - string strAddress = PubKeyToAddress(account.vchPubKey); - pwalletMain->SetAddressBookName(strAddress, strAccount); - walletdb.WriteAccount(strAccount, account); + // Generate a new key + if (account.vchPubKey.empty() || bForceNew || bKeyUsed) + { + if (pwalletMain->GetKeyPoolSize() < 1) + { + if (bKeyUsed || bForceNew) + throw JSONRPCError(-12, "Error: Keypool ran out, please call topupkeypool first"); + } + else + { + account.vchPubKey = pwalletMain->GetOrReuseKeyFromPool(); + string strAddress = PubKeyToAddress(account.vchPubKey); + pwalletMain->SetAddressBookName(strAddress, strAccount); + walletdb.WriteAccount(strAccount, account); + } + } } - walletdb.TxnCommit(); strAddress = PubKeyToAddress(account.vchPubKey); return strAddress; @@ -510,7 +533,12 @@ Value settxfee(const Array& params, bool fHelp) Value sendtoaddress(const Array& params, bool fHelp) { - if (fHelp || params.size() < 2 || params.size() > 4) + if (pwalletMain->IsCrypted() && (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\n" + "requires wallet passphrase to be set with walletpassphrase first"); + if (!pwalletMain->IsCrypted() && (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"); @@ -528,7 +556,11 @@ Value sendtoaddress(const Array& params, bool fHelp) wtx.mapValue["to"] = params[3].get_str(); CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(pwalletMain->cs_vMasterKey) { + if(pwalletMain->IsLocked()) + throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect."); + string strError = pwalletMain->SendMoneyToBitcoinAddress(strAddress, nAmount, wtx); if (strError != "") throw JSONRPCError(-4, strError); @@ -773,7 +805,12 @@ Value movecmd(const Array& params, bool fHelp) Value sendfrom(const Array& params, bool fHelp) { - if (fHelp || params.size() < 3 || params.size() > 6) + if (pwalletMain->IsCrypted() && (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\n" + "requires wallet passphrase to be set with walletpassphrase first"); + if (!pwalletMain->IsCrypted() && (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"); @@ -794,7 +831,11 @@ Value sendfrom(const Array& params, bool fHelp) CRITICAL_BLOCK(cs_main) CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + CRITICAL_BLOCK(pwalletMain->cs_vMasterKey) { + if(pwalletMain->IsLocked()) + throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect."); + // Check funds int64 nBalance = GetAccountBalance(strAccount, nMinDepth); if (nAmount > nBalance) @@ -809,9 +850,15 @@ Value sendfrom(const Array& params, bool fHelp) return wtx.GetHash().GetHex(); } + Value sendmany(const Array& params, bool fHelp) { - if (fHelp || params.size() < 2 || params.size() > 4) + if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4)) + throw runtime_error( + "sendmany {address:amount,...} [minconf=1] [comment]\n" + "amounts are double-precision floating point numbers\n" + "requires wallet passphrase to be set with walletpassphrase first"); + if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4)) throw runtime_error( "sendmany {address:amount,...} [minconf=1] [comment]\n" "amounts are double-precision floating point numbers"); @@ -851,7 +898,11 @@ Value sendmany(const Array& params, bool fHelp) CRITICAL_BLOCK(cs_main) CRITICAL_BLOCK(pwalletMain->cs_mapWallet) + CRITICAL_BLOCK(pwalletMain->cs_vMasterKey) { + if(pwalletMain->IsLocked()) + throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect."); + // Check funds int64 nBalance = GetAccountBalance(strAccount, nMinDepth); if (totalAmount > nBalance) @@ -1281,6 +1332,219 @@ Value backupwallet(const Array& params, bool fHelp) } +Value keypoolrefill(const Array& params, bool fHelp) +{ + if (pwalletMain->IsCrypted() && (fHelp || params.size() > 0)) + throw runtime_error( + "keypoolrefill\n" + "Fills the keypool, requires wallet passphrase to be set."); + if (!pwalletMain->IsCrypted() && (fHelp || params.size() > 0)) + throw runtime_error( + "keypoolrefill\n" + "Fills the keypool."); + + CRITICAL_BLOCK(pwalletMain->cs_vMasterKey) + { + if (pwalletMain->IsLocked()) + throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first."); + + pwalletMain->TopUpKeyPool(); + } + + if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100)) + throw JSONRPCError(-4, "Error refreshing keypool."); + + return Value::null; +} + + +void ThreadTopUpKeyPool(void* parg) +{ + pwalletMain->TopUpKeyPool(); +} + +void ThreadCleanWalletPassphrase(void* parg) +{ + int64 nMyWakeTime = GetTime() + *((int*)parg); + + if (nWalletUnlockTime == 0) + { + CRITICAL_BLOCK(cs_nWalletUnlockTime) + { + nWalletUnlockTime = nMyWakeTime; + } + + while (GetTime() < nWalletUnlockTime) + Sleep(GetTime() - nWalletUnlockTime); + + CRITICAL_BLOCK(cs_nWalletUnlockTime) + { + nWalletUnlockTime = 0; + } + } + else + { + CRITICAL_BLOCK(cs_nWalletUnlockTime) + { + if (nWalletUnlockTime < nMyWakeTime) + nWalletUnlockTime = nMyWakeTime; + } + free(parg); + return; + } + + pwalletMain->Lock(); + + delete (int*)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(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called."); + + if (!pwalletMain->IsLocked()) + throw JSONRPCError(-17, "Error: Wallet is already unlocked."); + + // Note that the walletpassphrase is stored in params[0] which is not mlock()ed + string strWalletPass; + strWalletPass.reserve(100); + mlock(&strWalletPass[0], strWalletPass.capacity()); + strWalletPass = params[0].get_str(); + + CRITICAL_BLOCK(pwalletMain->cs_vMasterKey) + { + if (strWalletPass.length() > 0) + { + if (!pwalletMain->Unlock(strWalletPass)) + { + fill(strWalletPass.begin(), strWalletPass.end(), '\0'); + munlock(&strWalletPass[0], strWalletPass.capacity()); + throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect."); + } + fill(strWalletPass.begin(), strWalletPass.end(), '\0'); + munlock(&strWalletPass[0], strWalletPass.capacity()); + } + else + throw runtime_error( + "walletpassphrase \n" + "Stores the wallet decryption key in memory for seconds."); + } + + CreateThread(ThreadTopUpKeyPool, NULL); + int* pnSleepTime = new int(params[1].get_int()); + CreateThread(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(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called."); + + string strOldWalletPass; + strOldWalletPass.reserve(100); + mlock(&strOldWalletPass[0], strOldWalletPass.capacity()); + strOldWalletPass = params[0].get_str(); + + string strNewWalletPass; + strNewWalletPass.reserve(100); + mlock(&strNewWalletPass[0], strNewWalletPass.capacity()); + strNewWalletPass = params[1].get_str(); + + if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1) + throw runtime_error( + "walletpassphrasechange \n" + "Changes the wallet passphrase from to ."); + + if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) + { + fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0'); + fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0'); + munlock(&strOldWalletPass[0], strOldWalletPass.capacity()); + munlock(&strNewWalletPass[0], strNewWalletPass.capacity()); + throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect."); + } + fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0'); + fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0'); + munlock(&strOldWalletPass[0], strOldWalletPass.capacity()); + munlock(&strNewWalletPass[0], strNewWalletPass.capacity()); + + 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(-15, "Error: running with an unencrypted wallet, but walletlock was called."); + + pwalletMain->Lock(); + CRITICAL_BLOCK(cs_nWalletUnlockTime) + { + 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(-15, "Error: running with an encrypted wallet, but encryptwallet was called."); + + string strWalletPass; + strWalletPass.reserve(100); + mlock(&strWalletPass[0], strWalletPass.capacity()); + strWalletPass = params[0].get_str(); + + if (strWalletPass.length() < 1) + throw runtime_error( + "encryptwallet \n" + "Encrypts the wallet with ."); + + if (!pwalletMain->EncryptWallet(strWalletPass)) + { + fill(strWalletPass.begin(), strWalletPass.end(), '\0'); + munlock(&strWalletPass[0], strWalletPass.capacity()); + throw JSONRPCError(-16, "Error: Failed to encrypt the wallet."); + } + fill(strWalletPass.begin(), strWalletPass.end(), '\0'); + munlock(&strWalletPass[0], strWalletPass.capacity()); + + return Value::null; +} + + Value validateaddress(const Array& params, bool fHelp) { if (fHelp || params.size() != 1) @@ -1432,44 +1696,49 @@ Value getwork(const Array& params, bool fHelp) pair pCallTable[] = { - make_pair("help", &help), - make_pair("stop", &stop), - make_pair("getblockcount", &getblockcount), - make_pair("getblocknumber", &getblocknumber), - make_pair("getconnectioncount", &getconnectioncount), - make_pair("getdifficulty", &getdifficulty), - make_pair("getgenerate", &getgenerate), - make_pair("setgenerate", &setgenerate), - make_pair("gethashespersec", &gethashespersec), - make_pair("getinfo", &getinfo), - make_pair("getnewaddress", &getnewaddress), - make_pair("getaccountaddress", &getaccountaddress), - make_pair("setaccount", &setaccount), - make_pair("setlabel", &setaccount), // deprecated - make_pair("getaccount", &getaccount), - make_pair("getlabel", &getaccount), // deprecated - make_pair("getaddressesbyaccount", &getaddressesbyaccount), - make_pair("getaddressesbylabel", &getaddressesbyaccount), // deprecated - make_pair("sendtoaddress", &sendtoaddress), - make_pair("getamountreceived", &getreceivedbyaddress), // deprecated, renamed to getreceivedbyaddress - make_pair("getallreceived", &listreceivedbyaddress), // deprecated, renamed to listreceivedbyaddress - make_pair("getreceivedbyaddress", &getreceivedbyaddress), - make_pair("getreceivedbyaccount", &getreceivedbyaccount), - make_pair("getreceivedbylabel", &getreceivedbyaccount), // deprecated - make_pair("listreceivedbyaddress", &listreceivedbyaddress), - make_pair("listreceivedbyaccount", &listreceivedbyaccount), - make_pair("listreceivedbylabel", &listreceivedbyaccount), // deprecated - make_pair("backupwallet", &backupwallet), - make_pair("validateaddress", &validateaddress), - make_pair("getbalance", &getbalance), - make_pair("move", &movecmd), - make_pair("sendfrom", &sendfrom), - make_pair("sendmany", &sendmany), - make_pair("gettransaction", &gettransaction), - make_pair("listtransactions", &listtransactions), - make_pair("getwork", &getwork), - make_pair("listaccounts", &listaccounts), - make_pair("settxfee", &settxfee), + make_pair("help", &help), + make_pair("stop", &stop), + make_pair("getblockcount", &getblockcount), + make_pair("getblocknumber", &getblocknumber), + make_pair("getconnectioncount", &getconnectioncount), + make_pair("getdifficulty", &getdifficulty), + make_pair("getgenerate", &getgenerate), + make_pair("setgenerate", &setgenerate), + make_pair("gethashespersec", &gethashespersec), + make_pair("getinfo", &getinfo), + make_pair("getnewaddress", &getnewaddress), + make_pair("getaccountaddress", &getaccountaddress), + make_pair("setaccount", &setaccount), + make_pair("setlabel", &setaccount), // deprecated + make_pair("getaccount", &getaccount), + make_pair("getlabel", &getaccount), // deprecated + make_pair("getaddressesbyaccount", &getaddressesbyaccount), + make_pair("getaddressesbylabel", &getaddressesbyaccount), // deprecated + make_pair("sendtoaddress", &sendtoaddress), + make_pair("getamountreceived", &getreceivedbyaddress), // deprecated, renamed to getreceivedbyaddress + make_pair("getallreceived", &listreceivedbyaddress), // deprecated, renamed to listreceivedbyaddress + make_pair("getreceivedbyaddress", &getreceivedbyaddress), + make_pair("getreceivedbyaccount", &getreceivedbyaccount), + make_pair("getreceivedbylabel", &getreceivedbyaccount), // deprecated + make_pair("listreceivedbyaddress", &listreceivedbyaddress), + make_pair("listreceivedbyaccount", &listreceivedbyaccount), + make_pair("listreceivedbylabel", &listreceivedbyaccount), // deprecated + make_pair("backupwallet", &backupwallet), + make_pair("keypoolrefill", &keypoolrefill), + make_pair("walletpassphrase", &walletpassphrase), + make_pair("walletpassphrasechange", &walletpassphrasechange), + make_pair("walletlock", &walletlock), + make_pair("encryptwallet", &encryptwallet), + make_pair("validateaddress", &validateaddress), + make_pair("getbalance", &getbalance), + make_pair("move", &movecmd), + make_pair("sendfrom", &sendfrom), + make_pair("sendmany", &sendmany), + make_pair("gettransaction", &gettransaction), + make_pair("listtransactions", &listtransactions), + make_pair("getwork", &getwork), + make_pair("listaccounts", &listaccounts), + make_pair("settxfee", &settxfee), }; map mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0])); @@ -1493,6 +1762,9 @@ string pAllowInSafeMode[] = "getaddressesbyaccount", "getaddressesbylabel", // deprecated "backupwallet", + "keypoolrefill", + "walletpassphrase", + "walletlock", "validateaddress", "getwork", }; @@ -2130,6 +2402,7 @@ int CommandLineRPC(int argc, char *argv[]) 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 == "sendmany" && n > 1) { string s = params[1].get_str(); diff --git a/src/script.cpp b/src/script.cpp index bd1b5b3..aa7f1f5 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1030,7 +1030,7 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash return false; // Compile solution - CRITICAL_BLOCK(keystore.cs_mapKeys) + CRITICAL_BLOCK(keystore.cs_KeyStore) { BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution) { @@ -1038,13 +1038,13 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash { // Sign const valtype& vchPubKey = item.second; - CPrivKey privkey; - if (!keystore.GetPrivKey(vchPubKey, privkey)) + CKey key; + if (!keystore.GetPrivKey(vchPubKey, key)) return false; if (hash != 0) { vector vchSig; - if (!CKey::Sign(privkey, hash, vchSig)) + if (!key.Sign(hash, vchSig)) return false; vchSig.push_back((unsigned char)nHashType); scriptSigRet << vchSig; @@ -1057,13 +1057,13 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash if (mi == mapPubKeys.end()) return false; const vector& vchPubKey = (*mi).second; - CPrivKey privkey; - if (!keystore.GetPrivKey(vchPubKey, privkey)) + CKey key; + if (!keystore.GetPrivKey(vchPubKey, key)) return false; if (hash != 0) { vector vchSig; - if (!CKey::Sign(privkey, hash, vchSig)) + if (!key.Sign(hash, vchSig)) return false; vchSig.push_back((unsigned char)nHashType); scriptSigRet << vchSig << vchPubKey; @@ -1089,8 +1089,40 @@ bool IsStandard(const CScript& scriptPubKey) bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) { - CScript scriptSig; - return Solver(keystore, scriptPubKey, 0, 0, scriptSig); + vector > vSolution; + if (!Solver(scriptPubKey, vSolution)) + return false; + + // Compile solution + CRITICAL_BLOCK(keystore.cs_KeyStore) + { + BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution) + { + if (item.first == OP_PUBKEY) + { + // Sign + const valtype& vchPubKey = item.second; + if (!keystore.HaveKey(vchPubKey)) + return false; + } + else if (item.first == OP_PUBKEYHASH) + { + // Sign and give pubkey + map::iterator mi = mapPubKeys.find(uint160(item.second)); + if (mi == mapPubKeys.end()) + return false; + const vector& vchPubKey = (*mi).second; + if (!keystore.HaveKey(vchPubKey)) + return false; + } + else + { + return false; + } + } + } + + return true; } diff --git a/src/serialize.h b/src/serialize.h index 31862a7..38c533d 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -28,6 +28,30 @@ typedef unsigned long long uint64; #if defined(_MSC_VER) && _MSC_VER < 1300 #define for if (false) ; else for #endif + +#ifdef __WXMSW__ +// 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. +#define mlock(p, n) VirtualLock((p), (n)); +#define munlock(p, n) VirtualUnlock((p), (n)); +#else +#include +#include +/* This comes from limits.h if it's not defined there set a sane default */ +#ifndef PAGESIZE +#include +#define PAGESIZE sysconf(_SC_PAGESIZE) +#endif +#define mlock(a,b) \ + mlock(((void *)(((size_t)(a)) & (~((PAGESIZE)-1)))),\ + (((((size_t)(a)) + (b) - 1) | ((PAGESIZE) - 1)) + 1) - (((size_t)(a)) & (~((PAGESIZE) - 1)))) +#define munlock(a,b) \ + munlock(((void *)(((size_t)(a)) & (~((PAGESIZE)-1)))),\ + (((((size_t)(a)) + (b) - 1) | ((PAGESIZE) - 1)) + 1) - (((size_t)(a)) & (~((PAGESIZE) - 1)))) +#endif + class CScript; class CDataStream; class CAutoFile; @@ -755,7 +779,8 @@ struct ser_streamplaceholder // -// Allocator that clears its contents before deletion +// Allocator that locks its contents from being paged +// out of memory and clears its contents before deletion. // template struct secure_allocator : public std::allocator @@ -777,10 +802,22 @@ struct secure_allocator : public std::allocator 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) + mlock(p, sizeof(T) * n); + return p; + } + void deallocate(T* p, std::size_t n) { if (p != NULL) + { memset(p, 0, sizeof(T) * n); + munlock(p, sizeof(T) * n); + } std::allocator::deallocate(p, n); } }; diff --git a/src/ui.cpp b/src/ui.cpp index 2cbcfd5..eae0a4f 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -240,11 +240,46 @@ void SetDefaultReceivingAddress(const string& strAddress) return; if (!mapPubKeys.count(hash160)) return; - CWalletDB(pwalletMain->strWalletFile).WriteDefaultKey(mapPubKeys[hash160]); + pwalletMain->SetDefaultKey(mapPubKeys[hash160]); pframeMain->m_textCtrlAddress->SetValue(strAddress); } } +bool GetWalletPassphrase() +{ + if (pwalletMain->IsLocked()) + { + string strWalletPass; + strWalletPass.reserve(100); + mlock(&strWalletPass[0], strWalletPass.capacity()); + + // obtain current wallet encrypt/decrypt key, from passphrase + // Note that the passphrase is not mlock()d during this entry and could potentially + // be obtained from disk long after bitcoin has run. + strWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."), + _("Passphrase")).ToStdString(); + + if (!strWalletPass.size()) + { + fill(strWalletPass.begin(), strWalletPass.end(), '\0'); + munlock(&strWalletPass[0], strWalletPass.capacity()); + wxMessageBox(_("Please supply the current wallet decryption passphrase."), "Bitcoin"); + return false; + } + + if (!pwalletMain->Unlock(strWalletPass)) + { + fill(strWalletPass.begin(), strWalletPass.end(), '\0'); + munlock(&strWalletPass[0], strWalletPass.capacity()); + wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin"); + return false; + } + fill(strWalletPass.begin(), strWalletPass.end(), '\0'); + munlock(&strWalletPass[0], strWalletPass.capacity()); + } + return true; +} + @@ -333,6 +368,11 @@ CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent) if (CWalletDB(pwalletMain->strWalletFile,"r").ReadDefaultKey(vchPubKey)) m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey)); + if (pwalletMain->IsCrypted()) + m_menuOptions->Remove(m_menuOptionsEncryptWallet); + else + m_menuOptions->Remove(m_menuOptionsChangeWalletPassphrase); + // Fill listctrl with wallet transactions RefreshListCtrl(); } @@ -522,7 +562,7 @@ string FormatTxStatus(const CWalletTx& wtx) // Status if (!wtx.IsFinal()) { - if (wtx.nLockTime < 500000000) + if (wtx.nLockTime < LOCKTIME_THRESHOLD) return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime); else return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str()); @@ -1122,6 +1162,169 @@ void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event) return; } +void CMainFrame::OnMenuOptionsEncryptWallet(wxCommandEvent& event) +{ + // Options->Encrypt Wallet + if (pwalletMain->IsCrypted()) + { + wxMessageBox(_("Wallet already encrypted."), "Bitcoin", wxOK | wxICON_ERROR); + return; + } + + string strWalletPass; + strWalletPass.reserve(100); + mlock(&strWalletPass[0], strWalletPass.capacity()); + + // obtain current wallet encrypt/decrypt key, from passphrase + // Note that the passphrase is not mlock()d during this entry and could potentially + // be obtained from disk long after bitcoin has run. + strWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase to the wallet.\nPlease use a passphrase of 10 or more random characters, or eight or more words."), + _("Passphrase")).ToStdString(); + + if (!strWalletPass.size()) + { + fill(strWalletPass.begin(), strWalletPass.end(), '\0'); + munlock(&strWalletPass[0], strWalletPass.capacity()); + wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR); + return; + } + + if(wxMessageBox(_("WARNING: If you encrypt your wallet and lose your passphrase, you will LOSE ALL OF YOUR BITCOINS!\nAre you sure you wish to encrypt your wallet?"), "Bitcoin", wxYES_NO) != wxYES) + return; + + string strWalletPassTest; + strWalletPassTest.reserve(100); + mlock(&strWalletPassTest[0], strWalletPassTest.capacity()); + strWalletPassTest = wxGetPasswordFromUser(_("Please re-enter your new wallet passphrase."), + _("Passphrase")).ToStdString(); + + if (strWalletPassTest != strWalletPass) + { + fill(strWalletPass.begin(), strWalletPass.end(), '\0'); + fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0'); + munlock(&strWalletPass[0], strWalletPass.capacity()); + munlock(&strWalletPassTest[0], strWalletPassTest.capacity()); + wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR); + return; + } + + if (!pwalletMain->EncryptWallet(strWalletPass)) + { + fill(strWalletPass.begin(), strWalletPass.end(), '\0'); + fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0'); + munlock(&strWalletPass[0], strWalletPass.capacity()); + munlock(&strWalletPassTest[0], strWalletPassTest.capacity()); + wxMessageBox(_("Wallet encryption failed."), "Bitcoin", wxOK | wxICON_ERROR); + return; + } + fill(strWalletPass.begin(), strWalletPass.end(), '\0'); + fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0'); + munlock(&strWalletPass[0], strWalletPass.capacity()); + munlock(&strWalletPassTest[0], strWalletPassTest.capacity()); + wxMessageBox(_("Wallet Encrypted.\nRemember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer."), "Bitcoin"); + + m_menuOptions->Remove(m_menuOptionsEncryptWallet); + m_menuOptions->Insert(m_menuOptions->GetMenuItemCount() - 1, m_menuOptionsChangeWalletPassphrase); +} + +void CMainFrame::OnMenuOptionsChangeWalletPassphrase(wxCommandEvent& event) +{ + // Options->Change Wallet Encryption Passphrase + if (!pwalletMain->IsCrypted()) + { + wxMessageBox(_("Wallet is unencrypted, please encrypt it first."), "Bitcoin", wxOK | wxICON_ERROR); + return; + } + + string strOldWalletPass; + strOldWalletPass.reserve(100); + mlock(&strOldWalletPass[0], strOldWalletPass.capacity()); + + // obtain current wallet encrypt/decrypt key, from passphrase + // Note that the passphrase is not mlock()d during this entry and could potentially + // be obtained from disk long after bitcoin has run. + strOldWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."), + _("Passphrase")).ToStdString(); + + CRITICAL_BLOCK(pwalletMain->cs_vMasterKey) + { + bool fWasLocked = pwalletMain->IsLocked(); + pwalletMain->Lock(); + + if (!strOldWalletPass.size() || !pwalletMain->Unlock(strOldWalletPass)) + { + fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0'); + munlock(&strOldWalletPass[0], strOldWalletPass.capacity()); + wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR); + return; + } + + if (fWasLocked) + pwalletMain->Lock(); + + string strNewWalletPass; + strNewWalletPass.reserve(100); + mlock(&strNewWalletPass[0], strNewWalletPass.capacity()); + + // obtain new wallet encrypt/decrypt key, from passphrase + // Note that the passphrase is not mlock()d during this entry and could potentially + // be obtained from disk long after bitcoin has run. + strNewWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase for the wallet."), + _("Passphrase")).ToStdString(); + + if (!strNewWalletPass.size()) + { + fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0'); + fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0'); + munlock(&strOldWalletPass[0], strOldWalletPass.capacity()); + munlock(&strNewWalletPass[0], strNewWalletPass.capacity()); + wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR); + return; + } + + string strNewWalletPassTest; + strNewWalletPassTest.reserve(100); + mlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity()); + + // obtain new wallet encrypt/decrypt key, from passphrase + // Note that the passphrase is not mlock()d during this entry and could potentially + // be obtained from disk long after bitcoin has run. + strNewWalletPassTest = wxGetPasswordFromUser(_("Re-enter the new passphrase for the wallet."), + _("Passphrase")).ToStdString(); + + if (strNewWalletPassTest != strNewWalletPass) + { + fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0'); + fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0'); + fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0'); + munlock(&strOldWalletPass[0], strOldWalletPass.capacity()); + munlock(&strNewWalletPass[0], strNewWalletPass.capacity()); + munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity()); + wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR); + return; + } + + if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) + { + fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0'); + fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0'); + fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0'); + munlock(&strOldWalletPass[0], strOldWalletPass.capacity()); + munlock(&strNewWalletPass[0], strNewWalletPass.capacity()); + munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity()); + wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR); + return; + } + fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0'); + fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0'); + fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0'); + munlock(&strOldWalletPass[0], strOldWalletPass.capacity()); + munlock(&strNewWalletPass[0], strNewWalletPass.capacity()); + munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity()); + wxMessageBox(_("Wallet Passphrase Changed."), "Bitcoin"); + } +} + void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event) { // Options->Options @@ -1182,8 +1385,19 @@ void CMainFrame::OnButtonNew(wxCommandEvent& event) return; string strName = dialog.GetValue(); - // Generate new key - string strAddress = PubKeyToAddress(pwalletMain->GetKeyFromKeyPool()); + string strAddress; + CRITICAL_BLOCK(pwalletMain->cs_vMasterKey) + { + bool fWasLocked = pwalletMain->IsLocked(); + if (!GetWalletPassphrase()) + return; + + // Generate new key + strAddress = PubKeyToAddress(pwalletMain->GetOrReuseKeyFromPool()); + + if (fWasLocked) + pwalletMain->Lock(); + } // Save CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) @@ -1947,7 +2161,12 @@ void CSendDialog::OnButtonSend(wxCommandEvent& event) if (fBitcoinAddress) { CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(pwalletMain->cs_vMasterKey) { + bool fWasLocked = pwalletMain->IsLocked(); + if (!GetWalletPassphrase()) + return; + // Send to bitcoin address CScript scriptPubKey; scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG; @@ -1956,13 +2175,22 @@ void CSendDialog::OnButtonSend(wxCommandEvent& event) if (strError == "") wxMessageBox(_("Payment sent "), _("Sending...")); else if (strError == "ABORTED") + { + if (fWasLocked) + pwalletMain->Lock(); return; // leave send dialog open + } else { wxMessageBox(strError + " ", _("Sending...")); EndModal(false); + if (fWasLocked) + pwalletMain->Lock(); return; } + + if (fWasLocked) + pwalletMain->Lock(); } } else @@ -2246,16 +2474,27 @@ void CSendingDialog::OnReply2(CDataStream& vRecv) Error(_("Insufficient funds")); return; } + CReserveKey reservekey(pwalletMain); int64 nFeeRequired; - if (!pwalletMain->CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired)) + CRITICAL_BLOCK(pwalletMain->cs_vMasterKey) { - if (nPrice + nFeeRequired > pwalletMain->GetBalance()) - Error(strprintf(_("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())); - else - Error(_("Transaction creation failed")); - return; - } + bool fWasLocked = pwalletMain->IsLocked(); + if (!GetWalletPassphrase()) + return; + + if (!pwalletMain->CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired)) + { + if (nPrice + nFeeRequired > pwalletMain->GetBalance()) + Error(strprintf(_("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())); + else + Error(_("Transaction creation failed")); + return; + } + + if (fWasLocked) + pwalletMain->Lock(); + } // Transaction fee if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this)) @@ -2382,7 +2621,7 @@ CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInit m_listCtrlReceiving->SetFocus(); // Fill listctrl with address book data - CRITICAL_BLOCK(pwalletMain->cs_mapKeys) + CRITICAL_BLOCK(pwalletMain->cs_KeyStore) CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook) { string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue(); @@ -2581,8 +2820,18 @@ void CAddressBookDialog::OnButtonNew(wxCommandEvent& event) return; strName = dialog.GetValue(); - // Generate new key - strAddress = PubKeyToAddress(pwalletMain->GetKeyFromKeyPool()); + CRITICAL_BLOCK(pwalletMain->cs_vMasterKey) + { + bool fWasLocked = pwalletMain->IsLocked(); + if (!GetWalletPassphrase()) + return; + + // Generate new key + strAddress = PubKeyToAddress(pwalletMain->GetOrReuseKeyFromPool()); + + if (fWasLocked) + pwalletMain->Lock(); + } } // Add to list and select it diff --git a/src/ui.h b/src/ui.h index 3f06ad9..3bf7415 100644 --- a/src/ui.h +++ b/src/ui.h @@ -59,6 +59,8 @@ protected: void OnMenuFileExit(wxCommandEvent& event); void OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event); void OnMenuOptionsChangeYourAddress(wxCommandEvent& event); + void OnMenuOptionsEncryptWallet(wxCommandEvent& event); + void OnMenuOptionsChangeWalletPassphrase(wxCommandEvent& event); void OnMenuOptionsOptions(wxCommandEvent& event); void OnMenuHelpAbout(wxCommandEvent& event); void OnButtonSend(wxCommandEvent& event); diff --git a/src/uibase.cpp b/src/uibase.cpp index 1b901a1..18eec44 100644 --- a/src/uibase.cpp +++ b/src/uibase.cpp @@ -32,6 +32,12 @@ CMainFrameBase::CMainFrameBase( wxWindow* parent, wxWindowID id, const wxString& m_menuOptionsChangeYourAddress = new wxMenuItem( m_menuOptions, wxID_ANY, wxString( _("&Your Receiving Addresses...") ) , wxEmptyString, wxITEM_NORMAL ); m_menuOptions->Append( m_menuOptionsChangeYourAddress ); + m_menuOptionsEncryptWallet = new wxMenuItem( m_menuOptions, wxID_ANY, wxString( _("&Encrypt Wallet...") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuOptions->Append( m_menuOptionsEncryptWallet ); + + m_menuOptionsChangeWalletPassphrase = new wxMenuItem( m_menuOptions, wxID_ANY, wxString( _("&Change Wallet Encryption Passphrase...") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuOptions->Append( m_menuOptionsChangeWalletPassphrase ); + wxMenuItem* m_menuOptionsOptions; m_menuOptionsOptions = new wxMenuItem( m_menuOptions, wxID_PREFERENCES, wxString( _("&Options...") ) , wxEmptyString, wxITEM_NORMAL ); m_menuOptions->Append( m_menuOptionsOptions ); @@ -187,6 +193,8 @@ CMainFrameBase::CMainFrameBase( wxWindow* parent, wxWindowID id, const wxString& this->Connect( wxEVT_PAINT, wxPaintEventHandler( CMainFrameBase::OnPaint ) ); this->Connect( m_menuFileExit->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuFileExit ) ); this->Connect( m_menuOptionsChangeYourAddress->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuOptionsChangeYourAddress ) ); + this->Connect( m_menuOptionsEncryptWallet->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuOptionsEncryptWallet ) ); + this->Connect( m_menuOptionsChangeWalletPassphrase->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuOptionsChangeWalletPassphrase ) ); this->Connect( m_menuOptionsOptions->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuOptionsOptions ) ); this->Connect( m_menuHelpAbout->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuHelpAbout ) ); this->Connect( wxID_BUTTONSEND, wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( CMainFrameBase::OnButtonSend ) ); @@ -245,6 +253,8 @@ CMainFrameBase::~CMainFrameBase() this->Disconnect( wxEVT_PAINT, wxPaintEventHandler( CMainFrameBase::OnPaint ) ); this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuFileExit ) ); this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuOptionsChangeYourAddress ) ); + this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuOptionsEncryptWallet ) ); + this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuOptionsChangeWalletPassphrase ) ); this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuOptionsOptions ) ); this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuHelpAbout ) ); this->Disconnect( wxID_BUTTONSEND, wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( CMainFrameBase::OnButtonSend ) ); diff --git a/src/uibase.h b/src/uibase.h index 78f3d1b..ca0730b 100644 --- a/src/uibase.h +++ b/src/uibase.h @@ -98,6 +98,8 @@ class CMainFrameBase : public wxFrame virtual void OnPaint( wxPaintEvent& event ) { event.Skip(); } virtual void OnMenuFileExit( wxCommandEvent& event ) { event.Skip(); } virtual void OnMenuOptionsChangeYourAddress( wxCommandEvent& event ) { event.Skip(); } + virtual void OnMenuOptionsEncryptWallet( wxCommandEvent& event ) { event.Skip(); } + virtual void OnMenuOptionsChangeWalletPassphrase( wxCommandEvent& event ) { event.Skip(); } virtual void OnMenuOptionsOptions( wxCommandEvent& event ) { event.Skip(); } virtual void OnMenuHelpAbout( wxCommandEvent& event ) { event.Skip(); } virtual void OnButtonSend( wxCommandEvent& event ) { event.Skip(); } @@ -115,6 +117,8 @@ class CMainFrameBase : public wxFrame public: wxMenu* m_menuOptions; + wxMenuItem* m_menuOptionsEncryptWallet; + wxMenuItem* m_menuOptionsChangeWalletPassphrase; wxStatusBar* m_statusBar; wxTextCtrl* m_textCtrlAddress; wxListCtrl* m_listCtrlAll; diff --git a/src/wallet.cpp b/src/wallet.cpp index ba9221f..a211fde 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -5,11 +5,11 @@ #include "headers.h" #include "db.h" #include "cryptopp/sha.h" +#include "crypter.h" using namespace std; - ////////////////////////////////////////////////////////////////////////////// // // mapWallet @@ -17,10 +17,180 @@ using namespace std; bool CWallet::AddKey(const CKey& key) { - this->CKeyStore::AddKey(key); + if (!CCryptoKeyStore::AddKey(key)) + return false; + if (!fFileBacked) + return true; + if (!IsCrypted()) + return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.GetPrivKey()); +} + +bool CWallet::AddCryptedKey(const vector &vchPubKey, const vector &vchCryptedSecret) +{ + if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) + return false; if (!fFileBacked) return true; - return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.GetPrivKey()); + CRITICAL_BLOCK(cs_pwalletdbEncryption) + { + if (pwalletdbEncryption) + return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret); + else + return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret); + } +} + +bool CWallet::Unlock(const string& strWalletPassphrase) +{ + CRITICAL_BLOCK(cs_vMasterKey) + { + if (!IsLocked()) + return false; + + CCrypter crypter; + CKeyingMaterial vMasterKey; + + 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 string& strOldWalletPassphrase, const string& strNewWalletPassphrase) +{ + CRITICAL_BLOCK(cs_vMasterKey) + { + bool fWasLocked = IsLocked(); + + 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; +} + + +// 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::EncryptWallet(const string& strWalletPassphrase) +{ + CRITICAL_BLOCK(cs_mapPubKeys) + CRITICAL_BLOCK(cs_KeyStore) + CRITICAL_BLOCK(cs_vMasterKey) + CRITICAL_BLOCK(cs_pwalletdbEncryption) + { + 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; + + mapMasterKeys[++nMasterKeyMaxID] = kMasterKey; + if (fFileBacked) + { + pwalletdbEncryption = new CWalletDB(strWalletFile); + pwalletdbEncryption->TxnBegin(); + 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. + } + + if (fFileBacked) + { + CCorruptAddress corruptAddress; + pwalletdbEncryption->WriteSetting("addrIncoming", corruptAddress); + 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. + + pwalletdbEncryption->Close(); + pwalletdbEncryption = NULL; + } + + Lock(); + } + return true; } void CWallet::WalletUpdateSpent(const CTransaction &tx) @@ -98,14 +268,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) BOOST_FOREACH(const CTxOut& txout, wtx.vout) { if (txout.scriptPubKey == scriptDefaultKey) - { - if (!fFileBacked) - continue; - CWalletDB walletdb(strWalletFile); - vchDefaultKey = GetKeyFromKeyPool(); - walletdb.WriteDefaultKey(vchDefaultKey); - walletdb.WriteName(PubKeyToAddress(vchDefaultKey), ""); - } + SetDefaultKey(GetOrReuseKeyFromPool()); } // Notify UI @@ -790,7 +953,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW // Reserve a new key pair from key pool vector vchPubKey = reservekey.GetReservedKey(); - assert(mapKeys.count(vchPubKey)); + // assert(mapKeys.count(vchPubKey)); // Fill a vout to ourself, using same address type as the payment CScript scriptChange; @@ -910,15 +1073,24 @@ string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, { CReserveKey reservekey(this); int64 nFeeRequired; - if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired)) + CRITICAL_BLOCK(cs_vMasterKey) { - string 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()); - else - strError = _("Error: Transaction creation failed "); - printf("SendMoney() : %s", strError.c_str()); - return strError; + if (IsLocked()) + { + string strError = _("Error: Wallet locked, unable to create transaction "); + printf("SendMoney() : %s", strError.c_str()); + return strError; + } + if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired)) + { + string 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()); + else + strError = _("Error: Transaction creation failed "); + printf("SendMoney() : %s", strError.c_str()); + return strError; + } } if (fAskFee && !ThreadSafeAskFee(nFeeRequired, _("Sending..."), NULL)) @@ -963,15 +1135,14 @@ bool CWallet::LoadWallet(bool& fFirstRunRet) return nLoadWalletRet; fFirstRunRet = vchDefaultKey.empty(); - if (!mapKeys.count(vchDefaultKey)) + if (!HaveKey(vchDefaultKey)) { - // Create new default key + // Create new keyUser and set as default key RandAddSeedPerfmon(); - vchDefaultKey = GetKeyFromKeyPool(); + SetDefaultKey(GetOrReuseKeyFromPool()); if (!SetAddressBookName(PubKeyToAddress(vchDefaultKey), "")) return false; - CWalletDB(strWalletFile).WriteDefaultKey(vchDefaultKey); } CreateThread(ThreadFlushWalletDB, &strWalletFile); @@ -1023,6 +1194,17 @@ bool CWallet::GetTransaction(const uint256 &hashTx, CWalletTx& wtx) return false; } +bool CWallet::SetDefaultKey(const std::vector &vchPubKey) +{ + if (fFileBacked) + { + if (!CWalletDB(strWalletFile).WriteDefaultKey(vchPubKey)) + return false; + } + vchDefaultKey = vchPubKey; + return true; +} + bool GetWalletFile(CWallet* pwallet, string &strWalletFileOut) { if (!pwallet->fFileBacked) @@ -1031,14 +1213,16 @@ bool GetWalletFile(CWallet* pwallet, string &strWalletFileOut) return true; } -void CWallet::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) +bool CWallet::TopUpKeyPool() { - nIndex = -1; - keypool.vchPubKey.clear(); CRITICAL_BLOCK(cs_main) CRITICAL_BLOCK(cs_mapWallet) CRITICAL_BLOCK(cs_setKeyPool) + CRITICAL_BLOCK(cs_vMasterKey) { + if (IsLocked()) + return false; + CWalletDB walletdb(strWalletFile); // Top up key pool @@ -1049,18 +1233,36 @@ void CWallet::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) if (!setKeyPool.empty()) nEnd = *(--setKeyPool.end()) + 1; if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey()))) - throw runtime_error("ReserveKeyFromKeyPool() : writing generated key failed"); + throw runtime_error("TopUpKeyPool() : writing generated key failed"); setKeyPool.insert(nEnd); printf("keypool added key %"PRI64d", size=%d\n", nEnd, setKeyPool.size()); } + } + return true; +} + +void CWallet::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) +{ + nIndex = -1; + keypool.vchPubKey.clear(); + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_setKeyPool) + { + if (!IsLocked()) + TopUpKeyPool(); // Get the oldest key - assert(!setKeyPool.empty()); + 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 (!mapKeys.count(keypool.vchPubKey)) + if (!HaveKey(keypool.vchPubKey)) throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool"); assert(!keypool.vchPubKey.empty()); printf("keypool reserve %"PRI64d"\n", nIndex); @@ -1089,11 +1291,13 @@ void CWallet::ReturnKey(int64 nIndex) printf("keypool return %"PRI64d"\n", nIndex); } -vector CWallet::GetKeyFromKeyPool() +vector CWallet::GetOrReuseKeyFromPool() { int64 nIndex = 0; CKeyPool keypool; ReserveKeyFromKeyPool(nIndex, keypool); + if(nIndex == -1) + return vchDefaultKey; KeepKey(nIndex); return keypool.vchPubKey; } @@ -1103,6 +1307,8 @@ int64 CWallet::GetOldestKeyPoolTime() int64 nIndex = 0; CKeyPool keypool; ReserveKeyFromKeyPool(nIndex, keypool); + if (nIndex == -1) + return GetTime(); ReturnKey(nIndex); return keypool.nTime; } @@ -1134,3 +1340,4 @@ void CReserveKey::ReturnKey() nIndex = -1; vchPubKey.clear(); } + diff --git a/src/wallet.h b/src/wallet.h index bf7d8cc..d336d38 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -12,12 +12,14 @@ class CWalletTx; class CReserveKey; class CWalletDB; -class CWallet : public CKeyStore +class CWallet : public CCryptoKeyStore { private: bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, std::set >& setCoinsRet, int64& nValueRet) const; bool SelectCoins(int64 nTargetValue, std::set >& setCoinsRet, int64& nValueRet) const; + CWalletDB *pwalletdbEncryption; + CCriticalSection cs_pwalletdbEncryption; public: bool fFileBacked; @@ -26,14 +28,22 @@ public: std::set setKeyPool; CCriticalSection cs_setKeyPool; + typedef std::map MasterKeyMap; + MasterKeyMap mapMasterKeys; + unsigned int nMasterKeyMaxID; + CWallet() { fFileBacked = false; + nMasterKeyMaxID = 0; + pwalletdbEncryption = NULL; } CWallet(std::string strWalletFileIn) { strWalletFile = strWalletFileIn; fFileBacked = true; + nMasterKeyMaxID = 0; + pwalletdbEncryption = NULL; } mutable CCriticalSection cs_mapWallet; @@ -48,7 +58,16 @@ public: std::vector vchDefaultKey; + // keystore implementation bool AddKey(const CKey& key); + bool LoadKey(const CKey& key) { return CCryptoKeyStore::AddKey(key); } + bool AddCryptedKey(const std::vector &vchPubKey, const std::vector &vchCryptedSecret); + bool LoadCryptedKey(const std::vector &vchPubKey, const std::vector &vchCryptedSecret) { return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); } + + bool Unlock(const std::string& strWalletPassphrase); + bool ChangeWalletPassphrase(const std::string& strOldWalletPassphrase, const std::string& strNewWalletPassphrase); + bool EncryptWallet(const std::string& strWalletPassphrase); + bool AddToWallet(const CWalletTx& wtxIn); bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate = false); bool EraseFromWallet(uint256 hash); @@ -64,10 +83,11 @@ public: std::string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); std::string SendMoneyToBitcoinAddress(std::string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); + bool TopUpKeyPool(); void ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool); void KeepKey(int64 nIndex); void ReturnKey(int64 nIndex); - std::vector GetKeyFromKeyPool(); + std::vector GetOrReuseKeyFromPool(); int64 GetOldestKeyPoolTime(); bool IsMine(const CTxIn& txin) const; @@ -174,8 +194,14 @@ public: } } + int GetKeyPoolSize() + { + return setKeyPool.size(); + } + bool GetTransaction(const uint256 &hashTx, CWalletTx& wtx); + bool SetDefaultKey(const std::vector &vchPubKey); };