void CAuxpowBuilder::setCoinbase (const CScript& scr) { CMutableTransaction mtx; mtx.vin.resize (1); mtx.vin[0].prevout.SetNull (); mtx.vin[0].scriptSig = scr; parentBlock.vtx.clear (); parentBlock.vtx.push_back (mtx); parentBlock.hashMerkleRoot = parentBlock.BuildMerkleTree (); }
bool SysTestBase::IsTxInTipBlock(const uint256& txHash) { CBlockIndex* pindex = chainActive.Tip(); CBlock block; if (!ReadBlockFromDisk(block, pindex)) return false; block.BuildMerkleTree(); std::tuple<bool, int> ret = block.GetTxIndex(txHash); if (!std::get<0>(ret)) { return false; } return true; }
bool GetTxIndexInBlock(const uint256& txHash, int& nIndex) { CBlockIndex* pindex = chainActive.Tip(); CBlock block; if (!ReadBlockFromDisk(block, pindex)) return false; block.BuildMerkleTree(); std::tuple<bool,int> ret = block.GetTxIndex(txHash); if (!std::get<0>(ret)) { return false; } nIndex = std::get<1>(ret); return true; }
Value getscriptid(const Array& params, bool fHelp) { if (fHelp || params.size() != 1) { throw runtime_error("getscriptid \n" "\nreturn an object containing regid and script\n" "\nArguments:\n" "1. txhash (string, required) the transaction hash.\n" "\nResult:\n" "\nExamples:\n" + HelpExampleCli("getscriptid", "5zQPcC1YpFMtwxiH787pSXanUECoGsxUq3KZieJxVG") + HelpExampleRpc("getscriptid","5zQPcC1YpFMtwxiH787pSXanUECoGsxUq3KZieJxVG")); } uint256 txhash(uint256S(params[0].get_str())); int nIndex = 0; int BlockHeight =GetTxComfirmHigh(txhash, *pScriptDBTip) ; if(BlockHeight > chainActive.Height()) { throw runtime_error("height larger than tip block \n"); } else if(BlockHeight == -1){ throw runtime_error("tx hash unconfirmed \n"); } CBlockIndex* pindex = chainActive[BlockHeight]; CBlock block; if (!ReadBlockFromDisk(block, pindex)) return false; block.BuildMerkleTree(); std::tuple<bool,int> ret = block.GetTxIndex(txhash); if (!std::get<0>(ret)) { throw runtime_error("tx not exit in block"); } nIndex = std::get<1>(ret); CRegID striptID(BlockHeight, nIndex); Object result; result.push_back(Pair("regid:", striptID.ToString())); result.push_back(Pair("script", HexStr(striptID.GetVec6()))); return result; }
TEST(wallet_tests, nullifier_is_spent) { CWallet wallet; auto sk = libzcash::SpendingKey::random(); wallet.AddSpendingKey(sk); auto wtx = GetValidReceive(sk, 10, true); auto note = GetNote(sk, wtx, 0, 1); auto nullifier = note.nullifier(sk); EXPECT_FALSE(wallet.IsSpent(nullifier)); wallet.AddToWallet(wtx, true, NULL); EXPECT_FALSE(wallet.IsSpent(nullifier)); auto wtx2 = GetValidSpend(sk, note, 5); wallet.AddToWallet(wtx2, true, NULL); EXPECT_FALSE(wallet.IsSpent(nullifier)); // Fake-mine the transaction EXPECT_EQ(-1, chainActive.Height()); CBlock block; block.vtx.push_back(wtx2); block.hashMerkleRoot = block.BuildMerkleTree(); auto blockHash = block.GetHash(); CBlockIndex fakeIndex {block}; mapBlockIndex.insert(std::make_pair(blockHash, &fakeIndex)); chainActive.SetTip(&fakeIndex); EXPECT_TRUE(chainActive.Contains(&fakeIndex)); EXPECT_EQ(0, chainActive.Height()); wtx2.SetMerkleBranch(block); wallet.AddToWallet(wtx2, true, NULL); EXPECT_TRUE(wallet.IsSpent(nullifier)); // Tear down chainActive.SetTip(NULL); mapBlockIndex.erase(blockHash); }
bool SignStakeBlock(CBlock &block, CKey &key, vector<const CWalletTx*> &StakeInputs, CWallet *pwallet, MiningCPID& BoincData) { //Append beacon signature to coinbase std::string PublicKey = GlobalCPUMiningCPID.BoincPublicKey; if (!PublicKey.empty()) { std::string sBoincSignature; std::string sError; bool bResult = SignBlockWithCPID(GlobalCPUMiningCPID.cpid, GlobalCPUMiningCPID.lastblockhash, sBoincSignature, sError); if (!bResult) { return error("SignStakeBlock: Failed to sign boinchash -> %s\n", sError); } BoincData.BoincSignature = sBoincSignature; if(fDebug2) LogPrintf("Signing BoincBlock for cpid %s and blockhash %s with sig %s\n", GlobalCPUMiningCPID.cpid, GlobalCPUMiningCPID.lastblockhash, BoincData.BoincSignature); } block.vtx[0].hashBoinc = SerializeBoincBlock(BoincData,block.nVersion); //if (fDebug2) LogPrintf("SignStakeBlock: %s\n",SerializedBoincData.c_str()); //Sign the coinstake transaction unsigned nIn = 0; for (auto const& pcoin : StakeInputs) { if (!SignSignature(*pwallet, *pcoin, block.vtx[1], nIn++)) { return error("SignStakeBlock: failed to sign coinstake"); } } //Sign the whole block block.hashMerkleRoot = block.BuildMerkleTree(); if( !key.Sign(block.GetHash(), block.vchBlockSig) ) { return error("SignStakeBlock: failed to sign block"); } return true; }
Value getworkaux(const Array& params, bool fHelp) { if (fHelp || params.size() < 1) throw runtime_error( "getworkaux <aux>\n" "getworkaux '' <data>\n" "getworkaux 'submit' <data>\n" "getworkaux '' <data> <chain-index> <branch>*\n" " get work with auxiliary data in coinbase, for multichain mining\n" "<aux> is the merkle root of the auxiliary chain block hashes, concatenated with the aux chain merkle tree size and a nonce\n" "<chain-index> is the aux chain index in the aux chain merkle tree\n" "<branch> is the optional merkle branch of the aux chain\n" "If <data> is not specified, returns formatted hash data to work on:\n" " \"midstate\" : precomputed hash state after hashing the first half of the data\n" " \"data\" : block data\n" " \"hash1\" : formatted hash buffer for second hash\n" " \"target\" : little endian hash target\n" "If <data> is specified and 'submit', tries to solve the block for this (parent) chain and returns true if it was successful." "If <data> is specified and empty first argument, returns the aux merkle root, with size and nonce." "If <data> and <chain-index> are specified, creates an auxiliary proof of work for the chain specified and returns:\n" " \"aux\" : merkle root of auxiliary chain block hashes\n" " \"auxpow\" : aux proof of work to submit to aux chain\n" ); if (vNodes.empty()) throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Skeincoin is not connected!"); if (IsInitialBlockDownload()) throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Skeincoin is downloading blocks..."); static map<uint256, pair<CBlock*, unsigned int> > mapNewBlock; static vector<CBlockTemplate*> vNewBlockTemplate; static CReserveKey reservekey(pwalletMain); if (params.size() == 1) { static vector<unsigned char> vchAuxPrev; vector<unsigned char> vchAux = ParseHex(params[0].get_str()); // Update block static unsigned int nTransactionsUpdatedLast; static CBlockIndex* pindexPrev; static int64 nStart; static CBlockTemplate* pblocktemplate; if (pindexPrev != pindexBest || vchAux != vchAuxPrev || (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) { if (pindexPrev != pindexBest) { // Deallocate old blocks since they're obsolete now mapNewBlock.clear(); BOOST_FOREACH(CBlockTemplate* pblocktemplate, vNewBlockTemplate) delete pblocktemplate; vNewBlockTemplate.clear(); } nTransactionsUpdatedLast = nTransactionsUpdated; pindexPrev = pindexBest; vchAuxPrev = vchAux; nStart = GetTime(); // Create new block pblocktemplate = CreateNewBlock(reservekey); if (!pblocktemplate) throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); vNewBlockTemplate.push_back(pblocktemplate); } CBlock* pblock = &pblocktemplate->block; // Update nTime pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); pblock->nNonce = 0; // Update nExtraNonce static unsigned int nExtraNonce = 0; static int64 nPrevTime = 0; IncrementExtraNonceWithAux(pblock, pindexPrev, nExtraNonce, nPrevTime, vchAux); // Save mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, nExtraNonce); // Prebuild hash buffers char pmidstate[32]; char pdata[128]; char phash1[64]; FormatHashBuffers(pblock, pmidstate, pdata, phash1); uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); Object result; result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata)))); result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); return result; } else { if (params[0].get_str() != "submit" && params[0].get_str() != "") throw JSONRPCError(RPC_INVALID_PARAMETER, "<aux> must be the empty string or 'submit' if work is being submitted"); // Parse parameters vector<unsigned char> vchData = ParseHex(params[1].get_str()); if (vchData.size() != 128) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); CBlock* pdata = (CBlock*)&vchData[0]; // Byte reverse for (int i = 0; i < 128/4; i++) ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]); // Get saved block if (!mapNewBlock.count(pdata->hashMerkleRoot)) return false; CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first; unsigned int nExtraNonce = mapNewBlock[pdata->hashMerkleRoot].second; pblock->nTime = pdata->nTime; pblock->nNonce = pdata->nNonce; // Get the aux merkle root from the coinbase CScript script = pblock->vtx[0].vin[0].scriptSig; opcodetype opcode; CScript::const_iterator pc = script.begin(); script.GetOp(pc, opcode); script.GetOp(pc, opcode); script.GetOp(pc, opcode); if (opcode != OP_2) throw JSONRPCError(RPC_MISC_ERROR, "invalid aux pow script"); vector<unsigned char> vchAux; script.GetOp(pc, opcode, vchAux); RemoveMergedMiningHeader(vchAux); pblock->vtx[0].vin[0].scriptSig = MakeCoinbaseWithAux(pblock->nBits, nExtraNonce, vchAux); pblock->hashMerkleRoot = pblock->BuildMerkleTree(); if (params.size() > 2) { // Requested aux proof of work int nChainIndex = params[2].get_int(); CAuxPow pow(pblock->vtx[0]); for (int i = 3 ; (unsigned int)i < params.size() ; i++) { uint256 nHash; nHash.SetHex(params[i].get_str()); pow.vChainMerkleBranch.push_back(nHash); } pow.SetMerkleBranch(pblock); pow.nChainIndex = nChainIndex; pow.parentBlock = *pblock; CDataStream ss(SER_GETHASH|SER_BLOCKHEADERONLY, PROTOCOL_VERSION); ss << pow; Object result; result.push_back(Pair("auxpow", HexStr(ss.begin(), ss.end()))); return result; } else { if (params[0].get_str() == "submit") { return CheckWork(pblock, *pwalletMain, reservekey); } else { Object result; result.push_back(Pair("aux", HexStr(vchAux.begin(), vchAux.end()))); result.push_back(Pair("hash", pblock->GetPoWHash().GetHex())); return result; } } } }
Value getwork(const Array& params, bool fHelp) { if (fHelp || params.size() > 1) throw runtime_error( "getwork [data]\n" "If [data] is not specified, returns formatted hash data to work on:\n" " \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated " \"data\" : block data\n" " \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated " \"target\" : little endian hash target\n" "If [data] is specified, tries to solve the block and returns true if it was successful."); if (vNodes.empty()) throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Xedoscoin is not connected!"); if (IsInitialBlockDownload()) throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Xedoscoin is downloading blocks..."); typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t; static mapNewBlock_t mapNewBlock; // FIXME: thread safety static vector<CBlockTemplate*> vNewBlockTemplate; if (params.size() == 0) { // Update block static unsigned int nTransactionsUpdatedLast; static CBlockIndex* pindexPrev; static int64 nStart; static CBlockTemplate* pblocktemplate; if (pindexPrev != pindexBest || (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) { if (pindexPrev != pindexBest) { // Deallocate old blocks since they're obsolete now mapNewBlock.clear(); BOOST_FOREACH(CBlockTemplate* pblocktemplate, vNewBlockTemplate) delete pblocktemplate; vNewBlockTemplate.clear(); } // Clear pindexPrev so future getworks make a new block, despite any failures from here on pindexPrev = NULL; // Store the pindexBest used before CreateNewBlock, to avoid races nTransactionsUpdatedLast = nTransactionsUpdated; CBlockIndex* pindexPrevNew = pindexBest; nStart = GetTime(); // Create new block pblocktemplate = CreateNewBlock(*pMiningKey); if (!pblocktemplate) throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); vNewBlockTemplate.push_back(pblocktemplate); // Need to update only after we know CreateNewBlock succeeded pindexPrev = pindexPrevNew; } CBlock* pblock = &pblocktemplate->block; // pointer for convenience // Update nTime UpdateTime(*pblock, pindexPrev); pblock->nNonce = 0; // Update nExtraNonce static unsigned int nExtraNonce = 0; IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); // Save mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig); // Pre-build hash buffers char pmidstate[32]; char pdata[128]; char phash1[64]; FormatHashBuffers(pblock, pmidstate, pdata, phash1); uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); Object result; result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata)))); result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); // deprecated result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget)))); return result; } else { // Parse parameters vector<unsigned char> vchData = ParseHex(params[0].get_str()); if (vchData.size() != 128) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); CBlock* pdata = (CBlock*)&vchData[0]; // Byte reverse for (int i = 0; i < 128/4; i++) ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]); // Get saved block if (!mapNewBlock.count(pdata->hashMerkleRoot)) return false; CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first; pblock->nTime = pdata->nTime; pblock->nNonce = pdata->nNonce; pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second; pblock->hashMerkleRoot = pblock->BuildMerkleTree(); return CheckWork(pblock, *pwalletMain, *pMiningKey); } }
UniValue getauxblock(const UniValue& params, bool fHelp) { if (fHelp || (params.size() != 0 && params.size() != 2)) throw std::runtime_error( "getauxblock (hash auxpow)\n" "\nCreate or submit a merge-mined block.\n" "\nWithout arguments, create a new block and return information\n" "required to merge-mine it. With arguments, submit a solved\n" "auxpow for a previously returned block.\n" "\nArguments:\n" "1. \"hash\" (string, optional) hash of the block to submit\n" "2. \"auxpow\" (string, optional) serialised auxpow found\n" "\nResult (without arguments):\n" "{\n" " \"hash\" (string) hash of the created block\n" " \"chainid\" (numeric) chain ID for this block\n" " \"previousblockhash\" (string) hash of the previous block\n" " \"coinbasevalue\" (numeric) value of the block's coinbase\n" " \"bits\" (string) compressed target of the block\n" " \"height\" (numeric) height of the block\n" " \"_target\" (string) target in reversed byte order, deprecated\n" "}\n" "\nResult (with arguments):\n" "xxxxx (boolean) whether the submitted block was correct\n" "\nExamples:\n" + HelpExampleCli("getauxblock", "") + HelpExampleCli("getauxblock", "\"hash\" \"serialised auxpow\"") + HelpExampleRpc("getauxblock", "") ); boost::shared_ptr<CReserveScript> coinbaseScript; GetMainSignals().ScriptForMining(coinbaseScript); //throw an error if no script was provided if (!coinbaseScript->reserveScript.size()) throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available (mining requires a wallet)"); if (vNodes.empty() && !Params().MineBlocksOnDemand()) throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Namecoin is not connected!"); if (IsInitialBlockDownload() && !Params().MineBlocksOnDemand()) throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Namecoin is downloading blocks..."); /* This should never fail, since the chain is already past the point of merge-mining start. Check nevertheless. */ { LOCK(cs_main); if (chainActive.Height() + 1 < Params().GetConsensus().nAuxpowStartHeight) throw std::runtime_error("getauxblock method is not yet available"); } /* The variables below are used to keep track of created and not yet submitted auxpow blocks. Lock them to be sure even for multiple RPC threads running in parallel. */ static CCriticalSection cs_auxblockCache; LOCK(cs_auxblockCache); static std::map<uint256, CBlock*> mapNewBlock; static std::vector<CBlockTemplate*> vNewBlockTemplate; /* Create a new block? */ if (params.size() == 0) { static unsigned nTransactionsUpdatedLast; static const CBlockIndex* pindexPrev = NULL; static uint64_t nStart; static CBlockTemplate* pblocktemplate; static unsigned nExtraNonce = 0; // Update block { LOCK(cs_main); if (pindexPrev != chainActive.Tip() || (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)) { if (pindexPrev != chainActive.Tip()) { // Deallocate old blocks since they're obsolete now mapNewBlock.clear(); BOOST_FOREACH(CBlockTemplate* pbt, vNewBlockTemplate) delete pbt; vNewBlockTemplate.clear(); } // Create new block with nonce = 0 and extraNonce = 1 pblocktemplate = CreateNewBlock(coinbaseScript->reserveScript); if (!pblocktemplate) throw JSONRPCError(RPC_OUT_OF_MEMORY, "out of memory"); // Update state only when CreateNewBlock succeeded nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); pindexPrev = chainActive.Tip(); nStart = GetTime(); // Finalise it by setting the version and building the merkle root CBlock* pblock = &pblocktemplate->block; IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); pblock->nVersion.SetAuxpow(true); pblock->hashMerkleRoot = pblock->BuildMerkleTree(); // Save mapNewBlock[pblock->GetHash()] = pblock; vNewBlockTemplate.push_back(pblocktemplate); } } const CBlock& block = pblocktemplate->block; arith_uint256 target; bool fNegative, fOverflow; target.SetCompact(block.nBits, &fNegative, &fOverflow); if (fNegative || fOverflow || target == 0) throw std::runtime_error("invalid difficulty bits in block"); UniValue result(UniValue::VOBJ); result.push_back(Pair("hash", block.GetHash().GetHex())); result.push_back(Pair("chainid", block.nVersion.GetChainId())); result.push_back(Pair("previousblockhash", block.hashPrevBlock.GetHex())); result.push_back(Pair("coinbasevalue", (int64_t)block.vtx[0].vout[0].nValue)); result.push_back(Pair("bits", strprintf("%08x", block.nBits))); result.push_back(Pair("height", static_cast<int64_t> (pindexPrev->nHeight + 1))); result.push_back(Pair("_target", HexStr(BEGIN(target), END(target)))); return result; } /* Submit a block instead. Note that this need not lock cs_main, since ProcessBlockFound below locks it instead. */ assert(params.size() == 2); uint256 hash; hash.SetHex(params[0].get_str()); const std::map<uint256, CBlock*>::iterator mit = mapNewBlock.find(hash); if (mit == mapNewBlock.end()) throw JSONRPCError(RPC_INVALID_PARAMETER, "block hash unknown"); CBlock& block = *mit->second; const std::vector<unsigned char> vchAuxPow = ParseHex(params[1].get_str()); CDataStream ss(vchAuxPow, SER_GETHASH, PROTOCOL_VERSION); CAuxPow pow; ss >> pow; block.SetAuxpow(new CAuxPow(pow)); assert(block.GetHash() == hash); const bool ok = ProcessBlockFound(&block, Params()); if (ok) coinbaseScript->KeepScript(); return ok; }
/** Initialize Gridcoin. * @pre Parameters should be parsed and config file should be read. */ bool AppInit2(ThreadHandlerPtr threads) { // ********************************************************* Step 1: setup #ifdef _MSC_VER // Turn off Microsoft heap dump noise _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); #endif #if _MSC_VER >= 1400 // Disable confusing "helpful" text message on abort, Ctrl-C _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); #endif #ifdef WIN32 // Enable Data Execution Prevention (DEP) // Minimum supported OS versions: WinXP SP3, WinVista >= SP1, Win Server 2008 // A failure is non-critical and needs no further attention! #ifndef PROCESS_DEP_ENABLE // We define this here, because GCCs winbase.h limits this to _WIN32_WINNT >= 0x0601 (Windows 7), // which is not correct. Can be removed, when GCCs winbase.h is fixed! #define PROCESS_DEP_ENABLE 0x00000001 #endif typedef BOOL (WINAPI *PSETPROCDEPPOL)(DWORD); PSETPROCDEPPOL setProcDEPPol = (PSETPROCDEPPOL)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetProcessDEPPolicy"); if (setProcDEPPol != NULL) setProcDEPPol(PROCESS_DEP_ENABLE); #endif #ifndef WIN32 umask(077); // Clean shutdown on SIGTERM struct sigaction sa; sa.sa_handler = HandleSIGTERM; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); // Reopen debug.log on SIGHUP struct sigaction sa_hup; sa_hup.sa_handler = HandleSIGHUP; sigemptyset(&sa_hup.sa_mask); sa_hup.sa_flags = 0; sigaction(SIGHUP, &sa_hup, NULL); #endif // ********************************************************* Step 2: parameter interactions // Gridcoin - Check to see if config is empty? if (IsConfigFileEmpty()) { uiInterface.ThreadSafeMessageBox( "Configuration file empty. \n" + _("Please wait for new user wizard to start..."), "", 0); } //6-10-2014: R Halford: Updating Boost version to 1.5.5 to prevent sync issues; print the boost version to verify: //5-04-2018: J Owens: Boost now needs to be 1.65 or higher to avoid thread sleep problems with system clock resets. std::string boost_version = ""; std::ostringstream s; s << boost_version << "Using Boost " << BOOST_VERSION / 100000 << "." // major version << BOOST_VERSION / 100 % 1000 << "." // minior version << BOOST_VERSION % 100 // patch level << "\n"; LogPrintf("Boost Version: %s", s.str()); //Placeholder: Load Remote CPIDs Here nNodeLifespan = GetArg("-addrlifespan", 7); fUseFastIndex = GetBoolArg("-fastindex", false); nMinerSleep = GetArg("-minersleep", 8000); nDerivationMethodIndex = 0; fTestNet = GetBoolArg("-testnet"); if (mapArgs.count("-bind")) { // when specifying an explicit binding address, you want to listen on it // even when -connect or -proxy is specified SoftSetBoolArg("-listen", true); } if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) { // when only connecting to trusted nodes, do not seed via DNS, or listen by default SoftSetBoolArg("-dnsseed", false); SoftSetBoolArg("-listen", false); } if (mapArgs.count("-proxy")) { // to protect privacy, do not listen by default if a proxy server is specified SoftSetBoolArg("-listen", false); } if (!GetBoolArg("-listen", true)) { // do not map ports or try to retrieve public IP when not listening (pointless) SoftSetBoolArg("-upnp", false); SoftSetBoolArg("-discover", false); } if (mapArgs.count("-externalip")) { // if an explicit public IP is specified, do not try to find others SoftSetBoolArg("-discover", false); } if (GetBoolArg("-salvagewallet")) { // Rewrite just private keys: rescan to find transactions SoftSetBoolArg("-rescan", true); } if (GetBoolArg("-zapwallettxes", false)) { // -zapwallettx implies a rescan SoftSetBoolArg("-rescan", true); } // Verify testnet is using the testnet directory for the config file: std::string sTestNetSpecificArg = GetArgument("testnetarg","default"); LogPrintf("Using specific arg %s",sTestNetSpecificArg); // ********************************************************* Step 3: parameter-to-internal-flags fDebug=false; if (fDebug) fDebugNet = true; else fDebugNet = GetBoolArg("-debugnet"); if (GetArg("-debug", "false")=="true") { fDebug = true; LogPrintf("Entering debug mode."); } fDebug2 = false; if (GetArg("-debug2", "false")=="true") { fDebug2 = true; LogPrintf("Entering GRC debug mode 2."); } fDebug3 = false; if (GetArg("-debug3", "false")=="true") { fDebug3 = true; LogPrintf("Entering GRC debug mode 3."); } if (GetArg("-debug4", "false")=="true") { fDebug4 = true; LogPrintf("Entering RPC time debug mode"); } fDebug10= (GetArg("-debug10","false")=="true"); #if defined(WIN32) fDaemon = false; #else if(fQtActive) fDaemon = false; else fDaemon = GetBoolArg("-daemon"); #endif if (fDaemon) fServer = true; else fServer = GetBoolArg("-server"); /* force fServer when running without GUI */ if(!fQtActive) fServer = true; fPrintToConsole = GetBoolArg("-printtoconsole"); fPrintToDebugger = GetBoolArg("-printtodebugger"); fLogTimestamps = GetBoolArg("-logtimestamps"); fLogTimestamps = true; if (mapArgs.count("-timeout")) { int nNewTimeout = GetArg("-timeout", 4000); if (nNewTimeout > 0 && nNewTimeout < 600000) nConnectTimeout = nNewTimeout; } if (mapArgs.count("-paytxfee")) { if (!ParseMoney(mapArgs["-paytxfee"], nTransactionFee)) return InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s'"), mapArgs["-paytxfee"])); if (nTransactionFee > 0.25 * COIN) InitWarning(_("Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.")); } fConfChange = GetBoolArg("-confchange", false); fEnforceCanonical = GetBoolArg("-enforcecanonical", true); if (mapArgs.count("-mininput")) { if (!ParseMoney(mapArgs["-mininput"], nMinimumInputValue)) return InitError(strprintf(_("Invalid amount for -mininput=<amount>: '%s'"), mapArgs["-mininput"])); } // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log // Sanity check if (!InitSanityCheck()) return InitError(_("Initialization sanity check failed. Gridcoin is shutting down.")); // Initialize internal hashing code with SSE/AVX2 optimizations. In the future we will also have ARM/NEON optimizations. std::string sha256_algo = SHA256AutoDetect(); LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo); std::string strDataDir = GetDataDir().string(); std::string strWalletFileName = GetArg("-wallet", "wallet.dat"); // strWalletFileName must be a plain filename without a directory if (strWalletFileName != boost::filesystem::basename(strWalletFileName) + boost::filesystem::extension(strWalletFileName)) return InitError(strprintf(_("Wallet %s resides outside data directory %s."), strWalletFileName, strDataDir)); // Make sure only a single Bitcoin process is using the data directory. 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(pathLockFile.string().c_str()); if (!lock.try_lock()) return InitError(strprintf(_("Cannot obtain a lock on data directory %s. Gridcoin is probably already running."), strDataDir)); #if !defined(WIN32) if (fDaemon) { // Daemonize pid_t pid = fork(); if (pid < 0) { fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno); return false; } if (pid > 0) { CreatePidFile(GetPidFile(), pid); // Now that we are forked we can request a shutdown so the parent // exits while the child lives on. StartShutdown(); return true; } pid_t sid = setsid(); if (sid < 0) fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno); } #endif ShrinkDebugFile(); LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); LogPrintf("Gridcoin version %s (%s)", FormatFullVersion(), CLIENT_DATE); LogPrintf("Using OpenSSL version %s", SSLeay_version(SSLEAY_VERSION)); if (!fLogTimestamps) LogPrintf("Startup time: %s", DateTimeStrFormat("%x %H:%M:%S", GetAdjustedTime())); LogPrintf("Default data directory %s", GetDefaultDataDir().string()); LogPrintf("Used data directory %s", strDataDir); std::ostringstream strErrors; fDevbuildCripple = false; if((CLIENT_VERSION_BUILD != 0) && !fTestNet) { fDevbuildCripple = true; LogPrintf("WARNING: Running development version outside of testnet!\n" "Staking and sending transactions will be disabled."); if( (GetArg("-devbuild", "") == "override") && fDebug ) fDevbuildCripple = false; } if (fDaemon) fprintf(stdout, "Gridcoin server starting\n"); int64_t nStart; // ********************************************************* Step 5: verify database integrity uiInterface.InitMessage(_("Verifying database integrity...")); if (!bitdb.Open(GetDataDir())) { string msg = strprintf(_("Error initializing database environment %s!" " To recover, BACKUP THAT DIRECTORY, then remove" " everything from it except for wallet.dat."), strDataDir); return InitError(msg); } if (GetBoolArg("-salvagewallet")) { // Recover readable keypairs: if (!CWalletDB::Recover(bitdb, strWalletFileName, true)) return false; } if (filesystem::exists(GetDataDir() / strWalletFileName)) { CDBEnv::VerifyResult r = bitdb.Verify(strWalletFileName, CWalletDB::Recover); if (r == CDBEnv::RECOVER_OK) { string msg = strprintf(_("Warning: wallet.dat corrupt, data salvaged!" " Original wallet.dat saved as wallet.{timestamp}.bak in %s; if" " your balance or transactions are incorrect you should" " restore from a backup."), strDataDir); uiInterface.ThreadSafeMessageBox(msg, _("Gridcoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL); } if (r == CDBEnv::RECOVER_FAIL) return InitError(_("wallet.dat corrupt, salvage failed")); } // ********************************************************* Step 6: network initialization int nSocksVersion = GetArg("-socks", 5); if (nSocksVersion != 4 && nSocksVersion != 5) return InitError(strprintf(_("Unknown -socks proxy version requested: %i"), nSocksVersion)); if (mapArgs.count("-onlynet")) { std::set<enum Network> nets; for (auto const& snet : mapMultiArgs["-onlynet"]) { enum Network net = ParseNetwork(snet); if (net == NET_UNROUTABLE) return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet)); nets.insert(net); } for (int n = 0; n < NET_MAX; n++) { enum Network net = (enum Network)n; if (!nets.count(net)) SetLimited(net); } } CService addrProxy; bool fProxy = false; if (mapArgs.count("-proxy")) { addrProxy = CService(mapArgs["-proxy"], 9050); if (!addrProxy.IsValid()) return InitError(strprintf(_("Invalid -proxy address: '%s'"), mapArgs["-proxy"])); if (!IsLimited(NET_IPV4)) SetProxy(NET_IPV4, addrProxy, nSocksVersion); if (nSocksVersion > 4) { if (!IsLimited(NET_IPV6)) SetProxy(NET_IPV6, addrProxy, nSocksVersion); SetNameProxy(addrProxy, nSocksVersion); } fProxy = true; } // -tor can override normal proxy, -notor disables tor entirely if (!(mapArgs.count("-tor") && mapArgs["-tor"] == "0") && (fProxy || mapArgs.count("-tor"))) { CService addrOnion; if (!mapArgs.count("-tor")) addrOnion = addrProxy; else addrOnion = CService(mapArgs["-tor"], 9050); if (!addrOnion.IsValid()) return InitError(strprintf(_("Invalid -tor address: '%s'"), mapArgs["-tor"])); SetProxy(NET_TOR, addrOnion, 5); SetReachable(NET_TOR); } // see Step 2: parameter interactions for more information about these fNoListen = !GetBoolArg("-listen", true); fDiscover = GetBoolArg("-discover", true); fNameLookup = GetBoolArg("-dns", true); #ifdef USE_UPNP fUseUPnP = GetBoolArg("-upnp", USE_UPNP); #endif bool fBound = false; if (!fNoListen) { std::string strError; if (mapArgs.count("-bind")) { for (auto const& strBind : mapMultiArgs["-bind"]) { CService addrBind; if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind)); fBound |= Bind(addrBind); } } else { struct in_addr inaddr_any; inaddr_any.s_addr = INADDR_ANY; if (!IsLimited(NET_IPV6)) fBound |= Bind(CService(in6addr_any, GetListenPort()), false); if (!IsLimited(NET_IPV4)) fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound); } if (!fBound) return InitError(_("Failed to listen on any port. Use -listen=0 if you want this.")); } if (mapArgs.count("-externalip")) { for (auto const& strAddr : mapMultiArgs["-externalip"]) { CService addrLocal(strAddr, GetListenPort(), fNameLookup); if (!addrLocal.IsValid()) return InitError(strprintf(_("Cannot resolve -externalip address: '%s'"), strAddr)); AddLocal(CService(strAddr, GetListenPort(), fNameLookup), LOCAL_MANUAL); } } if (mapArgs.count("-reservebalance")) // ppcoin: reserve balance amount { if (!ParseMoney(mapArgs["-reservebalance"], nReserveBalance)) { InitError(_("Invalid amount for -reservebalance=<amount>")); return false; } } for (auto const& strDest : mapMultiArgs["-seednode"]) AddOneShot(strDest); // ********************************************************* Step 7: load blockchain if (!bitdb.Open(GetDataDir())) { string msg = strprintf(_("Error initializing database environment %s!" " To recover, BACKUP THAT DIRECTORY, then remove" " everything from it except for wallet.dat."), strDataDir); return InitError(msg); } if (GetBoolArg("-loadblockindextest")) { CTxDB txdb("r"); txdb.LoadBlockIndex(); PrintBlockTree(); return false; } uiInterface.InitMessage(_("Loading block index...")); LogPrintf("Loading block index..."); nStart = GetTimeMillis(); if (!LoadBlockIndex()) return InitError(_("Error loading blkindex.dat")); // as LoadBlockIndex can take several minutes, it's possible the user // requested to kill bitcoin-qt during the last operation. If so, exit. // As the program has not fully started yet, Shutdown() is possibly overkill. if (fRequestShutdown) { LogPrintf("Shutdown requested. Exiting."); return false; } LogPrintf(" block index %15" PRId64 "ms", GetTimeMillis() - nStart); if (GetBoolArg("-printblockindex") || GetBoolArg("-printblocktree")) { PrintBlockTree(); return false; } if (mapArgs.count("-printblock")) { string strMatch = mapArgs["-printblock"]; int nFound = 0; for (BlockMap::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) { uint256 hash = (*mi).first; if (strncmp(hash.ToString().c_str(), strMatch.c_str(), strMatch.size()) == 0) { CBlockIndex* pindex = (*mi).second; CBlock block; block.ReadFromDisk(pindex); block.BuildMerkleTree(); block.print(); LogPrintf(""); nFound++; } } if (nFound == 0) LogPrintf("No blocks matching %s were found", strMatch); return false; } // ********************************************************* Step 8: load wallet uiInterface.InitMessage(_("Loading wallet...")); LogPrintf("Loading wallet..."); nStart = GetTimeMillis(); bool fFirstRun = true; pwalletMain = new CWallet(strWalletFileName); DBErrors nLoadWalletRet = pwalletMain->LoadWallet(fFirstRun); if (nLoadWalletRet != DB_LOAD_OK) { if (nLoadWalletRet == DB_CORRUPT) strErrors << _("Error loading wallet.dat: Wallet corrupted") << "\n"; else if (nLoadWalletRet == DB_NONCRITICAL_ERROR) { string msg(_("Warning: error reading wallet.dat! All keys read correctly, but transaction data" " or address book entries might be missing or incorrect.")); uiInterface.ThreadSafeMessageBox(msg, _("Gridcoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION | CClientUIInterface::MODAL); } else if (nLoadWalletRet == DB_TOO_NEW) strErrors << _("Error loading wallet.dat: Wallet requires newer version of Gridcoin") << "\n"; else if (nLoadWalletRet == DB_NEED_REWRITE) { strErrors << _("Wallet needed to be rewritten: restart Gridcoin to complete") << "\n"; LogPrintf("%s", strErrors.str()); return InitError(strErrors.str()); } else strErrors << _("Error loading wallet.dat") << "\n"; } if (GetBoolArg("-upgradewallet", fFirstRun)) { int nMaxVersion = GetArg("-upgradewallet", 0); if (nMaxVersion == 0) // the -upgradewallet without argument case { LogPrintf("Performing wallet upgrade to %i", FEATURE_LATEST); nMaxVersion = CLIENT_VERSION; pwalletMain->SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately } else LogPrintf("Allowing wallet upgrade up to %i", nMaxVersion); if (nMaxVersion < pwalletMain->GetVersion()) strErrors << _("Cannot downgrade wallet") << "\n"; pwalletMain->SetMaxVersion(nMaxVersion); } if (fFirstRun) { // Create new keyUser and set as default key RandAddSeedPerfmon(); CPubKey newDefaultKey; if (pwalletMain->GetKeyFromPool(newDefaultKey, false)) { pwalletMain->SetDefaultKey(newDefaultKey); if (!pwalletMain->SetAddressBookName(pwalletMain->vchDefaultKey.GetID(), "")) strErrors << _("Cannot write default address") << "\n"; } } LogPrintf("%s", strErrors.str()); LogPrintf(" wallet %15" PRId64 "ms", GetTimeMillis() - nStart); RegisterWallet(pwalletMain); CBlockIndex *pindexRescan = pindexBest; if (GetBoolArg("-rescan")) pindexRescan = pindexGenesisBlock; else { CWalletDB walletdb(strWalletFileName); CBlockLocator locator; if (walletdb.ReadBestBlock(locator)) pindexRescan = locator.GetBlockIndex(); } if (pindexBest != pindexRescan && pindexBest && pindexRescan && pindexBest->nHeight > pindexRescan->nHeight) { uiInterface.InitMessage(_("Rescanning...")); LogPrintf("Rescanning last %i blocks (from block %i)...", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight); nStart = GetTimeMillis(); pwalletMain->ScanForWalletTransactions(pindexRescan, true); LogPrintf(" rescan %15" PRId64 "ms", GetTimeMillis() - nStart); } // ********************************************************* Step 9: import blocks if (mapArgs.count("-loadblock")) { uiInterface.InitMessage(_("Importing blockchain data file.")); for (auto const& strFile : mapMultiArgs["-loadblock"]) { FILE *file = fopen(strFile.c_str(), "rb"); if (file) LoadExternalBlockFile(file); } exit(0); } filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat"; if (filesystem::exists(pathBootstrap)) { uiInterface.InitMessage(_("Importing bootstrap blockchain data file.")); FILE *file = fopen(pathBootstrap.string().c_str(), "rb"); if (file) { filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; LoadExternalBlockFile(file); RenameOver(pathBootstrap, pathBootstrapOld); } } // ********************************************************* Step 10: load peers uiInterface.InitMessage(_("Loading addresses...")); if (fDebug10) LogPrintf("Loading addresses..."); nStart = GetTimeMillis(); { CAddrDB adb; if (!adb.Read(addrman)) LogPrintf("Invalid or missing peers.dat; recreating"); } LogPrintf("Loaded %i addresses from peers.dat %" PRId64 "ms", addrman.size(), GetTimeMillis() - nStart); // ********************************************************* Step 11: start node uiInterface.InitMessage(_("Loading Persisted Data Cache...")); // std::string sOut = ""; if (fDebug3) LogPrintf("Loading admin Messages"); LoadAdminMessages(true,sOut); LogPrintf("Done loading Admin messages"); uiInterface.InitMessage(_("Compute Neural Network Hashes...")); ComputeNeuralNetworkSupermajorityHashes(); LoadCPIDs(); uiInterface.InitMessage(_("Finding first applicable Research Project...")); if (!CheckDiskSpace()) return false; RandAddSeedPerfmon(); //// debug print if (fDebug) { LogPrintf("mapBlockIndex.size() = %" PRIszu, mapBlockIndex.size()); LogPrintf("nBestHeight = %d", nBestHeight); LogPrintf("setKeyPool.size() = %" PRIszu, pwalletMain->setKeyPool.size()); LogPrintf("mapWallet.size() = %" PRIszu, pwalletMain->mapWallet.size()); LogPrintf("mapAddressBook.size() = %" PRIszu, pwalletMain->mapAddressBook.size()); } uiInterface.InitMessage(_("Loading Network Averages...")); if (fDebug3) LogPrintf("Loading network averages"); CBlockIndex* tallyHeight = FindTallyTrigger(pindexBest); if(tallyHeight) TallyResearchAverages(tallyHeight); if (!threads->createThread(StartNode, NULL, "Start Thread")) InitError(_("Error: could not start node")); if (fServer) StartRPCThreads(); // ********************************************************* Step 12: finished uiInterface.InitMessage(_("Done loading")); LogPrintf("Done loading"); if (!strErrors.str().empty()) return InitError(strErrors.str()); // Add wallet transactions that aren't already in a block to mapTransactions pwalletMain->ReacceptWalletTransactions(); int nMismatchSpent; int64_t nBalanceInQuestion; pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion); return true; }
TEST(wallet_tests, find_unspent_notes) { SelectParams(CBaseChainParams::TESTNET); CWallet wallet; auto sk = libzcash::SpendingKey::random(); wallet.AddSpendingKey(sk); auto wtx = GetValidReceive(sk, 10, true); auto note = GetNote(sk, wtx, 0, 1); auto nullifier = note.nullifier(sk); mapNoteData_t noteData; JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; CNoteData nd {sk.address(), nullifier}; noteData[jsoutpt] = nd; wtx.SetNoteData(noteData); wallet.AddToWallet(wtx, true, NULL); EXPECT_FALSE(wallet.IsSpent(nullifier)); // We currently have an unspent and unconfirmed note in the wallet (depth of -1) std::vector<CNotePlaintextEntry> entries; wallet.GetFilteredNotes(entries, "", 0); EXPECT_EQ(0, entries.size()); entries.clear(); wallet.GetFilteredNotes(entries, "", -1); EXPECT_EQ(1, entries.size()); entries.clear(); // Fake-mine the transaction EXPECT_EQ(-1, chainActive.Height()); CBlock block; block.vtx.push_back(wtx); block.hashMerkleRoot = block.BuildMerkleTree(); auto blockHash = block.GetHash(); CBlockIndex fakeIndex {block}; mapBlockIndex.insert(std::make_pair(blockHash, &fakeIndex)); chainActive.SetTip(&fakeIndex); EXPECT_TRUE(chainActive.Contains(&fakeIndex)); EXPECT_EQ(0, chainActive.Height()); wtx.SetMerkleBranch(block); wallet.AddToWallet(wtx, true, NULL); EXPECT_FALSE(wallet.IsSpent(nullifier)); // We now have an unspent and confirmed note in the wallet (depth of 1) wallet.GetFilteredNotes(entries, "", 0); EXPECT_EQ(1, entries.size()); entries.clear(); wallet.GetFilteredNotes(entries, "", 1); EXPECT_EQ(1, entries.size()); entries.clear(); wallet.GetFilteredNotes(entries, "", 2); EXPECT_EQ(0, entries.size()); entries.clear(); // Let's spend the note. auto wtx2 = GetValidSpend(sk, note, 5); wallet.AddToWallet(wtx2, true, NULL); EXPECT_FALSE(wallet.IsSpent(nullifier)); // Fake-mine a spend transaction EXPECT_EQ(0, chainActive.Height()); CBlock block2; block2.vtx.push_back(wtx2); block2.hashMerkleRoot = block2.BuildMerkleTree(); block2.hashPrevBlock = blockHash; auto blockHash2 = block2.GetHash(); CBlockIndex fakeIndex2 {block2}; mapBlockIndex.insert(std::make_pair(blockHash2, &fakeIndex2)); fakeIndex2.nHeight = 1; chainActive.SetTip(&fakeIndex2); EXPECT_TRUE(chainActive.Contains(&fakeIndex2)); EXPECT_EQ(1, chainActive.Height()); wtx2.SetMerkleBranch(block2); wallet.AddToWallet(wtx2, true, NULL); EXPECT_TRUE(wallet.IsSpent(nullifier)); // The note has been spent. By default, GetFilteredNotes() ignores spent notes. wallet.GetFilteredNotes(entries, "", 0); EXPECT_EQ(0, entries.size()); entries.clear(); // Let's include spent notes to retrieve it. wallet.GetFilteredNotes(entries, "", 0, false); EXPECT_EQ(1, entries.size()); entries.clear(); // The spent note has two confirmations. wallet.GetFilteredNotes(entries, "", 2, false); EXPECT_EQ(1, entries.size()); entries.clear(); // It does not have 3 confirmations. wallet.GetFilteredNotes(entries, "", 3, false); EXPECT_EQ(0, entries.size()); entries.clear(); // Let's receive a new note CWalletTx wtx3; { auto wtx = GetValidReceive(sk, 20, true); auto note = GetNote(sk, wtx, 0, 1); auto nullifier = note.nullifier(sk); mapNoteData_t noteData; JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; CNoteData nd {sk.address(), nullifier}; noteData[jsoutpt] = nd; wtx.SetNoteData(noteData); wallet.AddToWallet(wtx, true, NULL); EXPECT_FALSE(wallet.IsSpent(nullifier)); wtx3 = wtx; } // Fake-mine the new transaction EXPECT_EQ(1, chainActive.Height()); CBlock block3; block3.vtx.push_back(wtx3); block3.hashMerkleRoot = block3.BuildMerkleTree(); block3.hashPrevBlock = blockHash2; auto blockHash3 = block3.GetHash(); CBlockIndex fakeIndex3 {block3}; mapBlockIndex.insert(std::make_pair(blockHash3, &fakeIndex3)); fakeIndex3.nHeight = 2; chainActive.SetTip(&fakeIndex3); EXPECT_TRUE(chainActive.Contains(&fakeIndex3)); EXPECT_EQ(2, chainActive.Height()); wtx3.SetMerkleBranch(block3); wallet.AddToWallet(wtx3, true, NULL); // We now have an unspent note which has one confirmation, in addition to our spent note. wallet.GetFilteredNotes(entries, "", 1); EXPECT_EQ(1, entries.size()); entries.clear(); // Let's return the spent note too. wallet.GetFilteredNotes(entries, "", 1, false); EXPECT_EQ(2, entries.size()); entries.clear(); // Increasing number of confirmations will exclude our new unspent note. wallet.GetFilteredNotes(entries, "", 2, false); EXPECT_EQ(1, entries.size()); entries.clear(); // If we also ignore spent notes at thie depth, we won't find any notes. wallet.GetFilteredNotes(entries, "", 2, true); EXPECT_EQ(0, entries.size()); entries.clear(); // Tear down chainActive.SetTip(NULL); mapBlockIndex.erase(blockHash); mapBlockIndex.erase(blockHash2); mapBlockIndex.erase(blockHash3); }