Use zero to represent a nonexistent account

This also fixes a bug in the thin client where a nonexistent account
would have triggered a panic because we were using `balances[k]` instead
of `balances.get(key)`.

Fixes #534
This commit is contained in:
Greg Fitzgerald 2018-07-02 17:31:10 -06:00 committed by Greg Fitzgerald
parent d2bb4dc14a
commit 0dabdfd48e
4 changed files with 31 additions and 28 deletions

View File

@ -497,11 +497,11 @@ impl Bank {
self.process_transaction(&tx).map(|_| sig)
}
pub fn get_balance(&self, pubkey: &PublicKey) -> Option<i64> {
pub fn get_balance(&self, pubkey: &PublicKey) -> i64 {
let bals = self.balances
.read()
.expect("'balances' read lock in get_balance");
bals.get(pubkey).map(|x| *x)
bals.get(pubkey).map(|x| *x).unwrap_or(0)
}
pub fn transaction_count(&self) -> usize {
@ -541,11 +541,11 @@ mod tests {
bank.transfer(1_000, &mint.keypair(), pubkey, mint.last_id())
.unwrap();
assert_eq!(bank.get_balance(&pubkey).unwrap(), 1_000);
assert_eq!(bank.get_balance(&pubkey), 1_000);
bank.transfer(500, &mint.keypair(), pubkey, mint.last_id())
.unwrap();
assert_eq!(bank.get_balance(&pubkey).unwrap(), 1_500);
assert_eq!(bank.get_balance(&pubkey), 1_500);
assert_eq!(bank.transaction_count(), 2);
}
@ -588,8 +588,8 @@ mod tests {
assert_eq!(bank.transaction_count(), 1);
let mint_pubkey = mint.keypair().pubkey();
assert_eq!(bank.get_balance(&mint_pubkey).unwrap(), 10_000);
assert_eq!(bank.get_balance(&pubkey).unwrap(), 1_000);
assert_eq!(bank.get_balance(&mint_pubkey), 10_000);
assert_eq!(bank.get_balance(&pubkey), 1_000);
}
#[test]
@ -599,7 +599,7 @@ mod tests {
let pubkey = KeyPair::new().pubkey();
bank.transfer(500, &mint.keypair(), pubkey, mint.last_id())
.unwrap();
assert_eq!(bank.get_balance(&pubkey).unwrap(), 500);
assert_eq!(bank.get_balance(&pubkey), 500);
}
#[test]
@ -612,26 +612,26 @@ mod tests {
.unwrap();
// Mint's balance will be zero because all funds are locked up.
assert_eq!(bank.get_balance(&mint.pubkey()), None);
assert_eq!(bank.get_balance(&mint.pubkey()), 0);
// tx count is 1, because debits were applied.
assert_eq!(bank.transaction_count(), 1);
// pubkey's balance will be None because the funds have not been
// sent.
assert_eq!(bank.get_balance(&pubkey), None);
assert_eq!(bank.get_balance(&pubkey), 0);
// Now, acknowledge the time in the condition occurred and
// that pubkey's funds are now available.
bank.apply_timestamp(mint.pubkey(), dt).unwrap();
assert_eq!(bank.get_balance(&pubkey), Some(1));
assert_eq!(bank.get_balance(&pubkey), 1);
// tx count is still 1, because we chose not to count timestamp transactions
// tx count.
assert_eq!(bank.transaction_count(), 1);
bank.apply_timestamp(mint.pubkey(), dt).unwrap(); // <-- Attack! Attempt to process completed transaction.
assert_ne!(bank.get_balance(&pubkey), Some(2));
assert_ne!(bank.get_balance(&pubkey), 2);
}
#[test]
@ -646,8 +646,8 @@ mod tests {
bank.transfer_on_date(1, &mint.keypair(), pubkey, dt, mint.last_id())
.unwrap();
assert_eq!(bank.get_balance(&mint.pubkey()), None);
assert_eq!(bank.get_balance(&pubkey), Some(1));
assert_eq!(bank.get_balance(&mint.pubkey()), 0);
assert_eq!(bank.get_balance(&pubkey), 1);
}
#[test]
@ -663,22 +663,22 @@ mod tests {
assert_eq!(bank.transaction_count(), 1);
// Mint's balance will be zero because all funds are locked up.
assert_eq!(bank.get_balance(&mint.pubkey()), None);
assert_eq!(bank.get_balance(&mint.pubkey()), 0);
// pubkey's balance will be None because the funds have not been
// sent.
assert_eq!(bank.get_balance(&pubkey), None);
assert_eq!(bank.get_balance(&pubkey), 0);
// Now, cancel the trancaction. Mint gets her funds back, pubkey never sees them.
bank.apply_signature(mint.pubkey(), sig).unwrap();
assert_eq!(bank.get_balance(&mint.pubkey()), Some(1));
assert_eq!(bank.get_balance(&pubkey), None);
assert_eq!(bank.get_balance(&mint.pubkey()), 1);
assert_eq!(bank.get_balance(&pubkey), 0);
// Assert cancel doesn't cause count to go backward.
assert_eq!(bank.transaction_count(), 1);
bank.apply_signature(mint.pubkey(), sig).unwrap(); // <-- Attack! Attempt to cancel completed transaction.
assert_ne!(bank.get_balance(&mint.pubkey()), Some(2));
assert_ne!(bank.get_balance(&mint.pubkey()), 2);
}
#[test]
@ -776,7 +776,7 @@ mod tests {
let genesis = mint.create_entries();
let bank = Bank::default();
bank.process_ledger(genesis).unwrap();
assert_eq!(bank.get_balance(&mint.pubkey()).unwrap(), 1);
assert_eq!(bank.get_balance(&mint.pubkey()), 1);
}
fn create_sample_block(mint: &Mint) -> impl Iterator<Item = Entry> {
@ -797,7 +797,7 @@ mod tests {
let (ledger, pubkey) = create_sample_ledger();
let bank = Bank::default();
bank.process_ledger(ledger).unwrap();
assert_eq!(bank.get_balance(&pubkey).unwrap(), 1);
assert_eq!(bank.get_balance(&pubkey), 1);
}
// Write the given entries to a file and then return a file iterator to them.
@ -819,7 +819,7 @@ mod tests {
let bank = Bank::default();
bank.process_ledger(ledger).unwrap();
assert_eq!(bank.get_balance(&pubkey).unwrap(), 1);
assert_eq!(bank.get_balance(&pubkey), 1);
}
#[test]
@ -830,7 +830,7 @@ mod tests {
let bank = Bank::default();
bank.process_ledger(genesis.chain(block)).unwrap();
assert_eq!(bank.get_balance(&mint.pubkey()).unwrap(), 1);
assert_eq!(bank.get_balance(&mint.pubkey()), 1);
}
}

View File

@ -21,7 +21,7 @@ impl Request {
#[derive(Serialize, Deserialize, Debug)]
pub enum Response {
Balance { key: PublicKey, val: Option<i64> },
Balance { key: PublicKey, val: i64 },
LastId { id: Hash },
TransactionCount { transaction_count: u64 },
SignatureStatus { signature_status: bool },

View File

@ -20,7 +20,7 @@ pub struct ThinClient {
transactions_socket: UdpSocket,
last_id: Option<Hash>,
transaction_count: u64,
balances: HashMap<PublicKey, Option<i64>>,
balances: HashMap<PublicKey, i64>,
signature_status: bool,
}
@ -124,7 +124,10 @@ impl ThinClient {
}
self.process_response(resp);
}
self.balances[pubkey].ok_or(io::Error::new(io::ErrorKind::Other, "nokey"))
self.balances
.get(pubkey)
.map(|x| *x)
.ok_or(io::Error::new(io::ErrorKind::Other, "nokey"))
}
/// Request the transaction count. If the response packet is dropped by the network,
@ -186,7 +189,7 @@ impl ThinClient {
let now = Instant::now();
loop {
balance = self.get_balance(pubkey);
if balance.is_ok() || now.elapsed().as_secs() > 1 {
if balance.is_ok() && *balance.as_ref().unwrap() != 0 || now.elapsed().as_secs() > 1 {
break;
}
}

View File

@ -257,10 +257,10 @@ pub mod tests {
trace!("msg: {:?}", msg);
}
let alice_balance = bank.get_balance(&mint.keypair().pubkey()).unwrap();
let alice_balance = bank.get_balance(&mint.keypair().pubkey());
assert_eq!(alice_balance, alice_ref_balance);
let bob_balance = bank.get_balance(&bob_keypair.pubkey()).unwrap();
let bob_balance = bank.get_balance(&bob_keypair.pubkey());
assert_eq!(bob_balance, starting_balance - alice_ref_balance);
exit.store(true, Ordering::Relaxed);