{"action":"created","comment":{"url":"https://api.github.com/repos/solana-labs/solana/pulls/comments/222044811","pull_request_review_id":160839188,"id":222044811,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIyMjA0NDgxMQ==","diff_hunk":"@@ -0,0 +1,776 @@\n+//! The `leader_scheduler` module implements a structure and functions for tracking and\n+//! managing the schedule for leader rotation\n+\n+use bank::Bank;\n+use bincode::serialize;\n+use byteorder::{LittleEndian, ReadBytesExt};\n+use hash::hash;\n+use solana_program_interface::pubkey::Pubkey;\n+use std::collections::HashMap;\n+use std::io::Cursor;\n+\n+pub struct ActiveValidators {\n+    // Map from validator id to the last PoH height at which they voted,\n+    pub active_validators: HashMap<Pubkey, u64>,\n+    pub active_window_length: u64,\n+}\n+\n+impl ActiveValidators {\n+    pub fn new(active_window_length_option: Option<u64>) -> Self {\n+        let mut active_window_length = 1000;\n+        if let Some(input) = active_window_length_option {\n+            active_window_length = input;\n+        }\n+\n+        ActiveValidators {\n+            active_validators: HashMap::new(),\n+            active_window_length,\n+        }\n+    }\n+\n+    // Finds all the active voters who have voted in the range\n+    // (height - active_window_length, height], and removes\n+    // anybody who hasn't voted in that range from the map\n+    pub fn get_active_set(&mut self, height: u64) -> Vec<Pubkey> {\n+        // Don't filter anything if height is less than the\n+        // size of the active window. Otherwise, calculate the acceptable\n+        // window and filter the active_validators\n+\n+        // Note: height == 0 will only be included for all\n+        // height < self.active_window_length\n+        if height >= self.active_window_length {\n+            let lower_bound = height - self.active_window_length;\n+            self.active_validators\n+                .retain(|_, height| *height > lower_bound);\n+        }\n+\n+        self.active_validators.keys().cloned().collect()\n+    }\n+\n+    // Push a vote for a validator with id == \"id\" who voted at PoH height == \"height\"\n+    pub fn push_vote(&mut self, id: Pubkey, height: u64) -> () {\n+        let old_height = self.active_validators.entry(id).or_insert(height);\n+        if height > *old_height {\n+            *old_height = height;\n+        }\n+    }\n+}\n+\n+pub struct LeaderSchedulerConfig {\n+    // The first leader who will bootstrap the network\n+    pub bootstrap_leader: Pubkey,\n+\n+    // The interval at which to rotate the leader, should be much less than\n+    // seed_rotation_interval\n+    pub leader_rotation_interval_option: Option<u64>,\n+\n+    // The interval at which to generate the seed used for ranking the validators\n+    pub seed_rotation_interval_option: Option<u64>,\n+\n+    // The last height at which the bootstrap_leader will be in power before\n+    // the leader rotation process begins to pick future leaders\n+    pub bootstrap_height_option: Option<u64>,\n+\n+    // The length of the acceptable window for determining live validators\n+    pub active_window_length_option: Option<u64>,\n+}\n+\n+// Used to toggle leader rotation in fullnode so that tests that don't\n+// need leader rotation don't break\n+impl LeaderSchedulerConfig {\n+    #[allow(dead_code)]\n+    pub fn new(\n+        bootstrap_leader: Pubkey,\n+        bootstrap_height_option: Option<u64>,\n+        leader_rotation_interval_option: Option<u64>,\n+        seed_rotation_interval_option: Option<u64>,\n+        active_window_length_option: Option<u64>,\n+    ) -> Self {\n+        LeaderSchedulerConfig {\n+            bootstrap_leader,\n+            bootstrap_height_option,\n+            leader_rotation_interval_option,\n+            seed_rotation_interval_option,\n+            active_window_length_option,\n+        }\n+    }\n+}\n+\n+pub struct LeaderScheduler {\n+    // The interval at which to rotate the leader, should be much less than\n+    // seed_rotation_interval\n+    pub leader_rotation_interval: u64,\n+\n+    // The interval at which to generate the seed used for ranking the validators\n+    pub seed_rotation_interval: u64,\n+\n+    // The first leader who will bootstrap the network\n+    pub bootstrap_leader: Pubkey,\n+\n+    // The last height at which the bootstrap_leader will be in power before\n+    // the leader rotation process begins to pick future leaders\n+    pub bootstrap_height: u64,\n+\n+    // Maintain the set of active validators\n+    pub active_validators: ActiveValidators,\n+\n+    // Round-robin ordering for the validators\n+    leader_schedule: Vec<Pubkey>,\n+\n+    // The last height at which the seed + schedule was generated\n+    last_seed_height: Option<u64>,\n+\n+    // The seed used to determine the round robin order of leaders\n+    seed: u64,\n+}\n+\n+// The LeaderScheduler implements a schedule for leaders as follows:\n+//\n+// 1) During the bootstrapping period of bootstrap_height PoH counts, the\n+// leader is hard-coded to the input bootstrap_leader.\n+//\n+// 2) After the first seed is generated, this signals the beginning of actual leader rotation.\n+// From this point on, every seed_rotation_interval PoH counts we generate the seed based\n+// on the PoH height, and use it to do a weighted sample from the set\n+// of validators based on current stake weight. This gets you the first leader A for\n+// the next leader_rotation_interval PoH counts. On the same PoH count we generate the seed,\n+// we also order the validators based on their current stake weight, and starting\n+// from leader A, we then pick the next leader sequentially every leader_rotation_interval\n+// PoH counts based on this fixed ordering, so the next\n+// seed_rotation_interval / leader_rotation_interval leaders are determined.\n+//\n+// 3) When we we hit the next seed rotation PoH height, step 2) is executed again to\n+// calculate the leader schedule for the upcoming seed_rotation_interval PoH counts.\n+impl LeaderScheduler {\n+    pub fn new(config: &LeaderSchedulerConfig) -> Self {\n+        let mut bootstrap_height = 1000;\n+        if let Some(input) = config.bootstrap_height_option {\n+            bootstrap_height = input;\n+        }\n+\n+        let mut leader_rotation_interval = 100;\n+        if let Some(input) = config.leader_rotation_interval_option {\n+            leader_rotation_interval = input;\n+        }\n+\n+        let mut seed_rotation_interval = 1000;\n+        if let Some(input) = config.seed_rotation_interval_option {\n+            seed_rotation_interval = input;\n+        }\n+\n+        // Enforced invariants\n+        assert!(seed_rotation_interval >= leader_rotation_interval);\n+        assert!(bootstrap_height > 0);\n+        assert!(seed_rotation_interval % leader_rotation_interval == 0);\n+\n+        LeaderScheduler {\n+            active_validators: ActiveValidators::new(config.active_window_length_option),\n+            leader_rotation_interval,\n+            seed_rotation_interval,\n+            leader_schedule: Vec::new(),\n+            last_seed_height: None,\n+            bootstrap_leader: config.bootstrap_leader,\n+            bootstrap_height,\n+            seed: 0,\n+        }\n+    }\n+\n+    pub fn push_vote(&mut self, id: Pubkey, height: u64) {","path":"src/leader_scheduler.rs","position":178,"original_position":178,"commit_id":"4061a3b825dd5fbe9430e18b4cd45542fd7a39ae","original_commit_id":"4061a3b825dd5fbe9430e18b4cd45542fd7a39ae","user":{"login":"rob-solana","id":40183961,"node_id":"MDQ6VXNlcjQwMTgzOTYx","avatar_url":"https://avatars0.githubusercontent.com/u/40183961?v=4","gravatar_id":"","url":"https://api.github.com/users/rob-solana","html_url":"https://github.com/rob-solana","followers_url":"https://api.github.com/users/rob-solana/followers","following_url":"https://api.github.com/users/rob-solana/following{/other_user}","gists_url":"https://api.github.com/users/rob-solana/gists{/gist_id}","starred_url":"https://api.github.com/users/rob-solana/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rob-solana/subscriptions","organizations_url":"https://api.github.com/users/rob-solana/orgs","repos_url":"https://api.github.com/users/rob-solana/repos","events_url":"https://api.github.com/users/rob-solana/events{/privacy}","received_events_url":"https://api.github.com/users/rob-solana/received_events","type":"User","site_admin":false},"body":"I guess we want to keep these separate so that there's just one place where we update height...","created_at":"2018-10-02T17:35:48Z","updated_at":"2018-10-02T17:35:48Z","html_url":"https://github.com/solana-labs/solana/pull/1398#discussion_r222044811","pull_request_url":"https://api.github.com/repos/solana-labs/solana/pulls/1398","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/solana-labs/solana/pulls/comments/222044811"},"html":{"href":"https://github.com/solana-labs/solana/pull/1398#discussion_r222044811"},"pull_request":{"href":"https://api.github.com/repos/solana-labs/solana/pulls/1398"}},"in_reply_to_id":222044624},"pull_request":{"url":"https://api.github.com/repos/solana-labs/solana/pulls/1398","id":219097943,"node_id":"MDExOlB1bGxSZXF1ZXN0MjE5MDk3OTQz","html_url":"https://github.com/solana-labs/solana/pull/1398","diff_url":"https://github.com/solana-labs/solana/pull/1398.diff","patch_url":"https://github.com/solana-labs/solana/pull/1398.patch","issue_url":"https://api.github.com/repos/solana-labs/solana/issues/1398","number":1398,"state":"open","locked":false,"title":"Added LeaderScheduler module and tests","user":{"login":"carllin","id":3374799,"node_id":"MDQ6VXNlcjMzNzQ3OTk=","avatar_url":"https://avatars1.githubusercontent.com/u/3374799?v=4","gravatar_id":"","url":"https://api.github.com/users/carllin","html_url":"https://github.com/carllin","followers_url":"https://api.github.com/users/carllin/followers","following_url":"https://api.github.com/users/carllin/following{/other_user}","gists_url":"https://api.github.com/users/carllin/gists{/gist_id}","starred_url":"https://api.github.com/users/carllin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/carllin/subscriptions","organizations_url":"https://api.github.com/users/carllin/orgs","repos_url":"https://api.github.com/users/carllin/repos","events_url":"https://api.github.com/users/carllin/events{/privacy}","received_events_url":"https://api.github.com/users/carllin/received_events","type":"User","site_admin":false},"body":"","created_at":"2018-09-28T22:12:00Z","updated_at":"2018-10-02T17:35:48Z","closed_at":null,"merged_at":null,"merge_commit_sha":"5f9f5cc6d11e91cc845e00ec1423ab4f55e7c38d","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/solana-labs/solana/pulls/1398/commits","review_comments_url":"https://api.github.com/repos/solana-labs/solana/pulls/1398/comments","review_comment_url":"https://api.github.com/repos/solana-labs/solana/pulls/comments{/number}","comments_url":"https://api.github.com/repos/solana-labs/solana/issues/1398/comments","statuses_url":"https://api.github.com/repos/solana-labs/solana/statuses/4061a3b825dd5fbe9430e18b4cd45542fd7a39ae","head":{"label":"carllin:LeaderScheduler","ref":"LeaderScheduler","sha":"4061a3b825dd5fbe9430e18b4cd45542fd7a39ae","user":{"login":"carllin","id":3374799,"node_id":"MDQ6VXNlcjMzNzQ3OTk=","avatar_url":"https://avatars1.githubusercontent.com/u/3374799?v=4","gravatar_id":"","url":"https://api.github.com/users/carllin","html_url":"https://github.com/carllin","followers_url":"https://api.github.com/users/carllin/followers","following_url":"https://api.github.com/users/carllin/following{/other_user}","gists_url":"https://api.github.com/users/carllin/gists{/gist_id}","starred_url":"https://api.github.com/users/carllin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/carllin/subscriptions","organizations_url":"https://api.github.com/users/carllin/orgs","repos_url":"https://api.github.com/users/carllin/repos","events_url":"https://api.github.com/users/carllin/events{/privacy}","received_events_url":"https://api.github.com/users/carllin/received_events","type":"User","site_admin":false},"repo":{"id":136771278,"node_id":"MDEwOlJlcG9zaXRvcnkxMzY3NzEyNzg=","name":"solana","full_name":"carllin/solana","private":false,"owner":{"login":"carllin","id":3374799,"node_id":"MDQ6VXNlcjMzNzQ3OTk=","avatar_url":"https://avatars1.githubusercontent.com/u/3374799?v=4","gravatar_id":"","url":"https://api.github.com/users/carllin","html_url":"https://github.com/carllin","followers_url":"https://api.github.com/users/carllin/followers","following_url":"https://api.github.com/users/carllin/following{/other_user}","gists_url":"https://api.github.com/users/carllin/gists{/gist_id}","starred_url":"https://api.github.com/users/carllin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/carllin/subscriptions","organizations_url":"https://api.github.com/users/carllin/orgs","repos_url":"https://api.github.com/users/carllin/repos","events_url":"https://api.github.com/users/carllin/events{/privacy}","received_events_url":"https://api.github.com/users/carllin/received_events","type":"User","site_admin":false},"html_url":"https://github.com/carllin/solana","description":"Blockchain, Rebuilt for Scale","fork":true,"url":"https://api.github.com/repos/carllin/solana","forks_url":"https://api.github.com/repos/carllin/solana/forks","keys_url":"https://api.github.com/repos/carllin/solana/keys{/key_id}","collaborators_url":"https://api.github.com/repos/carllin/solana/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/carllin/solana/teams","hooks_url":"https://api.github.com/repos/carllin/solana/hooks","issue_events_url":"https://api.github.com/repos/carllin/solana/issues/events{/number}","events_url":"https://api.github.com/repos/carllin/solana/events","assignees_url":"https://api.github.com/repos/carllin/solana/assignees{/user}","branches_url":"https://api.github.com/repos/carllin/solana/branches{/branch}","tags_url":"https://api.github.com/repos/carllin/solana/tags","blobs_url":"https://api.github.com/repos/carllin/solana/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/carllin/solana/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/carllin/solana/git/refs{/sha}","trees_url":"https://api.github.com/repos/carllin/solana/git/trees{/sha}","statuses_url":"https://api.github.com/repos/carllin/solana/statuses/{sha}","languages_url":"https://api.github.com/repos/carllin/solana/languages","stargazers_url":"https://api.github.com/repos/carllin/solana/stargazers","contributors_url":"https://api.github.com/repos/carllin/solana/contributors","subscribers_url":"https://api.github.com/repos/carllin/solana/subscribers","subscription_url":"https://api.github.com/repos/carllin/solana/subscription","commits_url":"https://api.github.com/repos/carllin/solana/commits{/sha}","git_commits_url":"https://api.github.com/repos/carllin/solana/git/commits{/sha}","comments_url":"https://api.github.com/repos/carllin/solana/comments{/number}","issue_comment_url":"https://api.github.com/repos/carllin/solana/issues/comments{/number}","contents_url":"https://api.github.com/repos/carllin/solana/contents/{+path}","compare_url":"https://api.github.com/repos/carllin/solana/compare/{base}...{head}","merges_url":"https://api.github.com/repos/carllin/solana/merges","archive_url":"https://api.github.com/repos/carllin/solana/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/carllin/solana/downloads","issues_url":"https://api.github.com/repos/carllin/solana/issues{/number}","pulls_url":"https://api.github.com/repos/carllin/solana/pulls{/number}","milestones_url":"https://api.github.com/repos/carllin/solana/milestones{/number}","notifications_url":"https://api.github.com/repos/carllin/solana/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/carllin/solana/labels{/name}","releases_url":"https://api.github.com/repos/carllin/solana/releases{/id}","deployments_url":"https://api.github.com/repos/carllin/solana/deployments","created_at":"2018-06-10T01:50:19Z","updated_at":"2018-09-19T21:33:30Z","pushed_at":"2018-10-02T10:03:00Z","git_url":"git://github.com/carllin/solana.git","ssh_url":"git@github.com:carllin/solana.git","clone_url":"https://github.com/carllin/solana.git","svn_url":"https://github.com/carllin/solana","homepage":"https://solana.com","size":11771,"stargazers_count":0,"watchers_count":0,"language":"Rust","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":1,"license":{"key":"other","name":"NOASSERTION","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"base":{"label":"solana-labs:master","ref":"master","sha":"b1e941cab9f59c3f97ba700239906c304ff27a58","user":{"login":"solana-labs","id":35608259,"node_id":"MDEyOk9yZ2FuaXphdGlvbjM1NjA4MjU5","avatar_url":"https://avatars3.githubusercontent.com/u/35608259?v=4","gravatar_id":"","url":"https://api.github.com/users/solana-labs","html_url":"https://github.com/solana-labs","followers_url":"https://api.github.com/users/solana-labs/followers","following_url":"https://api.github.com/users/solana-labs/following{/other_user}","gists_url":"https://api.github.com/users/solana-labs/gists{/gist_id}","starred_url":"https://api.github.com/users/solana-labs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/solana-labs/subscriptions","organizations_url":"https://api.github.com/users/solana-labs/orgs","repos_url":"https://api.github.com/users/solana-labs/repos","events_url":"https://api.github.com/users/solana-labs/events{/privacy}","received_events_url":"https://api.github.com/users/solana-labs/received_events","type":"Organization","site_admin":false},"repo":{"id":121470383,"node_id":"MDEwOlJlcG9zaXRvcnkxMjE0NzAzODM=","name":"solana","full_name":"solana-labs/solana","private":false,"owner":{"login":"solana-labs","id":35608259,"node_id":"MDEyOk9yZ2FuaXphdGlvbjM1NjA4MjU5","avatar_url":"https://avatars3.githubusercontent.com/u/35608259?v=4","gravatar_id":"","url":"https://api.github.com/users/solana-labs","html_url":"https://github.com/solana-labs","followers_url":"https://api.github.com/users/solana-labs/followers","following_url":"https://api.github.com/users/solana-labs/following{/other_user}","gists_url":"https://api.github.com/users/solana-labs/gists{/gist_id}","starred_url":"https://api.github.com/users/solana-labs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/solana-labs/subscriptions","organizations_url":"https://api.github.com/users/solana-labs/orgs","repos_url":"https://api.github.com/users/solana-labs/repos","events_url":"https://api.github.com/users/solana-labs/events{/privacy}","received_events_url":"https://api.github.com/users/solana-labs/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/solana-labs/solana","description":"Blockchain, Rebuilt for Scale","fork":false,"url":"https://api.github.com/repos/solana-labs/solana","forks_url":"https://api.github.com/repos/solana-labs/solana/forks","keys_url":"https://api.github.com/repos/solana-labs/solana/keys{/key_id}","collaborators_url":"https://api.github.com/repos/solana-labs/solana/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/solana-labs/solana/teams","hooks_url":"https://api.github.com/repos/solana-labs/solana/hooks","issue_events_url":"https://api.github.com/repos/solana-labs/solana/issues/events{/number}","events_url":"https://api.github.com/repos/solana-labs/solana/events","assignees_url":"https://api.github.com/repos/solana-labs/solana/assignees{/user}","branches_url":"https://api.github.com/repos/solana-labs/solana/branches{/branch}","tags_url":"https://api.github.com/repos/solana-labs/solana/tags","blobs_url":"https://api.github.com/repos/solana-labs/solana/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/solana-labs/solana/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/solana-labs/solana/git/refs{/sha}","trees_url":"https://api.github.com/repos/solana-labs/solana/git/trees{/sha}","statuses_url":"https://api.github.com/repos/solana-labs/solana/statuses/{sha}","languages_url":"https://api.github.com/repos/solana-labs/solana/languages","stargazers_url":"https://api.github.com/repos/solana-labs/solana/stargazers","contributors_url":"https://api.github.com/repos/solana-labs/solana/contributors","subscribers_url":"https://api.github.com/repos/solana-labs/solana/subscribers","subscription_url":"https://api.github.com/repos/solana-labs/solana/subscription","commits_url":"https://api.github.com/repos/solana-labs/solana/commits{/sha}","git_commits_url":"https://api.github.com/repos/solana-labs/solana/git/commits{/sha}","comments_url":"https://api.github.com/repos/solana-labs/solana/comments{/number}","issue_comment_url":"https://api.github.com/repos/solana-labs/solana/issues/comments{/number}","contents_url":"https://api.github.com/repos/solana-labs/solana/contents/{+path}","compare_url":"https://api.github.com/repos/solana-labs/solana/compare/{base}...{head}","merges_url":"https://api.github.com/repos/solana-labs/solana/merges","archive_url":"https://api.github.com/repos/solana-labs/solana/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/solana-labs/solana/downloads","issues_url":"https://api.github.com/repos/solana-labs/solana/issues{/number}","pulls_url":"https://api.github.com/repos/solana-labs/solana/pulls{/number}","milestones_url":"https://api.github.com/repos/solana-labs/solana/milestones{/number}","notifications_url":"https://api.github.com/repos/solana-labs/solana/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/solana-labs/solana/labels{/name}","releases_url":"https://api.github.com/repos/solana-labs/solana/releases{/id}","deployments_url":"https://api.github.com/repos/solana-labs/solana/deployments","created_at":"2018-02-14T04:26:27Z","updated_at":"2018-10-02T15:00:08Z","pushed_at":"2018-10-02T15:00:15Z","git_url":"git://github.com/solana-labs/solana.git","ssh_url":"git@github.com:solana-labs/solana.git","clone_url":"https://github.com/solana-labs/solana.git","svn_url":"https://github.com/solana-labs/solana","homepage":"https://solana.com","size":11500,"stargazers_count":346,"watchers_count":346,"language":"Rust","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":55,"mirror_url":null,"archived":false,"open_issues_count":124,"license":{"key":"other","name":"NOASSERTION","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":55,"open_issues":124,"watchers":346,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/solana-labs/solana/pulls/1398"},"html":{"href":"https://github.com/solana-labs/solana/pull/1398"},"issue":{"href":"https://api.github.com/repos/solana-labs/solana/issues/1398"},"comments":{"href":"https://api.github.com/repos/solana-labs/solana/issues/1398/comments"},"review_comments":{"href":"https://api.github.com/repos/solana-labs/solana/pulls/1398/comments"},"review_comment":{"href":"https://api.github.com/repos/solana-labs/solana/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/solana-labs/solana/pulls/1398/commits"},"statuses":{"href":"https://api.github.com/repos/solana-labs/solana/statuses/4061a3b825dd5fbe9430e18b4cd45542fd7a39ae"}},"author_association":"COLLABORATOR"}}
{"actor":{"display_login":"rob-solana"}}