{"action":"created","comment":{"url":"https://api.github.com/repos/ScorexFoundation/Scorex/pulls/comments/214671147","pull_request_review_id":151761700,"id":214671147,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIxNDY3MTE0Nw==","diff_hunk":"@@ -121,114 +91,349 @@ class NetworkController(settings: NetworkSettings,\n       }\n \n     case SendToNetwork(message, sendingStrategy) =>\n-      (peerManagerRef ? FilterPeers(sendingStrategy))\n-        .map(_.asInstanceOf[Seq[ConnectedPeer]])\n-        .foreach(_.foreach(_.handlerRef ! message))\n+      filterConnections(sendingStrategy).foreach { connectedPeer =>\n+        connectedPeer.handlerRef ! message\n+      }\n   }\n \n-  private val outgoing = mutable.Set[InetSocketAddress]()\n-\n   def peerLogic: Receive = {\n-    case ConnectTo(remote) =>\n-      log.info(s\"Connecting to: $remote\")\n-      outgoing += remote\n-      tcpManager ! Connect(remote,\n-                          localAddress = externalSocketAddress,\n-                          options = KeepAlive(true) :: Nil,\n-                          timeout = connTimeout,\n-                          pullMode = true) //todo: check pullMode flag\n+    case ConnectTo(peer) =>\n+      connectTo(peer)\n \n     case DisconnectFrom(peer) =>\n-      log.info(s\"Disconnected from ${peer.socketAddress}\")\n+      log.info(s\"Disconnected from ${peer.remote}\")\n       peer.handlerRef ! CloseConnection\n-      peerManagerRef ! Disconnected(peer.socketAddress)\n \n     case Blacklist(peer) =>\n       peer.handlerRef ! PeerConnectionHandler.ReceivableMessages.Blacklist\n       // todo: the following message might become unnecessary if we refactor PeerManager to automatically\n       // todo: remove peer from `connectedPeers` on receiving `AddToBlackList` message.\n-      peerManagerRef ! Disconnected(peer.socketAddress)\n \n     case Connected(remote, local) =>\n-      val direction: ConnectionType = if(outgoing.contains(remote)) Outgoing else Incoming\n-      val logMsg = direction match {\n-        case Incoming => s\"New incoming connection from $remote established (bound to local $local)\"\n-        case Outgoing => s\"New outgoing connection to $remote established (bound to local $local)\"\n-      }\n-      log.info(logMsg)\n       val connection = sender()\n-      val connectionDescription = ConnectionDescription(connection, direction, externalSocketAddress, remote, features)\n-      val handlerProps: Props = PeerConnectionHandlerRef.props(settings, self, peerManagerRef,\n-        messageHandler, handshakeSerializer, connectionDescription, timeProvider)\n-      context.actorOf(handlerProps) // launch connection handler\n-      outgoing -= remote\n+      if (connectionForPeerAddress(remote).isEmpty) {\n+        createPeerConnectionHandler(remote, local, connection)\n+      } else {\n+        log.warn(s\"Connection to peer $remote is already established\")\n+        connection ! Close\n+      }\n \n-    case CommandFailed(c: Connect) =>\n+    case Handshaked(connectedPeer) =>\n+      handshaked(connectedPeer, sender())\n+\n+    case f@CommandFailed(c: Connect) =>\n       outgoing -= c.remoteAddress\n-      log.info(\"Failed to connect to : \" + c.remoteAddress)\n-      peerManagerRef ! Disconnected(c.remoteAddress)\n+      f.cause match {\n+        case Some(t) =>\n+          log.info(\"Failed to connect to : \" + c.remoteAddress, t)\n+        case None =>\n+          log.info(\"Failed to connect to : \" + c.remoteAddress)\n+      }\n+\n+    case Terminated(ref) =>\n+      connectionForHandler(ref).foreach { connectedPeer =>\n+        val addr = connectedPeer.remote\n+        connections -= addr\n+        outgoing -= addr\n+        context.system.eventStream.publish(DisconnectedPeer(addr))\n+      }\n   }\n \n   //calls from API / application\n   def interfaceCalls: Receive = {\n+    case GetConnectedPeers =>\n+      sender() ! connections.values.flatMap(_.peerInfo).toSeq\n+\n     case ShutdownNetwork =>\n       log.info(\"Going to shutdown all connections & unbind port\")\n-      (peerManagerRef ? FilterPeers(Broadcast))\n-        .map(_.asInstanceOf[Seq[ConnectedPeer]])\n-        .foreach(_.foreach(_.handlerRef ! CloseConnection))\n+      filterConnections(Broadcast).foreach { connectedPeer =>\n+        connectedPeer.handlerRef ! CloseConnection\n+      }\n       self ! Unbind\n       context stop self\n   }\n \n   override def receive: Receive = bindingLogic orElse businessLogic orElse peerLogic orElse interfaceCalls orElse {\n     case RegisterMessagesHandler(specs, handler) =>\n       log.info(s\"Registering handlers for ${specs.map(s => s.messageCode -> s.messageName)}\")\n-      messageHandlers += specs.map(_.messageCode) -> handler\n+      messageHandlers ++= specs.map(_.messageCode -> handler)\n \n-    case CommandFailed(cmd: Tcp.Command) =>\n+    case CommandFailed(cmd: Command) =>\n       log.info(\"Failed to execute command : \" + cmd)\n \n     case nonsense: Any =>\n       log.warn(s\"NetworkController: got something strange $nonsense\")\n   }\n+\n+  /**\n+    * Schedule a periodic connection to a random known peer\n+    */\n+  private def scheduleConnectToPeer(): Unit = {\n+    context.system.scheduler.schedule(5.seconds, 5.seconds){\n+      if (connections.size < settings.maxConnections) {\n+        val randomPeerF = peerManagerRef ? RandomPeerWithout(connections.values.flatMap(_.peerInfo).toSeq)\n+        randomPeerF.mapTo[Option[PeerInfo]].foreach { peerInfoOpt =>\n+          peerInfoOpt.foreach(peerInfo => self ! ConnectTo(peerInfo))\n+        }\n+      }\n+    }\n+  }\n+\n+  /**\n+    * Connect to peer\n+    * @param peer - PeerInfo\n+    */\n+  private def connectTo(peer: PeerInfo): Unit = {\n+    log.info(s\"Connecting to peer: $peer\")\n+    getPeerAddress(peer) match {\n+      case Some(remote) =>\n+        if (connectionForPeerAddress(remote).isEmpty && !outgoing.contains(remote)) {\n+          outgoing += remote\n+          tcpManager ! Connect(remote,\n+            options = KeepAlive(true) :: Nil,\n+            timeout = Some(settings.connectionTimeout),\n+            pullMode = true) //todo: check pullMode flag\n+        } else {\n+          log.warn(s\"Connection to peer $remote is already established\")\n+        }\n+      case None =>\n+        log.warn(s\"Can't obtain remote address for peer $peer\")\n+\n+    }\n+  }\n+\n+  /**\n+    * Creates a PeerConnectionHandler for the established connection\n+    * @param remote - remote address of socket to peer\n+    * @param local - local address of socket to peer\n+    * @param connection - connection ActorRef\n+    * @return\n+    */\n+  private def createPeerConnectionHandler(remote: InetSocketAddress, local: InetSocketAddress, connection: ActorRef) = {\n+    val direction: ConnectionType = if (outgoing.contains(remote)) Outgoing else Incoming\n+    val logMsg = direction match {\n+      case Incoming => s\"New incoming connection from $remote established (bound to local $local)\"\n+      case Outgoing => s\"New outgoing connection to $remote established (bound to local $local)\"\n+    }\n+    log.info(logMsg)\n+\n+    val peerFeatures = if (remote.getAddress.isSiteLocalAddress() || remote.getAddress.isLoopbackAddress()) {\n+      scorexContext.features :+ LocalAddressPeerFeature(new InetSocketAddress(local.getAddress, settings.bindAddress.getPort))\n+    } else {\n+      scorexContext.features\n+    }\n+\n+    val connectionDescription = ConnectionDescription(\n+      connection, direction, getNodeAddressForPeer(local), remote, peerFeatures)\n+\n+    val handlerProps: Props = PeerConnectionHandlerRef.props(settings, self, peerManagerRef,\n+      scorexContext, connectionDescription)\n+\n+    val handler = context.actorOf(handlerProps) // launch connection handler\n+    context.watch(handler)\n+    val connectedPeer = ConnectedPeer(remote, handler, None)\n+    connections += remote -> connectedPeer\n+    outgoing -= remote\n+  }\n+\n+  /**\n+    * The logic of handling the handshake\n+    * @param peerInfo\n+    * @param peerHandler\n+    */\n+  private def handshaked(peerInfo: PeerInfo, peerHandler: ActorRef): Unit = {\n+    connectionForHandler(peerHandler).foreach { connectedPeer =>\n+\n+      log.trace(s\"Got handshake from $peerInfo\")\n+      val peerAddress = peerInfo.declaredAddress.orElse(peerInfo.localAddress).getOrElse(connectedPeer.remote)\n+\n+      //drop connection to self if occurred or peer already connected\n+      if (isSelf(connectedPeer.remote) || connectionForPeerAddress(peerAddress).exists(_.handlerRef != peerHandler)) {\n+        connectedPeer.handlerRef ! CloseConnection\n+        peerManagerRef ! RemovePeer(peerAddress)\n+        connections -= connectedPeer.remote\n+      } else {\n+        if (peerInfo.reachablePeer) {\n+          val peerName = peerInfo.nodeName\n+          val peerFeats = peerInfo.features\n+          peerManagerRef ! AddOrUpdatePeer(peerAddress, peerName, peerInfo.connectionType, peerFeats)\n+        } else {\n+          peerManagerRef ! RemovePeer(peerAddress)\n+        }\n+        val updatedConnectedPeer = connectedPeer.copy(peerInfo = Some(peerInfo))\n+        connections += connectedPeer.remote -> updatedConnectedPeer\n+        context.system.eventStream.publish(HandshakedPeer(updatedConnectedPeer))\n+      }\n+    }\n+  }\n+\n+  /**\n+    * Returns connections filtered by given SendingStrategy\n+    * @param sendingStrategy SendingStrategy\n+    * @return sequence of ConnectedPeer instances according SendingStrategy\n+    */\n+  private def filterConnections(sendingStrategy: SendingStrategy) = {\n+    sendingStrategy.choose(connections.values.toSeq)\n+  }\n+\n+  /**\n+    * Returns connection for given PeerConnectionHandler ActorRef\n+    * @param handler ActorRef on PeerConnectionHandler actor\n+    * @return Some(ConnectedPeer) when the connection exists for this handler, and None otherwise\n+    */\n+  private def connectionForHandler(handler: ActorRef) = {\n+    connections.values.find { connectedPeer =>\n+       connectedPeer.handlerRef == handler\n+    }\n+  }\n+\n+  /**\n+    * Returns connection for given address of the peer\n+    * @param peerAddress - socket address of peer\n+    * @return Some(ConnectedPeer) when the connection exists for this peer, and None otherwise\n+    */\n+  private def connectionForPeerAddress(peerAddress: InetSocketAddress) = {\n+    connections.values.find { connectedPeer =>\n+      connectedPeer.remote == peerAddress ||\n+      connectedPeer.peerInfo.exists(peerInfo => getPeerAddress(peerInfo) == peerAddress)","path":"src/main/scala/scorex/core/network/NetworkController.scala","position":368,"original_position":368,"commit_id":"e19c148144a4bc02e4c56e9438f7c3b438af74d6","original_commit_id":"e19c148144a4bc02e4c56e9438f7c3b438af74d6","user":{"login":"catena2w","id":15503202,"node_id":"MDQ6VXNlcjE1NTAzMjAy","avatar_url":"https://avatars3.githubusercontent.com/u/15503202?v=4","gravatar_id":"","url":"https://api.github.com/users/catena2w","html_url":"https://github.com/catena2w","followers_url":"https://api.github.com/users/catena2w/followers","following_url":"https://api.github.com/users/catena2w/following{/other_user}","gists_url":"https://api.github.com/users/catena2w/gists{/gist_id}","starred_url":"https://api.github.com/users/catena2w/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/catena2w/subscriptions","organizations_url":"https://api.github.com/users/catena2w/orgs","repos_url":"https://api.github.com/users/catena2w/repos","events_url":"https://api.github.com/users/catena2w/events{/privacy}","received_events_url":"https://api.github.com/users/catena2w/received_events","type":"User","site_admin":false},"body":"comparison of `Option[InetSocketAddress]` with `InetSocketAddress`","created_at":"2018-09-03T12:15:23Z","updated_at":"2018-09-03T12:17:38Z","html_url":"https://github.com/ScorexFoundation/Scorex/pull/306#discussion_r214671147","pull_request_url":"https://api.github.com/repos/ScorexFoundation/Scorex/pulls/306","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/ScorexFoundation/Scorex/pulls/comments/214671147"},"html":{"href":"https://github.com/ScorexFoundation/Scorex/pull/306#discussion_r214671147"},"pull_request":{"href":"https://api.github.com/repos/ScorexFoundation/Scorex/pulls/306"}}},"pull_request":{"url":"https://api.github.com/repos/ScorexFoundation/Scorex/pulls/306","id":212613826,"node_id":"MDExOlB1bGxSZXF1ZXN0MjEyNjEzODI2","html_url":"https://github.com/ScorexFoundation/Scorex/pull/306","diff_url":"https://github.com/ScorexFoundation/Scorex/pull/306.diff","patch_url":"https://github.com/ScorexFoundation/Scorex/pull/306.patch","issue_url":"https://api.github.com/repos/ScorexFoundation/Scorex/issues/306","number":306,"state":"open","locked":false,"title":"Network layer refactoring","user":{"login":"VictorMikheev","id":6826386,"node_id":"MDQ6VXNlcjY4MjYzODY=","avatar_url":"https://avatars1.githubusercontent.com/u/6826386?v=4","gravatar_id":"","url":"https://api.github.com/users/VictorMikheev","html_url":"https://github.com/VictorMikheev","followers_url":"https://api.github.com/users/VictorMikheev/followers","following_url":"https://api.github.com/users/VictorMikheev/following{/other_user}","gists_url":"https://api.github.com/users/VictorMikheev/gists{/gist_id}","starred_url":"https://api.github.com/users/VictorMikheev/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/VictorMikheev/subscriptions","organizations_url":"https://api.github.com/users/VictorMikheev/orgs","repos_url":"https://api.github.com/users/VictorMikheev/repos","events_url":"https://api.github.com/users/VictorMikheev/events{/privacy}","received_events_url":"https://api.github.com/users/VictorMikheev/received_events","type":"User","site_admin":false},"body":"- connection management is moved to NetworkController\r\n- Akka Framing is not used now \r\n- parsing messages method is moved to MessageSerializer\r\n- refactor PeerConnectionHandler","created_at":"2018-09-02T22:05:37Z","updated_at":"2018-09-03T12:17:38Z","closed_at":null,"merged_at":null,"merge_commit_sha":null,"assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/ScorexFoundation/Scorex/pulls/306/commits","review_comments_url":"https://api.github.com/repos/ScorexFoundation/Scorex/pulls/306/comments","review_comment_url":"https://api.github.com/repos/ScorexFoundation/Scorex/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ScorexFoundation/Scorex/issues/306/comments","statuses_url":"https://api.github.com/repos/ScorexFoundation/Scorex/statuses/e19c148144a4bc02e4c56e9438f7c3b438af74d6","head":{"label":"ScorexFoundation:network-refactoring","ref":"network-refactoring","sha":"e19c148144a4bc02e4c56e9438f7c3b438af74d6","user":{"login":"ScorexFoundation","id":20186710,"node_id":"MDEyOk9yZ2FuaXphdGlvbjIwMTg2NzEw","avatar_url":"https://avatars1.githubusercontent.com/u/20186710?v=4","gravatar_id":"","url":"https://api.github.com/users/ScorexFoundation","html_url":"https://github.com/ScorexFoundation","followers_url":"https://api.github.com/users/ScorexFoundation/followers","following_url":"https://api.github.com/users/ScorexFoundation/following{/other_user}","gists_url":"https://api.github.com/users/ScorexFoundation/gists{/gist_id}","starred_url":"https://api.github.com/users/ScorexFoundation/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ScorexFoundation/subscriptions","organizations_url":"https://api.github.com/users/ScorexFoundation/orgs","repos_url":"https://api.github.com/users/ScorexFoundation/repos","events_url":"https://api.github.com/users/ScorexFoundation/events{/privacy}","received_events_url":"https://api.github.com/users/ScorexFoundation/received_events","type":"Organization","site_admin":false},"repo":{"id":62137956,"node_id":"MDEwOlJlcG9zaXRvcnk2MjEzNzk1Ng==","name":"Scorex","full_name":"ScorexFoundation/Scorex","owner":{"login":"ScorexFoundation","id":20186710,"node_id":"MDEyOk9yZ2FuaXphdGlvbjIwMTg2NzEw","avatar_url":"https://avatars1.githubusercontent.com/u/20186710?v=4","gravatar_id":"","url":"https://api.github.com/users/ScorexFoundation","html_url":"https://github.com/ScorexFoundation","followers_url":"https://api.github.com/users/ScorexFoundation/followers","following_url":"https://api.github.com/users/ScorexFoundation/following{/other_user}","gists_url":"https://api.github.com/users/ScorexFoundation/gists{/gist_id}","starred_url":"https://api.github.com/users/ScorexFoundation/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ScorexFoundation/subscriptions","organizations_url":"https://api.github.com/users/ScorexFoundation/orgs","repos_url":"https://api.github.com/users/ScorexFoundation/repos","events_url":"https://api.github.com/users/ScorexFoundation/events{/privacy}","received_events_url":"https://api.github.com/users/ScorexFoundation/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ScorexFoundation/Scorex","description":"Scorex 2.0 Core","fork":false,"url":"https://api.github.com/repos/ScorexFoundation/Scorex","forks_url":"https://api.github.com/repos/ScorexFoundation/Scorex/forks","keys_url":"https://api.github.com/repos/ScorexFoundation/Scorex/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ScorexFoundation/Scorex/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ScorexFoundation/Scorex/teams","hooks_url":"https://api.github.com/repos/ScorexFoundation/Scorex/hooks","issue_events_url":"https://api.github.com/repos/ScorexFoundation/Scorex/issues/events{/number}","events_url":"https://api.github.com/repos/ScorexFoundation/Scorex/events","assignees_url":"https://api.github.com/repos/ScorexFoundation/Scorex/assignees{/user}","branches_url":"https://api.github.com/repos/ScorexFoundation/Scorex/branches{/branch}","tags_url":"https://api.github.com/repos/ScorexFoundation/Scorex/tags","blobs_url":"https://api.github.com/repos/ScorexFoundation/Scorex/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ScorexFoundation/Scorex/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ScorexFoundation/Scorex/git/refs{/sha}","trees_url":"https://api.github.com/repos/ScorexFoundation/Scorex/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ScorexFoundation/Scorex/statuses/{sha}","languages_url":"https://api.github.com/repos/ScorexFoundation/Scorex/languages","stargazers_url":"https://api.github.com/repos/ScorexFoundation/Scorex/stargazers","contributors_url":"https://api.github.com/repos/ScorexFoundation/Scorex/contributors","subscribers_url":"https://api.github.com/repos/ScorexFoundation/Scorex/subscribers","subscription_url":"https://api.github.com/repos/ScorexFoundation/Scorex/subscription","commits_url":"https://api.github.com/repos/ScorexFoundation/Scorex/commits{/sha}","git_commits_url":"https://api.github.com/repos/ScorexFoundation/Scorex/git/commits{/sha}","comments_url":"https://api.github.com/repos/ScorexFoundation/Scorex/comments{/number}","issue_comment_url":"https://api.github.com/repos/ScorexFoundation/Scorex/issues/comments{/number}","contents_url":"https://api.github.com/repos/ScorexFoundation/Scorex/contents/{+path}","compare_url":"https://api.github.com/repos/ScorexFoundation/Scorex/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ScorexFoundation/Scorex/merges","archive_url":"https://api.github.com/repos/ScorexFoundation/Scorex/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ScorexFoundation/Scorex/downloads","issues_url":"https://api.github.com/repos/ScorexFoundation/Scorex/issues{/number}","pulls_url":"https://api.github.com/repos/ScorexFoundation/Scorex/pulls{/number}","milestones_url":"https://api.github.com/repos/ScorexFoundation/Scorex/milestones{/number}","notifications_url":"https://api.github.com/repos/ScorexFoundation/Scorex/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ScorexFoundation/Scorex/labels{/name}","releases_url":"https://api.github.com/repos/ScorexFoundation/Scorex/releases{/id}","deployments_url":"https://api.github.com/repos/ScorexFoundation/Scorex/deployments","created_at":"2016-06-28T12:13:40Z","updated_at":"2018-08-30T14:13:00Z","pushed_at":"2018-09-02T22:05:38Z","git_url":"git://github.com/ScorexFoundation/Scorex.git","ssh_url":"git@github.com:ScorexFoundation/Scorex.git","clone_url":"https://github.com/ScorexFoundation/Scorex.git","svn_url":"https://github.com/ScorexFoundation/Scorex","homepage":"","size":9372,"stargazers_count":452,"watchers_count":452,"language":"Scala","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":111,"mirror_url":null,"archived":false,"open_issues_count":47,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":111,"open_issues":47,"watchers":452,"default_branch":"master"}},"base":{"label":"ScorexFoundation:master","ref":"master","sha":"41f3db48d66c66406e5a29162982e617fb69885a","user":{"login":"ScorexFoundation","id":20186710,"node_id":"MDEyOk9yZ2FuaXphdGlvbjIwMTg2NzEw","avatar_url":"https://avatars1.githubusercontent.com/u/20186710?v=4","gravatar_id":"","url":"https://api.github.com/users/ScorexFoundation","html_url":"https://github.com/ScorexFoundation","followers_url":"https://api.github.com/users/ScorexFoundation/followers","following_url":"https://api.github.com/users/ScorexFoundation/following{/other_user}","gists_url":"https://api.github.com/users/ScorexFoundation/gists{/gist_id}","starred_url":"https://api.github.com/users/ScorexFoundation/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ScorexFoundation/subscriptions","organizations_url":"https://api.github.com/users/ScorexFoundation/orgs","repos_url":"https://api.github.com/users/ScorexFoundation/repos","events_url":"https://api.github.com/users/ScorexFoundation/events{/privacy}","received_events_url":"https://api.github.com/users/ScorexFoundation/received_events","type":"Organization","site_admin":false},"repo":{"id":62137956,"node_id":"MDEwOlJlcG9zaXRvcnk2MjEzNzk1Ng==","name":"Scorex","full_name":"ScorexFoundation/Scorex","owner":{"login":"ScorexFoundation","id":20186710,"node_id":"MDEyOk9yZ2FuaXphdGlvbjIwMTg2NzEw","avatar_url":"https://avatars1.githubusercontent.com/u/20186710?v=4","gravatar_id":"","url":"https://api.github.com/users/ScorexFoundation","html_url":"https://github.com/ScorexFoundation","followers_url":"https://api.github.com/users/ScorexFoundation/followers","following_url":"https://api.github.com/users/ScorexFoundation/following{/other_user}","gists_url":"https://api.github.com/users/ScorexFoundation/gists{/gist_id}","starred_url":"https://api.github.com/users/ScorexFoundation/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ScorexFoundation/subscriptions","organizations_url":"https://api.github.com/users/ScorexFoundation/orgs","repos_url":"https://api.github.com/users/ScorexFoundation/repos","events_url":"https://api.github.com/users/ScorexFoundation/events{/privacy}","received_events_url":"https://api.github.com/users/ScorexFoundation/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ScorexFoundation/Scorex","description":"Scorex 2.0 Core","fork":false,"url":"https://api.github.com/repos/ScorexFoundation/Scorex","forks_url":"https://api.github.com/repos/ScorexFoundation/Scorex/forks","keys_url":"https://api.github.com/repos/ScorexFoundation/Scorex/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ScorexFoundation/Scorex/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ScorexFoundation/Scorex/teams","hooks_url":"https://api.github.com/repos/ScorexFoundation/Scorex/hooks","issue_events_url":"https://api.github.com/repos/ScorexFoundation/Scorex/issues/events{/number}","events_url":"https://api.github.com/repos/ScorexFoundation/Scorex/events","assignees_url":"https://api.github.com/repos/ScorexFoundation/Scorex/assignees{/user}","branches_url":"https://api.github.com/repos/ScorexFoundation/Scorex/branches{/branch}","tags_url":"https://api.github.com/repos/ScorexFoundation/Scorex/tags","blobs_url":"https://api.github.com/repos/ScorexFoundation/Scorex/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ScorexFoundation/Scorex/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ScorexFoundation/Scorex/git/refs{/sha}","trees_url":"https://api.github.com/repos/ScorexFoundation/Scorex/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ScorexFoundation/Scorex/statuses/{sha}","languages_url":"https://api.github.com/repos/ScorexFoundation/Scorex/languages","stargazers_url":"https://api.github.com/repos/ScorexFoundation/Scorex/stargazers","contributors_url":"https://api.github.com/repos/ScorexFoundation/Scorex/contributors","subscribers_url":"https://api.github.com/repos/ScorexFoundation/Scorex/subscribers","subscription_url":"https://api.github.com/repos/ScorexFoundation/Scorex/subscription","commits_url":"https://api.github.com/repos/ScorexFoundation/Scorex/commits{/sha}","git_commits_url":"https://api.github.com/repos/ScorexFoundation/Scorex/git/commits{/sha}","comments_url":"https://api.github.com/repos/ScorexFoundation/Scorex/comments{/number}","issue_comment_url":"https://api.github.com/repos/ScorexFoundation/Scorex/issues/comments{/number}","contents_url":"https://api.github.com/repos/ScorexFoundation/Scorex/contents/{+path}","compare_url":"https://api.github.com/repos/ScorexFoundation/Scorex/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ScorexFoundation/Scorex/merges","archive_url":"https://api.github.com/repos/ScorexFoundation/Scorex/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ScorexFoundation/Scorex/downloads","issues_url":"https://api.github.com/repos/ScorexFoundation/Scorex/issues{/number}","pulls_url":"https://api.github.com/repos/ScorexFoundation/Scorex/pulls{/number}","milestones_url":"https://api.github.com/repos/ScorexFoundation/Scorex/milestones{/number}","notifications_url":"https://api.github.com/repos/ScorexFoundation/Scorex/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ScorexFoundation/Scorex/labels{/name}","releases_url":"https://api.github.com/repos/ScorexFoundation/Scorex/releases{/id}","deployments_url":"https://api.github.com/repos/ScorexFoundation/Scorex/deployments","created_at":"2016-06-28T12:13:40Z","updated_at":"2018-08-30T14:13:00Z","pushed_at":"2018-09-02T22:05:38Z","git_url":"git://github.com/ScorexFoundation/Scorex.git","ssh_url":"git@github.com:ScorexFoundation/Scorex.git","clone_url":"https://github.com/ScorexFoundation/Scorex.git","svn_url":"https://github.com/ScorexFoundation/Scorex","homepage":"","size":9372,"stargazers_count":452,"watchers_count":452,"language":"Scala","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":111,"mirror_url":null,"archived":false,"open_issues_count":47,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":111,"open_issues":47,"watchers":452,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ScorexFoundation/Scorex/pulls/306"},"html":{"href":"https://github.com/ScorexFoundation/Scorex/pull/306"},"issue":{"href":"https://api.github.com/repos/ScorexFoundation/Scorex/issues/306"},"comments":{"href":"https://api.github.com/repos/ScorexFoundation/Scorex/issues/306/comments"},"review_comments":{"href":"https://api.github.com/repos/ScorexFoundation/Scorex/pulls/306/comments"},"review_comment":{"href":"https://api.github.com/repos/ScorexFoundation/Scorex/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ScorexFoundation/Scorex/pulls/306/commits"},"statuses":{"href":"https://api.github.com/repos/ScorexFoundation/Scorex/statuses/e19c148144a4bc02e4c56e9438f7c3b438af74d6"}},"author_association":"COLLABORATOR"}}
{"actor":{"display_login":"catena2w"}}