Skip to content

Commit 468175f

Browse files
committed
Implement ban
1 parent c645fab commit 468175f

File tree

2 files changed

+154
-4
lines changed

2 files changed

+154
-4
lines changed

core/src/consensus/stake/action_data.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ lazy_static! {
3838
pub static ref CANDIDATES_KEY: H256 =
3939
ActionDataKeyBuilder::new(CUSTOM_ACTION_HANDLER_ID, 1).append(&"Candidates").into_key();
4040
pub static ref JAIL_KEY: H256 = ActionDataKeyBuilder::new(CUSTOM_ACTION_HANDLER_ID, 1).append(&"Jail").into_key();
41+
pub static ref BANNED_KEY: H256 =
42+
ActionDataKeyBuilder::new(CUSTOM_ACTION_HANDLER_ID, 1).append(&"Banned").into_key();
4143
}
4244

4345
pub fn get_delegation_key(address: &Address) -> H256 {
@@ -387,6 +389,10 @@ impl Jail {
387389
});
388390
}
389391

392+
pub fn remove(&mut self, address: &Address) {
393+
self.0.remove(address);
394+
}
395+
390396
pub fn try_release(&mut self, address: &Address, term_index: u64) -> ReleaseResult {
391397
match self.0.entry(*address) {
392398
Entry::Occupied(entry) => {
@@ -407,6 +413,34 @@ impl Jail {
407413
}
408414
}
409415

416+
pub struct Banned(BTreeSet<Address>);
417+
impl Banned {
418+
pub fn load_from_state(state: &TopLevelState) -> StateResult<Banned> {
419+
let key = *BANNED_KEY;
420+
let action_data = state.action_data(&key)?;
421+
Ok(Banned(decode_set(action_data.as_ref())))
422+
}
423+
424+
pub fn save_to_state(&self, state: &mut TopLevelState) -> StateResult<()> {
425+
let key = *BANNED_KEY;
426+
if !self.0.is_empty() {
427+
let encoded = encode_set(&self.0);
428+
state.update_action_data(&key, encoded)?;
429+
} else {
430+
state.remove_action_data(&key);
431+
}
432+
Ok(())
433+
}
434+
435+
pub fn add(&mut self, address: Address) {
436+
self.0.insert(address);
437+
}
438+
439+
pub fn is_banned(&self, address: &Address) -> bool {
440+
self.0.contains(address)
441+
}
442+
}
443+
410444
fn decode_set<V>(data: Option<&ActionData>) -> BTreeSet<V>
411445
where
412446
V: Ord + Decodable, {
@@ -1272,4 +1306,30 @@ mod tests {
12721306
let result = state.action_data(&*JAIL_KEY).unwrap();
12731307
assert_eq!(result, None, "Should clean the state if all prisoners are kicked");
12741308
}
1309+
1310+
#[test]
1311+
fn empty_ban_save_clean_state() {
1312+
let mut state = helpers::get_temp_state();
1313+
let banned = Banned::load_from_state(&state).unwrap();
1314+
banned.save_to_state(&mut state).unwrap();
1315+
1316+
let result = state.action_data(&*BANNED_KEY).unwrap();
1317+
assert_eq!(result, None, "Should clean the state if there are no banned accounts");
1318+
}
1319+
1320+
#[test]
1321+
fn added_to_ban_is_banned() {
1322+
let mut state = helpers::get_temp_state();
1323+
1324+
let address = Address::from(1);
1325+
let innocent = Address::from(2);
1326+
1327+
let mut banned = Banned::load_from_state(&state).unwrap();
1328+
banned.add(address);
1329+
banned.save_to_state(&mut state).unwrap();
1330+
1331+
let banned = Banned::load_from_state(&state).unwrap();
1332+
assert!(banned.is_banned(&address));
1333+
assert!(!banned.is_banned(&innocent));
1334+
}
12751335
}

core/src/consensus/stake/mod.rs

Lines changed: 94 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use rlp::{Decodable, UntrustedRlp};
3333
use self::action_data::{Candidates, Delegation, IntermediateRewards, Jail, ReleaseResult, StakeAccount, Stakeholders};
3434
use self::actions::Action;
3535
pub use self::distribute::fee_distribute;
36+
use consensus::stake::action_data::Banned;
3637

3738
const CUSTOM_ACTION_HANDLER_ID: u64 = 2;
3839

@@ -209,16 +210,18 @@ fn transfer_ccs(state: &mut TopLevelState, sender: &Address, receiver: &Address,
209210
}
210211

211212
fn delegate_ccs(state: &mut TopLevelState, sender: &Address, delegatee: &Address, quantity: u64) -> StateResult<()> {
212-
// TODO: remove parent hash from validator set.
213-
// TODO: handle banned account
214-
// TODO: handle jailed account
213+
let banned = Banned::load_from_state(state)?;
214+
if banned.is_banned(&delegatee) {
215+
return Err(RuntimeError::FailedToHandleCustomAction("Delegatee is banned".to_string()).into())
216+
}
215217
let candidates = Candidates::load_from_state(state)?;
216218
let jail = Jail::load_from_state(state)?;
217219
if candidates.get_candidate(delegatee).is_none() && jail.get_prisoner(delegatee).is_none() {
218220
return Err(
219221
RuntimeError::FailedToHandleCustomAction("Can delegate to who is a candidate or a prisoner".into()).into()
220222
)
221223
}
224+
222225
let mut delegator = StakeAccount::load_from_state(state, sender)?;
223226
let mut delegation = Delegation::load_from_state(state, &sender)?;
224227

@@ -253,7 +256,10 @@ fn self_nominate(
253256
) -> StateResult<()> {
254257
// TODO: proper handling of get_current_term
255258
// TODO: proper handling of NOMINATE_EXPIRATION
256-
// TODO: check banned accounts
259+
let blacklist = Banned::load_from_state(state)?;
260+
if blacklist.is_banned(&sender) {
261+
return Err(RuntimeError::FailedToHandleCustomAction("Account is blacklisted".to_string()).into())
262+
}
257263

258264
let mut jail = Jail::load_from_state(&state)?;
259265
let total_deposit = match jail.try_release(sender, current_term) {
@@ -383,6 +389,30 @@ pub fn jail(state: &mut TopLevelState, address: &Address, custody_until: u64, ki
383389
Ok(())
384390
}
385391

392+
#[allow(dead_code)]
393+
pub fn ban(state: &mut TopLevelState, criminal: Address) -> StateResult<()> {
394+
// TODO: remove pending rewards.
395+
// TODO: remove from validators.
396+
// TODO: give criminal's deposits to the informant
397+
// TODO: give criminal's rewards to diligent validators
398+
let mut candidates = Candidates::load_from_state(state)?;
399+
let mut banned = Banned::load_from_state(state)?;
400+
let mut jailed = Jail::load_from_state(state)?;
401+
402+
candidates.remove(&criminal);
403+
jailed.remove(&criminal);
404+
banned.add(criminal);
405+
406+
jailed.save_to_state(state)?;
407+
banned.save_to_state(state)?;
408+
candidates.save_to_state(state)?;
409+
410+
// Revert delegations
411+
revert_delegations(state, &[criminal])?;
412+
413+
Ok(())
414+
}
415+
386416
fn revert_delegations(state: &mut TopLevelState, reverted_delegatees: &[Address]) -> StateResult<()> {
387417
let stakeholders = Stakeholders::load_from_state(state)?;
388418
for stakeholder in stakeholders.iter() {
@@ -1207,4 +1237,64 @@ mod tests {
12071237
let account = StakeAccount::load_from_state(&state, &delegator).unwrap();
12081238
assert_eq!(account.balance, 100 - 40, "Delegation should be preserved");
12091239
}
1240+
1241+
#[test]
1242+
fn test_ban() {
1243+
let criminal = Address::random();
1244+
let delegator = Address::random();
1245+
1246+
let mut state = helpers::get_temp_state();
1247+
state.add_balance(&criminal, 1000).unwrap();
1248+
1249+
let stake = {
1250+
let mut genesis_stakes = HashMap::new();
1251+
genesis_stakes.insert(delegator, 100);
1252+
Stake::new(genesis_stakes)
1253+
};
1254+
stake.init(&mut state).unwrap();
1255+
1256+
self_nominate(&mut state, &criminal, 100, 0, 10).unwrap();
1257+
let action = Action::DelegateCCS {
1258+
address: criminal,
1259+
quantity: 40,
1260+
};
1261+
stake.execute(&action.rlp_bytes(), &mut state, &delegator).unwrap();
1262+
1263+
let result = ban(&mut state, criminal);
1264+
assert!(result.is_ok());
1265+
1266+
let banned = Banned::load_from_state(&state).unwrap();
1267+
assert!(banned.is_banned(&criminal));
1268+
1269+
let candidates = Candidates::load_from_state(&state).unwrap();
1270+
assert_eq!(candidates.len(), 0);
1271+
1272+
assert_eq!(state.balance(&criminal).unwrap(), 900, "Should lose deposit");
1273+
1274+
let delegation = Delegation::load_from_state(&state, &delegator).unwrap();
1275+
assert_eq!(delegation.get_quantity(&criminal), 0, "Delegation should be reverted");
1276+
1277+
let account_delegator = StakeAccount::load_from_state(&state, &delegator).unwrap();
1278+
assert_eq!(account_delegator.balance, 100, "Delegation should be reverted");
1279+
}
1280+
1281+
#[test]
1282+
fn ban_should_remove_prisoner_from_jail() {
1283+
let criminal = Address::random();
1284+
1285+
let mut state = helpers::get_temp_state();
1286+
let stake = Stake::new(HashMap::new());
1287+
stake.init(&mut state).unwrap();
1288+
1289+
self_nominate(&mut state, &criminal, 0, 0, 10).unwrap();
1290+
let custody_until = 10;
1291+
let kicked_at = 20;
1292+
jail(&mut state, &criminal, custody_until, kicked_at).unwrap();
1293+
1294+
let result = ban(&mut state, criminal);
1295+
assert!(result.is_ok());
1296+
1297+
let jail = Jail::load_from_state(&state).unwrap();
1298+
assert_eq!(jail.get_prisoner(&criminal), None, "Should be removed from the jail");
1299+
}
12101300
}

0 commit comments

Comments
 (0)