diff --git a/core/src/client/client.rs b/core/src/client/client.rs index af82080363..cfd0e8a7ac 100644 --- a/core/src/client/client.rs +++ b/core/src/client/client.rs @@ -481,6 +481,10 @@ impl StateInfo for Client { } impl EngineInfo for Client { + fn network_id(&self) -> NetworkId { + self.common_params(BlockId::Earliest).expect("Genesis state must exist").network_id() + } + fn common_params(&self, block_id: BlockId) -> Option { self.state_info(block_id.into()).map(|state| { state @@ -518,7 +522,7 @@ impl EngineInfo for Client { } fn possible_authors(&self, block_number: Option) -> Result>, 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)])) @@ -572,8 +576,7 @@ impl BlockChainTrait for Client { } fn genesis_accounts(&self) -> Vec { - // 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() } @@ -896,10 +899,6 @@ impl MiningBlockChainClient for Client { fn register_immune_users(&self, immune_user_vec: Vec
) { 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 681edb2680..f38dbdf58c 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -88,6 +88,7 @@ pub trait BlockChainTrait { } pub trait EngineInfo: Send + Sync { + fn network_id(&self) -> NetworkId; fn common_params(&self, block_id: BlockId) -> Option; fn metadata_seq(&self, block_id: BlockId) -> Option; fn block_reward(&self, block_number: u64) -> u64; @@ -284,9 +285,6 @@ pub trait MiningBlockChainClient: BlockChainClient + BlockProducer + FindActionH /// Append designated users to the immune user list. fn register_immune_users(&self, immune_user_vec: Vec
); - - /// 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 723a294c3d..ac6a5259ee 100644 --- a/core/src/client/test_client.rs +++ b/core/src/client/test_client.rs @@ -380,10 +380,6 @@ impl MiningBlockChainClient for TestBlockChainClient { fn register_immune_users(&self, immune_user_vec: Vec
) { self.miner.register_immune_users(immune_user_vec) } - - fn get_network_id(&self) -> NetworkId { - NetworkId::default() - } } impl AccountData for TestBlockChainClient { @@ -636,6 +632,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 { 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 7c59dc9135..7fbbcf414d 100644 --- a/core/src/consensus/stake/action_data.rs +++ b/core/src/consensus/stake/action_data.rs @@ -433,6 +433,10 @@ impl CurrentValidators { pub fn update(&mut self, validators: Vec) { self.0 = validators; } + + pub fn addresses(&self) -> Vec
{ + self.0.iter().rev().map(|v| public_to_address(&v.pubkey)).collect() + } } impl Deref for CurrentValidators { @@ -449,6 +453,45 @@ impl From for Vec { } } +#[derive(Debug)] +pub struct PreviousValidators(Vec); +impl PreviousValidators { + pub fn load_from_state(state: &TopLevelState) -> StateResult { + 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) { + self.0 = validators; + } +} + +impl Deref for PreviousValidators { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for Vec { + fn from(val: PreviousValidators) -> Self { + val.0 + } +} + #[derive(Default, Debug, PartialEq)] pub struct IntermediateRewards { current: BTreeMap, diff --git a/core/src/consensus/stake/mod.rs b/core/src/consensus/stake/mod.rs index 513f8a693c..3a9bcef653 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, CurrentValidators, NextValidators, Validator}; +pub use self::action_data::{Banned, CurrentValidators, NextValidators, PreviousValidators, Validator}; use self::action_data::{Candidates, Delegation, IntermediateRewards, Jail, ReleaseResult, StakeAccount, Stakeholders}; pub use self::actions::Action; pub use self::distribute::fee_distribute; diff --git a/core/src/consensus/tendermint/engine.rs b/core/src/consensus/tendermint/engine.rs index 61b0eaafaf..318ab153a4 100644 --- a/core/src/consensus/tendermint/engine.rs +++ b/core/src/consensus/tendermint/engine.rs @@ -153,15 +153,19 @@ impl ConsensusEngine for Tendermint { match term { 0 => {} _ => { - 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())?; + let mut previous_validators = stake::PreviousValidators::load_from_state(block.state())?; + previous_validators.update(stake::CurrentValidators::load_from_state(block.state())?.clone()); + previous_validators.save_to_state(block.state_mut())?; + + let mut current_validators = stake::CurrentValidators::load_from_state(block.state())?; + current_validators.update(stake::NextValidators::load_from_state(block.state())?.clone()); + current_validators.save_to_state(block.state_mut())?; } } if block_number == metadata.last_term_finished_block_num() + 1 { match term { - 0 => {} + 0 | 1 => {} _ => { let rewards = stake::drain_current_rewards(block.state_mut())?; let banned = stake::Banned::load_from_state(block.state())?; @@ -396,20 +400,21 @@ fn aggregate_work_info( end_of_the_last_term + 1 }; let mut header = start_of_the_next_term_header; - let mut parent_validators = validators.current_addresses(&header.parent_hash()); while start_of_the_current_term != header.number() { + let parent = chain.block_header(&header.parent_hash().into()).unwrap(); + let current_validators = validators.current_addresses(&parent.hash()); + let previous_validators = validators.previous_addresses(&parent.hash()); for index in TendermintSealView::new(&header.seal()).bitset()?.true_index_iter() { - let signer = *parent_validators.get(index).expect("The seal must be the signature of the validator"); + let signer = *current_validators.get(index).expect("The seal must be the signature of the validator"); work_info.entry(signer).or_default().signed += 1; } - header = chain.block_header(&header.parent_hash().into()).unwrap(); - parent_validators = validators.current_addresses(&header.parent_hash()); - - let author = header.author(); + let author = parent.author(); let info = work_info.entry(author).or_default(); info.proposed += 1; - info.missed += parent_validators.len() - TendermintSealView::new(&header.seal()).bitset()?.count(); + info.missed += previous_validators.len() - TendermintSealView::new(&header.seal()).bitset()?.count(); + + header = parent; } Ok(work_info) diff --git a/core/src/consensus/tendermint/worker.rs b/core/src/consensus/tendermint/worker.rs index 7453142639..b1248b3a03 100644 --- a/core/src/consensus/tendermint/worker.rs +++ b/core/src/consensus/tendermint/worker.rs @@ -1248,13 +1248,14 @@ 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_hash = header.parent_hash(); + let grand_parent_hash = + self.client().block_header(&(*parent_hash).into()).expect("The parent block must exist").parent_hash(); for (bitset_index, signature) in seal_view.signatures()? { - let public = self.validators.get(&grand_parent_hash, bitset_index); + let public = match self.validators.get_current(header.parent_hash(), bitset_index) { + Some(p) => p, + None => self.validators.get(&grand_parent_hash, bitset_index), + }; if !verify_schnorr(&public, &signature, &precommit_vote_on.hash())? { let address = public_to_address(&public); return Err(EngineError::BlockNotAuthorized(address.to_owned()).into()) @@ -1267,7 +1268,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_current(&parent_hash, &voted_validators)?; Ok(()) } @@ -1475,7 +1476,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 19fa775db8..6f9fd29568 100644 --- a/core/src/consensus/validator_set/dynamic_validator.rs +++ b/core/src/consensus/validator_set/dynamic_validator.rs @@ -24,7 +24,7 @@ use parking_lot::RwLock; use super::{RoundRobinValidator, ValidatorSet}; use crate::client::ConsensusClient; use crate::consensus::bit_set::BitSet; -use crate::consensus::stake::{CurrentValidators, NextValidators, Validator}; +use crate::consensus::stake::{CurrentValidators, NextValidators, PreviousValidators, Validator}; use crate::consensus::EngineError; /// Validator set containing a known set of public keys. @@ -87,6 +87,29 @@ impl DynamicValidator { } } + fn previous_validators(&self, hash: BlockHash) -> Option> { + let client: Arc = + self.client.read().as_ref().and_then(Weak::upgrade).expect("Client is not initialized"); + let block_id = hash.into(); + let term_id = client.current_term_id(block_id).expect( + "valdators() is called when creating a block or verifying a block. + Minor creates a block only when the parent block is imported. + The n'th block is verified only when the parent block is imported.", + ); + if term_id == 0 { + return None + } + let state = client.state_at(block_id)?; + let validators = PreviousValidators::load_from_state(&state).unwrap(); + if validators.is_empty() { + None + } else { + let mut validators: Vec<_> = validators.into(); + validators.reverse(); + Some(validators) + } + } + fn validators_pubkey(&self, hash: BlockHash) -> Option> { self.next_validators(hash).map(|validators| validators.into_iter().map(|val| *val.pubkey()).collect()) } @@ -95,6 +118,10 @@ impl DynamicValidator { self.current_validators(hash).map(|validators| validators.into_iter().map(|val| *val.pubkey()).collect()) } + fn previous_validators_pubkey(&self, hash: BlockHash) -> Option> { + self.previous_validators(hash).map(|validators| validators.into_iter().map(|val| *val.pubkey()).collect()) + } + pub fn proposer_index(&self, parent: BlockHash, prev_proposer_index: usize, proposed_view: usize) -> usize { if let Some(validators) = self.next_validators(parent) { let num_validators = validators.len(); @@ -104,6 +131,44 @@ impl DynamicValidator { (prev_proposer_index + proposed_view + 1) % num_validators } } + + pub fn get_current(&self, hash: &BlockHash, index: usize) -> Option { + let validators = self.current_validators_pubkey(*hash)?; + let n_validators = validators.len(); + Some(*validators.get(index % n_validators).unwrap()) + } + + pub fn check_enough_votes_with_current(&self, hash: &BlockHash, votes: &BitSet) -> Result<(), EngineError> { + if let Some(validators) = self.current_validators(*hash) { + 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, + })) + } + } else { + let client = self.client.read().as_ref().and_then(Weak::upgrade).expect("Client is not initialized"); + let header = client.block_header(&(*hash).into()).unwrap(); + self.check_enough_votes(&header.parent_hash(), votes) + } + } } impl ValidatorSet for DynamicValidator { @@ -208,6 +273,14 @@ impl ValidatorSet for DynamicValidator { *client_lock = Some(client); } + fn previous_addresses(&self, hash: &BlockHash) -> Vec
{ + if let Some(validators) = self.previous_validators_pubkey(*hash) { + validators.iter().map(public_to_address).collect() + } else { + self.initial_list.previous_addresses(hash) + } + } + fn current_addresses(&self, hash: &BlockHash) -> Vec
{ if let Some(validators) = self.current_validators_pubkey(*hash) { validators.iter().map(public_to_address).collect() diff --git a/core/src/consensus/validator_set/mod.rs b/core/src/consensus/validator_set/mod.rs index 835d0ecda5..c2b17eb7c9 100644 --- a/core/src/consensus/validator_set/mod.rs +++ b/core/src/consensus/validator_set/mod.rs @@ -57,6 +57,8 @@ pub trait ValidatorSet: Send + Sync { /// Allows blockchain state access. fn register_client(&self, _client: Weak) {} + fn previous_addresses(&self, _hash: &BlockHash) -> Vec
; + fn current_addresses(&self, _hash: &BlockHash) -> Vec
; fn next_addresses(&self, _hash: &BlockHash) -> Vec
; diff --git a/core/src/consensus/validator_set/validator_list.rs b/core/src/consensus/validator_set/validator_list.rs index 9f4fa278f2..4245a2b077 100644 --- a/core/src/consensus/validator_set/validator_list.rs +++ b/core/src/consensus/validator_set/validator_list.rs @@ -105,6 +105,10 @@ impl ValidatorSet for RoundRobinValidator { *self.client.write() = Some(client); } + fn previous_addresses(&self, _hash: &BlockHash) -> Vec
{ + self.validators.iter().map(public_to_address).collect() + } + fn current_addresses(&self, _hash: &BlockHash) -> Vec
{ self.validators.iter().map(public_to_address).collect() } diff --git a/foundry/run_node.rs b/foundry/run_node.rs index da04e16f30..f5017394cc 100644 --- a/foundry/run_node.rs +++ b/foundry/run_node.rs @@ -21,8 +21,8 @@ use std::time::{SystemTime, UNIX_EPOCH}; use ccore::snapshot_notify; use ccore::{ - AccountProvider, AccountProviderError, BlockId, ChainNotify, ClientConfig, ClientService, EngineInfo, EngineType, - Miner, MinerService, Scheme, NUM_COLUMNS, + AccountProvider, AccountProviderError, ChainNotify, ClientConfig, ClientService, EngineInfo, EngineType, Miner, + MinerService, Scheme, NUM_COLUMNS, }; use cdiscovery::{Config, Discovery}; use ckey::{Address, NetworkId, PlatformAddress}; @@ -264,7 +264,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/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 Account for AccountClient @@ -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) -> Result { 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) -> Result { 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 b68a585140..c7078332d7 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) -> Result> { 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) -> Result>> { 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) -> Result>> { 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> { 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> { 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 { - Ok(self.client.common_params(BlockId::Latest).unwrap().network_id()) + Ok(self.client.network_id()) } fn get_common_params(&self, block_number: Option) -> Result> { 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 MempoolClient { impl Mempool for MempoolClient where - C: BlockChainClient + MiningBlockChainClient + 'static, + C: BlockChainClient + MiningBlockChainClient + EngineInfo + 'static, { fn send_signed_transaction(&self, raw: Bytes) -> Result { Rlp::new(&raw.into_vec()) @@ -82,7 +82,7 @@ where fn get_banned_accounts(&self) -> Result> { 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> { 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()) }