aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authors_nakamoto <s_nakamoto@1a98c847-1fd6-4fd8-948a-caf3550aa51b>2010-10-09 19:33:35 +0000
committers_nakamoto <s_nakamoto@1a98c847-1fd6-4fd8-948a-caf3550aa51b>2010-10-09 19:33:35 +0000
commit103849419a9c014a69c76b6f96e48b66cbc838ca (patch)
tree73bce9b5c3d96304138b25d1e714222ea3d4a384
parent0a27bd065e4803a35e436c610bedb06fe39123c6 (diff)
downloadbitcoin-103849419a9c014a69c76b6f96e48b66cbc838ca.tar.xz
key pool for safer wallet backup
git-svn-id: https://bitcoin.svn.sourceforge.net/svnroot/bitcoin/trunk@163 1a98c847-1fd6-4fd8-948a-caf3550aa51b
-rw-r--r--db.cpp65
-rw-r--r--db.h81
-rw-r--r--irc.cpp6
-rw-r--r--main.cpp61
-rw-r--r--main.h6
-rw-r--r--rpc.cpp2
-rw-r--r--serialize.h16
-rw-r--r--ui.cpp18
8 files changed, 198 insertions, 57 deletions
diff --git a/db.cpp b/db.cpp
index a7c18a6ffc..336d5a5ec0 100644
--- a/db.cpp
+++ b/db.cpp
@@ -576,6 +576,9 @@ bool LoadAddresses()
// CWalletDB
//
+static set<int64> setKeyPool;
+static CCriticalSection cs_setKeyPool;
+
bool CWalletDB::LoadWallet()
{
vchDefaultKey.clear();
@@ -654,6 +657,12 @@ bool CWalletDB::LoadWallet()
{
ssValue >> vchDefaultKey;
}
+ else if (strType == "pool")
+ {
+ int64 nIndex;
+ ssKey >> nIndex;
+ setKeyPool.insert(nIndex);
+ }
else if (strType == "version")
{
ssValue >> nFileVersion;
@@ -836,3 +845,59 @@ void BackupWallet(const string& strDest)
Sleep(100);
}
}
+
+void CWalletDB::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool)
+{
+ nIndex = -1;
+ keypool.vchPubKey.clear();
+ CRITICAL_BLOCK(cs_setKeyPool)
+ {
+ // Top up key pool
+ int64 nTargetSize = max(GetArg("-keypool", 100), (int64)0);
+ while (setKeyPool.size() < nTargetSize+1)
+ {
+ int64 nEnd = 1;
+ if (!setKeyPool.empty())
+ nEnd = *(--setKeyPool.end()) + 1;
+ if (!Write(make_pair(string("pool"), nEnd), CKeyPool(GenerateNewKey())))
+ throw runtime_error("ReserveKeyFromKeyPool() : writing generated key failed");
+ setKeyPool.insert(nEnd);
+ printf("keypool added key %"PRI64d", size=%d\n", nEnd, setKeyPool.size());
+ }
+
+ // Get the oldest key
+ assert(!setKeyPool.empty());
+ nIndex = *(setKeyPool.begin());
+ setKeyPool.erase(setKeyPool.begin());
+ if (!Read(make_pair(string("pool"), nIndex), keypool))
+ throw runtime_error("ReserveKeyFromKeyPool() : read failed");
+ if (!mapKeys.count(keypool.vchPubKey))
+ throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool");
+ assert(!keypool.vchPubKey.empty());
+ printf("keypool reserve %"PRI64d"\n", nIndex);
+ }
+}
+
+void CWalletDB::KeepKey(int64 nIndex)
+{
+ // Remove from key pool
+ Erase(make_pair(string("pool"), nIndex));
+ printf("keypool keep %"PRI64d"\n", nIndex);
+}
+
+void CWalletDB::ReturnKey(int64 nIndex)
+{
+ // Return to key pool
+ CRITICAL_BLOCK(cs_setKeyPool)
+ setKeyPool.insert(nIndex);
+ printf("keypool return %"PRI64d"\n", nIndex);
+}
+
+vector<unsigned char> CWalletDB::GetKeyFromKeyPool()
+{
+ int64 nIndex = 0;
+ CKeyPool keypool;
+ ReserveKeyFromKeyPool(nIndex, keypool);
+ KeepKey(nIndex);
+ return keypool.vchPubKey;
+}
diff --git a/db.h b/db.h
index 9b1f5e58be..dac277a016 100644
--- a/db.h
+++ b/db.h
@@ -308,6 +308,35 @@ bool LoadAddresses();
+class CKeyPool
+{
+public:
+ int64 nTime;
+ vector<unsigned char> vchPubKey;
+
+ CKeyPool()
+ {
+ nTime = GetTime();
+ }
+
+ CKeyPool(const vector<unsigned char>& vchPubKeyIn)
+ {
+ nTime = GetTime();
+ vchPubKey = vchPubKeyIn;
+ }
+
+ IMPLEMENT_SERIALIZE
+ (
+ if (!(nType & SER_GETHASH))
+ READWRITE(nVersion);
+ READWRITE(nTime);
+ READWRITE(vchPubKey);
+ )
+};
+
+
+
+
class CWalletDB : public CDB
{
public:
@@ -396,6 +425,13 @@ public:
}
bool LoadWallet();
+protected:
+ void ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool);
+ void KeepKey(int64 nIndex);
+ static void ReturnKey(int64 nIndex);
+ friend class CReserveKey;
+public:
+ vector<unsigned char> GetKeyFromKeyPool();
};
bool LoadWallet(bool& fFirstRunRet);
@@ -405,3 +441,48 @@ inline bool SetAddressBookName(const string& strAddress, const string& strName)
{
return CWalletDB().WriteName(strAddress, strName);
}
+
+class CReserveKey
+{
+protected:
+ int64 nIndex;
+ vector<unsigned char> vchPubKey;
+public:
+ CReserveKey()
+ {
+ nIndex = -1;
+ }
+
+ ~CReserveKey()
+ {
+ ReturnKey();
+ }
+
+ vector<unsigned char> GetReservedKey()
+ {
+ if (nIndex == -1)
+ {
+ CKeyPool keypool;
+ CWalletDB().ReserveKeyFromKeyPool(nIndex, keypool);
+ vchPubKey = keypool.vchPubKey;
+ }
+ assert(!vchPubKey.empty());
+ return vchPubKey;
+ }
+
+ void KeepKey()
+ {
+ if (nIndex != -1)
+ CWalletDB().KeepKey(nIndex);
+ nIndex = -1;
+ vchPubKey.clear();
+ }
+
+ void ReturnKey()
+ {
+ if (nIndex != -1)
+ CWalletDB::ReturnKey(nIndex);
+ nIndex = -1;
+ vchPubKey.clear();
+ }
+};
diff --git a/irc.cpp b/irc.cpp
index a520173f1b..0675d18617 100644
--- a/irc.cpp
+++ b/irc.cpp
@@ -126,7 +126,7 @@ bool RecvLineIRC(SOCKET hSocket, string& strLine)
}
}
-int RecvUntil(SOCKET hSocket, const char* psz1, const char* psz2=NULL, const char* psz3=NULL)
+int RecvUntil(SOCKET hSocket, const char* psz1, const char* psz2=NULL, const char* psz3=NULL, const char* psz4=NULL)
{
loop
{
@@ -141,6 +141,8 @@ int RecvUntil(SOCKET hSocket, const char* psz1, const char* psz2=NULL, const cha
return 2;
if (psz3 && strLine.find(psz3) != -1)
return 3;
+ if (psz4 && strLine.find(psz4) != -1)
+ return 4;
}
}
@@ -210,7 +212,7 @@ void ThreadIRCSeed2(void* parg)
return;
}
- if (!RecvUntil(hSocket, "Found your hostname", "using your IP address instead", "Couldn't look up your hostname"))
+ if (!RecvUntil(hSocket, "Found your hostname", "using your IP address instead", "Couldn't look up your hostname", "ignoring hostname"))
{
closesocket(hSocket);
hSocket = INVALID_SOCKET;
diff --git a/main.cpp b/main.cpp
index c03172ef12..7b04a1f634 100644
--- a/main.cpp
+++ b/main.cpp
@@ -158,7 +158,8 @@ bool AddToWallet(const CWalletTx& wtxIn)
if (txout.scriptPubKey == scriptDefaultKey)
{
CWalletDB walletdb;
- walletdb.WriteDefaultKey(GenerateNewKey());
+ vchDefaultKey = walletdb.GetKeyFromKeyPool();
+ walletdb.WriteDefaultKey(vchDefaultKey);
walletdb.WriteName(PubKeyToAddress(vchDefaultKey), "");
}
}
@@ -1493,15 +1494,6 @@ bool CBlock::AcceptBlock()
(nHeight == 74000 && hash != uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")))
return error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight);
- // Scanback checkpoint lockin
- for (CBlockIndex* pindex = pindexPrev; pindex->nHeight >= 74000; pindex = pindex->pprev)
- {
- if (pindex->nHeight == 74000 && pindex->GetBlockHash() != uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20"))
- return error("AcceptBlock() : rejected by scanback lockin at %d", pindex->nHeight);
- if (pindex->nHeight == 74638 && pindex->GetBlockHash() == uint256("0x0000000000790ab3f22ec756ad43b6ab569abf0bddeb97c67a6f7b1470a7ec1c"))
- return error("AcceptBlock() : rejected by scanback lockin at %d", pindex->nHeight);
- }
-
// Write block to history file
if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK)))
return error("AcceptBlock() : out of disk space");
@@ -1961,7 +1953,7 @@ bool AlreadyHave(CTxDB& txdb, const CInv& inv)
{
switch (inv.type)
{
- case MSG_TX: return mapTransactions.count(inv.hash) || txdb.ContainsTx(inv.hash);
+ case MSG_TX: return mapTransactions.count(inv.hash) || mapOrphanTransactions.count(inv.hash) || txdb.ContainsTx(inv.hash);
case MSG_BLOCK: return mapBlockIndex.count(inv.hash) || mapOrphanBlocks.count(inv.hash);
}
// Don't know what it is, just say we already got one
@@ -2472,7 +2464,7 @@ bool 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] = GenerateNewKey();
+ mapReuseKey[pfrom->addr.ip] = CWalletDB().GetKeyFromKeyPool();
// Send back approval of order and pubkey to use
CScript scriptPubKey;
@@ -2933,8 +2925,7 @@ void BitcoinMiner()
if (mapArgs.count("-4way"))
f4WaySSE2 = (mapArgs["-4way"] != "0");
- CKey key;
- key.MakeNewKey();
+ CReserveKey reservekey;
CBigNum bnExtraNonce = 0;
while (fGenerateBitcoins)
{
@@ -2961,9 +2952,9 @@ void BitcoinMiner()
CTransaction txNew;
txNew.vin.resize(1);
txNew.vin[0].prevout.SetNull();
- txNew.vin[0].scriptSig << nBits << ++bnExtraNonce;
+ txNew.vin[0].scriptSig << ++bnExtraNonce;
txNew.vout.resize(1);
- txNew.vout[0].scriptPubKey << key.GetPubKey() << OP_CHECKSIG;
+ txNew.vout[0].scriptPubKey << reservekey.GetReservedKey() << OP_CHECKSIG;
//
@@ -3113,10 +3104,8 @@ void BitcoinMiner()
{
if (pindexPrev == pindexBest)
{
- // Save key
- if (!AddKey(key))
- return;
- key.MakeNewKey();
+ // Remove key from key pool
+ reservekey.KeepKey();
// Track how many getdata requests this block gets
CRITICAL_BLOCK(cs_mapRequestCount)
@@ -3183,7 +3172,10 @@ void BitcoinMiner()
break;
// Update nTime every few seconds
- pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
+ int64 nNewTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
+ if (nNewTime != pblock->nTime && bnExtraNonce > 10)
+ bnExtraNonce = 0;
+ pblock->nTime = nNewTime;
tmp.block.nTime = ByteReverse(pblock->nTime);
}
}
@@ -3342,7 +3334,7 @@ bool SelectCoins(int64 nTargetValue, set<CWalletTx*>& setCoinsRet)
-bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CKey& keyRet, int64& nFeeRequiredRet)
+bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRequiredRet)
{
nFeeRequiredRet = 0;
CRITICAL_BLOCK(cs_main)
@@ -3386,18 +3378,20 @@ bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CK
// rediscover unknown transactions that were written with keys of ours to recover
// post-backup change.
- // New private key
- if (keyRet.IsNull())
- keyRet.MakeNewKey();
+ // Reserve a new key pair from key pool
+ vector<unsigned char> vchPubKey = reservekey.GetReservedKey();
+ assert(mapKeys.count(vchPubKey));
// Fill a vout to ourself, using same address type as the payment
CScript scriptChange;
if (scriptPubKey.GetBitcoinAddressHash160() != 0)
- scriptChange.SetBitcoinAddress(keyRet.GetPubKey());
+ scriptChange.SetBitcoinAddress(vchPubKey);
else
- scriptChange << keyRet.GetPubKey() << OP_CHECKSIG;
+ scriptChange << vchPubKey << OP_CHECKSIG;
wtxNew.vout.push_back(CTxOut(nChange, scriptChange));
}
+ else
+ reservekey.ReturnKey();
// Fill a vout to the payee
if (fChangeFirst)
@@ -3440,7 +3434,7 @@ bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CK
}
// Call after CreateTransaction unless you want to abort
-bool CommitTransaction(CWalletTx& wtxNew, const CKey& key)
+bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
{
CRITICAL_BLOCK(cs_main)
{
@@ -3452,9 +3446,8 @@ bool CommitTransaction(CWalletTx& wtxNew, const CKey& key)
// maybe makes sense; please don't do it anywhere else.
CWalletDB walletdb("r");
- // Add the change's private key to wallet
- if (!key.IsNull() && !AddKey(key))
- throw runtime_error("CommitTransaction() : AddKey failed");
+ // Take key pair from key pool so it won't be used again
+ reservekey.KeepKey();
// Add tx to wallet, because if it has change it's also ours,
// otherwise just for transaction history.
@@ -3496,9 +3489,9 @@ string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAs
{
CRITICAL_BLOCK(cs_main)
{
- CKey key;
+ CReserveKey reservekey;
int64 nFeeRequired;
- if (!CreateTransaction(scriptPubKey, nValue, wtxNew, key, nFeeRequired))
+ if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired))
{
string strError;
if (nValue + nFeeRequired > GetBalance())
@@ -3512,7 +3505,7 @@ string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAs
if (fAskFee && !ThreadSafeAskFee(nFeeRequired, _("Sending..."), NULL))
return "ABORTED";
- if (!CommitTransaction(wtxNew, key))
+ if (!CommitTransaction(wtxNew, reservekey))
return _("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.");
}
MainFrameRepaint();
diff --git a/main.h b/main.h
index f6993691ed..d4293b13b5 100644
--- a/main.h
+++ b/main.h
@@ -76,8 +76,8 @@ bool ProcessMessages(CNode* pfrom);
bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv);
bool SendMessages(CNode* pto, bool fSendTrickle);
int64 GetBalance();
-bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CKey& keyRet, int64& nFeeRequiredRet);
-bool CommitTransaction(CWalletTx& wtxNew, const CKey& key);
+bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRequiredRet);
+bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
bool BroadcastTransaction(CWalletTx& wtxNew);
string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false);
string SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false);
@@ -361,7 +361,7 @@ public:
{
if (scriptPubKey.size() < 6)
return "CTxOut(error)";
- return strprintf("CTxOut(nValue=%"PRI64d".%08"PRI64d", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,24).c_str());
+ return strprintf("CTxOut(nValue=%"PRI64d".%08"PRI64d", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30).c_str());
}
void print() const
diff --git a/rpc.cpp b/rpc.cpp
index 9a4757390f..19ec950040 100644
--- a/rpc.cpp
+++ b/rpc.cpp
@@ -275,7 +275,7 @@ Value getnewaddress(const Array& params, bool fHelp)
strLabel = params[0].get_str();
// Generate a new key that is added to wallet
- string strAddress = PubKeyToAddress(GenerateNewKey());
+ string strAddress = PubKeyToAddress(CWalletDB().GetKeyFromKeyPool());
SetAddressBookName(strAddress, strLabel);
return strAddress;
diff --git a/serialize.h b/serialize.h
index 94fbe63978..fde83e7a81 100644
--- a/serialize.h
+++ b/serialize.h
@@ -22,7 +22,7 @@ class CDataStream;
class CAutoFile;
static const unsigned int MAX_SIZE = 0x02000000;
-static const int VERSION = 31302;
+static const int VERSION = 31303;
static const char* pszSubVer = "";
@@ -725,39 +725,39 @@ public:
typedef vector_type::const_iterator const_iterator;
typedef vector_type::reverse_iterator reverse_iterator;
- explicit CDataStream(int nTypeIn=0, int nVersionIn=VERSION)
+ explicit CDataStream(int nTypeIn=SER_NETWORK, int nVersionIn=VERSION)
{
Init(nTypeIn, nVersionIn);
}
- CDataStream(const_iterator pbegin, const_iterator pend, int nTypeIn=0, int nVersionIn=VERSION) : vch(pbegin, pend)
+ CDataStream(const_iterator pbegin, const_iterator pend, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(pbegin, pend)
{
Init(nTypeIn, nVersionIn);
}
#if !defined(_MSC_VER) || _MSC_VER >= 1300
- CDataStream(const char* pbegin, const char* pend, int nTypeIn=0, int nVersionIn=VERSION) : vch(pbegin, pend)
+ CDataStream(const char* pbegin, const char* pend, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(pbegin, pend)
{
Init(nTypeIn, nVersionIn);
}
#endif
- CDataStream(const vector_type& vchIn, int nTypeIn=0, int nVersionIn=VERSION) : vch(vchIn.begin(), vchIn.end())
+ CDataStream(const vector_type& vchIn, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(vchIn.begin(), vchIn.end())
{
Init(nTypeIn, nVersionIn);
}
- CDataStream(const vector<char>& vchIn, int nTypeIn=0, int nVersionIn=VERSION) : vch(vchIn.begin(), vchIn.end())
+ CDataStream(const vector<char>& vchIn, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch(vchIn.begin(), vchIn.end())
{
Init(nTypeIn, nVersionIn);
}
- CDataStream(const vector<unsigned char>& vchIn, int nTypeIn=0, int nVersionIn=VERSION) : vch((char*)&vchIn.begin()[0], (char*)&vchIn.end()[0])
+ CDataStream(const vector<unsigned char>& vchIn, int nTypeIn=SER_NETWORK, int nVersionIn=VERSION) : vch((char*)&vchIn.begin()[0], (char*)&vchIn.end()[0])
{
Init(nTypeIn, nVersionIn);
}
- void Init(int nTypeIn=0, int nVersionIn=VERSION)
+ void Init(int nTypeIn=SER_NETWORK, int nVersionIn=VERSION)
{
nReadPos = 0;
nType = nTypeIn;
diff --git a/ui.cpp b/ui.cpp
index a82365f361..d72e1a00a1 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -1170,7 +1170,7 @@ void CMainFrame::OnButtonNew(wxCommandEvent& event)
string strName = dialog.GetValue();
// Generate new key
- string strAddress = PubKeyToAddress(GenerateNewKey());
+ string strAddress = PubKeyToAddress(CWalletDB().GetKeyFromKeyPool());
// Save
SetAddressBookName(strAddress, strName);
@@ -1425,7 +1425,10 @@ CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetails
if (txout.IsMine())
strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
- strHTML += "<b>Inputs:</b><br>";
+ strHTML += "<br><b>Transaction:</b><br>";
+ strHTML += HtmlEscape(wtx.ToString(), true);
+
+ strHTML += "<br><b>Inputs:</b><br>";
CRITICAL_BLOCK(cs_mapWallet)
{
foreach(const CTxIn& txin, wtx.vin)
@@ -1444,9 +1447,6 @@ CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetails
}
}
}
-
- strHTML += "<br><hr><br><b>Transaction:</b><br>";
- strHTML += HtmlEscape(wtx.ToString(), true);
}
@@ -2245,9 +2245,9 @@ void CSendingDialog::OnReply2(CDataStream& vRecv)
Error(_("Insufficient funds"));
return;
}
- CKey key;
+ CReserveKey reservekey;
int64 nFeeRequired;
- if (!CreateTransaction(scriptPubKey, nPrice, wtx, key, nFeeRequired))
+ if (!CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
{
if (nPrice + nFeeRequired > GetBalance())
Error(strprintf(_("This is an oversized transaction that requires a transaction fee of %s"), FormatMoney(nFeeRequired).c_str()));
@@ -2287,7 +2287,7 @@ void CSendingDialog::OnReply2(CDataStream& vRecv)
return;
// Commit
- if (!CommitTransaction(wtx, key))
+ if (!CommitTransaction(wtx, reservekey))
{
Error(_("The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."));
return;
@@ -2565,7 +2565,7 @@ void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
strName = dialog.GetValue();
// Generate new key
- strAddress = PubKeyToAddress(GenerateNewKey());
+ strAddress = PubKeyToAddress(CWalletDB().GetKeyFromKeyPool());
}
// Add to list and select it