class NotBitProtocol(LineReaderProcessProtocol): def __init__(self, *args, **kw): super().__init__(*args, **kw) self.sPowCalculated = AsyncSignal() self.sMessageAccepted = AsyncSignal(str) def lineReceived(self, fd, line): log.debug(f'NotBit daemon message: {line}') match = re.search(r'\[\d*\-\d*\-\d.*?\]\s(.*)$', line) if match: # Handle specific events msg = match.group(1) # POW if msg.startswith('Finished calculating proof-of-work'): ensure(self.sPowCalculated.emit()) # Message accepted ma = re.search(r'Accepted message from (BM-\w*)$', msg) if ma: ensure(self.sMessageAccepted.emit(ma.group(1))) if line.startswith('Failed to bind socket'): log.debug('Notbit failed to start')
def __init__(self, treeItem, obj, parent=None): super().__init__(parent) self.sDownload = AsyncSignal() self.treeItem = treeItem self.obj = obj self.ipfsPath = IPFSPath(self.obj.get('path')) self.hlayout = QHBoxLayout(self) self.setLayout(self.hlayout) self.btnOpen = QPushButton(iOpen()) self.btnOpen.setText(iOpen()) self.btnOpen.setIcon(getIcon('open.png')) self.btnOpen.clicked.connect(partialEnsure(self.onOpen)) self.btnHashmark = HashmarkThisButton(self.ipfsPath) self.btnClipboard = IPFSPathClipboardButton(self.ipfsPath) self.btnDownload = QToolButton(self) self.btnDownload.setText('Download') self.btnDownload.clicked.connect(self.onDl) self.btnDownload.hide() self.hlayout.addWidget(self.btnClipboard) self.hlayout.addWidget(self.btnHashmark) self.hlayout.addWidget(self.btnOpen) self.hlayout.addWidget(self.btnDownload)
def __init__(self, dagMetaMfsPath, dagMetaHistoryMax=12, offline=False, unpinOnUpdate=False, autoPreviousNode=True, cipheredMeta=False, autoUpdateDates=False, loop=None, portalCacheTtl=60): super().__init__() self.loop = loop if loop else asyncio.get_event_loop() self.lock = aiorwlock.RWLock(loop=loop) self.loaded = asyncio.Future() self.portalCache = TTLCache(1, portalCacheTtl) self._curMetaEntry = None self._dagRoot = None self._dagMeta = None self._dagCid = None self._offline = offline self._dagMetaMaxHistoryItems = dagMetaHistoryMax self._dagMetaMfsPath = dagMetaMfsPath self._unpinOnUpdate = unpinOnUpdate self._autoPrevious = autoPreviousNode self._autoUpdateDates = autoUpdateDates self._cipheredMeta = cipheredMeta self.dagUpdated = AsyncSignal(str) self.available = AsyncSignal(object) self.changed.connect(lambda: ensure(self.ipfsSave()))
def __init__(self, app): super().__init__() self._app = app self.objectStats = {} self.resources = {} self.profiles = {} self._currentProfile = None self.ipfsClient = None self._softIdent = None # Async signals self.ipfsConnectionReady = AsyncSignal() self.ipfsRepositoryReady = AsyncSignal() self.ipfsDaemonStarted = AsyncSignal() self.peers = Peers(self) self.node = Node(self) self.p2p = P2PServices(self) self.pubsub = PubsubMaster(self) self.pinner = None self.pinnerTask = None self.orbitConnector = None self.rsaExec = RSAExecutor(loop=self.loop, executor=self.app.executor) self.eccExec = ECCExecutor(loop=self.loop, executor=self.app.executor) self.curve25Exec = Curve25519(loop=self.loop, executor=self.app.executor)
def __init__(self, ipfsCtx, peerId, ipHandle, ipIdentifier: IPIdentifier, pingavg=0, validated=False, authenticated=False): self._ipfsCtx = ipfsCtx self._ipid = ipIdentifier self.peerId = peerId self.iphandle = ipHandle self.pinghist = collections.deque([], 4) self._didPongLast = None self._identLast = None self._validated = validated self._authenticated = authenticated self._authFailedAttemptsCn = 0 self._authFailedLtLast = None self._identMsg = None self._avatarPath = None self._avatarImage = self.defaultAvatarImage() self._dtReg = datetime.now() self._processData = {} self.sInactive = AsyncSignal(str) self.sStatusChanged = AsyncSignal()
def __init__(self, dagNode: dict, ipid): self._srv = dagNode self._didEx = didExplode(self.id) self._ipid = ipid self.sChanged = AsyncSignal() self.sChanged.connectTo(self.onChanged)
class StoredSeedObjectActionsWidget(ObjectActionsWidget): def __init__(self, treeItem, obj, parent=None): super().__init__(treeItem, obj, parent=parent) self.sCancel = AsyncSignal() self.sRestart = AsyncSignal() self.btnCancel = QPushButton(self) self.btnCancel.setText('Cancel') self.btnCancel.setIcon(getIcon('cancel.png')) self.btnCancel.clicked.connect( lambda: ensure(self.sCancel.emit())) self.btnCancel.hide() self.btnRestart = QPushButton(self) self.btnRestart.setText('Restart') self.btnRestart.clicked.connect( lambda: ensure(self.sRestart.emit())) self.btnRestart.hide() self.hlayout.addWidget(self.btnCancel) self.hlayout.addWidget(self.btnRestart) def showControls(self, download=False, cancel=False, restart=False): self.btnDownload.setVisible(download) self.btnCancel.setVisible(cancel) self.btnRestart.setVisible(restart)
def __init__(self, inactiveMax=40): self.__tokens = {} self.__byChannel = {} self.inactiveMax = inactiveMax self.active = True self.sChanChanged = AsyncSignal(str) self.sTokenStatus = AsyncSignal(str, str, int)
def __init__(self, loop, exitFuture, startedFuture, debug=False): self._loop = loop self.debug = debug self.eventStarted = asyncio.Event() self.exitFuture = exitFuture self.startedFuture = startedFuture self.errAlreadyRunning = False self.bstrapRe = re.compile( r'\s*\d+:\d+:\d+\.\d*\s\[\w*\] Bootstrapped (\d+)\%') self.sTorBootstrapStatus = AsyncSignal(int, str)
def __init__(self, ipfsCtx, **kw): self._igraphs = kw.pop('igraphs', {}) super().__init__(ipfsCtx, TOPIC_RDFBAZAAR, runPeriodic=True, filterSelfMessages=True, **kw) self.__serviceToken = secrets.token_hex(64) self.curRevUid = uid4() self.sExch = AsyncSignal(RDFGraphsExchangeMessage) self.sSparql = AsyncSignal(SparQLHeartbeatMessage)
def __init__(self, did, localId=False, ldCache=None): self._did = did self._document = {} self._docCid = None self._localId = localId self._lastResolve = None self._latestModified = None # JSON-LD expanded cache self.cache = ldCache if ldCache else LRUCache(4) # Async sigs self.sChanged = AsyncSignal(str) self.sServicesChanged = AsyncSignal() self.sChanged.connectTo(self.onDidChanged)
def __init__(self, dataPath: Path = None, runtimeConfig=None, dotPath=None, app=None, config=None, parent=None): self.app = app if app else runningApp() self.rtCfg = runtimeConfig self.dotPath = dotPath self._config = config self.sServiceStarted = AsyncSignal() if self.dotPath is None and parent and parent.dotPath: self.dotPath = f'{parent.dotPath}.{self.name}' if dataPath: self.rootPath = dataPath else: self.rootPath = self.app.dataPathForService( self.dotPath if self.dotPath else 'tmp') Service.__init__(self) KeyListener.__init__(self) self.pubsubServices = [] self.serviceConfigBind()
def __init__(self, ipfsCtx, topic='galacteek.default', runPeriodic=False, filterSelfMessages=True, maxMsgTsDiff=None, minMsgTsDiff=None, maxMessageSize=32768, scheduler=None, peered=False, **kw): self.throttler = None self.ipfsCtx = ipfsCtx self.topicBase = topic self.inQueue = asyncio.Queue(maxsize=256) self.errorsQueue = asyncio.Queue() self.lock = asyncio.Lock() self.peered = peered self.scheduler = scheduler self._filterSelfLegacy = filterSelfMessages self._receivedCount = 0 self._errorsCount = 0 self._runPeriodic = runPeriodic self._filters = [] self._peerActivity = {} GService.__init__(self, **kw) Configurable.__init__(self, applyNow=True) self._shuttingDown = False # Async sigs self.rawMessageReceived = AsyncSignal(str, str, bytes) self.rawMessageSent = AsyncSignal(str, str, str) self.tskServe = None self.tskProcess = None self.tskPeriodic = None self.addMessageFilter(self.filterMessageSize) self.addMessageFilter(self.filterPeerActivity) configModRegCallback(self.onConfigChanged, mod='galacteek.ipfs.pubsub')
class HashmarksSynchronizer: syncing = AsyncSignal(bool) async def syncTask(self): try: while True: await asyncio.sleep(60) sources = await database.hashmarkSourcesNeedSync( minutes=60 * 3) count = len(sources) if count > 0: log.debug( 'Unsynced sources: {}, syncing now'.format(count)) await self.sync() except asyncio.CancelledError: log.debug('Sync task cancelled') except Exception as err: log.debug(f'Sync task error: {err}') async def sync(self): _count, _scount = 0, 0 await self.syncing.emit(True) log.info('Synchronizing hashmarks database ...') sources = await database.hashmarkSourceAll() for source in sources: if source.type == HashmarkSource.TYPE_PYMODULE: loader = ModuleCatalogLoader() elif source.type == HashmarkSource.TYPE_GITREPOS: loader = GitCatalogLoader() elif source.type == HashmarkSource.TYPE_IPFSMARKS_LEGACY: loader = IPFSMarksCatalogLoader() else: continue log.info('Synchronizing: {}'.format(source)) _count += await loader.load(source) if _count >= 0: _scount += 1 source.syncedlast = datetime.now() await source.save() shistory = database.HashmarkSyncHistory( hashmarkstotal=await database.hashmarksCount(), hashmarksadded=_count, srcsynccount=_scount ) await shistory.save() await self.syncing.emit(False)
def __init__(self, did, localId=False, ldCache=None): self._did = did self._p2pServices = weakref.WeakValueDictionary() self._document = {} self._docCid = None self._localId = localId self._lastResolve = None self._latestModified = None self._unlocked = False self.rsaAgent = None # JSON-LD expanded cache self.cache = ldCache if ldCache else LRUCache(4) # Async sigs self.sChanged = AsyncSignal(str) self.sServicesChanged = AsyncSignal() self.sServiceAvailable = AsyncSignal(IPIdentifier, IPService) self.sChanged.connectTo(self.onDidChanged)
def __init__(self, mailDirPath: Path, mailBoxesPath: Path): super().__init__() self.clearMailDirPath = mailDirPath self.clearMailDirPath.mkdir(parents=True, exist_ok=True) self.clearMailDir = Maildir(str(self.clearMailDirPath)) self.mailBoxesPath = mailBoxesPath self.mailBoxes = {} # Signals self.sNewMessage = AsyncSignal(str)
def __init__(self, seed, objidx, oname, odescr, parent=None): super().__init__(seed, objidx, oname, odescr, type=seedingObjItemType) self.stat = None self.oController = StoredSeedObjectActionsWidget(self, self.objdescr) self.oController.fLayout() self.oController.sDownload.connectTo(self.onDownload) self.oController.sCancel.connectTo(self.onCancel) self.oController.sRestart.connectTo(self.onRestart) self._job = None self.sJobActive = AsyncSignal(SeedingObjectTreeItem, bool)
def __init__(self, treeItem, obj, parent=None): super().__init__(treeItem, obj, parent=parent) self.sCancel = AsyncSignal() self.sRestart = AsyncSignal() self.btnCancel = QPushButton(self) self.btnCancel.setText('Cancel') self.btnCancel.setIcon(getIcon('cancel.png')) self.btnCancel.clicked.connect( lambda: ensure(self.sCancel.emit())) self.btnCancel.hide() self.btnRestart = QPushButton(self) self.btnRestart.setText('Restart') self.btnRestart.clicked.connect( lambda: ensure(self.sRestart.emit())) self.btnRestart.hide() self.hlayout.addWidget(self.btnCancel) self.hlayout.addWidget(self.btnRestart)
def __init__(self, ipfsCtx, client, topic='galacteek.default', runPeriodic=False, filterSelfMessages=True, maxMsgTsDiff=None, minMsgTsDiff=None, maxMessageSize=32768, scheduler=None): self.client = client self.ipfsCtx = ipfsCtx self.topic = topic self.inQueue = asyncio.Queue() self.errorsQueue = asyncio.Queue() self.lock = asyncio.Lock() self.scheduler = scheduler self._receivedCount = 0 self._errorsCount = 0 self._runPeriodic = runPeriodic self._filters = [] self._peerActivity = {} self._maxMsgTsDiff = maxMsgTsDiff self._minMsgTsDiff = minMsgTsDiff self._maxMessageSize = maxMessageSize # Async sigs self.rawMessageReceived = AsyncSignal(str, str, bytes) self.rawMessageSent = AsyncSignal(str, str, str) self.tskServe = None self.tskProcess = None self.tskPeriodic = None self.addMessageFilter(self.filterMessageSize) self.addMessageFilter(self.filterPeerActivity) if filterSelfMessages is True: self.addMessageFilter(self.filterSelf)
def __init__(self, name, rPath: Path, store, **kw): super(IGraph, self).__init__(store, **kw) self.name = name self.rPath = rPath self.exportsPath = self.rPath.joinpath('exports') self.exportsPath.mkdir(parents=True, exist_ok=True) self.xmlExportPath = self.exportsPath.joinpath('graph.xml') self.dbPath = str(self.rPath.joinpath('g_rdf.db')) self.dbUri = Literal(f"sqlite:///{self.dbPath}") self.cid = None self.sCidChanged = AsyncSignal(str, str)
class JSONPubsubService(PubsubService): """ JSON pubsub listener, handling incoming messages as JSON objects """ jsonMessageReceived = AsyncSignal(str, str, bytes) def msgDataToJson(self, msg): """ Decode JSON data contained in a pubsub message """ try: return json.loads(msg['data'].decode()) except Exception: logger.debug('Could not decode JSON message data') return None async def processMessages(self): try: while True: data = await self.inQueue.get() if data is None: continue msg = self.msgDataToJson(data) if msg is None: self.debug('Invalid JSON message') continue sender = data['from'] if isinstance(data['from'], str) else \ data['from'].decode() try: gHub.publish(keyPsJson, (sender, self.topic, msg)) await self.processJsonMessage(sender, msg) except Exception as exc: self.debug('processJsonMessage error: {}'.format(str(exc))) traceback.print_exc() await self.errorsQueue.put((msg, exc)) self._errorsCount += 1 except asyncio.CancelledError: self.debug('JSON process cancelled') except Exception as err: self.debug('JSON process exception: {}'.format(str(err))) async def processJsonMessage(self, sender, msg): """ Implement this method to process incoming JSON messages""" return True
class MockEthereumController: ethConnected = AsyncSignal(bool) ethNewBlock = AsyncSignal(str) def changeParams(self, params): self._params = params def getWeb3(self): return None async def stop(self): pass async def start(self): pass async def latestBlock(self): pass async def getBlock(self, block='latest'): pass async def connected(self): return False
class ObjectActionsWidget(QWidget): def __init__(self, treeItem, obj, parent=None): super().__init__(parent) self.sDownload = AsyncSignal() self.treeItem = treeItem self.obj = obj self.ipfsPath = IPFSPath(self.obj.get('path')) self.hlayout = QHBoxLayout(self) self.setLayout(self.hlayout) self.btnOpen = QPushButton(iOpen()) self.btnOpen.setText(iOpen()) self.btnOpen.setIcon(getIcon('open.png')) self.btnOpen.clicked.connect(partialEnsure(self.onOpen)) self.btnHashmark = HashmarkThisButton(self.ipfsPath) self.btnClipboard = IPFSPathClipboardButton(self.ipfsPath) self.btnDownload = QToolButton(self) self.btnDownload.setText('Download') self.btnDownload.clicked.connect(self.onDl) self.btnDownload.hide() self.hlayout.addWidget(self.btnClipboard) self.hlayout.addWidget(self.btnHashmark) self.hlayout.addWidget(self.btnOpen) self.hlayout.addWidget(self.btnDownload) def fLayout(self): self.hlayout.addItem( QSpacerItem(10, 10, QSizePolicy.Expanding, QSizePolicy.Minimum)) def onDl(self): ensure(self.sDownload.emit()) def showControls(self, download=False): self.btnDownload.setVisible(download) async def onOpen(self, *a): app = QApplication.instance() ensure(app.resourceOpener.open(self.obj['path']))
class PeersCountGraphView(BaseTimedGraph): peersCountFetched = AsyncSignal(int) def __init__(self, parent=None): super(PeersCountGraphView, self).__init__(parent=parent) self.setObjectName('PeersCountGraph') self.peersCountFetched.connectTo(self.onPeersCount) self.axisY.setTitleText(iPeers()) pen = QPen(QColor('#60cccf')) pen.setWidth(2) self.seriesPeers = QLineSeries() self.seriesPeers.setName('Peers') self.seriesPeers.setPen(pen) self.chart.addSeries(self.seriesPeers) self.seriesPeers.attachAxis(self.axisX) self.seriesPeers.attachAxis(self.axisY) self.setChart(self.chart) async def onPeersCount(self, count): dtNow = QDateTime.currentDateTime() dtMsecs = dtNow.toMSecsSinceEpoch() delta = 120 * 1000 if not self.seriesPurgedT or (dtMsecs - self.seriesPurgedT) > delta: self.removeOldSeries(self.seriesPeers, dtMsecs - delta) self.seriesPurgedT = dtMsecs peersVector = self.seriesPeers.pointsVector() values = [p.y() for p in peersVector] if values: self.axisY.setMax(max(values) + 10) self.axisY.setMin(min(values) - 10) dateMin = QDateTime.fromMSecsSinceEpoch(dtMsecs - (30 * 1000)) dateMax = QDateTime.fromMSecsSinceEpoch(dtMsecs + 2000) self.seriesPeers.append(dtMsecs, count) self.axisX.setRange(dateMin, dateMax)
def __init__(self, ipfsCtx, peerId, identMsg, ipIdentifier: IPIdentifier, pinglast=0, pingavg=0, validated=False, authenticated=False): self._ipfsCtx = ipfsCtx self._ipid = ipIdentifier self.peerId = peerId self.ident = identMsg self.pinglast = pinglast self.pingavg = pingavg self._identLast = int(time.time()) self._validated = validated self._authenticated = authenticated self._avatarPath = None self._avatarImage = self.defaultAvatarImage() self.sInactive = AsyncSignal(str)
class TorProtocol(asyncio.SubprocessProtocol): def __init__(self, loop, exitFuture, startedFuture, debug=False): self._loop = loop self.debug = debug self.eventStarted = asyncio.Event() self.exitFuture = exitFuture self.startedFuture = startedFuture self.errAlreadyRunning = False self.bstrapRe = re.compile( r'\s*\d+:\d+:\d+\.\d*\s\[\w*\] Bootstrapped (\d+)\%') self.sTorBootstrapStatus = AsyncSignal(int, str) @property def loop(self): return self._loop def pipe_data_received(self, fd, data): try: msg = data.decode().strip() except BaseException: return for line in msg.split('\n'): if self.debug: log.debug(f'TOR: {line}') ma = self.bstrapRe.search(line) if ma: try: pc = ma.group(1) ensure( self.sTorBootstrapStatus.emit(int(pc), 'bootstrapped')) except Exception: continue def process_exited(self): if not self.exitFuture.done(): self.exitFuture.set_result(True)
def __init__(self, maildir: BitMessageMailDir, parent=None): super().__init__(parent) self.app = QApplication.instance() self.maildir = maildir self.currentItemChanged.connect(partialEnsure( self.onCurMessageChanged)) self.setColumnCount(2) self.setHeaderLabels(['Subject', 'Date']) self.setHeaderHidden(True) self.setSortingEnabled(True) self.sMessageNeedsDisplay = AsyncSignal(MaildirMessage) self.viewActive.connect(self.onViewSwitched) self.fontNoBold = QFont(self.font()) self.fontNoBold.setBold(False) self.fontBold = QFont(self.font()) self.fontBold.setBold(True) self.header().setSectionResizeMode(0, QHeaderView.ResizeToContents) self.hideColumn(1)
class BandwidthGraphView(BaseTimedGraph): bwStatsFetched = AsyncSignal(dict) def __init__(self, parent=None): super(BandwidthGraphView, self).__init__(parent=parent) self.setObjectName('BandwidthGraph') self.chart.setTitle(iBandwidthUsage()) self.axisY.setTitleText(iRateKbPerSecond()) self.bwStatsFetched.connectTo(self.onBwStats) penIn = QPen(QColor('red')) penIn.setWidth(2) penOut = QPen(QColor('blue')) penOut.setWidth(2) self.seriesKbIn = QLineSeries() self.seriesKbIn.setName(iRateInput()) self.seriesKbIn.setPen(penIn) self.chart.addSeries(self.seriesKbIn) self.seriesKbIn.attachAxis(self.axisX) self.seriesKbIn.attachAxis(self.axisY) self.seriesKbOut = QLineSeries() self.seriesKbOut.setName(iRateOutput()) self.seriesKbOut.setPen(penOut) self.chart.addSeries(self.seriesKbOut) self.seriesKbOut.attachAxis(self.axisX) self.seriesKbOut.attachAxis(self.axisY) self.setChart(self.chart) async def onBwStats(self, stats): dtNow = QDateTime.currentDateTime() dtMsecs = dtNow.toMSecsSinceEpoch() delta = 120 * 1000 if not self.seriesPurgedT or (dtMsecs - self.seriesPurgedT) > delta: self.removeOldSeries(self.seriesKbIn, dtMsecs - delta) self.removeOldSeries(self.seriesKbOut, dtMsecs - delta) self.seriesPurgedT = dtMsecs rateIn = stats.get('RateIn', 0) rateOut = stats.get('RateOut', 0) rateInKb = int(rateIn / 1024) rateOutKb = int(rateOut / 1024) inVector = self.seriesKbIn.pointsVector() outVector = self.seriesKbIn.pointsVector() inValues = [p.y() for p in inVector] outValues = [p.y() for p in outVector] if inValues or outValues: self.axisY.setMax(max(max(inValues), max(outValues))) dateMin = QDateTime.fromMSecsSinceEpoch(dtMsecs - (30 * 1000)) dateMax = QDateTime.fromMSecsSinceEpoch(dtMsecs + 2000) self.seriesKbIn.append(dtMsecs, rateInKb) self.seriesKbOut.append(dtMsecs, rateOutKb) self.axisX.setRange(dateMin, dateMax)
class GalacteekApplication(QApplication): """ Galacteek application class :param bool debug: enable debugging :param str profile: application profile """ manualAvailable = pyqtSignal(str, dict) messageDisplayRequest = pyqtSignal(str, str) appImageImported = pyqtSignal(str) dbConfigured = AsyncSignal(bool) def __init__(self, debug=False, profile='main', sslverify=True, enableOrbital=False, progName=None, cmdArgs={}): QApplication.__init__(self, sys.argv) QCoreApplication.setApplicationName(GALACTEEK_NAME) self.dbConfigured.connectTo(self.onDbConfigured) self.setQuitOnLastWindowClosed(False) self._cmdArgs = cmdArgs self._debugEnabled = debug self._appProfile = profile self._loop = None self._executor = None self._ipfsClient = None self._ipfsOpMain = None self._ipfsd = None self._sslverify = sslverify self._progName = progName self._progCid = None self._system = platform.system() self._urlSchemes = {} self._shuttingDown = False self._icons = {} self._ipfsIconsCache = {} self._ipfsIconsCacheMax = 32 self.enableOrbital = enableOrbital self.orbitConnector = None self.translator = None self.mainWindow = None self.feedFollowerTask = None self.webProfiles = {} self.ipfsCtx = IPFSContext(self) self.peersTracker = peers.PeersTracker(self.ipfsCtx) self.desktopWidget = QDesktopWidget() self.desktopGeometry = self.desktopWidget.screenGeometry() self.setWindowIcon(getIcon('galacteek.png')) self.setupAsyncLoop() self.setupPaths() @property def cmdArgs(self): return self._cmdArgs @property def shuttingDown(self): return self._shuttingDown @property def offline(self): return self.cmdArgs.offline @property def system(self): return self._system @property def debugEnabled(self): return self._debugEnabled @property def ipfsIconsCacheMax(self): return self._ipfsIconsCacheMax @property def ipfsIconsCache(self): return self._ipfsIconsCache @property def progName(self): return self._progName @property def progCid(self): return self._progCid @property def sslverify(self): return self._sslverify @property def appProfile(self): return self._appProfile @property def ipfsd(self): return self._ipfsd @property def loop(self): return self._loop @property def executor(self): return self._executor @loop.setter def loop(self, newLoop): self._loop = newLoop @property def allTasks(self): return asyncio.Task.all_tasks(loop=self.loop) @property def pendingTasks(self): return [task for task in self.allTasks if not task.done()] @property def ipfsClient(self): return self._ipfsClient @ipfsClient.setter def ipfsClient(self, client): self.debug('IPFS client changed: {}'.format(client)) self._ipfsClient = client @property def ipfsOpMain(self): return self._ipfsOpMain @ipfsOpMain.setter def ipfsOpMain(self, op): """ The main IPFS operator, used by @ipfsOp """ self.debug('Main IPFS operator upgrade: ID {}'.format(op.uid)) self._ipfsOpMain = op @property def gatewayAuthority(self): params = self.getIpfsConnectionParams() return '{0}:{1}'.format(params.host, params.gatewayPort) @property def gatewayUrl(self): params = self.getIpfsConnectionParams() return params.gatewayUrl @property def dataLocation(self): return self._dataLocation @property def ipfsBinLocation(self): return self._ipfsBinLocation @property def ipfsDataLocation(self): return self._ipfsDataLocation @property def nsCacheLocation(self): return self._nsCacheLocation @property def orbitDataLocation(self): return self._orbitDataLocation def applyStyle(self, theme='default'): qssPath = ":/share/static/qss/{theme}/galacteek.qss".format( theme=theme) qssFile = QFile(qssPath) try: qssFile.open(QFile.ReadOnly) styleSheetBa = qssFile.readAll() styleSheetStr = styleSheetBa.data().decode('utf-8') self.setStyleSheet(styleSheetStr) except BaseException: # that would probably occur if the QSS is not # in the resources file.. set some default stylesheet here? pass self.gStyle = GalacteekStyle() self.setStyle(self.gStyle) def debug(self, msg): if self.debugEnabled: log.debug(msg) def initSystemTray(self): self.systemTray = QSystemTrayIcon(self) self.systemTray.setIcon(getIcon('galacteek-incandescent.png')) self.systemTray.show() self.systemTray.activated.connect(self.onSystemTrayIconClicked) systemTrayMenu = QMenu(self.mainWindow) actionShow = systemTrayMenu.addAction('Show') actionShow.setIcon(getIcon('galacteek-incandescent.png')) actionShow.triggered.connect(self.onShowWindow) systemTrayMenu.addSeparator() actionQuit = systemTrayMenu.addAction('Quit') actionQuit.setIcon(getIcon('quit.png')) actionQuit.triggered.connect(self.onExit) self.systemTray.setContextMenu(systemTrayMenu) def initMisc(self): self.multihashDb = IPFSObjectMetadataDatabase(self._mHashDbLocation, loop=self.loop) self.resourceOpener = IPFSResourceOpener(parent=self) self.downloadsManager = downloads.DownloadsManager(self) self.marksLocal = IPFSMarks(self.localMarksFileLocation, backup=True) self.marksLocal.addCategory('general') self.marksLocal.addCategory('uncategorized') self.marksNetwork = IPFSMarks(self.networkMarksFileLocation, autosave=False) self.tempDir = QTemporaryDir() self.tempDirWeb = self.tempDirCreate(self.tempDir.path(), 'webdownloads') def tempDirCreate(self, basedir, name=None): tmpdir = QDir(basedir) if not tmpdir.exists(): return uid = name if name else str(uuid.uuid4()) path = tmpdir.absoluteFilePath(uid) if tmpdir.mkpath(path): return path async def setupHashmarks(self): pkg = 'galacteek.hashmarks.default' res = await database.hashmarkSourceSearch( name='core', url=pkg, type=models.HashmarkSource.TYPE_PYMODULE) if not res: await database.hashmarkSourceAdd( type=models.HashmarkSource.TYPE_PYMODULE, url=pkg, name='core') await self.hmSynchronizer.sync() await database.hashmarkSourceAdd( type=models.HashmarkSource.TYPE_GITREPOS, url='https://github.com/galacteek/hashmarks-dwebland') await self.scheduler.spawn(self.hmSynchronizer.syncTask()) def setupTranslator(self): if self.translator: QApplication.removeTranslator(self.translator) self.translator = QTranslator() QApplication.installTranslator(self.translator) lang = self.settingsMgr.getSetting(CFG_SECTION_UI, CFG_KEY_LANG) self.translator.load( ':/share/translations/galacteek_{0}.qm'.format(lang)) def createMainWindow(self, show=True): self.mainWindow = mainui.MainWindow(self) if show is True: self.mainWindow.show() def onSystemTrayIconClicked(self, reason): if reason == QSystemTrayIcon.Unknown: pass elif reason == QSystemTrayIcon.Context: pass elif reason == QSystemTrayIcon.DoubleClick: self.mainWindow.showMaximized() else: pass def systemTrayMessage(self, title, message, timeout=2000, messageIcon=QSystemTrayIcon.Information): self.systemTray.showMessage(title, message, messageIcon, timeout) @ipfsOp async def setupRepository(self, op): pubsubEnabled = True # mandatory now .. hExchEnabled = self.settingsMgr.isTrue(CFG_SECTION_IPFS, CFG_KEY_HASHMARKSEXCH) self.ipfsCtx.resources['ipfs-logo-ice'] = await self.importQtResource( '/share/icons/ipfs-logo-128-ice.png') self.ipfsCtx.resources['ipfs-cube-64'] = await self.importQtResource( '/share/icons/ipfs-cube-64.png') self.ipfsCtx.resources['atom-feed'] = await self.importQtResource( '/share/icons/atom-feed.png') self.ipfsCtx.resources['markdown-reference'] = \ await self.importQtResource( '/share/static/docs/markdown-reference.html') await self.ipfsCtx.setup(pubsubEnable=pubsubEnabled, pubsubHashmarksExch=hExchEnabled, offline=self.offline) await self.ipfsCtx.profilesInit() await self.qSchemeHandler.start() await self.importLdContexts() self.feedFollower = FeedFollower(self) self.feedFollowerTask = await self.scheduler.spawn( self.feedFollower.process()) await self.ipfsCtx.ipfsRepositoryReady.emit() self.ipfsCtx._ipfsRepositoryReady.emit() # # If the application's binary name is a valid CID, pin it! # This happens when running the AppImage and ensures # self-seeding of the image! # if isinstance(self.progName, str): progNameClean = re.sub(r'[\.\/]*', '', self.progName) if cidhelpers.cidValid(progNameClean): self._progCid = progNameClean log.debug("Auto pinning program's CID: {0}".format( self.progCid)) await self.ipfsCtx.pin(joinIpfs(self.progCid), False, self.onAppReplication, qname='self-seeding') if self.cmdArgs.seed and self.cmdArgs.appimage: await self.seedAppImage() @ipfsOp async def seedAppImage(self, ipfsop): # Automatic AppImage seeding if os.path.isfile(self.cmdArgs.binarypath): log.info( 'AppImage seeding: {img}'.format(img=self.cmdArgs.binarypath)) ensure(ipfsop.addPath(self.cmdArgs.binarypath, wrap=True), futcallback=self.onAppSeed) def onAppSeed(self, future): try: replResult = future.result() except Exception as err: log.debug('AppImage seed: failed', exc_info=err) else: if isinstance(replResult, dict): cid = replResult.get('Hash') self.appImageImported.emit(cid) log.info('AppImage seed OK: CID {cid}'.format(cid=cid)) def onAppReplication(self, future): try: replResult = future.result() except Exception as err: log.debug('App replication: failed', exc_info=err) else: log.debug('App replication: success ({result})'.format( result=replResult)) @ipfsOp async def importLdContexts(self, ipfsop): """ Import the JSON-LD contexts and associate the directory entry with the 'galacteek.ld.contexts' key """ contextsPath = ipfsop.ldContextsRootPath() if not os.path.isdir(contextsPath): log.debug('LD contexts not found') return entry = await ipfsop.addPath(contextsPath, recursive=True, hidden=False) if entry: ldKeyName = 'galacteek.ld.contexts' log.debug('LD contexts sitting at: {}'.format(entry.get('Hash'))) await ipfsop.keyGen(ldKeyName, checkExisting=True) ensure( ipfsop.publish(entry['Hash'], key=ldKeyName, allow_offline=True)) @ipfsOp async def importQtResource(self, op, path): rscFile = QFile(':{0}'.format(path)) try: rscFile.open(QFile.ReadOnly) data = rscFile.readAll().data() entry = await op.addBytes(data) except Exception as e: log.debug('importQtResource: {}'.format(str(e))) else: return entry def ipfsTask(self, fn, *args, **kw): """ Schedule an async IPFS task """ return self.loop.create_task(fn(self.ipfsClient, *args, **kw)) def ipfsTaskOp(self, fn, *args, **kw): """ Schedule an async IPFS task using an IPFSOperator instance """ client = self.ipfsClient if client: return self.loop.create_task( fn(self.getIpfsOperator(), *args, **kw)) def getIpfsOperator(self): """ Returns a new IPFSOperator with the currently active IPFS client""" return IPFSOperator(self.ipfsClient, ctx=self.ipfsCtx, debug=self.debugEnabled, nsCachePath=self.nsCacheLocation) def getIpfsConnectionParams(self): mgr = self.settingsMgr section = CFG_SECTION_IPFSD if mgr.isTrue(section, CFG_KEY_ENABLED): return IPFSConnectionParams( '127.0.0.1', mgr.getSetting(section, CFG_KEY_APIPORT), mgr.getSetting(section, CFG_KEY_HTTPGWPORT)) else: section = CFG_SECTION_IPFSCONN1 return IPFSConnectionParams( mgr.getSetting(section, CFG_KEY_HOST), mgr.getSetting(section, CFG_KEY_APIPORT), mgr.getSetting(section, CFG_KEY_HTTPGWPORT)) def getEthParams(self): mgr = self.settingsMgr provType = mgr.getSetting(CFG_SECTION_ETHEREUM, CFG_KEY_PROVIDERTYPE) rpcUrl = mgr.getSetting(CFG_SECTION_ETHEREUM, CFG_KEY_RPCURL) return EthereumConnectionParams(rpcUrl, provType=provType) async def updateIpfsClient(self): from galacteek.ipfs import ConnectionError connParams = self.getIpfsConnectionParams() client = aioipfs.AsyncIPFS(host=connParams.host, port=connParams.apiPort, loop=self.loop) self.ipfsClient = client self.ipfsCtx.ipfsClient = client self.ipfsOpMain = self.getIpfsOperator() self.ipfsOpMain.ipidManager = self.ipidManager IPFSOpRegistry.regDefault(self.ipfsOpMain) try: await self.setupRepository() except ConnectionError: await messageBoxAsync( 'IPFS connection error (is your daemon running ?)') await self.ipfsCtx.ipfsConnectionReady.emit() async def stopIpfsServices(self): try: await self.ipfsCtx.shutdown() except BaseException as err: log.debug( 'Error shutting down context: {err}'.format(err=str(err))) if self.feedFollowerTask is not None: await self.feedFollowerTask.close() def setupDb(self): ensure(self.setupOrmDb(self._mainDbLocation)) def jobsExceptionHandler(self, scheduler, context): pass async def setupOrmDb(self, dbpath): self.scheduler = await aiojobs.create_scheduler(close_timeout=1.0, limit=150, pending_limit=1000) # Old database, just for Atom feeds right now self.sqliteDb = SqliteDatabase(self._sqliteDbLocation) ensure(self.sqliteDb.setup()) self.modelAtomFeeds = AtomFeedsModel(self.sqliteDb.feeds, parent=self) self.urlHistory = history.URLHistory( self.sqliteDb, enabled=self.settingsMgr.urlHistoryEnabled, parent=self) if not await database.initOrm(self._mainDbLocation): await self.dbConfigured.emit(False) return await self.setupHashmarks() await self.dbConfigured.emit(True) async def onDbConfigured(self, configured): if not configured: return self.debug('Database ready') self.setupClipboard() self.setupTranslator() self.initSystemTray() self.initMisc() self.initEthereum() self.initWebProfiles() self.initDapps() self.createMainWindow() self.clipboardInit() await self.setupIpfsConnection() async def fetchGoIpfs(self): ipfsPath = self.which('ipfs') fsMigratePath = self.which('fs-repo-migrations') if fsMigratePath is None or self.cmdArgs.forcegoipfsdl: await fetchFsMigrateWrapper(self) if ipfsPath is None or self.cmdArgs.forcegoipfsdl: path = await fetchGoIpfsWrapper(self) if path is None: self.systemTrayMessage('Galacteek', iGoIpfsFetchError()) async def setupIpfsConnection(self): sManager = self.settingsMgr await self.fetchGoIpfs() if sManager.isTrue(CFG_SECTION_IPFSD, CFG_KEY_ENABLED): fsMigratePath = shutil.which('fs-repo-migrations') hasFsMigrate = fsMigratePath is not None if hasFsMigrate is False and self.cmdArgs.migrate is True: self.systemTrayMessage('Galacteek', iFsRepoMigrateNotFound()) enableMigrate = hasFsMigrate is True and \ self.cmdArgs.migrate is True ipfsPath = self.which('ipfs') await self.startIpfsDaemon(goIpfsPath=ipfsPath, migrateRepo=enableMigrate) else: await self.updateIpfsClient() def setupMainObjects(self): self.manuals = ManualsManager(self) self.mimeDb = QMimeDatabase() self.jinjaEnv = defaultJinjaEnv() self.solarSystem = SolarSystem() self.mimeTypeIcons = preloadMimeIcons() self.hmSynchronizer = HashmarksSynchronizer() self.ipidManager = IPIDManager( resolveTimeout=self.settingsMgr.ipidIpnsTimeout) self.towers = { 'dags': DAGSignalsTower(self), 'schemes': URLSchemesTower(self), 'did': DIDTower() } self.rscAnalyzer = ResourceAnalyzer(parent=self) self.messageDisplayRequest.connect( lambda msg, title: ensure(messageBoxAsync(msg, title=title))) self.appImageImported.connect(lambda cid: ensure( messageBoxAsync('AppImage was imported in IPFS!'))) def setupAsyncLoop(self): """ Install the quamash event loop and enable debugging """ loop = QEventLoop(self) asyncio.set_event_loop(loop) logging.getLogger('quamash').setLevel(logging.INFO) if self.debugEnabled: logging.getLogger('asyncio').setLevel(logging.DEBUG) loop.set_debug(True) warnings.simplefilter('always', ResourceWarning) warnings.simplefilter('always', BytesWarning) warnings.simplefilter('always', ImportWarning) self._executor = concurrent.futures.ThreadPoolExecutor(max_workers=5) self.loop = loop return loop def task(self, fn, *args, **kw): return self.loop.create_task(fn(*args, **kw)) def configure(self): self.initSettings() self.setupMainObjects() self.setupSchemeHandlers() self.applyStyle() self.setupDb() def acquireLock(self): lpath = Path(self._pLockLocation) if not lpath.exists(): lpath.touch() self.lock = FileLock(lpath, timeout=2) try: self.lock.acquire() except Exception: return questionBox( 'Lock', 'The profile lock could not be acquired ' '(another instance could be running). Ignore ?') return True def setupPaths(self): qtDataLocation = QStandardPaths.writableLocation( QStandardPaths.DataLocation) if not qtDataLocation: raise Exception('No writable data location found') self._dataLocation = os.path.join(qtDataLocation, self._appProfile) self._ipfsBinLocation = os.path.join(qtDataLocation, 'ipfs-bin') self._ipfsDataLocation = os.path.join(self.dataLocation, 'ipfs') self._orbitDataLocation = os.path.join(self.dataLocation, 'orbitdb') self._mHashDbLocation = os.path.join(self.dataLocation, 'mhashmetadb') self._sqliteDbLocation = os.path.join(self.dataLocation, 'db.sqlite') self._pLockLocation = os.path.join(self.dataLocation, 'profile.lock') self._mainDbLocation = os.path.join(self.dataLocation, 'db_main.sqlite3') self.marksDataLocation = os.path.join(self.dataLocation, 'marks') self.uiDataLocation = os.path.join(self.dataLocation, 'ui') self.cryptoDataLocation = os.path.join(self.dataLocation, 'crypto') self.gpgDataLocation = os.path.join(self.cryptoDataLocation, 'gpg') self.localMarksFileLocation = os.path.join(self.marksDataLocation, 'ipfsmarks.local.json') self.networkMarksFileLocation = os.path.join(self.marksDataLocation, 'ipfsmarks.network.json') self.pinStatusLocation = os.path.join(self.dataLocation, 'pinstatus.json') self._nsCacheLocation = os.path.join(self.dataLocation, 'nscache.json') qtConfigLocation = QStandardPaths.writableLocation( QStandardPaths.ConfigLocation) self.configDirLocation = os.path.join(qtConfigLocation, GALACTEEK_NAME, self._appProfile) self.settingsFileLocation = os.path.join( self.configDirLocation, '{}.conf'.format(GALACTEEK_NAME)) for dir in [ self._ipfsDataLocation, self._mHashDbLocation, self.ipfsBinLocation, self.marksDataLocation, self.cryptoDataLocation, self.gpgDataLocation, self.uiDataLocation, self.configDirLocation ]: if not os.path.exists(dir): os.makedirs(dir) self.defaultDownloadsLocation = QStandardPaths.writableLocation( QStandardPaths.DownloadLocation) self.debug('Datapath: {0}, config: {1}, configfile: {2}'.format( self._dataLocation, self.configDirLocation, self.settingsFileLocation)) os.environ['PATH'] += os.pathsep + self.ipfsBinLocation def which(self, prog='ipfs'): path = self.ipfsBinLocation + os.pathsep + os.environ['PATH'] result = shutil.which(prog, path=path) self.debug('Program {0} found at {1}'.format(prog, result)) return result def initSettings(self): self.settingsMgr = SettingsManager(path=self.settingsFileLocation) setDefaultSettings(self) self.settingsMgr.sync() async def startIpfsDaemon(self, goIpfsPath='ipfs', migrateRepo=False): if self.ipfsd is not None: # we only support one daemon for now return pubsubEnabled = True # mandatory now .. corsEnabled = self.settingsMgr.isTrue(CFG_SECTION_IPFSD, CFG_KEY_CORS) sManager = self.settingsMgr section = CFG_SECTION_IPFSD # Instantiate an IPFS daemon using asyncipfsd and # start it in a task, monitoring the initialization process self._ipfsd = asyncipfsd.AsyncIPFSDaemon( self.ipfsDataLocation, goIpfsPath=goIpfsPath, apiport=sManager.getInt(section, CFG_KEY_APIPORT), swarmport=sManager.getInt(section, CFG_KEY_SWARMPORT), swarmportQuic=sManager.getInt(section, CFG_KEY_SWARMPORT_QUIC), swarmProtos=sManager.swarmProtosList, gatewayport=sManager.getInt(section, CFG_KEY_HTTPGWPORT), swarmLowWater=sManager.getInt(section, CFG_KEY_SWARMLOWWATER), swarmHighWater=sManager.getInt(section, CFG_KEY_SWARMHIGHWATER), storageMax=sManager.getInt(section, CFG_KEY_STORAGEMAX), gwWritable=sManager.isTrue(section, CFG_KEY_HTTPGWWRITABLE), routingMode=sManager.getSetting(section, CFG_KEY_ROUTINGMODE), pubsubRouter=sManager.getSetting(section, CFG_KEY_PUBSUB_ROUTER), namesysPubsub=sManager.isTrue(section, CFG_KEY_NAMESYS_PUBSUB), pubsubSigning=sManager.isTrue(section, CFG_KEY_PUBSUB_USESIGNING), fileStore=sManager.isTrue(section, CFG_KEY_FILESTORE), nice=sManager.getInt(section, CFG_KEY_NICE), pubsubEnable=pubsubEnabled, corsEnable=corsEnabled, migrateRepo=migrateRepo, debug=self.cmdArgs.goipfsdebug, offline=self.cmdArgs.offline, loop=self.loop) await self.scheduler.spawn(self.startIpfsdTask(self.ipfsd)) async def startIpfsdTask(self, ipfsd): started = await ipfsd.start() if started is False: return self.systemTrayMessage('IPFS', iIpfsDaemonProblem()) running = False logUser.info(iIpfsDaemonStarted()) # Use asyncio.wait_for to wait for the proto.eventStarted # event to be fired. for attempt in range(1, 32): logUser.info(iIpfsDaemonWaiting(attempt)) with async_timeout.timeout(1): try: await ipfsd.proto.eventStarted.wait() except asyncio.CancelledError: continue except asyncio.TimeoutError: # Event not set yet, wait again log.debug('IPFSD: timeout occured while waiting for ' 'daemon to start (attempt: {0})'.format(attempt)) continue else: # Event was set, good to go logUser.info(iIpfsDaemonReady()) running = True break if running is True: ensure(self.updateIpfsClient()) else: logUser.info(iIpfsDaemonInitProblem()) def initEthereum(self): try: from galacteek.dweb.ethereum.ctrl import EthereumController self.ethereum = EthereumController(self.getEthParams(), loop=self.loop, parent=self, executor=self.executor) if self.settingsMgr.ethereumEnabled: ensure(self.ethereum.start()) except ImportError: self.ethereum = MockEthereumController() def normalCursor(self): cursor = QCursor(Qt.ArrowCursor) QApplication.setOverrideCursor(cursor) QApplication.changeOverrideCursor(cursor) def setupClipboard(self): self.appClipboard = self.clipboard() self.clipTracker = ClipboardTracker(self, self.appClipboard) def clipboardInit(self): self.clipTracker.clipboardInit() def setClipboardText(self, text): self.clipTracker.setText(text) def getClipboardText(self): return self.clipTracker.getText() def initWebProfiles(self): self.scriptsIpfs = ipfsClientScripts(self.getIpfsConnectionParams()) self.webProfiles = { 'minimal': MinimalProfile(parent=self), 'ipfs': IPFSProfile(parent=self), 'web3': Web3Profile(parent=self) } def availableWebProfilesNames(self): return [p.profileName for n, p in self.webProfiles.items()] def initDapps(self): self.dappsRegistry = DappsRegistry(self.ethereum, parent=self) def setupSchemeHandlers(self): self.dwebSchemeHandler = DWebSchemeHandlerGateway(self) self.ensSchemeHandler = EthDNSSchemeHandler(self) self.ensProxySchemeHandler = EthDNSProxySchemeHandler(self) self.nativeIpfsSchemeHandler = NativeIPFSSchemeHandler( self, noMutexes=self.cmdArgs.noipfsmutexlock) self.qSchemeHandler = MultiObjectHostSchemeHandler(self) def subUrl(self, path): """ Joins the gatewayUrl and path to form a new URL """ sub = QUrl(str(self.gatewayUrl)) sub.setPath(path) return sub def getJinjaTemplate(self, name): try: tmpl = self.jinjaEnv.get_template(name) except jinja2.exceptions.TemplateNotFound: return None else: return tmpl @asyncify async def checkReleases(self): self.debug('Checking for new releases') newR = await pypicheck.newReleaseAvailable() if newR: self.systemTrayMessage('Galacteek', iNewReleaseAvailable(), timeout=8000) def showTasks(self): for task in self.pendingTasks: self.debug('Pending task: {}'.format(task)) def onShowWindow(self): self.mainWindow.showMaximized() def restart(self): ensure(self.restartApp()) async def restartApp(self): from galacteek.guientrypoint import appStarter pArgs = self.arguments() await self.exitApp() time.sleep(1) appStarter.startProcess(pArgs) def onExit(self): ensure(self.exitApp()) async def shutdownScheduler(self): # It ain't that bad. STFS with dignity for stry in range(0, 12): try: async with async_timeout.timeout(2): await self.scheduler.close() except asyncio.TimeoutError: log.warning('Timeout shutting down the scheduler (not fooled)') continue else: log.debug(f'Scheduler went down (try: {stry})') return async def exitApp(self): self._shuttingDown = True self.lock.release() self.mainWindow.stopTimers() try: self.systemTray.hide() except: pass try: await self.sqliteDb.close() await database.closeOrm() except: pass await self.stopIpfsServices() await self.loop.shutdown_asyncgens() await self.shutdownScheduler() await cancelAllTasks() await self.ethereum.stop() if self.ipfsClient: await self.ipfsClient.close() if self.ipfsd: self.ipfsd.stop() self.mainWindow.close() if self.debug: self.showTasks() self.tempDir.remove() self.quit()
class EthereumController: ethConnected = AsyncSignal(bool) ethNewBlock = AsyncSignal(str) def __init__(self, connParams, web3=None, loop=None, executor=None, parent=None): self.app = QApplication.instance() self.loop = loop if loop else asyncio.get_event_loop() self.executor = executor if executor else \ concurrent.futures.ThreadPoolExecutor(max_workers=4) self._params = connParams self._web3 = web3 self._watchTask = None self._blockLatest = None self._stop = False self._loadedContracts = {} @property def web3(self): return self._web3 @property def params(self): return self._params def changeParams(self, params): self._params = params def getWeb3(self): try: if self._params.provType == 'http': web3 = Web3(Web3.HTTPProvider(self.params.rpcUrl)) elif self._params.provType == 'websocket': web3 = Web3(WebsocketProvider(self.params.rpcUrl)) return web3 web3.eth.defaultAccount = web3.eth.accounts[0] self._ns = ENS.fromWeb3(web3) return web3 except Exception as err: log.debug('Cannot get Web3 connector: {}'.format(str(err))) return None async def _e(self, fn, *args, **kw): timeout = kw.pop('timeout', None) try: if isinstance(timeout, int): with async_timeout.timeout(timeout): return await self.loop.run_in_executor( self.executor, functools.partial(fn, *args, **kw)) else: return await self.loop.run_in_executor( self.executor, functools.partial(fn, *args, **kw)) except asyncio.TimeoutError: log.debug('ETH Timeout') except ConnectionError: log.debug( 'ETH Connection error while running fn: {0}'.format(fn)) return None # API coroutines async def stop(self): self._stop = True async def start(self): if not self._web3: self._web3 = self.getWeb3() if self.web3 is None: log.debug('Web3 invalid') return if await self.connected(): logUser.info('Ethereum: connected to {}'.format( self.params.rpcUrl)) await self.ethConnected.emit(True) self._watchTask = ensure(self._e(self.watchTask)) ensure(self.loadAutoDeploy()) # await self.loadContracts() else: await self.ethConnected.emit(False) async def latestBlock(self): return await self._e(self.web3.eth.getBlock, 'latest') async def getBlock(self, block='latest'): return EthBlock(await self._e(self.web3.eth.getBlock, block)) async def connected(self): return self.web3 and await self._e(self.web3.isConnected) async def ensAddress(self, name): return await self._e(self._ns.address, name) def watchTask(self): try: blkfilter = self.web3.eth.filter('latest') except Exception: # Node does not support filters ? return while not self._stop: time.sleep(5) for event in blkfilter.get_new_entries(): self.eventLatestBlock(event) def eventLatestBlock(self, event): if isinstance(event, bytes): blockHex = w3.toHex(event) ensure(self.ethNewBlock.emit(blockHex)) async def loadAutoDeploy(self): # Load contracts with autoload=True for lContract in listContracts(): dList = getContractDeployments(lContract.name, autoload=True) for depl in dList: log.debug('Loading auto-deployment for contract {}'.format( lContract.name)) contract, cOperator = await self.loadLocalContractFromAddr( lContract.name, depl['address']) async def loadContractFromAddress(self, address: str, abi): def load(addr, abi): contract = self.web3.eth.contract( address=addr, abi=abi ) return ContractOperator(self, contract) return await self._e(load, address, abi) async def loadLocalContractFromDeployment(self, contractName: str): if not await self.connected(): raise Exception('Not connected!') lContract = getContractByName(contractName) if not lContract: return deployments = getContractDeployments(contractName) if len(deployments) > 0: depl = deployments.pop() return await self.loadLocalContractFromAddr( contractName, depl['address'] ) return None, None async def loadLocalContractFromAddr(self, contractName: str, address: str): if not await self.connected(): raise Exception('Not connected!') lContract = getContractByName(contractName) if not lContract: return None, None def load(_lContract, address): # Blocking call w3Contract = _lContract.web3Contract(self.web3) return w3Contract(address) contract = await self._e(load, lContract, address) if contract: operator = lContract.module.contractOperator(self, contract, address, parent=self) self._loadedContracts.setdefault(contractName, {'default': None}) self._loadedContracts[contractName][address] = operator if self._loadedContracts[contractName]['default'] is None: self._loadedContracts[contractName]['default'] = operator log.debug('Contract {0} loaded at {1}'.format( contractName, address)) return lContract, operator else: log.debug('Contract invalid: {}'.format(address)) return None, None def getDefaultLoadedOperator(self, contractName: str): loaded = self._loadedContracts.get(contractName) if loaded: return loaded.get('default')