command line and JSON-RPC first draft, requires Boost 1.35 or higher for boost::asio,

added SetBitcoinAddress and GetBitcoinAddress methods on CScript, 
critsect interlocks around mapAddressBook, 
added some random delays in tx broadcast to improve privacy, 
now compiles with MSVC 8.0

git-svn-id: https://bitcoin.svn.sourceforge.net/svnroot/bitcoin/trunk@60 1a98c847-1fd6-4fd8-948a-caf3550aa51b
This commit is contained in:
s_nakamoto
2010-02-12 20:38:44 +00:00
parent fa9dbd6b62
commit 98500d70a8
23 changed files with 1202 additions and 348 deletions

151
main.cpp
View File

@@ -26,6 +26,7 @@ CBlockIndex* pindexGenesisBlock = NULL;
int nBestHeight = -1;
uint256 hashBestChain = 0;
CBlockIndex* pindexBest = NULL;
int64 nTimeBestReceived = 0;
map<uint256, CBlock*> mapOrphanBlocks;
multimap<uint256, CBlock*> mapOrphanBlocksByPrev;
@@ -45,6 +46,9 @@ CKey keyUser;
map<uint256, int> mapRequestCount;
CCriticalSection cs_mapRequestCount;
map<string, string> mapAddressBook;
CCriticalSection cs_mapAddressBook;
// Settings
int fGenerateBitcoins = false;
int64 nTransactionFee = 0;
@@ -573,7 +577,7 @@ bool CTransaction::RemoveFromMemoryPool()
int CMerkleTx::GetDepthInMainChain() const
int CMerkleTx::GetDepthInMainChain(int& nHeightRet) const
{
if (hashBlock == 0 || nIndex == -1)
return 0;
@@ -594,6 +598,7 @@ int CMerkleTx::GetDepthInMainChain() const
fMerkleVerified = true;
}
nHeightRet = pindex->nHeight;
return pindexBest->nHeight - pindex->nHeight + 1;
}
@@ -708,15 +713,20 @@ void CWalletTx::RelayWalletTransaction(CTxDB& txdb)
}
}
void RelayWalletTransactions()
void ResendWalletTransactions()
{
static int64 nLastTime;
if (GetTime() - nLastTime < 10 * 60)
// Do this infrequently and randomly to avoid giving away
// that these are our transactions.
static int64 nNextTime;
if (GetTime() < nNextTime)
return;
bool fFirst = (nNextTime == 0);
nNextTime = GetTime() + GetRand(120 * 60);
if (fFirst)
return;
nLastTime = GetTime();
// Rebroadcast any of our txes that aren't in a block yet
printf("RelayWalletTransactions()\n");
printf("ResendWalletTransactions()\n");
CTxDB txdb("r");
CRITICAL_BLOCK(cs_mapWallet)
{
@@ -725,7 +735,10 @@ void RelayWalletTransactions()
foreach(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
{
CWalletTx& wtx = item.second;
mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx));
// Don't rebroadcast until it's had plenty of time that
// it should have gotten in already by now.
if (nTimeBestReceived - wtx.nTimeReceived > 60 * 60)
mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx));
}
foreach(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted)
{
@@ -1219,10 +1232,11 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos)
}
}
// New best link
// New best block
hashBestChain = hash;
pindexBest = pindexNew;
nBestHeight = pindexBest->nHeight;
nTimeBestReceived = GetTime();
nTransactionsUpdated++;
printf("AddToBlockIndex: new best=%s height=%d\n", hashBestChain.ToString().substr(0,16).c_str(), nBestHeight);
}
@@ -1232,9 +1246,6 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos)
if (pindexNew == pindexBest)
{
// Relay wallet transactions that haven't gotten in yet
RelayWalletTransactions();
// Notify UI to display prev block's coinbase if it was ours
static uint256 hashPrevBestCoinBase;
CRITICAL_BLOCK(cs_mapWallet)
@@ -2248,7 +2259,7 @@ bool SendMessages(CNode* pto)
return true;
// Keep-alive ping
if (pto->nLastSend && GetTime() - pto->nLastSend > 12 * 60 && pto->vSend.empty())
if (pto->nLastSend && GetTime() - pto->nLastSend > 30 * 60 && pto->vSend.empty())
pto->PushMessage("ping");
// Address refresh broadcast
@@ -2270,60 +2281,81 @@ bool SendMessages(CNode* pto)
}
}
// Delay tx inv messages to protect privacy,
// trickle them out to a few nodes at a time.
bool fSendTxInv = false;
if (GetTimeMillis() - pto->nLastSentTxInv > 1800 + GetRand(200))
{
pto->nLastSentTxInv = GetTimeMillis();
fSendTxInv = true;
}
// Resend wallet transactions that haven't gotten in a block yet
ResendWalletTransactions();
//
// Message: addr
//
vector<CAddress> vAddrToSend;
vAddrToSend.reserve(pto->vAddrToSend.size());
vector<CAddress> vAddr;
vAddr.reserve(pto->vAddrToSend.size());
foreach(const CAddress& addr, pto->vAddrToSend)
{
// returns true if wasn't already contained in the set
if (pto->setAddrKnown.insert(addr).second)
{
vAddrToSend.push_back(addr);
if (vAddrToSend.size() >= 1000)
vAddr.push_back(addr);
if (vAddr.size() >= 1000)
{
pto->PushMessage("addr", vAddrToSend);
vAddrToSend.clear();
pto->PushMessage("addr", vAddr);
vAddr.clear();
}
}
}
pto->vAddrToSend.clear();
if (!vAddrToSend.empty())
pto->PushMessage("addr", vAddrToSend);
if (!vAddr.empty())
pto->PushMessage("addr", vAddr);
//
// Message: inventory
//
vector<CInv> vInventoryToSend;
vector<CInv> vInv;
vector<CInv> vInvWait;
CRITICAL_BLOCK(pto->cs_inventory)
{
vInventoryToSend.reserve(pto->vInventoryToSend.size());
vInv.reserve(pto->vInventoryToSend.size());
vInvWait.reserve(pto->vInventoryToSend.size());
foreach(const CInv& inv, pto->vInventoryToSend)
{
// delay txes
if (!fSendTxInv && inv.type == MSG_TX)
{
vInvWait.push_back(inv);
continue;
}
// returns true if wasn't already contained in the set
if (pto->setInventoryKnown.insert(inv).second)
{
vInventoryToSend.push_back(inv);
if (vInventoryToSend.size() >= 1000)
vInv.push_back(inv);
if (vInv.size() >= 1000)
{
pto->PushMessage("inv", vInventoryToSend);
vInventoryToSend.clear();
pto->PushMessage("inv", vInv);
vInv.clear();
}
}
}
pto->vInventoryToSend.clear();
pto->vInventoryToSend = vInvWait;
}
if (!vInventoryToSend.empty())
pto->PushMessage("inv", vInventoryToSend);
if (!vInv.empty())
pto->PushMessage("inv", vInv);
//
// Message: getdata
//
vector<CInv> vAskFor;
vector<CInv> vGetData;
int64 nNow = GetTime() * 1000000;
CTxDB txdb("r");
while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow)
@@ -2332,17 +2364,17 @@ bool SendMessages(CNode* pto)
if (!AlreadyHave(txdb, inv))
{
printf("sending getdata: %s\n", inv.ToString().c_str());
vAskFor.push_back(inv);
if (vAskFor.size() >= 1000)
vGetData.push_back(inv);
if (vGetData.size() >= 1000)
{
pto->PushMessage("getdata", vAskFor);
vAskFor.clear();
pto->PushMessage("getdata", vGetData);
vGetData.clear();
}
}
pto->mapAskFor.erase(pto->mapAskFor.begin());
}
if (!vAskFor.empty())
pto->PushMessage("getdata", vAskFor);
if (!vGetData.empty())
pto->PushMessage("getdata", vGetData);
}
return true;
@@ -2405,7 +2437,6 @@ void ThreadBitcoinMiner(void* parg)
vnThreadsRunning[3]--;
PrintException(NULL, "ThreadBitcoinMiner()");
}
printf("ThreadBitcoinMiner exiting, %d threads remaining\n", vnThreadsRunning[3]);
}
@@ -2842,6 +2873,13 @@ bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CK
// Fill a vout back to self with any change
if (nValueIn > nTotalValue)
{
// Note: We use a new key here to keep it from being obvious which side is the change.
// The drawback is that by not reusing a previous key, the change may be lost if a
// backup is restored, if the backup doesn't have the new private key for the change.
// If we reused the old key, it would be possible to add code to look for and
// rediscover unknown transactions that were written with keys of ours to recover
// post-backup change.
// New private key
if (keyRet.IsNull())
keyRet.MakeNewKey();
@@ -2899,7 +2937,7 @@ bool CommitTransactionSpent(const CWalletTx& wtxNew, const CKey& key)
//// update: This matters even less now that fSpent can get corrected
//// when transactions are seen in VerifySignature. The remote chance of
//// unmarked fSpent will be handled by that. Don't need to make this
//// transactional.
//// transactional. Pls delete this comment block later.
// This is only to keep the database open to defeat the auto-flush for the
// duration of this scope. This is the only place where this optimization
@@ -2932,7 +2970,7 @@ bool CommitTransactionSpent(const CWalletTx& wtxNew, const CKey& key)
bool SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew)
string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew)
{
CRITICAL_BLOCK(cs_main)
{
@@ -2945,13 +2983,13 @@ bool SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew)
strError = strprintf("Error: This is an oversized transaction that requires a transaction fee of %s ", FormatMoney(nFeeRequired).c_str());
else
strError = "Error: Transaction creation failed ";
wxMessageBox(strError, "Sending...");
return error("SendMoney() : %s", strError.c_str());
printf("SendMoney() : %s", strError.c_str());
return strError;
}
if (!CommitTransactionSpent(wtxNew, key))
{
wxMessageBox("Error finalizing transaction ", "Sending...");
return error("SendMoney() : Error finalizing transaction");
printf("SendMoney() : Error finalizing transaction");
return "Error finalizing transaction";
}
// Track how many getdata requests our transaction gets
@@ -2964,11 +3002,32 @@ bool SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew)
if (!wtxNew.AcceptTransaction())
{
// This must not fail. The transaction has already been signed and recorded.
wxMessageBox("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.", "Sending...");
return error("SendMoney() : Error: Transaction not valid");
printf("SendMoney() : Error: Transaction not valid");
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.";
}
wtxNew.RelayWalletTransaction();
}
MainFrameRepaint();
return true;
return "";
}
string SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWalletTx& wtxNew)
{
// Check amount
if (nValue <= 0)
return "Invalid amount";
if (nValue + nTransactionFee > GetBalance())
return "You don't have enough money";
// Parse bitcoin address
uint160 hash160;
if (!AddressToHash160(strAddress, hash160))
return "Invalid bitcoin address";
// Send to bitcoin address
CScript scriptPubKey;
scriptPubKey.SetBitcoinAddress(hash160);
return SendMoney(scriptPubKey, nValue, wtxNew);
}