github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/validator/src/journal/chain.rs (about)

     1  /*
     2   * Copyright 2018 Intel Corporation
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   * ------------------------------------------------------------------------------
    16   */
    17  
    18  use std::fs::File;
    19  use std::io;
    20  use std::io::prelude::*;
    21  use std::marker::Send;
    22  use std::marker::Sync;
    23  use std::path::PathBuf;
    24  use std::sync::atomic::AtomicBool;
    25  use std::sync::atomic::Ordering;
    26  use std::sync::mpsc;
    27  use std::sync::mpsc::channel;
    28  use std::sync::mpsc::Receiver;
    29  use std::sync::mpsc::RecvError;
    30  use std::sync::mpsc::Sender;
    31  use std::sync::Arc;
    32  use std::sync::Mutex;
    33  use std::sync::RwLock;
    34  use std::thread;
    35  use std::time::Duration;
    36  
    37  use protobuf;
    38  
    39  use batch::Batch;
    40  use journal;
    41  use journal::block_validator::{BlockValidationResult, BlockValidator, ValidationError};
    42  use journal::block_wrapper::BlockWrapper;
    43  use journal::chain_head_lock::ChainHeadLock;
    44  use metrics;
    45  use state::state_pruning_manager::StatePruningManager;
    46  
    47  use proto::transaction_receipt::TransactionReceipt;
    48  use scheduler::TxnExecutionResult;
    49  
    50  const RECV_TIMEOUT_MILLIS: u64 = 100;
    51  
    52  lazy_static! {
    53      static ref COLLECTOR: metrics::MetricsCollectorHandle =
    54          metrics::get_collector("sawtooth_validator.chain");
    55  }
    56  
    57  #[derive(Debug)]
    58  pub enum ChainControllerError {
    59      QueueRecvError(RecvError),
    60      ChainIdError(io::Error),
    61      ChainUpdateError(String),
    62      BlockValidationError(ValidationError),
    63      BrokenQueue,
    64  }
    65  
    66  impl From<RecvError> for ChainControllerError {
    67      fn from(err: RecvError) -> Self {
    68          ChainControllerError::QueueRecvError(err)
    69      }
    70  }
    71  
    72  impl From<io::Error> for ChainControllerError {
    73      fn from(err: io::Error) -> Self {
    74          ChainControllerError::ChainIdError(err)
    75      }
    76  }
    77  
    78  impl From<ValidationError> for ChainControllerError {
    79      fn from(err: ValidationError) -> Self {
    80          ChainControllerError::BlockValidationError(err)
    81      }
    82  }
    83  
    84  pub trait ChainObserver: Send + Sync {
    85      fn chain_update(&mut self, block: &BlockWrapper, receipts: &[&TransactionReceipt]);
    86  }
    87  
    88  pub trait ChainHeadUpdateObserver: Send + Sync {
    89      /// Called when the chain head has updated.
    90      ///
    91      /// Args:
    92      ///     block: the new chain head
    93      ///     committed_batches: all of the batches that have been committed
    94      ///         on the given fork. This may be across multiple blocks.
    95      ///     uncommitted_batches: all of the batches that have been uncommitted
    96      ///         from the previous fork, if one was dropped.
    97      fn on_chain_head_updated(
    98          &mut self,
    99          block: BlockWrapper,
   100          committed_batches: Vec<Batch>,
   101          uncommitted_batches: Vec<Batch>,
   102      );
   103  }
   104  
   105  pub trait BlockCache: Send + Sync {
   106      fn contains(&self, block_id: &str) -> bool;
   107  
   108      fn put(&mut self, block: BlockWrapper);
   109  
   110      fn get(&self, block_id: &str) -> Option<BlockWrapper>;
   111  }
   112  
   113  #[derive(Debug)]
   114  pub enum ChainReadError {
   115      GeneralReadError(String),
   116  }
   117  
   118  pub trait ChainReader: Send + Sync {
   119      fn chain_head(&self) -> Result<Option<BlockWrapper>, ChainReadError>;
   120      fn count_committed_transactions(&self) -> Result<usize, ChainReadError>;
   121      fn get_block_by_block_num(
   122          &self,
   123          block_num: u64,
   124      ) -> Result<Option<BlockWrapper>, ChainReadError>;
   125  }
   126  
   127  pub trait ChainWriter: Send + Sync {
   128      fn update_chain(
   129          &mut self,
   130          new_chain: &[BlockWrapper],
   131          old_chain: &[BlockWrapper],
   132      ) -> Result<(), ChainControllerError>;
   133  }
   134  
   135  struct ChainControllerState<BC: BlockCache, BV: BlockValidator, CW: ChainWriter> {
   136      block_cache: BC,
   137      block_validator: BV,
   138      chain_writer: CW,
   139      chain_reader: Box<ChainReader>,
   140      chain_head: Option<BlockWrapper>,
   141      chain_id_manager: ChainIdManager,
   142      observers: Vec<Box<ChainObserver>>,
   143      state_pruning_manager: StatePruningManager,
   144  }
   145  
   146  #[derive(Clone)]
   147  pub struct ChainController<BC: BlockCache, BV: BlockValidator, CW: ChainWriter> {
   148      state: Arc<RwLock<ChainControllerState<BC, BV, CW>>>,
   149      stop_handle: Arc<Mutex<Option<ChainThreadStopHandle>>>,
   150      block_queue_sender: Option<Sender<BlockWrapper>>,
   151      validation_result_sender: Option<Sender<(bool, BlockValidationResult)>>,
   152      state_pruning_block_depth: u32,
   153      chain_head_lock: ChainHeadLock,
   154  }
   155  
   156  impl<BC: BlockCache + 'static, BV: BlockValidator + 'static, CW: ChainWriter + 'static>
   157      ChainController<BC, BV, CW>
   158  {
   159      pub fn new(
   160          block_cache: BC,
   161          block_validator: BV,
   162          chain_writer: CW,
   163          chain_reader: Box<ChainReader>,
   164          chain_head_lock: ChainHeadLock,
   165          data_dir: String,
   166          state_pruning_block_depth: u32,
   167          observers: Vec<Box<ChainObserver>>,
   168          state_pruning_manager: StatePruningManager,
   169      ) -> Self {
   170          let mut chain_controller = ChainController {
   171              state: Arc::new(RwLock::new(ChainControllerState {
   172                  block_cache,
   173                  block_validator,
   174                  chain_writer,
   175                  chain_reader,
   176                  chain_id_manager: ChainIdManager::new(data_dir),
   177                  observers,
   178                  chain_head: None,
   179                  state_pruning_manager,
   180              })),
   181              stop_handle: Arc::new(Mutex::new(None)),
   182              block_queue_sender: None,
   183              validation_result_sender: None,
   184              state_pruning_block_depth,
   185              chain_head_lock,
   186          };
   187  
   188          chain_controller.initialize_chain_head();
   189  
   190          chain_controller
   191      }
   192  
   193      pub fn chain_head(&self) -> Option<BlockWrapper> {
   194          let state = self.state
   195              .read()
   196              .expect("No lock holder should have poisoned the lock");
   197  
   198          state.chain_head.clone()
   199      }
   200  
   201      pub fn on_block_received(&mut self, block: BlockWrapper) -> Result<(), ChainControllerError> {
   202          let mut state = self.state
   203              .write()
   204              .expect("No lock holder should have poisoned the lock");
   205  
   206          if has_block_no_lock(&state, block.header_signature()) {
   207              return Ok(());
   208          }
   209  
   210          if state.chain_head.is_none() {
   211              if let Err(err) = set_genesis(&mut state, &self.chain_head_lock, block.clone()) {
   212                  warn!(
   213                      "Unable to set chain head; genesis block {} is not valid: {:?}",
   214                      block.header_signature(),
   215                      err
   216                  );
   217              }
   218              return Ok(());
   219          }
   220  
   221          state.block_cache.put(block.clone());
   222          self.submit_blocks_for_verification(&state.block_validator, &[block])?;
   223          Ok(())
   224      }
   225  
   226      pub fn has_block(&self, block_id: &str) -> bool {
   227          let state = self.state
   228              .read()
   229              .expect("No lock holder should have poisoned the lock");
   230          has_block_no_lock(&state, block_id)
   231      }
   232  
   233      fn on_block_validated(&mut self, commit_new_block: bool, result: BlockValidationResult) {
   234          let mut state = self.state
   235              .write()
   236              .expect("No lock holder should have poisoned the lock");
   237  
   238          let mut blocks_considered_count =
   239              COLLECTOR.counter("ChainController.blocks_considered_count", None, None);
   240          blocks_considered_count.inc();
   241  
   242          let new_block = result.block;
   243          let initial_chain_head = result.chain_head.header_signature().clone();
   244          if state
   245              .chain_head
   246              .as_ref()
   247              .map(|block| initial_chain_head != block.header_signature())
   248              .unwrap_or(false)
   249          {
   250              info!(
   251                  "Chain head updated from {} to {} while processing block {}",
   252                  result.chain_head,
   253                  state.chain_head.as_ref().unwrap(),
   254                  new_block
   255              );
   256              if let Err(err) =
   257                  self.submit_blocks_for_verification(&state.block_validator, &[new_block])
   258              {
   259                  error!("Unable to submit block for verification: {:?}", err);
   260              }
   261          } else if commit_new_block {
   262              let mut chain_head_guard = self.chain_head_lock.acquire();
   263              let chain_head_block = new_block.clone();
   264              state.chain_head = Some(new_block);
   265  
   266              state.state_pruning_manager.update_queue(
   267                  &result
   268                      .new_chain
   269                      .iter()
   270                      .map(|block| block.state_root_hash())
   271                      .collect::<Vec<_>>(),
   272                  &result
   273                      .current_chain
   274                      .iter()
   275                      .map(|block| (block.block_num(), block.state_root_hash()))
   276                      .collect::<Vec<_>>(),
   277              );
   278  
   279              if let Err(err) = state
   280                  .chain_writer
   281                  .update_chain(&result.new_chain, &result.current_chain)
   282              {
   283                  error!("Unable to update chain {:?}", err);
   284                  return;
   285              }
   286  
   287              info!(
   288                  "Chain head updated to {}",
   289                  state.chain_head.as_ref().unwrap()
   290              );
   291  
   292              let mut chain_head_gauge = COLLECTOR.gauge("ChainController.chain_head", None, None);
   293              chain_head_gauge.set_value(&chain_head_block.header_signature()[0..8]);
   294  
   295              let mut committed_transactions_count =
   296                  COLLECTOR.counter("ChainController.committed_transactions_count", None, None);
   297              committed_transactions_count.inc_n(result.transaction_count);
   298  
   299              let mut block_num_guage = COLLECTOR.gauge("ChainController.block_num", None, None);
   300              block_num_guage.set_value(chain_head_block.block_num());
   301  
   302              let chain_head = state.chain_head.clone().unwrap();
   303              chain_head_guard.notify_on_chain_updated(
   304                  Some(chain_head),
   305                  result.committed_batches,
   306                  result.uncommitted_batches,
   307              );
   308  
   309              state.chain_head.as_ref().map(|block| {
   310                  block.batches().iter().for_each(|batch| {
   311                      if batch.trace {
   312                          debug!(
   313                              "TRACE: {}: ChainController.on_block_validated",
   314                              batch.header_signature
   315                          )
   316                      }
   317                  })
   318              });
   319  
   320              let mut new_chain = result.new_chain;
   321              new_chain.reverse();
   322  
   323              for block in new_chain {
   324                  let receipts: Vec<TransactionReceipt> = block
   325                      .execution_results
   326                      .iter()
   327                      .map(TransactionReceipt::from)
   328                      .collect();
   329                  for observer in state.observers.iter_mut() {
   330                      observer.chain_update(&block, &receipts.iter().collect::<Vec<_>>());
   331                  }
   332              }
   333              let total_committed_txns = match state.chain_reader.count_committed_transactions() {
   334                  Ok(count) => count,
   335                  Err(err) => {
   336                      error!(
   337                          "Unable to read total committed transactions count: {:?}",
   338                          err
   339                      );
   340                      0
   341                  }
   342              };
   343  
   344              let mut committed_transactions_gauge =
   345                  COLLECTOR.gauge("ChainController.committed_transactions_gauge", None, None);
   346              committed_transactions_gauge.set_value(total_committed_txns);
   347  
   348              let chain_head_block_num = state.chain_head.as_ref().unwrap().block_num();
   349              if chain_head_block_num + 1 > self.state_pruning_block_depth as u64 {
   350                  let prune_at = chain_head_block_num - (self.state_pruning_block_depth as u64);
   351                  match state.chain_reader.get_block_by_block_num(prune_at) {
   352                      Ok(Some(block)) => state
   353                          .state_pruning_manager
   354                          .add_to_queue(block.block_num(), block.state_root_hash()),
   355                      Ok(None) => warn!("No block at block height {}; ignoring...", prune_at),
   356                      Err(err) => error!("Unable to fetch block at height {}: {:?}", prune_at, err),
   357                  }
   358  
   359                  // Execute pruning:
   360                  state.state_pruning_manager.execute(prune_at)
   361              }
   362          } else {
   363              info!("Rejected new chain head: {}", new_block);
   364          }
   365      }
   366  
   367      /// Light clone makes a copy of this controller, without access to the stop
   368      /// handle.
   369      pub fn light_clone(&self) -> Self {
   370          ChainController {
   371              state: self.state.clone(),
   372              // This instance doesn't share the stop handle: it's not a
   373              // publicly accessible instance
   374              stop_handle: Arc::new(Mutex::new(None)),
   375              block_queue_sender: self.block_queue_sender.clone(),
   376              validation_result_sender: self.validation_result_sender.clone(),
   377              state_pruning_block_depth: self.state_pruning_block_depth,
   378              chain_head_lock: self.chain_head_lock.clone(),
   379          }
   380      }
   381  
   382      fn submit_blocks_for_verification(
   383          &self,
   384          block_validator: &BV,
   385          blocks: &[BlockWrapper],
   386      ) -> Result<(), ChainControllerError> {
   387          let sender = self.validation_result_sender
   388              .as_ref()
   389              .expect(
   390                  "Attempted to submit blocks for validation before starting the chain controller",
   391              )
   392              .clone();
   393          block_validator.submit_blocks_for_verification(blocks, sender);
   394          Ok(())
   395      }
   396  
   397      pub fn queue_block(&self, block: BlockWrapper) {
   398          if self.block_queue_sender.is_some() {
   399              let sender = self.block_queue_sender.clone();
   400              if let Err(err) = sender.as_ref().unwrap().send(block) {
   401                  error!("Unable to add block to block queue: {}", err);
   402              }
   403          } else {
   404              debug!(
   405                  "Attempting to queue block {} before chain controller is started; Ignoring",
   406                  block
   407              );
   408          }
   409      }
   410  
   411      fn initialize_chain_head(&mut self) {
   412          // we need to check to see if a genesis block was created and stored,
   413          // before this controller was started
   414          let mut state = self.state
   415              .write()
   416              .expect("No lock holder should have poisoned the lock");
   417  
   418          let chain_head = state
   419              .chain_reader
   420              .chain_head()
   421              .expect("Invalid block store. Head of the block chain cannot be determined");
   422  
   423          if chain_head.is_some() {
   424              info!(
   425                  "Chain controller initialized with chain head: {}",
   426                  chain_head.as_ref().unwrap()
   427              );
   428              let notify_block = chain_head.clone().unwrap();
   429              state.chain_head = chain_head;
   430              let mut gauge = COLLECTOR.gauge("ChainController.chain_head", None, None);
   431              gauge.set_value(&notify_block.header_signature()[0..8]);
   432  
   433              let mut block_num_guage = COLLECTOR.gauge("ChainController.block_num", None, None);
   434              block_num_guage.set_value(&notify_block.block_num());
   435              let mut guard = self.chain_head_lock.acquire();
   436              guard.notify_on_chain_updated(Some(notify_block), vec![], vec![]);
   437          }
   438      }
   439  
   440      pub fn start(&mut self) {
   441          // duplicating what happens at the constructor time, but there are multiple
   442          // points in the lifetime of this object where the value of the
   443          // block store's chain head may have been set
   444          self.initialize_chain_head();
   445          let mut stop_handle = self.stop_handle.lock().unwrap();
   446          if stop_handle.is_none() {
   447              let (block_queue_sender, block_queue_receiver) = channel();
   448              let (validation_result_sender, validation_result_receiver) = channel();
   449  
   450              self.block_queue_sender = Some(block_queue_sender);
   451              self.validation_result_sender = Some(validation_result_sender);
   452  
   453              let thread_chain_controller = self.light_clone();
   454              let exit_flag = Arc::new(AtomicBool::new(false));
   455              let mut chain_thread = ChainThread::new(
   456                  thread_chain_controller,
   457                  block_queue_receiver,
   458                  exit_flag.clone(),
   459              );
   460              *stop_handle = Some(ChainThreadStopHandle::new(exit_flag.clone()));
   461              let chain_thread_builder =
   462                  thread::Builder::new().name("ChainThread:BlockRecevier".into());
   463              chain_thread_builder
   464                  .spawn(move || {
   465                      if let Err(err) = chain_thread.run() {
   466                          error!("Error occurred during ChainController loop: {:?}", err);
   467                      }
   468                  })
   469                  .unwrap();
   470              let result_thread_builder =
   471                  thread::Builder::new().name("ChainThread:ValidationResultRecevier".into());
   472              let mut result_thread_controller = self.light_clone();
   473              let result_thread_exit = exit_flag.clone();
   474              result_thread_builder
   475                  .spawn(move || loop {
   476                      let (can_commit, result) = match validation_result_receiver
   477                          .recv_timeout(Duration::from_millis(RECV_TIMEOUT_MILLIS))
   478                      {
   479                          Err(mpsc::RecvTimeoutError::Timeout) => {
   480                              if result_thread_exit.load(Ordering::Relaxed) {
   481                                  break;
   482                              } else {
   483                                  continue;
   484                              }
   485                          }
   486                          Err(_) => {
   487                              error!("Result queue shutdown unexpectedly");
   488                              break;
   489                          }
   490                          Ok(res) => res,
   491                      };
   492  
   493                      if !result_thread_exit.load(Ordering::Relaxed) {
   494                          result_thread_controller.on_block_validated(can_commit, result);
   495                      } else {
   496                          break;
   497                      }
   498                  })
   499                  .unwrap();
   500          }
   501      }
   502  
   503      pub fn stop(&mut self) {
   504          let mut stop_handle = self.stop_handle.lock().unwrap();
   505          if stop_handle.is_some() {
   506              let handle: ChainThreadStopHandle = stop_handle.take().unwrap();
   507              handle.stop();
   508          }
   509      }
   510  }
   511  
   512  fn has_block_no_lock<BC: BlockCache, BV: BlockValidator, CW: ChainWriter>(
   513      state: &ChainControllerState<BC, BV, CW>,
   514      block_id: &str,
   515  ) -> bool {
   516      state.block_cache.contains(block_id) || state.block_validator.in_process(block_id)
   517          || state.block_validator.in_pending(block_id)
   518  }
   519  
   520  /// This is used by a non-genesis journal when it has received the
   521  /// genesis block from the genesis validator
   522  fn set_genesis<BC: BlockCache, BV: BlockValidator, CW: ChainWriter>(
   523      state: &mut ChainControllerState<BC, BV, CW>,
   524      lock: &ChainHeadLock,
   525      block: BlockWrapper,
   526  ) -> Result<(), ChainControllerError> {
   527      if block.previous_block_id() == journal::NULL_BLOCK_IDENTIFIER {
   528          let chain_id = state.chain_id_manager.get_block_chain_id()?;
   529          if chain_id
   530              .as_ref()
   531              .map(|block_id| block_id != block.header_signature())
   532              .unwrap_or(false)
   533          {
   534              warn!(
   535                  "Block id does not match block chain id {}. Ignoring initial chain head: {}",
   536                  chain_id.unwrap(),
   537                  block.header_signature()
   538              );
   539          } else {
   540              state.block_validator.validate_block(block.clone())?;
   541  
   542              if chain_id.is_none() {
   543                  state
   544                      .chain_id_manager
   545                      .save_block_chain_id(block.header_signature())?;
   546              }
   547  
   548              state.chain_writer.update_chain(&[block.clone()], &[])?;
   549              state.chain_head = Some(block.clone());
   550              let mut guard = lock.acquire();
   551              guard.notify_on_chain_updated(Some(block.clone()), vec![], vec![]);
   552          }
   553      }
   554  
   555      Ok(())
   556  }
   557  
   558  impl<'a> From<&'a TxnExecutionResult> for TransactionReceipt {
   559      fn from(result: &'a TxnExecutionResult) -> Self {
   560          let mut receipt = TransactionReceipt::new();
   561  
   562          receipt.set_data(protobuf::RepeatedField::from_vec(
   563              result.data.iter().map(|(_, data)| data.clone()).collect(),
   564          ));
   565          receipt.set_state_changes(protobuf::RepeatedField::from_vec(
   566              result.state_changes.clone(),
   567          ));
   568          receipt.set_events(protobuf::RepeatedField::from_vec(result.events.clone()));
   569          receipt.set_transaction_id(result.signature.clone());
   570  
   571          receipt
   572      }
   573  }
   574  
   575  struct ChainThread<BC: BlockCache, BV: BlockValidator, CW: ChainWriter> {
   576      chain_controller: ChainController<BC, BV, CW>,
   577      block_queue: Receiver<BlockWrapper>,
   578      exit: Arc<AtomicBool>,
   579  }
   580  
   581  trait StopHandle: Clone {
   582      fn stop(&self);
   583  }
   584  
   585  impl<BC: BlockCache + 'static, BV: BlockValidator + 'static, CW: ChainWriter + 'static>
   586      ChainThread<BC, BV, CW>
   587  {
   588      fn new(
   589          chain_controller: ChainController<BC, BV, CW>,
   590          block_queue: Receiver<BlockWrapper>,
   591          exit_flag: Arc<AtomicBool>,
   592      ) -> Self {
   593          ChainThread {
   594              chain_controller,
   595              block_queue,
   596              exit: exit_flag,
   597          }
   598      }
   599  
   600      fn run(&mut self) -> Result<(), ChainControllerError> {
   601          loop {
   602              let block = match self.block_queue
   603                  .recv_timeout(Duration::from_millis(RECV_TIMEOUT_MILLIS))
   604              {
   605                  Err(mpsc::RecvTimeoutError::Timeout) => {
   606                      if self.exit.load(Ordering::Relaxed) {
   607                          break Ok(());
   608                      } else {
   609                          continue;
   610                      }
   611                  }
   612                  Err(_) => break Err(ChainControllerError::BrokenQueue),
   613                  Ok(block) => block,
   614              };
   615              self.chain_controller.on_block_received(block)?;
   616  
   617              if self.exit.load(Ordering::Relaxed) {
   618                  break Ok(());
   619              }
   620          }
   621      }
   622  }
   623  
   624  #[derive(Clone)]
   625  struct ChainThreadStopHandle {
   626      exit: Arc<AtomicBool>,
   627  }
   628  
   629  impl ChainThreadStopHandle {
   630      fn new(exit_flag: Arc<AtomicBool>) -> Self {
   631          ChainThreadStopHandle { exit: exit_flag }
   632      }
   633  }
   634  
   635  impl StopHandle for ChainThreadStopHandle {
   636      fn stop(&self) {
   637          self.exit.store(true, Ordering::Relaxed)
   638      }
   639  }
   640  
   641  /// The ChainIdManager is in charge of of keeping track of the block-chain-id
   642  /// stored in the data_dir.
   643  #[derive(Clone, Debug)]
   644  struct ChainIdManager {
   645      data_dir: String,
   646  }
   647  
   648  impl ChainIdManager {
   649      pub fn new(data_dir: String) -> Self {
   650          ChainIdManager { data_dir }
   651      }
   652  
   653      pub fn save_block_chain_id(&self, block_chain_id: &str) -> Result<(), io::Error> {
   654          let mut path = PathBuf::new();
   655          path.push(&self.data_dir);
   656          path.push("block-chain-id");
   657  
   658          let mut file = File::create(path)?;
   659          file.write_all(block_chain_id.as_bytes())
   660      }
   661  
   662      pub fn get_block_chain_id(&self) -> Result<Option<String>, io::Error> {
   663          let mut path = PathBuf::new();
   664          path.push(&self.data_dir);
   665          path.push("block-chain-id");
   666  
   667          match File::open(path) {
   668              Ok(mut file) => {
   669                  let mut contents = String::new();
   670                  file.read_to_string(&mut contents)?;
   671                  Ok(Some(contents))
   672              }
   673              Err(ref err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
   674              Err(err) => Err(err),
   675          }
   676      }
   677  }
   678  
   679  #[cfg(tests)]
   680  mod tests {}