Example #1
0
   def startBitcoind(self, callback):
      self.btcOut, self.btcErr = None,None
      if self.disabled:
         LOGERROR('SDM was disabled, must be re-enabled before starting')
         return

      LOGINFO('Called startBitcoind')

      if self.isRunningBitcoind() or TheTDM.getTDMState()=='Downloading':
         raise self.BitcoindError, 'Looks like we have already started theSDM'

      if not os.path.exists(self.executable):
         raise self.BitcoindError, 'Could not find bitcoind'

      
      chk1 = os.path.exists(self.useTorrentFile)
      chk2 = self.shouldTryBootstrapTorrent()
      chk3 = TheTDM.getTDMState()=='ReadyToStart'

      if chk1 and chk2 and chk3:
         TheTDM.startDownload()
      else:
         self.launchBitcoindAndGuardian()
            
      #New backend code: we wont be polling the SDM state in the main thread
      #anymore, instead create a thread at bitcoind start to poll the SDM state
      #and notify the main thread once bitcoind is ready, then terminates
      self.pollBitcoindState(callback, async=True)
Example #2
0
    def startBitcoind(self, callback):
        self.btcOut, self.btcErr = None, None
        if self.disabled:
            LOGERROR('SDM was disabled, must be re-enabled before starting')
            return

        LOGINFO('Called startGroestlcoind')

        if self.isRunningBitcoind() or TheTDM.getTDMState() == 'Downloading':
            raise self.BitcoindError, 'Looks like we have already started theSDM'

        if not os.path.exists(self.executable):
            raise self.BitcoindError, 'Could not find groestlcoind'

        chk1 = os.path.exists(self.useTorrentFile)
        chk2 = self.shouldTryBootstrapTorrent()
        chk3 = TheTDM.getTDMState() == 'ReadyToStart'

        if chk1 and chk2 and chk3:
            TheTDM.startDownload()
        else:
            self.launchBitcoindAndGuardian()

        #New backend code: we wont be polling the SDM state in the main thread
        #anymore, instead create a thread at groestlcoind start to poll the SDM state
        #and notify the main thread once groestlcoind is ready, then terminates
        self.pollBitcoindState(callback, async=True)
Example #3
0
    def startBitcoind(self):
        self.btcOut, self.btcErr = None, None
        if self.disabled:
            LOGERROR('SDM was disabled, must be re-enabled before starting')
            return

        LOGINFO('Called startBitcoind')

        if self.isRunningBitcoind() or TheTDM.getTDMState() == 'Downloading':
            raise self.BitcoindError, 'Looks like we have already started theSDM'

        if not os.path.exists(self.executable):
            raise self.BitcoindError, 'Could not find bitcoind'

        chk1 = os.path.exists(self.useTorrentFile)
        chk2 = self.shouldTryBootstrapTorrent()
        chk3 = TheTDM.getTDMState() == 'ReadyToStart'

        if chk1 and chk2 and chk3:
            TheTDM.startDownload()
        else:
            self.launchBitcoindAndGuardian()
Example #4
0
   def startBitcoind(self):
      self.btcOut, self.btcErr = None,None
      if self.disabled:
         LOGERROR('SDM was disabled, must be re-enabled before starting')
         return

      LOGINFO('Called startBitcoind')

      if self.isRunningBitcoind() or TheTDM.getTDMState()=='Downloading':
         raise self.BitcoindError, 'Looks like we have already started theSDM'

      if not os.path.exists(self.executable):
         raise self.BitcoindError, 'Could not find peercoind'

      
      chk1 = os.path.exists(self.useTorrentFile)
      chk2 = self.shouldTryBootstrapTorrent()
      chk3 = TheTDM.getTDMState()=='ReadyToStart'

      if chk1 and chk2 and chk3:
         TheTDM.startDownload()
      else:
         self.launchBitcoindAndGuardian()
Example #5
0
   def shouldTryBootstrapTorrent(self):
      if DISABLE_TORRENTDL or TheTDM.getTDMState()=='Disabled':
         return False

      # The only torrent we have is for the primary Bitcoin network
      if not MAGIC_BYTES=='\xf9\xbe\xb4\xd9':
         return False
      
         

      if TheTDM.torrentSize:
         bootfile = os.path.join(self.satoshiHome, 'bootstrap.dat')
         if os.path.exists(bootfile):
            if os.path.getsize(bootfile) >= TheTDM.torrentSize/2:
               LOGWARN('Looks like a full bootstrap is already here')
               LOGWARN('Skipping torrent download')
               return False
               

      # If they don't even have a BTC_HOME_DIR, corebtc never been installed
      blockDir = os.path.join(self.satoshiHome, 'blocks')
      if not os.path.exists(self.satoshiHome) or not os.path.exists(blockDir):
         return True
      
      # Get the cumulative size of the blk*.dat files
      blockDirSize = sum([os.path.getsize(os.path.join(blockDir, a)) \
                  for a in os.listdir(blockDir) if a.startswith('blk')])
      sizeStr = bytesToHumanSize(blockDirSize)
      LOGINFO('Total size of files in %s is %s' % (blockDir, sizeStr))

      # If they have only a small portion of the blockchain, do it
      szThresh = 100*MEGABYTE if USE_TESTNET else 6*GIGABYTE
      if blockDirSize < szThresh:
         return True

      # So far we know they have a BTC_HOME_DIR, with more than 6GB in blocks/
      # The only thing that can induce torrent now is if we have a partially-
      # finished bootstrap file bigger than the blocks dir.
      bootFiles = ['','']
      bootFiles[0] = os.path.join(self.satoshiHome, 'bootstrap.dat')
      bootFiles[1] = os.path.join(self.satoshiHome, 'bootstrap.dat.partial')
      for fn in bootFiles:
         if os.path.exists(fn):
            if os.path.getsize(fn) > blockDirSize:
               return True
            
      # Okay, we give up -- just download [the rest] via P2P
      return False
Example #6
0
   def shouldTryBootstrapTorrent(self):
      if DISABLE_TORRENTDL or TheTDM.getTDMState()=='Disabled':
         return False

      # The only torrent we have is for the primary Bitcoin network
      if not MAGIC_BYTES=='\xf9\xbe\xb4\xd9':
         return False
      
         

      if TheTDM.torrentSize:
         bootfile = os.path.join(self.satoshiHome, 'bootstrap.dat')
         if os.path.exists(bootfile):
            if os.path.getsize(bootfile) >= TheTDM.torrentSize/2:
               LOGWARN('Looks like a full bootstrap is already here')
               LOGWARN('Skipping torrent download')
               return False
               

      # If they don't even have a BTC_HOME_DIR, corebtc never been installed
      blockDir = os.path.join(self.satoshiHome, 'blocks')
      if not os.path.exists(self.satoshiHome) or not os.path.exists(blockDir):
         return True
      
      # Get the cumulative size of the blk*.dat files
      blockDirSize = sum([os.path.getsize(os.path.join(blockDir, a)) \
                  for a in os.listdir(blockDir) if a.startswith('blk')])
      sizeStr = bytesToHumanSize(blockDirSize)
      LOGINFO('Total size of files in %s is %s' % (blockDir, sizeStr))

      # If they have only a small portion of the blockchain, do it
      szThresh = 100*MEGABYTE if USE_TESTNET else 6*GIGABYTE
      if blockDirSize < szThresh:
         return True

      # So far we know they have a BTC_HOME_DIR, with more than 6GB in blocks/
      # The only thing that can induce torrent now is if we have a partially-
      # finished bootstrap file bigger than the blocks dir.
      bootFiles = ['','']
      bootFiles[0] = os.path.join(self.satoshiHome, 'bootstrap.dat')
      bootFiles[1] = os.path.join(self.satoshiHome, 'bootstrap.dat.partial')
      for fn in bootFiles:
         if os.path.exists(fn):
            if os.path.getsize(fn) > blockDirSize:
               return True
            
      # Okay, we give up -- just download [the rest] via P2P
      return False
Example #7
0
    def getSDMStateLogic(self):

        if self.disabled:
            return 'GroestlcoindMgmtDisabled'

        if self.failedFindExe:
            return 'GroestlcoindExeMissing'

        if self.failedFindHome:
            return 'GroestlcoindHomeMissing'

        if TheTDM.isRunning():
            return 'TorrentSynchronizing'

        latestInfo = self.getTopBlockInfo()

        if self.groestlcoind == None and latestInfo['error'] == 'Uninitialized':
            return 'GroestlcoindNeverStarted'

        if not self.isRunningBitcoind():
            # Not running at all:  either never started, or process terminated
            if not self.btcErr == None and len(self.btcErr) > 0:
                errstr = self.btcErr.replace(',', ' ').replace('.',
                                                               ' ').replace(
                                                                   '!', ' ')
                errPcs = set([a.lower() for a in errstr.split()])
                runPcs = set(
                    ['cannot', 'obtain', 'lock', 'already', 'running'])
                dbePcs = set([
                    'database', 'recover', 'backup', 'except', 'wallet', 'dat'
                ])
                if len(errPcs.intersection(runPcs)) >= (len(runPcs) - 1):
                    return 'GroestlcoindAlreadyRunning'
                elif len(errPcs.intersection(dbePcs)) >= (len(dbePcs) - 1):
                    return 'GroestlcoindDatabaseEnvError'
                else:
                    return 'GroestlcoindUnknownCrash'
            else:
                return 'GroestlcoindNotAvailable'
        elif not self.bitcoindIsResponsive():
            # Running but not responsive... must still be initializing
            return 'GroestlcoindInitializing'
        else:
            # If it's responsive, get the top block and check
            # TODO: These conditionals are based on experimental results.  May
            #       not be accurate what the specific errors mean...
            if latestInfo['error'] == 'ValueError':
                return 'GroestlcoindWrongPassword'
            elif latestInfo['error'] == 'JsonRpcException':
                return 'GroestlcoindInitializing'
            elif latestInfo['error'] == 'SocketError':
                return 'GroestlcoindNotAvailable'

            if 'GroestlcoindReady' in self.circBufferState:
                # If ready, always ready
                return 'GroestlcoindReady'

            # If we get here, groestlcoind is gave us a response.
            secSinceLastBlk = RightNow() - latestInfo['toptime']
            blkspersec = latestInfo['blkspersec']
            #print 'Blocks per 10 sec:', ('UNKNOWN' if blkspersec==-1 else blkspersec*10)
            if secSinceLastBlk > 4 * HOUR or blkspersec == -1:
                return 'GroestlcoindSynchronizing'
            else:
                if blkspersec * 20 > 2 and not 'GroestlcoindReady' in self.circBufferState:
                    return 'GroestlcoindSynchronizing'
                else:
                    return 'GroestlcoindReady'
Example #8
0
    def tryToSetupTorrentDL(self, torrentPath):
        if self.torrentDisabled:
            LOGWARN('Tried to setup torrent download mgr but we are disabled')
            return False

        if not torrentPath or not os.path.exists(torrentPath):
            self.useTorrentFinalAnswer = False
            return False

        bootfile = os.path.join(self.satoshiHome, 'bootstrap.dat')
        bootfilePart = bootfile + '.partial'
        bootfileOld = bootfile + '.old'

        # cleartorrent.flag means we should remove any pre-existing files
        delTorrentFlag = os.path.join(ARMORY_HOME_DIR, 'cleartorrent.flag')
        if os.path.exists(delTorrentFlag):
            LOGWARN('Flag found to delete any pre-existing torrent files')
            if os.path.exists(bootfile): os.remove(bootfile)
            if os.path.exists(bootfilePart): os.remove(bootfilePart)
            if os.path.exists(bootfileOld): os.remove(bootfileOld)
            if os.path.exists(delTorrentFlag): os.remove(delTorrentFlag)

        TheTDM.setupTorrent(torrentPath, bootfile)
        if not TheTDM.getTDMState() == 'ReadyToStart':
            LOGERROR('Unknown error trying to start torrent manager')
            self.useTorrentFinalAnswer = False
            return False

        # We will tell the TDM to write status updates to the log file, and only
        # every 90 seconds.  After it finishes (or fails), simply launch groestlcoind
        # as we would've done without the torrent
        #####
        def torrentLogToFile(dpflag=Event(),
                             fractionDone=None,
                             timeEst=None,
                             downRate=None,
                             upRate=None,
                             activity=None,
                             statistics=None,
                             **kws):
            statStr = ''
            if fractionDone:
                statStr += '   Done: %0.1f%%  ' % (fractionDone * 100)
            if downRate:
                statStr += ' / DLRate: %0.1f/sec' % (downRate / 1024.)
            if timeEst:
                statStr += ' / TLeft: %s' % secondsToHumanTime(timeEst)
            if statistics:
                statStr += ' / Seeds: %d' % (statistics.numSeeds)
                statStr += ' / Peers: %d' % (statistics.numPeers)

            if len(statStr) == 0:
                statStr = 'No torrent info available'

            LOGINFO('Torrent: %s' % statStr)

        #####
        def torrentFinished():
            bootsz = '<Unknown>'
            if os.path.exists(bootfile):
                bootsz = bytesToHumanSize(os.path.getsize(bootfile))

            LOGINFO('Torrent finished; size of %s is %s', torrentPath, bootsz)
            LOGINFO('Remove the core btc databases before doing bootstrap')
            deleteBitcoindDBs()
            self.launchBitcoindAndGuardian()

        #####
        def warnUserHashFail():
            from PyQt4.QtGui import QMessageBox
            QMessageBox.warning(
                self, tr('Hash Failure'),
                tr("""The torrent download 
            is currently encountering too many packet hash failures to allow it to 
            progress properly. As a result, the torrent engine has been halted. You 
            should report this incident to the Groestlcoin Armory team and turn off this feature 
            until further notice."""), QMessageBox.Ok)

        #####
        def torrentFailed(errMsg=''):
            # Not sure there's actually anything we need to do here...
            if errMsg == 'hashFail':
                warnUserHashFail()

            bootsz = '<Unknown>'
            if os.path.exists(bootfile):
                bootsz = bytesToHumanSize(os.path.getsize(bootfile))

            LOGERROR('Torrent failed; size of %s is %s', torrentPath, bootsz)
            self.launchBitcoindAndGuardian()

        TheTDM.setSecondsBetweenUpdates(90)
        TheTDM.setCallback('displayFunc', torrentLogToFile)
        TheTDM.setCallback('finishedFunc', torrentFinished)
        TheTDM.setCallback('failedFunc', torrentFailed)

        LOGINFO('Bootstrap file is %s' % bytesToHumanSize(TheTDM.torrentSize))

        self.useTorrentFinalAnswer = True
        self.useTorrentFile = torrentPath
        return True
Example #9
0
   def getSDMStateLogic(self):

      if self.disabled:
         return 'BitcoindMgmtDisabled'

      if self.failedFindExe:
         return 'BitcoindExeMissing'

      if self.failedFindHome:
         return 'BitcoindHomeMissing'

      if TheTDM.isRunning():
         return 'TorrentSynchronizing'

      latestInfo = self.getTopBlockInfo()

      if self.bitcoind==None and latestInfo['error']=='Uninitialized':
         return 'BitcoindNeverStarted'

      if not self.isRunningBitcoind():
         # Not running at all:  either never started, or process terminated
         if not self.btcErr==None and len(self.btcErr)>0:
            errstr = self.btcErr.replace(',',' ').replace('.',' ').replace('!',' ')
            errPcs = set([a.lower() for a in errstr.split()])
            runPcs = set(['cannot','obtain','lock','already','running'])
            dbePcs = set(['database', 'recover','backup','except','wallet','dat'])
            if len(errPcs.intersection(runPcs))>=(len(runPcs)-1):
               return 'BitcoindAlreadyRunning'
            elif len(errPcs.intersection(dbePcs))>=(len(dbePcs)-1):
               return 'BitcoindDatabaseEnvError'
            else:
               return 'BitcoindUnknownCrash'
         else:
            return 'BitcoindNotAvailable'
      elif not self.bitcoindIsResponsive():
         # Running but not responsive... must still be initializing
         return 'BitcoindInitializing'
      else:
         # If it's responsive, get the top block and check
         # TODO: These conditionals are based on experimental results.  May
         #       not be accurate what the specific errors mean...
         if latestInfo['error']=='ValueError':
            return 'BitcoindWrongPassword'
         elif latestInfo['error']=='JsonRpcException':
            return 'BitcoindInitializing'
         elif latestInfo['error']=='SocketError':
            return 'BitcoindNotAvailable'

         if 'BitcoindReady' in self.circBufferState:
            # If ready, always ready
            return 'BitcoindReady'

         # If we get here, bitcoind is gave us a response.
         secSinceLastBlk = RightNow() - latestInfo['toptime']
         blkspersec = latestInfo['blkspersec']
         #print 'Blocks per 10 sec:', ('UNKNOWN' if blkspersec==-1 else blkspersec*10)
         if secSinceLastBlk > 4*HOUR or blkspersec==-1:
            return 'BitcoindSynchronizing'
         else:
            if blkspersec*20 > 2 and not 'BitcoindReady' in self.circBufferState:
               return 'BitcoindSynchronizing'
            else:
               return 'BitcoindReady'
Example #10
0
   def tryToSetupTorrentDL(self, torrentPath):
      if self.torrentDisabled:
         LOGWARN('Tried to setup torrent download mgr but we are disabled')
         return False
      
      if not torrentPath or not os.path.exists(torrentPath):
         self.useTorrentFinalAnswer = False
         return False

      bootfile = os.path.join(self.satoshiHome, 'bootstrap.dat')
      bootfilePart = bootfile + '.partial'
      bootfileOld  = bootfile + '.old'

      # cleartorrent.flag means we should remove any pre-existing files
      delTorrentFlag = os.path.join(ARMORY_HOME_DIR, 'cleartorrent.flag')
      if os.path.exists(delTorrentFlag):
         LOGWARN('Flag found to delete any pre-existing torrent files')
         if os.path.exists(bootfile):       os.remove(bootfile)
         if os.path.exists(bootfilePart):   os.remove(bootfilePart)
         if os.path.exists(bootfileOld):    os.remove(bootfileOld)
         if os.path.exists(delTorrentFlag): os.remove(delTorrentFlag)


      TheTDM.setupTorrent(torrentPath, bootfile)
      if not TheTDM.getTDMState()=='ReadyToStart':
         LOGERROR('Unknown error trying to start torrent manager')
         self.useTorrentFinalAnswer = False
         return False


      # We will tell the TDM to write status updates to the log file, and only
      # every 90 seconds.  After it finishes (or fails), simply launch bitcoind
      # as we would've done without the torrent
      #####
      def torrentLogToFile(dpflag=Event(), fractionDone=None, timeEst=None,
                           downRate=None, upRate=None, activity=None,
                           statistics=None, **kws):
         statStr = ''
         if fractionDone:
            statStr += '   Done: %0.1f%%  ' % (fractionDone*100)
         if downRate:
            statStr += ' / DLRate: %0.1f/sec' % (downRate/1024.)
         if timeEst:
            statStr += ' / TLeft: %s' % secondsToHumanTime(timeEst)
         if statistics:
            statStr += ' / Seeds: %d' % (statistics.numSeeds)
            statStr += ' / Peers: %d' % (statistics.numPeers)

         if len(statStr)==0:
            statStr = 'No torrent info available'

         LOGINFO('Torrent: %s' % statStr)

      #####
      def torrentFinished():
         bootsz = '<Unknown>'
         if os.path.exists(bootfile):
            bootsz = bytesToHumanSize(os.path.getsize(bootfile))

         LOGINFO('Torrent finished; size of %s is %s', torrentPath, bootsz)
         LOGINFO('Remove the core btc databases before doing bootstrap')
         deleteBitcoindDBs()
         self.launchBitcoindAndGuardian()

      #####
      def warnUserHashFail():
         from PyQt4.QtGui import QMessageBox
         QMessageBox.warning(self, tr('Hash Failure'), tr("""The torrent download 
            is currently encountering too many packet hash failures to allow it to 
            progress properly. As a result, the torrent engine has been halted. You 
            should report this incident to the Armory team and turn off this feature 
            until further notice."""), QMessageBox.Ok)      
      
      #####
      def torrentFailed(errMsg=''):
         # Not sure there's actually anything we need to do here...
         if errMsg == 'hashFail':
            warnUserHashFail()
            
         bootsz = '<Unknown>'
         if os.path.exists(bootfile):
            bootsz = bytesToHumanSize(os.path.getsize(bootfile))

         LOGERROR('Torrent failed; size of %s is %s', torrentPath, bootsz)
         self.launchBitcoindAndGuardian()
         

 
 
      TheTDM.setSecondsBetweenUpdates(90)
      TheTDM.setCallback('displayFunc',  torrentLogToFile)
      TheTDM.setCallback('finishedFunc', torrentFinished)
      TheTDM.setCallback('failedFunc',   torrentFailed)

      LOGINFO('Bootstrap file is %s' % bytesToHumanSize(TheTDM.torrentSize))
         
      self.useTorrentFinalAnswer = True
      self.useTorrentFile = torrentPath
      return True
Example #11
0
   def tryToSetupTorrentDL(self, torrentPath):
      if self.torrentDisabled:
         LOGWARN('Tried to setup torrent download mgr but we are disabled')
         return False
      
      if not torrentPath or not os.path.exists(torrentPath):
         self.useTorrentFinalAnswer = False
         return False

      bootfile = os.path.join(self.satoshiHome, 'bootstrap.dat')
      TheTDM.setupTorrent(torrentPath, bootfile)
      if not TheTDM.getTDMState()=='ReadyToStart':
         LOGERROR('Unknown error trying to start torrent manager')
         self.useTorrentFinalAnswer = False
         return False


      # We will tell the TDM to write status updates to the log file, and only
      # every 90 seconds.  After it finishes (or fails), simply launch bitcoind
      # as we would've done without the torrent
      #####
      def torrentLogToFile(dpflag=Event(), fractionDone=None, timeEst=None,
                           downRate=None, upRate=None, activity=None,
                           statistics=None, **kws):
         statStr = ''
         if fractionDone:
            statStr += '   Done: %0.1f%%  ' % (fractionDone*100)
         if downRate:
            statStr += ' / DLRate: %0.1f/sec' % (downRate/1024.)
         if timeEst:
            statStr += ' / TLeft: %s' % secondsToHumanTime(timeEst)
         if statistics:
            statStr += ' / Seeds: %d' % (statistics.numSeeds)
            statStr += ' / Peers: %d' % (statistics.numPeers)

         if len(statStr)==0:
            statStr = 'No torrent info available'

         LOGINFO('Torrent: %s' % statStr)

      #####
      def torrentFinished():
         bootsz = '<Unknown>'
         if os.path.exists(bootfile):
            bootsz = bytesToHumanSize(os.path.getsize(bootfile))

         LOGINFO('Torrent finished; size of %s is %s', torrentPath, bootsz)
         LOGINFO('Remove the core btc databases before doing bootstrap')
         deleteBitcoindDBs()
         self.launchBitcoindAndGuardian()

      #####
      def torrentFailed():
         # Not sure there's actually anything we need to do here...
         bootsz = '<Unknown>'
         if os.path.exists(bootfile):
            bootsz = bytesToHumanSize(os.path.getsize(bootfile))

         LOGERROR('Torrent failed; size of %s is %s', torrentPath, bootsz)
         self.launchBitcoindAndGuardian()
 
      TheTDM.setSecondsBetweenUpdates(90)
      TheTDM.setCallback('displayFunc',  torrentLogToFile)
      TheTDM.setCallback('finishedFunc', torrentFinished)
      TheTDM.setCallback('failedFunc',   torrentFailed)

      LOGINFO('Bootstrap file is %s' % bytesToHumanSize(TheTDM.torrentSize))
         
      self.useTorrentFinalAnswer = True
      self.useTorrentFile = torrentPath
      return True
Example #12
0
   def tryToSetupTorrentDL(self, torrentPath):
      if self.torrentDisabled:
         LOGWARN('Tried to setup torrent download mgr but we are disabled')
         return False
      
      if not torrentPath or not os.path.exists(torrentPath):
         self.useTorrentFinalAnswer = False
         return False

      bootfile = os.path.join(self.satoshiHome, 'bootstrap.dat')
      TheTDM.setupTorrent(torrentPath, bootfile)
      if not TheTDM.getTDMState()=='ReadyToStart':
         LOGERROR('Unknown error trying to start torrent manager')
         self.useTorrentFinalAnswer = False
         return False


      # We will tell the TDM to write status updates to the log file, and only
      # every 90 seconds.  After it finishes (or fails), simply launch bitcoind
      # as we would've done without the torrent
      #####
      def torrentLogToFile(dpflag=Event(), fractionDone=None, timeEst=None,
                           downRate=None, upRate=None, activity=None,
                           statistics=None, **kws):
         statStr = ''
         if fractionDone:
            statStr += '   Done: %0.1f%%  ' % (fractionDone*100)
         if downRate:
            statStr += ' / DLRate: %0.1f/sec' % (downRate/1024.)
         if timeEst:
            statStr += ' / TLeft: %s' % secondsToHumanTime(timeEst)
         if statistics:
            statStr += ' / Seeds: %d' % (statistics.numSeeds)
            statStr += ' / Peers: %d' % (statistics.numPeers)

         if len(statStr)==0:
            statStr = 'No torrent info available'

         LOGINFO('Torrent: %s' % statStr)

      #####
      def torrentFinished():
         bootsz = '<Unknown>'
         if os.path.exists(bootfile):
            bootsz = bytesToHumanSize(os.path.getsize(bootfile))

         LOGINFO('Torrent finished; size of %s is %s', torrentPath, bootsz)
         LOGINFO('Remove the core btc databases before doing bootstrap')
         deleteBitcoindDBs()
         self.launchBitcoindAndGuardian()

      #####
      def torrentFailed():
         # Not sure there's actually anything we need to do here...
         bootsz = '<Unknown>'
         if os.path.exists(bootfile):
            bootsz = bytesToHumanSize(os.path.getsize(bootfile))

         LOGERROR('Torrent failed; size of %s is %s', torrentPath, bootsz)
         self.launchBitcoindAndGuardian()
 
      TheTDM.setSecondsBetweenUpdates(90)
      TheTDM.setCallback('displayFunc',  torrentLogToFile)
      TheTDM.setCallback('finishedFunc', torrentFinished)
      TheTDM.setCallback('failedFunc',   torrentFailed)

      LOGINFO('Bootstrap file is %s' % bytesToHumanSize(TheTDM.torrentSize))
         
      self.useTorrentFinalAnswer = True
      self.useTorrentFile = torrentPath
      return True