diff --git a/onionr/api.py b/onionr/api.py index d41245d1..1f68a49d 100755 --- a/onionr/api.py +++ b/onionr/api.py @@ -404,6 +404,46 @@ class API: resp = Response(resp) return resp + + @app.route('/public/announce/', methods=['POST']) + def acceptAnnounce(): + self.validateHost('public') + resp = 'failure' + powHash = '' + randomData = '' + newNode = '' + ourAdder = self._core.hsAddress.encode() + try: + newNode = request.form['node'].encode() + except KeyError: + logger.warn('No block specified for upload') + pass + else: + try: + randomData = request.form['random'] + randomData = base64.b64decode(randomData) + except KeyError: + logger.warn('No random data specified for upload') + else: + nodes = newNode + self._core.hsAddress.encode() + nodes = self._core._crypto.blake2bHash(nodes) + powHash = self._core._crypto.blake2bHash(randomData + nodes) + try: + powHash = powHash.decode() + except AttributeError: + pass + if powHash.startswith('0000'): + try: + newNode = newNode.decode() + except AttributeError: + pass + if self._core.addAddress(newNode): + resp = 'Success' + else: + logger.warn(newNode.decode() + ' failed to meet POW: ' + powHash) + resp = Response(resp) + return resp + @app.route('/public/') def public_handler(): # Public means it is publicly network accessible @@ -428,15 +468,6 @@ class API: resp = Response(self._utils.getBlockDBHash()) elif action == 'getBlockHashes': resp = Response('\n'.join(self._core.getBlockList())) - elif action == 'announce': - if data != '': - # TODO: require POW for this - if self._core.addAddress(data): - resp = Response('Success') - else: - resp = Response('') - else: - resp = Response('') # setData should be something the communicator initiates, not this api elif action == 'getData': resp = '' @@ -488,6 +519,9 @@ class API: logger.info('Starting client on ' + self.host + ':' + str(bindPort) + '...', timestamp=False) try: + while len(self._core.hsAddress) == 0: + self._core.refreshFirstStartVars() + time.sleep(0.5) self.http_server = WSGIServer((self.host, bindPort), app) self.http_server.serve_forever() except KeyboardInterrupt: diff --git a/onionr/communicator2.py b/onionr/communicator2.py index 6a37a376..b3af1d0f 100755 --- a/onionr/communicator2.py +++ b/onionr/communicator2.py @@ -93,7 +93,7 @@ class OnionrCommunicatorDaemon: OnionrCommunicatorTimers(self, self.clearOfflinePeer, 58) OnionrCommunicatorTimers(self, self.lookupKeys, 60, requiresPeer=True) OnionrCommunicatorTimers(self, self.lookupAdders, 60, requiresPeer=True) - announceTimer = OnionrCommunicatorTimers(self, self.daemonTools.announceNode, 305, requiresPeer=True) + announceTimer = OnionrCommunicatorTimers(self, self.daemonTools.announceNode, 305, requiresPeer=True, maxThreads=1) cleanupTimer = OnionrCommunicatorTimers(self, self.peerCleanup, 300, requiresPeer=True) # set loop to execute instantly to load up peer pool (replaced old pool init wait) @@ -446,17 +446,10 @@ class OnionrCommunicatorDaemon: def announce(self, peer): '''Announce to peers our address''' - announceCount = 0 - announceAmount = 2 - for peer in self.onlinePeers: - announceCount += 1 - if self.peerAction(peer, 'announce', self._core.hsAddress) == 'Success': - logger.info('Successfully introduced node to ' + peer) - break - else: - if announceCount == announceAmount: - logger.warn('Could not introduce node. Try again soon') - break + if self.daemonTools.announceNode(): + logger.info('Successfully introduced node to ' + peer) + else: + logger.warn('Could not introduce node.') def detectAPICrash(self): '''exit if the api server crashes/stops''' diff --git a/onionr/core.py b/onionr/core.py index 64ad6439..b3857393 100644 --- a/onionr/core.py +++ b/onionr/core.py @@ -78,6 +78,12 @@ class Core: sys.exit(1) return + def refreshFirstStartVars(self): + '''Hack to refresh some vars which may not be set on first start''' + if os.path.exists('data/hs/hostname'): + with open('data/hs/hostname', 'r') as hs: + self.hsAddress = hs.read().strip() + def addPeer(self, peerID, powID, name=''): ''' Adds a public key to the key database (misleading function name) diff --git a/onionr/onionr.py b/onionr/onionr.py index 0de17850..c4c4529d 100755 --- a/onionr/onionr.py +++ b/onionr/onionr.py @@ -40,7 +40,7 @@ except ImportError: raise Exception("You need the PySocks module (for use with socks5 proxy to use Tor)") ONIONR_TAGLINE = 'Anonymous P2P Platform - GPLv3 - https://Onionr.VoidNet.Tech' -ONIONR_VERSION = '0.1.1' # for debugging and stuff +ONIONR_VERSION = '0.2.0' # for debugging and stuff ONIONR_VERSION_TUPLE = tuple(ONIONR_VERSION.split('.')) # (MAJOR, MINOR, VERSION) API_VERSION = '4' # increments of 1; only change when something fundemental about how the API works changes. This way other nodes know how to communicate without learning too much information about you. diff --git a/onionr/onionrdaemontools.py b/onionr/onionrdaemontools.py index e615aacb..50679184 100644 --- a/onionr/onionrdaemontools.py +++ b/onionr/onionrdaemontools.py @@ -17,13 +17,40 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ''' -import onionrexceptions, onionrpeers +import onionrexceptions, onionrpeers, onionrproofs, base64 class DaemonTools: def __init__(self, daemon): self.daemon = daemon + self.announceCache = {} def announceNode(self): '''Announce our node to our peers''' - peer = self.daemon.pickOnlinePeer() - self.daemon.peerAction(peer, 'announce', self.daemon._core.hsAddress) - self.daemon.decrementThreadCount('announceNode') \ No newline at end of file + retData = False + + # Announce to random online peers + for i in self.daemon.onlinePeers: + if not i in self.announceCache: + peer = i + break + else: + peer = self.daemon.pickOnlinePeer() + + ourID = self.daemon._core.hsAddress.strip() + + url = 'http://' + peer + '/public/announce/' + data = {'node': ourID} + + combinedNodes = ourID + peer + + if peer in self.announceCache: + data['random'] = self.announceCache[peer] + else: + proof = onionrproofs.DataPOW(combinedNodes, forceDifficulty=4) + data['random'] = base64.b64encode(proof.waitForResult()[1]) + self.announceCache[peer] = data['random'] + + logger.info('Announcing node to ' + url) + if self.daemon._core._utils.doPostRequest(url, data) == 'Success': + retData = True + self.daemon.decrementThreadCount('announceNode') + return retData \ No newline at end of file