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 }