github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/validator/src/journal/candidate_block.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::HashSet; 19 20 use cpython; 21 use cpython::ObjectProtocol; 22 use cpython::PyClone; 23 use cpython::Python; 24 use cpython::ToPyObject; 25 26 use batch::Batch; 27 use transaction::Transaction; 28 29 use journal::chain_commit_state::TransactionCommitCache; 30 use journal::validation_rule_enforcer; 31 32 use pylogger; 33 34 use scheduler::Scheduler; 35 36 pub enum CandidateBlockError { 37 ConsensusNotReady, 38 NoPendingBatchesRemaining, 39 } 40 41 pub struct FinalizeBlockResult { 42 pub block: Option<cpython::PyObject>, 43 pub remaining_batches: Vec<Batch>, 44 pub last_batch: Batch, 45 pub injected_batch_ids: Vec<String>, 46 } 47 48 pub struct CandidateBlock { 49 block_store: cpython::PyObject, 50 consensus: cpython::PyObject, 51 scheduler: Box<Scheduler>, 52 max_batches: usize, 53 block_builder: cpython::PyObject, 54 batch_injectors: Vec<cpython::PyObject>, 55 identity_signer: cpython::PyObject, 56 settings_view: cpython::PyObject, 57 58 pending_batches: Vec<Batch>, 59 pending_batch_ids: HashSet<String>, 60 injected_batch_ids: HashSet<String>, 61 62 committed_txn_cache: TransactionCommitCache, 63 } 64 65 impl CandidateBlock { 66 pub fn new( 67 block_store: cpython::PyObject, 68 consensus: cpython::PyObject, 69 scheduler: Box<Scheduler>, 70 committed_txn_cache: TransactionCommitCache, 71 block_builder: cpython::PyObject, 72 max_batches: usize, 73 batch_injectors: Vec<cpython::PyObject>, 74 identity_signer: cpython::PyObject, 75 settings_view: cpython::PyObject, 76 ) -> Self { 77 CandidateBlock { 78 block_store, 79 consensus, 80 scheduler, 81 max_batches, 82 committed_txn_cache, 83 block_builder, 84 batch_injectors, 85 identity_signer, 86 settings_view, 87 pending_batches: vec![], 88 pending_batch_ids: HashSet::new(), 89 injected_batch_ids: HashSet::new(), 90 } 91 } 92 93 pub fn cancel(&mut self) { 94 self.scheduler.cancel().unwrap(); 95 } 96 97 pub fn previous_block_id(&self) -> String { 98 let gil = cpython::Python::acquire_gil(); 99 let py = gil.python(); 100 self.block_builder 101 .getattr(py, "previous_block_id") 102 .expect("BlockBuilder has no attribute 'previous_block_id'") 103 .extract::<String>(py) 104 .unwrap() 105 } 106 107 pub fn last_batch(&self) -> Option<&Batch> { 108 self.pending_batches.last() 109 } 110 111 pub fn can_add_batch(&self) -> bool { 112 self.max_batches == 0 || self.pending_batches.len() < self.max_batches 113 } 114 115 fn check_batch_dependencies_add_batch(&mut self, batch: &Batch) -> bool { 116 for txn in &batch.transactions { 117 if self.txn_is_already_committed(txn, &self.committed_txn_cache) { 118 debug!( 119 "Transaction rejected as it is already in the chain {}", 120 txn.header_signature 121 ); 122 return false; 123 } else if !self.check_transaction_dependencies(txn) { 124 self.committed_txn_cache.remove_batch(batch); 125 return false; 126 } 127 self.committed_txn_cache.add(txn.header_signature.clone()); 128 } 129 true 130 } 131 132 fn check_batch_dependencies( 133 &mut self, 134 batch: &Batch, 135 committed_txn_cache: &mut TransactionCommitCache, 136 ) -> bool { 137 for txn in &batch.transactions { 138 if self.txn_is_already_committed(txn, committed_txn_cache) { 139 debug!( 140 "Transaction rejected as it is already in the chain {}", 141 txn.header_signature 142 ); 143 return false; 144 } else if !self.check_transaction_dependencies(txn) { 145 committed_txn_cache.remove_batch(batch); 146 return false; 147 } 148 committed_txn_cache.add(txn.header_signature.clone()); 149 } 150 true 151 } 152 153 fn check_transaction_dependencies(&self, txn: &Transaction) -> bool { 154 for dep in &txn.dependencies { 155 if !self.committed_txn_cache.contains(dep.as_str()) { 156 debug!( 157 "Transaction rejected due to missing dependency, transaction {} depends on {}", 158 txn.header_signature.as_str(), 159 dep.as_str() 160 ); 161 return false; 162 } 163 } 164 true 165 } 166 167 fn txn_is_already_committed( 168 &self, 169 txn: &Transaction, 170 committed_txn_cache: &TransactionCommitCache, 171 ) -> bool { 172 committed_txn_cache.contains(txn.header_signature.as_str()) || { 173 let gil = cpython::Python::acquire_gil(); 174 let py = gil.python(); 175 self.block_store 176 .call_method( 177 py, 178 "has_transaction", 179 (txn.header_signature.as_str(),), 180 None, 181 ) 182 .expect("Blockstore has no method 'has_batch'") 183 .extract::<bool>(py) 184 .unwrap() 185 } 186 } 187 188 fn batch_is_already_committed(&self, batch: &Batch) -> bool { 189 self.pending_batch_ids 190 .contains(batch.header_signature.as_str()) || { 191 let gil = cpython::Python::acquire_gil(); 192 let py = gil.python(); 193 self.block_store 194 .call_method(py, "has_batch", (batch.header_signature.as_str(),), None) 195 .expect("Blockstore has no method 'has_batch'") 196 .extract::<bool>(py) 197 .unwrap() 198 } 199 } 200 201 fn poll_injectors<F: Fn(&cpython::PyObject) -> Vec<cpython::PyObject>>( 202 &mut self, 203 poller: F, 204 ) -> Vec<Batch> { 205 let mut batches = vec![]; 206 let gil = Python::acquire_gil(); 207 let py = gil.python(); 208 for injector in self.batch_injectors.iter() { 209 let inject_list = poller(injector); 210 if !inject_list.is_empty() { 211 for b in inject_list { 212 match b.extract::<Batch>(py) { 213 Ok(b) => { 214 self.injected_batch_ids.insert(b.header_signature.clone()); 215 batches.push(b); 216 } 217 Err(err) => pylogger::exception(py, "During batch injection", err), 218 } 219 } 220 } 221 } 222 batches 223 } 224 225 pub fn add_batch(&mut self, batch: Batch) { 226 let batch_header_signature = batch.header_signature.clone(); 227 228 if batch.trace { 229 debug!( 230 "TRACE {}: {}", 231 batch_header_signature.as_str(), 232 "CandidateBlock, add_batch" 233 ); 234 } 235 236 if self.batch_is_already_committed(&batch) { 237 debug!( 238 "Dropping previously committed batch: {}", 239 batch_header_signature.as_str() 240 ); 241 return; 242 } else if self.check_batch_dependencies_add_batch(&batch) { 243 let mut batches_to_add = vec![]; 244 245 // Inject blocks at the beginning of a Candidate Block 246 let previous_block_id = self.previous_block_id(); 247 if self.pending_batches.is_empty() { 248 let mut injected_batches = self.poll_injectors(|injector: &cpython::PyObject| { 249 let gil = cpython::Python::acquire_gil(); 250 let py = gil.python(); 251 match injector 252 .call_method(py, "block_start", (previous_block_id.as_str(),), None) 253 .expect("BlockInjector has not method 'block_start'") 254 .extract::<cpython::PyList>(py) 255 { 256 Ok(injected) => injected.iter(py).collect(), 257 Err(err) => { 258 pylogger::exception( 259 py, 260 "During block injection, calling block_start", 261 err, 262 ); 263 vec![] 264 } 265 } 266 }); 267 batches_to_add.append(&mut injected_batches); 268 } 269 270 batches_to_add.push(batch); 271 272 { 273 let batches_to_test = self.pending_batches 274 .iter() 275 .chain(batches_to_add.iter()) 276 .collect::<Vec<_>>(); 277 if !validation_rule_enforcer::enforce_validation_rules( 278 &self.settings_view, 279 &self.get_signer_public_key_hex(), 280 &batches_to_test, 281 ) { 282 return; 283 } 284 } 285 286 for b in batches_to_add { 287 let batch_id = b.header_signature.clone(); 288 self.pending_batches.push(b.clone()); 289 self.pending_batch_ids.insert(batch_id.clone()); 290 291 let injected = self.injected_batch_ids.contains(batch_id.as_str()); 292 293 self.scheduler.add_batch(b, None, injected).unwrap() 294 } 295 } else { 296 debug!( 297 "Dropping batch due to missing dependencies: {}", 298 batch_header_signature.as_str() 299 ); 300 } 301 } 302 303 fn get_signer_public_key_hex(&self) -> String { 304 let gil = cpython::Python::acquire_gil(); 305 let py = gil.python(); 306 307 self.identity_signer 308 .call_method(py, "get_public_key", cpython::NoArgs, None) 309 .expect("IdentitySigner has no method 'get_public_key'") 310 .call_method(py, "as_hex", cpython::NoArgs, None) 311 .expect("PublicKey has no method 'as_hex'") 312 .extract(py) 313 .expect("Unable to convert python string to rust") 314 } 315 316 pub fn sign_block(&self, block_builder: &cpython::PyObject) { 317 let gil = cpython::Python::acquire_gil(); 318 let py = gil.python(); 319 let header_bytes = block_builder 320 .getattr(py, "block_header") 321 .expect("BlockBuilder has no attribute 'block_header'") 322 .call_method(py, "SerializeToString", cpython::NoArgs, None) 323 .unwrap(); 324 let signature = self.identity_signer 325 .call_method(py, "sign", (header_bytes,), None) 326 .expect("Signer has no method 'sign'"); 327 block_builder 328 .call_method(py, "set_signature", (signature,), None) 329 .expect("BlockBuilder has no method 'set_signature'"); 330 } 331 332 fn check_publish_block(&self, py: cpython::Python, block_builder: &cpython::PyObject) -> bool { 333 self.consensus 334 .call_method( 335 py, 336 "check_publish_block", 337 (block_builder 338 .getattr(py, "block_header") 339 .expect("BlockBuilder has no attribute 'block_header'"),), 340 None, 341 ) 342 .expect("consensus has no method 'check_publish_block'") 343 .extract::<bool>(py) 344 .unwrap() 345 } 346 347 pub fn finalize(&mut self, force: bool) -> Result<FinalizeBlockResult, CandidateBlockError> { 348 if !(force || !self.pending_batches.is_empty()) { 349 return Err(CandidateBlockError::NoPendingBatchesRemaining); 350 } 351 { 352 let gil = cpython::Python::acquire_gil(); 353 let py = gil.python(); 354 355 if !self.check_publish_block(py, &self.block_builder) { 356 return Err(CandidateBlockError::ConsensusNotReady); 357 } 358 } 359 360 self.scheduler.finalize(true).unwrap(); 361 let execution_results = self.scheduler.complete(true).unwrap().unwrap(); 362 363 let mut committed_txn_cache = { 364 let gil = cpython::Python::acquire_gil(); 365 let py = gil.python(); 366 TransactionCommitCache::new(self.block_store.clone_ref(py)) 367 }; 368 369 let batches_w_no_results: Vec<String> = execution_results 370 .batch_results 371 .iter() 372 .filter(|(_, txns)| txns.is_none()) 373 .map(|(batch_id, _)| batch_id.clone()) 374 .collect(); 375 376 let valid_batch_ids: Vec<String> = execution_results 377 .batch_results 378 .into_iter() 379 .filter(|(_, txns)| match txns { 380 Some(t) => !t.iter().any(|t| !t.is_valid), 381 None => false, 382 }) 383 .map(|(b_id, _)| b_id) 384 .collect(); 385 386 let builder = { 387 let gil = Python::acquire_gil(); 388 let py = gil.python(); 389 self.block_builder.clone_ref(py) 390 }; 391 392 let mut bad_batches = vec![]; 393 let mut pending_batches = vec![]; 394 395 for batch in self.pending_batches.clone() { 396 let header_signature = &batch.header_signature.clone(); 397 if batch.trace { 398 debug!("TRACE {} : CandidateBlock finalize", header_signature) 399 } 400 401 if batches_w_no_results.contains(&batch.header_signature) { 402 if !self.injected_batch_ids 403 .contains(batch.header_signature.as_str()) 404 { 405 pending_batches.push(batch) 406 } else { 407 warn! { 408 "Failed to inject batch {}", 409 header_signature 410 }; 411 } 412 } else if valid_batch_ids.contains(&batch.header_signature) { 413 if !self.check_batch_dependencies(&batch, &mut committed_txn_cache) { 414 debug!( 415 "Batch {} is invalid, due to missing txn dependency", 416 header_signature 417 ); 418 bad_batches.push(batch.clone()); 419 pending_batches.clear(); 420 pending_batches.append(&mut self.pending_batches 421 .clone() 422 .into_iter() 423 .filter(|b| !bad_batches.contains(b)) 424 .collect()); 425 return self.build_result(None, pending_batches); 426 } else { 427 let gil = Python::acquire_gil(); 428 let py = gil.python(); 429 builder 430 .call_method(py, "add_batch", (batch.clone(),), None) 431 .expect("BlockBuilder has no method 'add_batch'"); 432 committed_txn_cache.add_batch(&batch.clone()); 433 } 434 } else { 435 bad_batches.push(batch.clone()); 436 debug!("Batch {} invalid, not added to block", header_signature); 437 } 438 } 439 if execution_results.ending_state_hash.is_none() || self.no_batches_added(&builder) { 440 debug!("Abandoning block, no batches added"); 441 return self.build_result(None, pending_batches); 442 } 443 if !self.consensus_finalize_block(&builder) { 444 debug!("Abandoning block consensus failed to finalize it"); 445 pending_batches.clear(); 446 pending_batches.append(&mut self.pending_batches 447 .clone() 448 .into_iter() 449 .filter(|b| !bad_batches.contains(b)) 450 .collect()); 451 return self.build_result(None, pending_batches); 452 } 453 let gil = cpython::Python::acquire_gil(); 454 let py = gil.python(); 455 builder 456 .call_method( 457 py, 458 "set_state_hash", 459 (execution_results.ending_state_hash.unwrap(),), 460 None, 461 ) 462 .expect("BlockBuilder has no method 'set_state_hash'"); 463 self.sign_block(&builder); 464 465 self.build_result( 466 Some( 467 builder 468 .call_method(py, "build_block", cpython::NoArgs, None) 469 .expect("BlockBuilder has no method 'build_block'"), 470 ), 471 pending_batches, 472 ) 473 } 474 475 fn consensus_finalize_block(&self, builder: &cpython::PyObject) -> bool { 476 let gil = cpython::Python::acquire_gil(); 477 let py = gil.python(); 478 let block_header = builder 479 .getattr(py, "block_header") 480 .expect("BlockBuilder has no attribute 'block_header'"); 481 self.consensus 482 .call_method(py, "finalize_block", (block_header,), None) 483 .expect("Consensus has no method 'finalize_block'") 484 .extract::<bool>(py) 485 .unwrap() 486 } 487 488 fn no_batches_added(&self, builder: &cpython::PyObject) -> bool { 489 let gil = cpython::Python::acquire_gil(); 490 let py = gil.python(); 491 builder 492 .getattr(py, "batches") 493 .expect("BlockBuilder has no attribute 'batches'") 494 .extract::<cpython::PyList>(py) 495 .unwrap() 496 .len(py) == 0 497 } 498 499 fn build_result( 500 &self, 501 block: Option<cpython::PyObject>, 502 remaining: Vec<Batch>, 503 ) -> Result<FinalizeBlockResult, CandidateBlockError> { 504 if let Some(last_batch) = self.last_batch().cloned() { 505 Ok(FinalizeBlockResult { 506 block, 507 remaining_batches: remaining, 508 last_batch, 509 injected_batch_ids: self.injected_batch_ids 510 .clone() 511 .into_iter() 512 .collect::<Vec<String>>(), 513 }) 514 } else { 515 Err(CandidateBlockError::NoPendingBatchesRemaining) 516 } 517 } 518 }