github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/adm/src/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 protobuf;
    19  use protobuf::Message;
    20  use sawtooth_sdk::messages::block::{Block, BlockHeader};
    21  
    22  use database::error::DatabaseError;
    23  use database::lmdb::LmdbDatabase;
    24  
    25  pub struct Blockstore<'a> {
    26      db: LmdbDatabase<'a>,
    27  }
    28  
    29  impl<'a> Blockstore<'a> {
    30      pub fn new(db: LmdbDatabase<'a>) -> Self {
    31          Blockstore { db: db }
    32      }
    33  
    34      pub fn get(&self, block_id: &str) -> Result<Block, DatabaseError> {
    35          let reader = self.db.reader()?;
    36          let packed = reader
    37              .get(&block_id.as_bytes())
    38              .ok_or_else(|| DatabaseError::NotFoundError(format!("Block not found: {}", block_id)))?;
    39          let block: Block = protobuf::parse_from_bytes(&packed).map_err(|err| {
    40              DatabaseError::CorruptionError(format!(
    41                  "Could not interpret stored data as a block: {}",
    42                  err
    43              ))
    44          })?;
    45          Ok(block)
    46      }
    47  
    48      pub fn get_by_height(&self, height: u64) -> Result<Block, DatabaseError> {
    49          let reader = self.db.reader()?;
    50          let block_num = format!("0x{:0>16x}", height);
    51          let block_id = reader
    52              .index_get("index_block_num", &block_num.as_bytes())
    53              .and_then(|block_id| {
    54                  block_id.ok_or_else(|| {
    55                      DatabaseError::NotFoundError(format!("Block not found: {}", height))
    56                  })
    57              })?;
    58          let packed = reader.get(&block_id).ok_or_else(|| {
    59              DatabaseError::CorruptionError(format!("Block not found: {:?}", block_id))
    60          })?;
    61          let block: Block = protobuf::parse_from_bytes(&packed).map_err(|err| {
    62              DatabaseError::CorruptionError(format!(
    63                  "Could not interpret stored data as a block: {}",
    64                  err
    65              ))
    66          })?;
    67          Ok(block)
    68      }
    69  
    70      pub fn get_by_batch(&self, batch_id: &str) -> Result<Block, DatabaseError> {
    71          let reader = self.db.reader()?;
    72          let block_id = reader
    73              .index_get("index_batch", &batch_id.as_bytes())
    74              .and_then(|block_id| {
    75                  block_id.ok_or_else(|| {
    76                      DatabaseError::NotFoundError(format!("Batch not found: {}", batch_id))
    77                  })
    78              })?;
    79          let packed = reader.get(&block_id).ok_or_else(|| {
    80              DatabaseError::CorruptionError(format!("Block not found: {:?}", block_id))
    81          })?;
    82          let block: Block = protobuf::parse_from_bytes(&packed).map_err(|err| {
    83              DatabaseError::CorruptionError(format!(
    84                  "Could not interpret stored data as a block: {}",
    85                  err
    86              ))
    87          })?;
    88          Ok(block)
    89      }
    90  
    91      pub fn get_by_transaction(&self, transaction_id: &str) -> Result<Block, DatabaseError> {
    92          let reader = self.db.reader()?;
    93          let block_id = reader
    94              .index_get("index_transaction", &transaction_id.as_bytes())
    95              .and_then(|block_id| {
    96                  block_id.ok_or_else(|| {
    97                      DatabaseError::NotFoundError(format!(
    98                          "Transaction not found: {}",
    99                          transaction_id
   100                      ))
   101                  })
   102              })?;
   103          let packed = reader.get(&block_id).ok_or_else(|| {
   104              DatabaseError::CorruptionError(format!("Block not found: {:?}", block_id))
   105          })?;
   106          let block: Block = protobuf::parse_from_bytes(&packed).map_err(|err| {
   107              DatabaseError::CorruptionError(format!(
   108                  "Could not interpret stored data as a block: {}",
   109                  err
   110              ))
   111          })?;
   112          Ok(block)
   113      }
   114  
   115      pub fn put(&self, block: &Block) -> Result<(), DatabaseError> {
   116          let block_header: BlockHeader = protobuf::parse_from_bytes(&block.header).map_err(|err| {
   117              DatabaseError::CorruptionError(format!("Invalid block header: {}", err))
   118          })?;
   119          let mut writer = self.db.writer()?;
   120          // Add block to main db
   121          let packed = block.write_to_bytes().map_err(|err| {
   122              DatabaseError::WriterError(format!("Failed to serialize block: {}", err))
   123          })?;
   124          writer.put(&block.header_signature.as_bytes(), &packed)?;
   125  
   126          // Add block to block num index
   127          let block_num_index = format!("0x{:0>16x}", block_header.block_num);
   128          writer.index_put(
   129              "index_block_num",
   130              &block_num_index.as_bytes(),
   131              &block.header_signature.as_bytes(),
   132          )?;
   133  
   134          for batch in block.batches.iter() {
   135              for txn in batch.transactions.iter() {
   136                  writer.index_put(
   137                      "index_transaction",
   138                      &txn.header_signature.as_bytes(),
   139                      &block.header_signature.as_bytes(),
   140                  )?;
   141              }
   142          }
   143  
   144          // Add block to batch index
   145          for batch in block.batches.iter() {
   146              writer.index_put(
   147                  "index_batch",
   148                  &batch.header_signature.as_bytes(),
   149                  &block.header_signature.as_bytes(),
   150              )?;
   151          }
   152  
   153          writer.commit()
   154      }
   155  
   156      pub fn delete(&self, block_id: &str) -> Result<(), DatabaseError> {
   157          let block = self.get(block_id)?;
   158          let block_id = &block.header_signature;
   159          let block_header: BlockHeader = protobuf::parse_from_bytes(&block.header).map_err(|err| {
   160              DatabaseError::CorruptionError(format!("Invalid block header: {}", err))
   161          })?;
   162          // Delete block from main db
   163          let mut writer = self.db.writer()?;
   164          writer.delete(&block_id.as_bytes())?;
   165  
   166          // Delete block from block_num index
   167          let block_num_index = format!("0x{:0>16x}", block_header.block_num);
   168          writer.index_delete("index_block_num", &block_num_index.as_bytes())?;
   169  
   170          // Delete block from transaction index
   171          for batch in block.batches.iter() {
   172              for txn in batch.transactions.iter() {
   173                  writer.index_delete("index_transaction", &txn.header_signature.as_bytes())?;
   174              }
   175          }
   176  
   177          // Delete block from batch index
   178          for batch in block.batches.iter() {
   179              writer.index_delete("index_batch", &batch.header_signature.as_bytes())?;
   180          }
   181          writer.commit()
   182      }
   183  
   184      /// Get the header signature of the highest block in the blockstore.
   185      pub fn get_chain_head(&self) -> Result<String, DatabaseError> {
   186          let reader = self.db.reader()?;
   187          let mut cursor = reader.index_cursor("index_block_num")?;
   188          let (_, val) = cursor
   189              .last()
   190              .ok_or_else(|| DatabaseError::NotFoundError("No chain head".into()))?;
   191          String::from_utf8(val).map_err(|err| {
   192              DatabaseError::CorruptionError(format!("Chain head block id is corrupt: {}", err))
   193          })
   194      }
   195  
   196      // Get the number of blocks
   197      pub fn get_current_height(&self) -> Result<usize, DatabaseError> {
   198          let reader = self.db.reader()?;
   199          reader.count()
   200      }
   201  
   202      pub fn get_transaction_count(&self) -> Result<usize, DatabaseError> {
   203          let reader = self.db.reader()?;
   204          reader.index_count("index_transaction")
   205      }
   206  
   207      pub fn get_batch_count(&self) -> Result<usize, DatabaseError> {
   208          let reader = self.db.reader()?;
   209          reader.index_count("index_batch")
   210      }
   211  }
   212  
   213  #[cfg(test)]
   214  mod tests {
   215      use super::*;
   216      use config;
   217      use database::lmdb::LmdbContext;
   218      use sawtooth_sdk::messages::batch::{Batch, BatchHeader};
   219      use sawtooth_sdk::messages::transaction::Transaction;
   220  
   221      /// Asserts that BLOCKSTORE has a current height of COUNT.
   222      fn assert_current_height(count: usize, blockstore: &Blockstore) {
   223          assert_eq!(blockstore.get_current_height().unwrap(), count,);
   224      }
   225  
   226      /// Asserts that BLOCK has SIGNATURE.
   227      fn assert_header_signature(block: Block, signature: String) {
   228          assert_eq!(block.header_signature, signature,);
   229      }
   230  
   231      /// Asserts that BLOCKSTORE's chain head has SIGNATURE.
   232      fn assert_chain_head(signature: String, blockstore: &Blockstore) {
   233          assert_eq!(blockstore.get_chain_head().unwrap(), signature,);
   234      }
   235  
   236      /// Opens a blockstore and executes its basic operations (adding,
   237      /// deleting, and looking up blocks), making assertions about the
   238      /// blockstore contents at each step.
   239      #[test]
   240      fn test_blockstore() {
   241          let path_config = config::get_path_config();
   242  
   243          let blockstore_path = &path_config.data_dir.join(config::get_blockstore_filename());
   244  
   245          // Set the file size to 10MB, so as to support file systems that do
   246          // not support sparse files.
   247          let ctx = LmdbContext::new(blockstore_path, 3, Some(10 * 1024 * 1024))
   248              .map_err(|err| DatabaseError::InitError(format!("{}", err)))
   249              .unwrap();
   250  
   251          let database = LmdbDatabase::new(
   252              &ctx,
   253              &["index_batch", "index_transaction", "index_block_num"],
   254          ).map_err(|err| DatabaseError::InitError(format!("{}", err)))
   255              .unwrap();
   256  
   257          let blockstore = Blockstore::new(database);
   258  
   259          // The blockstore starts with no blocks.
   260          assert_current_height(0, &blockstore);
   261  
   262          // Add 5 blocks.
   263          for i in 0..5 {
   264              let mut block = Block::new();
   265              block.set_header_signature(format!("block-{}", i));
   266              let mut header = BlockHeader::new();
   267              header.set_block_num(i);
   268              block.set_header(header.write_to_bytes().unwrap());
   269  
   270              blockstore.put(&block).unwrap();
   271  
   272              assert_current_height(i as usize + 1, &blockstore);
   273              assert_chain_head(format!("block-{}", i), &blockstore);
   274          }
   275  
   276          assert_current_height(5, &blockstore);
   277  
   278          // Check that the blocks are in the right order.
   279          for i in 0..5 {
   280              let block = blockstore.get_by_height(i).unwrap();
   281  
   282              assert_header_signature(block, format!("block-{}", i));
   283          }
   284  
   285          // Get a block.
   286          let get_block = blockstore.get("block-2").unwrap();
   287  
   288          assert_header_signature(get_block, String::from("block-2"));
   289  
   290          // Add a block with a batch.
   291          let mut transaction = Transaction::new();
   292          transaction.set_header_signature(String::from("transaction"));
   293  
   294          let mut batch = Batch::new();
   295          batch.set_header_signature(String::from("batch"));
   296          batch.set_transactions(protobuf::RepeatedField::from_vec(vec![transaction]));
   297          let batch_header = BatchHeader::new();
   298          batch.set_header(batch_header.write_to_bytes().unwrap());
   299  
   300          let mut block = Block::new();
   301          block.set_header_signature(String::from("block-with-batch"));
   302          let mut block_header = BlockHeader::new();
   303          block_header.set_block_num(6);
   304          block.set_header(block_header.write_to_bytes().unwrap());
   305          block.set_batches(protobuf::RepeatedField::from_vec(vec![batch]));
   306  
   307          blockstore.put(&block).unwrap();
   308  
   309          assert_current_height(6, &blockstore);
   310          assert_chain_head(String::from("block-with-batch"), &blockstore);
   311  
   312          let get_by_batch = blockstore.get_by_batch("batch").unwrap();
   313  
   314          assert_header_signature(get_by_batch, String::from("block-with-batch"));
   315  
   316          let get_by_transaction = blockstore.get_by_transaction("transaction").unwrap();
   317  
   318          assert_header_signature(get_by_transaction, String::from("block-with-batch"));
   319  
   320          // Delete a block.
   321          blockstore.delete("block-with-batch").unwrap();
   322  
   323          assert_current_height(5, &blockstore);
   324          assert_chain_head(String::from("block-4"), &blockstore);
   325      }
   326  }