{"action":"created","comment":{"url":"https://api.github.com/repos/maidsafe/routing/pulls/comments/132428831","pull_request_review_id":55507039,"id":132428831,"diff_hunk":"@@ -136,16 +145,103 @@ impl RateLimiter {\n         self.update();\n \n         let used = self.used.get(client_ip).map_or(0, |used| *used);\n-        let allowance = CLIENT_CAPACITY - used;\n+        let new_balance = used + bytes_to_add;\n \n-        if bytes_to_add > allowance {\n+        if new_balance > self.client_allowance(client_ip) {\n             return Err(RoutingError::ExceedsRateLimit(*hash));\n         }\n \n-        let _ = self.used.insert(*client_ip, used + bytes_to_add);\n+        if overcharged {\n+            match self.overcharged.entry(*msg_id) {\n+                // If there's no overcharge recorded, record it.\n+                Entry::Vacant(entry) => {\n+                    let _ = entry.insert(bytes_to_add);\n+                }\n+                // Disallow a 2nd GET with the same message ID.\n+                Entry::Occupied(_) => {\n+                    return Err(RoutingError::InvalidMessage);\n+                }\n+            }\n+        }\n+\n+        let _ = self.used.insert(*client_ip, new_balance);\n         Ok(bytes_to_add)\n     }\n \n+    /// Compute the usage limit for any single client at the current point in time.\n+    fn client_allowance(&self, client_ip: &IpAddr) -> u64 {\n+        let num_clients = if self.used.contains_key(client_ip) {\n+            self.used.len()\n+        } else {\n+            self.used.len() + 1\n+        };\n+        cmp::max(MIN_CLIENT_CAPACITY, SOFT_CAPACITY / num_clients as u64)\n+    }\n+\n+    /// Update a client's balance to compensate for initial over-counting.\n+    ///\n+    /// When a request is made, clients are charged at the maximum size of the data\n+    /// being requested. This method compensates the client for the over-counting by\n+    /// crediting them the difference between the maximum and the actual size of the response.\n+    pub fn deduct_response(\n+        &mut self,\n+        client_ip: &IpAddr,\n+        msg_id: &MessageId,\n+        part_count: u32,\n+        part_index: u32,\n+        payload: &[u8],\n+    ) -> Option<u64> {\n+        use Response::*;\n+\n+        // Check that the response isn't a single-part response that we never overcharged for.\n+        // This prevents a malicious client from discounting their GET by preceding\n+        // it with a PUT with the same `msg_id`.","path":"src/rate_limiter.rs","position":182,"original_position":182,"commit_id":"944749dedc5a9c32cd958432d854e542e88be916","original_commit_id":"944749dedc5a9c32cd958432d854e542e88be916","user":{"login":"michaelsproul","id":4452260,"avatar_url":"https://avatars1.githubusercontent.com/u/4452260?v=4","gravatar_id":"","url":"https://api.github.com/users/michaelsproul","html_url":"https://github.com/michaelsproul","followers_url":"https://api.github.com/users/michaelsproul/followers","following_url":"https://api.github.com/users/michaelsproul/following{/other_user}","gists_url":"https://api.github.com/users/michaelsproul/gists{/gist_id}","starred_url":"https://api.github.com/users/michaelsproul/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/michaelsproul/subscriptions","organizations_url":"https://api.github.com/users/michaelsproul/orgs","repos_url":"https://api.github.com/users/michaelsproul/repos","events_url":"https://api.github.com/users/michaelsproul/events{/privacy}","received_events_url":"https://api.github.com/users/michaelsproul/received_events","type":"User","site_admin":false},"body":"Yeah, I'll clarify that the get/put is just an example. I think the code as written should prevent problems with other small messages too.","created_at":"2017-08-10T11:29:55Z","updated_at":"2017-08-10T11:29:55Z","html_url":"https://github.com/maidsafe/routing/pull/1508#discussion_r132428831","pull_request_url":"https://api.github.com/repos/maidsafe/routing/pulls/1508","_links":{"self":{"href":"https://api.github.com/repos/maidsafe/routing/pulls/comments/132428831"},"html":{"href":"https://github.com/maidsafe/routing/pull/1508#discussion_r132428831"},"pull_request":{"href":"https://api.github.com/repos/maidsafe/routing/pulls/1508"}}},"pull_request":{"url":"https://api.github.com/repos/maidsafe/routing/pulls/1508","id":134856987,"html_url":"https://github.com/maidsafe/routing/pull/1508","diff_url":"https://github.com/maidsafe/routing/pull/1508.diff","patch_url":"https://github.com/maidsafe/routing/pull/1508.patch","issue_url":"https://api.github.com/repos/maidsafe/routing/issues/1508","number":1508,"state":"open","locked":false,"title":"MAID-2198: improve rate limiter","user":{"login":"michaelsproul","id":4452260,"avatar_url":"https://avatars1.githubusercontent.com/u/4452260?v=4","gravatar_id":"","url":"https://api.github.com/users/michaelsproul","html_url":"https://github.com/michaelsproul","followers_url":"https://api.github.com/users/michaelsproul/followers","following_url":"https://api.github.com/users/michaelsproul/following{/other_user}","gists_url":"https://api.github.com/users/michaelsproul/gists{/gist_id}","starred_url":"https://api.github.com/users/michaelsproul/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/michaelsproul/subscriptions","organizations_url":"https://api.github.com/users/michaelsproul/orgs","repos_url":"https://api.github.com/users/michaelsproul/repos","events_url":"https://api.github.com/users/michaelsproul/events{/privacy}","received_events_url":"https://api.github.com/users/michaelsproul/received_events","type":"User","site_admin":false},"body":"","created_at":"2017-08-09T10:14:46Z","updated_at":"2017-08-10T11:29:55Z","closed_at":null,"merged_at":null,"merge_commit_sha":"e4f99590723ada8bf52cdcfae57de6ecc360aa95","assignee":null,"assignees":[],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/maidsafe/routing/pulls/1508/commits","review_comments_url":"https://api.github.com/repos/maidsafe/routing/pulls/1508/comments","review_comment_url":"https://api.github.com/repos/maidsafe/routing/pulls/comments{/number}","comments_url":"https://api.github.com/repos/maidsafe/routing/issues/1508/comments","statuses_url":"https://api.github.com/repos/maidsafe/routing/statuses/944749dedc5a9c32cd958432d854e542e88be916","head":{"label":"michaelsproul:maid-2198","ref":"maid-2198","sha":"944749dedc5a9c32cd958432d854e542e88be916","user":{"login":"michaelsproul","id":4452260,"avatar_url":"https://avatars1.githubusercontent.com/u/4452260?v=4","gravatar_id":"","url":"https://api.github.com/users/michaelsproul","html_url":"https://github.com/michaelsproul","followers_url":"https://api.github.com/users/michaelsproul/followers","following_url":"https://api.github.com/users/michaelsproul/following{/other_user}","gists_url":"https://api.github.com/users/michaelsproul/gists{/gist_id}","starred_url":"https://api.github.com/users/michaelsproul/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/michaelsproul/subscriptions","organizations_url":"https://api.github.com/users/michaelsproul/orgs","repos_url":"https://api.github.com/users/michaelsproul/repos","events_url":"https://api.github.com/users/michaelsproul/events{/privacy}","received_events_url":"https://api.github.com/users/michaelsproul/received_events","type":"User","site_admin":false},"repo":{"id":76030480,"name":"routing","full_name":"michaelsproul/routing","owner":{"login":"michaelsproul","id":4452260,"avatar_url":"https://avatars1.githubusercontent.com/u/4452260?v=4","gravatar_id":"","url":"https://api.github.com/users/michaelsproul","html_url":"https://github.com/michaelsproul","followers_url":"https://api.github.com/users/michaelsproul/followers","following_url":"https://api.github.com/users/michaelsproul/following{/other_user}","gists_url":"https://api.github.com/users/michaelsproul/gists{/gist_id}","starred_url":"https://api.github.com/users/michaelsproul/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/michaelsproul/subscriptions","organizations_url":"https://api.github.com/users/michaelsproul/orgs","repos_url":"https://api.github.com/users/michaelsproul/repos","events_url":"https://api.github.com/users/michaelsproul/events{/privacy}","received_events_url":"https://api.github.com/users/michaelsproul/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/michaelsproul/routing","description":"Routing - specialised storage DHT","fork":true,"url":"https://api.github.com/repos/michaelsproul/routing","forks_url":"https://api.github.com/repos/michaelsproul/routing/forks","keys_url":"https://api.github.com/repos/michaelsproul/routing/keys{/key_id}","collaborators_url":"https://api.github.com/repos/michaelsproul/routing/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/michaelsproul/routing/teams","hooks_url":"https://api.github.com/repos/michaelsproul/routing/hooks","issue_events_url":"https://api.github.com/repos/michaelsproul/routing/issues/events{/number}","events_url":"https://api.github.com/repos/michaelsproul/routing/events","assignees_url":"https://api.github.com/repos/michaelsproul/routing/assignees{/user}","branches_url":"https://api.github.com/repos/michaelsproul/routing/branches{/branch}","tags_url":"https://api.github.com/repos/michaelsproul/routing/tags","blobs_url":"https://api.github.com/repos/michaelsproul/routing/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/michaelsproul/routing/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/michaelsproul/routing/git/refs{/sha}","trees_url":"https://api.github.com/repos/michaelsproul/routing/git/trees{/sha}","statuses_url":"https://api.github.com/repos/michaelsproul/routing/statuses/{sha}","languages_url":"https://api.github.com/repos/michaelsproul/routing/languages","stargazers_url":"https://api.github.com/repos/michaelsproul/routing/stargazers","contributors_url":"https://api.github.com/repos/michaelsproul/routing/contributors","subscribers_url":"https://api.github.com/repos/michaelsproul/routing/subscribers","subscription_url":"https://api.github.com/repos/michaelsproul/routing/subscription","commits_url":"https://api.github.com/repos/michaelsproul/routing/commits{/sha}","git_commits_url":"https://api.github.com/repos/michaelsproul/routing/git/commits{/sha}","comments_url":"https://api.github.com/repos/michaelsproul/routing/comments{/number}","issue_comment_url":"https://api.github.com/repos/michaelsproul/routing/issues/comments{/number}","contents_url":"https://api.github.com/repos/michaelsproul/routing/contents/{+path}","compare_url":"https://api.github.com/repos/michaelsproul/routing/compare/{base}...{head}","merges_url":"https://api.github.com/repos/michaelsproul/routing/merges","archive_url":"https://api.github.com/repos/michaelsproul/routing/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/michaelsproul/routing/downloads","issues_url":"https://api.github.com/repos/michaelsproul/routing/issues{/number}","pulls_url":"https://api.github.com/repos/michaelsproul/routing/pulls{/number}","milestones_url":"https://api.github.com/repos/michaelsproul/routing/milestones{/number}","notifications_url":"https://api.github.com/repos/michaelsproul/routing/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/michaelsproul/routing/labels{/name}","releases_url":"https://api.github.com/repos/michaelsproul/routing/releases{/id}","deployments_url":"https://api.github.com/repos/michaelsproul/routing/deployments","created_at":"2016-12-09T11:38:31Z","updated_at":"2016-12-09T11:38:33Z","pushed_at":"2017-08-10T09:34:28Z","git_url":"git://github.com/michaelsproul/routing.git","ssh_url":"git@github.com:michaelsproul/routing.git","clone_url":"https://github.com/michaelsproul/routing.git","svn_url":"https://github.com/michaelsproul/routing","homepage":null,"size":111541,"stargazers_count":0,"watchers_count":0,"language":"Rust","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"maidsafe:master","ref":"master","sha":"9a4dd1ee749ced842d908edbaf88129ffe4b366a","user":{"login":"maidsafe","id":536423,"avatar_url":"https://avatars1.githubusercontent.com/u/536423?v=4","gravatar_id":"","url":"https://api.github.com/users/maidsafe","html_url":"https://github.com/maidsafe","followers_url":"https://api.github.com/users/maidsafe/followers","following_url":"https://api.github.com/users/maidsafe/following{/other_user}","gists_url":"https://api.github.com/users/maidsafe/gists{/gist_id}","starred_url":"https://api.github.com/users/maidsafe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/maidsafe/subscriptions","organizations_url":"https://api.github.com/users/maidsafe/orgs","repos_url":"https://api.github.com/users/maidsafe/repos","events_url":"https://api.github.com/users/maidsafe/events{/privacy}","received_events_url":"https://api.github.com/users/maidsafe/received_events","type":"Organization","site_admin":false},"repo":{"id":31270514,"name":"routing","full_name":"maidsafe/routing","owner":{"login":"maidsafe","id":536423,"avatar_url":"https://avatars1.githubusercontent.com/u/536423?v=4","gravatar_id":"","url":"https://api.github.com/users/maidsafe","html_url":"https://github.com/maidsafe","followers_url":"https://api.github.com/users/maidsafe/followers","following_url":"https://api.github.com/users/maidsafe/following{/other_user}","gists_url":"https://api.github.com/users/maidsafe/gists{/gist_id}","starred_url":"https://api.github.com/users/maidsafe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/maidsafe/subscriptions","organizations_url":"https://api.github.com/users/maidsafe/orgs","repos_url":"https://api.github.com/users/maidsafe/repos","events_url":"https://api.github.com/users/maidsafe/events{/privacy}","received_events_url":"https://api.github.com/users/maidsafe/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/maidsafe/routing","description":"Routing - specialised storage DHT","fork":false,"url":"https://api.github.com/repos/maidsafe/routing","forks_url":"https://api.github.com/repos/maidsafe/routing/forks","keys_url":"https://api.github.com/repos/maidsafe/routing/keys{/key_id}","collaborators_url":"https://api.github.com/repos/maidsafe/routing/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/maidsafe/routing/teams","hooks_url":"https://api.github.com/repos/maidsafe/routing/hooks","issue_events_url":"https://api.github.com/repos/maidsafe/routing/issues/events{/number}","events_url":"https://api.github.com/repos/maidsafe/routing/events","assignees_url":"https://api.github.com/repos/maidsafe/routing/assignees{/user}","branches_url":"https://api.github.com/repos/maidsafe/routing/branches{/branch}","tags_url":"https://api.github.com/repos/maidsafe/routing/tags","blobs_url":"https://api.github.com/repos/maidsafe/routing/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/maidsafe/routing/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/maidsafe/routing/git/refs{/sha}","trees_url":"https://api.github.com/repos/maidsafe/routing/git/trees{/sha}","statuses_url":"https://api.github.com/repos/maidsafe/routing/statuses/{sha}","languages_url":"https://api.github.com/repos/maidsafe/routing/languages","stargazers_url":"https://api.github.com/repos/maidsafe/routing/stargazers","contributors_url":"https://api.github.com/repos/maidsafe/routing/contributors","subscribers_url":"https://api.github.com/repos/maidsafe/routing/subscribers","subscription_url":"https://api.github.com/repos/maidsafe/routing/subscription","commits_url":"https://api.github.com/repos/maidsafe/routing/commits{/sha}","git_commits_url":"https://api.github.com/repos/maidsafe/routing/git/commits{/sha}","comments_url":"https://api.github.com/repos/maidsafe/routing/comments{/number}","issue_comment_url":"https://api.github.com/repos/maidsafe/routing/issues/comments{/number}","contents_url":"https://api.github.com/repos/maidsafe/routing/contents/{+path}","compare_url":"https://api.github.com/repos/maidsafe/routing/compare/{base}...{head}","merges_url":"https://api.github.com/repos/maidsafe/routing/merges","archive_url":"https://api.github.com/repos/maidsafe/routing/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/maidsafe/routing/downloads","issues_url":"https://api.github.com/repos/maidsafe/routing/issues{/number}","pulls_url":"https://api.github.com/repos/maidsafe/routing/pulls{/number}","milestones_url":"https://api.github.com/repos/maidsafe/routing/milestones{/number}","notifications_url":"https://api.github.com/repos/maidsafe/routing/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/maidsafe/routing/labels{/name}","releases_url":"https://api.github.com/repos/maidsafe/routing/releases{/id}","deployments_url":"https://api.github.com/repos/maidsafe/routing/deployments","created_at":"2015-02-24T16:52:53Z","updated_at":"2017-07-29T15:46:48Z","pushed_at":"2017-08-10T09:34:30Z","git_url":"git://github.com/maidsafe/routing.git","ssh_url":"git@github.com:maidsafe/routing.git","clone_url":"https://github.com/maidsafe/routing.git","svn_url":"https://github.com/maidsafe/routing","homepage":null,"size":111763,"stargazers_count":155,"watchers_count":155,"language":"Rust","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":45,"mirror_url":null,"open_issues_count":4,"forks":45,"open_issues":4,"watchers":155,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/maidsafe/routing/pulls/1508"},"html":{"href":"https://github.com/maidsafe/routing/pull/1508"},"issue":{"href":"https://api.github.com/repos/maidsafe/routing/issues/1508"},"comments":{"href":"https://api.github.com/repos/maidsafe/routing/issues/1508/comments"},"review_comments":{"href":"https://api.github.com/repos/maidsafe/routing/pulls/1508/comments"},"review_comment":{"href":"https://api.github.com/repos/maidsafe/routing/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/maidsafe/routing/pulls/1508/commits"},"statuses":{"href":"https://api.github.com/repos/maidsafe/routing/statuses/944749dedc5a9c32cd958432d854e542e88be916"}}}}
{"actor":{"display_login":"michaelsproul"}}