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 }