github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/families/smallbank/smallbank_rust/src/handler.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::HashMap;
    19  
    20  use crypto::digest::Digest;
    21  use crypto::sha2::Sha512;
    22  use protobuf;
    23  
    24  use sawtooth_sdk::messages::processor::TpProcessRequest;
    25  use sawtooth_sdk::processor::handler::ApplyError;
    26  use sawtooth_sdk::processor::handler::TransactionContext;
    27  use sawtooth_sdk::processor::handler::TransactionHandler;
    28  
    29  use smallbank::{Account, SmallbankTransactionPayload,
    30                  SmallbankTransactionPayload_AmalgamateTransactionData,
    31                  SmallbankTransactionPayload_CreateAccountTransactionData,
    32                  SmallbankTransactionPayload_DepositCheckingTransactionData,
    33                  SmallbankTransactionPayload_PayloadType,
    34                  SmallbankTransactionPayload_SendPaymentTransactionData,
    35                  SmallbankTransactionPayload_TransactSavingsTransactionData,
    36                  SmallbankTransactionPayload_WriteCheckTransactionData};
    37  
    38  pub struct SmallbankTransactionHandler {
    39      family_name: String,
    40      family_versions: Vec<String>,
    41      namespaces: Vec<String>,
    42  }
    43  
    44  impl SmallbankTransactionHandler {
    45      pub fn new() -> SmallbankTransactionHandler {
    46          SmallbankTransactionHandler {
    47              family_name: "smallbank".to_string(),
    48              family_versions: vec!["1.0".to_string()],
    49              namespaces: vec![get_smallbank_prefix().to_string()],
    50          }
    51      }
    52  }
    53  
    54  impl TransactionHandler for SmallbankTransactionHandler {
    55      fn family_name(&self) -> String {
    56          self.family_name.clone()
    57      }
    58  
    59      fn family_versions(&self) -> Vec<String> {
    60          self.family_versions.clone()
    61      }
    62  
    63      fn namespaces(&self) -> Vec<String> {
    64          self.namespaces.clone()
    65      }
    66  
    67      fn apply(
    68          &self,
    69          request: &TpProcessRequest,
    70          context: &mut TransactionContext,
    71      ) -> Result<(), ApplyError> {
    72          let mut payload = unpack_payload(request.get_payload())?;
    73          debug!(
    74              "Smallbank txn {}: type {:?}",
    75              request.get_signature(),
    76              payload.get_payload_type()
    77          );
    78  
    79          match payload.get_payload_type() {
    80              SmallbankTransactionPayload_PayloadType::CREATE_ACCOUNT => {
    81                  apply_create_account(payload.take_create_account(), context)
    82              }
    83              SmallbankTransactionPayload_PayloadType::DEPOSIT_CHECKING => {
    84                  apply_deposit_checking(&payload.take_deposit_checking(), context)
    85              }
    86              SmallbankTransactionPayload_PayloadType::WRITE_CHECK => {
    87                  apply_write_check(&payload.take_write_check(), context)
    88              }
    89              SmallbankTransactionPayload_PayloadType::TRANSACT_SAVINGS => {
    90                  apply_transact_savings(&payload.take_transact_savings(), context)
    91              }
    92              SmallbankTransactionPayload_PayloadType::SEND_PAYMENT => {
    93                  apply_send_payment(&payload.take_send_payment(), context)
    94              }
    95              SmallbankTransactionPayload_PayloadType::AMALGAMATE => {
    96                  apply_amalgamate(&payload.take_amalgamate(), context)
    97              }
    98              SmallbankTransactionPayload_PayloadType::PAYLOAD_TYPE_UNSET => Err(
    99                  ApplyError::InvalidTransaction(String::from("Transaction type unset")),
   100              ),
   101          }
   102      }
   103  }
   104  
   105  fn unpack_payload(payload: &[u8]) -> Result<SmallbankTransactionPayload, ApplyError> {
   106      protobuf::parse_from_bytes(&payload).map_err(|err| {
   107          warn!(
   108              "Invalid transaction: Failed to unmarshal SmallbankTransaction: {:?}",
   109              err
   110          );
   111          ApplyError::InvalidTransaction(format!(
   112              "Failed to unmarshal SmallbankTransaction: {:?}",
   113              err
   114          ))
   115      })
   116  }
   117  
   118  fn apply_create_account(
   119      mut create_account_data: SmallbankTransactionPayload_CreateAccountTransactionData,
   120      context: &mut TransactionContext,
   121  ) -> Result<(), ApplyError> {
   122      match load_account(create_account_data.get_customer_id(), context)? {
   123          Some(_) => {
   124              warn!("Invalid transaction: during CREATE_ACCOUNT, Customer Name must be set");
   125              Err(ApplyError::InvalidTransaction(format!(
   126                  "Customer Name must be set"
   127              )))
   128          }
   129          None => {
   130              if create_account_data.get_customer_name().is_empty() {
   131                  warn!("Invalid transaction: during CREATE_ACCOUNT, Customer Name must be set");
   132                  Err(ApplyError::InvalidTransaction(format!(
   133                      "Customer Name must be set"
   134                  )))
   135              } else {
   136                  let mut new_account = Account::new();
   137                  new_account.set_customer_id(create_account_data.get_customer_id());
   138                  new_account.set_customer_name(create_account_data.take_customer_name());
   139                  new_account.set_savings_balance(create_account_data.get_initial_savings_balance());
   140                  new_account
   141                      .set_checking_balance(create_account_data.get_initial_checking_balance());
   142                  save_account(&new_account, context)
   143              }
   144          }
   145      }
   146  }
   147  
   148  fn apply_deposit_checking(
   149      deposit_checking_data: &SmallbankTransactionPayload_DepositCheckingTransactionData,
   150      context: &mut TransactionContext,
   151  ) -> Result<(), ApplyError> {
   152      match load_account(deposit_checking_data.get_customer_id(), context)? {
   153          None => {
   154              warn!("Invalid transaction: during DEPOSIT_CHECKING, Account must exist");
   155              Err(ApplyError::InvalidTransaction(format!(
   156                  "Account must exist"
   157              )))
   158          }
   159          Some(mut account) => {
   160              let balance = account.get_checking_balance() + deposit_checking_data.get_amount();
   161              account.set_checking_balance(balance);
   162              save_account(&account, context)
   163          }
   164      }
   165  }
   166  
   167  fn apply_write_check(
   168      write_check_data: &SmallbankTransactionPayload_WriteCheckTransactionData,
   169      context: &mut TransactionContext,
   170  ) -> Result<(), ApplyError> {
   171      match load_account(write_check_data.get_customer_id(), context)? {
   172          None => {
   173              warn!("Invalid transaction: during WRITE_CHECK, Account must exist");
   174              Err(ApplyError::InvalidTransaction(format!(
   175                  "Account must exist"
   176              )))
   177          }
   178          Some(mut account) => {
   179              let balance = account.get_checking_balance() - write_check_data.get_amount();
   180              account.set_checking_balance(balance);
   181              save_account(&account, context)
   182          }
   183      }
   184  }
   185  
   186  fn apply_transact_savings(
   187      transact_savings_data: &SmallbankTransactionPayload_TransactSavingsTransactionData,
   188      context: &mut TransactionContext,
   189  ) -> Result<(), ApplyError> {
   190      match load_account(transact_savings_data.get_customer_id(), context)? {
   191          None => {
   192              warn!("Invalid transaction: during TRANSACT_SAVINGS, Account must exist");
   193              Err(ApplyError::InvalidTransaction(format!(
   194                  "Account must exist"
   195              )))
   196          }
   197          Some(mut account) => {
   198              if transact_savings_data.get_amount() < 0
   199                  && (-transact_savings_data.get_amount() as u32) > account.get_savings_balance()
   200              {
   201                  warn!("Invalid transaction: during TRANSACT_SAVINGS, Insufficient funds in source savings account");
   202                  return Err(ApplyError::InvalidTransaction(format!(
   203                      "Insufficient funds in source savings account"
   204                  )));
   205              }
   206  
   207              let balance = {
   208                  if transact_savings_data.get_amount() < 0 {
   209                      account.get_savings_balance() - (-transact_savings_data.get_amount() as u32)
   210                  } else {
   211                      account.get_savings_balance() + (transact_savings_data.get_amount() as u32)
   212                  }
   213              };
   214  
   215              account.set_savings_balance(balance);
   216              save_account(&account, context)
   217          }
   218      }
   219  }
   220  
   221  fn apply_send_payment(
   222      send_payment_data: &SmallbankTransactionPayload_SendPaymentTransactionData,
   223      context: &mut TransactionContext,
   224  ) -> Result<(), ApplyError> {
   225      fn err() -> ApplyError {
   226          warn!("Invalid transaction: during SEND_PAYMENT, both source and dest accounts must exist");
   227          ApplyError::InvalidTransaction(String::from("Both source and dest accounts must exist"))
   228      }
   229  
   230      let mut source_account =
   231          load_account(send_payment_data.get_source_customer_id(), context)?.ok_or_else(err)?;
   232      let mut dest_account =
   233          load_account(send_payment_data.get_dest_customer_id(), context)?.ok_or_else(err)?;
   234  
   235      if source_account.get_checking_balance() < send_payment_data.get_amount() {
   236          warn!("Invalid transaction: during SEND_PAYMENT, Insufficient funds in source checking account");
   237          Err(ApplyError::InvalidTransaction(String::from(
   238              "Insufficient funds in source checking account",
   239          )))
   240      } else {
   241          let source_balance = source_account.get_checking_balance() - send_payment_data.get_amount();
   242          source_account.set_checking_balance(source_balance);
   243          let dest_balance = dest_account.get_checking_balance() + send_payment_data.get_amount();
   244          dest_account.set_checking_balance(dest_balance);
   245          save_account(&source_account, context).and(save_account(&dest_account, context))
   246      }
   247  }
   248  
   249  fn apply_amalgamate(
   250      amalgamate_data: &SmallbankTransactionPayload_AmalgamateTransactionData,
   251      context: &mut TransactionContext,
   252  ) -> Result<(), ApplyError> {
   253      fn err() -> ApplyError {
   254          warn!("Invalid transaction: during AMALGAMATE, both source and dest accounts must exist");
   255          ApplyError::InvalidTransaction(String::from("Both source and dest accounts must exist"))
   256      }
   257  
   258      let mut source_account =
   259          load_account(amalgamate_data.get_source_customer_id(), context)?.ok_or_else(err)?;
   260      let mut dest_account =
   261          load_account(amalgamate_data.get_dest_customer_id(), context)?.ok_or_else(err)?;
   262  
   263      let balance = dest_account.get_checking_balance() + source_account.get_savings_balance();
   264      source_account.set_savings_balance(0);
   265      dest_account.set_checking_balance(balance);
   266      save_account(&source_account, context).and(save_account(&dest_account, context))
   267  }
   268  
   269  fn unpack_account(account_data: &[u8]) -> Result<Account, ApplyError> {
   270      protobuf::parse_from_bytes(&account_data).map_err(|err| {
   271          warn!(
   272              "Invalid transaction: Failed to unmarshal Account: {:?}",
   273              err
   274          );
   275          ApplyError::InvalidTransaction(format!("Failed to unmarshal Account: {:?}", err))
   276      })
   277  }
   278  
   279  fn load_account(
   280      customer_id: u32,
   281      context: &mut TransactionContext,
   282  ) -> Result<Option<Account>, ApplyError> {
   283      let response = context
   284          .get_state(vec![create_smallbank_address(&format!("{}", customer_id))])
   285          .map_err(|err| {
   286              warn!("Invalid transaction: Failed to load Account: {:?}", err);
   287              ApplyError::InvalidTransaction(format!("Failed to load Account: {:?}", err))
   288          })?;
   289      match response {
   290          Some(packed) => unpack_account(&packed).map(Some),
   291          None => Ok(None),
   292      }
   293  }
   294  
   295  fn save_account(account: &Account, context: &mut TransactionContext) -> Result<(), ApplyError> {
   296      let address = create_smallbank_address(&format!("{}", account.get_customer_id()));
   297      let data = protobuf::Message::write_to_bytes(account).map_err(|err| {
   298          warn!(
   299              "Invalid transaction: Failed to serialize Account: {:?}",
   300              err
   301          );
   302          ApplyError::InvalidTransaction(format!("Failed to serialize Account: {:?}", err))
   303      })?;
   304  
   305      let mut sets = HashMap::new();
   306      sets.insert(address, data);
   307      context.set_state(sets).map_err(|err| {
   308          warn!("Invalid transaction: Failed to load Account: {:?}", err);
   309          ApplyError::InvalidTransaction(format!("Failed to load Account: {:?}", err))
   310      })
   311  }
   312  
   313  fn get_smallbank_prefix() -> String {
   314      let mut sha = Sha512::new();
   315      sha.input_str("smallbank");
   316      sha.result_str()[..6].to_string()
   317  }
   318  
   319  fn create_smallbank_address(payload: &str) -> String {
   320      let mut sha = Sha512::new();
   321      sha.input(payload.as_bytes());
   322      get_smallbank_prefix() + &sha.result_str()[..64].to_string()
   323  }