From: Scott Nadal Date: Mon, 23 Jul 2012 00:09:18 +0000 (+0100) Subject: Merge with Bitcoin v0.6.3 X-Git-Tag: v0.4.0-unstable~129 X-Git-Url: https://git.novaco.in/?p=novacoin.git;a=commitdiff_plain;h=0561bbd1c69263dceb24ffacf850788e6e961a13 Merge with Bitcoin v0.6.3 --- 0561bbd1c69263dceb24ffacf850788e6e961a13 diff --cc src/base58.h index 10e03b0,bc681a0..0accbf9 --- a/src/base58.h +++ b/src/base58.h @@@ -1,8 -1,7 +1,8 @@@ // Copyright (c) 2009-2010 Satoshi Nakamoto - // Copyright (c) 2011 The Bitcoin Developers - // Copyright (c) 2011 The PPCoin developers + // Copyright (c) 2009-2012 The Bitcoin Developers ++// Copyright (c) 2011-2012 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying - // file license.txt or http://www.opensource.org/licenses/mit-license.php. + // file COPYING or http://www.opensource.org/licenses/mit-license.php. // @@@ -252,17 -252,26 +253,26 @@@ public bool operator> (const CBase58Data& b58) const { return CompareTo(b58) > 0; } }; - #define PPCOIN_ADDRESS_VERSION 55 // ppcoin: addresses begin with 'P' - - // 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 + /** base58-encoded bitcoin addresses. - * Public-key-hash-addresses have version 0 (or 111 testnet). ++ * Public-key-hash-addresses have version 55 (or 111 testnet). + * The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. - * Script-hash-addresses have version 5 (or 196 testnet). ++ * Script-hash-addresses have version 57 (or 196 testnet). + * The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. + */ class CBitcoinAddress : public CBase58Data { public: + enum + { - PUBKEY_ADDRESS = 0, - SCRIPT_ADDRESS = 5, ++ PUBKEY_ADDRESS = 55, // ppcoin: addresses begin with 'P' ++ SCRIPT_ADDRESS = 57, // ppcoin: addresses begin with 'Q' + PUBKEY_ADDRESS_TEST = 111, + SCRIPT_ADDRESS_TEST = 196, + }; + bool SetHash160(const uint160& hash160) { - SetData(fTestNet ? 111 : PPCOIN_ADDRESS_VERSION, &hash160, 20); + SetData(fTestNet ? PUBKEY_ADDRESS_TEST : PUBKEY_ADDRESS, &hash160, 20); return true; } diff --cc src/bitcoinrpc.cpp index 70b608a,3553f81..08a1dac --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@@ -1,16 -1,20 +1,22 @@@ // Copyright (c) 2010 Satoshi Nakamoto - // Copyright (c) 2011 The Bitcoin developers + // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2011-2012 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying - // file license.txt or http://www.opensource.org/licenses/mit-license.php. + // file COPYING or http://www.opensource.org/licenses/mit-license.php. - #include "headers.h" + #include "main.h" + #include "wallet.h" #include "db.h" + #include "walletdb.h" #include "net.h" #include "init.h" +#include "checkpoints.h" + #include "ui_interface.h" + #include "bitcoinrpc.h" + #undef printf #include + #include #include #include #include @@@ -52,22 -52,35 +54,35 @@@ Object JSONRPCError(int code, const str return error; } - - void PrintConsole(const std::string &format, ...) + double GetDifficulty(const CBlockIndex* blockindex = NULL) { - char buffer[50000]; - int limit = sizeof(buffer); - va_list arg_ptr; - va_start(arg_ptr, format); - int ret = _vsnprintf(buffer, limit, format.c_str(), arg_ptr); - va_end(arg_ptr); - if (ret < 0 || ret >= limit) + // Floating point number that is a multiple of the minimum difficulty, + // minimum difficulty = 1.0. + if (blockindex == NULL) { - ret = limit - 1; - buffer[limit-1] = 0; + if (pindexBest == NULL) + return 1.0; + else - blockindex = pindexBest; ++ blockindex = GetLastBlockIndex(pindexBest, false); } - printf("%s", buffer); - fprintf(stdout, "%s", buffer); + + int nShift = (blockindex->nBits >> 24) & 0xff; + + double dDiff = + (double)0x0000ffff / (double)(blockindex->nBits & 0x00ffffff); + + while (nShift < 29) + { + dDiff *= 256.0; + nShift++; + } + while (nShift > 29) + { + dDiff /= 256.0; + nShift--; + } + + return dDiff; } @@@ -164,14 -223,10 +225,10 @@@ Value stop(const Array& params, bool fH if (fHelp || params.size() != 0) throw runtime_error( "stop\n" - "Stop bitcoin server."); + "Stop ppcoin server."); - #ifndef QT_GUI // Shutdown will take long enough that the response should get back - CreateThread(Shutdown, NULL); + StartShutdown(); - return "bitcoin server stopping"; + return "ppcoin server stopping"; - #else - throw runtime_error("NYI: cannot shut down GUI with RPC command"); - #endif } @@@ -307,18 -333,14 +335,17 @@@ Value getinfo(const Array& params, boo "Returns an object containing various state info."); Object obj; - obj.push_back(Pair("version", (int)CLIENT_VERSION)); + obj.push_back(Pair("version", FormatFullVersion())); + obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION)); + obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); + obj.push_back(Pair("newmint", ValueFromAmount(pwalletMain->GetNewMint()))); + obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake()))); obj.push_back(Pair("blocks", (int)nBestHeight)); obj.push_back(Pair("connections", (int)vNodes.size())); obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string()))); + obj.push_back(Pair("ip", addrSeenByPeer.ToStringIP())); - obj.push_back(Pair("generate", (bool)fGenerateBitcoins)); - obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1))); obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("hashespersec", gethashespersec(params, false))); obj.push_back(Pair("testnet", fTestNet)); obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime())); obj.push_back(Pair("keypoolsize", pwalletMain->GetKeyPoolSize())); @@@ -965,7 -1085,8 +1089,8 @@@ Value ListReceived(const Array& params for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; + - if (wtx.IsCoinBase() || !wtx.IsFinal()) + if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal()) continue; int nDepth = wtx.GetDepthInMainChain(); @@@ -1478,15 -1616,9 +1621,15 @@@ Value walletpassphrase(const Array& par "Stores the wallet decryption key in memory for seconds."); CreateThread(ThreadTopUpKeyPool, NULL); - int* pnSleepTime = new int(params[1].get_int()); + int64* pnSleepTime = new int64(params[1].get_int64()); CreateThread(ThreadCleanWalletPassphrase, pnSleepTime); + // ppcoin: if user OS account compromised prevent trivial sendmoney commands + if (params.size() > 2) + fWalletUnlockStakeOnly = params[2].get_bool(); + else + fWalletUnlockStakeOnly = false; + return Value::null; } @@@ -1580,8 -1707,8 +1718,8 @@@ Value encryptwallet(const Array& params // 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 // unencrypted private keys. So: - CreateThread(Shutdown, NULL); + StartShutdown(); - return "wallet encrypted; bitcoin server stopping, restart to run with encrypted wallet"; + return "wallet encrypted; ppcoin server stopping, restart to run with encrypted wallet"; } @@@ -1768,10 -1924,10 +1937,10 @@@ Value getmemorypool(const Array& params Array transactions; BOOST_FOREACH(CTransaction tx, pblock->vtx) { - if(tx.IsCoinBase()) + if(tx.IsCoinBase() || tx.IsCoinStake()) continue; - CDataStream ssTx; + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << tx; transactions.push_back(HexStr(ssTx.begin(), ssTx.end())); @@@ -1804,343 -1957,129 +1970,388 @@@ } } + Value getblockhash(const Array& params, bool fHelp) + { + if (fHelp || params.size() != 1) + throw runtime_error( + "getblockhash \n" + "Returns hash of block in best-block-chain at ."); + + int nHeight = params[0].get_int(); + if (nHeight < 0 || nHeight > nBestHeight) + throw runtime_error("Block number out of range."); + + CBlock block; + CBlockIndex* pblockindex = mapBlockIndex[hashBestChain]; + while (pblockindex->nHeight > nHeight) + pblockindex = pblockindex->pprev; + return pblockindex->phashBlock->GetHex(); + } + + Value getblock(const Array& params, bool fHelp) + { + if (fHelp || params.size() != 1) + throw runtime_error( + "getblock \n" + "Returns details of a block with given block-hash."); + + std::string strHash = params[0].get_str(); + uint256 hash(strHash); + + if (mapBlockIndex.count(hash) == 0) + throw JSONRPCError(-5, "Block not found"); + + CBlock block; + CBlockIndex* pblockindex = mapBlockIndex[hash]; + block.ReadFromDisk(pblockindex, true); + + return blockToJSON(block, pblockindex); + } + +// ppcoin: get information of sync-checkpoint +Value getcheckpoint(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getcheckpoint\n" + "Show info of synchronized checkpoint.\n"); + + Object result; + CBlockIndex* pindexCheckpoint; + + result.push_back(Pair("synccheckpoint", Checkpoints::hashSyncCheckpoint.ToString().c_str())); + pindexCheckpoint = mapBlockIndex[Checkpoints::hashSyncCheckpoint]; + result.push_back(Pair("height", pindexCheckpoint->nHeight)); + result.push_back(Pair("timestamp", DateTimeStrFormat("%x %H:%M:%S", pindexCheckpoint->GetBlockTime()).c_str())); + + return result; +} + + +// ppcoin: reserve balance from being staked for network protection +Value reservebalance(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "reservebalance [ [amount]]\n" + " is true or false to turn balance reserve on or off.\n" + " is a real and rounded to cent.\n" + "Set reserve amount not participating in network protection.\n" + "If no parameters provided current setting is printed.\n"); + + if (params.size() > 0) + { + bool fReserve = params[0].get_bool(); + if (fReserve) + { + if (params.size() == 1) + throw runtime_error("must provide amount to reserve balance.\n"); + int64 nAmount = AmountFromValue(params[1]); + nAmount = (nAmount / CENT) * CENT; // round to cent + if (nAmount < 0) + throw runtime_error("amount cannot be negative.\n"); - WriteSetting("nBalanceReserve", nBalanceReserve = nAmount); ++ // TODO: handle persistence of nBalanceReserve ++ // settings removed since bitcoin 0.6 ++ // WriteSetting("nBalanceReserve", nBalanceReserve = nAmount); ++ nBalanceReserve = nAmount; + } + else + { + if (params.size() > 1) + throw runtime_error("cannot specify amount to turn off reserve.\n"); - WriteSetting("nBalanceReserve", nBalanceReserve = 0); ++ // TODO: handle persistence of nBalanceReserve ++ // settings removed since bitcoin 0.6 ++ // WriteSetting("nBalanceReserve", nBalanceReserve = 0); ++ nBalanceReserve = 0; + } + } + + Object result; + result.push_back(Pair("reserve", (nBalanceReserve > 0))); + result.push_back(Pair("amount", ValueFromAmount(nBalanceReserve))); + return result; +} + + +// ppcoin: check wallet integrity +Value checkwallet(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "checkwallet\n" + "Check wallet for integrity.\n"); + + int nMismatchSpent; + int64 nBalanceInQuestion; + if (!pwalletMain->CheckSpentCoins(nMismatchSpent, nBalanceInQuestion)) + { + Object result; + result.push_back(Pair("mismatched spent coins", nMismatchSpent)); + result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion))); + return result; + } + return Value::null; +} + + +// ppcoin: repair wallet +Value repairwallet(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "repairwallet\n" + "Repair wallet if checkwallet reports any problem.\n"); + + int nMismatchSpent; + int64 nBalanceInQuestion; + pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion); + Object result; + if (nMismatchSpent == 0) + { + result.push_back(Pair("wallet check passed", true)); + } + else + { + result.push_back(Pair("mismatched spent coins", nMismatchSpent)); + result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion))); + } + return result; +} + +// ppcoin: make a public-private key pair +Value makekeypair(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "makekeypair [prefix]\n" + "Make a public/private key pair.\n" + "[prefix] is optional preferred prefix for the public key.\n"); + + string strPrefix = ""; + if (params.size() > 0) + strPrefix = params[0].get_str(); + + CKey key; + int nCount = 0; + do + { - key.MakeNewKey(); ++ key.MakeNewKey(false); + nCount++; + } while (nCount < 10000 && strPrefix != HexStr(key.GetPubKey()).substr(0, strPrefix.size())); + + if (strPrefix != HexStr(key.GetPubKey()).substr(0, strPrefix.size())) + return Value::null; + + CPrivKey vchPrivKey = key.GetPrivKey(); + Object result; + result.push_back(Pair("PrivateKey", HexStr(vchPrivKey.begin(), vchPrivKey.end()))); + result.push_back(Pair("PublicKey", HexStr(key.GetPubKey()))); + return result; +} + +extern CCriticalSection cs_mapAlerts; +extern map mapAlerts; + +// ppcoin: send alert. +// There is a known deadlock situation with ThreadMessageHandler +// ThreadMessageHandler: holds cs_vSend and acquiring cs_main in SendMessages() +// ThreadRPCServer: holds cs_main and acquiring cs_vSend in alert.RelayTo()/PushMessage()/BeginMessage() +Value sendalert(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 5) + throw runtime_error( + "sendalert [cancelupto]\n" + " is the alert text message\n" + " is hex string of alert master private key\n" + " is the minimum applicable client version\n" + " is the maximum applicable client version\n" + " is the alert id\n" + "[cancelupto] cancels all alert id's up to this number\n" + "Returns true or false."); + + CAlert alert; + CKey key; + + alert.strStatusBar = params[0].get_str(); + alert.nMinVer = params[2].get_int(); + alert.nMaxVer = params[3].get_int(); + alert.nID = params[4].get_int(); + if (params.size() > 5) + alert.nCancel = params[5].get_int(); - alert.nVersion = VERSION; ++ alert.nVersion = PROTOCOL_VERSION; + alert.nRelayUntil = GetAdjustedTime() + 365*24*60*60; + alert.nExpiration = GetAdjustedTime() + 365*24*60*60; + alert.nPriority = 1; + - CDataStream sMsg; ++ CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION); + sMsg << (CUnsignedAlert)alert; + alert.vchMsg = vector(sMsg.begin(), sMsg.end()); + + vector vchPrivKey = ParseHex(params[1].get_str()); + key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash + if (!key.Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig)) + throw runtime_error( + "Unable to sign alert, check private key?\n"); + if(!alert.ProcessAlert()) + throw runtime_error( + "Failed to process alert.\n"); + // Relay alert - CRITICAL_BLOCK(cs_vNodes) ++ { ++ LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + alert.RelayTo(pnode); ++ } + Object result; + result.push_back(Pair("strStatusBar", alert.strStatusBar)); + result.push_back(Pair("nVersion", alert.nVersion)); + result.push_back(Pair("nMinVer", alert.nMinVer)); + result.push_back(Pair("nMaxVer", alert.nMaxVer)); + result.push_back(Pair("nID", alert.nID)); + if (alert.nCancel > 0) + result.push_back(Pair("nCancel", alert.nCancel)); + return result; +} + +// ppcoin: send checkpoint +Value sendcheckpoint(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2 || params.size() < 1 ) + throw runtime_error( + "sendcheckpoint [checkpointhash]\n" + " is hex string of checkpoint master private key\n" + " is the hash of checkpoint block\n"); + CSyncCheckpoint checkpoint; + CKey key; + // TODO: omit checkpointhash parameter + if (params.size() > 1) + { + checkpoint.hashCheckpoint = uint256(params[1].get_str()); + if (!mapBlockIndex.count(checkpoint.hashCheckpoint)) + throw runtime_error( + "Provided checkpoint block is not on main chain\n"); + } + else + { + checkpoint.hashCheckpoint = Checkpoints::AutoSelectSyncCheckpoint(); + if (checkpoint.hashCheckpoint == Checkpoints::hashSyncCheckpoint) + throw runtime_error( + "Unable to select a more recent sync-checkpoint"); + } - CDataStream sMsg; ++ CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION); + sMsg << (CUnsignedSyncCheckpoint)checkpoint; + checkpoint.vchMsg = vector(sMsg.begin(), sMsg.end()); + vector vchPrivKey = ParseHex(params[0].get_str()); + key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash + if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig)) + throw runtime_error( + "Unable to sign checkpoint, check private key?\n"); + if(!checkpoint.ProcessSyncCheckpoint(NULL)) + throw runtime_error( + "Failed to process checkpoint.\n"); + // Relay checkpoint - CRITICAL_BLOCK(cs_vNodes) ++ { ++ LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + checkpoint.RelayTo(pnode); ++ } + Object result; + result.push_back(Pair("checkpoint", Checkpoints::hashSyncCheckpoint.ToString().c_str())); + result.push_back(Pair("height", mapBlockIndex[Checkpoints::hashSyncCheckpoint]->nHeight)); + result.push_back(Pair("timestamp", DateTimeStrFormat("%x %H:%M:%S", mapBlockIndex[Checkpoints::hashSyncCheckpoint]->GetBlockTime()).c_str())); + return result; +} // // Call Table // - pair pCallTable[] = - { - make_pair("help", &help), - make_pair("stop", &stop), - make_pair("getblockcount", &getblockcount), - make_pair("getblocknumber", &getblocknumber), - make_pair("getconnectioncount", &getconnectioncount), - make_pair("getdifficulty", &getdifficulty), - make_pair("getgenerate", &getgenerate), - make_pair("setgenerate", &setgenerate), - make_pair("gethashespersec", &gethashespersec), - make_pair("getinfo", &getinfo), - make_pair("getnewaddress", &getnewaddress), - make_pair("getaccountaddress", &getaccountaddress), - make_pair("setaccount", &setaccount), - make_pair("getaccount", &getaccount), - make_pair("getaddressesbyaccount", &getaddressesbyaccount), - make_pair("sendtoaddress", &sendtoaddress), - make_pair("getreceivedbyaddress", &getreceivedbyaddress), - make_pair("getreceivedbyaccount", &getreceivedbyaccount), - make_pair("listreceivedbyaddress", &listreceivedbyaddress), - make_pair("listreceivedbyaccount", &listreceivedbyaccount), - make_pair("backupwallet", &backupwallet), - make_pair("keypoolrefill", &keypoolrefill), - make_pair("walletpassphrase", &walletpassphrase), - make_pair("walletpassphrasechange", &walletpassphrasechange), - make_pair("walletlock", &walletlock), - make_pair("encryptwallet", &encryptwallet), - make_pair("validateaddress", &validateaddress), - make_pair("getbalance", &getbalance), - make_pair("move", &movecmd), - make_pair("sendfrom", &sendfrom), - make_pair("sendmany", &sendmany), - make_pair("gettransaction", &gettransaction), - make_pair("listtransactions", &listtransactions), - make_pair("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), - make_pair("getcheckpoint", &getcheckpoint), - make_pair("reservebalance", &reservebalance), - make_pair("checkwallet", &checkwallet), - make_pair("repairwallet", &repairwallet), - make_pair("makekeypair", &makekeypair), - make_pair("sendalert", &sendalert), - make_pair("sendcheckpoint", &sendcheckpoint), - }; - map mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0])); - - string pAllowInSafeMode[] = - { - "help", - "stop", - "getblockcount", - "getblocknumber", // deprecated - "getconnectioncount", - "getdifficulty", - "getgenerate", - "setgenerate", - "gethashespersec", - "getinfo", - "getnewaddress", - "getaccountaddress", - "getaccount", - "getaddressesbyaccount", - "backupwallet", - "keypoolrefill", - "walletpassphrase", - "walletlock", - "validateaddress", - "getwork", - "getmemorypool", - "getcheckpoint", + + static const CRPCCommand vRPCCommands[] = + { // name function safe mode? + // ------------------------ ----------------------- ---------- + { "help", &help, true }, + { "stop", &stop, true }, + { "getblockcount", &getblockcount, true }, + { "getblocknumber", &getblocknumber, true }, + { "getconnectioncount", &getconnectioncount, true }, + { "getdifficulty", &getdifficulty, true }, + { "getgenerate", &getgenerate, true }, + { "setgenerate", &setgenerate, true }, + { "gethashespersec", &gethashespersec, true }, + { "getinfo", &getinfo, true }, + { "getmininginfo", &getmininginfo, true }, + { "getnewaddress", &getnewaddress, true }, + { "getaccountaddress", &getaccountaddress, true }, + { "setaccount", &setaccount, true }, + { "getaccount", &getaccount, false }, + { "getaddressesbyaccount", &getaddressesbyaccount, true }, + { "sendtoaddress", &sendtoaddress, false }, + { "getreceivedbyaddress", &getreceivedbyaddress, false }, + { "getreceivedbyaccount", &getreceivedbyaccount, false }, + { "listreceivedbyaddress", &listreceivedbyaddress, false }, + { "listreceivedbyaccount", &listreceivedbyaccount, false }, + { "backupwallet", &backupwallet, true }, + { "keypoolrefill", &keypoolrefill, true }, + { "walletpassphrase", &walletpassphrase, true }, + { "walletpassphrasechange", &walletpassphrasechange, false }, + { "walletlock", &walletlock, true }, + { "encryptwallet", &encryptwallet, false }, + { "validateaddress", &validateaddress, true }, + { "getbalance", &getbalance, false }, + { "move", &movecmd, false }, + { "sendfrom", &sendfrom, false }, + { "sendmany", &sendmany, false }, + { "addmultisigaddress", &addmultisigaddress, false }, + { "getblock", &getblock, false }, + { "getblockhash", &getblockhash, false }, + { "gettransaction", &gettransaction, false }, + { "listtransactions", &listtransactions, false }, + { "signmessage", &signmessage, false }, + { "verifymessage", &verifymessage, false }, + { "getwork", &getwork, true }, + { "listaccounts", &listaccounts, false }, + { "settxfee", &settxfee, false }, + { "getmemorypool", &getmemorypool, true }, + { "listsinceblock", &listsinceblock, false }, + { "dumpprivkey", &dumpprivkey, false }, + { "importprivkey", &importprivkey, false }, ++ { "getcheckpoint", &getcheckpoint, true }, ++ { "reservebalance", &reservebalance, false}, ++ { "checkwallet", &checkwallet, false}, ++ { "repairwallet", &repairwallet, false}, ++ { "makekeypair", &makekeypair, false}, ++ { "sendalert", &sendalert, false}, ++ { "sendcheckpoint", &sendcheckpoint, false}, }; - set setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0])); + CRPCTable::CRPCTable() + { + unsigned int vcidx; + for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++) + { + const CRPCCommand *pcmd; + pcmd = &vRPCCommands[vcidx]; + mapCommands[pcmd->name] = pcmd; + } + } + const CRPCCommand *CRPCTable::operator[](string name) const + { + map::const_iterator it = mapCommands.find(name); + if (it == mapCommands.end()) + return NULL; + return (*it).second; + } // // HTTP protocol @@@ -2423,9 -2360,11 +2632,11 @@@ void ThreadRPCServer2(void* parg printf("ThreadRPCServer started\n"); strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; - if (strRPCUserColonPass == ":") + if (mapArgs["-rpcpassword"] == "") { + unsigned char rand_pwd[32]; + RAND_bytes(rand_pwd, 32); - string strWhatAmI = "To use bitcoind"; + string strWhatAmI = "To use ppcoind"; if (mapArgs.count("-server")) strWhatAmI = strprintf(_("To use the %s option"), "\"-server\""); else if (mapArgs.count("-daemon")) @@@ -2445,12 -2388,23 +2660,23 @@@ asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback(); asio::io_service io_service; - ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332)); + ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", RPC_PORT)); - ip::tcp::acceptor acceptor(io_service, endpoint); - - acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + 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(boost::system::system_error &e) + { + ThreadSafeMessageBox(strprintf(_("An error occured while setting up the RPC port %i for listening: %s"), endpoint.port(), e.what()), + _("Error"), wxOK | wxMODAL); + StartShutdown(); + return; + } - #ifdef USE_SSL ssl::context context(io_service, ssl::context::sslv23); if (fUseSSL) { @@@ -2622,17 -2568,8 +2840,8 @@@ Object CallRPC(const string& strMethod SSLStream sslStream(io_service, context); SSLIOStreamDevice d(sslStream, fUseSSL); iostreams::stream stream(d); - if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332"))) + if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str()))) throw runtime_error("couldn't connect to server"); - #else - if (fUseSSL) - throw runtime_error("-rpcssl=1, but ppcoin compiled without full openssl libraries."); - - ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str())); - if (stream.fail()) - throw runtime_error("couldn't connect to server"); - #endif - // HTTP basic authentication string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); @@@ -2747,8 -2680,15 +2957,17 @@@ int CommandLineRPC(int argc, char *argv params[1] = v.get_obj(); } if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]); + if (strMethod == "reservebalance" && n > 0) ConvertTo(params[0]); + if (strMethod == "reservebalance" && n > 1) ConvertTo(params[1]); + if (strMethod == "addmultisigaddress" && n > 0) ConvertTo(params[0]); + if (strMethod == "addmultisigaddress" && n > 1) + { + string s = params[1].get_str(); + Value v; + if (!read_string(s, v) || v.type() != array_type) + throw runtime_error("type mismatch "+s); + params[1] = v.get_array(); + } // Execute Object reply = CallRPC(strMethod, params); diff --cc src/checkpoints.cpp index d004c84,67e2c4c..abd72d4 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@@ -1,17 -1,18 +1,20 @@@ - // Copyright (c) 2011 The Bitcoin developers + // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2011-2012 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying - // file license.txt or http://www.opensource.org/licenses/mit-license.php. + // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include // for 'map_list_of()' #include - #include "headers.h" #include "checkpoints.h" ++#include "db.h" + #include "main.h" + #include "uint256.h" + namespace Checkpoints { - typedef std::map MapCheckpoints; + typedef std::map MapCheckpoints; // hardened checkpoints // // What makes a good checkpoint block? @@@ -43,14 -50,8 +46,13 @@@ CBlockIndex* GetLastCheckpoint(const std::map& mapBlockIndex) { - if (fTestNet) return NULL; + if (fTestNet) { + std::map::const_iterator t = mapBlockIndex.find(hashGenesisBlock); + if (t != mapBlockIndex.end()) + return t->second; + return NULL; + } - int64 nResult; BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, mapCheckpoints) { const uint256& hash = i.second; @@@ -60,304 -61,4 +62,293 @@@ } return NULL; } + + // ppcoin: synchronized checkpoint (centrally broadcasted) + uint256 hashSyncCheckpoint = 0; + uint256 hashPendingCheckpoint = 0; + CSyncCheckpoint checkpointMessage; + CSyncCheckpoint checkpointMessagePending; + uint256 hashInvalidCheckpoint = 0; + CCriticalSection cs_hashSyncCheckpoint; + + // ppcoin: get last synchronized checkpoint + CBlockIndex* GetLastSyncCheckpoint() + { - CRITICAL_BLOCK(cs_hashSyncCheckpoint) - { - if (!mapBlockIndex.count(hashSyncCheckpoint)) - error("GetSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str()); - else - return mapBlockIndex[hashSyncCheckpoint]; - } ++ LOCK(cs_hashSyncCheckpoint); ++ if (!mapBlockIndex.count(hashSyncCheckpoint)) ++ error("GetSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str()); ++ else ++ return mapBlockIndex[hashSyncCheckpoint]; + return NULL; + } + + // ppcoin: only descendant of current sync-checkpoint is allowed + bool ValidateSyncCheckpoint(uint256 hashCheckpoint) + { + if (!mapBlockIndex.count(hashSyncCheckpoint)) + return error("ValidateSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str()); + if (!mapBlockIndex.count(hashCheckpoint)) + return error("ValidateSyncCheckpoint: block index missing for received sync-checkpoint %s", hashCheckpoint.ToString().c_str()); + + CBlockIndex* pindexSyncCheckpoint = mapBlockIndex[hashSyncCheckpoint]; + CBlockIndex* pindexCheckpointRecv = mapBlockIndex[hashCheckpoint]; + + if (pindexCheckpointRecv->nHeight <= pindexSyncCheckpoint->nHeight) + { + // Received an older checkpoint, trace back from current checkpoint + // to the same height of the received checkpoint to verify + // that current checkpoint should be a descendant block + CBlockIndex* pindex = pindexSyncCheckpoint; + while (pindex->nHeight > pindexCheckpointRecv->nHeight) + if (!(pindex = pindex->pprev)) + return error("ValidateSyncCheckpoint: pprev1 null - block index structure failure"); + if (pindex->GetBlockHash() != hashCheckpoint) + { + hashInvalidCheckpoint = hashCheckpoint; + return error("ValidateSyncCheckpoint: new sync-checkpoint %s is conflicting with current sync-checkpoint %s", hashCheckpoint.ToString().c_str(), hashSyncCheckpoint.ToString().c_str()); + } + return false; // ignore older checkpoint + } + + // Received checkpoint should be a descendant block of the current + // checkpoint. Trace back to the same height of current checkpoint + // to verify. + CBlockIndex* pindex = pindexCheckpointRecv; + while (pindex->nHeight > pindexSyncCheckpoint->nHeight) + if (!(pindex = pindex->pprev)) + return error("ValidateSyncCheckpoint: pprev2 null - block index structure failure"); + if (pindex->GetBlockHash() != hashSyncCheckpoint) + { + hashInvalidCheckpoint = hashCheckpoint; + return error("ValidateSyncCheckpoint: new sync-checkpoint %s is not a descendant of current sync-checkpoint %s", hashCheckpoint.ToString().c_str(), hashSyncCheckpoint.ToString().c_str()); + } + return true; + } + + bool WriteSyncCheckpoint(const uint256& hashCheckpoint) + { + CTxDB txdb; + txdb.TxnBegin(); + if (!txdb.WriteSyncCheckpoint(hashCheckpoint)) + { + txdb.TxnAbort(); + return error("WriteSyncCheckpoint(): failed to write to db sync checkpoint %s", hashCheckpoint.ToString().c_str()); + } + if (!txdb.TxnCommit()) + return error("WriteSyncCheckpoint(): failed to commit to db sync checkpoint %s", hashCheckpoint.ToString().c_str()); + txdb.Close(); + + Checkpoints::hashSyncCheckpoint = hashCheckpoint; + return true; + } + + bool AcceptPendingSyncCheckpoint() + { - CRITICAL_BLOCK(cs_hashSyncCheckpoint) ++ LOCK(cs_hashSyncCheckpoint); ++ if (hashPendingCheckpoint != 0 && mapBlockIndex.count(hashPendingCheckpoint)) + { - if (hashPendingCheckpoint != 0 && mapBlockIndex.count(hashPendingCheckpoint)) ++ if (!ValidateSyncCheckpoint(hashPendingCheckpoint)) + { - if (!ValidateSyncCheckpoint(hashPendingCheckpoint)) - { - hashPendingCheckpoint = 0; - checkpointMessagePending.SetNull(); - return false; - } ++ hashPendingCheckpoint = 0; ++ checkpointMessagePending.SetNull(); ++ return false; ++ } + - CTxDB txdb; - CBlockIndex* pindexCheckpoint = mapBlockIndex[hashPendingCheckpoint]; - if (!pindexCheckpoint->IsInMainChain()) ++ CTxDB txdb; ++ CBlockIndex* pindexCheckpoint = mapBlockIndex[hashPendingCheckpoint]; ++ if (!pindexCheckpoint->IsInMainChain()) ++ { ++ txdb.TxnBegin(); ++ if (!Reorganize(txdb, pindexCheckpoint)) + { - txdb.TxnBegin(); - if (!Reorganize(txdb, pindexCheckpoint)) - { - txdb.TxnAbort(); - hashInvalidCheckpoint = hashPendingCheckpoint; - return error("ProcessSyncCheckpoint: Reorganize failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str()); - } ++ txdb.TxnAbort(); ++ hashInvalidCheckpoint = hashPendingCheckpoint; ++ return error("ProcessSyncCheckpoint: Reorganize failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str()); + } - txdb.Close(); - - if (!WriteSyncCheckpoint(hashPendingCheckpoint)) - return error("AcceptPendingSyncCheckpoint(): failed to write sync checkpoint %s", hashPendingCheckpoint.ToString().c_str()); - hashPendingCheckpoint = 0; - checkpointMessage = checkpointMessagePending; - checkpointMessagePending.SetNull(); - printf("AcceptPendingSyncCheckpoint : sync-checkpoint at %s\n", hashSyncCheckpoint.ToString().c_str()); - // relay the checkpoint - if (!checkpointMessage.IsNull()) - BOOST_FOREACH(CNode* pnode, vNodes) - checkpointMessage.RelayTo(pnode); - return true; + } ++ txdb.Close(); ++ ++ if (!WriteSyncCheckpoint(hashPendingCheckpoint)) ++ return error("AcceptPendingSyncCheckpoint(): failed to write sync checkpoint %s", hashPendingCheckpoint.ToString().c_str()); ++ hashPendingCheckpoint = 0; ++ checkpointMessage = checkpointMessagePending; ++ checkpointMessagePending.SetNull(); ++ printf("AcceptPendingSyncCheckpoint : sync-checkpoint at %s\n", hashSyncCheckpoint.ToString().c_str()); ++ // relay the checkpoint ++ if (!checkpointMessage.IsNull()) ++ { ++ BOOST_FOREACH(CNode* pnode, vNodes) ++ checkpointMessage.RelayTo(pnode); ++ } ++ return true; + } - + return false; + } + + uint256 AutoSelectSyncCheckpoint() + { + // select a block some time ago + CBlockIndex *pindex = mapBlockIndex[hashSyncCheckpoint]; + while (pindex->pnext && pindex->pnext->GetBlockTime() + CHECKPOINT_MIN_SPAN <= GetAdjustedTime()) + pindex = pindex->pnext; + return pindex->GetBlockHash(); + } + + // Check against synchronized checkpoint + bool CheckSync(const uint256& hashBlock, const CBlockIndex* pindexPrev) + { + if (fTestNet) return true; // Testnet has no checkpoints + int nHeight = pindexPrev->nHeight + 1; + - CRITICAL_BLOCK(cs_hashSyncCheckpoint) - { - // sync-checkpoint should always be accepted block - assert(mapBlockIndex.count(hashSyncCheckpoint)); - const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint]; ++ LOCK(cs_hashSyncCheckpoint); ++ // sync-checkpoint should always be accepted block ++ assert(mapBlockIndex.count(hashSyncCheckpoint)); ++ const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint]; + - if (nHeight > pindexSync->nHeight) - { - // trace back to same height as sync-checkpoint - const CBlockIndex* pindex = pindexPrev; - while (pindex->nHeight > pindexSync->nHeight) - if (!(pindex = pindex->pprev)) - return error("CheckSync: pprev null - block index structure failure"); - if (pindex->nHeight < pindexSync->nHeight || pindex->GetBlockHash() != hashSyncCheckpoint) - return false; // only descendant of sync-checkpoint can pass check - } - if (nHeight == pindexSync->nHeight && hashBlock != hashSyncCheckpoint) - return false; // same height with sync-checkpoint - if (nHeight < pindexSync->nHeight && !mapBlockIndex.count(hashBlock)) - return false; // lower height than sync-checkpoint ++ if (nHeight > pindexSync->nHeight) ++ { ++ // trace back to same height as sync-checkpoint ++ const CBlockIndex* pindex = pindexPrev; ++ while (pindex->nHeight > pindexSync->nHeight) ++ if (!(pindex = pindex->pprev)) ++ return error("CheckSync: pprev null - block index structure failure"); ++ if (pindex->nHeight < pindexSync->nHeight || pindex->GetBlockHash() != hashSyncCheckpoint) ++ return false; // only descendant of sync-checkpoint can pass check + } ++ if (nHeight == pindexSync->nHeight && hashBlock != hashSyncCheckpoint) ++ return false; // same height with sync-checkpoint ++ if (nHeight < pindexSync->nHeight && !mapBlockIndex.count(hashBlock)) ++ return false; // lower height than sync-checkpoint + return true; + } + + bool WantedByPendingSyncCheckpoint(uint256 hashBlock) + { - CRITICAL_BLOCK(cs_hashSyncCheckpoint) - { - if (hashPendingCheckpoint == 0) - return false; - if (hashBlock == hashPendingCheckpoint) - return true; - if (mapOrphanBlocks.count(hashPendingCheckpoint) - && hashBlock == WantedByOrphan(mapOrphanBlocks[hashPendingCheckpoint])) - return true; - } ++ LOCK(cs_hashSyncCheckpoint); ++ if (hashPendingCheckpoint == 0) ++ return false; ++ if (hashBlock == hashPendingCheckpoint) ++ return true; ++ if (mapOrphanBlocks.count(hashPendingCheckpoint) ++ && hashBlock == WantedByOrphan(mapOrphanBlocks[hashPendingCheckpoint])) ++ return true; + return false; + } + + // ppcoin: reset synchronized checkpoint to last hardened checkpoint + bool ResetSyncCheckpoint() + { - CRITICAL_BLOCK(cs_hashSyncCheckpoint) ++ LOCK(cs_hashSyncCheckpoint); ++ const uint256& hash = mapCheckpoints.rbegin()->second; ++ if (mapBlockIndex.count(hash) && !mapBlockIndex[hash]->IsInMainChain()) + { - const uint256& hash = mapCheckpoints.rbegin()->second; - if (mapBlockIndex.count(hash) && !mapBlockIndex[hash]->IsInMainChain()) - { - // checkpoint block accepted but not yet in main chain - printf("ResetSyncCheckpoint: Reorganize to hardened checkpoint %s\n", hash.ToString().c_str()); - CTxDB txdb; - txdb.TxnBegin(); - if (!Reorganize(txdb, mapBlockIndex[hash])) - { - txdb.TxnAbort(); - return error("ResetSyncCheckpoint: Reorganize failed for hardened checkpoint %s", hash.ToString().c_str()); - } - txdb.Close(); - } - else if(!mapBlockIndex.count(hash)) ++ // checkpoint block accepted but not yet in main chain ++ printf("ResetSyncCheckpoint: Reorganize to hardened checkpoint %s\n", hash.ToString().c_str()); ++ CTxDB txdb; ++ txdb.TxnBegin(); ++ if (!Reorganize(txdb, mapBlockIndex[hash])) + { - // checkpoint block not yet accepted - hashPendingCheckpoint = hash; - checkpointMessagePending.SetNull(); - printf("ResetSyncCheckpoint: pending for sync-checkpoint %s\n", hashPendingCheckpoint.ToString().c_str()); ++ txdb.TxnAbort(); ++ return error("ResetSyncCheckpoint: Reorganize failed for hardened checkpoint %s", hash.ToString().c_str()); + } ++ txdb.Close(); ++ } ++ else if(!mapBlockIndex.count(hash)) ++ { ++ // checkpoint block not yet accepted ++ hashPendingCheckpoint = hash; ++ checkpointMessagePending.SetNull(); ++ printf("ResetSyncCheckpoint: pending for sync-checkpoint %s\n", hashPendingCheckpoint.ToString().c_str()); ++ } + - BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, mapCheckpoints) ++ BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, mapCheckpoints) ++ { ++ const uint256& hash = i.second; ++ if (mapBlockIndex.count(hash) && mapBlockIndex[hash]->IsInMainChain()) + { - const uint256& hash = i.second; - if (mapBlockIndex.count(hash) && mapBlockIndex[hash]->IsInMainChain()) - { - if (!WriteSyncCheckpoint(hash)) - return error("ResetSyncCheckpoint: failed to write sync checkpoint %s", hash.ToString().c_str()); - printf("ResetSyncCheckpoint: sync-checkpoint reset to %s\n", hashSyncCheckpoint.ToString().c_str()); - return true; - } ++ if (!WriteSyncCheckpoint(hash)) ++ return error("ResetSyncCheckpoint: failed to write sync checkpoint %s", hash.ToString().c_str()); ++ printf("ResetSyncCheckpoint: sync-checkpoint reset to %s\n", hashSyncCheckpoint.ToString().c_str()); ++ return true; + } - - return false; + } ++ ++ return false; + } + + void AskForPendingSyncCheckpoint(CNode* pfrom) + { - CRITICAL_BLOCK(cs_hashSyncCheckpoint) - if (pfrom && hashPendingCheckpoint != 0 && (!mapBlockIndex.count(hashPendingCheckpoint)) && (!mapOrphanBlocks.count(hashPendingCheckpoint))) - pfrom->AskFor(CInv(MSG_BLOCK, hashPendingCheckpoint)); ++ LOCK(cs_hashSyncCheckpoint); ++ if (pfrom && hashPendingCheckpoint != 0 && (!mapBlockIndex.count(hashPendingCheckpoint)) && (!mapOrphanBlocks.count(hashPendingCheckpoint))) ++ pfrom->AskFor(CInv(MSG_BLOCK, hashPendingCheckpoint)); + } +} + +// ppcoin: sync-checkpoint master key +const std::string CSyncCheckpoint::strMasterPubKey = "0424f20205e5da98ba632bbd278a11a6499585f62bfb2c782377ef59f0251daab8085fc31471bcb8180bc75ed0fa41bb50c7c084511d54015a3a5241d645c7268a"; + +// ppcoin: verify signature of sync-checkpoint message +bool CSyncCheckpoint::CheckSignature() +{ + CKey key; + if (!key.SetPubKey(ParseHex(CSyncCheckpoint::strMasterPubKey))) + return error("CSyncCheckpoint::CheckSignature() : SetPubKey failed"); + if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig)) + return error("CSyncCheckpoint::CheckSignature() : verify signature failed"); + + // Now unserialize the data - CDataStream sMsg(vchMsg); ++ CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION); + sMsg >> *(CUnsignedSyncCheckpoint*)this; + return true; +} + +// ppcoin: process synchronized checkpoint +bool CSyncCheckpoint::ProcessSyncCheckpoint(CNode* pfrom) +{ + if (!CheckSignature()) + return false; + - CRITICAL_BLOCK(Checkpoints::cs_hashSyncCheckpoint) ++ LOCK(Checkpoints::cs_hashSyncCheckpoint); ++ if (!mapBlockIndex.count(hashCheckpoint)) + { - if (!mapBlockIndex.count(hashCheckpoint)) ++ // We haven't received the checkpoint chain, keep the checkpoint as pending ++ Checkpoints::hashPendingCheckpoint = hashCheckpoint; ++ Checkpoints::checkpointMessagePending = *this; ++ printf("ProcessSyncCheckpoint: pending for sync-checkpoint %s\n", hashCheckpoint.ToString().c_str()); ++ // Ask this guy to fill in what we're missing ++ if (pfrom) + { - // We haven't received the checkpoint chain, keep the checkpoint as pending - Checkpoints::hashPendingCheckpoint = hashCheckpoint; - Checkpoints::checkpointMessagePending = *this; - printf("ProcessSyncCheckpoint: pending for sync-checkpoint %s\n", hashCheckpoint.ToString().c_str()); - // Ask this guy to fill in what we're missing - if (pfrom) - { - pfrom->PushGetBlocks(pindexBest, hashCheckpoint); - // ask directly as well in case rejected earlier by duplicate - // proof-of-stake because getblocks may not get it this time - pfrom->AskFor(CInv(MSG_BLOCK, mapOrphanBlocks.count(hashCheckpoint)? WantedByOrphan(mapOrphanBlocks[hashCheckpoint]) : hashCheckpoint)); - } - return false; ++ pfrom->PushGetBlocks(pindexBest, hashCheckpoint); ++ // ask directly as well in case rejected earlier by duplicate ++ // proof-of-stake because getblocks may not get it this time ++ pfrom->AskFor(CInv(MSG_BLOCK, mapOrphanBlocks.count(hashCheckpoint)? WantedByOrphan(mapOrphanBlocks[hashCheckpoint]) : hashCheckpoint)); + } ++ return false; ++ } + - if (!Checkpoints::ValidateSyncCheckpoint(hashCheckpoint)) - return false; ++ if (!Checkpoints::ValidateSyncCheckpoint(hashCheckpoint)) ++ return false; + - CTxDB txdb; - CBlockIndex* pindexCheckpoint = mapBlockIndex[hashCheckpoint]; - if (!pindexCheckpoint->IsInMainChain()) ++ CTxDB txdb; ++ CBlockIndex* pindexCheckpoint = mapBlockIndex[hashCheckpoint]; ++ if (!pindexCheckpoint->IsInMainChain()) ++ { ++ // checkpoint chain received but not yet main chain ++ txdb.TxnBegin(); ++ if (!Reorganize(txdb, pindexCheckpoint)) + { - // checkpoint chain received but not yet main chain - txdb.TxnBegin(); - if (!Reorganize(txdb, pindexCheckpoint)) - { - txdb.TxnAbort(); - Checkpoints::hashInvalidCheckpoint = hashCheckpoint; - return error("ProcessSyncCheckpoint: Reorganize failed for sync checkpoint %s", hashCheckpoint.ToString().c_str()); - } ++ txdb.TxnAbort(); ++ Checkpoints::hashInvalidCheckpoint = hashCheckpoint; ++ return error("ProcessSyncCheckpoint: Reorganize failed for sync checkpoint %s", hashCheckpoint.ToString().c_str()); + } - txdb.Close(); - - if (!Checkpoints::WriteSyncCheckpoint(hashCheckpoint)) - return error("ProcessSyncCheckpoint(): failed to write sync checkpoint %s", hashCheckpoint.ToString().c_str()); - Checkpoints::checkpointMessage = *this; - Checkpoints::hashPendingCheckpoint = 0; - Checkpoints::checkpointMessagePending.SetNull(); - printf("ProcessSyncCheckpoint: sync-checkpoint at %s\n", hashCheckpoint.ToString().c_str()); + } ++ txdb.Close(); ++ ++ if (!Checkpoints::WriteSyncCheckpoint(hashCheckpoint)) ++ return error("ProcessSyncCheckpoint(): failed to write sync checkpoint %s", hashCheckpoint.ToString().c_str()); ++ Checkpoints::checkpointMessage = *this; ++ Checkpoints::hashPendingCheckpoint = 0; ++ Checkpoints::checkpointMessagePending.SetNull(); ++ printf("ProcessSyncCheckpoint: sync-checkpoint at %s\n", hashCheckpoint.ToString().c_str()); + return true; } diff --cc src/checkpoints.h index c09e568,70e9365..ff815c8 --- a/src/checkpoints.h +++ b/src/checkpoints.h @@@ -1,24 -1,17 +1,24 @@@ - // Copyright (c) 2011 The Bitcoin developers + // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2011-2012 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying - // file license.txt or http://www.opensource.org/licenses/mit-license.php. + // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_CHECKPOINT_H #define BITCOIN_CHECKPOINT_H #include ++#include "net.h" +#include "util.h" + +#define STAKE_MIN_AGE (60 * 60 * 24) // minimum age for coin age +#define CHECKPOINT_MIN_SPAN (60 * 60 * 4) // 4 hours checkpoint class uint256; class CBlockIndex; +class CSyncCheckpoint; - // - // Block-chain checkpoints are compiled-in sanity checks. - // They are updated every release or three. - // + /** Block-chain checkpoints are compiled-in sanity checks. + * They are updated every release or three. + */ namespace Checkpoints { // Returns true if block passes checkpoint checks diff --cc src/db.cpp index 2fec183,c67a34c..04f400e --- a/src/db.cpp +++ b/src/db.cpp @@@ -1,13 -1,12 +1,15 @@@ // Copyright (c) 2009-2010 Satoshi Nakamoto - // Copyright (c) 2011 The Bitcoin developers + // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2011-2012 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying - // file license.txt or http://www.opensource.org/licenses/mit-license.php. + // file COPYING or http://www.opensource.org/licenses/mit-license.php. - #include "headers.h" #include "db.h" +#include "net.h" +#include "checkpoints.h" + #include "util.h" + #include "main.h" + #include #include #include @@@ -575,6 -579,24 +609,9 @@@ bool CTxDB::LoadBlockIndex( } pcursor->close(); + if (fRequestShutdown) + return true; + - // Calculate bnChainWork - vector > vSortedByHeight; - vSortedByHeight.reserve(mapBlockIndex.size()); - BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) - { - CBlockIndex* pindex = item.second; - vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex)); - } - sort(vSortedByHeight.begin(), vSortedByHeight.end()); - BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight) - { - CBlockIndex* pindex = item.second; - pindex->bnChainWork = (pindex->pprev ? pindex->pprev->bnChainWork : 0) + pindex->GetBlockWork(); - } - // Load hashBestChain pointer to end of best chain if (!ReadHashBestChain(hashBestChain)) { @@@ -586,22 -608,25 +623,30 @@@ return error("CTxDB::LoadBlockIndex() : hashBestChain not found in the block index"); pindexBest = mapBlockIndex[hashBestChain]; nBestHeight = pindexBest->nHeight; - bnBestChainWork = pindexBest->bnChainWork; - printf("LoadBlockIndex(): hashBestChain=%s height=%d\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight); + nBestChainTrust = pindexBest->nChainTrust; + printf("LoadBlockIndex(): hashBestChain=%s height=%d trust=%d\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, nBestChainTrust); + + // ppcoin: load hashSyncCheckpoint + if (!ReadSyncCheckpoint(Checkpoints::hashSyncCheckpoint)) + return error("CTxDB::LoadBlockIndex() : hashSyncCheckpoint not loaded"); + printf("LoadBlockIndex(): synchronized checkpoint %s\n", Checkpoints::hashSyncCheckpoint.ToString().c_str()); - // Load bnBestInvalidWork, OK if it doesn't exist - ReadBestInvalidWork(bnBestInvalidWork); + // Load nBestInvalidTrust, OK if it doesn't exist + ReadBestInvalidTrust(nBestInvalidTrust); // Verify blocks in the best chain + int nCheckLevel = GetArg("-checklevel", 1); + int nCheckDepth = GetArg( "-checkblocks", 2500); + if (nCheckDepth == 0) + nCheckDepth = 1000000000; // suffices until the year 19000 + if (nCheckDepth > nBestHeight) + nCheckDepth = nBestHeight; + printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); CBlockIndex* pindexFork = NULL; + map, CBlockIndex*> mapBlockPos; for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) { - if (pindex->nHeight < nBestHeight-2500 && !mapArgs.count("-checkblocks")) + if (pindex->nHeight < nBestHeight-nCheckDepth) break; CBlock block; if (!block.ReadFromDisk(pindex)) diff --cc src/db.h index 038c6c4,acb531f..792d5ca --- a/src/db.h +++ b/src/db.h @@@ -1,8 -1,7 +1,8 @@@ // Copyright (c) 2009-2010 Satoshi Nakamoto - // Copyright (c) 2011 The Bitcoin developers + // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2011-2012 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying - // file license.txt or http://www.opensource.org/licenses/mit-license.php. + // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_DB_H #define BITCOIN_DB_H diff --cc src/init.cpp index e66764d,281a8ca..65386e8 --- a/src/init.cpp +++ b/src/init.cpp @@@ -1,10 -1,9 +1,10 @@@ // Copyright (c) 2009-2010 Satoshi Nakamoto - // Copyright (c) 2011 The Bitcoin developers + // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2011-2012 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying - // file license.txt or http://www.opensource.org/licenses/mit-license.php. - #include "headers.h" + // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "db.h" + #include "walletdb.h" #include "bitcoinrpc.h" #include "net.h" #include "init.h" @@@ -64,9 -73,12 +74,12 @@@ void Shutdown(void* parg delete pwalletMain; CreateThread(ExitTimeout, NULL); Sleep(50); - printf("Bitcoin exiting\n\n"); + printf("PPCoin exiting\n\n"); fExit = true; + #ifndef QT_GUI + // ensure non UI client get's exited here, but let Bitcoin-Qt reach return 0; in bitcoin.cpp exit(0); + #endif } else { @@@ -173,72 -175,82 +178,81 @@@ bool AppInit2(int argc, char* argv[] if (mapArgs.count("-?") || mapArgs.count("--help")) { string strUsage = string() + - _("Bitcoin version") + " " + FormatFullVersion() + "\n\n" + + _("PPCoin version") + " " + FormatFullVersion() + "\n\n" + _("Usage:") + "\t\t\t\t\t\t\t\t\t\t\n" + - " bitcoind [options] \t " + "\n" + - " bitcoind [options] [params]\t " + _("Send command to -server or bitcoind") + "\n" + - " bitcoind [options] help \t\t " + _("List commands") + "\n" + - " bitcoind [options] help \t\t " + _("Get help for a command") + "\n" + + " ppcoind [options] \t " + "\n" + - " ppcoind [options] [params]\t " + _("Send command to -server or ppcoind\n") + - " ppcoind [options] help \t\t " + _("List commands\n") + - " ppcoind [options] help \t\t " + _("Get help for a command\n") + - _("Options:\n") + - " -conf= \t\t " + _("Specify configuration file (default: ppcoin.conf)\n") + - " -pid= \t\t " + _("Specify pid file (default: ppcoind.pid)\n") + - " -gen \t\t " + _("Generate coins\n") + - " -gen=0 \t\t " + _("Don't generate coins\n") + - " -min \t\t " + _("Start minimized\n") + - " -datadir= \t\t " + _("Specify data directory\n") + - " -timeout= \t " + _("Specify connection timeout (in milliseconds)\n") + - " -proxy= \t " + _("Connect through socks4 proxy\n") + - " -dns \t " + _("Allow DNS lookups for addnode and connect\n") + - " -port= \t\t " + _("Listen for connections on (default: 9901 or testnet: 9903)\n") + - " -maxconnections=\t " + _("Maintain at most connections to peers (default: 125)\n") + - " -addnode= \t " + _("Add a node to connect to\n") + - " -connect= \t\t " + _("Connect only to the specified node\n") + - " -nolisten \t " + _("Don't accept connections from outside\n") + - " -nodnsseed \t " + _("Don't bootstrap list of peers using DNS\n") + - " -banscore= \t " + _("Threshold for disconnecting misbehaving peers (default: 100)\n") + - " -bantime= \t " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)\n") + - " -maxreceivebuffer=\t " + _("Maximum per-connection receive buffer, *1000 bytes (default: 10000)\n") + - " -maxsendbuffer=\t " + _("Maximum per-connection send buffer, *1000 bytes (default: 10000)\n") + ++ " ppcoind [options] [params]\t " + _("Send command to -server or ppcoind") + "\n" + ++ " ppcoind [options] help \t\t " + _("List commands") + "\n" + ++ " ppcoind [options] help \t\t " + _("Get help for a command") + "\n" + + _("Options:") + "\n" + - " -conf= \t\t " + _("Specify configuration file (default: bitcoin.conf)") + "\n" + - " -pid= \t\t " + _("Specify pid file (default: bitcoind.pid)") + "\n" + ++ " -conf= \t\t " + _("Specify configuration file (default: ppcoin.conf)") + "\n" + ++ " -pid= \t\t " + _("Specify pid file (default: ppcoind.pid)") + "\n" + + " -gen \t\t " + _("Generate coins") + "\n" + + " -gen=0 \t\t " + _("Don't generate coins") + "\n" + + " -min \t\t " + _("Start minimized") + "\n" + + " -splash \t\t " + _("Show splash screen on startup (default: 1)") + "\n" + + " -datadir= \t\t " + _("Specify data directory") + "\n" + + " -dbcache= \t\t " + _("Set database cache size in megabytes (default: 25)") + "\n" + + " -dblogsize= \t\t " + _("Set database disk log size in megabytes (default: 100)") + "\n" + + " -timeout= \t " + _("Specify connection timeout (in milliseconds)") + "\n" + + " -proxy= \t " + _("Connect through socks4 proxy") + "\n" + + " -dns \t " + _("Allow DNS lookups for addnode and connect") + "\n" + - " -port= \t\t " + _("Listen for connections on (default: 8333 or testnet: 18333)") + "\n" + ++ " -port= \t\t " + _("Listen for connections on (default: 9901 or testnet: 9903)") + "\n" + + " -maxconnections=\t " + _("Maintain at most connections to peers (default: 125)") + "\n" + + " -addnode= \t " + _("Add a node to connect to and attempt to keep the connection open") + "\n" + + " -connect= \t\t " + _("Connect only to the specified node") + "\n" + - " -irc \t " + _("Find peers using internet relay chat (default: 0)") + "\n" + + " -listen \t " + _("Accept connections from outside (default: 1)") + "\n" + + #ifdef QT_GUI + " -lang= \t\t " + _("Set language, for example \"de_DE\" (default: system locale)") + "\n" + + #endif + " -dnsseed \t " + _("Find peers using DNS lookup (default: 1)") + "\n" + + " -banscore= \t " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n" + + " -bantime= \t " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n" + + " -maxreceivebuffer=\t " + _("Maximum per-connection receive buffer, *1000 bytes (default: 10000)") + "\n" + + " -maxsendbuffer=\t " + _("Maximum per-connection send buffer, *1000 bytes (default: 10000)") + "\n" + #ifdef USE_UPNP #if USE_UPNP - " -noupnp \t " + _("Don't attempt to use UPnP to map the listening port\n") + + " -upnp \t " + _("Use Universal Plug and Play to map the listening port (default: 1)") + "\n" + #else - " -upnp \t " + _("Attempt to use UPnP to map the listening port\n") + + " -upnp \t " + _("Use Universal Plug and Play to map the listening port (default: 0)") + "\n" + #endif + " -detachdb \t " + _("Detach block and address databases. Increases shutdown time (default: 0)") + "\n" + #endif - " -paytxfee= \t " + _("Fee per KB to add to transactions you send\n") + - #ifdef GUI - " -server \t\t " + _("Accept command line and JSON-RPC commands\n") + + " -paytxfee= \t " + _("Fee per KB to add to transactions you send") + "\n" + + #ifdef QT_GUI + " -server \t\t " + _("Accept command line and JSON-RPC commands") + "\n" + #endif - #ifndef WIN32 - " -daemon \t\t " + _("Run in the background as a daemon and accept commands\n") + + #if !defined(WIN32) && !defined(QT_GUI) + " -daemon \t\t " + _("Run in the background as a daemon and accept commands") + "\n" + #endif - " -testnet \t\t " + _("Use the test network\n") + - " -debug \t\t " + _("Output extra debugging information\n") + - " -logtimestamps \t " + _("Prepend debug output with timestamp\n") + - " -printtoconsole \t " + _("Send trace/debug info to console instead of debug.log file\n") + + " -testnet \t\t " + _("Use the test network") + "\n" + + " -debug \t\t " + _("Output extra debugging information") + "\n" + + " -logtimestamps \t " + _("Prepend debug output with timestamp") + "\n" + + " -printtoconsole \t " + _("Send trace/debug info to console instead of debug.log file") + "\n" + #ifdef WIN32 - " -printtodebugger \t " + _("Send trace/debug info to debugger\n") + + " -printtodebugger \t " + _("Send trace/debug info to debugger") + "\n" + #endif - " -rpcuser= \t " + _("Username for JSON-RPC connections\n") + - " -rpcpassword=\t " + _("Password for JSON-RPC connections\n") + - " -rpcport= \t\t " + _("Listen for JSON-RPC connections on (default: 9902)\n") + - " -rpcallowip= \t\t " + _("Allow JSON-RPC connections from specified IP address\n") + - " -rpcconnect= \t " + _("Send commands to node running on (default: 127.0.0.1)\n") + - " -keypool= \t " + _("Set key pool size to (default: 100)\n") + - " -rescan \t " + _("Rescan the block chain for missing wallet transactions\n"); - - #ifdef USE_SSL + " -rpcuser= \t " + _("Username for JSON-RPC connections") + "\n" + + " -rpcpassword=\t " + _("Password for JSON-RPC connections") + "\n" + - " -rpcport= \t\t " + _("Listen for JSON-RPC connections on (default: 8332)") + "\n" + ++ " -rpcport= \t\t " + _("Listen for JSON-RPC connections on (default: 9902)") + "\n" + + " -rpcallowip= \t\t " + _("Allow JSON-RPC connections from specified IP address") + "\n" + + " -rpcconnect= \t " + _("Send commands to node running on (default: 127.0.0.1)") + "\n" + + " -blocknotify= " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" + + " -upgradewallet \t " + _("Upgrade wallet to latest format") + "\n" + + " -keypool= \t " + _("Set key pool size to (default: 100)") + "\n" + + " -rescan \t " + _("Rescan the block chain for missing wallet transactions") + "\n" + + " -checkblocks= \t\t " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" + + " -checklevel= \t\t " + _("How thorough the block verification is (0-6, default: 1)") + "\n"; + strUsage += string() + - _("\nSSL options: (see the Bitcoin Wiki for SSL setup instructions)\n") + - " -rpcssl \t " + _("Use OpenSSL (https) for JSON-RPC connections\n") + - " -rpcsslcertificatechainfile=\t " + _("Server certificate file (default: server.cert)\n") + - " -rpcsslprivatekeyfile= \t " + _("Server private key (default: server.pem)\n") + - " -rpcsslciphers= \t " + _("Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)\n"); - #endif + _("\nSSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n" + + " -rpcssl \t " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n" + + " -rpcsslcertificatechainfile=\t " + _("Server certificate file (default: server.cert)") + "\n" + + " -rpcsslprivatekeyfile= \t " + _("Server private key (default: server.pem)") + "\n" + + " -rpcsslciphers= \t " + _("Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)") + "\n"; strUsage += string() + - " -? \t\t " + _("This help message\n"); + " -? \t\t " + _("This help message") + "\n"; // Remove tabs strUsage.erase(std::remove(strUsage.begin(), strUsage.end(), '\t'), strUsage.end()); @@@ -306,11 -325,11 +327,11 @@@ } #endif - if (!fDebug && !pszSetDataDir[0]) + if (!fDebug) ShrinkDebugFile(); printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); - printf("PPCoin version %s\n", FormatFullVersion().c_str()); - printf("Default data directory %s\n", GetDefaultDataDir().c_str()); - printf("Bitcoin version %s (%s)\n", FormatFullVersion().c_str(), CLIENT_DATE.c_str()); ++ printf("PPCoin version %s (%s)\n", FormatFullVersion().c_str(), CLIENT_DATE.c_str()); + printf("Default data directory %s\n", GetDefaultDataDir().string().c_str()); if (GetBoolArg("-loadblockindextest")) { @@@ -321,13 -340,13 +342,13 @@@ } // Make sure only a single bitcoin process is using the data directory. - string strLockFile = GetDataDir() + "/.lock"; - FILE* file = fopen(strLockFile.c_str(), "a"); // empty lock file; created if it doesn't exist. + boost::filesystem::path pathLockFile = GetDataDir() / ".lock"; + FILE* file = fopen(pathLockFile.string().c_str(), "a"); // empty lock file; created if it doesn't exist. if (file) fclose(file); - static boost::interprocess::file_lock lock(strLockFile.c_str()); + static boost::interprocess::file_lock lock(pathLockFile.string().c_str()); if (!lock.try_lock()) { - wxMessageBox(strprintf(_("Cannot obtain a lock on data directory %s. PPCoin is probably already running."), GetDataDir().c_str()), "PPCoin"); - ThreadSafeMessageBox(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), GetDataDir().string().c_str()), _("Bitcoin"), wxOK|wxMODAL); ++ ThreadSafeMessageBox(strprintf(_("Cannot obtain a lock on data directory %s. PPCoin is probably already running."), GetDataDir().string().c_str()), _("PPCoin"), wxOK|wxMODAL); return false; } @@@ -346,8 -355,7 +357,7 @@@ // Load data files // if (fDaemon) - fprintf(stdout, "bitcoin server starting\n"); + fprintf(stdout, "ppcoin server starting\n"); - strErrors = ""; int64 nStart; InitMessage(_("Loading addresses...")); @@@ -373,13 -390,14 +392,14 @@@ if (nLoadWalletRet != DB_LOAD_OK) { if (nLoadWalletRet == DB_CORRUPT) - strErrors += _("Error loading wallet.dat: Wallet corrupted \n"); + strErrors << _("Error loading wallet.dat: Wallet corrupted") << "\n"; else if (nLoadWalletRet == DB_TOO_NEW) - strErrors += _("Error loading wallet.dat: Wallet requires newer version of PPCoin \n"); - strErrors << _("Error loading wallet.dat: Wallet requires newer version of Bitcoin") << "\n"; ++ strErrors << _("Error loading wallet.dat: Wallet requires newer version of PPCoin") << "\n"; else if (nLoadWalletRet == DB_NEED_REWRITE) { - strErrors += _("Wallet needed to be rewritten: restart PPCoin to complete \n"); - wxMessageBox(strErrors, "PPCoin", wxOK | wxICON_ERROR); - strErrors << _("Wallet needed to be rewritten: restart Bitcoin to complete") << "\n"; ++ strErrors << _("Wallet needed to be rewritten: restart PPCoin to complete") << "\n"; + printf("%s", strErrors.str().c_str()); - ThreadSafeMessageBox(strErrors.str(), _("Bitcoin"), wxOK | wxICON_ERROR | wxMODAL); ++ ThreadSafeMessageBox(strErrors.str(), _("PPCoin"), wxOK | wxICON_ERROR | wxMODAL); return false; } else @@@ -411,16 -460,16 +462,16 @@@ InitMessage(_("Done loading")); printf("Done loading\n"); - //// debug print - printf("mapBlockIndex.size() = %d\n", mapBlockIndex.size()); - printf("nBestHeight = %d\n", nBestHeight); - printf("setKeyPool.size() = %d\n", pwalletMain->setKeyPool.size()); - printf("mapWallet.size() = %d\n", pwalletMain->mapWallet.size()); - printf("mapAddressBook.size() = %d\n", pwalletMain->mapAddressBook.size()); + //// debug print + printf("mapBlockIndex.size() = %d\n", mapBlockIndex.size()); + printf("nBestHeight = %d\n", nBestHeight); + printf("setKeyPool.size() = %d\n", pwalletMain->setKeyPool.size()); + printf("mapWallet.size() = %d\n", pwalletMain->mapWallet.size()); + printf("mapAddressBook.size() = %d\n", pwalletMain->mapAddressBook.size()); - if (!strErrors.empty()) + if (!strErrors.str().empty()) { - wxMessageBox(strErrors, "PPCoin", wxOK | wxICON_ERROR); - ThreadSafeMessageBox(strErrors.str(), _("Bitcoin"), wxOK | wxICON_ERROR | wxMODAL); ++ ThreadSafeMessageBox(strErrors.str(), _("PPCoin"), wxOK | wxICON_ERROR | wxMODAL); return false; } @@@ -469,12 -522,41 +524,41 @@@ if (mapArgs.count("-proxy")) { fUseProxy = true; - addrProxy = CAddress(mapArgs["-proxy"]); + addrProxy = CService(mapArgs["-proxy"], 9050); if (!addrProxy.IsValid()) { - wxMessageBox(_("Invalid -proxy address"), "PPCoin"); - ThreadSafeMessageBox(_("Invalid -proxy address"), _("Bitcoin"), wxOK | wxMODAL); ++ ThreadSafeMessageBox(_("Invalid -proxy address"), _("PPCcoin"), wxOK | wxMODAL); + return false; + } + } + + bool fTor = (fUseProxy && addrProxy.GetPort() == 9050); + if (fTor) + { + // Use SoftSetBoolArg here so user can override any of these if they wish. + // Note: the GetBoolArg() calls for all of these must happen later. + SoftSetBoolArg("-listen", false); + SoftSetBoolArg("-irc", false); + SoftSetBoolArg("-dnsseed", false); + SoftSetBoolArg("-upnp", false); + SoftSetBoolArg("-dns", false); + } + + fAllowDNS = GetBoolArg("-dns"); + fNoListen = !GetBoolArg("-listen", true); + + // Continue to put "/P2SH/" in the coinbase to monitor + // BIP16 support. + // This can be removed eventually... + const char* pszP2SH = "/P2SH/"; + COINBASE_FLAGS << std::vector(pszP2SH, pszP2SH+strlen(pszP2SH)); + + if (!fNoListen) + { + std::string strError; + if (!BindListenPort(strError)) + { - ThreadSafeMessageBox(strError, _("Bitcoin"), wxOK | wxMODAL); ++ ThreadSafeMessageBox(strError, _("PPCoin"), wxOK | wxMODAL); return false; } } @@@ -492,25 -574,13 +576,13 @@@ if (mapArgs.count("-paytxfee")) { - if (!ParseMoney(mapArgs["-paytxfee"], nTransactionFee)) + if (!ParseMoney(mapArgs["-paytxfee"], nTransactionFee) || nTransactionFee < MIN_TX_FEE) { - wxMessageBox(_("Invalid amount for -paytxfee="), "PPCoin"); - ThreadSafeMessageBox(_("Invalid amount for -paytxfee="), _("Bitcoin"), wxOK | wxMODAL); ++ ThreadSafeMessageBox(_("Invalid amount for -paytxfee="), _("PPCoin"), wxOK | wxMODAL); return false; } - nTransactionFee = (nTransactionFee / CENT) * CENT; // round to cent - if (nTransactionFee >= 0.25 * COIN) - wxMessageBox(_("Warning: -paytxfee is set very high. This is the transaction fee you will pay if you send a transaction."), "PPCoin", wxOK | wxICON_EXCLAMATION); - } - - if (fHaveUPnP) - { - #if USE_UPNP - if (GetBoolArg("-noupnp")) - fUseUPnP = false; - #else - if (GetBoolArg("-upnp")) - fUseUPnP = true; - #endif + if (nTransactionFee > 0.25 * COIN) - ThreadSafeMessageBox(_("Warning: -paytxfee is set very high. This is the transaction fee you will pay if you send a transaction."), _("Bitcoin"), wxOK | wxICON_EXCLAMATION | wxMODAL); ++ ThreadSafeMessageBox(_("Warning: -paytxfee is set very high. This is the transaction fee you will pay if you send a transaction."), _("PPCoin"), wxOK | wxICON_EXCLAMATION | wxMODAL); } // @@@ -522,7 -592,7 +594,7 @@@ RandAddSeedPerfmon(); if (!CreateThread(StartNode, NULL)) - wxMessageBox(_("Error: CreateThread(StartNode) failed"), "PPCoin"); - ThreadSafeMessageBox(_("Error: CreateThread(StartNode) failed"), _("Bitcoin"), wxOK | wxMODAL); ++ ThreadSafeMessageBox(_("Error: CreateThread(StartNode) failed"), _("PPCoin"), wxOK | wxMODAL); if (fServer) CreateThread(ThreadRPCServer, NULL); diff --cc src/main.cpp index aaf3f36,9a7ff16..25634df --- a/src/main.cpp +++ b/src/main.cpp @@@ -1,9 -1,8 +1,9 @@@ // Copyright (c) 2009-2010 Satoshi Nakamoto - // Copyright (c) 2011 The Bitcoin developers + // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2011-2012 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying - // file license.txt or http://www.opensource.org/licenses/mit-license.php. - #include "headers.h" + // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include "checkpoints.h" #include "db.h" #include "net.h" @@@ -23,20 -24,16 +25,17 @@@ set setpwalletRegistered CCriticalSection cs_main; - static map mapTransactions; - CCriticalSection cs_mapTransactions; + CTxMemPool mempool; unsigned int nTransactionsUpdated = 0; - map mapNextTx; map mapBlockIndex; -uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); +set > setStakeSeen; +uint256 hashGenesisBlock = hashGenesisBlockOfficial; 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; -CBigNum bnBestInvalidWork = 0; +uint64 nBestChainTrust = 0; +uint64 nBestInvalidTrust = 0; uint256 hashBestChain = 0; CBlockIndex* pindexBest = NULL; int64 nTimeBestReceived = 0; @@@ -45,29 -42,23 +44,25 @@@ CMedianFilter cPeerBlockCounts(5, map mapOrphanBlocks; multimap mapOrphanBlocksByPrev; +set > setStakeSeenOrphan; map mapOrphanTransactions; - multimap mapOrphanTransactionsByPrev; + map > mapOrphanTransactionsByPrev; + + // Constant stuff for coinbase transactions we create: + CScript COINBASE_FLAGS; + const string strMessageMagic = "Bitcoin Signed Message:\n"; double dHashesPerSec; int64 nHPSTimerStart; // Settings - int fGenerateBitcoins = false; - int64 nTransactionFee = MIN_TX_FEE; - int fLimitProcessors = false; - int nLimitProcessors = 1; - int fMinimizeToTray = true; - int fMinimizeOnClose = true; - #if USE_UPNP - int fUseUPnP = true; - #else - int fUseUPnP = false; - #endif + int64 nTransactionFee = 0; +int64 nBalanceReserve = 0; + + ////////////////////////////////////////////////////////////////////////////// // // dispatching functions @@@ -118,18 -109,8 +113,20 @@@ void static EraseFromWallets(uint256 ha } // 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) +void static SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false, bool fConnect = true) { + if (!fConnect) + { + // ppcoin: wallets need to refund inputs when disconnecting coinstake + if (tx.IsCoinStake()) ++ { + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + if (pwallet->IsFromMe(tx)) + pwallet->DisableTransaction(tx); ++ } + return; + } + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate); } @@@ -369,38 -476,28 +495,31 @@@ bool CTxMemPool::accept(CTxDB& txdb, CT if (pfMissingInputs) *pfMissingInputs = false; - if (!CheckTransaction()) - return error("AcceptToMemoryPool() : CheckTransaction failed"); + if (!tx.CheckTransaction()) + return error("CTxMemPool::accept() : CheckTransaction failed"); // Coinbase is only valid in a block, not as a loose transaction - if (IsCoinBase()) - return DoS(100, error("AcceptToMemoryPool() : coinbase as individual tx")); + if (tx.IsCoinBase()) + return tx.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx")); + // ppcoin: coinstake is also only valid in a block, not as a loose transaction - if (IsCoinStake()) - return DoS(100, error("AcceptToMemoryPool() : coinstake as individual tx")); ++ if (tx.IsCoinStake()) ++ return tx.DoS(100, error("CTxMemPool::accept() : coinstake as individual tx")); // To help v0.1.5 clients who would see it as a negative number - if ((int64)nLockTime > INT_MAX) - return error("AcceptToMemoryPool() : not accepting nLockTime beyond 2038 yet"); - - // Safety limits - unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK); - // Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service - // attacks disallow transactions with more than one SigOp per 34 bytes. - // 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() : transaction with out-of-bounds SigOpCount"); + if ((int64)tx.nLockTime > std::numeric_limits::max()) + return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet"); // Rather not work on nonstandard transactions (unless -testnet) - if (!fTestNet && !IsStandard()) - return error("AcceptToMemoryPool() : nonstandard transaction type"); + if (!fTestNet && !tx.IsStandard()) + return error("CTxMemPool::accept() : nonstandard transaction type"); // Do we already have it? - uint256 hash = GetHash(); - CRITICAL_BLOCK(cs_mapTransactions) - if (mapTransactions.count(hash)) + uint256 hash = tx.GetHash(); + { + LOCK(cs); + if (mapTx.count(hash)) return false; + } if (fCheckInputs) if (txdb.ContainsTx(hash)) return false; @@@ -435,19 -532,32 +554,32 @@@ if (fCheckInputs) { - // Check against previous transactions + MapPrevTx mapInputs; map mapUnused; - int64 nFees = 0; - if (!ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false)) + bool fInvalid = false; + if (!tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) { + if (fInvalid) + return error("CTxMemPool::accept() : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str()); if (pfMissingInputs) *pfMissingInputs = true; - return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); + return error("CTxMemPool::accept() : FetchInputs failed %s", hash.ToString().substr(0,10).c_str()); } + // Check for non-standard pay-to-script-hash in inputs + if (!tx.AreInputsStandard(mapInputs) && !fTestNet) + return error("CTxMemPool::accept() : nonstandard transaction input"); + + // Note: if you modify this code to accept non-standard transactions, then + // you should add code here to check that the transaction does a + // reasonable number of ECDSA signature verifications. + + int64 nFees = tx.GetValueIn(mapInputs)-tx.GetValueOut(); + unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + // Don't accept it if it can't get into a block - if (nFees < GetMinFee(1000, false, true)) - return error("AcceptToMemoryPool() : not enough fees"); - if (nFees < tx.GetMinFee(1000, true, GMF_RELAY)) ++ if (nFees < tx.GetMinFee(1000, false, GMF_RELAY)) + return error("CTxMemPool::accept() : not enough fees"); // Continuously rate-limit free transactions // This mitigates 'penny-flooding' -- sending thousands of free transactions just to @@@ -473,6 -583,13 +605,13 @@@ dFreeCount += nSize; } } + + // Check against previous transactions + // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!tx.ConnectInputs(mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false)) ++ if (!tx.ConnectInputs(txdb, mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false)) + { + return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); + } } // Store transaction in memory @@@ -598,10 -720,10 +742,10 @@@ bool CWalletTx::AcceptWalletTransaction // Add previous supporting transactions first BOOST_FOREACH(CMerkleTx& tx, vtxPrev) { - if (!tx.IsCoinBase()) + if (!(tx.IsCoinBase() || tx.IsCoinStake())) { uint256 hash = tx.GetHash(); - if (!mapTransactions.count(hash) && !txdb.ContainsTx(hash)) + if (!mempool.exists(hash) && !txdb.ContainsTx(hash)) tx.AcceptToMemoryPool(txdb, fCheckInputs); } } @@@ -832,6 -943,15 +976,11 @@@ void static InvalidChainFound(CBlockInd printf("InvalidChainFound: WARNING: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.\n"); } + void CBlock::UpdateTime(const CBlockIndex* pindexPrev) + { - nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); - - // Updating time can change work required on testnet: - if (fTestNet) - nBits = GetNextWorkRequired(pindexPrev, this); ++ nTime = max(GetBlockTime(), GetAdjustedTime()); + } + @@@ -876,8 -998,126 +1027,126 @@@ bool CTransaction::DisconnectInputs(CTx } - bool CTransaction::ConnectInputs(CTxDB& txdb, map& mapTestPool, CDiskTxPos posThisTx, - CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee) + bool CTransaction::FetchInputs(CTxDB& txdb, const map& mapTestPool, + bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid) + { + // FetchInputs can return false either because we just haven't seen some inputs + // (in which case the transaction should be stored as an orphan) + // or because the transaction is malformed (in which case the transaction should + // be dropped). If tx is definitely invalid, fInvalid will be set to true. + fInvalid = false; + + if (IsCoinBase()) + return true; // Coinbase transactions have no inputs to fetch. + + for (unsigned int i = 0; i < vin.size(); i++) + { + COutPoint prevout = vin[i].prevout; + if (inputsRet.count(prevout.hash)) + continue; // Got it already + + // Read txindex + CTxIndex& txindex = inputsRet[prevout.hash].first; + bool fFound = true; + if ((fBlock || fMiner) && mapTestPool.count(prevout.hash)) + { + // Get txindex from current proposed changes + txindex = mapTestPool.find(prevout.hash)->second; + } + else + { + // Read txindex from txdb + fFound = txdb.ReadTxIndex(prevout.hash, txindex); + } + if (!fFound && (fBlock || fMiner)) + return fMiner ? false : error("FetchInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); + + // Read txPrev + CTransaction& txPrev = inputsRet[prevout.hash].second; + if (!fFound || txindex.pos == CDiskTxPos(1,1,1)) + { + // Get prev tx from single transactions in memory + { + LOCK(mempool.cs); + if (!mempool.exists(prevout.hash)) + return error("FetchInputs() : %s mempool Tx prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); + txPrev = mempool.lookup(prevout.hash); + } + if (!fFound) + txindex.vSpent.resize(txPrev.vout.size()); + } + else + { + // Get prev tx from disk + if (!txPrev.ReadFromDisk(txindex.pos)) + return error("FetchInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); + } + } + + // Make sure all prevout.n's are valid: + for (unsigned int i = 0; i < vin.size(); i++) + { + const COutPoint prevout = vin[i].prevout; + assert(inputsRet.count(prevout.hash) != 0); + const CTxIndex& txindex = inputsRet[prevout.hash].first; + const CTransaction& txPrev = inputsRet[prevout.hash].second; + if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) + { + // Revisit this if/when transaction replacement is implemented and allows + // adding inputs: + fInvalid = true; + 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 true; + } + + const CTxOut& CTransaction::GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const + { + MapPrevTx::const_iterator mi = inputs.find(input.prevout.hash); + if (mi == inputs.end()) + throw std::runtime_error("CTransaction::GetOutputFor() : prevout.hash not found"); + + const CTransaction& txPrev = (mi->second).second; + if (input.prevout.n >= txPrev.vout.size()) + throw std::runtime_error("CTransaction::GetOutputFor() : prevout.n out of range"); + + return txPrev.vout[input.prevout.n]; + } + + int64 CTransaction::GetValueIn(const MapPrevTx& inputs) const + { + if (IsCoinBase()) + return 0; + + int64 nResult = 0; + for (unsigned int i = 0; i < vin.size(); i++) + { + nResult += GetOutputFor(vin[i], inputs).nValue; + } + return nResult; + + } + + unsigned int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const + { + if (IsCoinBase()) + return 0; + + unsigned int nSigOps = 0; + for (unsigned int i = 0; i < vin.size(); i++) + { + const CTxOut& prevout = GetOutputFor(vin[i], inputs); + if (prevout.scriptPubKey.IsPayToScriptHash()) + nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig); + } + return nSigOps; + } + -bool CTransaction::ConnectInputs(MapPrevTx inputs, ++bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs, + map& mapTestPool, const CDiskTxPos& posThisTx, + const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash) { // Take over previous transactions' spent pointers // fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain @@@ -930,23 -1137,27 +1166,31 @@@ if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) 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()) + // If prev is coinbase/coinstake, check that it's matured + if (txPrev.IsCoinBase() || txPrev.IsCoinStake()) - for (CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < COINBASE_MATURITY; pindex = pindex->pprev) + for (const CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < COINBASE_MATURITY; pindex = pindex->pprev) if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile) - return error("ConnectInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - pindex->nHeight); + return error("ConnectInputs() : tried to spend coinbase/coinstake at depth %d", pindexBlock->nHeight - pindex->nHeight); + + // ppcoin: check transaction timestamp + if (txPrev.nTime > nTime) + return DoS(100, error("ConnectInputs() : transaction timestamp earlier than input transaction")); - // Skip ECDSA signature verification when connecting blocks (fBlock=true) during initial download - // (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 && IsInitialBlockDownload())) - // Verify signature - if (!VerifySignature(txPrev, *this, i)) - return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).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 DoS(100, error("ConnectInputs() : txin values out of range")); + + } + // The first loop above does all the inexpensive checks. + // Only if ALL inputs pass do we perform expensive ECDSA signature checks. + // Helps prevent CPU exhaustion attacks. + for (unsigned int i = 0; i < vin.size(); i++) + { + COutPoint prevout = vin[i].prevout; + assert(inputs.count(prevout.hash) > 0); + CTxIndex& txindex = inputs[prevout.hash].first; + CTransaction& txPrev = inputs[prevout.hash].second; // Check for conflicts (double-spend) // This doesn't trigger the DoS code on purpose; if it did, it would make it easier @@@ -969,45 -1192,18 +1225,34 @@@ } } - if (nValueIn < GetValueOut()) - return DoS(100, error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str())); + if (IsCoinStake()) + { + // ppcoin: coin stake tx earns reward instead of paying fee + uint64 nCoinAge; + if (!GetCoinAge(txdb, nCoinAge)) + return error("ConnectInputs() : %s unable to get coin age for coinstake", GetHash().ToString().substr(0,10).c_str()); + int64 nStakeReward = GetValueOut() - nValueIn; + if (nStakeReward > GetProofOfStakeReward(nCoinAge)) + return DoS(100, error("ConnectInputs() : %s stake reward exceeded", GetHash().ToString().substr(0,10).c_str())); + } + else + { + if (nValueIn < GetValueOut()) + 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 DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str())); - nFees += nTxFee; - if (!MoneyRange(nFees)) - return DoS(100, error("ConnectInputs() : nFees out of range")); + // Tally transaction fees + int64 nTxFee = nValueIn - GetValueOut(); + if (nTxFee < 0) + return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str())); + // ppcoin: enforce transaction fees for every block - if (nTxFee < nMinFee) - return fBlock? DoS(100, error("ConnectInputs() : %s not paying required fee=%s, paid=%s", GetHash().ToString().substr(0,10).c_str(), FormatMoney(nMinFee).c_str(), FormatMoney(nTxFee).c_str())) : false; ++ if (nTxFee < GetMinFee()) ++ return fBlock? DoS(100, error("ConnectInputs() : %s not paying required fee=%s, paid=%s", GetHash().ToString().substr(0,10).c_str(), FormatMoney(GetMinFee()).c_str(), FormatMoney(nTxFee).c_str())) : false; + nFees += nTxFee; + if (!MoneyRange(nFees)) + return DoS(100, error("ConnectInputs() : nFees out of range")); + } } - if (fBlock) - { - // Add transaction to changes - mapTestPool[GetHash()] = CTxIndex(posThisTx, vout.size()); - } - else if (fMiner) - { - // Add transaction to test pool - mapTestPool[GetHash()] = CTxIndex(CDiskTxPos(1,1,1), vout.size()); - } - return true; } @@@ -1090,19 -1283,75 +1336,75 @@@ bool CBlock::ConnectBlock(CTxDB& txdb, if (!CheckBlock()) return false; + // Do not allow blocks that contain transactions which 'overwrite' older transactions, + // unless those are already completely spent. + // If such overwrites are allowed, coinbases and transactions depending upon those + // can be duplicated to remove the ability to spend the first instance -- even after + // being sent to another address. + // See BIP30 and http://r6.ca/blog/20120206T005236Z.html for more information. + // This logic is not necessary for memory pool transactions, as AcceptToMemoryPool + // already refuses previously-known transaction id's entirely. + // This rule applies to all blocks whose timestamp is after March 15, 2012, 0:00 UTC. + // On testnet it is enabled as of februari 20, 2012, 0:00 UTC. + if (pindex->nTime > 1331769600 || (fTestNet && pindex->nTime > 1329696000)) + { + BOOST_FOREACH(CTransaction& tx, vtx) + { + CTxIndex txindexOld; + if (txdb.ReadTxIndex(tx.GetHash(), txindexOld)) + { + BOOST_FOREACH(CDiskTxPos &pos, txindexOld.vSpent) + if (pos.IsNull()) + return false; + } + } + } + + // BIP16 didn't become active until Apr 1 2012 (Feb 15 on testnet) + int64 nBIP16SwitchTime = fTestNet ? 1329264000 : 1333238400; + bool fStrictPayToScriptHash = (pindex->nTime >= nBIP16SwitchTime); + //// issue here: it doesn't know the version - unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK) - (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(vtx.size()); - unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - 1 + GetSizeOfCompactSize(vtx.size()); ++ unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(vtx.size()); map mapQueuedChanges; int64 nFees = 0; + unsigned int nSigOps = 0; BOOST_FOREACH(CTransaction& tx, vtx) { + nSigOps += tx.GetLegacySigOpCount(); + if (nSigOps > MAX_BLOCK_SIGOPS) + return DoS(100, error("ConnectBlock() : too many sigops")); + CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos); - nTxPos += ::GetSerializeSize(tx, SER_DISK); + nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); - if (!tx.ConnectInputs(txdb, mapQueuedChanges, posThisTx, pindex, nFees, true, false, tx.GetMinFee())) - return false; + MapPrevTx mapInputs; - if (!tx.IsCoinBase()) ++ if (!(tx.IsCoinBase() || tx.IsCoinStake())) + { + bool fInvalid; + if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs, fInvalid)) + return false; + + if (fStrictPayToScriptHash) + { + // Add in sigops done by pay-to-script-hash inputs; + // this is to prevent a "rogue miner" from creating + // an incredibly-expensive-to-validate block. + nSigOps += tx.GetP2SHSigOpCount(mapInputs); + if (nSigOps > MAX_BLOCK_SIGOPS) + return DoS(100, error("ConnectBlock() : too many sigops")); + } + + nFees += tx.GetValueIn(mapInputs)-tx.GetValueOut(); + - if (!tx.ConnectInputs(mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fStrictPayToScriptHash)) ++ if (!tx.ConnectInputs(txdb, mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fStrictPayToScriptHash)) + return false; + } + + mapQueuedChanges[tx.GetHash()] = CTxIndex(posThisTx, tx.vout.size()); } + // Write queued txindex changes for (map::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi) { @@@ -1241,27 -1526,30 +1583,30 @@@ bool CBlock::SetBestChain(CTxDB& txdb, } else if (hashPrevBlock == hashBestChain) { - // Adding to current best branch - if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash)) + if (!SetBestChainInner(txdb, pindexNew)) + return error("SetBestChain() : SetBestChainInner failed"); + } + else + { + // the first block in the new chain that will cause it to become the new best chain + CBlockIndex *pindexIntermediate = pindexNew; + + // list of blocks that need to be connected afterwards + std::vector vpindexSecondary; + + // Reorganize is costly in terms of db load, as it works in a single db transaction. + // Try to limit how much needs to be done inside - while (pindexIntermediate->pprev && pindexIntermediate->pprev->bnChainWork > pindexBest->bnChainWork) ++ while (pindexIntermediate->pprev && pindexIntermediate->pprev->nChainTrust > pindexBest->nChainTrust) { - txdb.TxnAbort(); - InvalidChainFound(pindexNew); - return error("SetBestChain() : ConnectBlock failed"); + vpindexSecondary.push_back(pindexIntermediate); + pindexIntermediate = pindexIntermediate->pprev; } - if (!txdb.TxnCommit()) - return error("SetBestChain() : TxnCommit failed"); - // Add to current best branch - pindexNew->pprev->pnext = pindexNew; + if (!vpindexSecondary.empty()) + printf("Postponing %i reconnects\n", vpindexSecondary.size()); - // Delete redundant memory transactions - BOOST_FOREACH(CTransaction& tx, vtx) - tx.RemoveFromMemoryPool(); - } - else - { - // New best branch - if (!Reorganize(txdb, pindexNew)) + // Switch to new best branch + if (!Reorganize(txdb, pindexIntermediate)) { txdb.TxnAbort(); InvalidChainFound(pindexNew); @@@ -1280,147 -1587,23 +1644,155 @@@ hashBestChain = hash; pindexBest = pindexNew; nBestHeight = pindexBest->nHeight; - bnBestChainWork = pindexNew->bnChainWork; + nBestChainTrust = pindexNew->nChainTrust; nTimeBestReceived = GetTime(); nTransactionsUpdated++; - printf("SetBestChain: new best=%s height=%d work=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str()); + printf("SetBestChain: new best=%s height=%d trust=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(nBestChainTrust).ToString().c_str()); + std::string strCmd = GetArg("-blocknotify", ""); + + if (!fIsInitialDownload && !strCmd.empty()) + { + boost::replace_all(strCmd, "%s", hashBestChain.GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } + return true; } +// ppcoin: coinstake must meet hash target according to the protocol: +// input 0 must meet the formula +// hash(nBits + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime) < bnTarget * nCoinDay +// this ensures that the chance of getting a coinstake is proportional to the +// amount of coin age one owns. +// The reason this hash is chosen is the following: +// nBits: encodes all past block timestamps, making computing hash in advance +// more difficult +// txPrev.block.nTime: prevent nodes from guessing a good timestamp to +// generate transaction for future advantage +// txPrev.offset: offset of txPrev inside block, to reduce the chance of +// nodes generating coinstake at the same time +// txPrev.nTime: reduce the chance of nodes generating coinstake at the same +// time +// txPrev.vout.n: output number of txPrev, to reduce the chance of nodes +// generating coinstake at the same time +// block/tx hash should not be used here as they can be generated in vast +// quantities so as to generate blocks faster, degrading the system back into +// a proof-of-work situation. +// +bool CTransaction::CheckProofOfStake(unsigned int nBits) const +{ + CBigNum bnTargetPerCoinDay; + bnTargetPerCoinDay.SetCompact(nBits); + + if (!IsCoinStake()) + return true; + + // Input 0 must match the stake hash target per coin age (nBits) + const CTxIn& txin = vin[0]; + + // First try finding the previous transaction in database + CTxDB txdb("r"); + CTransaction txPrev; + CTxIndex txindex; + if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex)) + return false; // previous transaction not in main chain + txdb.Close(); + if (nTime < txPrev.nTime) + return false; // Transaction timestamp violation + + // Verify signature - if (!VerifySignature(txPrev, *this, 0)) ++ if (!VerifySignature(txPrev, *this, 0, true, 0)) + return DoS(100, error("CheckProofOfStake() : VerifySignature failed on coinstake %s", GetHash().ToString().c_str())); + + // Read block header + CBlock block; + if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) + return false; // unable to read block of previous transaction + if (block.GetBlockTime() + STAKE_MIN_AGE > nTime) + return false; // only count coins meeting min age requirement + + int64 nValueIn = txPrev.vout[txin.prevout.n].nValue; + CBigNum bnCoinDay = CBigNum(nValueIn) * (nTime-txPrev.nTime) / COIN / (24 * 60 * 60); + // Calculate hash - CDataStream ss(SER_GETHASH, VERSION); ++ CDataStream ss(SER_GETHASH, 0); + ss << nBits << block.nTime << (txindex.pos.nTxPos - txindex.pos.nBlockPos) << txPrev.nTime << txin.prevout.n << nTime; + if (CBigNum(Hash(ss.begin(), ss.end())) <= bnCoinDay * bnTargetPerCoinDay) + return true; + else + return DoS(100, error("CheckProofOfStake() : check target failed on coinstake %s", GetHash().ToString().c_str())); +} + +// ppcoin: total coin age spent in transaction, in the unit of coin-days. +// Only those coins meeting minimum age requirement counts. As those +// transactions not in main chain are not currently indexed so we +// might not find out about their coin age. Older transactions are +// guaranteed to be in main chain by sync-checkpoint. This rule is +// introduced to help nodes establish a consistent view of the coin +// age (trust score) of competing branches. +bool CTransaction::GetCoinAge(CTxDB& txdb, uint64& nCoinAge) const +{ + CBigNum bnCentSecond = 0; // coin age in the unit of cent-seconds + nCoinAge = 0; + + if (IsCoinBase()) + return true; + + BOOST_FOREACH(const CTxIn& txin, vin) + { + // First try finding the previous transaction in database + CTransaction txPrev; + CTxIndex txindex; + if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex)) + continue; // previous transaction not in main chain + if (nTime < txPrev.nTime) + return false; // Transaction timestamp violation + + // Read block header + CBlock block; + if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) + return false; // unable to read block of previous transaction + if (block.GetBlockTime() + STAKE_MIN_AGE > nTime) + continue; // only count coins meeting min age requirement + + int64 nValueIn = txPrev.vout[txin.prevout.n].nValue; + bnCentSecond += CBigNum(nValueIn) * (nTime-txPrev.nTime) / CENT; + + if (fDebug && GetBoolArg("-printcoinage")) + printf("coin age nValueIn=%-12I64d nTimeDiff=%d bnCentSecond=%s\n", nValueIn, nTime - txPrev.nTime, bnCentSecond.ToString().c_str()); + } + + CBigNum bnCoinDay = bnCentSecond * CENT / COIN / (24 * 60 * 60); + if (fDebug && GetBoolArg("-printcoinage")) + printf("coin age bnCoinDay=%s\n", bnCoinDay.ToString().c_str()); + nCoinAge = bnCoinDay.getuint64(); + return true; +} + +// ppcoin: total coin age spent in block, in the unit of coin-days. +bool CBlock::GetCoinAge(uint64& nCoinAge) const +{ + nCoinAge = 0; + + CTxDB txdb("r"); + BOOST_FOREACH(const CTransaction& tx, vtx) + { + uint64 nTxCoinAge; + if (tx.GetCoinAge(txdb, nTxCoinAge)) + nCoinAge += nTxCoinAge; + else + return false; + } + + if (nCoinAge == 0) // block coin age minimum 1 coin-day + nCoinAge = 1; + if (fDebug && GetBoolArg("-printcoinage")) + printf("block coin age total nCoinDays=%"PRI64d"\n", nCoinAge); + return true; +} + + bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) { // Check for duplicate @@@ -1443,15 -1623,11 +1815,16 @@@ pindexNew->pprev = (*miPrev).second; pindexNew->nHeight = pindexNew->pprev->nHeight + 1; } - pindexNew->bnChainWork = (pindexNew->pprev ? pindexNew->pprev->bnChainWork : 0) + pindexNew->GetBlockWork(); + + // ppcoin: compute chain trust score + uint64 nCoinAge; + if (!GetCoinAge(nCoinAge)) + return error("AddToBlockIndex() : invalid transaction in block"); + pindexNew->nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + nCoinAge; CTxDB txdb; - txdb.TxnBegin(); + if (!txdb.TxnBegin()) + return false; txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew)); if (!txdb.TxnCommit()) return false; @@@ -1502,35 -1678,27 +1875,49 @@@ bool CBlock::CheckBlock() cons if (vtx[i].IsCoinBase()) return DoS(100, error("CheckBlock() : more than one coinbase")); + // ppcoin: only the second transaction can be the optional coinstake + for (int i = 2; i < vtx.size(); i++) + if (vtx[i].IsCoinStake()) + return DoS(100, error("CheckBlock() : coinstake in wrong position")); + + // ppcoin: coinbase output should be empty if proof-of-stake block + if (IsProofOfStake() && !vtx[0].vout[0].IsEmpty()) + return error("CheckBlock() : coinbase output not empty for proof-of-stake block"); + + // Check coinbase timestamp + if (GetBlockTime() > (int64)vtx[0].nTime + nMaxClockDrift) + return DoS(50, error("CheckBlock() : coinbase timestamp is too early")); + + // Check coinstake timestamp + if (IsProofOfStake() && GetBlockTime() > (int64)vtx[1].nTime + nMaxClockDrift) + return DoS(50, error("CheckBlock() : coinstake timestamp is too early")); + // Check transactions BOOST_FOREACH(const CTransaction& tx, vtx) + { if (!tx.CheckTransaction()) return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); + // ppcoin: check transaction timestamp + if (GetBlockTime() < (int64)tx.nTime) + return DoS(50, error("CheckBlock() : block timestamp earlier than transaction timestamp")); + } - // Check that it's not full of nonstandard transactions - if (GetSigOpCount() > MAX_BLOCK_SIGOPS) + // Check for duplicate txids. This is caught by ConnectInputs(), + // but catching it earlier avoids a potential DoS attack: + set uniqueTx; + BOOST_FOREACH(const CTransaction& tx, vtx) + { + uniqueTx.insert(tx.GetHash()); + } + if (uniqueTx.size() != vtx.size()) + return DoS(100, error("CheckBlock() : duplicate transaction")); + + unsigned int nSigOps = 0; + BOOST_FOREACH(const CTransaction& tx, vtx) + { + nSigOps += tx.GetLegacySigOpCount(); + } + if (nSigOps > MAX_BLOCK_SIGOPS) return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); // Check merkleroot @@@ -1571,16 -1735,12 +1958,16 @@@ bool CBlock::AcceptBlock( if (!tx.IsFinal(nHeight, GetBlockTime())) 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 DoS(100, error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight)); + // Check that the block chain matches the known block chain up to a hardened checkpoint + if (!Checkpoints::CheckHardened(nHeight, hash)) + return DoS(100, error("AcceptBlock() : rejected by hardened checkpoint lockin at %d", nHeight)); + + // ppcoin: check that the block satisfies synchronized checkpoint + if (!Checkpoints::CheckSync(hash, pindexPrev)) + return error("AcceptBlock() : rejected by synchronized checkpoint"); // Write block to history file - if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK))) + if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION))) return error("AcceptBlock() : out of disk space"); unsigned int nFile = -1; unsigned int nBlockPos = 0; @@@ -1590,15 -1750,15 +1977,18 @@@ return error("AcceptBlock() : AddToBlockIndex failed"); // Relay inventory, but don't relay old inventory during initial block download + int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); if (hashBestChain == hash) - CRITICAL_BLOCK(cs_vNodes) - BOOST_FOREACH(CNode* pnode, vNodes) - if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 140700)) - pnode->PushInventory(CInv(MSG_BLOCK, hash)); + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) + pnode->PushInventory(CInv(MSG_BLOCK, hash)); + } + // ppcoin: check pending sync-checkpoint + Checkpoints::AcceptPendingSyncCheckpoint(); + return true; } @@@ -1633,12 -1783,18 +2023,13 @@@ bool ProcessBlock(CNode* pfrom, CBlock CBigNum bnNewBlock; bnNewBlock.SetCompact(pblock->nBits); CBigNum bnRequired; - bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime)); + bnRequired.SetCompact(ComputeMinWork(GetLastBlockIndex(pcheckpoint, pblock->IsProofOfStake())->nBits, deltaTime)); + if (bnNewBlock > bnRequired) { - pfrom->Misbehaving(100); + if (pfrom) + pfrom->Misbehaving(100); - return error("ProcessBlock() : block with too little proof-of-work"); + return error("ProcessBlock() : block with too little %s", pblock->IsProofOfStake()? "proof-of-stake" : "proof-of-work"); } } @@@ -1703,7 -1840,7 +2094,53 @@@ return true; } ++// ppcoin: sign block ++bool CBlock::SignBlock(const CKeyStore& keystore) ++{ ++ vector vSolutions; ++ txnouttype whichType; ++ const CTxOut& txout = IsProofOfStake()? vtx[1].vout[1] : vtx[0].vout[0]; ++ if (!Solver(txout.scriptPubKey, whichType, vSolutions)) ++ return false; ++ if (whichType == TX_PUBKEY) ++ { ++ // Sign ++ const valtype& vchPubKey = vSolutions[0]; ++ CKey key; ++ if (!keystore.GetKey(Hash160(vchPubKey), key)) ++ return false; ++ if (key.GetPubKey() != vchPubKey) ++ return false; ++ return key.Sign(GetHash(), vchBlockSig); ++ } ++ return false; ++} ++ ++// ppcoin: check block signature ++bool CBlock::CheckBlockSignature() const ++{ ++ if (GetHash() == hashGenesisBlock) ++ return vchBlockSig.empty(); ++ ++ vector vSolutions; ++ txnouttype whichType; ++ const CTxOut& txout = IsProofOfStake()? vtx[1].vout[1] : vtx[0].vout[0]; ++ ++ if (!Solver(txout.scriptPubKey, whichType, vSolutions)) ++ return false; ++ if (whichType == TX_PUBKEY) ++ { ++ const valtype& vchPubKey = vSolutions[0]; ++ CKey key; ++ if (!key.SetPubKey(vchPubKey)) ++ return false; ++ if (vchBlockSig.empty()) ++ return false; ++ return key.Verify(GetHash(), vchBlockSig); ++ } ++ return false; ++} @@@ -1985,15 -2098,9 +2422,15 @@@ string GetWarnings(string strFor strStatusBar = strRPC = "WARNING: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade."; } + if (Checkpoints::hashInvalidCheckpoint != 0) + { + nPriority = 3000; + strStatusBar = strRPC = "WARNING: Invalid checkpoint found! Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade."; + } + // Alerts - CRITICAL_BLOCK(cs_mapAlerts) { + LOCK(cs_mapAlerts); BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) { const CAlert& alert = item.second; @@@ -2193,15 -2321,12 +2655,19 @@@ bool static ProcessMessage(CNode* pfrom } // Relay alerts - CRITICAL_BLOCK(cs_mapAlerts) + { + LOCK(cs_mapAlerts); BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) item.second.RelayTo(pfrom); + } + // ppcoin: relay sync-checkpoint - CRITICAL_BLOCK(Checkpoints::cs_hashSyncCheckpoint) ++ { ++ LOCK(Checkpoints::cs_hashSyncCheckpoint); + if (!Checkpoints::checkpointMessage.IsNull()) + Checkpoints::checkpointMessage.RelayTo(pfrom); ++ } + pfrom->fSuccessfullyConnected = true; printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight); @@@ -2619,20 -2774,6 +3119,20 @@@ } } + else if (strCommand == "checkpoint") + { + CSyncCheckpoint checkpoint; + vRecv >> checkpoint; + + if (checkpoint.ProcessSyncCheckpoint(pfrom)) + { + // Relay + pfrom->hashCheckpointKnown = checkpoint.hashCheckpoint; - CRITICAL_BLOCK(cs_vNodes) - BOOST_FOREACH(CNode* pnode, vNodes) - checkpoint.RelayTo(pnode); ++ LOCK(cs_vNodes); ++ BOOST_FOREACH(CNode* pnode, vNodes) ++ checkpoint.RelayTo(pnode); + } + } else { @@@ -3059,9 -3177,12 +3536,12 @@@ public }; + uint64 nLastBlockTx = 0; + uint64 nLastBlockSize = 0; + -CBlock* CreateNewBlock(CReserveKey& reservekey) +CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfWorkOnly) { - CBlockIndex* pindexPrev = pindexBest; + CReserveKey reservekey(pwallet); // Create new block auto_ptr pblock(new CBlock()); @@@ -3078,51 -3199,20 +3558,51 @@@ // Add our coinbase tx as first transaction pblock->vtx.push_back(txNew); + // ppcoin: if coinstake available add coinstake tx + static unsigned int nLastCoinStakeCheckTime = GetAdjustedTime() - nMaxClockDrift + 60; // only initialized at startup + CBlockIndex* pindexPrev = pindexBest; + + if (!fProofOfWorkOnly) + { + while (nLastCoinStakeCheckTime < GetAdjustedTime()) + { + pindexPrev = pindexBest; // get best block again to avoid getting stale + pblock->nBits = GetNextTargetRequired(pindexPrev, true); - static CCriticalSection cs; + CTransaction txCoinStake; - CRITICAL_BLOCK(cs) + { ++ static CCriticalSection cs; ++ LOCK(cs); + // mining may have been suspended for a while so + // need to take max to satisfy the timestamp protocol - nLastCoinStakeCheckTime = max(++nLastCoinStakeCheckTime, (unsigned int) (GetAdjustedTime() - nMaxClockDrift + 60)); ++ nLastCoinStakeCheckTime++; ++ nLastCoinStakeCheckTime = max(nLastCoinStakeCheckTime, (unsigned int) (GetAdjustedTime() - nMaxClockDrift + 60)); + txCoinStake.nTime = nLastCoinStakeCheckTime; + } + if (pwallet->CreateCoinStake(pblock->nBits, txCoinStake)) + { + pblock->vtx.push_back(txCoinStake); + pblock->vtx[0].vout[0].SetEmpty(); + break; + } + } + } + + pblock->nBits = GetNextTargetRequired(pindexPrev, pblock->IsProofOfStake()); + // Collect memory pool transactions into the block int64 nFees = 0; - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapTransactions) { + LOCK2(cs_main, mempool.cs); CTxDB txdb("r"); // Priority order to process transactions list vOrphan; // list memory doesn't move map > mapDependers; multimap mapPriority; - for (map::iterator mi = mapTransactions.begin(); mi != mapTransactions.end(); ++mi) + for (map::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi) { CTransaction& tx = (*mi).second; - if (tx.IsCoinBase() || !tx.IsFinal()) + if (tx.IsCoinBase() || tx.IsCoinStake() || !tx.IsFinal()) continue; COrphan* porphan = NULL; @@@ -3180,7 -3271,7 +3661,6 @@@ while (!mapPriority.empty()) { // Take highest priority transaction off priority queue -- double dPriority = -(*mapPriority.begin()).first; CTransaction& tx = *(*mapPriority.begin()).second; mapPriority.erase(mapPriority.begin()); @@@ -3192,18 -3285,29 +3674,32 @@@ if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) continue; - // Transaction fee required depends on block size - bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority)); - int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree, GMF_BLOCK); + // Timestamp limit + if (tx.nTime > GetAdjustedTime()) + continue; + + // ppcoin: simplify transaction fee - allow free = false - int64 nMinFee = tx.GetMinFee(nBlockSize, false, true); ++ int64 nMinFee = tx.GetMinFee(nBlockSize, false, GMF_BLOCK); // Connecting shouldn't fail due to dependency on other memory pool transactions // because we're already processing them in order of dependency map mapTestPoolTmp(mapTestPool); - if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee)) + MapPrevTx mapInputs; + bool fInvalid; + if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs, fInvalid)) + continue; + + int64 nTxFees = tx.GetValueIn(mapInputs)-tx.GetValueOut(); + if (nTxFees < nMinFee) + continue; + + nTxSigOps += tx.GetP2SHSigOpCount(mapInputs); + if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) + continue; + - if (!tx.ConnectInputs(mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, false, true)) ++ if (!tx.ConnectInputs(txdb, mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, false, true)) continue; + mapTestPoolTmp[tx.GetHash()] = CTxIndex(CDiskTxPos(1,1,1), tx.vout.size()); swap(mapTestPool, mapTestPoolTmp); // Added @@@ -3226,16 -3332,19 +3724,22 @@@ } } } + + nLastBlockTx = nBlockTx; + nLastBlockSize = nBlockSize; + printf("CreateNewBlock(): total size %lu\n", nBlockSize); + } - pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees); + if (pblock->IsProofOfWork()) + pblock->vtx[0].vout[0].nValue = GetProofOfWorkReward(pblock->nBits); // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + pblock->nTime = max(pblock->GetBlockTime(), pblock->GetMaxTransactionTime()); + pblock->nTime = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - nMaxClockDrift); + pblock->UpdateTime(pindexPrev); - pblock->nBits = GetNextWorkRequired(pindexPrev, pblock.get()); pblock->nNonce = 0; return pblock.release(); @@@ -3498,12 -3591,14 +4006,13 @@@ void static BitcoinMiner(CWallet *pwall break; // Update nTime every few seconds + pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + pblock->nTime = max(pblock->GetBlockTime(), pblock->GetMaxTransactionTime()); + pblock->nTime = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - nMaxClockDrift); + pblock->UpdateTime(pindexPrev); nBlockTime = ByteReverse(pblock->nTime); - if (fTestNet) - { - // Changing pblock->nTime can change work required on testnet: - nBlockBits = ByteReverse(pblock->nBits); - hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); - } + if (pblock->GetBlockTime() >= (int64)pblock->vtx[0].nTime + nMaxClockDrift) + break; // need to update coinbase timestamp } } } diff --cc src/main.h index 2811ec1,241ef46..b1d136f --- a/src/main.h +++ b/src/main.h @@@ -1,8 -1,7 +1,8 @@@ // Copyright (c) 2009-2010 Satoshi Nakamoto - // Copyright (c) 2011 The Bitcoin developers + // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2011-2012 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying - // file license.txt or http://www.opensource.org/licenses/mit-license.php. + // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_MAIN_H #define BITCOIN_MAIN_H @@@ -14,13 -15,11 +16,12 @@@ #include + class CWallet; class CBlock; class CBlockIndex; - class CWalletTx; - class CWallet; class CKeyItem; class CReserveKey; - class CWalletDB; ++class COutPoint; class CAddress; class CInv; @@@ -29,13 -28,11 +30,11 @@@ class CNode static const unsigned int MAX_BLOCK_SIZE = 1000000; static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2; - static const int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; - static const int64 COIN = 1000000; - static const int64 CENT = 10000; + static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; + static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100; -static const int64 MIN_TX_FEE = 50000; +static const int64 MIN_TX_FEE = 10000; static const int64 MIN_RELAY_TX_FEE = 10000; -static const int64 MAX_MONEY = 21000000 * COIN; +static const int64 MAX_MONEY = 2000000000 * COIN; inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } static const int COINBASE_MATURITY = 100; // Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp. @@@ -46,9 -43,10 +45,12 @@@ static const int fHaveUPnP = true static const int fHaveUPnP = false; #endif +static const uint256 hashGenesisBlockOfficial("0x000000007c82d1f0aa2896b01bf533a8cc26a1f44790be4ceb4ecde7bee24add"); +static const uint256 hashGenesisBlockTestNet("0x00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"); + extern CScript COINBASE_FLAGS; + + @@@ -69,17 -69,9 +74,11 @@@ extern int64 nHPSTimerStart extern int64 nTimeBestReceived; extern CCriticalSection cs_setpwalletRegistered; extern std::set setpwalletRegistered; +extern std::map mapOrphanBlocks; // Settings - extern int fGenerateBitcoins; extern int64 nTransactionFee; - extern int fLimitProcessors; - extern int nLimitProcessors; - extern int fMinimizeToTray; - extern int fMinimizeOnClose; - extern int fUseUPnP; +extern int64 nBalanceReserve; @@@ -366,17 -339,6 +349,17 @@@ public return (nValue == -1); } - bool SetEmpty() ++ void SetEmpty() + { + nValue = 0; + scriptPubKey.clear(); + } + + bool IsEmpty() const + { + return (nValue == 0 && scriptPubKey.empty()); + } + uint256 GetHash() const { return SerializeHash(*this); @@@ -510,36 -476,38 +501,44 @@@ public bool IsCoinBase() const { - return (vin.size() == 1 && vin[0].prevout.IsNull()); + return (vin.size() == 1 && vin[0].prevout.IsNull() && vout.size() == 1); + } + + bool IsCoinStake() const + { + // ppcoin: the coin stake transaction is marked with the first output empty + return (vin.size() > 0 && vout.size() == 2 && vout[0].IsEmpty()); } - int GetSigOpCount() const - { - int n = 0; - BOOST_FOREACH(const CTxIn& txin, vin) - n += txin.scriptSig.GetSigOpCount(); - BOOST_FOREACH(const CTxOut& txout, vout) - n += txout.scriptPubKey.GetSigOpCount(); - return n; - } + /** Check for standard transaction types + @return True if all outputs (scriptPubKeys) use only standard transaction forms + */ + bool IsStandard() const; - bool IsStandard() const - { - BOOST_FOREACH(const CTxIn& txin, vin) - if (!txin.scriptSig.IsPushOnly()) - return error("nonstandard txin: %s", txin.scriptSig.ToString().c_str()); - BOOST_FOREACH(const CTxOut& txout, vout) - if (!::IsStandard(txout.scriptPubKey)) - return error("nonstandard txout: %s", txout.scriptPubKey.ToString().c_str()); - return true; - } + /** Check for standard transaction types + @param[in] mapInputs Map of previous transactions that have outputs we're spending + @return True if all inputs (scriptSigs) use only standard transaction forms + @see CTransaction::FetchInputs + */ + bool AreInputsStandard(const MapPrevTx& mapInputs) const; + + /** Count ECDSA signature operations the old-fashioned (pre-0.6) way + @return number of sigops this transaction's outputs will produce when spent + @see CTransaction::FetchInputs + */ + unsigned int GetLegacySigOpCount() const; + /** Count ECDSA signature operations in pay-to-script-hash inputs. + + @param[in] mapInputs Map of previous transactions that have outputs we're spending + @return maximum number of sigops required to validate this transaction's inputs + @see CTransaction::FetchInputs + */ + unsigned int GetP2SHSigOpCount(const MapPrevTx& mapInputs) const; + + /** Amount of bitcoins spent by this transaction. + @return sum of all outputs (note: does not include fees) + */ int64 GetValueOut() const { int64 nValueOut = 0; @@@ -559,12 -537,12 +568,12 @@@ return dPriority > COIN * 144 / 250; } - int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=false, bool fForRelay=false) const - int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=true, enum GetMinFee_mode mode=GMF_BLOCK) const ++ int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=false, enum GetMinFee_mode mode=GMF_BLOCK) const { // Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE - int64 nBaseFee = fForRelay ? MIN_RELAY_TX_FEE : MIN_TX_FEE; + int64 nBaseFee = (mode == GMF_RELAY) ? MIN_RELAY_TX_FEE : MIN_TX_FEE; - unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK); + unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION); unsigned int nNewBlockSize = nBlockSize + nBytes; int64 nMinFee = (1 + (int64)nBytes / 1000) * nBaseFee; @@@ -669,18 -652,41 +686,43 @@@ bool ReadFromDisk(CTxDB& txdb, COutPoint prevout); bool ReadFromDisk(COutPoint prevout); bool DisconnectInputs(CTxDB& txdb); - bool ConnectInputs(CTxDB& txdb, std::map& mapTestPool, CDiskTxPos posThisTx, - CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee=0); + + /** Fetch from memory and/or disk. inputsRet keys are transaction hashes. + + @param[in] txdb Transaction database + @param[in] mapTestPool List of pending changes to the transaction index database + @param[in] fBlock True if being called to add a new best-block to the chain + @param[in] fMiner True if being called by CreateNewBlock + @param[out] inputsRet Pointers to this transaction's inputs + @param[out] fInvalid returns true if transaction is invalid + @return Returns true if all inputs are in txdb or mapTestPool + */ + bool FetchInputs(CTxDB& txdb, const std::map& mapTestPool, + bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid); + + /** Sanity check previous transactions, then, if all checks succeed, + mark them as spent by this transaction. + + @param[in] inputs Previous transactions (from FetchInputs) + @param[out] mapTestPool Keeps track of inputs that need to be updated on disk + @param[in] posThisTx Position of this transaction on disk + @param[in] pindexBlock + @param[in] fBlock true if called from ConnectBlock + @param[in] fMiner true if called from CreateNewBlock + @param[in] fStrictPayToScriptHash true if fully validating p2sh transactions + @return Returns true if all checks succeed + */ - bool ConnectInputs(MapPrevTx inputs, ++ bool ConnectInputs(CTxDB& txdb, MapPrevTx inputs, + std::map& mapTestPool, const CDiskTxPos& posThisTx, + const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash=true); bool ClientConnectInputs(); bool CheckTransaction() const; bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL); - bool AcceptToMemoryPool(bool fCheckInputs=true, bool* pfMissingInputs=NULL); - protected: - bool AddToMemoryPoolUnchecked(); - public: - bool RemoveFromMemoryPool(); + bool GetCoinAge(CTxDB& txdb, uint64& nCoinAge) const; // ppcoin: get transaction coin age + bool CheckProofOfStake(unsigned int nBits) const; + + protected: + const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const; }; @@@ -891,40 -884,9 +930,33 @@@ public return (int64)nTime; } + void UpdateTime(const CBlockIndex* pindexPrev); + + // ppcoin: two types of block: proof-of-work or proof-of-stake + bool IsProofOfStake() const + { + return (vtx.size() > 1 && vtx[1].IsCoinStake()); + } + + bool IsProofOfWork() const + { + return !IsProofOfStake(); + } + + std::pair GetProofOfStake() const + { + return IsProofOfStake()? std::make_pair(vtx[1].vin[0].prevout, vtx[1].nTime) : std::make_pair(COutPoint(), (unsigned int)0); + } + + // ppcoin: get max transaction timestamp + int64 GetMaxTransactionTime() const + { + int64 maxTransactionTime = 0; + BOOST_FOREACH(const CTransaction& tx, vtx) + maxTransactionTime = std::max(maxTransactionTime, (int64)tx.nTime); + return maxTransactionTime; + } - int GetSigOpCount() const - { - int n = 0; - BOOST_FOREACH(const CTransaction& tx, vtx) - n += tx.GetSigOpCount(); - return n; - } - - uint256 BuildMerkleTree() const { vMerkleTree.clear(); @@@ -1019,10 -982,15 +1052,15 @@@ filein.nType |= SER_BLOCKHEADERONLY; // Read block - filein >> *this; + try { + filein >> *this; + } + catch (std::exception &e) { + return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); + } // Check the header - if (!CheckProofOfWork(GetHash(), nBits)) + if (fReadTransactions && IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits)) return error("CBlock::ReadFromDisk() : errors in block header"); return true; @@@ -1038,9 -1006,8 +1076,9 @@@ hashPrevBlock.ToString().substr(0,20).c_str(), hashMerkleRoot.ToString().substr(0,10).c_str(), nTime, nBits, nNonce, - vtx.size()); + vtx.size(), + HexStr(vchBlockSig.begin(), vchBlockSig.end()).c_str()); - for (int i = 0; i < vtx.size(); i++) + for (unsigned int i = 0; i < vtx.size(); i++) { printf(" "); vtx[i].print(); @@@ -1109,7 -1026,9 +1097,12 @@@ bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos); bool CheckBlock() const; bool AcceptBlock(); + bool GetCoinAge(uint64& nCoinAge) const; // ppcoin: calculate total coin age spent in block ++ bool SignBlock(const CKeyStore& keystore); ++ bool CheckBlockSignature() const; + + private: + bool SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew); }; diff --cc src/makefile.mingw index b793bea,37201e3..98ed2cd --- a/src/makefile.mingw +++ b/src/makefile.mingw @@@ -59,13 -12,13 +59,13 @@@ INCLUDEPATHS= LIBPATHS= \ -L"C:\boost-1.47.0-mgw\stage\lib" \ -L"C:\db-4.8.30.NC-mgw\build_unix" \ - -L"C:\openssl-1.0.0d-mgw" + -L"C:\openssl-1.0.1b-mgw" LIBS= \ - -l boost_system-mgw45-mt-s-1_47 \ - -l boost_filesystem-mgw45-mt-s-1_47 \ - -l boost_program_options-mgw45-mt-s-1_47 \ - -l boost_thread-mgw45-mt-s-1_47 \ + -l boost_system-mgw46-mt-s-1_47 \ + -l boost_filesystem-mgw46-mt-s-1_47 \ + -l boost_program_options-mgw46-mt-s-1_47 \ + -l boost_thread-mgw46-mt-s-1_47 \ -l db_cxx \ -l ssl \ -l crypto @@@ -116,29 -56,32 +103,32 @@@ OBJS= obj/net.o \ obj/protocol.o \ obj/bitcoinrpc.o \ + obj/rpcdump.o \ obj/script.o \ obj/util.o \ - obj/wallet.o + obj/wallet.o \ + obj/walletdb.o \ + obj/noui.o -all: bitcoind.exe +all: ppcoind.exe - obj/nogui/%.o: %.cpp $(HEADERS) + obj/%.o: %.cpp $(HEADERS) g++ -c $(CFLAGS) -o $@ $< - ppcoind.exe: $(OBJS:obj/%=obj/nogui/%) -bitcoind.exe: $(OBJS:obj/%=obj/%) ++ppcoind.exe: $(OBJS:obj/%=obj/%) g++ $(CFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) - obj/test/test_bitcoin.o: $(wildcard test/*.cpp) $(HEADERS) - g++ -c $(CFLAGS) -o $@ test/test_bitcoin.cpp + TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp)) - test_bitcoin.exe: obj/test/test_bitcoin.o $(filter-out obj/nogui/init.o,$(OBJS:obj/%=obj/nogui/%)) - g++ $(CFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) + obj-test/%.o: test/%.cpp $(HEADERS) + g++ -c $(TESTDEFS) $(CFLAGS) -o $@ $< + + test_bitcoin.exe: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%)) + g++ $(CFLAGS) -o $@ $(LIBPATHS) $^ -lboost_unit_test_framework $(LIBS) clean: - -del /Q bitcoind test_bitcoin + -del /Q ppcoind test_bitcoin -del /Q obj\* - -del /Q obj\nogui\* - -del /Q obj\test\* - -del /Q test\*.o - -del /Q headers.h.gch + -del /Q obj-test\* + -del /Q build.h diff --cc src/makefile.unix index 3aced5b,58331ca..cae7d44 --- a/src/makefile.unix +++ b/src/makefile.unix @@@ -119,52 -101,53 +102,63 @@@ OBJS= obj/net.o \ obj/protocol.o \ obj/bitcoinrpc.o \ + obj/rpcdump.o \ obj/script.o \ obj/util.o \ - obj/wallet.o + obj/wallet.o \ + obj/walletdb.o \ + obj/noui.o -all: bitcoind +all: ppcoind # auto-generated dependencies: - -include obj/nogui/*.P - -include obj/test/*.P + -include obj/*.P + -include obj-test/*.P + + obj/build.h: FORCE + /bin/sh ../share/genbuild.sh obj/build.h + version.cpp: obj/build.h + DEFS += -DHAVE_BUILD_INFO - obj/nogui/%.o: %.cpp + obj/%.o: %.cpp $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< @cp $(@:%.o=%.d) $(@:%.o=%.P); \ sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ rm -f $(@:%.o=%.d) - ppcoind: $(OBJS:obj/%=obj/nogui/%) -bitcoind: $(OBJS:obj/%=obj/%) - $(CXX) $(xCXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) ++ppcoind: $(OBJS:obj/%=obj/%) + $(CXX) $(xCXXFLAGS) -rdynamic -o $@ $^ $(LDFLAGS) $(LIBS) - obj/test/%.o: test/%.cpp - $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< + TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp)) + + obj-test/%.o: test/%.cpp + $(CXX) -c $(TESTDEFS) $(xCXXFLAGS) -MMD -o $@ $< @cp $(@:%.o=%.d) $(@:%.o=%.P); \ sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ rm -f $(@:%.o=%.d) - test_ppcoin: obj/test/test_ppcoin.o $(filter-out obj/nogui/init.o,$(OBJS:obj/%=obj/nogui/%)) - $(CXX) $(xCXXFLAGS) -o $@ $(LIBPATHS) $^ -Wl,-Bstatic -lboost_unit_test_framework $(LDFLAGS) $(LIBS) -test_bitcoin: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%)) ++test_ppcoin: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%)) + $(CXX) $(xCXXFLAGS) -o $@ $(LIBPATHS) $^ -Wl,-B$(LMODE) -lboost_unit_test_framework $(LDFLAGS) $(LIBS) clean: - -rm -f bitcoind test_bitcoin + -rm -f ppcoind test_ppcoin genesis -rm -f obj/*.o - -rm -f obj/nogui/*.o - -rm -f obj/test/*.o + -rm -f obj-test/*.o -rm -f obj/*.P - -rm -f obj/nogui/*.P - -rm -f obj/test/*.P + -rm -f obj-test/*.P + -rm -f src/build.h + -rm -f ppcoin/obj/* + +ppcoin/obj/genesis.o: ppcoin/genesis.cpp + $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< + $(CXX) -c $(xCXXFLAGS) -MMD -DPPCOIN_GENESIS -o obj/nogui/init.o init.cpp + +genesis: ppcoin/obj/genesis.o $(OBJS:obj/%=obj/nogui/%) + $(CXX) $(CXXFLAGS) -o $@ $^ $(LIBS) - -rm -f obj/nogui/init.* ++ -rm -f obj/init.* + -rm -f ppcoin/obj/genesis.* + + FORCE: diff --cc src/net.cpp index 633d521,d3236b3..5d5be71 --- a/src/net.cpp +++ b/src/net.cpp @@@ -1,10 -1,8 +1,9 @@@ // Copyright (c) 2009-2010 Satoshi Nakamoto - // Copyright (c) 2011 The Bitcoin developers + // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2011-2012 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying - // file license.txt or http://www.opensource.org/licenses/mit-license.php. + // file COPYING or http://www.opensource.org/licenses/mit-license.php. - #include "headers.h" #include "irc.h" #include "db.h" #include "net.h" @@@ -45,13 -44,14 +45,15 @@@ bool OpenNetworkConnection(const CAddre // bool fClient = false; bool fAllowDNS = false; + static bool fUseUPnP = false; uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK); - CAddress addrLocalHost("0.0.0.0", 0, false, nLocalServices); - CAddress addrSeenByPeer("0.0.0.0", 0, false, nLocalServices); + CAddress addrLocalHost(CService("0.0.0.0", 0), nLocalServices); ++CAddress addrSeenByPeer(CService("0.0.0.0", 0), nLocalServices); static CNode* pnodeLocalHost = NULL; uint64 nLocalHostNonce = 0; - array vnThreadsRunning; + array vnThreadsRunning; static SOCKET hListenSocket = INVALID_SOCKET; + CAddrMan addrman; vector vNodes; CCriticalSection cs_vNodes; @@@ -407,8 -248,8 +250,8 @@@ bool GetMyExternalIP(CNetAddr& ipRet void ThreadGetMyExternalIP(void* parg) { - // Wait for IRC to get it first - if (GetBoolArg("-irc", false)) + // Wait for IRC to get it first - disabled with ppcoin - if (false && !GetBoolArg("-noirc")) ++ if (false && GetBoolArg("-irc", false)) { for (int i = 0; i < 2 * 60; i++) { @@@ -1223,10 -973,15 +975,14 @@@ void MapPort(bool /* unused fMapPort */ + // DNS seeds + // Each pair gives a source name and a seed name. + // The first name is used as information source for addrman. + // The second name should resolve to a list of seed addresses. +// testnet dns seed begins with 't', all else are ppcoin dns seeds. - static const char *strDNSSeed[] = { - "ppcseed.zapto.org", - "tncseed.zapto.org" + static const char *strDNSSeed[][2] = { - {"xf2.org", "bitseed.xf2.org"}, - {"bluematt.me", "dnsseed.bluematt.me"}, - {"bitcoin.sipa.be", "seed.bitcoin.sipa.be"}, - {"dashjr.org", "dnsseed.bitcoin.dashjr.org"}, ++ {"ppcseed", "ppcseed.zapto.org"}, ++ {"tncseed", "tncseed.zapto.org"}, }; void ThreadDNSAddressSeed(void* parg) @@@ -1253,32 -1008,26 +1009,29 @@@ void ThreadDNSAddressSeed2(void* parg printf("ThreadDNSAddressSeed started\n"); int found = 0; - if (!fTestNet) + if (true /*!fTestNet*/) // ppcoin enables dns seeding with testnet too { printf("Loading addresses from DNS seeds (could take a while)\n"); - CAddrDB addrDB; - addrDB.TxnBegin(); - - for (int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) { - if (fTestNet && strDNSSeed[seed_idx][0] != 't') continue; - if ((!fTestNet) && strDNSSeed[seed_idx][0] == 't') continue; - - vector vaddr; - if (Lookup(strDNSSeed[seed_idx], vaddr, NODE_NETWORK, -1, true)) + + for (unsigned int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) { ++ if (fTestNet && strDNSSeed[seed_idx][1][0] != 't') continue; ++ if ((!fTestNet) && strDNSSeed[seed_idx][1][0] == 't') continue; ++ + vector vaddr; + vector vAdd; + if (LookupHost(strDNSSeed[seed_idx][1], vaddr)) { - BOOST_FOREACH (CAddress& addr, vaddr) + BOOST_FOREACH(CNetAddr& ip, vaddr) { - if (addr.GetByte(3) != 127) - { - addr.nTime = 0; - AddAddress(addr, 0, &addrDB); - found++; - } + int nOneDay = 24*3600; + CAddress addr = CAddress(CService(ip, GetDefaultPort())); + addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old + vAdd.push_back(addr); + found++; } } + addrman.Add(vAdd, CNetAddr(strDNSSeed[seed_idx][0], true)); } - - addrDB.TxnCommit(); // Save addresses (it's ok if this fails) } printf("%d addresses found from DNS seeds\n", found); @@@ -1297,10 -1046,116 +1050,40 @@@ unsigned int pnSeed[] = { - 0xfc01a8c0 - 0x959bd347, 0xf8de42b2, 0x73bc0518, 0xea6edc50, 0x21b00a4d, 0xc725b43d, 0xd665464d, 0x1a2a770e, - 0x27c93946, 0x65b2fa46, 0xb80ae255, 0x66b3b446, 0xb1877a3e, 0x6ee89e3e, 0xc3175b40, 0x2a01a83c, - 0x95b1363a, 0xa079ad3d, 0xe6ca801f, 0x027f4f4a, 0x34f7f03a, 0xf790f04a, 0x16ca801f, 0x2f4d5e40, - 0x3a4d5e40, 0xc43a322e, 0xc8159753, 0x14d4724c, 0x7919a118, 0xe0bdb34e, 0x68a16b2e, 0xff64b44d, - 0x6099115b, 0x9b57b05b, 0x7bd1b4ad, 0xdf95944f, 0x29d2b73d, 0xafa8db79, 0xe247ba41, 0x24078348, - 0xf722f03c, 0x33567ebc, 0xace64ed4, 0x984d3932, 0xb5f34e55, 0x27b7024d, 0x94579247, 0x8894042e, - 0x9357d34c, 0x1063c24b, 0xcaa228b1, 0xa3c5a8b2, 0x5dc64857, 0xa2c23643, 0xa8369a54, 0x31203077, - 0x00707c5c, 0x09fc0b3a, 0x272e9e2e, 0xf80f043e, 0x9449ca3e, 0x5512c33e, 0xd106b555, 0xe8024157, - 0xe288ec29, 0xc79c5461, 0xafb63932, 0xdb02ab4b, 0x0e512777, 0x8a145a4c, 0xb201ff4f, 0x5e09314b, - 0xcd9bfbcd, 0x1c023765, 0x4394e75c, 0xa728bd4d, 0x65331552, 0xa98420b1, 0x89ecf559, 0x6e80801f, - 0xf404f118, 0xefd62b51, 0x05918346, 0x9b186d5f, 0xacabab46, 0xf912e255, 0xc188ea62, 0xcc55734e, - 0xc668064d, 0xd77a4558, 0x46201c55, 0xf17dfc80, 0xf7142f2e, 0x87bfb718, 0x8aa54fb2, 0xc451d518, - 0xc4ae8831, 0x8dd44d55, 0x5bbd206c, 0x64536b5d, 0x5c667e60, 0x3b064242, 0xfe963a42, 0xa28e6dc8, - 0xe8a9604a, 0xc989464e, 0xd124a659, 0x50065140, 0xa44dfe5e, 0x1079e655, 0x3fb986d5, 0x47895b18, - 0x7d3ce4ad, 0x4561ba50, 0x296eec62, 0x255b41ad, 0xaed35ec9, 0x55556f12, 0xc7d3154d, 0x3297b65d, - 0x8930121f, 0xabf42e4e, 0x4a29e044, 0x1212685d, 0x676c1e40, 0xce009744, 0x383a8948, 0xa2dbd0ad, - 0xecc2564d, 0x07dbc252, 0x887ee24b, 0x5171644c, 0x6bb798c1, 0x847f495d, 0x4cbb7145, 0x3bb81c32, - 0x45eb262e, 0xc8015a4e, 0x250a361b, 0xf694f946, 0xd64a183e, 0xd4f1dd59, 0x8f20ffd4, 0x51d9e55c, - 0x09521763, 0x5e02002e, 0x32c8074d, 0xe685762e, 0x8290b0bc, 0x762a922e, 0xfc5ee754, 0x83a24829, - 0x775b224d, 0x6295bb4d, 0x38ec0555, 0xbffbba50, 0xe5560260, 0x86b16a7c, 0xd372234e, 0x49a3c24b, - 0x2f6a171f, 0x4d75ed60, 0xae94115b, 0xcb543744, 0x63080c59, 0x3f9c724c, 0xc977ce18, 0x532efb18, - 0x69dc3b2e, 0x5f94d929, 0x1732bb4d, 0x9c814b4d, 0xe6b3762e, 0xc024f662, 0x8face35b, 0x6b5b044d, - 0x798c7b57, 0x79a6b44c, 0x067d3057, 0xf9e94e5f, 0x91cbe15b, 0x71405eb2, 0x2662234e, 0xcbcc4a6d, - 0xbf69d54b, 0xa79b4e55, 0xec6d3e51, 0x7c0b3c02, 0x60f83653, 0x24c1e15c, 0x1110b62e, 0x10350f59, - 0xa56f1d55, 0x3509e7a9, 0xeb128354, 0x14268e2e, 0x934e28bc, 0x8e32692e, 0x8331a21f, 0x3e633932, - 0xc812b12e, 0xc684bf2e, 0x80112d2e, 0xe0ddc96c, 0xc630ca4a, 0x5c09b3b2, 0x0b580518, 0xc8e9d54b, - 0xd169aa43, 0x17d0d655, 0x1d029963, 0x7ff87559, 0xcb701f1f, 0x6fa3e85d, 0xe45e9a54, 0xf05d1802, - 0x44d03b2e, 0x837b692e, 0xccd4354e, 0x3d6da13c, 0x3423084d, 0xf707c34a, 0x55f6db3a, 0xad26e442, - 0x6233a21f, 0x09e80e59, 0x8caeb54d, 0xbe870941, 0xb407d20e, 0x20b51018, 0x56fb152e, 0x460d2a4e, - 0xbb9a2946, 0x560eb12e, 0xed83dd29, 0xd6724f53, 0xa50aafb8, 0x451346d9, 0x88348e2e, 0x7312fead, - 0x8ecaf96f, 0x1bda4e5f, 0xf1671e40, 0x3c8c3e3b, 0x4716324d, 0xdde24ede, 0xf98cd17d, 0xa91d4644, - 0x28124eb2, 0x147d5129, 0xd022042e, 0x61733d3b, 0xad0d5e02, 0x8ce2932e, 0xe5c18502, 0x549c1e32, - 0x9685801f, 0x86e217ad, 0xd948214b, 0x4110f462, 0x3a2e894e, 0xbd35492e, 0x87e0d558, 0x64b8ef7d, - 0x7c3eb962, 0x72a84b3e, 0x7cd667c9, 0x28370a2e, 0x4bc60e7b, 0x6fc1ec60, 0x14a6983f, 0x86739a4b, - 0x46954e5f, 0x32e2e15c, 0x2e9326cf, 0xe5801c5e, 0x379607b2, 0x32151145, 0xf0e39744, 0xacb54c55, - 0xa37dfb60, 0x83b55cc9, 0x388f7ca5, 0x15034f5f, 0x3e94965b, 0x68e0ffad, 0x35280f59, 0x8fe190cf, - 0x7c6ba5b2, 0xa5e9db43, 0x4ee1fc60, 0xd9d94e5f, 0x04040677, 0x0ea9b35e, 0x5961f14f, 0x67fda063, - 0xa48a5a31, 0xc6524e55, 0x283d325e, 0x3f37515f, 0x96b94b3e, 0xacce620e, 0x6481cc5b, 0xa4a06d4b, - 0x9e95d2d9, 0xe40c03d5, 0xc2f4514b, 0xb79aad44, 0xf64be843, 0xb2064070, 0xfca00455, 0x429dfa4e, - 0x2323f173, 0xeda4185e, 0xabd5227d, 0x9efd4d58, 0xb1104758, 0x4811e955, 0xbd9ab355, 0xe921f44b, - 0x9f166dce, 0x09e279b2, 0xe0c9ac7b, 0x7901a5ad, 0xa145d4b0, 0x79104671, 0xec31e35a, 0x4fe0b555, - 0xc7d9cbad, 0xad057f55, 0xe94cc759, 0x7fe0b043, 0xe4529f2e, 0x0d4dd4b2, 0x9f11a54d, 0x031e2e4e, - 0xe6014f5f, 0x11d1ca6c, 0x26bd7f61, 0xeb86854f, 0x4d347b57, 0x116bbe2e, 0xdba7234e, 0x7bcbfd2e, - 0x174dd4b2, 0x6686762e, 0xb089ba50, 0xc6258246, 0x087e767b, 0xc4a8cb4a, 0x595dba50, 0x7f0ae502, - 0x7b1dbd5a, 0xa0603492, 0x57d1af4b, 0x9e21ffd4, 0x6393064d, 0x7407376e, 0xe484762e, 0x122a4e53, - 0x4a37aa43, 0x3888a6be, 0xee77864e, 0x039c8dd5, 0x688d89af, 0x0e988f62, 0x08218246, 0xfc2f8246, - 0xd1d97040, 0xd64cd4b2, 0x5ae4a6b8, 0x7d0de9bc, 0x8d304d61, 0x06c5c672, 0xa4c8bd4d, 0xe0fd373b, - 0x575ebe4d, 0x72d26277, 0x55570f55, 0x77b154d9, 0xe214293a, 0xfc740f4b, 0xfe3f6a57, 0xa9c55f02, - 0xae4054db, 0x2394d918, 0xb511b24a, 0xb8741ab2, 0x0758e65e, 0xc7b5795b, 0xb0a30a4c, 0xaf7f170c, - 0xf3b4762e, 0x8179576d, 0x738a1581, 0x4b95b64c, 0x9829b618, 0x1bea932e, 0x7bdeaa4b, 0xcb5e0281, - 0x65618f54, 0x0658474b, 0x27066acf, 0x40556d65, 0x7d204d53, 0xf28bc244, 0xdce23455, 0xadc0ff54, - 0x3863c948, 0xcee34e5f, 0xdeb85e02, 0x2ed17a61, 0x6a7b094d, 0x7f0cfc40, 0x59603f54, 0x3220afbc, - 0xb5dfd962, 0x125d21c0, 0x13f8d243, 0xacfefb4e, 0x86c2c147, 0x3d8bbd59, 0xbd02a21f, 0x2593042e, - 0xc6a17a7c, 0x28925861, 0xb487ed44, 0xb5f4fd6d, 0x90c28a45, 0x5a14f74d, 0x43d71b4c, 0x728ebb5d, - 0x885bf950, 0x08134dd0, 0x38ec046e, 0xc575684b, 0x50082d2e, 0xa2f47757, 0x270f86ae, 0xf3ff6462, - 0x10ed3f4e, 0x4b58d462, 0xe01ce23e, 0x8c5b092e, 0x63e52f4e, 0x22c1e85d, 0xa908f54e, 0x8591624f, - 0x2c0fb94e, 0xa280ba3c, 0xb6f41b4c, 0x24f9aa47, 0x27201647, 0x3a3ea6dc, 0xa14fc3be, 0x3c34bdd5, - 0x5b8d4f5b, 0xaadeaf4b, 0xc71cab50, 0x15697a4c, 0x9a1a734c, 0x2a037d81, 0x2590bd59, 0x48ec2741, - 0x53489c5b, 0x7f00314b, 0x2170d362, 0xf2e92542, 0x42c10b44, 0x98f0f118, 0x883a3456, 0x099a932e, - 0xea38f7bc, 0x644e9247, 0xbb61b62e, 0x30e0863d, 0x5f51be54, 0x207215c7, 0x5f306c45, 0xaa7f3932, - 0x98da7d45, 0x4e339b59, 0x2e411581, 0xa808f618, 0xad2c0c59, 0x54476741, 0x09e99fd1, 0x5db8f752, - 0xc16df8bd, 0x1dd4b44f, 0x106edf2e, 0x9e15c180, 0x2ad6b56f, 0x633a5332, 0xff33787c, 0x077cb545, - 0x6610be6d, 0x75aad2c4, 0x72fb4d5b, 0xe81e0f59, 0x576f6332, 0x47333373, 0x351ed783, 0x2d90fb50, - 0x8d5e0f6c, 0x5b27a552, 0xdb293ebb, 0xe55ef950, 0x4b133ad8, 0x75df975a, 0x7b6a8740, 0xa899464b, - 0xfab15161, 0x10f8b64d, 0xd055ea4d, 0xee8e146b, 0x4b14afb8, 0x4bc1c44a, 0x9b961dcc, 0xd111ff43, - 0xfca0b745, 0xc800e412, 0x0afad9d1, 0xf751c350, 0xf9f0cccf, 0xa290a545, 0x8ef13763, 0x7ec70d59, - 0x2b066acf, 0x65496c45, 0xade02c1b, 0xae6eb077, 0x92c1e65b, 0xc064e6a9, 0xc649e56d, 0x5287a243, - 0x36de4f5b, 0x5b1df6ad, 0x65c39a59, 0xdba805b2, 0x20067aa8, 0x6457e56d, 0x3cee26cf, 0xfd3ff26d, - 0x04f86d4a, 0x06b8e048, 0xa93bcd5c, 0x91135852, 0xbe90a643, 0x8fa0094d, 0x06d8215f, 0x2677094d, - 0xd735685c, 0x164a00c9, 0x5209ac5f, 0xa9564c5c, 0x3b504f5f, 0xcc826bd0, 0x4615042e, 0x5fe13b4a, - 0x8c81b86d, 0x879ab68c, 0x1de564b8, 0x434487d8, 0x2dcb1b63, 0x82ab524a, 0xb0676abb, 0xa13d9c62, - 0xdbb5b86d, 0x5b7f4b59, 0xaddfb44d, 0xad773532, 0x3997054c, 0x72cebd89, 0xb194544c, 0xc5b8046e, - 0x6e1adeb2, 0xaa5abb51, 0xefb54b44, 0x15efc54f, 0xe9f1bc4d, 0x5f401b6c, 0x97f018ad, 0xc82f9252, - 0x2cdc762e, 0x8e52e56d, 0x1827175e, 0x9b7d7d80, 0xb2ad6845, 0x51065140, 0x71180a18, 0x5b27006c, - 0x0621e255, 0x721cbe58, 0x670c0cb8, 0xf8bd715d, 0xe0bdc5d9, 0xed843501, 0x4b84554d, 0x7f1a18bc, - 0x53bcaf47, 0x5729d35f, 0xf0dda246, 0x22382bd0, 0x4d641fb0, 0x316afcde, 0x50a22f1f, 0x73608046, - 0xc461d84a, 0xb2dbe247, ++ 0xfc01a8c0, }; + void DumpAddresses() + { + CAddrDB adb; + adb.WriteAddrman(addrman); + } + + void ThreadDumpAddress2(void* parg) + { + vnThreadsRunning[THREAD_DUMPADDRESS]++; + while (!fShutdown) + { + DumpAddresses(); + vnThreadsRunning[THREAD_DUMPADDRESS]--; + Sleep(100000); + vnThreadsRunning[THREAD_DUMPADDRESS]++; + } + vnThreadsRunning[THREAD_DUMPADDRESS]--; + } + void ThreadDumpAddress(void* parg) + { + IMPLEMENT_RANDOMIZE_STACK(ThreadDumpAddress(parg)); + try + { + ThreadDumpAddress2(parg); + } + catch (std::exception& e) { + PrintException(&e, "ThreadDumpAddress()"); + } + printf("ThreadDumpAddress exiting\n"); + } void ThreadOpenConnections(void* parg) { diff --cc src/net.h index c2f0aad,65118a4..c0cda53 --- a/src/net.h +++ b/src/net.h @@@ -1,8 -1,7 +1,8 @@@ // Copyright (c) 2009-2010 Satoshi Nakamoto - // Copyright (c) 2011 The Bitcoin developers + // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2012 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying - // file license.txt or http://www.opensource.org/licenses/mit-license.php. + // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_NET_H #define BITCOIN_NET_H @@@ -77,9 -86,9 +87,10 @@@ extern bool fClient extern bool fAllowDNS; extern uint64 nLocalServices; extern CAddress addrLocalHost; +extern CAddress addrSeenByPeer; extern uint64 nLocalHostNonce; - extern boost::array vnThreadsRunning; + extern boost::array vnThreadsRunning; + extern CAddrMan addrman; extern std::vector vNodes; extern CCriticalSection cs_vNodes; @@@ -146,10 -151,9 +153,10 @@@ public std::set setAddrKnown; bool fGetAddr; std::set setKnown; + uint256 hashCheckpointKnown; // ppcoin: known sent sync-checkpoint // inventory based relay - std::set setInventoryKnown; + mruset setInventoryKnown; std::vector vInventoryToSend; CCriticalSection cs_inventory; std::multimap mapAskFor; @@@ -192,9 -184,8 +187,9 @@@ hashLastGetBlocksEnd = 0; nStartingHeight = -1; fGetAddr = false; - vfSubscribe.assign(256, false); nMisbehavior = 0; + hashCheckpointKnown = 0; + setInventoryKnown.max_size(SendBufferSize() / 1000); // Be shy and don't send version until we hear if (!fInbound) diff --cc src/protocol.h index 5943f8a,b516f1b..30f9714 --- a/src/protocol.h +++ b/src/protocol.h @@@ -14,23 -15,12 +15,17 @@@ #include #include "uint256.h" +#define PPCOIN_PORT 9901 +#define RPC_PORT 9902 +#define TESTNET_PORT 9903 + extern bool fTestNet; + static inline unsigned short GetDefaultPort(const bool testnet = fTestNet) { - return testnet ? 18333 : 8333; + return testnet ? TESTNET_PORT : PPCOIN_PORT; } - // - // Message header - // (4) message start - // (12) command - // (4) size - // (4) checksum extern unsigned char pchMessageStart[4]; diff --cc src/script.cpp index 12c6b6f,ccb19c3..a3ab8e1 --- a/src/script.cpp +++ b/src/script.cpp @@@ -1,9 -1,9 +1,10 @@@ // Copyright (c) 2009-2010 Satoshi Nakamoto - // Copyright (c) 2011-2012 The Bitcoin developers + // Copyright (c) 2009-2012 The Bitcoin developers ++// Copyright (c) 2012 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying - // file license.txt or http://www.opensource.org/licenses/mit-license.php. - #include "headers.h" - #include "script.h" + // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include + #include using namespace std; using namespace boost; diff --cc src/script.h index 3d40964,5397a19..2aa08fe --- a/src/script.h +++ b/src/script.h @@@ -1,7 -1,7 +1,8 @@@ // Copyright (c) 2009-2010 Satoshi Nakamoto - // Copyright (c) 2011 The Bitcoin developers + // Copyright (c) 2009-2012 The Bitcoin developers ++// Copyright (c) 2012 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying - // file license.txt or http://www.opensource.org/licenses/mit-license.php. + // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef H_BITCOIN_SCRIPT #define H_BITCOIN_SCRIPT @@@ -13,10 -12,10 +13,12 @@@ #include +typedef std::vector valtype; + class CTransaction; + class CKeyStore; + /** Signature hash types/flags */ enum { SIGHASH_ALL = 1, diff --cc src/serialize.h index 61120fd,349a40b..ca12051 --- a/src/serialize.h +++ b/src/serialize.h @@@ -1,8 -1,7 +1,8 @@@ // Copyright (c) 2009-2010 Satoshi Nakamoto - // Copyright (c) 2011 The Bitcoin developers + // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2012 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying - // file license.txt or http://www.opensource.org/licenses/mit-license.php. + // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_SERIALIZE_H #define BITCOIN_SERIALIZE_H diff --cc src/util.cpp index 18d0ce8,a407714..89509b1 --- a/src/util.cpp +++ b/src/util.cpp @@@ -1,10 -1,24 +1,25 @@@ // Copyright (c) 2009-2010 Satoshi Nakamoto - // Copyright (c) 2011 The Bitcoin developers + // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2011-2012 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying - // file license.txt or http://www.opensource.org/licenses/mit-license.php. - #include "headers.h" + // file COPYING or http://www.opensource.org/licenses/mit-license.php. + + #include "util.h" #include "strlcpy.h" + #include "version.h" + #include "ui_interface.h" + #include + + // Work around clang compilation problem in Boost 1.46: + // /usr/include/boost/program_options/detail/config_file.hpp:163:17: error: call to function 'to_internal' that is neither visible in the template definition nor found by argument-dependent lookup + // See also: http://stackoverflow.com/questions/10020179/compilation-fail-in-boost-librairies-program-options + // http://clang.debian.net/status.php?version=3.0&key=CANNOT_FIND_FUNCTION + namespace boost { + namespace program_options { + std::string to_internal(const std::string&); + } + } + #include #include #include @@@ -12,11 -26,33 +27,37 @@@ #include #include #include + #include + #include + #include + + #ifdef WIN32 + #ifdef _MSC_VER + #pragma warning(disable:4786) + #pragma warning(disable:4804) + #pragma warning(disable:4805) + #pragma warning(disable:4717) + #endif + #ifdef _WIN32_WINNT + #undef _WIN32_WINNT + #endif + #define _WIN32_WINNT 0x0501 + #ifdef _WIN32_IE + #undef _WIN32_IE + #endif + #define _WIN32_IE 0x0400 + #define WIN32_LEAN_AND_MEAN 1 + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include "shlobj.h" + #include "shlwapi.h" + #endif +#ifndef WIN32 +#include +#endif + using namespace std; using namespace boost; @@@ -174,12 -205,12 +210,10 @@@ inline int OutputDebugStringF(const cha else { // print to debug.log - static FILE* fileout = NULL; - if (!fileout) { - char pszFile[MAX_PATH+100]; - GetDataDir(pszFile); - strlcat(pszFile, "/debug.log", sizeof(pszFile)); - fileout = fopen(pszFile, "a"); + boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; + fileout = fopen(pathDebug.string().c_str(), "a"); if (fileout) setbuf(fileout, NULL); // unbuffered } if (fileout) @@@ -683,30 -801,6 +804,19 @@@ void PrintException(std::exception* pex throw; } +void LogStackTrace() { + printf("\n\n******* exception encountered *******\n"); + if (fileout) + { +#ifndef WIN32 + void* pszBuffer[32]; + size_t size; + size = backtrace(pszBuffer, 32); + backtrace_symbols_fd(pszBuffer, size, fileno(fileout)); +#endif + } +} + - void ThreadOneMessageBox(string strMessage) - { - // Skip message boxes if one is already open - static bool fMessageBoxOpen; - if (fMessageBoxOpen) - return; - fMessageBoxOpen = true; - ThreadSafeMessageBox(strMessage, "Bitcoin", wxOK | wxICON_EXCLAMATION); - fMessageBoxOpen = false; - } - void PrintExceptionContinue(std::exception* pex, const char* pszThread) { char pszMessage[10000]; @@@ -759,82 -832,77 +848,77 @@@ boost::filesystem::path MyGetSpecialFol } #endif - string GetDefaultDataDir() + boost::filesystem::path GetDefaultDataDir() { + namespace fs = boost::filesystem; + - // Windows: C:\Documents and Settings\username\Application Data\Bitcoin - // Mac: ~/Library/Application Support/Bitcoin - // Unix: ~/.bitcoin + // Windows: C:\Documents and Settings\username\Application Data\PPCoin + // Mac: ~/Library/Application Support/PPCoin + // Unix: ~/.ppcoin #ifdef WIN32 // Windows - return MyGetSpecialFolderPath(CSIDL_APPDATA, true) + "\\PPCoin"; - return MyGetSpecialFolderPath(CSIDL_APPDATA, true) / "Bitcoin"; ++ return MyGetSpecialFolderPath(CSIDL_APPDATA, true) / "PPCoin"; #else + fs::path pathRet; char* pszHome = getenv("HOME"); if (pszHome == NULL || strlen(pszHome) == 0) - pszHome = (char*)"/"; - string strHome = pszHome; - if (strHome[strHome.size()-1] != '/') - strHome += '/'; + pathRet = fs::path("/"); + else + pathRet = fs::path(pszHome); #ifdef MAC_OSX // Mac - strHome += "Library/Application Support/"; - filesystem::create_directory(strHome.c_str()); - return strHome + "PPCoin"; + pathRet /= "Library/Application Support"; + fs::create_directory(pathRet); - return pathRet / "Bitcoin"; ++ return pathRet / "PPCoin"; #else // Unix - return strHome + ".ppcoin"; - return pathRet / ".bitcoin"; ++ return pathRet / ".ppcoin"; #endif #endif } - void GetDataDir(char* pszDir) + const boost::filesystem::path &GetDataDir(bool fNetSpecific) { - // pszDir must be at least MAX_PATH length. - int nVariation; - if (pszSetDataDir[0] != 0) - { - strlcpy(pszDir, pszSetDataDir, MAX_PATH); - nVariation = 0; - } - else - { - // This can be called during exceptions by printf, so we cache the - // value so we don't have to do memory allocations after that. - static char pszCachedDir[MAX_PATH]; - if (pszCachedDir[0] == 0) - strlcpy(pszCachedDir, GetDefaultDataDir().c_str(), sizeof(pszCachedDir)); - strlcpy(pszDir, pszCachedDir, MAX_PATH); - nVariation = 1; - } - if (fTestNet) - { - char* p = pszDir + strlen(pszDir); - if (p > pszDir && p[-1] != '/' && p[-1] != '\\') - *p++ = '/'; - strcpy(p, "testnet"); - nVariation += 2; - } - static bool pfMkdir[4]; - if (!pfMkdir[nVariation]) - { - pfMkdir[nVariation] = true; - boost::filesystem::create_directory(pszDir); + namespace fs = boost::filesystem; + + static fs::path pathCached[2]; + static CCriticalSection csPathCached; + static bool cachedPath[2] = {false, false}; + + fs::path &path = pathCached[fNetSpecific]; + + // This can be called during exceptions by printf, so we cache the + // value so we don't have to do memory allocations after that. + if (cachedPath[fNetSpecific]) + return path; + + LOCK(csPathCached); + + if (mapArgs.count("-datadir")) { + path = fs::system_complete(mapArgs["-datadir"]); + if (!fs::is_directory(path)) { + path = ""; + return path; + } + } else { + path = GetDefaultDataDir(); } - } + if (fNetSpecific && GetBoolArg("-testnet", false)) + path /= "testnet"; - string GetDataDir() - { - char pszDir[MAX_PATH]; - GetDataDir(pszDir); - return pszDir; + fs::create_directory(path); + + cachedPath[fNetSpecific]=true; + return path; } - string GetConfigFile() + boost::filesystem::path GetConfigFile() { namespace fs = boost::filesystem; - fs::path pathConfig(GetArg("-conf", "ppcoin.conf")); - if (!pathConfig.is_complete()) - pathConfig = fs::path(GetDataDir()) / pathConfig; - return pathConfig.string(); + - fs::path pathConfigFile(GetArg("-conf", "bitcoin.conf")); ++ fs::path pathConfigFile(GetArg("-conf", "ppcoin.conf")); + if (!pathConfigFile.is_complete()) pathConfigFile = GetDataDir(false) / pathConfigFile; + return pathConfigFile; } void ReadConfigFile(map& mapSettingsRet, @@@ -860,18 -932,18 +948,18 @@@ } } - string GetPidFile() + boost::filesystem::path GetPidFile() { namespace fs = boost::filesystem; - fs::path pathConfig(GetArg("-pid", "ppcoind.pid")); - if (!pathConfig.is_complete()) - pathConfig = fs::path(GetDataDir()) / pathConfig; - return pathConfig.string(); + - fs::path pathPidFile(GetArg("-pid", "bitcoind.pid")); ++ fs::path pathPidFile(GetArg("-pid", "ppcoind.pid")); + if (!pathPidFile.is_complete()) pathPidFile = GetDataDir() / pathPidFile; + return pathPidFile; } - void CreatePidFile(string pidFile, pid_t pid) + void CreatePidFile(const boost::filesystem::path &path, pid_t pid) { - FILE* file = fopen(pidFile.c_str(), "w"); + FILE* file = fopen(path.string().c_str(), "w"); if (file) { fprintf(file, "%d\n", pid); @@@ -1017,14 -1088,163 +1104,164 @@@ string FormatVersion(int nVersion string FormatFullVersion() { - string s = FormatVersion(PPCOIN_VERSION) + pszSubVer; - if (VERSION_IS_BETA) { - s += "-"; - s += _("beta"); + return CLIENT_BUILD; + } + + // Format the subversion field according to BIP 14 spec (https://en.bitcoin.it/wiki/BIP_0014) + std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector& comments) + { + std::ostringstream ss; + ss << "/"; + ss << name << ":" << FormatVersion(nClientVersion); + if (!comments.empty()) + ss << "(" << boost::algorithm::join(comments, "; ") << ")"; + ss << "/"; + return ss.str(); + } + + #ifdef WIN32 + boost::filesystem::path static StartupShortcutPath() + { + return MyGetSpecialFolderPath(CSIDL_STARTUP, true) / "Bitcoin.lnk"; + } + + bool GetStartOnSystemStartup() + { + return filesystem::exists(StartupShortcutPath()); + } + + bool SetStartOnSystemStartup(bool fAutoStart) + { + // If the shortcut exists already, remove it for updating + boost::filesystem::remove(StartupShortcutPath()); + + if (fAutoStart) + { + CoInitialize(NULL); + + // Get a pointer to the IShellLink interface. + IShellLink* psl = NULL; + HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, + CLSCTX_INPROC_SERVER, IID_IShellLink, + reinterpret_cast(&psl)); + + if (SUCCEEDED(hres)) + { + // Get the current executable path + TCHAR pszExePath[MAX_PATH]; + GetModuleFileName(NULL, pszExePath, sizeof(pszExePath)); + + TCHAR pszArgs[5] = TEXT("-min"); + + // Set the path to the shortcut target + psl->SetPath(pszExePath); + PathRemoveFileSpec(pszExePath); + psl->SetWorkingDirectory(pszExePath); + psl->SetShowCmd(SW_SHOWMINNOACTIVE); + psl->SetArguments(pszArgs); + + // Query IShellLink for the IPersistFile interface for + // saving the shortcut in persistent storage. + IPersistFile* ppf = NULL; + hres = psl->QueryInterface(IID_IPersistFile, + reinterpret_cast(&ppf)); + if (SUCCEEDED(hres)) + { + WCHAR pwsz[MAX_PATH]; + // Ensure that the string is ANSI. + MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().string().c_str(), -1, pwsz, MAX_PATH); + // Save the link by calling IPersistFile::Save. + hres = ppf->Save(pwsz, TRUE); + ppf->Release(); + psl->Release(); + CoUninitialize(); + return true; + } + psl->Release(); + } + CoUninitialize(); + return false; + } + return true; + } + + #elif defined(LINUX) + + // Follow the Desktop Application Autostart Spec: + // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html + + boost::filesystem::path static GetAutostartDir() + { + namespace fs = boost::filesystem; + + char* pszConfigHome = getenv("XDG_CONFIG_HOME"); + if (pszConfigHome) return fs::path(pszConfigHome) / "autostart"; + char* pszHome = getenv("HOME"); + if (pszHome) return fs::path(pszHome) / ".config" / "autostart"; + return fs::path(); + } + + boost::filesystem::path static GetAutostartFilePath() + { + return GetAutostartDir() / "bitcoin.desktop"; + } + + bool GetStartOnSystemStartup() + { + boost::filesystem::ifstream optionFile(GetAutostartFilePath()); + if (!optionFile.good()) + return false; + // Scan through file for "Hidden=true": + string line; + while (!optionFile.eof()) + { + getline(optionFile, line); + if (line.find("Hidden") != string::npos && + line.find("true") != string::npos) + return false; ++>>>>>>> bitcoin } - return s; + optionFile.close(); + + return true; } + bool SetStartOnSystemStartup(bool fAutoStart) + { + if (!fAutoStart) + boost::filesystem::remove(GetAutostartFilePath()); + else + { + char pszExePath[MAX_PATH+1]; + memset(pszExePath, 0, sizeof(pszExePath)); + if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1) + return false; + + boost::filesystem::create_directories(GetAutostartDir()); + + boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc); + if (!optionFile.good()) + return false; + // Write a bitcoin.desktop file to the autostart directory: + optionFile << "[Desktop Entry]\n"; + optionFile << "Type=Application\n"; + optionFile << "Name=Bitcoin\n"; + optionFile << "Exec=" << pszExePath << " -min\n"; + optionFile << "Terminal=false\n"; + optionFile << "Hidden=false\n"; + optionFile.close(); + } + return true; + } + #else + + // TODO: OSX startup stuff; see: + // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html + + bool GetStartOnSystemStartup() { return false; } + bool SetStartOnSystemStartup(bool fAutoStart) { return false; } + + #endif diff --cc src/util.h index e609eeb,ef15260..dc40af5 --- a/src/util.h +++ b/src/util.h @@@ -1,8 -1,7 +1,8 @@@ // Copyright (c) 2009-2010 Satoshi Nakamoto - // Copyright (c) 2011 The Bitcoin developers + // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2012 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying - // file license.txt or http://www.opensource.org/licenses/mit-license.php. + // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_UTIL_H #define BITCOIN_UTIL_H @@@ -25,20 -31,13 +32,13 @@@ typedef int pid_t; /* define for window #include #include + #include "netbase.h" // for AddTimeData - #if defined(_MSC_VER) || defined(__BORLANDC__) - typedef __int64 int64; - typedef unsigned __int64 uint64; - #else typedef long long int64; typedef unsigned long long uint64; - #endif - #if defined(_MSC_VER) && _MSC_VER < 1300 - #define for if (false) ; else for - #endif - #ifndef _MSC_VER - #define __forceinline inline - #endif + -static const int64 COIN = 100000000; -static const int64 CENT = 1000000; ++static const int64 COIN = 1000000; ++static const int64 CENT = 10000; #define loop for (;;) #define BEGIN(a) ((char*)&(a)) diff --cc src/version.cpp index 0000000,60b7aae..3ae1784 mode 000000,100644..100644 --- a/src/version.cpp +++ b/src/version.cpp @@@ -1,0 -1,67 +1,67 @@@ + // Copyright (c) 2012 The Bitcoin developers + // Distributed under the MIT/X11 software license, see the accompanying + // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include + + #include "version.h" + + // Name of client reported in the 'version' message. Report the same name + // for both bitcoind and bitcoin-qt, to make it harder for attackers to + // target servers or GUI users specifically. + const std::string CLIENT_NAME("Satoshi"); + + // Client version number + #define CLIENT_VERSION_SUFFIX "-beta" + + + // The following part of the code determines the CLIENT_BUILD variable. + // Several mechanisms are used for this: + // * first, if HAVE_BUILD_INFO is defined, include build.h, a file that is + // generated by the build environment, possibly containing the output + // of git-describe in a macro called BUILD_DESC + // * secondly, if this is an exported version of the code, GIT_ARCHIVE will + // be defined (automatically using the export-subst git attribute), and + // GIT_COMMIT will contain the commit id. + // * then, three options exist for determining CLIENT_BUILD: + // * if BUILD_DESC is defined, use that literally (output of git-describe) + // * if not, but GIT_COMMIT is defined, use v[maj].[min].[rev].[build]-g[commit] + // * otherwise, use v[maj].[min].[rev].[build]-unk + // finally CLIENT_VERSION_SUFFIX is added + + // First, include build.h if requested + #ifdef HAVE_BUILD_INFO + # include "build.h" + #endif + + // git will put "#define GIT_ARCHIVE 1" on the next line inside archives. $Format:%n#define GIT_ARCHIVE 1$ + #ifdef GIT_ARCHIVE + # define GIT_COMMIT_ID "$Format:%h$" + # define GIT_COMMIT_DATE "$Format:%cD" + #endif + + #define STRINGIFY(s) #s + + #define BUILD_DESC_FROM_COMMIT(maj,min,rev,build,commit) \ + "v" STRINGIFY(maj) "." STRINGIFY(min) "." STRINGIFY(rev) "." STRINGIFY(build) "-g" commit + + #define BUILD_DESC_FROM_UNKNOWN(maj,min,rev,build) \ + "v" STRINGIFY(maj) "." STRINGIFY(min) "." STRINGIFY(rev) "." STRINGIFY(build) "-unk" + + #ifndef BUILD_DESC + # ifdef GIT_COMMIT_ID -# define BUILD_DESC BUILD_DESC_FROM_COMMIT(CLIENT_VERSION_MAJOR, CLIENT_VERSION_MINOR, CLIENT_VERSION_REVISION, CLIENT_VERSION_BUILD, GIT_COMMIT_ID) ++# define BUILD_DESC BUILD_DESC_FROM_COMMIT(PPCOIN_VERSION_MAJOR, PPCOIN_VERSION_MINOR, PPCOIN_VERSION_REVISION, PPCOIN_VERSION_BUILD, GIT_COMMIT_ID) + # else -# define BUILD_DESC BUILD_DESC_FROM_UNKNOWN(CLIENT_VERSION_MAJOR, CLIENT_VERSION_MINOR, CLIENT_VERSION_REVISION, CLIENT_VERSION_BUILD) ++# define BUILD_DESC BUILD_DESC_FROM_UNKNOWN(PPCOIN_VERSION_MAJOR, PPCOIN_VERSION_MINOR, PPCOIN_VERSION_REVISION, PPCOIN_VERSION_BUILD) + # endif + #endif + + #ifndef BUILD_DATE + # ifdef GIT_COMMIT_DATE + # define BUILD_DATE GIT_COMMIT_DATE + # else + # define BUILD_DATE __DATE__ ", " __TIME__ + # endif + #endif + + const std::string CLIENT_BUILD(BUILD_DESC CLIENT_VERSION_SUFFIX); + const std::string CLIENT_DATE(BUILD_DATE); diff --cc src/version.h index 0000000,9718e75..29efa06 mode 000000,100644..100644 --- a/src/version.h +++ b/src/version.h @@@ -1,0 -1,49 +1,55 @@@ + // Copyright (c) 2012 The Bitcoin developers + // Distributed under the MIT/X11 software license, see the accompanying + // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_VERSION_H + #define BITCOIN_VERSION_H + + #include + + // + // client versioning + // + + // These need to be macro's, as version.cpp's voodoo requires it + #define CLIENT_VERSION_MAJOR 0 + #define CLIENT_VERSION_MINOR 6 + #define CLIENT_VERSION_REVISION 3 + #define CLIENT_VERSION_BUILD 0 + + static const int CLIENT_VERSION = + 1000000 * CLIENT_VERSION_MAJOR + + 10000 * CLIENT_VERSION_MINOR + + 100 * CLIENT_VERSION_REVISION + + 1 * CLIENT_VERSION_BUILD; + + extern const std::string CLIENT_NAME; + extern const std::string CLIENT_BUILD; + extern const std::string CLIENT_DATE; + ++// ppcoin version - intended for display purpose ONLY ++#define PPCOIN_VERSION_MAJOR 0 ++#define PPCOIN_VERSION_MINOR 1 ++#define PPCOIN_VERSION_REVISION 0 ++#define PPCOIN_VERSION_BUILD 0 ++ + // + // network protocol versioning + // + + static const int PROTOCOL_VERSION = 60001; + + // earlier versions not supported as of Feb 2012, and are disconnected -static const int MIN_PROTO_VERSION = 209; ++static const int MIN_PROTO_VERSION = 60001; + + // nTime field added to CAddress, starting with this version; + // if possible, avoid requesting addresses nodes older than this + static const int CADDR_TIME_VERSION = 31402; + + // only request blocks from nodes outside this range of versions + static const int NOBLKS_VERSION_START = 32000; + static const int NOBLKS_VERSION_END = 32400; + + // BIP 0031, pong message, is enabled for all versions AFTER this one + static const int BIP0031_VERSION = 60000; + + #endif diff --cc src/wallet.cpp index 12fb9fd,d7a70fe..8acc825 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@@ -1,13 -1,12 +1,14 @@@ - // Copyright (c) 2009-2011 Satoshi Nakamoto - // Copyright (c) 2011 The Bitcoin developers + // Copyright (c) 2009-2010 Satoshi Nakamoto + // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2011-2012 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying - // file license.txt or http://www.opensource.org/licenses/mit-license.php. + // file COPYING or http://www.opensource.org/licenses/mit-license.php. - #include "headers.h" - #include "db.h" + #include "wallet.h" + #include "walletdb.h" #include "crypter.h" +#include "checkpoints.h" + #include "ui_interface.h" using namespace std; @@@ -44,10 -60,15 +62,19 @@@ bool CWallet::AddCryptedKey(const vecto return false; } + bool CWallet::AddCScript(const CScript& redeemScript) + { + if (!CCryptoKeyStore::AddCScript(redeemScript)) + return false; + if (!fFileBacked) + return true; + return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript); + } + +// ppcoin: optional setting to create coinstake only when unlocked; +// serves to disable the trivial sendmoney when OS account compromised +bool fWalletUnlockStakeOnly = false; + bool CWallet::Unlock(const SecureString& strWalletPassphrase) { if (!IsLocked()) @@@ -380,9 -483,9 +489,9 @@@ int CWalletTx::GetRequestCount() cons { // Returns -1 if it wasn't being tracked int nRequests = -1; - CRITICAL_BLOCK(pwallet->cs_wallet) { + LOCK(pwallet->cs_wallet); - if (IsCoinBase()) + if (IsCoinBase() || IsCoinStake()) { // Generated block if (hashBlock != 0) @@@ -763,39 -877,7 +883,35 @@@ int64 CWallet::GetUnconfirmedBalance() return nTotal; } -bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set >& setCoinsRet, int64& nValueRet) const +// ppcoin: total coins staked (non-spendable until maturity) +int64 CWallet::GetStake() const +{ + int64 nTotal = 0; - CRITICAL_BLOCK(cs_wallet) ++ LOCK(cs_wallet); ++ for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { - for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - const CWalletTx* pcoin = &(*it).second; - if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) - nTotal += CWallet::GetCredit(*pcoin); - } ++ const CWalletTx* pcoin = &(*it).second; ++ if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) ++ nTotal += CWallet::GetCredit(*pcoin); + } + return nTotal; +} + +int64 CWallet::GetNewMint() const +{ + int64 nTotal = 0; - CRITICAL_BLOCK(cs_wallet) ++ LOCK(cs_wallet); ++ for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { - for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - const CWalletTx* pcoin = &(*it).second; - if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) - nTotal += CWallet::GetCredit(*pcoin); - } ++ const CWalletTx* pcoin = &(*it).second; ++ if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) ++ nTotal += CWallet::GetCredit(*pcoin); + } + return nTotal; +} + + +bool CWallet::SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, set >& setCoinsRet, int64& nValueRet) const { setCoinsRet.clear(); nValueRet = 0; @@@ -1052,7 -1130,8 +1167,7 @@@ bool CWallet::CreateTransaction(const v // Check that enough fee is included int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000); - int64 nMinFee = wtxNew.GetMinFee(1, false); - bool fAllowFree = CTransaction::AllowFree(dPriority); - int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree, GMF_SEND); ++ int64 nMinFee = wtxNew.GetMinFee(1, false, GMF_SEND); if (nFeeRet < max(nPayFee, nMinFee)) { nFeeRet = max(nPayFee, nMinFee); @@@ -1077,97 -1156,6 +1192,94 @@@ bool CWallet::CreateTransaction(CScrip return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet); } +// ppcoin: create coin stake transaction +bool CWallet::CreateCoinStake(unsigned int nBits, CTransaction& txNew) +{ + CBigNum bnTargetPerCoinDay; + bnTargetPerCoinDay.SetCompact(nBits); + - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_wallet) ++ LOCK2(cs_main, cs_wallet); ++ txNew.vin.clear(); ++ txNew.vout.clear(); ++ // Mark coin stake transaction ++ CScript scriptEmpty; ++ scriptEmpty.clear(); ++ txNew.vout.push_back(CTxOut(0, scriptEmpty)); ++ // Choose coins to use ++ int64 nBalance = GetBalance(); ++ if (nBalance <= nBalanceReserve) ++ return false; ++ set > setCoins; ++ vector vwtxPrev; ++ int64 nValueIn = 0; ++ if (!SelectCoins(nBalance - nBalanceReserve, txNew.nTime, setCoins, nValueIn)) ++ return false; ++ if (setCoins.empty()) ++ return false; ++ int64 nCredit = 0; ++ BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) + { - txNew.vin.clear(); - txNew.vout.clear(); - // Mark coin stake transaction - CScript scriptEmpty; - scriptEmpty.clear(); - txNew.vout.push_back(CTxOut(0, scriptEmpty)); - // Choose coins to use - int64 nBalance = GetBalance(); - if (nBalance <= nBalanceReserve) - return false; - set > setCoins; - vector vwtxPrev; - int64 nValueIn = 0; - if (!SelectCoins(nBalance - nBalanceReserve, txNew.nTime, setCoins, nValueIn)) - return false; - if (setCoins.empty()) - return false; - int64 nCredit = 0; - BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) - { - CTxDB txdb("r"); - CTxIndex txindex; - if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex)) - continue; ++ CTxDB txdb("r"); ++ CTxIndex txindex; ++ if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex)) ++ continue; + - // Read block header - CBlock block; - if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) - continue; - if (block.GetBlockTime() + STAKE_MIN_AGE > txNew.nTime) - continue; // only count coins meeting min age requirement - - int64 nValueIn = pcoin.first->vout[pcoin.second].nValue; - CBigNum bnCoinDay = CBigNum(nValueIn) * (txNew.nTime-pcoin.first->nTime) / COIN / (24 * 60 * 60); - // Calculate hash - CDataStream ss(SER_GETHASH, VERSION); - ss << nBits << block.nTime << (txindex.pos.nTxPos - txindex.pos.nBlockPos) << pcoin.first->nTime << pcoin.second << txNew.nTime; - if (CBigNum(Hash(ss.begin(), ss.end())) <= bnCoinDay * bnTargetPerCoinDay) - { - txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second)); - nCredit += pcoin.first->vout[pcoin.second].nValue; - vwtxPrev.push_back(pcoin.first); - // Set output scriptPubKey - txNew.vout.push_back(CTxOut(0, pcoin.first->vout[pcoin.second].scriptPubKey)); - break; - } - } - if (nCredit == 0 || nCredit > nBalance - nBalanceReserve) - return false; - BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) ++ // Read block header ++ CBlock block; ++ if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) ++ continue; ++ if (block.GetBlockTime() + STAKE_MIN_AGE > txNew.nTime) ++ continue; // only count coins meeting min age requirement ++ ++ int64 nValueIn = pcoin.first->vout[pcoin.second].nValue; ++ CBigNum bnCoinDay = CBigNum(nValueIn) * (txNew.nTime-pcoin.first->nTime) / COIN / (24 * 60 * 60); ++ // Calculate hash ++ CDataStream ss(SER_GETHASH, 0); ++ ss << nBits << block.nTime << (txindex.pos.nTxPos - txindex.pos.nBlockPos) << pcoin.first->nTime << pcoin.second << txNew.nTime; ++ if (CBigNum(Hash(ss.begin(), ss.end())) <= bnCoinDay * bnTargetPerCoinDay) + { - if (pcoin.first->vout[pcoin.second].scriptPubKey == txNew.vout[1].scriptPubKey && pcoin.first->GetHash() != txNew.vin[0].prevout.hash) - { - if (nCredit + pcoin.first->vout[pcoin.second].nValue > nBalance - nBalanceReserve) - break; - txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second)); - nCredit += pcoin.first->vout[pcoin.second].nValue; - vwtxPrev.push_back(pcoin.first); - } ++ txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second)); ++ nCredit += pcoin.first->vout[pcoin.second].nValue; ++ vwtxPrev.push_back(pcoin.first); ++ // Set output scriptPubKey ++ txNew.vout.push_back(CTxOut(0, pcoin.first->vout[pcoin.second].scriptPubKey)); ++ break; + } - // Calculate coin age reward ++ } ++ if (nCredit == 0 || nCredit > nBalance - nBalanceReserve) ++ return false; ++ BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) ++ { ++ if (pcoin.first->vout[pcoin.second].scriptPubKey == txNew.vout[1].scriptPubKey && pcoin.first->GetHash() != txNew.vin[0].prevout.hash) + { - uint64 nCoinAge; - CTxDB txdb("r"); - if (!txNew.GetCoinAge(txdb, nCoinAge)) - return error("CreateCoinStake : failed to calculate coin age"); - nCredit += GetProofOfStakeReward(nCoinAge); ++ if (nCredit + pcoin.first->vout[pcoin.second].nValue > nBalance - nBalanceReserve) ++ break; ++ txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second)); ++ nCredit += pcoin.first->vout[pcoin.second].nValue; ++ vwtxPrev.push_back(pcoin.first); + } - // Set output amount - txNew.vout[1].nValue = nCredit; ++ } ++ // Calculate coin age reward ++ { ++ uint64 nCoinAge; ++ CTxDB txdb("r"); ++ if (!txNew.GetCoinAge(txdb, nCoinAge)) ++ return error("CreateCoinStake : failed to calculate coin age"); ++ nCredit += GetProofOfStakeReward(nCoinAge); ++ } ++ // Set output amount ++ txNew.vout[1].nValue = nCredit; + - // Sign - int nIn = 0; - BOOST_FOREACH(const CWalletTx* pcoin, vwtxPrev) - { - if (!SignSignature(*this, *pcoin, txNew, nIn++)) - return error("CreateCoinStake : failed to sign coinstake"); - } ++ // Sign ++ int nIn = 0; ++ BOOST_FOREACH(const CWalletTx* pcoin, vwtxPrev) ++ { ++ if (!SignSignature(*this, *pcoin, txNew, nIn++)) ++ return error("CreateCoinStake : failed to sign coinstake"); + } + return true; +} + // Call after CreateTransaction unless you want to abort bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) { @@@ -1339,17 -1309,12 +1439,17 @@@ bool CWallet::DelAddressBookName(const void CWallet::PrintWallet(const CBlock& block) { - CRITICAL_BLOCK(cs_wallet) { + LOCK(cs_wallet); - if (mapWallet.count(block.vtx[0].GetHash())) + if (block.IsProofOfWork() && mapWallet.count(block.vtx[0].GetHash())) { CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()]; - printf(" mine: %d %d %d", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit()); + printf(" mine: %d %d %s", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), FormatMoney(wtx.GetCredit()).c_str()); + } + if (block.IsProofOfStake() && mapWallet.count(block.vtx[1].GetHash())) + { + CWalletTx& wtx = mapWallet[block.vtx[1].GetHash()]; + printf(" stake: %d %d %s", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), FormatMoney(wtx.GetCredit()).c_str()); } } printf("\n"); @@@ -1521,110 -1505,6 +1640,107 @@@ int64 CWallet::GetOldestKeyPoolTime( return keypool.nTime; } +// ppcoin: check 'spent' consistency between wallet and txindex +bool CWallet::CheckSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion) +{ + nMismatchFound = 0; + nBalanceInQuestion = 0; - CRITICAL_BLOCK(cs_wallet) - { - vector vCoins; - vCoins.reserve(mapWallet.size()); - for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - vCoins.push_back(&(*it).second); + - CTxDB txdb("r"); - BOOST_FOREACH(const CWalletTx* pcoin, vCoins) - { - // Find the corresponding transaction index - CTxIndex txindex; - if (!txdb.ReadTxIndex(pcoin->GetHash(), txindex)) - continue; - for (int n=0; n < pcoin->vout.size(); n++) - { - if (pcoin->IsSpent(n) && (txindex.vSpent.size() <= n || txindex.vSpent[n].IsNull())) - { - printf("CheckSpentCoins found lost coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n); - nMismatchFound++; - nBalanceInQuestion += pcoin->vout[n].nValue; - } - else if (!pcoin->IsSpent(n) && (txindex.vSpent.size() > n && !txindex.vSpent[n].IsNull())) - { - printf("CheckSpentCoins found spent coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n); - nMismatchFound++; - nBalanceInQuestion += pcoin->vout[n].nValue; - } - } - } ++ LOCK(cs_wallet); ++ vector vCoins; ++ vCoins.reserve(mapWallet.size()); ++ for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) ++ vCoins.push_back(&(*it).second); ++ ++ CTxDB txdb("r"); ++ BOOST_FOREACH(const CWalletTx* pcoin, vCoins) ++ { ++ // Find the corresponding transaction index ++ CTxIndex txindex; ++ if (!txdb.ReadTxIndex(pcoin->GetHash(), txindex)) ++ continue; ++ for (int n=0; n < pcoin->vout.size(); n++) ++ { ++ if (pcoin->IsSpent(n) && (txindex.vSpent.size() <= n || txindex.vSpent[n].IsNull())) ++ { ++ printf("CheckSpentCoins found lost coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n); ++ nMismatchFound++; ++ nBalanceInQuestion += pcoin->vout[n].nValue; ++ } ++ else if (!pcoin->IsSpent(n) && (txindex.vSpent.size() > n && !txindex.vSpent[n].IsNull())) ++ { ++ printf("CheckSpentCoins found spent coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n); ++ nMismatchFound++; ++ nBalanceInQuestion += pcoin->vout[n].nValue; ++ } ++ } + } + return (nMismatchFound == 0); +} + +// ppcoin: fix wallet spent state according to txindex +void CWallet::FixSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion) +{ + nMismatchFound = 0; + nBalanceInQuestion = 0; - CRITICAL_BLOCK(cs_wallet) - { - vector vCoins; - vCoins.reserve(mapWallet.size()); - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - vCoins.push_back(&(*it).second); + - CTxDB txdb("r"); - BOOST_FOREACH(CWalletTx* pcoin, vCoins) - { - // Find the corresponding transaction index - CTxIndex txindex; - if (!txdb.ReadTxIndex(pcoin->GetHash(), txindex)) - continue; - for (int n=0; n < pcoin->vout.size(); n++) - { - if (pcoin->IsSpent(n) && (txindex.vSpent.size() <= n || txindex.vSpent[n].IsNull())) - { - printf("FixSpentCoins found lost coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n); - nMismatchFound++; - nBalanceInQuestion += pcoin->vout[n].nValue; - pcoin->MarkUnspent(n); - pcoin->WriteToDisk(); - } - else if (!pcoin->IsSpent(n) && (txindex.vSpent.size() > n && !txindex.vSpent[n].IsNull())) - { - printf("FixSpentCoins found spent coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n); - nMismatchFound++; - nBalanceInQuestion += pcoin->vout[n].nValue; - pcoin->MarkSpent(n); - pcoin->WriteToDisk(); - } - } - } ++ LOCK(cs_wallet); ++ vector vCoins; ++ vCoins.reserve(mapWallet.size()); ++ for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) ++ vCoins.push_back(&(*it).second); ++ ++ CTxDB txdb("r"); ++ BOOST_FOREACH(CWalletTx* pcoin, vCoins) ++ { ++ // Find the corresponding transaction index ++ CTxIndex txindex; ++ if (!txdb.ReadTxIndex(pcoin->GetHash(), txindex)) ++ continue; ++ for (int n=0; n < pcoin->vout.size(); n++) ++ { ++ if (pcoin->IsSpent(n) && (txindex.vSpent.size() <= n || txindex.vSpent[n].IsNull())) ++ { ++ printf("FixSpentCoins found lost coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n); ++ nMismatchFound++; ++ nBalanceInQuestion += pcoin->vout[n].nValue; ++ pcoin->MarkUnspent(n); ++ pcoin->WriteToDisk(); ++ } ++ else if (!pcoin->IsSpent(n) && (txindex.vSpent.size() > n && !txindex.vSpent[n].IsNull())) ++ { ++ printf("FixSpentCoins found spent coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n); ++ nMismatchFound++; ++ nBalanceInQuestion += pcoin->vout[n].nValue; ++ pcoin->MarkSpent(n); ++ pcoin->WriteToDisk(); ++ } ++ } + } +} + +// ppcoin: disable transaction (only for coinstake) +void CWallet::DisableTransaction(const CTransaction &tx) +{ + if (!tx.IsCoinStake() || !IsFromMe(tx)) + return; // only disconnecting coinstake requires marking input unspent - CRITICAL_BLOCK(cs_wallet) ++ ++ LOCK(cs_wallet); ++ BOOST_FOREACH(const CTxIn& txin, tx.vin) + { - BOOST_FOREACH(const CTxIn& txin, tx.vin) ++ map::iterator mi = mapWallet.find(txin.prevout.hash); ++ if (mi != mapWallet.end()) + { - map::iterator mi = mapWallet.find(txin.prevout.hash); - if (mi != mapWallet.end()) ++ CWalletTx& prev = (*mi).second; ++ if (txin.prevout.n < prev.vout.size() && IsMine(prev.vout[txin.prevout.n])) + { - CWalletTx& prev = (*mi).second; - if (txin.prevout.n < prev.vout.size() && IsMine(prev.vout[txin.prevout.n])) - { - prev.MarkUnspent(txin.prevout.n); - prev.WriteToDisk(); - } ++ prev.MarkUnspent(txin.prevout.n); ++ prev.WriteToDisk(); + } + } + } +} + vector CReserveKey::GetReservedKey() { if (nIndex == -1) diff --cc src/wallet.h index 775143f,9e451f8..d3c25f9 --- a/src/wallet.h +++ b/src/wallet.h @@@ -1,17 -1,15 +1,18 @@@ // Copyright (c) 2009-2010 Satoshi Nakamoto - // Copyright (c) 2011 The Bitcoin developers + // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2011-2012 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying - // file license.txt or http://www.opensource.org/licenses/mit-license.php. + // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_WALLET_H #define BITCOIN_WALLET_H - #include "bignum.h" + #include "main.h" #include "key.h" + #include "keystore.h" #include "script.h" +extern bool fWalletUnlockStakeOnly; + class CWalletTx; class CReserveKey; class CWalletDB; @@@ -86,13 -145,9 +148,12 @@@ public void ResendWalletTransactions(); int64 GetBalance() const; int64 GetUnconfirmedBalance() const; + int64 GetStake() const; + int64 GetNewMint() const; bool CreateTransaction(const std::vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); + bool CreateCoinStake(unsigned int nBits, CTransaction& txNew); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); - bool BroadcastTransaction(CWalletTx& wtxNew); std::string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); std::string SendMoneyToBitcoinAddress(const CBitcoinAddress& address, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); @@@ -215,12 -261,17 +267,21 @@@ bool SetDefaultKey(const std::vector &vchPubKey); + // signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower + bool SetMinVersion(enum WalletFeature, CWalletDB* pwalletdbIn = NULL, bool fExplicit = false); + + // change which version we're allowed to upgrade to (note that this does not immediately imply upgrading to that format) + bool SetMaxVersion(int nVersion); + + // get the current wallet format (the oldest client version guaranteed to understand this wallet) + int GetVersion() { return nWalletVersion; } ++ + bool CheckSpentCoins(int& nMismatchSpent, int64& nBalanceInQuestion); + void FixSpentCoins(int& nMismatchSpent, int64& nBalanceInQuestion); + void DisableTransaction(const CTransaction &tx); }; - + /** A key allocated from the key pool. */ class CReserveKey { protected: