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 }