TEST(wallet_tests, navigate_from_nullifier_to_note) { 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); EXPECT_EQ(0, wallet.mapNullifiersToNotes.count(nullifier)); wallet.AddToWallet(wtx, true, NULL); EXPECT_EQ(1, wallet.mapNullifiersToNotes.count(nullifier)); EXPECT_EQ(wtx.GetHash(), wallet.mapNullifiersToNotes[nullifier].hash); EXPECT_EQ(0, wallet.mapNullifiersToNotes[nullifier].js); EXPECT_EQ(1, wallet.mapNullifiersToNotes[nullifier].n); }
TEST(wallet_tests, GetNoteNullifier) { CWallet wallet; auto sk = libzcash::SpendingKey::random(); auto address = sk.address(); auto dec = ZCNoteDecryption(sk.viewing_key()); auto wtx = GetValidReceive(sk, 10, true); auto note = GetNote(sk, wtx, 0, 1); auto nullifier = note.nullifier(sk); auto hSig = wtx.vjoinsplit[0].h_sig( *params, wtx.joinSplitPubKey); auto ret = wallet.GetNoteNullifier( wtx.vjoinsplit[0], address, dec, hSig, 1); EXPECT_NE(nullifier, ret); wallet.AddSpendingKey(sk); ret = wallet.GetNoteNullifier( wtx.vjoinsplit[0], address, dec, hSig, 1); EXPECT_EQ(nullifier, ret); }
static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64_t blockTime) { CMutableTransaction tx; tx.nLockTime = lockTime; SetMockTime(mockTime); CBlockIndex* block = nullptr; if (blockTime > 0) { auto locked_chain = wallet.chain().lock(); auto inserted = mapBlockIndex.emplace(GetRandHash(), new CBlockIndex); assert(inserted.second); const uint256& hash = inserted.first->first; block = inserted.first->second; block->nTime = blockTime; block->phashBlock = &hash; } CWalletTx wtx(&wallet, MakeTransactionRef(tx)); if (block) { wtx.SetMerkleBranch(block, 0); } { LOCK(cs_main); wallet.AddToWallet(wtx); } LOCK(wallet.cs_wallet); return wallet.mapWallet.at(wtx.GetHash()).nTimeSmart; }
// Simple benchmark for wallet coin selection. Note that it maybe be necessary // to build up more complicated scenarios in order to get meaningful // measurements of performance. From laanwj, "Wallet coin selection is probably // the hardest, as you need a wider selection of scenarios, just testing the // same one over and over isn't too useful. Generating random isn't useful // either for measurements." // (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484) static void CoinSelection(benchmark::State& state) { const CWallet wallet; vector<COutput> vCoins; LOCK(wallet.cs_wallet); while (state.KeepRunning()) { // Empty wallet. BOOST_FOREACH (COutput output, vCoins) delete output.tx; vCoins.clear(); // Add coins. for (int i = 0; i < 1000; i++) addCoin(1000 * COIN, wallet, vCoins); addCoin(3 * COIN, wallet, vCoins); set<pair<const CWalletTx*, unsigned int> > setCoinsRet; CAmount nValueRet; bool success = wallet.SelectCoinsMinConf(1003 * COIN, 1, 6, 0, vCoins, setCoinsRet, nValueRet); assert(success); assert(nValueRet == 1003 * COIN); assert(setCoinsRet.size() == 2); } }
// Simple benchmark for wallet coin selection. Note that it maybe be necessary // to build up more complicated scenarios in order to get meaningful // measurements of performance. From laanwj, "Wallet coin selection is probably // the hardest, as you need a wider selection of scenarios, just testing the // same one over and over isn't too useful. Generating random isn't useful // either for measurements." // (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484) static void CoinSelection(benchmark::State& state) { auto chain = interfaces::MakeChain(); const CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy()); std::vector<std::unique_ptr<CWalletTx>> wtxs; LOCK(wallet.cs_wallet); // Add coins. for (int i = 0; i < 1000; ++i) { addCoin(1000 * COIN, wallet, wtxs); } addCoin(3 * COIN, wallet, wtxs); // Create groups std::vector<OutputGroup> groups; for (const auto& wtx : wtxs) { COutput output(wtx.get(), 0 /* iIn */, 6 * 24 /* nDepthIn */, true /* spendable */, true /* solvable */, true /* safe */); groups.emplace_back(output.GetInputCoin(), 6, false, 0, 0); } const CoinEligibilityFilter filter_standard(1, 6, 0); const CoinSelectionParams coin_selection_params(true, 34, 148, CFeeRate(0), 0); while (state.KeepRunning()) { std::set<CInputCoin> setCoinsRet; CAmount nValueRet; bool bnb_used; bool success = wallet.SelectCoinsMinConf(1003 * COIN, filter_standard, groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used); assert(success); assert(nValueRet == 1003 * COIN); assert(setCoinsRet.size() == 2); } }
// Verify importwallet RPC starts rescan at earliest block with timestamp // greater or equal than key birthday. Previously there was a bug where // importwallet RPC would start the scan at the latest block with timestamp less // than or equal to key birthday. BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) { g_address_type = OUTPUT_TYPE_DEFAULT; g_change_type = OUTPUT_TYPE_DEFAULT; // Create two blocks with same timestamp to verify that importwallet rescan // will pick up both blocks, not just the first. const int64_t BLOCK_TIME = chainActive.Tip()->GetBlockTimeMax() + 5; SetMockTime(BLOCK_TIME); coinbaseTxns.emplace_back(*CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); coinbaseTxns.emplace_back(*CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); // Set key birthday to block time increased by the timestamp window, so // rescan will start at the block time. const int64_t KEY_TIME = BLOCK_TIME + TIMESTAMP_WINDOW; SetMockTime(KEY_TIME); coinbaseTxns.emplace_back(*CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); LOCK(cs_main); // Import key into wallet and call dumpwallet to create backup file. { CWallet wallet; LOCK(wallet.cs_wallet); wallet.mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME; wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); JSONRPCRequest request; request.params.setArray(); request.params.push_back((pathTemp / "wallet.backup").string()); vpwallets.insert(vpwallets.begin(), &wallet); ::dumpwallet(request); } // Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME // were scanned, and no prior blocks were scanned. { CWallet wallet; JSONRPCRequest request; request.params.setArray(); request.params.push_back((pathTemp / "wallet.backup").string()); vpwallets[0] = &wallet; ::importwallet(request); LOCK(wallet.cs_wallet); BOOST_CHECK_EQUAL(wallet.mapWallet.size(), 3); BOOST_CHECK_EQUAL(coinbaseTxns.size(), 103); for (size_t i = 0; i < coinbaseTxns.size(); ++i) { bool found = wallet.GetWalletTx(coinbaseTxns[i].GetHash()); bool expected = i >= 100; BOOST_CHECK_EQUAL(found, expected); } } SetMockTime(0); vpwallets.erase(vpwallets.begin()); }
qint64 WalletModel::getTotBalance() const { qint64 nTotBalance = 0; BOOST_FOREACH(const wallet_map::value_type& item, pWalletManager->GetWalletMap()) { CWallet* pwallet = pWalletManager->GetWallet(item.first.c_str()).get(); nTotBalance+=pwallet->GetBalance(); } return nTotBalance; }
TEST(wallet_tests, spent_note_is_from_me) { 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); auto wtx2 = GetValidSpend(sk, note, 5); EXPECT_FALSE(wallet.IsFromMe(wtx)); EXPECT_FALSE(wallet.IsFromMe(wtx2)); mapNoteData_t noteData; JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; CNoteData nd {sk.address(), nullifier}; noteData[jsoutpt] = nd; wtx.SetNoteData(noteData); EXPECT_FALSE(wallet.IsFromMe(wtx)); EXPECT_FALSE(wallet.IsFromMe(wtx2)); wallet.AddToWallet(wtx, true, NULL); EXPECT_FALSE(wallet.IsFromMe(wtx)); EXPECT_TRUE(wallet.IsFromMe(wtx2)); }
TEST(wallet_tests, get_conflicted_notes) { 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); auto wtx2 = GetValidSpend(sk, note, 5); auto wtx3 = GetValidSpend(sk, note, 10); auto hash2 = wtx2.GetHash(); auto hash3 = wtx3.GetHash(); // No conflicts for no spends EXPECT_EQ(0, wallet.GetConflicts(hash2).size()); wallet.AddToWallet(wtx, true, NULL); EXPECT_EQ(0, wallet.GetConflicts(hash2).size()); // No conflicts for one spend wallet.AddToWallet(wtx2, true, NULL); EXPECT_EQ(0, wallet.GetConflicts(hash2).size()); // Conflicts for two spends wallet.AddToWallet(wtx3, true, NULL); auto c3 = wallet.GetConflicts(hash2); EXPECT_EQ(2, c3.size()); EXPECT_EQ(std::set<uint256>({hash2, hash3}), c3); }
CPubKey CAccountHD::GenerateNewKey(CWallet& wallet, int keyChain) { CExtPubKey childKey; do { GetPubKey(childKey, keyChain); } while (wallet.HaveKey(childKey.pubkey.GetID())); /// LogPrintf("CAccount::GenerateNewKey(): NewHDKey [%s]\n", CBitcoinAddress(childKey.pubkey.GetID()).ToString()); if (!wallet.AddKeyPubKey(childKey.nChild, childKey.pubkey, *this, keyChain)) throw std::runtime_error("CAccount::GenerateNewKey(): AddKeyPubKey failed"); return childKey.pubkey; }
void MultisigDialog::on_saveRedeemScriptButton_clicked() { if(!model) return; CWallet *wallet = model->getWallet(); std::string redeemScript = ui->redeemScript->text().toStdString(); std::vector<unsigned char> scriptData(ParseHex(redeemScript)); CScript script(scriptData.begin(), scriptData.end()); CScriptID scriptID = script.GetID(); LOCK(wallet->cs_wallet); if(!wallet->HaveCScript(scriptID)) wallet->AddCScript(script); }
double benchmark_try_decrypt_notes(size_t nAddrs) { CWallet wallet; for (int i = 0; i < nAddrs; i++) { auto sk = libzcash::SpendingKey::random(); wallet.AddSpendingKey(sk); } auto sk = libzcash::SpendingKey::random(); auto tx = GetValidReceive(*pzcashParams, sk, 10, true); struct timeval tv_start; timer_start(tv_start); auto nd = wallet.FindMyNotes(tx); return timer_stop(tv_start); }
TEST(wallet_tests, find_note_in_tx) { 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); auto noteMap = wallet.FindMyNotes(wtx); EXPECT_EQ(2, noteMap.size()); JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; CNoteData nd {sk.address(), nullifier}; EXPECT_EQ(1, noteMap.count(jsoutpt)); EXPECT_EQ(nd, noteMap[jsoutpt]); }
// Check that GetImmatureCredit() returns a newly calculated value instead of // the cached value after a MarkDirty() call. // // This is a regression test written to verify a bugfix for the immature credit // function. Similar tests probably should be written for the other credit and // debit functions. BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup) { CWallet wallet; CWalletTx wtx(&wallet, MakeTransactionRef(coinbaseTxns.back())); LOCK2(cs_main, wallet.cs_wallet); wtx.hashBlock = chainActive.Tip()->GetBlockHash(); wtx.nIndex = 0; // Call GetImmatureCredit() once before adding the key to the wallet to // cache the current immature credit amount, which is 0. BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(), 0); // Invalidate the cached value, add the key, and make sure a new immature // credit amount is calculated. wtx.MarkDirty(); wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(), 50*COIN); }
CPubKey CAccountHD::GenerateNewKey(CWallet& wallet, CKeyMetadata& metadata, int keyChain) { CExtPubKey childKey; do { GetPubKey(childKey, keyChain); } while( wallet.HaveKey(childKey.pubkey.GetID()) );//fixme: (Post-2.1) (BIP44) No longer need wallet here. //LogPrintf("CAccount::GenerateNewKey(): NewHDKey [%s]\n", CGuldenAddress(childKey.pubkey.GetID()).ToString()); metadata.hdKeypath = std::string("m/44'/87'/") + std::to_string(m_nIndex) + "/" + std::to_string(keyChain) + "/" + std::to_string(childKey.nChild) + "'"; metadata.hdAccountUUID = getUUIDAsString(getUUID()); if (!wallet.AddHDKeyPubKey(childKey.nChild, childKey.pubkey, *this, keyChain)) throw std::runtime_error("CAccount::GenerateNewKey(): AddKeyPubKey failed"); return childKey.pubkey; }
CPubKey CAccount::GenerateNewKey(CWallet& wallet, int keyChain) { CKey secret; secret.MakeNewKey(true); CPubKey pubkey = secret.GetPubKey(); assert(secret.VerifyPubKey(pubkey)); if (!wallet.AddKeyPubKey(secret, pubkey, *this, keyChain)) throw std::runtime_error("CAccount::GenerateNewKey(): AddKeyPubKey failed"); return pubkey; }
void MultisigDialog::on_saveMultisigAddressButton_clicked() { if(!model) return; CWallet *wallet = model->getWallet(); std::string redeemScript = ui->redeemScript->text().toStdString(); std::string address = ui->multisigAddress->text().toStdString(); std::string label("multisig"); if(!model->validateAddress(QString(address.c_str()))) return; std::vector<unsigned char> scriptData(ParseHex(redeemScript)); CScript script(scriptData.begin(), scriptData.end()); CScriptID scriptID = script.GetID(); LOCK(wallet->cs_wallet); if(!wallet->HaveCScript(scriptID)) wallet->AddCScript(script); if(!wallet->mapAddressBook.count(CBitcoinAddress(address).Get())) wallet->SetAddressBookName(CBitcoinAddress(address).Get(), label); }
// darksilk: attempt to generate suitable proof-of-stake bool CBlock::SignBlock(CWallet& wallet, CAmount nFees) { // if we are trying to sign // something except proof-of-stake block template if (!vtx[0].vout[0].IsEmpty()) return false; // if we are trying to sign // a complete proof-of-stake block if (IsProofOfStake()) return true; static int64_t nLastCoinStakeSearchTime = GetAdjustedTime(); // startup timestamp CKey key; CTransaction txCoinStake; txCoinStake.nTime &= ~STAKE_TIMESTAMP_MASK; int64_t nSearchTime = txCoinStake.nTime; // search to current time if (nSearchTime > nLastCoinStakeSearchTime) { int64_t nSearchInterval = 1; if (wallet.CreateCoinStake(wallet, nBits, nSearchInterval, nFees, txCoinStake, key)) { if (txCoinStake.nTime >= pindexBest->GetPastTimeLimit()+1) { // make sure coinstake would meet timestamp protocol // as it would be the same as the block timestamp vtx[0].nTime = nTime = txCoinStake.nTime; // we have to make sure that we have no future timestamps in // our transactions set for (vector<CTransaction>::iterator it = vtx.begin(); it != vtx.end();) if (it->nTime > nTime) { it = vtx.erase(it); } else { ++it; } vtx.insert(vtx.begin() + 1, txCoinStake); hashMerkleRoot = BuildMerkleTree(); // append a signature to our block return key.Sign(GetHash(), vchBlockSig); } } nLastCoinStakeSearchInterval = nSearchTime - nLastCoinStakeSearchTime; nLastCoinStakeSearchTime = nSearchTime; } return false; }
double benchmark_increment_note_witnesses(size_t nTxs) { CWallet wallet; ZCIncrementalMerkleTree tree; auto sk = libzcash::SpendingKey::random(); wallet.AddSpendingKey(sk); // First block CBlock block1; for (int i = 0; i < nTxs; i++) { auto wtx = GetValidReceive(*pzcashParams, sk, 10, true); auto note = GetNote(*pzcashParams, 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); block1.vtx.push_back(wtx); } CBlockIndex index1(block1); index1.nHeight = 1; // Increment to get transactions witnessed wallet.ChainTip(&index1, &block1, tree, true); // Second block CBlock block2; block2.hashPrevBlock = block1.GetHash(); { auto wtx = GetValidReceive(*pzcashParams, sk, 10, true); auto note = GetNote(*pzcashParams, 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); block2.vtx.push_back(wtx); } CBlockIndex index2(block2); index2.nHeight = 2; struct timeval tv_start; timer_start(tv_start); wallet.ChainTip(&index2, &block2, tree, true); return timer_stop(tv_start); }
CPubKey CAccount::GenerateNewKey(CWallet& wallet, CKeyMetadata& metadata, int keyChain) { if (IsFixedKeyPool()) throw std::runtime_error(strprintf("GenerateNewKey called on a \"%sy\" witness account - this is invalid", GetAccountTypeString(m_Type).c_str())); CKey secret; secret.MakeNewKey(true); CPubKey pubkey = secret.GetPubKey(); assert(secret.VerifyPubKey(pubkey)); if (!wallet.AddKeyPubKey(secret, pubkey, *this, keyChain)) throw std::runtime_error("CAccount::GenerateNewKey(): AddKeyPubKey failed"); return pubkey; }
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); }
WalletFixture() { fTestNet = true; nTransactionFee = 0; bool fFirst = true; wallet = new CWallet("test_wallet1.dat"); wallet->LoadWallet(fFirst); wallet2 = new CWallet("test_wallet2.dat"); wallet2->LoadWallet(fFirst); wallet3 = new CWallet("test_wallet3.dat"); wallet3->LoadWallet(fFirst); strAddress1 = PubKeyToAddress(wallet->GetKeyFromKeyPool()); //cout << "a1:" << strAddress1 << "\n"; strAddressOut = PubKeyToAddress(wallet->GetKeyFromKeyPool()); strAddress2 = PubKeyToAddress(wallet2->GetKeyFromKeyPool()); strAddress3 = PubKeyToAddress(wallet3->GetKeyFromKeyPool()); //cout << "a2:" << strAddress2 << "\n"; strMultisignAddress = string("1,") + strAddress1 + "," + strAddress2; strMultisignAddress2 = string("2,") + strAddress1 + "," + strAddress2; strMultisignAddress2of3 = string("2,") + strAddress1 + "," + strAddress2 + "," + strAddress3; }
static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0) { balance += nValue; static int nextLockTime = 0; CMutableTransaction tx; tx.nLockTime = nextLockTime++; // so all transactions get different hashes tx.vout.resize(nInput + 1); tx.vout[nInput].nValue = nValue; if (fIsFromMe) { // IsFromMe() returns (GetDebit() > 0), and GetDebit() is 0 if vin.empty(), // so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe() tx.vin.resize(1); } std::unique_ptr<CWalletTx> wtx(new CWalletTx(&testWallet, MakeTransactionRef(std::move(tx)))); if (fIsFromMe) { wtx->fDebitCached = true; wtx->nDebitCached = 1; } COutput output(wtx.get(), nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */); vCoins.push_back(output); testWallet.AddToWallet(*wtx.get()); wtxn.emplace_back(std::move(wtx)); }
bool CreateCoinStake( CBlock &blocknew, CKey &key, vector<const CWalletTx*> &StakeInputs, uint64_t &CoinAge, CWallet &wallet, CBlockIndex* pindexPrev ) { int64_t CoinWeight; CBigNum StakeKernelHash; CTxDB txdb("r"); int64_t StakeWeightSum = 0; double StakeValueSum = 0; int64_t StakeWeightMin=MAX_MONEY; int64_t StakeWeightMax=0; uint64_t StakeCoinAgeSum=0; double StakeDiffSum = 0; double StakeDiffMax = 0; CTransaction &txnew = blocknew.vtx[1]; // second tx is coinstake //initialize the transaction txnew.nTime = blocknew.nTime & (~STAKE_TIMESTAMP_MASK); txnew.vin.clear(); txnew.vout.clear(); // Choose coins to use set <pair <const CWalletTx*,unsigned int> > CoinsToStake; int64_t BalanceToStake = wallet.GetBalance(); int64_t nValueIn = 0; //Request all the coins here, check reserve later if ( BalanceToStake<=0 || !wallet.SelectCoinsForStaking(BalanceToStake*2, txnew.nTime, CoinsToStake, nValueIn) ) { LOCK(MinerStatus.lock); MinerStatus.ReasonNotStaking+=_("No coins; "); if (fDebug) LogPrintf("CreateCoinStake: %s",MinerStatus.ReasonNotStaking); return false; } BalanceToStake -= nReserveBalance; if(fDebug2) LogPrintf("\nCreateCoinStake: Staking nTime/16= %d Bits= %u", txnew.nTime/16,blocknew.nBits); for(const auto& pcoin : CoinsToStake) { const CTransaction &CoinTx =*pcoin.first; //transaction that produced this coin unsigned int CoinTxN =pcoin.second; //index of this coin inside it CTxIndex txindex; { LOCK2(cs_main, wallet.cs_wallet); if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex)) continue; //error? } CBlock CoinBlock; //Block which contains CoinTx { LOCK2(cs_main, wallet.cs_wallet); if (!CoinBlock.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) continue; } // only count coins meeting min age requirement if (CoinBlock.GetBlockTime() + nStakeMinAge > txnew.nTime) continue; if (CoinTx.vout[CoinTxN].nValue > BalanceToStake) continue; { int64_t nStakeValue= CoinTx.vout[CoinTxN].nValue; StakeValueSum += nStakeValue /(double)COIN; //crazy formula... // todo: clean this // todo reuse calculated value for interst CBigNum bn = CBigNum(nStakeValue) * (blocknew.nTime-CoinTx.nTime) / CENT; bn = bn * CENT / COIN / (24 * 60 * 60); StakeCoinAgeSum += bn.getuint64(); } if(blocknew.nVersion==7) { NetworkTimer(); CoinWeight = CalculateStakeWeightV3(CoinTx,CoinTxN,GlobalCPUMiningCPID); StakeKernelHash= CalculateStakeHashV3(CoinBlock,CoinTx,CoinTxN,txnew.nTime,GlobalCPUMiningCPID,mdPORNonce); } else { uint64_t StakeModifier = 0; if(!FindStakeModifierRev(StakeModifier,pindexPrev)) continue; CoinWeight = CalculateStakeWeightV8(CoinTx,CoinTxN,GlobalCPUMiningCPID); StakeKernelHash= CalculateStakeHashV8(CoinBlock,CoinTx,CoinTxN,txnew.nTime,StakeModifier,GlobalCPUMiningCPID); } CBigNum StakeTarget; StakeTarget.SetCompact(blocknew.nBits); StakeTarget*=CoinWeight; StakeWeightSum += CoinWeight; StakeWeightMin=std::min(StakeWeightMin,CoinWeight); StakeWeightMax=std::max(StakeWeightMax,CoinWeight); double StakeKernelDiff = GetBlockDifficulty(StakeKernelHash.GetCompact())*CoinWeight; StakeDiffSum += StakeKernelDiff; StakeDiffMax = std::max(StakeDiffMax,StakeKernelDiff); if (fDebug2) { int64_t RSA_WEIGHT = GetRSAWeightByBlock(GlobalCPUMiningCPID); LogPrintf( "CreateCoinStake: V%d Time %.f, Por_Nonce %.f, Bits %jd, Weight %jd\n" " RSA_WEIGHT %.f\n" " Stk %72s\n" " Trg %72s\n" " Diff %0.7f of %0.7f\n", blocknew.nVersion, (double)txnew.nTime, mdPORNonce, (intmax_t)blocknew.nBits,(intmax_t)CoinWeight, (double)RSA_WEIGHT, StakeKernelHash.GetHex().c_str(), StakeTarget.GetHex().c_str(), StakeKernelDiff, GetBlockDifficulty(blocknew.nBits) ); } if( StakeKernelHash <= StakeTarget ) { // Found a kernel LogPrintf("\nCreateCoinStake: Found Kernel;\n"); blocknew.nNonce= mdPORNonce; vector<valtype> vSolutions; txnouttype whichType; CScript scriptPubKeyOut; CScript scriptPubKeyKernel; scriptPubKeyKernel = CoinTx.vout[CoinTxN].scriptPubKey; if (!Solver(scriptPubKeyKernel, whichType, vSolutions)) { LogPrintf("CreateCoinStake: failed to parse kernel\n"); break; } if (whichType == TX_PUBKEYHASH) // pay to address type { // convert to pay to public key type if (!wallet.GetKey(uint160(vSolutions[0]), key)) { LogPrintf("CreateCoinStake: failed to get key for kernel type=%d\n", whichType); break; // unable to find corresponding public key } scriptPubKeyOut << key.GetPubKey() << OP_CHECKSIG; } else if (whichType == TX_PUBKEY) // pay to public key type { valtype& vchPubKey = vSolutions[0]; if (!wallet.GetKey(Hash160(vchPubKey), key) || key.GetPubKey() != vchPubKey) { LogPrintf("CreateCoinStake: failed to get key for kernel type=%d\n", whichType); break; // unable to find corresponding public key } scriptPubKeyOut = scriptPubKeyKernel; } else { LogPrintf("CreateCoinStake: no support for kernel type=%d\n", whichType); break; // only support pay to public key and pay to address } txnew.vin.push_back(CTxIn(CoinTx.GetHash(), CoinTxN)); StakeInputs.push_back(pcoin.first); if (!txnew.GetCoinAge(txdb, CoinAge)) return error("CreateCoinStake: failed to calculate coin age"); int64_t nCredit = CoinTx.vout[CoinTxN].nValue; txnew.vout.push_back(CTxOut(0, CScript())); // First Must be empty txnew.vout.push_back(CTxOut(nCredit, scriptPubKeyOut)); //txnew.vout.push_back(CTxOut(0, scriptPubKeyOut)); LogPrintf("CreateCoinStake: added kernel type=%d credit=%f\n", whichType,CoinToDouble(nCredit)); LOCK(MinerStatus.lock); MinerStatus.KernelsFound++; MinerStatus.KernelDiffMax = 0; MinerStatus.KernelDiffSum = StakeDiffSum; return true; } } LOCK(MinerStatus.lock); MinerStatus.WeightSum = StakeWeightSum; MinerStatus.ValueSum = StakeValueSum; MinerStatus.WeightMin=StakeWeightMin; MinerStatus.WeightMax=StakeWeightMax; MinerStatus.CoinAgeSum=StakeCoinAgeSum; MinerStatus.KernelDiffMax = std::max(MinerStatus.KernelDiffMax,StakeDiffMax); MinerStatus.KernelDiffSum = StakeDiffSum; MinerStatus.nLastCoinStakeSearchInterval= txnew.nTime; return false; }
BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) { LOCK(cs_main); // Cap last block file size, and mine new block in a new block file. CBlockIndex* oldTip = chainActive.Tip(); GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE; CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); CBlockIndex* newTip = chainActive.Tip(); // Verify ScanForWalletTransactions picks up transactions in both the old // and new block files. { CWallet wallet; LOCK(wallet.cs_wallet); wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN); } // Prune the older block file. PruneOneBlockFile(oldTip->GetBlockPos().nFile); UnlinkPrunedFiles({oldTip->GetBlockPos().nFile}); // Verify ScanForWalletTransactions only picks transactions in the new block // file. { CWallet wallet; LOCK(wallet.cs_wallet); wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); BOOST_CHECK_EQUAL(newTip, wallet.ScanForWalletTransactions(oldTip)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN); } // Verify importmulti RPC returns failure for a key whose creation time is // before the missing block, and success for a key whose creation time is // after. { CWallet wallet; CWallet *backup = ::pwalletMain; ::pwalletMain = &wallet; UniValue keys; keys.setArray(); UniValue key; key.setObject(); key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(coinbaseKey.GetPubKey()))); key.pushKV("timestamp", 0); key.pushKV("internal", UniValue(true)); keys.push_back(key); key.clear(); key.setObject(); CKey futureKey; futureKey.MakeNewKey(true); key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(futureKey.GetPubKey()))); key.pushKV("timestamp", newTip->GetBlockTimeMax() + TIMESTAMP_WINDOW); key.pushKV("internal", UniValue(true)); keys.push_back(key); JSONRPCRequest request; request.params.setArray(); request.params.push_back(keys); UniValue response = importmulti(request); BOOST_CHECK_EQUAL(response.write(), strprintf("[{\"success\":false,\"error\":{\"code\":-1,\"message\":\"Failed to rescan before time %d, transactions may be missing.\"}},{\"success\":true}]", newTip->GetBlockTimeMax())); ::pwalletMain = backup; } // Verify ScanForWalletTransactions does not return null when the scan is // elided due to the nTimeFirstKey optimization. { CWallet wallet; { LOCK(wallet.cs_wallet); wallet.UpdateTimeFirstKey(newTip->GetBlockTime() + 7200 + 1); } BOOST_CHECK_EQUAL(newTip, wallet.ScanForWalletTransactions(newTip)); } }
BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) { LOCK(cs_main); // Cap last block file size, and mine new block in a new block file. CBlockIndex* const nullBlock = nullptr; CBlockIndex* oldTip = chainActive.Tip(); GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE; CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); CBlockIndex* newTip = chainActive.Tip(); // Verify ScanForWalletTransactions picks up transactions in both the old // and new block files. { CWallet wallet; LOCK(wallet.cs_wallet); wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(oldTip)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN); } // Prune the older block file. PruneOneBlockFile(oldTip->GetBlockPos().nFile); UnlinkPrunedFiles({oldTip->GetBlockPos().nFile}); // Verify ScanForWalletTransactions only picks transactions in the new block // file. { CWallet wallet; LOCK(wallet.cs_wallet); wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN); } // Verify importmulti RPC returns failure for a key whose creation time is // before the missing block, and success for a key whose creation time is // after. { CWallet wallet; vpwallets.insert(vpwallets.begin(), &wallet); UniValue keys; keys.setArray(); UniValue key; key.setObject(); key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(coinbaseKey.GetPubKey()))); key.pushKV("timestamp", 0); key.pushKV("internal", UniValue(true)); keys.push_back(key); key.clear(); key.setObject(); CKey futureKey; futureKey.MakeNewKey(true); key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(futureKey.GetPubKey()))); key.pushKV("timestamp", newTip->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1); key.pushKV("internal", UniValue(true)); keys.push_back(key); JSONRPCRequest request; request.params.setArray(); request.params.push_back(keys); UniValue response = importmulti(request); BOOST_CHECK_EQUAL(response.write(), strprintf("[{\"success\":false,\"error\":{\"code\":-1,\"message\":\"Rescan failed for key with creation " "timestamp %d. There was an error reading a block from time %d, which is after or within %d " "seconds of key creation, and could contain transactions pertaining to the key. As a result, " "transactions and coins using this key may not appear in the wallet. This error could be caused " "by pruning or data corruption (see bitcoind log for details) and could be dealt with by " "downloading and rescanning the relevant blocks (see -reindex and -rescan " "options).\"}},{\"success\":true}]", 0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW)); vpwallets.erase(vpwallets.begin()); } }
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); }
static void AddKey(CWallet& wallet, const CKey& key) { LOCK(wallet.cs_wallet); wallet.AddKeyPubKey(key, key.GetPubKey()); }
/** * This test covers methods on CWallet * GenerateNewZKey() * AddZKey() * LoadZKey() * LoadZKeyMetadata() */ TEST(wallet_zkeys_tests, store_and_load_zkeys) { SelectParams(CBaseChainParams::MAIN); CWallet wallet; // wallet should be empty std::set<libzcash::PaymentAddress> addrs; wallet.GetPaymentAddresses(addrs); ASSERT_EQ(0, addrs.size()); // wallet should have one key CZCPaymentAddress paymentAddress = wallet.GenerateNewZKey(); wallet.GetPaymentAddresses(addrs); ASSERT_EQ(1, addrs.size()); // verify wallet has spending key for the address auto addr = paymentAddress.Get(); ASSERT_TRUE(wallet.HaveSpendingKey(addr)); // manually add new spending key to wallet auto sk = libzcash::SpendingKey::random(); ASSERT_TRUE(wallet.AddZKey(sk)); // verify wallet did add it addr = sk.address(); ASSERT_TRUE(wallet.HaveSpendingKey(addr)); // verify spending key stored correctly libzcash::SpendingKey keyOut; wallet.GetSpendingKey(addr, keyOut); ASSERT_EQ(sk, keyOut); // verify there are two keys wallet.GetPaymentAddresses(addrs); ASSERT_EQ(2, addrs.size()); ASSERT_EQ(1, addrs.count(addr)); // Load a third key into the wallet sk = libzcash::SpendingKey::random(); ASSERT_TRUE(wallet.LoadZKey(sk)); // attach metadata to this third key addr = sk.address(); int64_t now = GetTime(); CKeyMetadata meta(now); ASSERT_TRUE(wallet.LoadZKeyMetadata(addr, meta)); // check metadata is the same CKeyMetadata m= wallet.mapZKeyMetadata[addr]; ASSERT_EQ(m.nCreateTime, now); }
void MultisigDialog::on_signTransactionButton_clicked() { ui->signedTransaction->clear(); if(!model) return; CWallet *wallet = model->getWallet(); // Decode the raw transaction std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString())); CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; try { ss >> tx; } catch(std::exception &e) { (void)e; return; } CTransaction mergedTx(tx); // Fetch previous transactions (inputs) std::map<COutPoint, CScript> mapPrevOut; for(unsigned int i = 0; i < mergedTx.vin.size(); i++) { CTransaction tempTx; MapPrevTx mapPrevTx; CTxDB txdb("r"); std::map<uint256, CTxIndex> unused; bool fInvalid; tempTx.vin.push_back(mergedTx.vin[i]); tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid); BOOST_FOREACH(const CTxIn& txin, tempTx.vin) { const uint256& prevHash = txin.prevout.hash; if(mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size() > txin.prevout.n) mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey; } } // Add the redeem scripts to the wallet keystore for(int i = 0; i < ui->inputs->count(); i++) { MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget()); if(entry) { QString redeemScriptStr = entry->getRedeemScript(); if(redeemScriptStr.size() > 0) { std::vector<unsigned char> scriptData(ParseHex(redeemScriptStr.toStdString())); CScript redeemScript(scriptData.begin(), scriptData.end()); wallet->AddCScript(redeemScript); } } } WalletModel::UnlockContext ctx(model->requestUnlock()); if(!ctx.isValid()) return; // Sign what we can bool fComplete = true; for(unsigned int i = 0; i < mergedTx.vin.size(); i++) { CTxIn& txin = mergedTx.vin[i]; if(mapPrevOut.count(txin.prevout) == 0) { fComplete = false; continue; } const CScript& prevPubKey = mapPrevOut[txin.prevout]; txin.scriptSig.clear(); SignSignature(*wallet, prevPubKey, mergedTx, i, SIGHASH_ALL); txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, tx.vin[i].scriptSig); if(!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, 0)) { fComplete = false; } } CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << mergedTx; ui->signedTransaction->setText(HexStr(ssTx.begin(), ssTx.end()).c_str()); if(fComplete) { ui->statusLabel->setText(tr("Transaction signature is complete")); ui->sendTransactionButton->setEnabled(true); } else { ui->statusLabel->setText(tr("Transaction is NOT completely signed")); ui->sendTransactionButton->setEnabled(false); } }