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  }