static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
-
+// Encode a byte sequence as a base58-encoded string
inline std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend)
{
CAutoBN_CTX pctx;
return str;
}
+// Encode a byte vector as a base58-encoded string
inline std::string EncodeBase58(const std::vector<unsigned char>& vch)
{
return EncodeBase58(&vch[0], &vch[0] + vch.size());
}
+// Decode a base58-encoded string psz into byte vector vchRet
+// returns true if decoding is succesful
inline bool DecodeBase58(const char* psz, std::vector<unsigned char>& vchRet)
{
CAutoBN_CTX pctx;
return true;
}
+// Decode a base58-encoded string str into byte vector vchRet
+// returns true if decoding is succesful
inline bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet)
{
return DecodeBase58(str.c_str(), vchRet);
-
+// Encode a byte vector to a base58-encoded string, including checksum
inline std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn)
{
// add 4-byte hash check to the end
return EncodeBase58(vch);
}
+// Decode a base58-encoded string psz that includes a checksum, into byte vector vchRet
+// returns true if decoding is succesful
inline bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet)
{
if (!DecodeBase58(psz, vchRet))
return true;
}
+// Decode a base58-encoded string str that includes a checksum, into byte vector vchRet
+// returns true if decoding is succesful
inline bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet)
{
return DecodeBase58Check(str.c_str(), vchRet);
-
+// Base class for all base58-encoded data
class CBase58Data
{
protected:
+ // the version byte
unsigned char nVersion;
+
+ // the actually encoded data
std::vector<unsigned char> vchData;
CBase58Data()
~CBase58Data()
{
+ // zero the memory, as it may contain sensitive data
if (!vchData.empty())
memset(&vchData[0], 0, vchData.size());
}
bool operator> (const CBase58Data& b58) const { return CompareTo(b58) > 0; }
};
-
+// base58-encoded bitcoin addresses
+// Addresses have version 0 or 111 (testnet)
+// The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key
class CBitcoinAddress : public CBase58Data
{
public:
bool IsValid() const
{
- int nExpectedSize = 20;
+ unsigned int nExpectedSize = 20;
bool fExpectTestNet = false;
switch(nVersion)
{
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
#include "headers.h"
-#include "cryptopp/sha.h"
#include "db.h"
#include "net.h"
#include "init.h"
typedef Value(*rpcfn_type)(const Array& params, bool fHelp);
extern map<string, rpcfn_type> mapCallTable;
+static std::string strRPCUserColonPass;
+
static int64 nWalletUnlockTime;
static CCriticalSection cs_nWalletUnlockTime;
}
-void PrintConsole(const char* format, ...)
+void PrintConsole(const std::string &format, ...)
{
char buffer[50000];
int limit = sizeof(buffer);
va_list arg_ptr;
va_start(arg_ptr, format);
- int ret = _vsnprintf(buffer, limit, format, arg_ptr);
+ int ret = _vsnprintf(buffer, limit, format.c_str(), arg_ptr);
va_end(arg_ptr);
if (ret < 0 || ret >= limit)
{
buffer[limit-1] = 0;
}
printf("%s", buffer);
-#if defined(__WXMSW__) && defined(GUI)
- MyMessageBox(buffer, "Bitcoin", wxOK | wxICON_EXCLAMATION);
-#else
fprintf(stdout, "%s", buffer);
-#endif
}
// We already filter duplicates, but these deprecated screw up the sort order
if (strMethod == "getamountreceived" ||
strMethod == "getallreceived" ||
+ strMethod == "getblocknumber" || // deprecated
(strMethod.find("label") != string::npos))
continue;
if (strCommand != "" && strMethod != strCommand)
// Help text is returned in an exception
string strHelp = string(e.what());
if (strCommand == "")
- if (strHelp.find('\n') != -1)
+ if (strHelp.find('\n') != string::npos)
strHelp = strHelp.substr(0, strHelp.find('\n'));
strRet += strHelp + "\n";
}
throw runtime_error(
"stop\n"
"Stop bitcoin server.");
-
+#ifndef QT_GUI
// Shutdown will take long enough that the response should get back
CreateThread(Shutdown, NULL);
return "bitcoin server stopping";
+#else
+ throw runtime_error("NYI: cannot shut down GUI with RPC command");
+#endif
}
}
+// deprecated
Value getblocknumber(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 0)
throw runtime_error(
"getblocknumber\n"
- "Returns the block number of the latest block in the longest block chain.");
+ "Deprecated. Use getblockcount.");
return nBestHeight;
}
return wtx.GetHash().GetHex();
}
+static const string strMessageMagic = "Bitcoin Signed Message:\n";
+
+Value signmessage(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() != 2)
+ throw runtime_error(
+ "signmessage <bitcoinaddress> <message>\n"
+ "Sign a message with the private key of an address");
+
+ if (pwalletMain->IsLocked())
+ throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
+
+ string strAddress = params[0].get_str();
+ string strMessage = params[1].get_str();
+
+ CBitcoinAddress addr(strAddress);
+ if (!addr.IsValid())
+ throw JSONRPCError(-3, "Invalid address");
+
+ CKey key;
+ if (!pwalletMain->GetKey(addr, key))
+ throw JSONRPCError(-4, "Private key not available");
+
+ CDataStream ss(SER_GETHASH);
+ ss << strMessageMagic;
+ ss << strMessage;
+
+ vector<unsigned char> vchSig;
+ if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
+ throw JSONRPCError(-5, "Sign failed");
+
+ return EncodeBase64(&vchSig[0], vchSig.size());
+}
+
+Value verifymessage(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() != 3)
+ throw runtime_error(
+ "verifymessage <bitcoinaddress> <signature> <message>\n"
+ "Verify a signed message");
+
+ string strAddress = params[0].get_str();
+ string strSign = params[1].get_str();
+ string strMessage = params[2].get_str();
+
+ CBitcoinAddress addr(strAddress);
+ if (!addr.IsValid())
+ throw JSONRPCError(-3, "Invalid address");
+
+ bool fInvalid = false;
+ vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
+
+ if (fInvalid)
+ throw JSONRPCError(-5, "Malformed base64 encoding");
+
+ CDataStream ss(SER_GETHASH);
+ ss << strMessageMagic;
+ ss << strMessage;
+
+ CKey key;
+ if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
+ return false;
+
+ return (key.GetAddress() == addr);
+}
+
Value getreceivedbyaddress(const Array& params, bool fHelp)
{
Object obj;
obj.push_back(Pair("address", address.ToString()));
obj.push_back(Pair("account", strAccount));
- obj.push_back(Pair("label", strAccount)); // deprecated
obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
ret.push_back(obj);
int nConf = (*it).second.nConf;
Object obj;
obj.push_back(Pair("account", (*it).first));
- obj.push_back(Pair("label", (*it).first)); // deprecated
obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
ret.push_back(obj);
return ret;
}
+Value listsinceblock(const Array& params, bool fHelp)
+{
+ if (fHelp)
+ throw runtime_error(
+ "listsinceblock [blockid] [target-confirmations]\n"
+ "Get all transactions in blocks since block [blockid], or all transactions if omitted");
+
+ CBlockIndex *pindex = NULL;
+ int target_confirms = 1;
+
+ if (params.size() > 0)
+ {
+ uint256 blockId = 0;
+
+ blockId.SetHex(params[0].get_str());
+ pindex = CBlockLocator(blockId).GetBlockIndex();
+ }
+
+ if (params.size() > 1)
+ {
+ target_confirms = params[1].get_int();
+
+ if (target_confirms < 1)
+ throw JSONRPCError(-8, "Invalid parameter");
+ }
+
+ int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
+
+ Array transactions;
+
+ for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
+ {
+ CWalletTx tx = (*it).second;
+
+ if (depth == -1 || tx.GetDepthInMainChain() < depth)
+ ListTransactions(tx, "*", 0, true, transactions);
+ }
+
+ uint256 lastblock;
+
+ if (target_confirms == 1)
+ {
+ printf("oops!\n");
+ lastblock = hashBestChain;
+ }
+ else
+ {
+ int target_height = pindexBest->nHeight + 1 - target_confirms;
+
+ CBlockIndex *block;
+ for (block = pindexBest;
+ block && block->nHeight > target_height;
+ block = block->pprev) { }
+
+ lastblock = block ? block->GetBlockHash() : 0;
+ }
+
+ Object ret;
+ ret.push_back(Pair("transactions", transactions));
+ ret.push_back(Pair("lastblock", lastblock.GetHex()));
+
+ return ret;
+}
+
Value gettransaction(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw JSONRPCError(-17, "Error: Wallet is already unlocked.");
// Note that the walletpassphrase is stored in params[0] which is not mlock()ed
- string strWalletPass;
+ SecureString strWalletPass;
strWalletPass.reserve(100);
- mlock(&strWalletPass[0], strWalletPass.capacity());
- strWalletPass = params[0].get_str();
+ // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
+ // Alternately, find a way to make params[0] mlock()'d to begin with.
+ strWalletPass = params[0].get_str().c_str();
if (strWalletPass.length() > 0)
{
if (!pwalletMain->Unlock(strWalletPass))
- {
- 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(
if (!pwalletMain->IsCrypted())
throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
- string strOldWalletPass;
+ // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
+ // Alternately, find a way to make params[0] mlock()'d to begin with.
+ SecureString strOldWalletPass;
strOldWalletPass.reserve(100);
- mlock(&strOldWalletPass[0], strOldWalletPass.capacity());
- strOldWalletPass = params[0].get_str();
+ strOldWalletPass = params[0].get_str().c_str();
- string strNewWalletPass;
+ SecureString strNewWalletPass;
strNewWalletPass.reserve(100);
- mlock(&strNewWalletPass[0], strNewWalletPass.capacity());
- strNewWalletPass = params[1].get_str();
+ strNewWalletPass = params[1].get_str().c_str();
if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
throw runtime_error(
"Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
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;
}
if (pwalletMain->IsCrypted())
throw JSONRPCError(-15, "Error: running with an encrypted wallet, but encryptwallet was called.");
-#ifdef GUI
+#ifdef QT_GUI
// shutting down via RPC while the GUI is running does not work (yet):
throw runtime_error("Not Yet Implemented: use GUI to encrypt wallet, not RPC command");
#endif
- string strWalletPass;
+ // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
+ // Alternately, find a way to make params[0] mlock()'d to begin with.
+ SecureString strWalletPass;
strWalletPass.reserve(100);
- mlock(&strWalletPass[0], strWalletPass.capacity());
- strWalletPass = params[0].get_str();
+ strWalletPass = params[0].get_str().c_str();
if (strWalletPass.length() < 1)
throw runtime_error(
"Encrypts the wallet with <passphrase>.");
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());
// BDB seems to have a bad habit of writing old data into
// slack space in .dat files; that is bad if the old data is
throw runtime_error(
"getwork [data]\n"
"If [data] is not specified, returns formatted hash data to work on:\n"
- " \"midstate\" : precomputed hash state after hashing the first half of the data\n"
+ " \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated
" \"data\" : block data\n"
- " \"hash1\" : formatted hash buffer for second hash\n"
+ " \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated
" \"target\" : little endian hash target\n"
"If [data] is specified, tries to solve the block and returns true if it was successful.");
uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
Object result;
- result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate))));
+ result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated
result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata))));
- result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1))));
+ result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); // deprecated
result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget))));
return result;
}
// Byte reverse
for (int i = 0; i < 128/4; i++)
- ((unsigned int*)pdata)[i] = CryptoPP::ByteReverse(((unsigned int*)pdata)[i]);
+ ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);
// Get saved block
if (!mapNewBlock.count(pdata->hashMerkleRoot))
}
+Value getmemorypool(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() > 1)
+ throw runtime_error(
+ "getmemorypool [data]\n"
+ "If [data] is not specified, returns data needed to construct a block to work on:\n"
+ " \"version\" : block version\n"
+ " \"previousblockhash\" : hash of current highest block\n"
+ " \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n"
+ " \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n"
+ " \"time\" : timestamp appropriate for next block\n"
+ " \"bits\" : compressed target of next block\n"
+ "If [data] is specified, tries to solve the block and returns true if it was successful.");
+
+ if (params.size() == 0)
+ {
+ if (vNodes.empty())
+ throw JSONRPCError(-9, "Bitcoin is not connected!");
+
+ if (IsInitialBlockDownload())
+ throw JSONRPCError(-10, "Bitcoin is downloading blocks...");
+
+ static CReserveKey reservekey(pwalletMain);
+
+ // Update block
+ static unsigned int nTransactionsUpdatedLast;
+ static CBlockIndex* pindexPrev;
+ static int64 nStart;
+ static CBlock* pblock;
+ if (pindexPrev != pindexBest ||
+ (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5))
+ {
+ nTransactionsUpdatedLast = nTransactionsUpdated;
+ pindexPrev = pindexBest;
+ nStart = GetTime();
+
+ // Create new block
+ if(pblock)
+ delete pblock;
+ pblock = CreateNewBlock(reservekey);
+ if (!pblock)
+ throw JSONRPCError(-7, "Out of memory");
+ }
+
+ // Update nTime
+ pblock->UpdateTime(pindexPrev);
+ pblock->nNonce = 0;
+
+ Array transactions;
+ BOOST_FOREACH(CTransaction tx, pblock->vtx) {
+ if(tx.IsCoinBase())
+ continue;
+
+ CDataStream ssTx;
+ ssTx << tx;
+
+ transactions.push_back(HexStr(ssTx.begin(), ssTx.end()));
+ }
+
+ Object result;
+ result.push_back(Pair("version", pblock->nVersion));
+ result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
+ result.push_back(Pair("transactions", transactions));
+ result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
+ result.push_back(Pair("time", (int64_t)pblock->nTime));
+
+ union {
+ int32_t nBits;
+ char cBits[4];
+ } uBits;
+ uBits.nBits = htonl((int32_t)pblock->nBits);
+ result.push_back(Pair("bits", HexStr(BEGIN(uBits.cBits), END(uBits.cBits))));
+
+ return result;
+ }
+ else
+ {
+ // Parse parameters
+ CDataStream ssBlock(ParseHex(params[0].get_str()));
+ CBlock pblock;
+ ssBlock >> pblock;
+
+ return ProcessBlock(NULL, &pblock);
+ }
+}
+
+
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("sendmany", &sendmany),
make_pair("gettransaction", &gettransaction),
make_pair("listtransactions", &listtransactions),
+ make_pair("signmessage", &signmessage),
+ make_pair("verifymessage", &verifymessage),
make_pair("getwork", &getwork),
make_pair("listaccounts", &listaccounts),
make_pair("settxfee", &settxfee),
+ make_pair("getmemorypool", &getmemorypool),
+ make_pair("listsinceblock", &listsinceblock),
};
map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
"help",
"stop",
"getblockcount",
- "getblocknumber",
+ "getblocknumber", // deprecated
"getconnectioncount",
"getdifficulty",
"getgenerate",
"getinfo",
"getnewaddress",
"getaccountaddress",
- "setlabel", // deprecated
"getaccount",
- "getlabel", // deprecated
"getaddressesbyaccount",
- "getaddressesbylabel", // deprecated
"backupwallet",
"keypoolrefill",
"walletpassphrase",
"walletlock",
"validateaddress",
"getwork",
+ "getmemorypool",
};
set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
<< "Host: 127.0.0.1\r\n"
<< "Content-Type: application/json\r\n"
<< "Content-Length: " << strMsg.size() << "\r\n"
+ << "Connection: close\r\n"
<< "Accept: application/json\r\n";
BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
s << item.first << ": " << item.second << "\r\n";
"</HEAD>\r\n"
"<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
"</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
- string strStatus;
- if (nStatus == 200) strStatus = "OK";
- else if (nStatus == 400) strStatus = "Bad Request";
- else if (nStatus == 403) strStatus = "Forbidden";
- else if (nStatus == 404) strStatus = "Not Found";
- else if (nStatus == 500) strStatus = "Internal Server Error";
+ const char *cStatus;
+ if (nStatus == 200) cStatus = "OK";
+ else if (nStatus == 400) cStatus = "Bad Request";
+ else if (nStatus == 403) cStatus = "Forbidden";
+ else if (nStatus == 404) cStatus = "Not Found";
+ else if (nStatus == 500) cStatus = "Internal Server Error";
+ else cStatus = "";
return strprintf(
"HTTP/1.1 %d %s\r\n"
"Date: %s\r\n"
"\r\n"
"%s",
nStatus,
- strStatus.c_str(),
+ cStatus,
rfc1123Time().c_str(),
strMsg.size(),
FormatFullVersion().c_str(),
return nStatus;
}
-string EncodeBase64(string s)
-{
- BIO *b64, *bmem;
- BUF_MEM *bptr;
-
- b64 = BIO_new(BIO_f_base64());
- BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
- bmem = BIO_new(BIO_s_mem());
- b64 = BIO_push(b64, bmem);
- BIO_write(b64, s.c_str(), s.size());
- BIO_flush(b64);
- BIO_get_mem_ptr(b64, &bptr);
-
- string result(bptr->data, bptr->length);
- BIO_free_all(b64);
-
- return result;
-}
-
-string DecodeBase64(string s)
-{
- BIO *b64, *bmem;
-
- char* buffer = static_cast<char*>(calloc(s.size(), sizeof(char)));
-
- b64 = BIO_new(BIO_f_base64());
- BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
- bmem = BIO_new_mem_buf(const_cast<char*>(s.c_str()), s.size());
- bmem = BIO_push(b64, bmem);
- BIO_read(bmem, buffer, s.size());
- BIO_free_all(bmem);
-
- string result(buffer);
- free(buffer);
- return result;
-}
-
bool HTTPAuthorized(map<string, string>& mapHeaders)
{
string strAuth = mapHeaders["authorization"];
return false;
string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
string strUserPass = DecodeBase64(strUserPass64);
- string::size_type nColon = strUserPass.find(":");
- if (nColon == string::npos)
- return false;
- string strUser = strUserPass.substr(0, nColon);
- string strPassword = strUserPass.substr(nColon+1);
- return (strUser == mapArgs["-rpcuser"] && strPassword == mapArgs["-rpcpassword"]);
+ return strUserPass == strRPCUserColonPass;
}
//
printf("ThreadRPCServer exiting\n");
}
+#ifdef QT_GUI
+extern bool HACK_SHUTDOWN;
+#endif
+
void ThreadRPCServer2(void* parg)
{
printf("ThreadRPCServer started\n");
- if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
+ strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
+ if (strRPCUserColonPass == ":")
{
unsigned char rand_pwd[32];
RAND_bytes(rand_pwd, 32);
strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
else if (mapArgs.count("-daemon"))
strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
- PrintConsole(
- _("Warning: %s, you must set a rpcpassword in the configuration file:\n %s\n"
+ ThreadSafeMessageBox(strprintf(
+ _("Error: %s, you must set a rpcpassword in the configuration file:\n %s\n"
"It is recommended you use the following random password:\n"
"rpcuser=bitcoinrpc\n"
"rpcpassword=%s\n"
"If the file does not exist, create it with owner-readable-only file permissions.\n"),
strWhatAmI.c_str(),
GetConfigFile().c_str(),
- EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str());
+ EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()),
+ _("Error"), wxOK | wxMODAL);
+#ifndef QT_GUI
CreateThread(Shutdown, NULL);
+#endif
return;
}
asio::io_service io_service;
ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332));
+#ifndef QT_GUI
ip::tcp::acceptor acceptor(io_service, endpoint);
acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
+#else
+ ip::tcp::acceptor acceptor(io_service);
+ try
+ {
+ acceptor.open(endpoint.protocol());
+ acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
+ acceptor.bind(endpoint);
+ acceptor.listen(socket_base::max_connections);
+ }
+ catch(system::system_error &e)
+ {
+ HACK_SHUTDOWN = true;
+ ThreadSafeMessageBox(strprintf(_("An error occured while setting up the RPC port %i for listening: %s"), endpoint.port(), e.what()),
+ _("Error"), wxOK | wxMODAL);
+ return;
+ }
+#endif
#ifdef USE_SSL
ssl::context context(io_service, ssl::context::sslv23);
if (valMethod.type() != str_type)
throw JSONRPCError(-32600, "Method must be a string");
string strMethod = valMethod.get_str();
- if (strMethod != "getwork")
+ if (strMethod != "getwork" && strMethod != "getmemorypool")
printf("ThreadRPCServer method=%s\n", strMethod.c_str());
// Parse params
if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
- if (strMethod == "getamountreceived" && n > 1) ConvertTo<boost::int64_t>(params[1]); // deprecated
if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
- if (strMethod == "getreceivedbylabel" && n > 1) ConvertTo<boost::int64_t>(params[1]); // deprecated
- if (strMethod == "getallreceived" && n > 0) ConvertTo<boost::int64_t>(params[0]); // deprecated
- if (strMethod == "getallreceived" && n > 1) ConvertTo<bool>(params[1]); // deprecated
if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
- if (strMethod == "listreceivedbylabel" && n > 0) ConvertTo<boost::int64_t>(params[0]); // deprecated
- if (strMethod == "listreceivedbylabel" && n > 1) ConvertTo<bool>(params[1]); // deprecated
if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
+ if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "sendmany" && n > 1)
{
string s = params[1].get_str();
if (strPrint != "")
{
-#if defined(__WXMSW__) && defined(GUI)
- // Windows GUI apps can't print to command line,
- // so settle for a message box yuck
- MyMessageBox(strPrint, "Bitcoin", wxOK);
-#else
fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
-#endif
}
return nRet;
}
#include <vector>
#include <string>
#include "headers.h"
-#ifdef __WXMSW__
+#ifdef WIN32
#include <windows.h>
#endif
#include "main.h"
#include "util.h"
-bool CCrypter::SetKeyFromPassphrase(const std::string& strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod)
+bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod)
{
if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE)
return false;
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)
+ if (i != (int)WALLET_CRYPTO_KEY_SIZE)
{
memset(&chKey, 0, sizeof chKey);
memset(&chIV, 0, sizeof chIV);
// see www.keylength.com
// script supports up to 75 for single byte push
+// Generate a private key from just the secret parameter
int static inline EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key)
{
int ok = 0;
return(ok);
}
+// Perform ECDSA key recovery (see SEC1 4.1.6) for curves over (mod p)-fields
+// recid selects which key is recovered
+// if check is nonzero, additional checks are performed
+int static inline ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check)
+{
+ if (!eckey) return 0;
+
+ int ret = 0;
+ BN_CTX *ctx = NULL;
+
+ BIGNUM *x = NULL;
+ BIGNUM *e = NULL;
+ BIGNUM *order = NULL;
+ BIGNUM *sor = NULL;
+ BIGNUM *eor = NULL;
+ BIGNUM *field = NULL;
+ EC_POINT *R = NULL;
+ EC_POINT *O = NULL;
+ EC_POINT *Q = NULL;
+ BIGNUM *rr = NULL;
+ BIGNUM *zero = NULL;
+ int n = 0;
+ int i = recid / 2;
+
+ const EC_GROUP *group = EC_KEY_get0_group(eckey);
+ if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; }
+ BN_CTX_start(ctx);
+ order = BN_CTX_get(ctx);
+ if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; }
+ x = BN_CTX_get(ctx);
+ if (!BN_copy(x, order)) { ret=-1; goto err; }
+ if (!BN_mul_word(x, i)) { ret=-1; goto err; }
+ if (!BN_add(x, x, ecsig->r)) { ret=-1; goto err; }
+ field = BN_CTX_get(ctx);
+ if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; }
+ if (BN_cmp(x, field) >= 0) { ret=0; goto err; }
+ if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
+ if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; }
+ if (check)
+ {
+ if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
+ if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; }
+ if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; }
+ }
+ if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
+ n = EC_GROUP_get_degree(group);
+ e = BN_CTX_get(ctx);
+ if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; }
+ if (8*msglen > n) BN_rshift(e, e, 8-(n & 7));
+ zero = BN_CTX_get(ctx);
+ if (!BN_zero(zero)) { ret=-1; goto err; }
+ if (!BN_mod_sub(e, zero, e, order, ctx)) { ret=-1; goto err; }
+ rr = BN_CTX_get(ctx);
+ if (!BN_mod_inverse(rr, ecsig->r, order, ctx)) { ret=-1; goto err; }
+ sor = BN_CTX_get(ctx);
+ if (!BN_mod_mul(sor, ecsig->s, rr, order, ctx)) { ret=-1; goto err; }
+ eor = BN_CTX_get(ctx);
+ if (!BN_mod_mul(eor, e, rr, order, ctx)) { ret=-1; goto err; }
+ if (!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret=-2; goto err; }
+ if (!EC_KEY_set_public_key(eckey, Q)) { ret=-2; goto err; }
+
+ ret = 1;
+
+err:
+ if (ctx) {
+ BN_CTX_end(ctx);
+ BN_CTX_free(ctx);
+ }
+ if (R != NULL) EC_POINT_free(R);
+ if (O != NULL) EC_POINT_free(O);
+ if (Q != NULL) EC_POINT_free(Q);
+ return ret;
+}
class key_error : public std::runtime_error
{
// secure_allocator is defined in serialize.h
+// CPrivKey is a serialized private key, with all parameters included (279 bytes)
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CPrivKey;
+// CSecret is a serialization of just the secret parameter (32 bytes)
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CSecret;
class CKey
EC_KEY* pkey;
bool fSet;
+ void SetCompressedPubKey()
+ {
+ EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
+ }
+
public:
CKey()
{
CPrivKey GetPrivKey() const
{
- unsigned int nSize = i2d_ECPrivateKey(pkey, NULL);
+ int nSize = i2d_ECPrivateKey(pkey, NULL);
if (!nSize)
throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey failed");
CPrivKey vchPrivKey(nSize, 0);
std::vector<unsigned char> GetPubKey() const
{
- unsigned int nSize = i2o_ECPublicKey(pkey, NULL);
+ int nSize = i2o_ECPublicKey(pkey, NULL);
if (!nSize)
throw key_error("CKey::GetPubKey() : i2o_ECPublicKey failed");
std::vector<unsigned char> vchPubKey(nSize, 0);
return true;
}
+ // create a compact signature (65 bytes), which allows reconstructing the used public key
+ // The format is one header byte, followed by two times 32 bytes for the serialized r and s values.
+ // The header byte: 0x1B = first key with even y, 0x1C = first key with odd y,
+ // 0x1D = second key with even y, 0x1E = second key with odd y
+ bool SignCompact(uint256 hash, std::vector<unsigned char>& vchSig)
+ {
+ bool fOk = false;
+ ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey);
+ if (sig==NULL)
+ return false;
+ vchSig.clear();
+ vchSig.resize(65,0);
+ int nBitsR = BN_num_bits(sig->r);
+ int nBitsS = BN_num_bits(sig->s);
+ if (nBitsR <= 256 && nBitsS <= 256)
+ {
+ int nRecId = -1;
+ for (int i=0; i<4; i++)
+ {
+ CKey keyRec;
+ keyRec.fSet = true;
+ if (ECDSA_SIG_recover_key_GFp(keyRec.pkey, sig, (unsigned char*)&hash, sizeof(hash), i, 1) == 1)
+ if (keyRec.GetPubKey() == this->GetPubKey())
+ {
+ nRecId = i;
+ break;
+ }
+ }
+
+ if (nRecId == -1)
+ throw key_error("CKey::SignCompact() : unable to construct recoverable key");
+
+ vchSig[0] = nRecId+27;
+ BN_bn2bin(sig->r,&vchSig[33-(nBitsR+7)/8]);
+ BN_bn2bin(sig->s,&vchSig[65-(nBitsS+7)/8]);
+ fOk = true;
+ }
+ ECDSA_SIG_free(sig);
+ return fOk;
+ }
+
+ // reconstruct public key from a compact signature
+ // This is only slightly more CPU intensive than just verifying it.
+ // If this function succeeds, the recovered public key is guaranteed to be valid
+ // (the signature is a valid signature of the given data for that key)
+ bool SetCompactSignature(uint256 hash, const std::vector<unsigned char>& vchSig)
+ {
+ if (vchSig.size() != 65)
+ return false;
+ int nV = vchSig[0];
+ if (nV<27 || nV>=35)
+ return false;
+ ECDSA_SIG *sig = ECDSA_SIG_new();
+ BN_bin2bn(&vchSig[1],32,sig->r);
+ BN_bin2bn(&vchSig[33],32,sig->s);
+
+ EC_KEY_free(pkey);
+ pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
+ if (nV >= 31)
+ {
+ SetCompressedPubKey();
+ nV -= 4;
+ }
+ if (ECDSA_SIG_recover_key_GFp(pkey, sig, (unsigned char*)&hash, sizeof(hash), nV - 27, 0) == 1)
+ {
+ fSet = true;
+ ECDSA_SIG_free(sig);
+ return true;
+ }
+ return false;
+ }
+
bool Verify(uint256 hash, const std::vector<unsigned char>& vchSig)
{
// -1 = error, 0 = bad sig, 1 = good
return true;
}
+ // Verify a compact signature
+ bool VerifyCompact(uint256 hash, const std::vector<unsigned char>& vchSig)
+ {
+ CKey key;
+ if (!key.SetCompactSignature(hash, vchSig))
+ return false;
+ if (GetPubKey() != key.GetPubKey())
+ return false;
+ return true;
+ }
+
+ // Get the address corresponding to this key
CBitcoinAddress GetAddress() const
{
return CBitcoinAddress(GetPubKey());
#include "db.h"
#include "net.h"
#include "init.h"
-#include "cryptopp/sha.h"
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
map<uint256, CBlockIndex*> mapBlockIndex;
uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32);
-const int nInitialBlockThreshold = 120; // Regard blocks up until N-threshold as "initial download"
CBlockIndex* pindexGenesisBlock = NULL;
int nBestHeight = -1;
CBigNum bnBestChainWork = 0;
CBlockIndex* pindexBest = NULL;
int64 nTimeBestReceived = 0;
+CMedianFilter<int> cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have
+
map<uint256, CBlock*> mapOrphanBlocks;
multimap<uint256, CBlock*> mapOrphanBlocksByPrev;
#endif
-
-
-
-
-
//////////////////////////////////////////////////////////////////////////////
//
// dispatching functions
//
+// These functions dispatch to one or all registered wallets
+
+
void RegisterWallet(CWallet* pwalletIn)
{
CRITICAL_BLOCK(cs_setpwalletRegistered)
}
}
+// check whether the passed transaction is from us
bool static IsFromMe(CTransaction& tx)
{
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
return false;
}
+// get the wallet transaction with the given hash (if it exists)
bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx)
{
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
return false;
}
+// erases transaction with the given hash from all wallets
void static EraseFromWallets(uint256 hash)
{
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->EraseFromWallet(hash);
}
+// make sure all wallets know about the given transaction, in the given block
void static SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false)
{
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate);
}
+// notify wallets about a new best chain
void static SetBestChain(const CBlockLocator& loc)
{
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->SetBestChain(loc);
}
+// notify wallets about an updated transaction
void static UpdatedTransaction(const uint256& hashTx)
{
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->UpdatedTransaction(hashTx);
}
+// dump all wallets
void static PrintWallets(const CBlock& block)
{
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->PrintWallet(block);
}
+// notify wallets about an incoming inventory (for request counts)
void static Inventory(const uint256& hash)
{
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->Inventory(hash);
}
+// ask wallets to resend their transactions
void static ResendWalletTransactions()
{
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
{
// Basic checks that don't depend on any context
if (vin.empty())
- return error("CTransaction::CheckTransaction() : vin empty");
+ return DoS(10, error("CTransaction::CheckTransaction() : vin empty"));
if (vout.empty())
- return error("CTransaction::CheckTransaction() : vout empty");
+ return DoS(10, error("CTransaction::CheckTransaction() : vout empty"));
// Size limits
if (::GetSerializeSize(*this, SER_NETWORK) > MAX_BLOCK_SIZE)
- return error("CTransaction::CheckTransaction() : size limits failed");
+ return DoS(100, error("CTransaction::CheckTransaction() : size limits failed"));
// Check for negative or overflow output values
int64 nValueOut = 0;
BOOST_FOREACH(const CTxOut& txout, vout)
{
if (txout.nValue < 0)
- return error("CTransaction::CheckTransaction() : txout.nValue negative");
+ return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative"));
if (txout.nValue > MAX_MONEY)
- return error("CTransaction::CheckTransaction() : txout.nValue too high");
+ return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high"));
nValueOut += txout.nValue;
if (!MoneyRange(nValueOut))
- return error("CTransaction::CheckTransaction() : txout total out of range");
+ return DoS(100, error("CTransaction::CheckTransaction() : txout total out of range"));
}
// Check for duplicate inputs
if (IsCoinBase())
{
if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100)
- return error("CTransaction::CheckTransaction() : coinbase script size");
+ return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size"));
}
else
{
BOOST_FOREACH(const CTxIn& txin, vin)
if (txin.prevout.IsNull())
- return error("CTransaction::CheckTransaction() : prevout is null");
+ return DoS(10, error("CTransaction::CheckTransaction() : prevout is null"));
}
return true;
// Coinbase is only valid in a block, not as a loose transaction
if (IsCoinBase())
- return error("AcceptToMemoryPool() : coinbase as individual tx");
+ return DoS(100, error("AcceptToMemoryPool() : coinbase as individual tx"));
// To help v0.1.5 clients who would see it as a negative number
if ((int64)nLockTime > INT_MAX)
// Check for conflicts with in-memory transactions
CTransaction* ptxOld = NULL;
- for (int i = 0; i < vin.size(); i++)
+ for (unsigned int i = 0; i < vin.size(); i++)
{
COutPoint outpoint = vin[i].prevout;
if (mapNextTx.count(outpoint))
return false;
if (!IsNewerThan(*ptxOld))
return false;
- for (int i = 0; i < vin.size(); i++)
+ for (unsigned int i = 0; i < vin.size(); i++)
{
COutPoint outpoint = vin[i].prevout;
if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld)
// 34 bytes because a TxOut is:
// 20-byte address + 8 byte bitcoin amount + 5 bytes of ops + 1 byte script length
if (GetSigOpCount() > nSize / 34 || nSize < 100)
- return error("AcceptToMemoryPool() : nonstandard transaction");
+ return error("AcceptToMemoryPool() : transaction with out-of-bounds SigOpCount");
int64 nFees = GetValueIn(mapInputs)-GetValueOut();
{
uint256 hash = GetHash();
mapTransactions[hash] = *this;
- for (int i = 0; i < vin.size(); i++)
+ for (unsigned int i = 0; i < vin.size(); i++)
mapNextTx[vin[i].prevout] = CInPoint(&mapTransactions[hash], i);
nTransactionsUpdated++;
}
return true;
}
+// Return maximum amount of blocks that other nodes claim to have
+int GetNumBlocksOfPeers()
+{
+ return std::max(cPeerBlockCounts.median(), Checkpoints::GetTotalBlocksEstimate());
+}
+
bool IsInitialBlockDownload()
{
- if (pindexBest == NULL || nBestHeight < (Checkpoints::GetTotalBlocksEstimate()-nInitialBlockThreshold))
+ if (pindexBest == NULL || nBestHeight < Checkpoints::GetTotalBlocksEstimate())
return true;
static int64 nLastUpdate;
static CBlockIndex* pindexLastBest;
if (IsCoinBase())
return true; // Coinbase transactions have no inputs to fetch.
- for (int i = 0; i < vin.size(); i++)
+ for (unsigned int i = 0; i < vin.size(); i++)
{
COutPoint prevout = vin[i].prevout;
if (inputsRet.count(prevout.hash))
}
// Make sure all prevout.n's are valid:
- for (int i = 0; i < vin.size(); i++)
+ for (unsigned int i = 0; i < vin.size(); i++)
{
const COutPoint prevout = vin[i].prevout;
assert(inputsRet.count(prevout.hash) != 0);
// Revisit this if/when transaction replacement is implemented and allows
// adding inputs:
fInvalid = true;
- return error("FetchInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str());
+ return DoS(100, error("FetchInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()));
}
}
return 0;
int64 nResult = 0;
- for (int i = 0; i < vin.size(); i++)
+ for (unsigned int i = 0; i < vin.size(); i++)
{
nResult += GetOutputFor(vin[i], inputs).nValue;
}
return 0;
int nSigOps = 0;
- for (int i = 0; i < vin.size(); i++)
+ for (unsigned int i = 0; i < vin.size(); i++)
{
const CTxOut& prevout = GetOutputFor(vin[i], inputs);
if (prevout.scriptPubKey.IsPayToScriptHash())
{
int64 nValueIn = 0;
int64 nFees = 0;
- for (int i = 0; i < vin.size(); i++)
+ for (unsigned int i = 0; i < vin.size(); i++)
{
COutPoint prevout = vin[i].prevout;
assert(inputs.count(prevout.hash) > 0);
CTransaction& txPrev = inputs[prevout.hash].second;
if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size())
- return error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str());
+ return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()));
// If prev is coinbase, check that it's matured
if (txPrev.IsCoinBase())
return error("ConnectInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - pindex->nHeight);
// Check for conflicts (double-spend)
+ // This doesn't trigger the DoS code on purpose; if it did, it would make it easier
+ // for an attacker to attempt to split the network.
if (!txindex.vSpent[prevout.n].IsNull())
return fMiner ? false : error("ConnectInputs() : %s prev tx already used at %s", GetHash().ToString().substr(0,10).c_str(), txindex.vSpent[prevout.n].ToString().c_str());
// Check for negative or overflow input values
nValueIn += txPrev.vout[prevout.n].nValue;
if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
- return error("ConnectInputs() : txin values out of range");
+ return DoS(100, error("ConnectInputs() : txin values out of range"));
- // Verify signature
- if (!VerifySignature(txPrev, *this, i, fStrictPayToScriptHash, 0))
+ // Skip ECDSA signature verification when connecting blocks (fBlock=true)
+ // before the last blockchain checkpoint. This is safe because block merkle hashes are
+ // still computed and checked, and any change will be caught at the next checkpoint.
+ if (!(fBlock && (nBestHeight < Checkpoints::GetTotalBlocksEstimate())))
{
- // only during transition phase for P2SH: do not invoke (external)
- // anti-DoS code for potentially old clients relaying bad P2SH
- // transactions
- if (fStrictPayToScriptHash && VerifySignature(txPrev, *this, i, false, 0))
- return error("ConnectInputs() : %s P2SH VerifySignature failed", GetHash().ToString().substr(0,10).c_str());
+ // Verify signature
+ if (!VerifySignature(txPrev, *this, i, fStrictPayToScriptHash, 0))
+ {
+ // only during transition phase for P2SH: do not invoke anti-DoS code for
+ // potentially old clients relaying bad P2SH transactions
+ if (fStrictPayToScriptHash && VerifySignature(txPrev, *this, i, false, 0))
+ return error("ConnectInputs() : %s P2SH VerifySignature failed", GetHash().ToString().substr(0,10).c_str());
- return error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str());
+ return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
+ }
}
// Mark outpoints as spent
}
if (nValueIn < GetValueOut())
- return error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str());
+ return DoS(100, error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str()));
// Tally transaction fees
int64 nTxFee = nValueIn - GetValueOut();
if (nTxFee < 0)
- return error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str());
+ return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()));
nFees += nTxFee;
if (!MoneyRange(nFees))
- return error("ConnectInputs() : nFees out of range");
+ return DoS(100, error("ConnectInputs() : nFees out of range"));
}
return true;
CRITICAL_BLOCK(cs_mapTransactions)
{
int64 nValueIn = 0;
- for (int i = 0; i < vin.size(); i++)
+ for (unsigned int i = 0; i < vin.size(); i++)
{
// Get prev tx from single transactions in memory
COutPoint prevout = vin[i].prevout;
{
nSigOps += tx.GetSigOpCount();
if (nSigOps > MAX_BLOCK_SIGOPS)
- return error("ConnectBlock() : too many sigops");
+ return DoS(100, error("ConnectBlock() : too many sigops"));
CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos);
nTxPos += ::GetSerializeSize(tx, SER_DISK);
// an incredibly-expensive-to-validate block.
nSigOps += tx.GetP2SHSigOpCount(mapInputs);
if (nSigOps > MAX_BLOCK_SIGOPS)
- return error("ConnectBlock() : too many sigops");
+ return DoS(100, error("ConnectBlock() : too many sigops"));
}
nFees += tx.GetValueIn(mapInputs)-tx.GetValueOut();
// Connect longer branch
vector<CTransaction> vDelete;
- for (int i = 0; i < vConnect.size(); i++)
+ for (unsigned int i = 0; i < vConnect.size(); i++)
{
CBlockIndex* pindex = vConnect[i];
CBlock block;
// Size limits
if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK) > MAX_BLOCK_SIZE)
- return error("CheckBlock() : size limits failed");
+ return DoS(100, error("CheckBlock() : size limits failed"));
// Check proof of work matches claimed amount
if (!CheckProofOfWork(GetHash(), nBits))
- return error("CheckBlock() : proof of work failed");
+ return DoS(50, error("CheckBlock() : proof of work failed"));
// Check timestamp
if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60)
// First transaction must be coinbase, the rest must not be
if (vtx.empty() || !vtx[0].IsCoinBase())
- return error("CheckBlock() : first tx is not coinbase");
+ return DoS(100, error("CheckBlock() : first tx is not coinbase"));
- for (int i = 1; i < vtx.size(); i++)
+ for (unsigned int i = 1; i < vtx.size(); i++)
if (vtx[i].IsCoinBase())
- return error("CheckBlock() : more than one coinbase");
+ return DoS(100, error("CheckBlock() : more than one coinbase"));
// Check transactions
BOOST_FOREACH(const CTransaction& tx, vtx)
if (!tx.CheckTransaction())
- return error("CheckBlock() : CheckTransaction failed");
+ return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed"));
// Check that it's not full of nonstandard transactions
if (GetSigOpCount() > MAX_BLOCK_SIGOPS)
- return error("CheckBlock() : out-of-bounds SigOpCount");
+ return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"));
// Check merkleroot
if (hashMerkleRoot != BuildMerkleTree())
- return error("CheckBlock() : hashMerkleRoot mismatch");
+ return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));
return true;
}
// Get prev block index
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashPrevBlock);
if (mi == mapBlockIndex.end())
- return error("AcceptBlock() : prev block not found");
+ return DoS(10, error("AcceptBlock() : prev block not found"));
CBlockIndex* pindexPrev = (*mi).second;
int nHeight = pindexPrev->nHeight+1;
// Check proof of work
if (nBits != GetNextWorkRequired(pindexPrev, this))
- return error("AcceptBlock() : incorrect proof of work");
+ return DoS(100, error("AcceptBlock() : incorrect proof of work"));
// Check timestamp against prev
if (GetBlockTime() <= pindexPrev->GetMedianTimePast())
// Check that all transactions are finalized
BOOST_FOREACH(const CTransaction& tx, vtx)
if (!tx.IsFinal(nHeight, GetBlockTime()))
- return error("AcceptBlock() : contains a non-final transaction");
+ return DoS(10, error("AcceptBlock() : contains a non-final transaction"));
// Check that the block chain matches the known block chain up to a checkpoint
if (!Checkpoints::CheckBlock(nHeight, hash))
- return error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight);
+ return DoS(100, error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight));
// Write block to history file
if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK)))
return true;
}
-bool static ProcessBlock(CNode* pfrom, CBlock* pblock)
+bool ProcessBlock(CNode* pfrom, CBlock* pblock)
{
// Check for duplicate
uint256 hash = pblock->GetHash();
int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
if (deltaTime < 0)
{
+ if (pfrom)
+ pfrom->Misbehaving(100);
return error("ProcessBlock() : block with timestamp before last checkpoint");
}
CBigNum bnNewBlock;
bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime));
if (bnNewBlock > bnRequired)
{
+ if (pfrom)
+ pfrom->Misbehaving(100);
return error("ProcessBlock() : block with too little proof-of-work");
}
}
// Recursively process any orphan blocks that depended on this one
vector<uint256> vWorkQueue;
vWorkQueue.push_back(hash);
- for (int i = 0; i < vWorkQueue.size(); i++)
+ for (unsigned int i = 0; i < vWorkQueue.size(); i++)
{
uint256 hashPrev = vWorkQueue[i];
for (multimap<uint256, CBlock*>::iterator mi = mapOrphanBlocksByPrev.lower_bound(hashPrev);
// put the main timechain first
vector<CBlockIndex*>& vNext = mapNext[pindex];
- for (int i = 0; i < vNext.size(); i++)
+ for (unsigned int i = 0; i < vNext.size(); i++)
{
if (vNext[i]->pnext)
{
}
// iterate children
- for (int i = 0; i < vNext.size(); i++)
+ for (unsigned int i = 0; i < vNext.size(); i++)
vStack.push_back(make_pair(nCol+i, vNext[i]));
}
}
{
switch (inv.type)
{
- case MSG_TX: return mapTransactions.count(inv.hash) || mapOrphanTransactions.count(inv.hash) || txdb.ContainsTx(inv.hash);
+ case MSG_TX:
+ {
+ bool txInMap = false;
+ CRITICAL_BLOCK(cs_mapTransactions)
+ {
+ txInMap = (mapTransactions.count(inv.hash) != 0);
+ }
+ return txInMap ||
+ 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
{
// Each connection can only send one version message
if (pfrom->nVersion != 0)
+ {
+ pfrom->Misbehaving(1);
return false;
+ }
int64 nTime;
CAddress addrMe;
pfrom->fSuccessfullyConnected = true;
printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight);
+
+ cPeerBlockCounts.input(pfrom->nStartingHeight);
}
else if (pfrom->nVersion == 0)
{
// Must have a version message before anything else
+ pfrom->Misbehaving(1);
return false;
}
if (pfrom->nVersion < 31402 && mapAddresses.size() > 1000)
return true;
if (vAddr.size() > 1000)
+ {
+ pfrom->Misbehaving(20);
return error("message addr size() = %d", vAddr.size());
+ }
// Store the new addresses
CAddrDB addrDB;
vector<CInv> vInv;
vRecv >> vInv;
if (vInv.size() > 50000)
+ {
+ pfrom->Misbehaving(20);
return error("message inv size() = %d", vInv.size());
+ }
CTxDB txdb("r");
BOOST_FOREACH(const CInv& inv, vInv)
vector<CInv> vInv;
vRecv >> vInv;
if (vInv.size() > 50000)
+ {
+ pfrom->Misbehaving(20);
return error("message getdata size() = %d", vInv.size());
+ }
BOOST_FOREACH(const CInv& inv, vInv)
{
vWorkQueue.push_back(inv.hash);
// Recursively process any orphan transactions that depended on this one
- for (int i = 0; i < vWorkQueue.size(); i++)
+ for (unsigned int i = 0; i < vWorkQueue.size(); i++)
{
uint256 hashPrev = vWorkQueue[i];
for (multimap<uint256, CDataStream*>::iterator mi = mapOrphanTransactionsByPrev.lower_bound(hashPrev);
if (nEvicted > 0)
printf("mapOrphan overflow, removed %d tx\n", nEvicted);
}
+ if (tx.nDoS) pfrom->Misbehaving(tx.nDoS);
}
if (ProcessBlock(pfrom, &block))
mapAlreadyAskedFor.erase(inv);
+ if (block.nDoS) pfrom->Misbehaving(block.nDoS);
}
bool SendMessages(CNode* pto, bool fSendTrickle)
{
- CRITICAL_BLOCK(cs_main)
+ TRY_CRITICAL_BLOCK(cs_main)
{
// Don't send anything until we get their version message
if (pto->nVersion == 0)
return blocks;
}
static const unsigned int pSHA256InitState[8] =
{0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19};
-inline void SHA256Transform(void* pstate, void* pinput, const void* pinit)
+void SHA256Transform(void* pstate, void* pinput, const void* pinit)
{
- memcpy(pstate, pinit, 32);
- CryptoPP::SHA256::Transform((CryptoPP::word32*)pstate, (CryptoPP::word32*)pinput);
+ SHA256_CTX ctx;
+ unsigned char data[64];
+
+ SHA256_Init(&ctx);
+
+ for (int i = 0; i < 16; i++)
+ ((uint32_t*)data)[i] = ByteReverse(((uint32_t*)pinput)[i]);
+
+ for (int i = 0; i < 8; i++)
+ ctx.h[i] = ((uint32_t*)pinit)[i];
+
+ SHA256_Update(&ctx, data, sizeof(data));
+ for (int i = 0; i < 8; i++)
+ ((uint32_t*)pstate)[i] = ctx.h[i];
}
//
FormatHashBlocks(&tmp.hash1, sizeof(tmp.hash1));
// Byte swap all the input buffer
- for (int i = 0; i < sizeof(tmp)/4; i++)
+ for (unsigned int i = 0; i < sizeof(tmp)/4; i++)
((unsigned int*)&tmp)[i] = ByteReverse(((unsigned int*)&tmp)[i]);
// Precalc the first half of the first hash, which stays constant
return error("BitcoinMiner : ProcessBlock, block not accepted");
}
- Sleep(2000);
return true;
}
// Check if something found
if (nNonceFound != -1)
{
- for (int i = 0; i < sizeof(hash)/4; i++)
+ for (unsigned int i = 0; i < sizeof(hash)/4; i++)
((unsigned int*)&hash)[i] = ByteReverse(((unsigned int*)&hash)[i]);
if (hash <= hashTarget)
void RegisterWallet(CWallet* pwalletIn);
void UnregisterWallet(CWallet* pwalletIn);
+bool ProcessBlock(CNode* pfrom, CBlock* pblock);
bool CheckDiskSpace(uint64 nAdditionalBytes=0);
FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb");
FILE* AppendBlockFile(unsigned int& nFileRet);
bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey);
bool CheckProofOfWork(uint256 hash, unsigned int nBits);
unsigned int ComputeMinWork(unsigned int nBase, int64 nTime);
+int GetNumBlocksOfPeers();
bool IsInitialBlockDownload();
std::string GetWarnings(std::string strFor);
std::vector<CTxOut> vout;
unsigned int nLockTime;
+ // Denial-of-service detection:
+ mutable int nDoS;
+ bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; }
CTransaction()
{
vin.clear();
vout.clear();
nLockTime = 0;
+ nDoS = 0; // Denial-of-service prevention
}
bool IsNull() const
{
if (vin.size() != old.vin.size())
return false;
- for (int i = 0; i < vin.size(); i++)
+ for (unsigned int i = 0; i < vin.size(); i++)
if (vin[i].prevout != old.vin[i].prevout)
return false;
bool fNewer = false;
unsigned int nLowest = UINT_MAX;
- for (int i = 0; i < vin.size(); i++)
+ for (unsigned int i = 0; i < vin.size(); i++)
{
if (vin[i].nSequence != old.vin[i].nSequence)
{
vin.size(),
vout.size(),
nLockTime);
- for (int i = 0; i < vin.size(); i++)
+ for (unsigned int i = 0; i < vin.size(); i++)
str += " " + vin[i].ToString() + "\n";
- for (int i = 0; i < vout.size(); i++)
+ for (unsigned int i = 0; i < vout.size(); i++)
str += " " + vout[i].ToString() + "\n";
return str;
}
// memory only
mutable std::vector<uint256> vMerkleTree;
+ // Denial-of-service detection:
+ mutable int nDoS;
+ bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; }
CBlock()
{
nNonce = 0;
vtx.clear();
vMerkleTree.clear();
+ nDoS = 0;
}
bool IsNull() const
fflush(fileout);
if (!IsInitialBlockDownload() || (nBestHeight+1) % 500 == 0)
{
-#ifdef __WXMSW__
+#ifdef WIN32
_commit(_fileno(fileout));
#else
fsync(fileno(fileout));
hashMerkleRoot.ToString().substr(0,10).c_str(),
nTime, nBits, nNonce,
vtx.size());
- for (int i = 0; i < vtx.size(); i++)
+ for (unsigned int i = 0; i < vtx.size(); i++)
{
printf(" ");
vtx[i].print();
}
printf(" vMerkleTree: ");
- for (int i = 0; i < vMerkleTree.size(); i++)
+ for (unsigned int i = 0; i < vMerkleTree.size(); i++)
printf("%s ", vMerkleTree[i].ToString().substr(0,10).c_str());
printf("\n");
}
#include "init.h"
#include "strlcpy.h"
-#ifdef __WXMSW__
+#ifdef WIN32
#include <string.h>
#endif
bool fProxy = (fUseProxy && addrConnect.IsRoutable());
struct sockaddr_in sockaddr = (fProxy ? addrProxy.GetSockAddr() : addrConnect.GetSockAddr());
-#ifdef __WXMSW__
+#ifdef WIN32
u_long fNonblock = 1;
if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR)
#else
return false;
}
socklen_t nRetSize = sizeof(nRet);
-#ifdef __WXMSW__
+#ifdef WIN32
if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, (char*)(&nRet), &nRetSize) == SOCKET_ERROR)
#else
if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, &nRet, &nRetSize) == SOCKET_ERROR)
return false;
}
}
-#ifdef __WXMSW__
+#ifdef WIN32
else if (WSAGetLastError() != WSAEISCONN)
#else
else
CNode::ConnectNode immediately turns the socket back to non-blocking
but we'll turn it back to blocking just in case
*/
-#ifdef __WXMSW__
+#ifdef WIN32
fNonblock = 0;
if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR)
#else
}
if (pszKeyword == NULL)
break;
- if (strLine.find(pszKeyword) != -1)
+ if (strLine.find(pszKeyword) != string::npos)
{
strLine = strLine.substr(strLine.find(pszKeyword) + strlen(pszKeyword));
break;
}
}
closesocket(hSocket);
- if (strLine.find("<") != -1)
+ if (strLine.find("<") != string::npos)
strLine = strLine.substr(0, strLine.find("<"));
strLine = strLine.substr(strspn(strLine.c_str(), " \t\n\r"));
while (strLine.size() > 0 && isspace(strLine[strLine.size()-1]))
printf("connected %s\n", addrConnect.ToString().c_str());
// Set to nonblocking
-#ifdef __WXMSW__
+#ifdef WIN32
u_long nOne = 1;
if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR)
printf("ConnectSocket() : ioctlsocket nonblocking setting failed, error %d\n", WSAGetLastError());
}
+std::map<unsigned int, int64> CNode::setBanned;
+CCriticalSection CNode::cs_setBanned;
+
+void CNode::ClearBanned()
+{
+ setBanned.clear();
+}
+
+bool CNode::IsBanned(unsigned int ip)
+{
+ bool fResult = false;
+ CRITICAL_BLOCK(cs_setBanned)
+ {
+ std::map<unsigned int, int64>::iterator i = setBanned.find(ip);
+ if (i != setBanned.end())
+ {
+ int64 t = (*i).second;
+ if (GetTime() < t)
+ fResult = true;
+ }
+ }
+ return fResult;
+}
+
+bool CNode::Misbehaving(int howmuch)
+{
+ if (addr.IsLocal())
+ {
+ printf("Warning: local node %s misbehaving\n", addr.ToString().c_str());
+ return false;
+ }
+
+ nMisbehavior += howmuch;
+ if (nMisbehavior >= GetArg("-banscore", 100))
+ {
+ int64 banTime = GetTime()+GetArg("-bantime", 60*60*24); // Default 24-hour ban
+ CRITICAL_BLOCK(cs_setBanned)
+ if (setBanned[addr.ip] < banTime)
+ setBanned[addr.ip] = banTime;
+ CloseSocketDisconnect();
+ printf("Disconnected %s for misbehavior (score=%d)\n", addr.ToString().c_str(), nMisbehavior);
+ return true;
+ }
+ return false;
+}
+
if (hSocketMax > -1)
{
printf("socket select error %d\n", nErr);
- for (int i = 0; i <= hSocketMax; i++)
+ for (unsigned int i = 0; i <= hSocketMax; i++)
FD_SET(i, &fdsetRecv);
}
FD_ZERO(&fdsetSend);
{
closesocket(hSocket);
}
+ else if (CNode::IsBanned(addr.ip))
+ {
+ printf("connection from %s dropped (banned)\n", addr.ToString().c_str());
+ closesocket(hSocket);
+ }
else
{
printf("accepted connection %s\n", addr.ToString().c_str());
{
printf("Loading addresses from DNS seeds (could take a while)\n");
- for (int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) {
+ for (unsigned int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) {
vector<CAddress> vaddr;
if (Lookup(strDNSSeed[seed_idx], vaddr, NODE_NETWORK, -1, true))
{
if (fAddSeeds)
{
- for (int i = 0; i < ARRAYLEN(pnSeed); i++)
+ for (unsigned int i = 0; i < ARRAYLEN(pnSeed); i++)
{
// It'll only connect to one or two seed nodes because once it connects,
// it'll get a pile of addresses with newer timestamps.
BOOST_FOREACH(CNode* pnode, vNodes)
setConnected.insert(pnode->addr.ip & 0x0000ffff);
+ int64 nANow = GetAdjustedTime();
+
CRITICAL_BLOCK(cs_mapAddresses)
{
BOOST_FOREACH(const PAIRTYPE(vector<unsigned char>, CAddress)& item, mapAddresses)
const CAddress& addr = item.second;
if (!addr.IsIPv4() || !addr.IsValid() || setConnected.count(addr.ip & 0x0000ffff))
continue;
- int64 nSinceLastSeen = GetAdjustedTime() - addr.nTime;
- int64 nSinceLastTry = GetAdjustedTime() - addr.nLastTry;
+ int64 nSinceLastSeen = nANow - addr.nTime;
+ int64 nSinceLastTry = nANow - addr.nLastTry;
// Randomize the order in a deterministic way, putting the standard port first
int64 nRandomizer = (uint64)(nStart * 4951 + addr.nLastTry * 9567851 + addr.ip * 7789) % (2 * 60 * 60);
//
if (fShutdown)
return false;
- if (addrConnect.ip == addrLocalHost.ip || !addrConnect.IsIPv4() || FindNode(addrConnect.ip))
+ if (addrConnect.ip == addrLocalHost.ip || !addrConnect.IsIPv4() ||
+ FindNode(addrConnect.ip) || CNode::IsBanned(addrConnect.ip))
return false;
vnThreadsRunning[1]--;
int nOne = 1;
addrLocalHost.port = htons(GetListenPort());
-#ifdef __WXMSW__
+#ifdef WIN32
// Initialize Windows Sockets
WSADATA wsadata;
int ret = WSAStartup(MAKEWORD(2,2), &wsadata);
setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int));
#endif
-#ifndef __WXMSW__
+#ifndef WIN32
// Allow binding if the port is still in TIME_WAIT state after
// the program was closed and restarted. Not an issue on windows.
setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int));
#endif
-#ifdef __WXMSW__
+#ifdef WIN32
// Set to nonblocking, incoming connections will also inherit this
if (ioctlsocket(hListenSocket, FIONBIO, (u_long*)&nOne) == SOCKET_ERROR)
#else
if (pnodeLocalHost == NULL)
pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress("127.0.0.1", 0, false, nLocalServices));
-#ifdef __WXMSW__
+#ifdef WIN32
// Get local host ip
char pszHostName[1000] = "";
if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR)
if (closesocket(hListenSocket) == SOCKET_ERROR)
printf("closesocket(hListenSocket) failed with error %d\n", WSAGetLastError());
-#ifdef __WXMSW__
+#ifdef WIN32
// Shutdown Windows Sockets
WSACleanup();
#endif
#include <boost/foreach.hpp>
#include <openssl/rand.h>
-#ifndef __WXMSW__
+#ifndef WIN32
#include <arpa/inet.h>
#endif
int64 nLastRecv;
int64 nLastSendEmpty;
int64 nTimeConnected;
- unsigned int nHeaderStart;
+ signed int nHeaderStart;
unsigned int nMessageStart;
CAddress addr;
int nVersion;
bool fDisconnect;
protected:
int nRefCount;
+
+ // Denial-of-service detection/prevention
+ // Key is ip address, value is banned-until-time
+ static std::map<unsigned int, int64> setBanned;
+ static CCriticalSection cs_setBanned;
+ int nMisbehavior;
+
public:
int64 nReleaseTime;
std::map<uint256, CRequestTracker> mapRequests;
// publish and subscription
std::vector<char> vfSubscribe;
-
CNode(SOCKET hSocketIn, CAddress addrIn, bool fInboundIn=false)
{
nServices = 0;
nStartingHeight = -1;
fGetAddr = false;
vfSubscribe.assign(256, false);
+ nMisbehavior = 0;
// Be shy and don't send version until we hear
if (!fInbound)
void CancelSubscribe(unsigned int nChannel);
void CloseSocketDisconnect();
void Cleanup();
+
+
+ // Denial-of-service detection/prevention
+ // The idea is to detect peers that are behaving
+ // badly and disconnect/ban them, but do it in a
+ // one-coding-mistake-won't-shatter-the-entire-network
+ // way.
+ // IMPORTANT: There should be nothing I can give a
+ // node that it will forward on that will make that
+ // node's peers drop it. If there is, an attacker
+ // can isolate a node and/or try to split the network.
+ // Dropping a node for sending stuff that is invalid
+ // now but might be valid in a later version is also
+ // dangerous, because it can cause a network split
+ // between nodes running old code and nodes running
+ // new code.
+ static void ClearBanned(); // needed for unit testing
+ static bool IsBanned(unsigned int ip);
+ bool Misbehaving(int howmuch); // 1 == a little, 100 == a lot
};
#include "protocol.h"
#include "util.h"
-#ifndef __WXMSW__
+#ifndef WIN32
# include <arpa/inet.h>
#endif
CInv::CInv(const std::string& strType, const uint256& hashIn)
{
- int i;
+ unsigned int i;
for (i = 1; i < ARRAYLEN(ppszTypeName); i++)
{
if (strType == ppszTypeName[i])
ppmutexOpenSSL[i] = new boost::interprocess::interprocess_mutex();
CRYPTO_set_locking_callback(locking_callback);
-#ifdef __WXMSW__
+#ifdef WIN32
// Seed random number generator with screen scrape and other hardware sources
RAND_screen();
#endif
return;
nLastPerfmon = GetTime();
-#ifdef __WXMSW__
+#ifdef WIN32
// Don't need this on Linux, OpenSSL automatically uses /dev/urandom
// Seed with the entire set of perfmon data
unsigned char pdata[250000];
}
}
-#ifdef __WXMSW__
+#ifdef WIN32
if (fPrintToDebugger)
{
static CCriticalSection cs_OutputDebugStringF;
return ret;
}
-
-string strprintf(const char* format, ...)
+string strprintf(const std::string &format, ...)
{
char buffer[50000];
char* p = buffer;
{
va_list arg_ptr;
va_start(arg_ptr, format);
- ret = _vsnprintf(p, limit, format, arg_ptr);
+ ret = _vsnprintf(p, limit, format.c_str(), arg_ptr);
va_end(arg_ptr);
if (ret >= 0 && ret < limit)
break;
return str;
}
-
-bool error(const char* format, ...)
+bool error(const std::string &format, ...)
{
char buffer[50000];
int limit = sizeof(buffer);
va_list arg_ptr;
va_start(arg_ptr, format);
- int ret = _vsnprintf(buffer, limit, format, arg_ptr);
+ int ret = _vsnprintf(buffer, limit, format.c_str(), arg_ptr);
va_end(arg_ptr);
if (ret < 0 || ret >= limit)
{
vector<unsigned char> ParseHex(const char* psz)
{
- static char phexdigit[256] =
+ static signed char phexdigit[256] =
{ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1,
-1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
- -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
{
while (isspace(*psz))
psz++;
- char c = phexdigit[(unsigned char)*psz++];
- if (c == (char)-1)
+ signed char c = phexdigit[(unsigned char)*psz++];
+ if (c == (signed char)-1)
break;
unsigned char n = (c << 4);
c = phexdigit[(unsigned char)*psz++];
- if (c == (char)-1)
+ if (c == (signed char)-1)
break;
n |= c;
vch.push_back(n);
return ParseHex(str.c_str());
}
-
void ParseParameters(int argc, char* argv[])
{
mapArgs.clear();
pszValue = strchr(psz, '=');
*pszValue++ = '\0';
}
- #ifdef __WXMSW__
+ #ifdef WIN32
_strlwr(psz);
if (psz[0] == '/')
psz[0] = '-';
}
+string EncodeBase64(const unsigned char* pch, size_t len)
+{
+ static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ string strRet="";
+ strRet.reserve((len+2)/3*4);
+
+ int mode=0, left=0;
+ const unsigned char *pchEnd = pch+len;
-const char* wxGetTranslation(const char* pszEnglish)
+ while (pch<pchEnd)
+ {
+ int enc = *(pch++);
+ switch (mode)
+ {
+ case 0: // we have no bits
+ strRet += pbase64[enc >> 2];
+ left = (enc & 3) << 4;
+ mode = 1;
+ break;
+
+ case 1: // we have two bits
+ strRet += pbase64[left | (enc >> 4)];
+ left = (enc & 15) << 2;
+ mode = 2;
+ break;
+
+ case 2: // we have four bits
+ strRet += pbase64[left | (enc >> 6)];
+ strRet += pbase64[enc & 63];
+ mode = 0;
+ break;
+ }
+ }
+
+ if (mode)
+ {
+ strRet += pbase64[left];
+ strRet += '=';
+ if (mode == 1)
+ strRet += '=';
+ }
+
+ return strRet;
+}
+
+string EncodeBase64(const string& str)
{
-#ifdef GUI
- // Wrapper of wxGetTranslation returning the same const char* type as was passed in
- static CCriticalSection cs;
- CRITICAL_BLOCK(cs)
+ return EncodeBase64((const unsigned char*)str.c_str(), str.size());
+}
+
+vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid)
+{
+ static const int decode64_table[256] =
+ {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
+ -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ };
+
+ if (pfInvalid)
+ *pfInvalid = false;
+
+ vector<unsigned char> vchRet;
+ vchRet.reserve(strlen(p)*3/4);
+
+ int mode = 0;
+ int left = 0;
+
+ while (1)
{
- // Look in cache
- static map<string, char*> mapCache;
- map<string, char*>::iterator mi = mapCache.find(pszEnglish);
- if (mi != mapCache.end())
- return (*mi).second;
-
- // wxWidgets translation
- wxString strTranslated = wxGetTranslation(wxString(pszEnglish, wxConvUTF8));
-
- // We don't cache unknown strings because caller might be passing in a
- // dynamic string and we would keep allocating memory for each variation.
- if (strcmp(pszEnglish, strTranslated.utf8_str()) == 0)
- return pszEnglish;
-
- // Add to cache, memory doesn't need to be freed. We only cache because
- // we must pass back a pointer to permanently allocated memory.
- char* pszCached = new char[strlen(strTranslated.utf8_str())+1];
- strcpy(pszCached, strTranslated.utf8_str());
- mapCache[pszEnglish] = pszCached;
- return pszCached;
+ int dec = decode64_table[(unsigned char)*p];
+ if (dec == -1) break;
+ p++;
+ switch (mode)
+ {
+ case 0: // we have no bits and get 6
+ left = dec;
+ mode = 1;
+ break;
+
+ case 1: // we have 6 bits and keep 4
+ vchRet.push_back((left<<2) | (dec>>4));
+ left = dec & 15;
+ mode = 2;
+ break;
+
+ case 2: // we have 4 bits and get 6, we keep 2
+ vchRet.push_back((left<<4) | (dec>>2));
+ left = dec & 3;
+ mode = 3;
+ break;
+
+ case 3: // we have 2 bits and get 6
+ vchRet.push_back((left<<6) | dec);
+ mode = 0;
+ break;
+ }
}
- return NULL;
-#else
- return pszEnglish;
-#endif
+
+ if (pfInvalid)
+ switch (mode)
+ {
+ case 0: // 4n base64 characters processed: ok
+ break;
+
+ case 1: // 4n+1 base64 character processed: impossible
+ *pfInvalid = true;
+ break;
+
+ case 2: // 4n+2 base64 characters processed: require '=='
+ if (left || p[0] != '=' || p[1] != '=' || decode64_table[(unsigned char)p[2]] != -1)
+ *pfInvalid = true;
+ break;
+
+ case 3: // 4n+3 base64 characters processed: require '='
+ if (left || p[0] != '=' || decode64_table[(unsigned char)p[1]] != -1)
+ *pfInvalid = true;
+ break;
+ }
+
+ return vchRet;
+}
+
+string DecodeBase64(const string& str)
+{
+ vector<unsigned char> vchRet = DecodeBase64(str.c_str());
+ return string((const char*)&vchRet[0], vchRet.size());
}
void FormatException(char* pszMessage, std::exception* pex, const char* pszThread)
{
-#ifdef __WXMSW__
+#ifdef WIN32
char pszModule[MAX_PATH];
pszModule[0] = '\0';
GetModuleFileNameA(NULL, pszModule, sizeof(pszModule));
printf("\n\n************************\n%s\n", pszMessage);
fprintf(stderr, "\n\n************************\n%s\n", pszMessage);
strMiscWarning = pszMessage;
-#ifdef GUI
- if (wxTheApp && !fDaemon)
- MyMessageBox(pszMessage, "Bitcoin", wxOK | wxICON_ERROR);
-#endif
throw;
}
printf("\n\n************************\n%s\n", pszMessage);
fprintf(stderr, "\n\n************************\n%s\n", pszMessage);
strMiscWarning = pszMessage;
-#ifdef GUI
- if (wxTheApp && !fDaemon)
- boost::thread(boost::bind(ThreadOneMessageBox, string(pszMessage)));
-#endif
}
-#ifdef __WXMSW__
+#ifdef WIN32
typedef WINSHELLAPI BOOL (WINAPI *PSHGETSPECIALFOLDERPATHA)(HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate);
string MyGetSpecialFolderPath(int nFolder, bool fCreate)
// Windows: C:\Documents and Settings\username\Application Data\Bitcoin
// Mac: ~/Library/Application Support/Bitcoin
// Unix: ~/.bitcoin
-#ifdef __WXMSW__
+#ifdef WIN32
// Windows
return MyGetSpecialFolderPath(CSIDL_APPDATA, true) + "\\Bitcoin";
#else
string strHome = pszHome;
if (strHome[strHome.size()-1] != '/')
strHome += '/';
-#ifdef __WXMAC_OSX__
+#ifdef MAC_OSX
// Mac
strHome += "Library/Application Support/";
filesystem::create_directory(strHome.c_str());
// - Median of other nodes's clocks
// - The user (asking the user to fix the system clock if the first two disagree)
//
+static int64 nMockTime = 0; // For unit testing
+
int64 GetTime()
{
+ if (nMockTime) return nMockTime;
+
return time(NULL);
}
+void SetMockTime(int64 nMockTimeIn)
+{
+ nMockTime = nMockTimeIn;
+}
+
static int64 nTimeOffset = 0;
int64 GetAdjustedTime()
#include "headers.h"
#include "db.h"
-#include "cryptopp/sha.h"
#include "crypter.h"
using namespace std;
return false;
}
-bool CWallet::Unlock(const string& strWalletPassphrase)
+bool CWallet::Unlock(const SecureString& strWalletPassphrase)
{
if (!IsLocked())
return false;
return false;
}
-bool CWallet::ChangeWalletPassphrase(const string& strOldWalletPassphrase, const string& strNewWalletPassphrase)
+bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase)
{
bool fWasLocked = IsLocked();
)
};
-bool CWallet::EncryptWallet(const string& strWalletPassphrase)
+bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
{
if (IsCrypted())
return false;
if (fInsertedNew || fUpdated)
if (!wtx.WriteToDisk())
return false;
-
+#ifndef QT_GUI
// If default receiving address gets used, replace it with a new one
CScript scriptDefaultKey;
scriptDefaultKey.SetBitcoinAddress(vchDefaultKey);
}
}
}
-
+#endif
// Notify UI
vWalletUpdated.push_back(hash);
return true;
}
+// Add a transaction to the wallet, or update it.
+// pblock is optional, but should be provided if the transaction is known to be in a block.
+// If fUpdate is true, existing transactions will be updated.
bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate)
{
uint256 hash = tx.GetHash();
{
map<uint256, const CMerkleTx*> mapWalletPrev;
set<uint256> setAlreadyDone;
- for (int i = 0; i < vWorkQueue.size(); i++)
+ for (unsigned int i = 0; i < vWorkQueue.size(); i++)
{
uint256 hash = vWorkQueue[i];
if (setAlreadyDone.count(hash))
return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this);
}
+// Scan the block chain (starting in pindexStart) for transactions
+// from or to us. If fUpdate is true, found transactions that already
+// exist in the wallet will be updated.
int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
{
int ret = 0;
printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %d != wtx.vout.size() %d\n", txindex.vSpent.size(), wtx.vout.size());
continue;
}
- for (int i = 0; i < txindex.vSpent.size(); i++)
+ for (unsigned int i = 0; i < txindex.vSpent.size(); i++)
{
if (wtx.IsSpent(i))
continue;
return nTotal;
}
+int64 CWallet::GetUnconfirmedBalance() const
+{
+ int64 nTotal = 0;
+ CRITICAL_BLOCK(cs_wallet)
+ {
+ for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ {
+ const CWalletTx* pcoin = &(*it).second;
+ if (pcoin->IsFinal() && pcoin->IsConfirmed())
+ continue;
+ nTotal += pcoin->GetAvailableCredit();
+ }
+ }
+ return nTotal;
+}
bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
{
if (nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs))
continue;
- for (int i = 0; i < pcoin->vout.size(); i++)
+ for (unsigned int i = 0; i < pcoin->vout.size(); i++)
{
if (pcoin->IsSpent(i) || !IsMine(pcoin->vout[i]))
continue;
if (nTotalLower == nTargetValue || nTotalLower == nTargetValue + CENT)
{
- for (int i = 0; i < vValue.size(); ++i)
+ for (unsigned int i = 0; i < vValue.size(); ++i)
{
setCoinsRet.insert(vValue[i].second);
nValueRet += vValue[i].first;
bool fReachedTarget = false;
for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++)
{
- for (int i = 0; i < vValue.size(); i++)
+ for (unsigned int i = 0; i < vValue.size(); i++)
{
if (nPass == 0 ? rand() % 2 : !vfIncluded[i])
{
nValueRet += coinLowestLarger.first;
}
else {
- for (int i = 0; i < vValue.size(); i++)
+ for (unsigned int i = 0; i < vValue.size(); i++)
if (vfBest[i])
{
setCoinsRet.insert(vValue[i].second);
//// debug print
printf("SelectCoins() best subset: ");
- for (int i = 0; i < vValue.size(); i++)
+ for (unsigned int i = 0; i < vValue.size(); i++)
if (vfBest[i])
printf("%s ", FormatMoney(vValue[i].first).c_str());
printf("total %s\n", FormatMoney(nBest).c_str());
class CReserveKey;
class CWalletDB;
+// A CWallet is an extension of a keystore, which also maintains a set of
+// transactions and balances, and provides the ability to create new
+// transactions
class CWallet : public CCryptoKeyStore
{
private:
std::vector<unsigned char> vchDefaultKey;
// keystore implementation
+ // Adds a key to the store, and saves it to disk.
bool AddKey(const CKey& key);
+ // Adds a key to the store, without saving it to disk (used by LoadWallet)
bool LoadKey(const CKey& key) { return CCryptoKeyStore::AddKey(key); }
+
+ // Adds an encrypted key to the store, and saves it to disk.
bool AddCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
+ // Adds an encrypted key to the store, without saving it to disk (used by LoadWallet)
bool LoadCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &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 Unlock(const SecureString& strWalletPassphrase);
+ bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
+ bool EncryptWallet(const SecureString& strWalletPassphrase);
bool AddToWallet(const CWalletTx& wtxIn);
bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate = false);
void ReacceptWalletTransactions();
void ResendWalletTransactions();
int64 GetBalance() const;
+ int64 GetUnconfirmedBalance() const;
bool CreateTransaction(const std::vector<std::pair<CScript, int64> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet);
bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet);
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
unsigned int nTimeReceived; // time received by this node
char fFromMe;
std::string strFromAccount;
- std::vector<char> vfSpent;
+ std::vector<char> vfSpent; // which outputs are already spent
// memory only
mutable char fDebitCached;
bool UpdateSpent(const std::vector<char>& vfNewSpent)
{
bool fReturn = false;
- for (int i=0; i < vfNewSpent.size(); i++)
+ for (unsigned int i = 0; i < vfNewSpent.size(); i++)
{
if (i == vfSpent.size())
break;
return fReturn;
}
+ // make sure balances are recalculated
void MarkDirty()
{
fCreditCached = false;
return nAvailableCreditCached;
int64 nCredit = 0;
- for (int i = 0; i < vout.size(); i++)
+ for (unsigned int i = 0; i < vout.size(); i++)
{
if (!IsSpent(i))
{
std::vector<const CMerkleTx*> vWorkQueue;
vWorkQueue.reserve(vtxPrev.size()+1);
vWorkQueue.push_back(this);
- for (int i = 0; i < vWorkQueue.size(); i++)
+ for (unsigned int i = 0; i < vWorkQueue.size(); i++)
{
const CMerkleTx* ptx = vWorkQueue[i];