github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/adm/src/commands/blockstore.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::collections::HashMap;
    19  use std::fs::File;
    20  use std::io::{self, Read, Write};
    21  
    22  use clap::ArgMatches;
    23  use protobuf;
    24  use protobuf::Message;
    25  use serde_yaml;
    26  
    27  use sawtooth_sdk::messages::block::{Block, BlockHeader};
    28  use sawtooth_sdk::messages::transaction::TransactionHeader;
    29  
    30  use blockstore::Blockstore;
    31  use config;
    32  use database::error::DatabaseError;
    33  use database::lmdb;
    34  use err::CliError;
    35  use wrappers::Block as BlockWrapper;
    36  
    37  const NULL_BLOCK_IDENTIFIER: &str = "0000000000000000";
    38  
    39  pub fn run<'a>(args: &ArgMatches<'a>) -> Result<(), CliError> {
    40      match args.subcommand() {
    41          ("backup", Some(args)) => run_backup_command(args),
    42          ("restore", Some(args)) => run_restore_command(args),
    43          ("list", Some(args)) => run_list_command(args),
    44          ("show", Some(args)) => run_show_command(args),
    45          ("prune", Some(args)) => run_prune_command(args),
    46          ("export", Some(args)) => run_export_command(args),
    47          ("import", Some(args)) => run_import_command(args),
    48          ("stats", Some(args)) => run_stats_command(args),
    49          _ => {
    50              println!("Invalid subcommand; Pass --help for usage.");
    51              Ok(())
    52          }
    53      }
    54  }
    55  
    56  fn run_backup_command<'a>(args: &ArgMatches<'a>) -> Result<(), CliError> {
    57      let ctx = create_context()?;
    58      let blockstore = open_blockstore(&ctx)?;
    59  
    60      let filepath = args.value_of("output")
    61          .ok_or_else(|| CliError::ArgumentError("No output file".into()))?;
    62      let mut file = File::create(filepath)
    63          .map_err(|err| CliError::EnvironmentError(format!("Failed to create file: {}", err)))?;
    64  
    65      let mut current = match args.value_of("start") {
    66          None => blockstore
    67              .get_chain_head()
    68              .map_err(|err| CliError::EnvironmentError(format!("{}", err))),
    69          Some(sig) => Ok(sig.into()),
    70      }?;
    71  
    72      while current != NULL_BLOCK_IDENTIFIER {
    73          let block = blockstore.get(&current).map_err(|err| {
    74              CliError::EnvironmentError(format!("Block in chain missing from blockstore: {}", err))
    75          })?;
    76          backup_block(&block, &mut file)?;
    77          let block_header: BlockHeader = protobuf::parse_from_bytes(&block.header)
    78              .map_err(|err| CliError::ParseError(format!("{}", err)))?;
    79          current = block_header.previous_block_id
    80      }
    81      Ok(())
    82  }
    83  
    84  fn run_restore_command<'a>(args: &ArgMatches<'a>) -> Result<(), CliError> {
    85      let ctx = create_context()?;
    86      let blockstore = open_blockstore(&ctx)?;
    87  
    88      let filepath = args.value_of("input")
    89          .ok_or_else(|| CliError::ArgumentError("No input file".into()))?;
    90      let mut file = File::open(filepath)
    91          .map_err(|err| CliError::EnvironmentError(format!("Failed to open file: {}", err)))?;
    92  
    93      let mut source = protobuf::CodedInputStream::new(&mut file);
    94  
    95      while let Some(block) = restore_block(&mut source)? {
    96          blockstore
    97              .put(&block)
    98              .map_err(|err| CliError::EnvironmentError(format!("Failed to put block: {}", err)))?;
    99      }
   100      Ok(())
   101  }
   102  
   103  fn run_list_command<'a>(args: &ArgMatches<'a>) -> Result<(), CliError> {
   104      let ctx = create_context()?;
   105      let blockstore = open_blockstore(&ctx)?;
   106  
   107      let mut count = u64::from_str_radix(args.value_of("count").unwrap_or("100"), 10).unwrap();
   108  
   109      // Get the chain head
   110      let head_sig = match args.value_of("start") {
   111          None => blockstore
   112              .get_chain_head()
   113              .map_err(|err| CliError::EnvironmentError(format!("{}", err))),
   114          Some(sig) => Ok(sig.into()),
   115      }?;
   116  
   117      // Walk back from the chain head
   118      let mut block_id = head_sig;
   119      print_block_store_list_header();
   120  
   121      while block_id != NULL_BLOCK_IDENTIFIER && count > 0 {
   122          let block = blockstore
   123              .get(&block_id)
   124              .map_err(|err| CliError::EnvironmentError(format!("{}", err)))?;
   125          let block_header: BlockHeader = protobuf::parse_from_bytes(&block.header)
   126              .map_err(|err| CliError::ParseError(format!("{}", err)))?;
   127          let batches = block.batches.len();
   128          let txns = block
   129              .batches
   130              .iter()
   131              .fold(0, |acc, batch| acc + batch.transactions.len());
   132          print_block_store_list_row(
   133              block_header.block_num,
   134              &block.header_signature,
   135              batches,
   136              txns,
   137              &block_header.signer_public_key,
   138          );
   139          block_id = block_header.previous_block_id;
   140          count -= 1;
   141      }
   142      Ok(())
   143  }
   144  
   145  fn print_block_store_list_header() {
   146      println!(
   147          "{:<5} {:<128} {:<5} {:<5} {}",
   148          "NUM", "BLOCK_ID", "BATS", "TXNS", "SIGNER"
   149      );
   150  }
   151  
   152  fn print_block_store_list_row(
   153      block_num: u64,
   154      block_id: &str,
   155      batches: usize,
   156      txns: usize,
   157      signer: &str,
   158  ) {
   159      println!(
   160          "{:<5} {:<128} {:<5} {:<5} {}...",
   161          block_num,
   162          block_id,
   163          batches,
   164          txns,
   165          &signer[..6]
   166      );
   167  }
   168  
   169  fn run_show_command<'a>(args: &ArgMatches<'a>) -> Result<(), CliError> {
   170      let ctx = create_context()?;
   171      let blockstore = open_blockstore(&ctx)?;
   172  
   173      let block = {
   174          if args.is_present("block") {
   175              let block = args.value_of("block")
   176                  .ok_or_else(|| CliError::ArgumentError("No block".into()))?;
   177              blockstore.get(block)
   178          } else if args.is_present("batch") {
   179              let batch = args.value_of("batch")
   180                  .ok_or_else(|| CliError::ArgumentError("No batch".into()))?;
   181              blockstore.get_by_batch(batch)
   182          } else if args.is_present("transaction") {
   183              let transaction = args.value_of("transaction")
   184                  .ok_or_else(|| CliError::ArgumentError("No transaction".into()))?;
   185              blockstore.get_by_transaction(transaction)
   186          } else if args.is_present("blocknum") {
   187              let blocknum = args.value_of("blocknum")
   188                  .ok_or_else(|| CliError::ArgumentError("No block num".into()))?;
   189              let height: u64 = blocknum
   190                  .parse()
   191                  .map_err(|err| CliError::ArgumentError(format!("Invalid block num: {}", err)))?;
   192              blockstore.get_by_height(height)
   193          } else {
   194              return Err(CliError::ArgumentError("No identifier specified".into()));
   195          }
   196      }.map_err(|err| CliError::ArgumentError(format!("Error getting block: {}", err)))?;
   197  
   198      let block_wrapper = BlockWrapper::try_from(block)
   199          .map_err(|err| CliError::EnvironmentError(format!("{}", err)))?;
   200  
   201      let block_yaml = serde_yaml::to_string(&block_wrapper)
   202          .map_err(|err| CliError::EnvironmentError(format!("{}", err)))?;
   203  
   204      println!("{}", block_yaml);
   205      Ok(())
   206  }
   207  
   208  fn run_prune_command<'a>(args: &ArgMatches<'a>) -> Result<(), CliError> {
   209      let ctx = create_context()?;
   210      let blockstore = open_blockstore(&ctx)?;
   211  
   212      let block_id = args.value_of("block")
   213          .ok_or_else(|| CliError::ArgumentError("No block id".into()))?;
   214  
   215      blockstore
   216          .get(block_id)
   217          .map_err(|_| CliError::ArgumentError(format!("Block not found: {}", block_id)))?;
   218  
   219      // Get the chain head
   220      let chain_head = blockstore
   221          .get_chain_head()
   222          .map_err(|err| CliError::EnvironmentError(format!("{}", err)))?;
   223  
   224      let mut current = blockstore
   225          .get(&chain_head)
   226          .map_err(|err| CliError::EnvironmentError(format!("{}", err)))?;
   227  
   228      loop {
   229          blockstore
   230              .delete(&current.header_signature)
   231              .map_err(|err| CliError::EnvironmentError(format!("{}", err)))?;
   232          if current.header_signature == block_id {
   233              break;
   234          }
   235          let header: BlockHeader = protobuf::parse_from_bytes(&current.header)
   236              .map_err(|err| CliError::ParseError(format!("{}", err)))?;
   237  
   238          current = blockstore
   239              .get(&header.previous_block_id)
   240              .map_err(|err| CliError::EnvironmentError(format!("{}", err)))?;
   241      }
   242      Ok(())
   243  }
   244  
   245  fn run_export_command<'a>(args: &ArgMatches<'a>) -> Result<(), CliError> {
   246      let ctx = create_context()?;
   247      let blockstore = open_blockstore(&ctx)?;
   248  
   249      let block_id = args.value_of("block")
   250          .ok_or_else(|| CliError::ArgumentError("No block id".into()))?;
   251  
   252      let block = blockstore
   253          .get(block_id)
   254          .map_err(|_| CliError::ArgumentError(format!("Block not found: {}", block_id)))?;
   255  
   256      match args.value_of("output") {
   257          Some(filepath) => {
   258              let mut file = File::create(filepath).map_err(|err| {
   259                  CliError::EnvironmentError(format!("Failed to create file: {}", err))
   260              })?;
   261              block
   262                  .write_to_writer(&mut file)
   263                  .map_err(|err| CliError::EnvironmentError(format!("{}", err)))
   264          }
   265          None => {
   266              let stdout = io::stdout();
   267              let mut handle = stdout.lock();
   268              block
   269                  .write_to_writer(&mut handle)
   270                  .map_err(|err| CliError::EnvironmentError(format!("{}", err)))
   271          }
   272      }
   273  }
   274  
   275  fn run_import_command<'a>(args: &ArgMatches<'a>) -> Result<(), CliError> {
   276      let ctx = create_context()?;
   277      let blockstore = open_blockstore(&ctx)?;
   278  
   279      let filepath = args.value_of("blockfile")
   280          .ok_or_else(|| CliError::ArgumentError("No file".into()))?;
   281      let mut file = File::open(filepath)
   282          .map_err(|err| CliError::EnvironmentError(format!("Failed to open file: {}", err)))?;
   283      let mut packed = Vec::new();
   284      file.read_to_end(&mut packed)
   285          .map_err(|err| CliError::EnvironmentError(format!("Failed to read file: {}", err)))?;
   286  
   287      let block: Block = protobuf::parse_from_bytes(&packed)
   288          .map_err(|err| CliError::ParseError(format!("{}", err)))?;
   289      let block_header: BlockHeader = protobuf::parse_from_bytes(&block.header)
   290          .map_err(|err| CliError::ParseError(format!("{}", err)))?;
   291      let block_id = block.header_signature.clone();
   292  
   293      // Ensure this block is an immediate child of the current chain head
   294      match blockstore.get_chain_head() {
   295          Ok(chain_head) => {
   296              if block_header.previous_block_id != chain_head {
   297                  return Err(CliError::ArgumentError(format!(
   298                      "New block must be an immediate child of the current chain head: {}",
   299                      chain_head
   300                  )));
   301              }
   302          }
   303          Err(DatabaseError::NotFoundError(_)) => (),
   304          Err(err) => {
   305              return Err(CliError::EnvironmentError(format!("{}", err)));
   306          }
   307      }
   308  
   309      blockstore.put(&block).map_err(|err| {
   310          CliError::ArgumentError(format!("Failed to put block into database: {}", err))
   311      })?;
   312  
   313      println!("Block {} added", block_id);
   314      Ok(())
   315  }
   316  
   317  fn run_stats_command<'a>(args: &ArgMatches<'a>) -> Result<(), CliError> {
   318      let ctx = create_context()?;
   319      let blockstore = open_blockstore(&ctx)?;
   320  
   321      let block_count = blockstore
   322          .get_current_height()
   323          .map_err(|err| CliError::EnvironmentError(format!("{}", err)))?;
   324      let batch_count = blockstore
   325          .get_batch_count()
   326          .map_err(|err| CliError::EnvironmentError(format!("{}", err)))?;
   327      let txn_count = blockstore
   328          .get_transaction_count()
   329          .map_err(|err| CliError::EnvironmentError(format!("{}", err)))?;
   330  
   331      if args.is_present("extended") {
   332          let mut txn_family_counts = HashMap::new();
   333          let chain_head = blockstore
   334              .get_chain_head()
   335              .map_err(|err| CliError::EnvironmentError(format!("{}", err)))?;
   336          let mut block = blockstore
   337              .get(&chain_head)
   338              .map_err(|err| CliError::EnvironmentError(format!("{}", err)))?;
   339  
   340          loop {
   341              for batch in &block.batches {
   342                  for txn in &batch.transactions {
   343                      let txn_header: TransactionHeader = protobuf::parse_from_bytes(&txn.header)
   344                          .map_err(|err| CliError::ParseError(format!("{}", err)))?;
   345                      let count = txn_family_counts.entry(txn_header.family_name).or_insert(0);
   346                      *count += 1;
   347                  }
   348              }
   349              let header: BlockHeader = protobuf::parse_from_bytes(&block.header)
   350                  .map_err(|err| CliError::ParseError(format!("{}", err)))?;
   351              if header.previous_block_id == NULL_BLOCK_IDENTIFIER {
   352                  break;
   353              }
   354              block = blockstore
   355                  .get(&header.previous_block_id)
   356                  .map_err(|err| CliError::EnvironmentError(format!("{}", err)))?;
   357          }
   358  
   359          println!("Blocks:       {}", block_count);
   360          println!("Batches:      {}", batch_count);
   361          println!("Transactions: {}", txn_count);
   362          for (family, count) in &txn_family_counts {
   363              println!("  {}: {}", family, count);
   364          }
   365      } else {
   366          println!("Blocks:       {}", block_count);
   367          println!("Batches:      {}", batch_count);
   368          println!("Transactions: {}", txn_count);
   369      }
   370  
   371      Ok(())
   372  }
   373  
   374  fn create_context() -> Result<lmdb::LmdbContext, CliError> {
   375      let path_config = config::get_path_config();
   376      let blockstore_path = &path_config.data_dir.join(config::get_blockstore_filename());
   377  
   378      lmdb::LmdbContext::new(blockstore_path, 3, None)
   379          .map_err(|err| CliError::EnvironmentError(format!("{}", err)))
   380  }
   381  
   382  fn open_blockstore(ctx: &lmdb::LmdbContext) -> Result<Blockstore, CliError> {
   383      let blockstore_db = lmdb::LmdbDatabase::new(
   384          ctx,
   385          &["index_batch", "index_transaction", "index_block_num"],
   386      ).map_err(|err| CliError::EnvironmentError(format!("{}", err)))?;
   387  
   388      Ok(Blockstore::new(blockstore_db))
   389  }
   390  
   391  fn backup_block<W: Write>(block: &Block, writer: &mut W) -> Result<(), CliError> {
   392      block
   393          .write_length_delimited_to_writer(writer)
   394          .map_err(|err| CliError::EnvironmentError(format!("{}", err)))
   395  }
   396  
   397  fn restore_block(source: &mut protobuf::CodedInputStream) -> Result<Option<Block>, CliError> {
   398      let eof = source
   399          .eof()
   400          .map_err(|err| CliError::EnvironmentError(format!("Failed to check EOF: {}", err)))?;
   401      if eof {
   402          return Ok(None);
   403      }
   404  
   405      let block = protobuf::parse_length_delimited_from(source)
   406          .map_err(|err| CliError::EnvironmentError(format!("Failed to parse block: {}", err)))?;
   407  
   408      Ok(Some(block))
   409  }