Skip to content

Instantly share code, notes, and snippets.

@fulmicoton
Last active March 5, 2024 06:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fulmicoton/022e70942c5a34d5533558adb1b93a18 to your computer and use it in GitHub Desktop.
Save fulmicoton/022e70942c5a34d5533558adb1b93a18 to your computer and use it in GitHub Desktop.
syntax = "proto3";
package bank;
service Bank {
rpc ApplyTransaction(TransactionRequest) returns (TransactionReply) {}
}
message TransactionRequest {
string from_account = 1;
string to_account = 2;
uint64 amount = 3;
}
message TransactionReply {
}
use std::collections::HashMap;
use tokio::sync::Mutex;
use tonic::transport::Server;
use tracing::info;
tonic::include_proto!("bank");
#[derive(Default)]
pub struct MyBank {
accounts: Mutex<HashMap<String, u64>>,
}
async fn set_account(account_lock: &mut HashMap<String, u64>, account: &str, new_amount: u64) {
info!("SET ACCOUNT: {} -> {}", account, new_amount);
account_lock.insert(account.to_string(), new_amount);
}
#[tonic::async_trait]
impl bank_server::Bank for MyBank {
async fn apply_transaction(
&self,
tonic_request: tonic::Request<TransactionRequest>,
) -> Result<tonic::Response<TransactionReply>, tonic::Status> {
let TransactionRequest {
from_account,
to_account,
amount,
} = tonic_request.into_inner();
if from_account == to_account {
// We choose to return an error here.
return Err(tonic::Status::invalid_argument("from and to accounts are the same"));
}
let mut accounts_lock = self.accounts.lock().await;
// We check this won't overflow first, the borrow checker prevents us
// from holding 2 mutable reference items at the same time but whatever.
let Some(&to_amount) = accounts_lock.get(&to_account) else {
return Err(tonic::Status::invalid_argument("to account does not exist"));
};
let Some(new_to_amount) = to_amount.checked_add(amount) else {
return Err(tonic::Status::invalid_argument("to account would overflow"));
};
let Some(&from_amount) = accounts_lock.get(&from_account) else {
return Err(tonic::Status::invalid_argument("from account does not exist"));
};
let Some(new_from_amount) = from_amount.checked_sub(amount) else {
return Err(tonic::Status::invalid_argument("insufficient founds"));
};
// All clear! Let's run the transaction.
set_account(&mut accounts_lock, &to_account, new_to_amount).await;
set_account(&mut accounts_lock, &from_account, new_from_amount).await;
Ok(tonic::Response::new(TransactionReply {}))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment