Skip to content
Permalink
Browse files

key pool for safer wallet backup

git-svn-id: https://bitcoin.svn.sourceforge.net/svnroot/bitcoin/trunk@163 1a98c847-1fd6-4fd8-948a-caf3550aa51b
  • Loading branch information...
non-github-bitcoin committed Oct 9, 2010
1 parent 0a27bd0 commit 103849419a9c014a69c76b6f96e48b66cbc838ca
Showing with 198 additions and 57 deletions.
  1. +65 −0 db.cpp
  2. +81 −0 db.h
  3. +4 −2 irc.cpp
  4. +27 −34 main.cpp
  5. +3 −3 main.h
  6. +1 −1 rpc.cpp
  7. +8 −8 serialize.h
  8. +9 −9 ui.cpp
65 db.cpp
@@ -576,6 +576,9 @@ bool LoadAddresses()
// CWalletDB // CWalletDB
// //


static set<int64> setKeyPool;
static CCriticalSection cs_setKeyPool;

bool CWalletDB::LoadWallet() bool CWalletDB::LoadWallet()
{ {
vchDefaultKey.clear(); vchDefaultKey.clear();
@@ -654,6 +657,12 @@ bool CWalletDB::LoadWallet()
{ {
ssValue >> vchDefaultKey; ssValue >> vchDefaultKey;
} }
else if (strType == "pool")
{
int64 nIndex;
ssKey >> nIndex;
setKeyPool.insert(nIndex);
}
else if (strType == "version") else if (strType == "version")
{ {
ssValue >> nFileVersion; ssValue >> nFileVersion;
@@ -836,3 +845,59 @@ void BackupWallet(const string& strDest)
Sleep(100); 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;
}
81 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 class CWalletDB : public CDB
{ {
public: public:
@@ -396,6 +425,13 @@ class CWalletDB : public CDB
} }


bool LoadWallet(); 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); bool LoadWallet(bool& fFirstRunRet);
@@ -405,3 +441,48 @@ inline bool SetAddressBookName(const string& strAddress, const string& strName)
{ {
return CWalletDB().WriteName(strAddress, 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();
}
};
@@ -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 loop
{ {
@@ -141,6 +141,8 @@ int RecvUntil(SOCKET hSocket, const char* psz1, const char* psz2=NULL, const cha
return 2; return 2;
if (psz3 && strLine.find(psz3) != -1) if (psz3 && strLine.find(psz3) != -1)
return 3; return 3;
if (psz4 && strLine.find(psz4) != -1)
return 4;
} }
} }


@@ -210,7 +212,7 @@ void ThreadIRCSeed2(void* parg)
return; 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); closesocket(hSocket);
hSocket = INVALID_SOCKET; hSocket = INVALID_SOCKET;
@@ -158,7 +158,8 @@ bool AddToWallet(const CWalletTx& wtxIn)
if (txout.scriptPubKey == scriptDefaultKey) if (txout.scriptPubKey == scriptDefaultKey)
{ {
CWalletDB walletdb; CWalletDB walletdb;
walletdb.WriteDefaultKey(GenerateNewKey()); vchDefaultKey = walletdb.GetKeyFromKeyPool();
walletdb.WriteDefaultKey(vchDefaultKey);
walletdb.WriteName(PubKeyToAddress(vchDefaultKey), ""); walletdb.WriteName(PubKeyToAddress(vchDefaultKey), "");
} }
} }
@@ -1493,15 +1494,6 @@ bool CBlock::AcceptBlock()
(nHeight == 74000 && hash != uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20"))) (nHeight == 74000 && hash != uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")))
return error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight); 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 // Write block to history file
if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK))) if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK)))
return error("AcceptBlock() : out of disk space"); return error("AcceptBlock() : out of disk space");
@@ -1961,7 +1953,7 @@ bool AlreadyHave(CTxDB& txdb, const CInv& inv)
{ {
switch (inv.type) 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); case MSG_BLOCK: return mapBlockIndex.count(inv.hash) || mapOrphanBlocks.count(inv.hash);
} }
// Don't know what it is, just say we already got one // 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 // Keep giving the same key to the same ip until they use it
if (!mapReuseKey.count(pfrom->addr.ip)) 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 // Send back approval of order and pubkey to use
CScript scriptPubKey; CScript scriptPubKey;
@@ -2933,8 +2925,7 @@ void BitcoinMiner()
if (mapArgs.count("-4way")) if (mapArgs.count("-4way"))
f4WaySSE2 = (mapArgs["-4way"] != "0"); f4WaySSE2 = (mapArgs["-4way"] != "0");


CKey key; CReserveKey reservekey;
key.MakeNewKey();
CBigNum bnExtraNonce = 0; CBigNum bnExtraNonce = 0;
while (fGenerateBitcoins) while (fGenerateBitcoins)
{ {
@@ -2961,9 +2952,9 @@ void BitcoinMiner()
CTransaction txNew; CTransaction txNew;
txNew.vin.resize(1); txNew.vin.resize(1);
txNew.vin[0].prevout.SetNull(); txNew.vin[0].prevout.SetNull();
txNew.vin[0].scriptSig << nBits << ++bnExtraNonce; txNew.vin[0].scriptSig << ++bnExtraNonce;
txNew.vout.resize(1); 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) if (pindexPrev == pindexBest)
{ {
// Save key // Remove key from key pool
if (!AddKey(key)) reservekey.KeepKey();
return;
key.MakeNewKey();


// Track how many getdata requests this block gets // Track how many getdata requests this block gets
CRITICAL_BLOCK(cs_mapRequestCount) CRITICAL_BLOCK(cs_mapRequestCount)
@@ -3183,7 +3172,10 @@ void BitcoinMiner()
break; break;


// Update nTime every few seconds // 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); 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; nFeeRequiredRet = 0;
CRITICAL_BLOCK(cs_main) 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 // rediscover unknown transactions that were written with keys of ours to recover
// post-backup change. // post-backup change.


// New private key // Reserve a new key pair from key pool
if (keyRet.IsNull()) vector<unsigned char> vchPubKey = reservekey.GetReservedKey();
keyRet.MakeNewKey(); assert(mapKeys.count(vchPubKey));


// Fill a vout to ourself, using same address type as the payment // Fill a vout to ourself, using same address type as the payment
CScript scriptChange; CScript scriptChange;
if (scriptPubKey.GetBitcoinAddressHash160() != 0) if (scriptPubKey.GetBitcoinAddressHash160() != 0)
scriptChange.SetBitcoinAddress(keyRet.GetPubKey()); scriptChange.SetBitcoinAddress(vchPubKey);
else else
scriptChange << keyRet.GetPubKey() << OP_CHECKSIG; scriptChange << vchPubKey << OP_CHECKSIG;
wtxNew.vout.push_back(CTxOut(nChange, scriptChange)); wtxNew.vout.push_back(CTxOut(nChange, scriptChange));
} }
else
reservekey.ReturnKey();


// Fill a vout to the payee // Fill a vout to the payee
if (fChangeFirst) if (fChangeFirst)
@@ -3440,7 +3434,7 @@ bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CK
} }


// Call after CreateTransaction unless you want to abort // 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) 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. // maybe makes sense; please don't do it anywhere else.
CWalletDB walletdb("r"); CWalletDB walletdb("r");


// Add the change's private key to wallet // Take key pair from key pool so it won't be used again
if (!key.IsNull() && !AddKey(key)) reservekey.KeepKey();
throw runtime_error("CommitTransaction() : AddKey failed");


// Add tx to wallet, because if it has change it's also ours, // Add tx to wallet, because if it has change it's also ours,
// otherwise just for transaction history. // otherwise just for transaction history.
@@ -3496,9 +3489,9 @@ string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAs
{ {
CRITICAL_BLOCK(cs_main) CRITICAL_BLOCK(cs_main)
{ {
CKey key; CReserveKey reservekey;
int64 nFeeRequired; int64 nFeeRequired;
if (!CreateTransaction(scriptPubKey, nValue, wtxNew, key, nFeeRequired)) if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired))
{ {
string strError; string strError;
if (nValue + nFeeRequired > GetBalance()) if (nValue + nFeeRequired > GetBalance())
@@ -3512,7 +3505,7 @@ string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAs
if (fAskFee && !ThreadSafeAskFee(nFeeRequired, _("Sending..."), NULL)) if (fAskFee && !ThreadSafeAskFee(nFeeRequired, _("Sending..."), NULL))
return "ABORTED"; 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."); 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(); MainFrameRepaint();
6 main.h
@@ -76,8 +76,8 @@ bool ProcessMessages(CNode* pfrom);
bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv); bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv);
bool SendMessages(CNode* pto, bool fSendTrickle); bool SendMessages(CNode* pto, bool fSendTrickle);
int64 GetBalance(); int64 GetBalance();
bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CKey& keyRet, int64& nFeeRequiredRet); bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRequiredRet);
bool CommitTransaction(CWalletTx& wtxNew, const CKey& key); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
bool BroadcastTransaction(CWalletTx& wtxNew); bool BroadcastTransaction(CWalletTx& wtxNew);
string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false);
string SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); string SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false);
@@ -361,7 +361,7 @@ class CTxOut
{ {
if (scriptPubKey.size() < 6) if (scriptPubKey.size() < 6)
return "CTxOut(error)"; 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 void print() const

0 comments on commit 1038494

Please sign in to comment.
You can’t perform that action at this time.