vector<UnspentTxOut> BlockDataViewer::getUnspentTxoutsForAddr160List( const vector<BinaryData>& scrAddrVec, bool ignoreZc) const { checkBDMisReady(); ScrAddrFilter* saf = bdmPtr_->getScrAddrFilter(); if (bdmPtr_->config().armoryDbType != ARMORY_DB_SUPER) { for (const auto& scrAddr : scrAddrVec) { if (!saf->hasScrAddress(scrAddr)) throw std::range_error("Don't have this scrAddr tracked"); } } vector<UnspentTxOut> UTXOs; for (const auto& scrAddr : scrAddrVec) { const auto& zcTxioMap = zeroConfCont_.getZCforScrAddr(scrAddr); StoredScriptHistory ssh; db_->getStoredScriptHistory(ssh, scrAddr); map<BinaryData, UnspentTxOut> scrAddrUtxoMap; db_->getFullUTXOMapForSSH(ssh, scrAddrUtxoMap); for (const auto& utxoPair : scrAddrUtxoMap) { auto zcIter = zcTxioMap.find(utxoPair.first); if (zcIter != zcTxioMap.end()) if (zcIter->second.hasTxInZC()) continue; UTXOs.push_back(utxoPair.second); } if (ignoreZc) continue; for (const auto& zcTxio : zcTxioMap) { if (!zcTxio.second.hasTxOutZC()) continue; if (zcTxio.second.hasTxInZC()) continue; TxOut txout = zcTxio.second.getTxOutCopy(db_); UnspentTxOut UTXO = UnspentTxOut(db_, txout, UINT32_MAX); UTXOs.push_back(UTXO); } } return UTXOs; }
bool ScrAddrFilter::startSideScan( function<void(const vector<string>&, double prog, unsigned time)> progress) { ScrAddrFilter* sca = child_.get(); if (sca != nullptr && !isScanning_) { isScanning_ = true; sca->scanThreadProgressCallback_ = progress; sca->scanScrAddrMapInNewThread(); if (sca->doScan_ != false) return true; } return false; }
bool ScrAddrFilter::registerAddressBatch( const map<shared_ptr<BtcWallet>, vector<BinaryData>>& wltNAddrMap, bool areNew) { /*** Gets a scrAddr ready for loading. Returns false if the BDM is initialized, in which case wltPtr will be called back with the address once it is ready doScan: 1: don't scan, new addresses 0: scan while taking count of the existing history -1: wipe existing history then scan ***/ //check if the BDM is initialized. There ought to be a better way than //checking the top block if (bdmIsRunning()) { //BDM is initialized and maintenance thread is running, check mode if (armoryDbType_ == ARMORY_DB_SUPER) { //supernode: nothing to do, signal the wallet that its scrAddr bulk //is ready by passing isNew as true. Pass a blank BinaryData for the //top scanned block hash in this case, it will be ignored anyways while (mergeLock_.fetch_or(1, memory_order_acquire)); for (auto& batch : wltNAddrMap) { for (auto& sa : batch.second) scrAddrDataForSideScan_.scrAddrsToMerge_.insert({ sa, 0 }); mergeFlag_ = true; } mergeLock_.store(0, memory_order_release); for (auto& batch : wltNAddrMap) { batch.first->prepareScrAddrForMerge( batch.second, true, BinaryData()); batch.first->needsRefresh(); } return false; } //check DB for the scrAddr's SSH StoredScriptHistory ssh; ScrAddrFilter* topChild = this; while (topChild->child_) topChild = topChild->child_.get(); topChild->child_ = shared_ptr<ScrAddrFilter>(copy()); ScrAddrFilter* sca = topChild->child_.get(); sca->setRoot(this); if (!areNew) { //mark existing history for wipe and rescan from block 0 sca->doScan_ = true; for (auto& batch : wltNAddrMap) { for (const auto& scrAddr : batch.second) sca->regScrAddrForScan(scrAddr, 0); } } else { //mark addresses as fresh to skip DB scan sca->doScan_ = false; for (auto& batch : wltNAddrMap) { for (const auto& scrAddr : batch.second) sca->regScrAddrForScan(scrAddr, 0); } } sca->buildSideScanData(wltNAddrMap); flagForScanThread(); return false; } else { //BDM isnt initialized yet, the maintenance thread isnt running, //just register the scrAddr and return true. for (auto& batch : wltNAddrMap) { for (const auto& scrAddr : batch.second) scrAddrMap_.insert(make_pair(scrAddr, 0)); } return true; } }
void ScrAddrFilter::scanScrAddrThread() { //Only one wallet at a time uint32_t endBlock = currentTopBlockHeight(); vector<string> wltIDs = scrAddrDataForSideScan_.getWalletIDString(); BinaryData topScannedBlockHash; { LMDBEnv::Transaction tx; lmdb_->beginDBTransaction(&tx, HEADERS, LMDB::ReadOnly); StoredHeader sbh; lmdb_->getBareHeader(sbh, endBlock); topScannedBlockHash = sbh.thisHash_; } if(doScan_ == false) { //new addresses, set their last seen block in the SSH entries setSSHLastScanned(currentTopBlockHeight()); } else { //wipe SSH vector<BinaryData> saVec; for (const auto& scrAddrPair : scrAddrMap_) saVec.push_back(scrAddrPair.first); wipeScrAddrsSSH(saVec); saVec.clear(); //scan from 0 topScannedBlockHash = applyBlockRangeToDB(0, endBlock, wltIDs); } for (auto& batch : scrAddrDataForSideScan_.wltNAddrMap_) { if (batch.first->hasBdvPtr()) { //merge with main ScrAddrScanData object merge(topScannedBlockHash); vector<BinaryData> addressVec; addressVec.reserve(scrAddrMap_.size()); //notify the wallets that the scrAddr are ready for (auto& scrAddrPair : scrAddrMap_) { addressVec.push_back(scrAddrPair.first); } if (!scrAddrMap_.empty()) { batch.first->prepareScrAddrForMerge(addressVec, !((bool)doScan_), topScannedBlockHash); //notify the bdv that it needs to refresh through the wallet batch.first->needsRefresh(); } } } //clean up if (root_ != nullptr) { ScrAddrFilter* root = root_; shared_ptr<ScrAddrFilter> newChild = child_; root->child_ = newChild; root->isScanning_ = false; if (root->child_) root->flagForScanThread(); } for (const auto& wID : wltIDs) LOGINFO << "Done with side scan of wallet " << wID; }
void ScrAddrFilter::scanScrAddrThread() { //Only one wallet at a time uint32_t endBlock = currentTopBlockHeight(); vector<string> wltIDs = scrAddrDataForSideScan_.getWalletIDString(); BinaryData topScannedBlockHash; { //grab top scanned block from sdbi StoredDBInfo sdbi; lmdb_->getStoredDBInfo(SSH, sdbi); topScannedBlockHash = sdbi.topScannedBlkHash_; } if(doScan_ == false) { //new addresses, create the DB key and //set the last seen block in the SSH entries buildSSHKeys(); setSSHLastScanned(currentTopBlockHeight()); } else { //no need for that otherwise, the addresses will be scanned, which //will create the DB keys and set the proper last seen block for //each SSH //instead, let's make sure the SSH we are scanning have no prior history //by wiping them vector<BinaryData> saVec; for (const auto& scrAddrPair : scrAddrMap_) saVec.push_back(scrAddrPair.first); wipeScrAddrsSSH(saVec); saVec.clear(); //scan from height 0 topScannedBlockHash = applyBlockRangeToDB(0, endBlock, wltIDs); } for (auto& batch : scrAddrDataForSideScan_.wltNAddrMap_) { if (batch.first->hasBdvPtr()) { //merge with main ScrAddrScanData object merge(topScannedBlockHash); vector<BinaryData> addressVec; addressVec.reserve(scrAddrMap_.size()); //notify the wallets that the scrAddr are ready for (auto& scrAddrPair : scrAddrMap_) { addressVec.push_back(scrAddrPair.first); } if (!scrAddrMap_.empty()) { batch.first->prepareScrAddrForMerge(addressVec, !((bool)doScan_), topScannedBlockHash); //notify the bdv that it needs to refresh through the wallet batch.first->needsRefresh(); } } } //clean up if (root_ != nullptr) { ScrAddrFilter* root = root_; shared_ptr<ScrAddrFilter> newChild = child_; root->child_ = newChild; root->isScanning_ = false; if (root->child_) root->flagForScanThread(); } for (const auto& wID : wltIDs) LOGINFO << "Done with side scan of wallet " << wID; }