diff --git a/codechain/run_node.rs b/codechain/run_node.rs
index 1e93f1a9d6..56b13af6ad 100644
--- a/codechain/run_node.rs
+++ b/codechain/run_node.rs
@@ -21,8 +21,8 @@ use std::time::{SystemTime, UNIX_EPOCH};
 
 use ccore::snapshot_notify;
 use ccore::{
-    AccountProvider, AccountProviderError, BlockId, ChainNotify, Client, ClientConfig, ClientService, EngineInfo,
-    EngineType, Miner, MinerService, Scheme, Stratum, StratumConfig, StratumError, NUM_COLUMNS,
+    AccountProvider, AccountProviderError, ChainNotify, Client, ClientConfig, ClientService, EngineInfo, EngineType,
+    Miner, MinerService, Scheme, Stratum, StratumConfig, StratumError, NUM_COLUMNS,
 };
 use cdiscovery::{Config, Discovery};
 use ckey::{Address, NetworkId, PlatformAddress};
@@ -286,7 +286,7 @@ pub fn run_node(matches: &ArgMatches) -> Result<(), String> {
             let network_config = config.network_config()?;
             // XXX: What should we do if the network id has been changed.
             let c = client.client();
-            let network_id = c.common_params(BlockId::Number(0)).unwrap().network_id();
+            let network_id = c.network_id();
             let routing_table = RoutingTable::new();
             let service = network_start(network_id, timer_loop, &network_config, Arc::clone(&routing_table))?;
 
diff --git a/core/src/client/client.rs b/core/src/client/client.rs
index 22c6192684..bd6e8c7140 100644
--- a/core/src/client/client.rs
+++ b/core/src/client/client.rs
@@ -491,6 +491,10 @@ impl StateInfo for Client {
 }
 
 impl EngineInfo for Client {
+    fn network_id(&self) -> NetworkId {
+        self.common_params(BlockId::Earliest).expect("Genesis state should exist").network_id()
+    }
+
     fn common_params(&self, block_id: BlockId) -> Option<CommonParams> {
         self.state_info(block_id.into()).map(|state| {
             state
@@ -528,7 +532,7 @@ impl EngineInfo for Client {
     }
 
     fn possible_authors(&self, block_number: Option<u64>) -> Result<Option<Vec<PlatformAddress>>, EngineError> {
-        let network_id = self.common_params(BlockId::Latest).unwrap().network_id();
+        let network_id = self.network_id();
         if block_number == Some(0) {
             let genesis_author = self.block_header(&0.into()).expect("genesis block").author();
             return Ok(Some(vec![PlatformAddress::new_v1(network_id, genesis_author)]))
@@ -594,8 +598,7 @@ impl BlockChainTrait for Client {
     }
 
     fn genesis_accounts(&self) -> Vec<PlatformAddress> {
-        // XXX: What should we do if the network id has been changed
-        let network_id = self.common_params(BlockId::Latest).unwrap().network_id();
+        let network_id = self.network_id();
         self.genesis_accounts.iter().map(|addr| PlatformAddress::new_v1(network_id, *addr)).collect()
     }
 
@@ -945,10 +948,6 @@ impl MiningBlockChainClient for Client {
     fn register_immune_users(&self, immune_user_vec: Vec<Address>) {
         self.importer.miner.register_immune_users(immune_user_vec)
     }
-
-    fn get_network_id(&self) -> NetworkId {
-        self.common_params(BlockId::Latest).unwrap().network_id()
-    }
 }
 
 impl ChainTimeInfo for Client {
diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs
index 02c71223f4..66e7435f3c 100644
--- a/core/src/client/mod.rs
+++ b/core/src/client/mod.rs
@@ -89,6 +89,7 @@ pub trait BlockChainTrait {
 }
 
 pub trait EngineInfo: Send + Sync {
+    fn network_id(&self) -> NetworkId;
     fn common_params(&self, block_id: BlockId) -> Option<CommonParams>;
     fn metadata_seq(&self, block_id: BlockId) -> Option<u64>;
     fn block_reward(&self, block_number: u64) -> u64;
@@ -300,9 +301,6 @@ pub trait MiningBlockChainClient: BlockChainClient + BlockProducer + FindActionH
 
     /// Append designated users to the immune user list.
     fn register_immune_users(&self, immune_user_vec: Vec<Address>);
-
-    /// Returns network id.
-    fn get_network_id(&self) -> NetworkId;
 }
 
 /// Provides methods to access database.
diff --git a/core/src/client/test_client.rs b/core/src/client/test_client.rs
index b2b0514778..75131faa48 100644
--- a/core/src/client/test_client.rs
+++ b/core/src/client/test_client.rs
@@ -59,7 +59,7 @@ use crate::client::{
     AccountData, BlockChainClient, BlockChainTrait, BlockProducer, BlockStatus, ConsensusClient, EngineInfo,
     ImportBlock, ImportResult, MiningBlockChainClient, StateInfo, StateOrBlock, TermInfo,
 };
-use crate::consensus::stake::{Validator, Validators};
+use crate::consensus::stake::{NextValidators, Validator};
 use crate::consensus::EngineError;
 use crate::db::{COL_STATE, NUM_COLUMNS};
 use crate::encoded;
@@ -106,7 +106,7 @@ pub struct TestBlockChainClient {
     /// Fixed validator keys
     pub validator_keys: RwLock<HashMap<Public, Private>>,
     /// Fixed validators
-    pub validators: Validators,
+    pub validators: NextValidators,
 }
 
 impl Default for TestBlockChainClient {
@@ -160,7 +160,7 @@ impl TestBlockChainClient {
             history: RwLock::new(None),
             term_id: Some(1),
             validator_keys: RwLock::new(HashMap::new()),
-            validators: Validators::from_vector_to_test(vec![]),
+            validators: NextValidators::from_vector_to_test(vec![]),
         };
 
         // insert genesis hash.
@@ -325,14 +325,14 @@ impl TestBlockChainClient {
             self.validator_keys.write().insert(*key_pair.public(), *key_pair.private());
             pubkeys.push(*key_pair.public());
         }
-        let fixed_validators: Validators = Validators::from_vector_to_test(
+        let fixed_validators: NextValidators = NextValidators::from_vector_to_test(
             pubkeys.into_iter().map(|pubkey| Validator::new_for_test(0, 0, pubkey)).collect(),
         );
 
         self.validators = fixed_validators;
     }
 
-    pub fn get_validators(&self) -> &Validators {
+    pub fn get_validators(&self) -> &NextValidators {
         &self.validators
     }
 }
@@ -381,10 +381,6 @@ impl MiningBlockChainClient for TestBlockChainClient {
     fn register_immune_users(&self, immune_user_vec: Vec<Address>) {
         self.miner.register_immune_users(immune_user_vec)
     }
-
-    fn get_network_id(&self) -> NetworkId {
-        NetworkId::default()
-    }
 }
 
 impl AccountData for TestBlockChainClient {
@@ -657,6 +653,10 @@ impl super::EngineClient for TestBlockChainClient {
 }
 
 impl EngineInfo for TestBlockChainClient {
+    fn network_id(&self) -> NetworkId {
+        self.scheme.engine.machine().genesis_common_params().network_id()
+    }
+
     fn common_params(&self, _block_id: BlockId) -> Option<CommonParams> {
         Some(*self.scheme.engine.machine().genesis_common_params())
     }
diff --git a/core/src/consensus/stake/action_data.rs b/core/src/consensus/stake/action_data.rs
index af14c8b33e..6272b0559a 100644
--- a/core/src/consensus/stake/action_data.rs
+++ b/core/src/consensus/stake/action_data.rs
@@ -41,8 +41,10 @@ lazy_static! {
     pub static ref JAIL_KEY: H256 = ActionDataKeyBuilder::new(CUSTOM_ACTION_HANDLER_ID, 1).append(&"Jail").into_key();
     pub static ref BANNED_KEY: H256 =
         ActionDataKeyBuilder::new(CUSTOM_ACTION_HANDLER_ID, 1).append(&"Banned").into_key();
-    pub static ref VALIDATORS_KEY: H256 =
+    pub static ref NEXT_VALIDATORS_KEY: H256 =
         ActionDataKeyBuilder::new(CUSTOM_ACTION_HANDLER_ID, 1).append(&"Validators").into_key();
+    pub static ref CURRENT_VALIDATORS_KEY: H256 =
+        ActionDataKeyBuilder::new(CUSTOM_ACTION_HANDLER_ID, 1).append(&"CurrentValidators").into_key();
 }
 
 pub fn get_delegation_key(address: &Address) -> H256 {
@@ -274,17 +276,17 @@ impl Validator {
 }
 
 #[derive(Debug)]
-pub struct Validators(Vec<Validator>);
-impl Validators {
+pub struct NextValidators(Vec<Validator>);
+impl NextValidators {
     pub fn from_vector_to_test(vec: Vec<Validator>) -> Self {
-        Validators(vec)
+        Self(vec)
     }
 
     pub fn load_from_state(state: &TopLevelState) -> StateResult<Self> {
-        let key = &*VALIDATORS_KEY;
+        let key = &*NEXT_VALIDATORS_KEY;
         let validators = state.action_data(&key)?.map(|data| decode_list(&data)).unwrap_or_default();
 
-        Ok(Validators(validators))
+        Ok(Self(validators))
     }
 
     pub fn elect(state: &TopLevelState) -> StateResult<Self> {
@@ -335,7 +337,7 @@ impl Validators {
 
 
     pub fn save_to_state(&self, state: &mut TopLevelState) -> StateResult<()> {
-        let key = &*VALIDATORS_KEY;
+        let key = &*NEXT_VALIDATORS_KEY;
         if !self.is_empty() {
             state.update_action_data(&key, encode_list(&self.0).to_vec())?;
         } else {
@@ -384,7 +386,7 @@ impl Validators {
     }
 }
 
-impl Deref for Validators {
+impl Deref for NextValidators {
     type Target = Vec<Validator>;
 
     fn deref(&self) -> &Self::Target {
@@ -392,13 +394,13 @@ impl Deref for Validators {
     }
 }
 
-impl From<Validators> for Vec<Validator> {
-    fn from(val: Validators) -> Self {
+impl From<NextValidators> for Vec<Validator> {
+    fn from(val: NextValidators) -> Self {
         val.0
     }
 }
 
-impl IntoIterator for Validators {
+impl IntoIterator for NextValidators {
     type Item = Validator;
     type IntoIter = vec::IntoIter<Self::Item>;
 
@@ -407,6 +409,49 @@ impl IntoIterator for Validators {
     }
 }
 
+#[derive(Debug)]
+pub struct CurrentValidators(Vec<Validator>);
+impl CurrentValidators {
+    pub fn load_from_state(state: &TopLevelState) -> StateResult<Self> {
+        let key = &*CURRENT_VALIDATORS_KEY;
+        let validators = state.action_data(&key)?.map(|data| decode_list(&data)).unwrap_or_default();
+
+        Ok(Self(validators))
+    }
+
+    pub fn save_to_state(&self, state: &mut TopLevelState) -> StateResult<()> {
+        let key = &*CURRENT_VALIDATORS_KEY;
+        if !self.is_empty() {
+            state.update_action_data(&key, encode_list(&self.0).to_vec())?;
+        } else {
+            state.remove_action_data(&key);
+        }
+        Ok(())
+    }
+
+    pub fn update(&mut self, validators: Vec<Validator>) {
+        self.0 = validators;
+    }
+
+    pub fn addresses(&self) -> Vec<Address> {
+        self.0.iter().rev().map(|v| public_to_address(&v.pubkey)).collect()
+    }
+
+    pub fn get_validator(&self, index: usize) -> &Validator {
+        let len = self.0.len();
+        // NOTE: validator list is reversed when reading a validator by index
+        self.0.iter().nth_back(index % len).unwrap()
+    }
+}
+
+impl Deref for CurrentValidators {
+    type Target = Vec<Validator>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
 pub mod v0 {
     use std::mem;
 
@@ -603,7 +648,7 @@ impl Candidates {
 
     pub fn renew_candidates(
         &mut self,
-        validators: &Validators,
+        validators: &NextValidators,
         nomination_ends_at: u64,
         inactive_validators: &[Address],
         banned: &Banned,
@@ -1868,7 +1913,7 @@ mod tests {
         }
         candidates.save_to_state(&mut state).unwrap();
 
-        let dummy_validators = Validators(
+        let dummy_validators = NextValidators(
             pubkeys[0..5]
                 .iter()
                 .map(|pubkey| Validator {
diff --git a/core/src/consensus/stake/mod.rs b/core/src/consensus/stake/mod.rs
index 5134367362..771013c7eb 100644
--- a/core/src/consensus/stake/mod.rs
+++ b/core/src/consensus/stake/mod.rs
@@ -33,7 +33,7 @@ use parking_lot::RwLock;
 use primitives::{Bytes, H256};
 use rlp::{Decodable, Rlp};
 
-pub use self::action_data::{Banned, Validator, Validators};
+pub use self::action_data::{Banned, CurrentValidators, NextValidators, Validator};
 use self::action_data::{Candidates, Delegation, Jail, ReleaseResult, StakeAccount, Stakeholders};
 pub use self::actions::Action;
 pub use self::distribute::fee_distribute;
@@ -317,8 +317,8 @@ pub fn get_stakes(state: &TopLevelState) -> StateResult<HashMap<Address, u64>> {
     Ok(result)
 }
 
-pub fn get_validators(state: &TopLevelState) -> StateResult<Validators> {
-    Validators::load_from_state(state)
+pub fn get_validators(state: &TopLevelState) -> StateResult<NextValidators> {
+    NextValidators::load_from_state(state)
 }
 
 pub mod v0 {
@@ -379,7 +379,7 @@ pub mod v1 {
 }
 
 pub fn update_validator_weights(state: &mut TopLevelState, block_author: &Address) -> StateResult<()> {
-    let mut validators = Validators::load_from_state(state)?;
+    let mut validators = NextValidators::load_from_state(state)?;
     validators.update_weight(block_author);
     validators.save_to_state(state)
 }
@@ -451,7 +451,7 @@ pub fn on_term_close(
 
     jail(state, inactive_validators, custody_until, kick_at)?;
 
-    let validators = Validators::elect(state)?;
+    let validators = NextValidators::elect(state)?;
     validators.save_to_state(state)?;
 
     state.increase_term_id(last_term_finished_block_num)?;
@@ -469,7 +469,7 @@ fn update_candidates(
     let mut candidates = Candidates::load_from_state(state)?;
     let nomination_ends_at = current_term + nomination_expiration;
 
-    let current_validators = Validators::load_from_state(state)?;
+    let current_validators = NextValidators::load_from_state(state)?;
     candidates.renew_candidates(&current_validators, nomination_ends_at, &inactive_validators, &banned);
 
     let expired = candidates.drain_expired_candidates(current_term);
@@ -519,7 +519,7 @@ pub fn ban(state: &mut TopLevelState, informant: &Public, criminal: Address) ->
 
     let mut candidates = Candidates::load_from_state(state)?;
     let mut jailed = Jail::load_from_state(state)?;
-    let mut validators = Validators::load_from_state(state)?;
+    let mut validators = NextValidators::load_from_state(state)?;
 
     let deposit = match (candidates.remove(&criminal), jailed.remove(&criminal)) {
         (Some(_), Some(_)) => unreachable!("A candidate that are jailed cannot exist"),
diff --git a/core/src/consensus/tendermint/engine.rs b/core/src/consensus/tendermint/engine.rs
index 4404cdaef2..69bc34b565 100644
--- a/core/src/consensus/tendermint/engine.rs
+++ b/core/src/consensus/tendermint/engine.rs
@@ -142,6 +142,17 @@ impl ConsensusEngine for Tendermint {
         let block_number = block.header().number();
         let metadata = block.state().metadata()?.expect("Metadata must exist");
         let era = metadata.term_params().map_or(0, |p| p.era());
+
+        match era {
+            0 => {}
+            1 => {
+                let mut validators = stake::CurrentValidators::load_from_state(block.state())?;
+                validators.update(stake::NextValidators::load_from_state(block.state())?.clone());
+                validators.save_to_state(block.state_mut())?;
+            }
+            _ => unimplemented!(),
+        }
+
         if block_number == metadata.last_term_finished_block_num() + 1 {
             match era {
                 0 => {}
@@ -274,7 +285,7 @@ impl ConsensusEngine for Tendermint {
 
                 stake::v0::move_current_to_previous_intermediate_rewards(block.state_mut())?;
 
-                let validators = stake::Validators::load_from_state(block.state())?
+                let validators = stake::NextValidators::load_from_state(block.state())?
                     .into_iter()
                     .map(|val| public_to_address(val.pubkey()))
                     .collect();
@@ -286,7 +297,7 @@ impl ConsensusEngine for Tendermint {
                 }
 
                 let start_of_the_current_term = metadata.last_term_finished_block_num() + 1;
-                let validators = stake::Validators::load_from_state(block.state())?
+                let validators = stake::NextValidators::load_from_state(block.state())?
                     .into_iter()
                     .map(|val| public_to_address(val.pubkey()))
                     .collect();
@@ -448,10 +459,30 @@ fn calculate_pending_rewards_of_the_previous_term(
     let mut missed_signatures = HashMap::<Address, (usize, usize)>::with_capacity(MAX_NUM_OF_VALIDATORS);
     let mut signed_blocks = HashMap::<Address, usize>::with_capacity(MAX_NUM_OF_VALIDATORS);
 
+    let era = {
+        let end_of_the_current_term_header = chain
+            .block_header(&start_of_the_current_term_header.parent_hash().into())
+            .expect("The parent of the term end block must exist");
+        let state = chain
+            .state_at(end_of_the_current_term_header.parent_hash().into())
+            .expect("The state at parent of the term end block must exist");
+        let metadata = state.metadata()?.expect("Metadata of the term end block should exist");
+        metadata.term_params().map_or(0, |p| p.era())
+    };
+
     let mut header = start_of_the_current_term_header;
     let mut parent_validators = {
-        let grand_parent_header = chain.block_header(&header.parent_hash().into()).unwrap();
-        validators.addresses(&grand_parent_header.parent_hash())
+        match era {
+            0 => {
+                let grand_parent_header = chain.block_header(&header.parent_hash().into()).unwrap();
+                validators.addresses(&grand_parent_header.parent_hash())
+            }
+            1 => {
+                let state = chain.state_at(header.parent_hash().into()).expect("The block's state must exist");
+                stake::CurrentValidators::load_from_state(&state)?.addresses()
+            }
+            _ => unimplemented!(),
+        }
     };
     while start_of_the_previous_term != header.number() {
         for index in TendermintSealView::new(&header.seal()).bitset()?.true_index_iter() {
@@ -461,10 +492,17 @@ fn calculate_pending_rewards_of_the_previous_term(
 
         header = chain.block_header(&header.parent_hash().into()).unwrap();
         parent_validators = {
-            // The seal of the current block has the signatures of the parent block.
-            // It needs the hash of the grand parent block to find the validators of the parent block.
-            let grand_parent_header = chain.block_header(&header.parent_hash().into()).unwrap();
-            validators.addresses(&grand_parent_header.parent_hash())
+            match era {
+                0 => {
+                    let grand_parent_header = chain.block_header(&header.parent_hash().into()).unwrap();
+                    validators.addresses(&grand_parent_header.parent_hash())
+                }
+                1 => {
+                    let state = chain.state_at(header.hash().into()).expect("The block's state must exist");
+                    stake::CurrentValidators::load_from_state(&state)?.addresses()
+                }
+                _ => unimplemented!(),
+            }
         };
 
         let author = header.author();
diff --git a/core/src/consensus/tendermint/worker.rs b/core/src/consensus/tendermint/worker.rs
index f9abe27922..7cb4aabcb8 100644
--- a/core/src/consensus/tendermint/worker.rs
+++ b/core/src/consensus/tendermint/worker.rs
@@ -35,7 +35,7 @@ use super::backup::{backup, restore, BackupView};
 use super::message::*;
 use super::network;
 use super::params::TimeGapParams;
-use super::stake::CUSTOM_ACTION_HANDLER_ID;
+use super::stake::{CurrentValidators, CUSTOM_ACTION_HANDLER_ID};
 use super::types::{Height, Proposal, Step, TendermintSealView, TendermintState, TwoThirdsMajority, View};
 use super::vote_collector::{DoubleVote, VoteCollector};
 use super::vote_regression_checker::VoteRegressionChecker;
@@ -1244,13 +1244,19 @@ impl Worker {
         };
 
         let mut voted_validators = BitSet::new();
-        let grand_parent_hash = self
-            .client()
-            .block_header(&(*header.parent_hash()).into())
-            .expect("The parent block must exist")
-            .parent_hash();
+        let parent = self.client().block_header(&(*header.parent_hash()).into()).expect("The parent block must exist");
+        let grand_parent_hash = parent.parent_hash();
         for (bitset_index, signature) in seal_view.signatures()? {
-            let public = self.validators.get(&grand_parent_hash, bitset_index);
+            let public = {
+                let state = self.client().state_at(parent.hash().into()).expect("The parent state must exist");
+                let validators = CurrentValidators::load_from_state(&state)?;
+                // This happens when era == 0
+                if validators.is_empty() {
+                    self.validators.get(&grand_parent_hash, bitset_index)
+                } else {
+                    *validators.get_validator(bitset_index).pubkey()
+                }
+            };
             if !verify_schnorr(&public, &signature, &precommit_vote_on.hash())? {
                 let address = public_to_address(&public);
                 return Err(EngineError::BlockNotAuthorized(address.to_owned()).into())
@@ -1263,7 +1269,7 @@ impl Worker {
         if header.number() == 1 {
             return Ok(())
         }
-        self.validators.check_enough_votes(&grand_parent_hash, &voted_validators)?;
+        self.validators.check_enough_votes_with_header(&parent.decode(), &voted_validators)?;
         Ok(())
     }
 
@@ -1471,7 +1477,7 @@ impl Worker {
     }
 
     fn report_double_vote(&self, double: &DoubleVote) {
-        let network_id = self.client().common_params(BlockId::Latest).unwrap().network_id();
+        let network_id = self.client().network_id();
         let seq = match self.signer.address() {
             Some(address) => self.client().latest_seq(address),
             None => {
diff --git a/core/src/consensus/validator_set/dynamic_validator.rs b/core/src/consensus/validator_set/dynamic_validator.rs
index 829e9cd5df..7091333e06 100644
--- a/core/src/consensus/validator_set/dynamic_validator.rs
+++ b/core/src/consensus/validator_set/dynamic_validator.rs
@@ -18,13 +18,13 @@ use std::sync::{Arc, Weak};
 
 use ckey::{public_to_address, Address, Public};
 use ctypes::util::unexpected::OutOfBounds;
-use ctypes::BlockHash;
+use ctypes::{BlockHash, Header};
 use parking_lot::RwLock;
 
 use super::{RoundRobinValidator, ValidatorSet};
 use crate::client::ConsensusClient;
 use crate::consensus::bit_set::BitSet;
-use crate::consensus::stake::{get_validators, Validator};
+use crate::consensus::stake::{get_validators, CurrentValidators, Validator};
 use crate::consensus::EngineError;
 
 /// Validator set containing a known set of public keys.
@@ -77,6 +77,58 @@ impl DynamicValidator {
             (prev_proposer_index + proposed_view + 1) % num_validators
         }
     }
+
+    pub fn check_enough_votes_with_validators(
+        &self,
+        validators: &[Validator],
+        votes: &BitSet,
+    ) -> Result<(), EngineError> {
+        let mut voted_delegation = 0u64;
+        let n_validators = validators.len();
+        for index in votes.true_index_iter() {
+            assert!(index < n_validators);
+            let validator = validators.get(index).ok_or_else(|| {
+                EngineError::ValidatorNotExist {
+                    height: 0, // FIXME
+                    index,
+                }
+            })?;
+            voted_delegation += validator.delegation();
+        }
+        let total_delegation: u64 = validators.iter().map(Validator::delegation).sum();
+        if voted_delegation * 3 > total_delegation * 2 {
+            Ok(())
+        } else {
+            let threshold = total_delegation as usize * 2 / 3;
+            Err(EngineError::BadSealFieldSize(OutOfBounds {
+                min: Some(threshold),
+                max: Some(total_delegation as usize),
+                found: voted_delegation as usize,
+            }))
+        }
+    }
+
+    pub fn check_enough_votes_with_header(&self, header: &Header, votes: &BitSet) -> Result<(), EngineError> {
+        let client: Arc<dyn ConsensusClient> =
+            self.client.read().as_ref().and_then(Weak::upgrade).expect("Client is not initialized");
+
+        let validators = {
+            match client.state_at(header.hash().into()).map(|s| CurrentValidators::load_from_state(&s)) {
+                Some(Ok(current_validators)) if !current_validators.is_empty() => {
+                    let mut result = (*current_validators).clone();
+                    result.reverse();
+                    Some(result)
+                }
+                _ => self.validators(*header.parent_hash()),
+            }
+        };
+
+        if let Some(validators) = validators {
+            self.check_enough_votes_with_validators(&validators, votes)
+        } else {
+            self.initial_list.check_enough_votes(header.parent_hash(), votes)
+        }
+    }
 }
 
 impl ValidatorSet for DynamicValidator {
@@ -145,29 +197,7 @@ impl ValidatorSet for DynamicValidator {
 
     fn check_enough_votes(&self, parent: &BlockHash, votes: &BitSet) -> Result<(), EngineError> {
         if let Some(validators) = self.validators(*parent) {
-            let mut voted_delegation = 0u64;
-            let n_validators = validators.len();
-            for index in votes.true_index_iter() {
-                assert!(index < n_validators);
-                let validator = validators.get(index).ok_or_else(|| {
-                    EngineError::ValidatorNotExist {
-                        height: 0, // FIXME
-                        index,
-                    }
-                })?;
-                voted_delegation += validator.delegation();
-            }
-            let total_delegation: u64 = validators.iter().map(Validator::delegation).sum();
-            if voted_delegation * 3 > total_delegation * 2 {
-                Ok(())
-            } else {
-                let threshold = total_delegation as usize * 2 / 3;
-                Err(EngineError::BadSealFieldSize(OutOfBounds {
-                    min: Some(threshold),
-                    max: Some(total_delegation as usize),
-                    found: voted_delegation as usize,
-                }))
-            }
+            self.check_enough_votes_with_validators(&validators, votes)
         } else {
             self.initial_list.check_enough_votes(parent, votes)
         }
diff --git a/rpc/src/v1/impls/account.rs b/rpc/src/v1/impls/account.rs
index a4c16207e9..fa10fbc063 100644
--- a/rpc/src/v1/impls/account.rs
+++ b/rpc/src/v1/impls/account.rs
@@ -18,8 +18,8 @@ use std::convert::TryInto;
 use std::sync::Arc;
 use std::time::Duration;
 
-use ccore::{AccountData, AccountProvider, BlockId, EngineInfo, MinerService, MiningBlockChainClient, TermInfo};
-use ckey::{NetworkId, Password, PlatformAddress, Signature};
+use ccore::{AccountData, AccountProvider, EngineInfo, MinerService, MiningBlockChainClient, TermInfo};
+use ckey::{Password, PlatformAddress, Signature};
 use ctypes::transaction::IncompleteTransaction;
 use jsonrpc_core::Result;
 use parking_lot::Mutex;
@@ -46,11 +46,6 @@ where
             miner,
         }
     }
-
-    fn network_id(&self) -> NetworkId {
-        // XXX: What should we do if the network id has been changed
-        self.client.common_params(BlockId::Latest).unwrap().network_id()
-    }
 }
 
 impl<C, M> Account for AccountClient<C, M>
@@ -62,7 +57,10 @@ where
         self.account_provider
             .get_list()
             .map(|addresses| {
-                addresses.into_iter().map(|address| PlatformAddress::new_v1(self.network_id(), address)).collect()
+                addresses
+                    .into_iter()
+                    .map(|address| PlatformAddress::new_v1(self.client.network_id(), address))
+                    .collect()
             })
             .map_err(account_provider)
     }
@@ -70,13 +68,13 @@ where
     fn create_account(&self, passphrase: Option<Password>) -> Result<PlatformAddress> {
         let (address, _) =
             self.account_provider.new_account_and_public(&passphrase.unwrap_or_default()).map_err(account_provider)?;
-        Ok(PlatformAddress::new_v1(self.network_id(), address))
+        Ok(PlatformAddress::new_v1(self.client.network_id(), address))
     }
 
     fn create_account_from_secret(&self, secret: H256, passphrase: Option<Password>) -> Result<PlatformAddress> {
         self.account_provider
             .insert_account(secret.into(), &passphrase.unwrap_or_default())
-            .map(|address| PlatformAddress::new_v1(self.network_id(), address))
+            .map(|address| PlatformAddress::new_v1(self.client.network_id(), address))
             .map_err(account_provider)
     }
 
diff --git a/rpc/src/v1/impls/chain.rs b/rpc/src/v1/impls/chain.rs
index 576e571d00..d9a3797f0c 100644
--- a/rpc/src/v1/impls/chain.rs
+++ b/rpc/src/v1/impls/chain.rs
@@ -128,10 +128,11 @@ where
             return Ok(None)
         }
         let block_id = block_number.map(BlockId::from).unwrap_or(BlockId::Latest);
-        Ok(self.client.get_text(transaction_hash, block_id).map_err(errors::transaction_state)?.map(|text| {
-            let parent_block_id = block_number.map(|n| (n - 1).into()).unwrap_or(BlockId::ParentOfLatest);
-            Text::from_core(text, self.client.common_params(parent_block_id).unwrap().network_id())
-        }))
+        Ok(self
+            .client
+            .get_text(transaction_hash, block_id)
+            .map_err(errors::transaction_state)?
+            .map(|text| Text::from_core(text, self.client.network_id())))
     }
 
     fn get_asset(
@@ -178,8 +179,7 @@ where
     fn get_regular_key_owner(&self, public: Public, block_number: Option<u64>) -> Result<Option<PlatformAddress>> {
         let block_id = block_number.map(BlockId::Number).unwrap_or(BlockId::Latest);
         Ok(self.client.regular_key_owner(&public_to_address(&public), block_id.into()).and_then(|address| {
-            let parent_block_id = block_number.map(|n| (n - 1).into()).unwrap_or(BlockId::ParentOfLatest);
-            let network_id = self.client.common_params(parent_block_id).unwrap().network_id();
+            let network_id = self.client.network_id();
             Some(PlatformAddress::new_v1(network_id, address))
         }))
     }
@@ -206,8 +206,7 @@ where
     fn get_shard_owners(&self, shard_id: ShardId, block_number: Option<u64>) -> Result<Option<Vec<PlatformAddress>>> {
         let block_id = block_number.map(BlockId::Number).unwrap_or(BlockId::Latest);
         Ok(self.client.shard_owners(shard_id, block_id.into()).map(|owners| {
-            let parent_block_id = block_number.map(|n| (n - 1).into()).unwrap_or(BlockId::ParentOfLatest);
-            let network_id = self.client.common_params(parent_block_id).unwrap().network_id();
+            let network_id = self.client.network_id();
             owners.into_iter().map(|owner| PlatformAddress::new_v1(network_id, owner)).collect()
         }))
     }
@@ -215,8 +214,7 @@ where
     fn get_shard_users(&self, shard_id: ShardId, block_number: Option<u64>) -> Result<Option<Vec<PlatformAddress>>> {
         let block_id = block_number.map(BlockId::Number).unwrap_or(BlockId::Latest);
         Ok(self.client.shard_users(shard_id, block_id.into()).map(|users| {
-            let parent_block_id = block_number.map(|n| (n - 1).into()).unwrap_or(BlockId::ParentOfLatest);
-            let network_id = self.client.common_params(parent_block_id).unwrap().network_id();
+            let network_id = self.client.network_id();
             users.into_iter().map(|user| PlatformAddress::new_v1(network_id, user)).collect()
         }))
     }
@@ -239,26 +237,14 @@ where
 
     fn get_block_by_number(&self, block_number: u64) -> Result<Option<Block>> {
         let id = BlockId::Number(block_number);
-        Ok(self.client.block(&id).map(|block| {
-            let block_id_to_read_params = if block_number == 0 {
-                0.into()
-            } else {
-                (block_number - 1).into()
-            };
-            Block::from_core(block.decode(), self.client.common_params(block_id_to_read_params).unwrap().network_id())
-        }))
+        Ok(self.client.block(&id).map(|block| Block::from_core(block.decode(), self.client.network_id())))
     }
 
     fn get_block_by_hash(&self, block_hash: BlockHash) -> Result<Option<Block>> {
         let id = BlockId::Hash(block_hash);
         Ok(self.client.block(&id).map(|block| {
             let block = block.decode();
-            let block_id_to_read_params = if block.header.number() == 0 {
-                0.into()
-            } else {
-                (*block.header.parent_hash()).into()
-            };
-            Block::from_core(block, self.client.common_params(block_id_to_read_params).unwrap().network_id())
+            Block::from_core(block, self.client.network_id())
         }))
     }
 
@@ -301,7 +287,7 @@ where
     }
 
     fn get_network_id(&self) -> Result<NetworkId> {
-        Ok(self.client.common_params(BlockId::Latest).unwrap().network_id())
+        Ok(self.client.network_id())
     }
 
     fn get_common_params(&self, block_number: Option<u64>) -> Result<Option<Params>> {
diff --git a/rpc/src/v1/impls/engine.rs b/rpc/src/v1/impls/engine.rs
index f106f29346..730ec09073 100644
--- a/rpc/src/v1/impls/engine.rs
+++ b/rpc/src/v1/impls/engine.rs
@@ -62,7 +62,7 @@ where
             Ok(None)
         } else {
             // XXX: What should we do if the network id has been changed
-            let network_id = self.client.common_params(BlockId::Latest).unwrap().network_id();
+            let network_id = self.client.network_id();
             Ok(Some(PlatformAddress::new_v1(network_id, author)))
         }
     }
diff --git a/rpc/src/v1/impls/mempool.rs b/rpc/src/v1/impls/mempool.rs
index ed021f1c8d..ab78c740e8 100644
--- a/rpc/src/v1/impls/mempool.rs
+++ b/rpc/src/v1/impls/mempool.rs
@@ -16,7 +16,7 @@
 
 use std::sync::Arc;
 
-use ccore::{BlockChainClient, MiningBlockChainClient, SignedTransaction};
+use ccore::{BlockChainClient, EngineInfo, MiningBlockChainClient, SignedTransaction};
 use cjson::bytes::Bytes;
 use ckey::{Address, PlatformAddress};
 use ctypes::{Tracker, TxHash};
@@ -42,7 +42,7 @@ impl<C> MempoolClient<C> {
 
 impl<C> Mempool for MempoolClient<C>
 where
-    C: BlockChainClient + MiningBlockChainClient + 'static,
+    C: BlockChainClient + MiningBlockChainClient + EngineInfo + 'static,
 {
     fn send_signed_transaction(&self, raw: Bytes) -> Result<TxHash> {
         Rlp::new(&raw.into_vec())
@@ -82,7 +82,7 @@ where
 
     fn get_banned_accounts(&self) -> Result<Vec<PlatformAddress>> {
         let malicious_user_vec = self.client.get_malicious_users();
-        let network_id = self.client.get_network_id();
+        let network_id = self.client.network_id();
         Ok(malicious_user_vec.into_iter().map(|address| PlatformAddress::new_v1(network_id, address)).collect())
     }
 
@@ -102,7 +102,7 @@ where
 
     fn get_immune_accounts(&self) -> Result<Vec<PlatformAddress>> {
         let immune_user_vec = self.client.get_immune_users();
-        let network_id = self.client.get_network_id();
+        let network_id = self.client.network_id();
         Ok(immune_user_vec.into_iter().map(|address| PlatformAddress::new_v1(network_id, address)).collect())
     }
 
diff --git a/test/src/e2e.dynval/2/snapshot.test.ts b/test/src/e2e.dynval/2/snapshot.test.ts
index bb548d4aef..746b878147 100644
--- a/test/src/e2e.dynval/2/snapshot.test.ts
+++ b/test/src/e2e.dynval/2/snapshot.test.ts
@@ -17,6 +17,7 @@
 import * as chai from "chai";
 import { expect } from "chai";
 import * as chaiAsPromised from "chai-as-promised";
+import { SDK } from "codechain-sdk";
 import * as stake from "codechain-stakeholder-sdk";
 import * as fs from "fs";
 import "mocha";
@@ -24,8 +25,9 @@ import * as path from "path";
 
 import mkdirp = require("mkdirp");
 import { validators } from "../../../tendermint.dynval/constants";
+import { faucetAddress, faucetSecret } from "../../helper/constants";
 import { PromiseExpect } from "../../helper/promise";
-import CodeChain from "../../helper/spawn";
+import CodeChain, { Signer } from "../../helper/spawn";
 import { setTermTestTimeout, withNodes } from "../setup";
 
 chai.use(chaiAsPromised);
@@ -36,6 +38,7 @@ const SNAPSHOT_PATH = `${__dirname}/../../../../snapshot/`;
 describe("Snapshot for Tendermint with Dynamic Validator", function() {
     const promiseExpect = new PromiseExpect();
     const snapshotValidators = validators.slice(0, 3);
+    const freshNodeValidator = validators[3];
     const { nodes } = withNodes(this, {
         promiseExpect,
         overrideParams: {
@@ -82,9 +85,77 @@ describe("Snapshot for Tendermint with Dynamic Validator", function() {
         ).to.satisfy(fs.existsSync);
     });
 
+    it("should be able to boot with the snapshot", async function() {
+        const termWaiter = setTermTestTimeout(this, {
+            terms: 3
+        });
+        const termMetadata1 = await termWaiter.waitNodeUntilTerm(nodes[0], {
+            target: 2,
+            termPeriods: 1
+        });
+        const snapshotBlock = await getSnapshotBlock(nodes[0], termMetadata1);
+        await makeItValidator(nodes[0], freshNodeValidator);
+        const snapshotPath = fs.mkdtempSync(SNAPSHOT_PATH);
+        const node = new CodeChain({
+            chain: `${__dirname}/../../scheme/tendermint-dynval.json`,
+            argv: [
+                "--engine-signer",
+                freshNodeValidator.platformAddress.toString(),
+                "--password-path",
+                `test/tendermint.dynval/${freshNodeValidator.platformAddress.value}/password.json`,
+                "--force-sealing",
+                "--snapshot-path",
+                snapshotPath,
+                "--config",
+                SNAPSHOT_CONFIG,
+                "--snapshot-hash",
+                snapshotBlock.hash.toString(),
+                "--snapshot-number",
+                snapshotBlock.number.toString()
+            ],
+            additionalKeysPath: `tendermint.dynval/${freshNodeValidator.platformAddress.value}/keys`
+        });
+        try {
+            await node.start();
+            await node.connect(nodes[0]);
+            await termWaiter.waitNodeUntilTerm(node, {
+                target: 4,
+                termPeriods: 2
+            });
+
+            await freshValidatorCheck(nodes[0].sdk);
+
+            expect(await node.sdk.rpc.chain.getBlock(snapshotBlock.number - 1))
+                .to.be.null;
+            expect(await node.sdk.rpc.chain.getBlock(snapshotBlock.number)).not
+                .to.be.null;
+            // Check that the freshNodeValidator is still a validator & make sure it doesn't have a block/header before termMetadata1.
+        } catch (e) {
+            node.keepLogs();
+            throw e;
+        } finally {
+            await node.clean();
+        }
+    });
+
     afterEach(async function() {
         promiseExpect.checkFulfilled();
     });
+
+    async function freshValidatorCheck(sdk: SDK) {
+        const blockNumber = await sdk.rpc.chain.getBestBlockNumber();
+        const termMedata = await stake.getTermMetadata(sdk, blockNumber);
+        const currentTermInitialBlockNumber =
+            termMedata!.lastTermFinishedBlockNumber + 1;
+        const validatorsAfter = (await stake.getPossibleAuthors(
+            sdk,
+            currentTermInitialBlockNumber
+        ))!.map(platformAddr => platformAddr.toString());
+
+        expect(validatorsAfter).and.contains(
+            freshNodeValidator.platformAddress.toString()
+        );
+    }
 });
 
 async function getSnapshotBlock(
@@ -95,3 +166,44 @@ async function getSnapshotBlock(
     await node.waitBlockNumber(blockNumber);
     return (await node.sdk.rpc.chain.getBlock(blockNumber))!;
 }
+
+async function makeItValidator(node: CodeChain, freshNodeValidator: Signer) {
+    const faucetSeq = await node.sdk.rpc.chain.getSeq(faucetAddress);
+    const payTx = node.sdk.core
+        .createPayTransaction({
+            recipient: freshNodeValidator.platformAddress,
+            quantity: 200000000
+        })
+        .sign({
+            secret: faucetSecret,
+            seq: faucetSeq,
+            fee: 10
+        });
+    await node.waitForTx(await node.sdk.rpc.chain.sendSignedTransaction(payTx));
+    const selfNominateTx = stake
+        .createSelfNominateTransaction(node.sdk, 10000000, "")
+        .sign({
+            secret: freshNodeValidator.privateKey,
+            seq: await node.sdk.rpc.chain.getSeq(
+                freshNodeValidator.platformAddress
+            ),
+            fee: 10
+        });
+    await node.waitForTx(
+        await node.sdk.rpc.chain.sendSignedTransaction(selfNominateTx)
+    );
+    const delegateTx = stake
+        .createDelegateCCSTransaction(
+            node.sdk,
+            freshNodeValidator.platformAddress,
+            10000
+        )
+        .sign({
+            secret: faucetSecret,
+            seq: faucetSeq + 1,
+            fee: 10
+        });
+    await node.waitForTx(
+        await node.sdk.rpc.chain.sendSignedTransaction(delegateTx)
+    );
+}
diff --git a/test/src/e2e/snapshot.test.ts b/test/src/e2e/snapshot.test.ts
index d0b81f614d..195cf00c8a 100644
--- a/test/src/e2e/snapshot.test.ts
+++ b/test/src/e2e/snapshot.test.ts
@@ -54,6 +54,60 @@ describe("Snapshot", async function() {
         ).to.satisfies(fs.existsSync);
     });
 
+    it("can restore from snapshot", async function() {
+        for (let i = 0; i < 10; i++) {
+            const tx = await node.sendPayTx({
+                quantity: 100,
+                recipient: aliceAddress
+            });
+            await node.waitForTx(tx.hash());
+        }
+
+        const pay = await node.sendPayTx({
+            quantity: 100,
+            recipient: aliceAddress
+        });
+
+        const blockHash = (await node.sdk.rpc.chain.getTransaction(pay.hash()))!
+            .blockHash!;
+
+        const block = (await node.sdk.rpc.chain.getBlock(blockHash))!;
+        await node.sdk.rpc.sendRpcRequest("devel_snapshot", [
+            blockHash.toJSON()
+        ]);
+        // Wait for 1 secs
+        await new Promise(resolve => setTimeout(resolve, 1000));
+
+        const newNode = new CodeChain({
+            argv: [
+                "--snapshot-hash",
+                block.hash.toString(),
+                "--snapshot-number",
+                block.number.toString()
+            ]
+        });
+
+        try {
+            await newNode.start();
+            await newNode.connect(node);
+            await newNode.waitBlockNumber(block.number);
+            await node.sdk.rpc.devel.stopSealing();
+            // New node creates block
+            const newPay = await newNode.sendPayTx({
+                quantity: 100,
+                recipient: aliceAddress
+            });
+            await newNode.waitForTx(newPay.hash());
+            await node.sdk.rpc.devel.startSealing();
+            await node.waitForTx(newPay.hash());
+        } catch (e) {
+            newNode.keepLogs();
+            throw e;
+        } finally {
+            await newNode.clean();
+        }
+    });
+
     afterEach(function() {
         if (this.currentTest!.state === "failed") {
             node.keepLogs();