Esempio n. 1
0
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;
}