Don't process transaction if channel.send() fails.

Do all input validation first, then log (which can fail). If all
goes swimmingly, process the transaction.
This commit is contained in:
Greg Fitzgerald 2018-03-02 10:16:39 -07:00
parent 36bb1f989d
commit dfd1c4eab3
1 changed files with 31 additions and 22 deletions

View File

@ -8,6 +8,16 @@ use historian::Historian;
use ring::signature::Ed25519KeyPair; use ring::signature::Ed25519KeyPair;
use std::sync::mpsc::SendError; use std::sync::mpsc::SendError;
use std::collections::HashMap; use std::collections::HashMap;
use std::result;
#[derive(Debug, PartialEq, Eq)]
pub enum AccountingError {
InsufficientFunds,
InvalidEvent,
SendError,
}
pub type Result<T> = result::Result<T, AccountingError>;
pub struct Accountant { pub struct Accountant {
pub historian: Historian<u64>, pub historian: Historian<u64>,
@ -43,12 +53,13 @@ impl Accountant {
key: PublicKey, key: PublicKey,
data: u64, data: u64,
sig: Signature, sig: Signature,
) -> Result<(), SendError<Event<u64>>> { ) -> Result<()> {
let event = Event::Claim { key, data, sig }; let event = Event::Claim { key, data, sig };
if !self.historian.verify_event(&event) { if !self.historian.verify_event(&event) {
// TODO: Replace the SendError result with a custom one. return Err(AccountingError::InvalidEvent);
println!("Rejecting transaction: Invalid event"); }
return Ok(()); if let Err(SendError(_)) = self.historian.sender.send(event) {
return Err(AccountingError::SendError);
} }
if self.balances.contains_key(&key) { if self.balances.contains_key(&key) {
@ -59,14 +70,10 @@ impl Accountant {
self.balances.insert(key, data); self.balances.insert(key, data);
} }
self.historian.sender.send(event) Ok(())
} }
pub fn deposit( pub fn deposit(self: &mut Self, n: u64, keypair: &Ed25519KeyPair) -> Result<Signature> {
self: &mut Self,
n: u64,
keypair: &Ed25519KeyPair,
) -> Result<Signature, SendError<Event<u64>>> {
use event::{get_pubkey, sign_serialized}; use event::{get_pubkey, sign_serialized};
let key = get_pubkey(keypair); let key = get_pubkey(keypair);
let sig = sign_serialized(&n, keypair); let sig = sign_serialized(&n, keypair);
@ -79,7 +86,11 @@ impl Accountant {
to: PublicKey, to: PublicKey,
data: u64, data: u64,
sig: Signature, sig: Signature,
) -> Result<(), SendError<Event<u64>>> { ) -> Result<()> {
if self.get_balance(&from).unwrap_or(0) < data {
return Err(AccountingError::InsufficientFunds);
}
let event = Event::Transaction { let event = Event::Transaction {
from, from,
to, to,
@ -87,15 +98,10 @@ impl Accountant {
sig, sig,
}; };
if !self.historian.verify_event(&event) { if !self.historian.verify_event(&event) {
// TODO: Replace the SendError result with a custom one. return Err(AccountingError::InvalidEvent);
println!("Rejecting transaction: Invalid event");
return Ok(());
} }
if let Err(SendError(_)) = self.historian.sender.send(event) {
if self.get_balance(&from).unwrap_or(0) < data { return Err(AccountingError::SendError);
// TODO: Replace the SendError result with a custom one.
println!("Rejecting transaction: Insufficient funds");
return Ok(());
} }
if let Some(x) = self.balances.get_mut(&from) { if let Some(x) = self.balances.get_mut(&from) {
@ -110,7 +116,7 @@ impl Accountant {
self.balances.insert(to, data); self.balances.insert(to, data);
} }
self.historian.sender.send(event) Ok(())
} }
pub fn transfer( pub fn transfer(
@ -118,7 +124,7 @@ impl Accountant {
n: u64, n: u64,
keypair: &Ed25519KeyPair, keypair: &Ed25519KeyPair,
to: PublicKey, to: PublicKey,
) -> Result<Signature, SendError<Event<u64>>> { ) -> Result<Signature> {
use event::{get_pubkey, sign_transaction_data}; use event::{get_pubkey, sign_transaction_data};
let from = get_pubkey(keypair); let from = get_pubkey(keypair);
let sig = sign_transaction_data(&n, keypair, &to); let sig = sign_transaction_data(&n, keypair, &to);
@ -190,7 +196,10 @@ mod tests {
acc.wait_on_signature(&sig); acc.wait_on_signature(&sig);
let bob_pubkey = get_pubkey(&bob_keypair); let bob_pubkey = get_pubkey(&bob_keypair);
acc.transfer(10_001, &alice_keypair, bob_pubkey).unwrap(); assert_eq!(
acc.transfer(10_001, &alice_keypair, bob_pubkey),
Err(AccountingError::InsufficientFunds)
);
sleep(Duration::from_millis(30)); sleep(Duration::from_millis(30));
let alice_pubkey = get_pubkey(&alice_keypair); let alice_pubkey = get_pubkey(&alice_keypair);