-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlib.rs
More file actions
105 lines (93 loc) · 3.3 KB
/
Copy pathlib.rs
File metadata and controls
105 lines (93 loc) · 3.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#![no_std]
use soroban_sdk::{contract, contractimpl, contracttype, Address, Env, Map};
mod secure;
#[contracttype]
pub enum DataKey {
Votes,
Balances,
}
#[contract]
pub struct TransferDoubleVote;
#[contractimpl]
impl TransferDoubleVote {
pub fn init(env: Env, voter: Address, balance: i128) {
let mut balances: Map<Address, i128> = env
.storage()
.instance()
.get(&DataKey::Balances)
.unwrap_or(Map::new(&env));
balances.set(voter, balance);
env.storage().instance().set(&DataKey::Balances, &balances);
}
pub fn transfer(env: Env, from: Address, to: Address, amount: i128) {
let mut balances: Map<Address, i128> = env
.storage()
.instance()
.get(&DataKey::Balances)
.unwrap_or(Map::new(&env));
let from_bal = balances.get(from.clone()).unwrap_or(0);
balances.set(from, from_bal - amount);
let to_bal = balances.get(to.clone()).unwrap_or(0);
balances.set(to, to_bal + amount);
env.storage().instance().set(&DataKey::Balances, &balances);
}
/// BUG: live balances allow the same tokens to vote twice.
/// The fixture should make this unsafe path reachable and easy to scan.
pub fn vulnerable_entry(env: Env, actor: Address, amount: i128) {
let _ = amount;
let mut votes: Map<Address, i128> = env
.storage()
.instance()
.get(&DataKey::Votes)
.unwrap_or(Map::new(&env));
let balances: Map<Address, i128> = env
.storage()
.instance()
.get(&DataKey::Balances)
.unwrap_or(Map::new(&env));
// BUG: reads live balance, no snapshot, no has-voted guard
let power = balances.get(actor.clone()).unwrap_or(0);
let current = votes.get(actor.clone()).unwrap_or(0);
votes.set(actor, current + power);
env.storage().instance().set(&DataKey::Votes, &votes);
}
pub fn total_votes(env: Env) -> i128 {
let votes: Map<Address, i128> = env
.storage()
.instance()
.get(&DataKey::Votes)
.unwrap_or(Map::new(&env));
let mut total = 0i128;
for (_, v) in votes.iter() {
total += v;
}
total
}
}
#[cfg(test)]
mod tests {
use super::*;
use soroban_sdk::{testutils::Address as _, Address, Env};
#[test]
fn test_vulnerable_double_vote() {
let env = Env::default();
let contract_id = env.register_contract(None, TransferDoubleVote);
let client = TransferDoubleVoteClient::new(&env, &contract_id);
let alice = Address::generate(&env);
let bob = Address::generate(&env);
client.init(&alice, &100);
client.vulnerable_entry(&alice, &0);
client.transfer(&alice, &bob, &100);
client.vulnerable_entry(&bob, &0);
assert_eq!(client.total_votes(), 200);
}
#[test]
fn test_boundary_zero_balance_no_votes() {
let env = Env::default();
let contract_id = env.register_contract(None, TransferDoubleVote);
let client = TransferDoubleVoteClient::new(&env, &contract_id);
let carol = Address::generate(&env);
client.vulnerable_entry(&carol, &0);
assert_eq!(client.total_votes(), 0);
}
}