github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/validator/src/journal/chain_ffi.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  use cpython;
    18  use cpython::{FromPyObject, ObjectProtocol, PyList, PyObject, Python, PythonObject, ToPyObject};
    19  use database::lmdb::LmdbDatabase;
    20  use journal::block_validator::{BlockValidationResult, BlockValidator, ValidationError};
    21  use journal::block_wrapper::BlockWrapper;
    22  use journal::chain::*;
    23  use journal::chain_head_lock::ChainHeadLock;
    24  use py_ffi;
    25  use pylogger;
    26  use state::state_pruning_manager::StatePruningManager;
    27  use std::ffi::CStr;
    28  use std::os::raw::{c_char, c_void};
    29  use std::sync::mpsc::Sender;
    30  use std::thread;
    31  
    32  use batch::Batch;
    33  
    34  use protobuf::Message;
    35  
    36  use proto::transaction_receipt::TransactionReceipt;
    37  
    38  #[repr(u32)]
    39  #[derive(Debug)]
    40  pub enum ErrorCode {
    41      Success = 0,
    42      NullPointerProvided = 0x01,
    43      InvalidDataDir = 0x02,
    44      InvalidPythonObject = 0x03,
    45      InvalidBlockId = 0x04,
    46  
    47      Unknown = 0xff,
    48  }
    49  
    50  macro_rules! check_null {
    51      ($($arg:expr) , *) => {
    52          $(if $arg.is_null() { return ErrorCode::NullPointerProvided; })*
    53      }
    54  }
    55  
    56  #[no_mangle]
    57  pub extern "C" fn chain_controller_new(
    58      block_store: *mut py_ffi::PyObject,
    59      block_cache: *mut py_ffi::PyObject,
    60      block_validator: *mut py_ffi::PyObject,
    61      state_database: *const c_void,
    62      chain_head_lock: *const c_void,
    63      observers: *mut py_ffi::PyObject,
    64      state_pruning_block_depth: u32,
    65      data_directory: *const c_char,
    66      chain_controller_ptr: *mut *const c_void,
    67  ) -> ErrorCode {
    68      check_null!(
    69          block_store,
    70          block_cache,
    71          block_validator,
    72          state_database,
    73          chain_head_lock,
    74          observers,
    75          data_directory
    76      );
    77  
    78      let data_dir = unsafe {
    79          match CStr::from_ptr(data_directory).to_str() {
    80              Ok(s) => s,
    81              Err(_) => return ErrorCode::InvalidDataDir,
    82          }
    83      };
    84  
    85      let py = unsafe { Python::assume_gil_acquired() };
    86  
    87      let py_block_store_reader = unsafe { PyObject::from_borrowed_ptr(py, block_store) };
    88      let py_block_store_writer = unsafe { PyObject::from_borrowed_ptr(py, block_store) };
    89      let py_block_cache = unsafe { PyObject::from_borrowed_ptr(py, block_cache) };
    90      let py_block_validator = unsafe { PyObject::from_borrowed_ptr(py, block_validator) };
    91      let py_observers = unsafe { PyObject::from_borrowed_ptr(py, observers) };
    92      let chain_head_lock_ref =
    93          unsafe { (chain_head_lock as *const ChainHeadLock).as_ref().unwrap() };
    94  
    95      let observer_wrappers = if let Ok(py_list) = py_observers.extract::<PyList>(py) {
    96          let mut res: Vec<Box<ChainObserver>> = Vec::with_capacity(py_list.len(py));
    97          py_list
    98              .iter(py)
    99              .for_each(|pyobj| res.push(Box::new(PyChainObserver::new(pyobj))));
   100          res
   101      } else {
   102          return ErrorCode::InvalidPythonObject;
   103      };
   104  
   105      let state_database = unsafe { (*(state_database as *const LmdbDatabase)).clone() };
   106  
   107      let state_pruning_manager = StatePruningManager::new(state_database);
   108  
   109      let chain_controller = ChainController::new(
   110          PyBlockCache::new(py_block_cache),
   111          PyBlockValidator::new(py_block_validator),
   112          PyBlockStore::new(py_block_store_writer),
   113          Box::new(PyBlockStore::new(py_block_store_reader)),
   114          chain_head_lock_ref.clone(),
   115          data_dir.into(),
   116          state_pruning_block_depth,
   117          observer_wrappers,
   118          state_pruning_manager,
   119      );
   120  
   121      unsafe {
   122          *chain_controller_ptr = Box::into_raw(Box::new(chain_controller)) as *const c_void;
   123      }
   124  
   125      ErrorCode::Success
   126  }
   127  
   128  #[no_mangle]
   129  pub extern "C" fn chain_controller_drop(chain_controller: *mut c_void) -> ErrorCode {
   130      check_null!(chain_controller);
   131  
   132      unsafe {
   133          Box::from_raw(
   134              chain_controller as *mut ChainController<PyBlockCache, PyBlockValidator, PyBlockStore>,
   135          )
   136      };
   137      ErrorCode::Success
   138  }
   139  
   140  #[no_mangle]
   141  pub extern "C" fn chain_controller_start(chain_controller: *mut c_void) -> ErrorCode {
   142      check_null!(chain_controller);
   143  
   144      unsafe {
   145          (*(chain_controller as *mut ChainController<PyBlockCache, PyBlockValidator, PyBlockStore>))
   146              .start();
   147      }
   148  
   149      ErrorCode::Success
   150  }
   151  
   152  #[no_mangle]
   153  pub extern "C" fn chain_controller_stop(chain_controller: *mut c_void) -> ErrorCode {
   154      check_null!(chain_controller);
   155  
   156      unsafe {
   157          (*(chain_controller as *mut ChainController<PyBlockCache, PyBlockValidator, PyBlockStore>))
   158              .stop();
   159      }
   160      ErrorCode::Success
   161  }
   162  
   163  #[no_mangle]
   164  pub extern "C" fn chain_controller_has_block(
   165      chain_controller: *mut c_void,
   166      block_id: *const c_char,
   167      result: *mut bool,
   168  ) -> ErrorCode {
   169      check_null!(chain_controller, block_id);
   170  
   171      let block_id = unsafe {
   172          match CStr::from_ptr(block_id).to_str() {
   173              Ok(s) => s,
   174              Err(_) => return ErrorCode::InvalidBlockId,
   175          }
   176      };
   177  
   178      unsafe {
   179          *result = (*(chain_controller
   180              as *mut ChainController<PyBlockCache, PyBlockValidator, PyBlockStore>))
   181              .has_block(block_id);
   182      }
   183  
   184      ErrorCode::Success
   185  }
   186  
   187  #[no_mangle]
   188  pub extern "C" fn chain_controller_queue_block(
   189      chain_controller: *mut c_void,
   190      block: *mut py_ffi::PyObject,
   191  ) -> ErrorCode {
   192      check_null!(chain_controller, block);
   193  
   194      let gil_guard = Python::acquire_gil();
   195      let py = gil_guard.python();
   196  
   197      let block: BlockWrapper = unsafe {
   198          match PyObject::from_borrowed_ptr(py, block).extract(py) {
   199              Ok(val) => val,
   200              Err(py_err) => {
   201                  pylogger::exception(
   202                      py,
   203                      "chain_controller_queue_block: unable to get block",
   204                      py_err,
   205                  );
   206                  return ErrorCode::InvalidPythonObject;
   207              }
   208          }
   209      };
   210      unsafe {
   211          let controller = (*(chain_controller
   212              as *mut ChainController<PyBlockCache, PyBlockValidator, PyBlockStore>))
   213              .light_clone();
   214  
   215          py.allow_threads(move || {
   216              let builder = thread::Builder::new().name("ChainController.queue_block".into());
   217              builder
   218                  .spawn(move || {
   219                      controller.queue_block(block);
   220                  })
   221                  .unwrap()
   222                  .join()
   223                  .unwrap();
   224          });
   225      }
   226  
   227      ErrorCode::Success
   228  }
   229  
   230  /// This is only exposed for the current python tests, it should be removed
   231  /// when proper rust tests are written for the ChainController
   232  #[no_mangle]
   233  pub extern "C" fn chain_controller_on_block_received(
   234      chain_controller: *mut c_void,
   235      block: *mut py_ffi::PyObject,
   236  ) -> ErrorCode {
   237      check_null!(chain_controller, block);
   238  
   239      let gil_guard = Python::acquire_gil();
   240      let py = gil_guard.python();
   241  
   242      let block: BlockWrapper = unsafe {
   243          match PyObject::from_borrowed_ptr(py, block).extract(py) {
   244              Ok(val) => val,
   245              Err(py_err) => {
   246                  pylogger::exception(
   247                      py,
   248                      "chain_controller_on_block_received: unable to get block",
   249                      py_err,
   250                  );
   251                  return ErrorCode::InvalidPythonObject;
   252              }
   253          }
   254      };
   255      unsafe {
   256          let mut controller = (*(chain_controller
   257              as *mut ChainController<PyBlockCache, PyBlockValidator, PyBlockStore>))
   258              .light_clone();
   259  
   260          py.allow_threads(move || {
   261              // A thread has to be spawned here, otherwise, any subsequent attempt to
   262              // re-acquire the GIL and import of python modules will fail.
   263              let builder = thread::Builder::new().name("ChainController.on_block_received".into());
   264              builder
   265                  .spawn(move || match controller.on_block_received(block) {
   266                      Ok(_) => ErrorCode::Success,
   267                      Err(err) => {
   268                          error!("Unable to call on_block_received: {:?}", err);
   269                          ErrorCode::Unknown
   270                      }
   271                  })
   272                  .unwrap()
   273                  .join()
   274                  .unwrap()
   275          })
   276      }
   277  }
   278  
   279  #[no_mangle]
   280  pub extern "C" fn chain_controller_chain_head(
   281      chain_controller: *mut c_void,
   282      block: *mut *const py_ffi::PyObject,
   283  ) -> ErrorCode {
   284      check_null!(chain_controller);
   285      unsafe {
   286          let chain_head = (*(chain_controller
   287              as *mut ChainController<PyBlockCache, PyBlockValidator, PyBlockStore>))
   288              .chain_head();
   289  
   290          let gil_guard = Python::acquire_gil();
   291          let py = gil_guard.python();
   292  
   293          *block = chain_head.to_py_object(py).steal_ptr();
   294      }
   295      ErrorCode::Success
   296  }
   297  
   298  #[no_mangle]
   299  pub extern "C" fn sender_drop(sender: *const c_void) -> ErrorCode {
   300      check_null!(sender);
   301  
   302      unsafe { Box::from_raw(sender as *mut Sender<(bool, BlockValidationResult)>) };
   303  
   304      ErrorCode::Success
   305  }
   306  
   307  #[no_mangle]
   308  pub extern "C" fn sender_send(
   309      sender: *const c_void,
   310      result_tuple: *mut py_ffi::PyObject,
   311  ) -> ErrorCode {
   312      check_null!(sender, result_tuple);
   313  
   314      let gil_guard = Python::acquire_gil();
   315      let py = gil_guard.python();
   316  
   317      let py_result_tuple = unsafe { PyObject::from_borrowed_ptr(py, result_tuple) };
   318      let (can_commit, result): (bool, BlockValidationResult) = py_result_tuple
   319          .extract(py)
   320          .expect("Unable to extract result tuple");
   321  
   322      unsafe {
   323          match (*(sender as *mut Sender<(bool, BlockValidationResult)>)).send((can_commit, result)) {
   324              Ok(_) => ErrorCode::Success,
   325              Err(err) => {
   326                  error!("Unable to send validation result: {:?}", err);
   327                  ErrorCode::Unknown
   328              }
   329          }
   330      }
   331  }
   332  
   333  struct PyBlockCache {
   334      py_block_cache: PyObject,
   335  }
   336  
   337  impl PyBlockCache {
   338      fn new(py_block_cache: PyObject) -> Self {
   339          PyBlockCache { py_block_cache }
   340      }
   341  }
   342  
   343  impl BlockCache for PyBlockCache {
   344      fn contains(&self, block_id: &str) -> bool {
   345          let gil_guard = Python::acquire_gil();
   346          let py = gil_guard.python();
   347  
   348          match self.py_block_cache
   349              .call_method(py, "__contains__", (block_id,), None)
   350          {
   351              Err(py_err) => {
   352                  pylogger::exception(py, "Unable to call __contains__ on BlockCache", py_err);
   353                  false
   354              }
   355              Ok(py_bool) => py_bool.extract(py).expect("Unable to extract boolean"),
   356          }
   357      }
   358  
   359      fn put(&mut self, block: BlockWrapper) {
   360          let gil_guard = Python::acquire_gil();
   361          let py = gil_guard.python();
   362  
   363          match self.py_block_cache
   364              .set_item(py, block.header_signature(), &block)
   365          {
   366              Err(py_err) => {
   367                  pylogger::exception(py, "Unable to call __setitem__ on BlockCache", py_err);
   368                  ()
   369              }
   370              Ok(_) => (),
   371          }
   372      }
   373  
   374      fn get(&self, block_id: &str) -> Option<BlockWrapper> {
   375          let gil_guard = Python::acquire_gil();
   376          let py = gil_guard.python();
   377  
   378          match self.py_block_cache.get_item(py, block_id) {
   379              Err(_) => {
   380                  // This is probably a key error, so we can return none
   381                  None
   382              }
   383              Ok(res) => Some(res.extract(py).expect("Unable to extract block")),
   384          }
   385      }
   386  }
   387  
   388  struct PyBlockValidator {
   389      py_block_validator: PyObject,
   390      ctypes_c_void: PyObject,
   391      py_validation_response_sender: PyObject,
   392      py_callback_maker: PyObject,
   393  }
   394  
   395  impl PyBlockValidator {
   396      fn new(py_block_validator: PyObject) -> Self {
   397          let gil_guard = Python::acquire_gil();
   398          let py = gil_guard.python();
   399  
   400          let ctypes_module = py.import("ctypes").expect("Unable to import ctypes");
   401  
   402          let ctypes_c_void = ctypes_module
   403              .get(py, "c_void_p")
   404              .expect("Unable to get c_void_p");
   405  
   406          let chain_module = py.import("sawtooth_validator.journal.chain")
   407              .expect("Unable to import sawtooth_validator.journal.chain");
   408          let py_validation_response_sender = chain_module
   409              .get(py, "ValidationResponseSender")
   410              .expect("Unable to get ValidationResponseSender");
   411  
   412          let ffi_module = py.import("sawtooth_validator.ffi")
   413              .expect("Unable to import sawtooth_validator.ffi");
   414          let py_callback_maker = ffi_module
   415              .get(py, "python_to_sender_callback")
   416              .expect("Unable to get python_to_sender_callback");
   417  
   418          PyBlockValidator {
   419              py_block_validator,
   420              ctypes_c_void,
   421              py_validation_response_sender,
   422              py_callback_maker,
   423          }
   424      }
   425  
   426      fn query_block_status(&self, fn_name: &str, block_id: &str) -> bool {
   427          let gil_guard = Python::acquire_gil();
   428          let py = gil_guard.python();
   429  
   430          match self.py_block_validator
   431              .call_method(py, fn_name, (block_id,), None)
   432          {
   433              Err(_) => {
   434                  // Presumably a KeyError, so no
   435                  false
   436              }
   437              Ok(py_bool) => py_bool.extract(py).expect("Unable to extract boolean"),
   438          }
   439      }
   440  }
   441  
   442  impl BlockValidator for PyBlockValidator {
   443      fn in_process(&self, block_id: &str) -> bool {
   444          self.query_block_status("in_process", block_id)
   445      }
   446  
   447      fn in_pending(&self, block_id: &str) -> bool {
   448          self.query_block_status("in_pending", block_id)
   449      }
   450  
   451      fn validate_block(&self, block: BlockWrapper) -> Result<(), ValidationError> {
   452          let gil_guard = Python::acquire_gil();
   453          let py = gil_guard.python();
   454  
   455          self.py_block_validator
   456              .call_method(py, "validate_block", (block,), None)
   457              .map(|_| ())
   458              .map_err(|py_err| {
   459                  ValidationError::BlockValidationFailure(py_err.get_type(py).name(py).into_owned())
   460              })
   461      }
   462  
   463      fn submit_blocks_for_verification(
   464          &self,
   465          blocks: &[BlockWrapper],
   466          response_sender: Sender<(bool, BlockValidationResult)>,
   467      ) {
   468          let gil_guard = Python::acquire_gil();
   469          let py = gil_guard.python();
   470  
   471          let sender_ptr = Box::into_raw(Box::new(response_sender)) as u64;
   472  
   473          let sender_c_void = self.ctypes_c_void
   474              .call(py, (sender_ptr,), None)
   475              .expect("unable to create ctypes.c_void_p");
   476  
   477          let py_sender = self.py_validation_response_sender
   478              .call(py, (sender_c_void,), None)
   479              .expect("unable to create ValidationResponseSender");
   480  
   481          let py_callback = self.py_callback_maker
   482              .call(py, (py_sender,), None)
   483              .expect("Unable to create py_callback");
   484  
   485          self.py_block_validator
   486              .call_method(
   487                  py,
   488                  "submit_blocks_for_verification",
   489                  (blocks, py_callback),
   490                  None,
   491              )
   492              .map(|_| ())
   493              .map_err(|py_err| {
   494                  pylogger::exception(py, "Unable to call submit_blocks_for_verification", py_err);
   495                  ()
   496              })
   497              .unwrap_or(());
   498      }
   499  }
   500  
   501  struct PyBlockStore {
   502      py_block_store: PyObject,
   503  }
   504  
   505  impl PyBlockStore {
   506      fn new(py_block_store: PyObject) -> Self {
   507          PyBlockStore { py_block_store }
   508      }
   509  }
   510  
   511  impl ChainWriter for PyBlockStore {
   512      fn update_chain(
   513          &mut self,
   514          new_chain: &[BlockWrapper],
   515          old_chain: &[BlockWrapper],
   516      ) -> Result<(), ChainControllerError> {
   517          let gil_guard = Python::acquire_gil();
   518          let py = gil_guard.python();
   519  
   520          self.py_block_store
   521              .call_method(py, "update_chain", (new_chain, old_chain), None)
   522              .map(|_| ())
   523              .map_err(|py_err| {
   524                  ChainControllerError::ChainUpdateError(format!(
   525                      "An error occurred while executing update_chain: {}",
   526                      py_err.get_type(py).name(py)
   527                  ))
   528              })
   529      }
   530  }
   531  
   532  impl ChainReader for PyBlockStore {
   533      fn chain_head(&self) -> Result<Option<BlockWrapper>, ChainReadError> {
   534          let gil_guard = Python::acquire_gil();
   535          let py = gil_guard.python();
   536  
   537          self.py_block_store
   538              .getattr(py, "chain_head")
   539              .and_then(|result| result.extract(py))
   540              .map_err(|py_err| {
   541                  pylogger::exception(py, "Unable to call block_store.chain_head", py_err);
   542                  ChainReadError::GeneralReadError("Unable to read from python block store".into())
   543              })
   544      }
   545  
   546      fn get_block_by_block_num(
   547          &self,
   548          block_num: u64,
   549      ) -> Result<Option<BlockWrapper>, ChainReadError> {
   550          let gil_guard = Python::acquire_gil();
   551          let py = gil_guard.python();
   552  
   553          self.py_block_store
   554              .call_method(py, "get_block_by_number", (block_num,), None)
   555              .and_then(|result| result.extract(py))
   556              .or_else(|py_err| {
   557                  if py_err.get_type(py).name(py) == "KeyError" {
   558                      Ok(None)
   559                  } else {
   560                      Err(py_err)
   561                  }
   562              })
   563              .map_err(|py_err| {
   564                  pylogger::exception(py, "Unable to call block_store.chain_head", py_err);
   565                  ChainReadError::GeneralReadError("Unable to read from python block store".into())
   566              })
   567      }
   568  
   569      fn count_committed_transactions(&self) -> Result<usize, ChainReadError> {
   570          let gil_guard = Python::acquire_gil();
   571          let py = gil_guard.python();
   572  
   573          self.py_block_store
   574              .call_method(py, "get_transaction_count", cpython::NoArgs, None)
   575              .and_then(|result| result.extract(py))
   576              .map_err(|py_err| {
   577                  pylogger::exception(py, "Unable to call block_store.chain_head", py_err);
   578                  ChainReadError::GeneralReadError("Unable to read from python block store".into())
   579              })
   580      }
   581  }
   582  
   583  struct PyChainObserver {
   584      py_observer: PyObject,
   585  }
   586  
   587  impl PyChainObserver {
   588      fn new(py_observer: PyObject) -> Self {
   589          PyChainObserver { py_observer }
   590      }
   591  }
   592  
   593  impl ChainObserver for PyChainObserver {
   594      fn chain_update(&mut self, block: &BlockWrapper, receipts: &[&TransactionReceipt]) {
   595          let gil_guard = Python::acquire_gil();
   596          let py = gil_guard.python();
   597  
   598          self.py_observer
   599              .call_method(py, "chain_update", (block, receipts), None)
   600              .map(|_| ())
   601              .map_err(|py_err| {
   602                  pylogger::exception(py, "Unable to call observer.chain_update", py_err);
   603                  ()
   604              })
   605              .unwrap_or(())
   606      }
   607  }
   608  
   609  impl ToPyObject for TransactionReceipt {
   610      type ObjectType = PyObject;
   611  
   612      fn to_py_object(&self, py: Python) -> PyObject {
   613          let txn_receipt_protobuf_mod = py.import(
   614              "sawtooth_validator.protobuf.transaction_receipt_pb2",
   615          ).expect("Unable to import transaction_receipt_pb2");
   616          let py_txn_receipt_class = txn_receipt_protobuf_mod
   617              .get(py, "TransactionReceipt")
   618              .expect("Unable to get TransactionReceipt");
   619  
   620          let py_txn_receipt = py_txn_receipt_class
   621              .call(py, cpython::NoArgs, None)
   622              .expect("Unable to instantiate TransactionReceipt");
   623          py_txn_receipt
   624              .call_method(
   625                  py,
   626                  "ParseFromString",
   627                  (cpython::PyBytes::new(py, &self.write_to_bytes().unwrap()).into_object(),),
   628                  None,
   629              )
   630              .expect("Unable to ParseFromString");
   631  
   632          py_txn_receipt
   633      }
   634  }
   635  
   636  impl<'source> FromPyObject<'source> for BlockValidationResult {
   637      fn extract(py: Python, obj: &'source PyObject) -> cpython::PyResult<Self> {
   638          Ok(BlockValidationResult {
   639              chain_head: obj.getattr(py, "chain_head")?.extract(py)?,
   640              block: obj.getattr(py, "block")?.extract(py)?,
   641              transaction_count: obj.getattr(py, "transaction_count")?.extract(py)?,
   642              new_chain: obj.getattr(py, "new_chain")?.extract(py)?,
   643              current_chain: obj.getattr(py, "current_chain")?.extract(py)?,
   644  
   645              committed_batches: obj.getattr(py, "committed_batches")?.extract(py)?,
   646              uncommitted_batches: obj.getattr(py, "uncommitted_batches")?.extract(py)?,
   647          })
   648      }
   649  }