Skip to content

Commit 719d4e8

Browse files
committed
Implement Redelegation Action
1 parent 31cdf9d commit 719d4e8

File tree

3 files changed

+382
-1
lines changed

3 files changed

+382
-1
lines changed

core/src/consensus/stake/actions.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const ACTION_TAG_DELEGATE_CCS: u8 = 2;
3131
const ACTION_TAG_REVOKE: u8 = 3;
3232
const ACTION_TAG_SELF_NOMINATE: u8 = 4;
3333
const ACTION_TAG_REPORT_DOUBLE_VOTE: u8 = 5;
34+
const ACTION_TAG_REDELEGATE: u8 = 6;
3435
const ACTION_TAG_CHANGE_PARAMS: u8 = 0xFF;
3536

3637
#[derive(Debug, PartialEq)]
@@ -47,6 +48,11 @@ pub enum Action<M: Message> {
4748
address: Address,
4849
quantity: u64,
4950
},
51+
Redelegate {
52+
prev_delegatee: Address,
53+
next_delegatee: Address,
54+
quantity: u64,
55+
},
5056
SelfNominate {
5157
deposit: u64,
5258
metadata: Bytes,
@@ -79,6 +85,9 @@ impl<M: Message> Action<M> {
7985
Action::Revoke {
8086
..
8187
} => {}
88+
Action::Redelegate {
89+
..
90+
} => {}
8291
Action::SelfNominate {
8392
metadata,
8493
..
@@ -195,6 +204,17 @@ impl<M: Message> Encodable for Action<M> {
195204
} => {
196205
s.begin_list(3).append(&ACTION_TAG_REVOKE).append(address).append(quantity);
197206
}
207+
Action::Redelegate {
208+
prev_delegatee,
209+
next_delegatee,
210+
quantity,
211+
} => {
212+
s.begin_list(4)
213+
.append(&ACTION_TAG_REDELEGATE)
214+
.append(prev_delegatee)
215+
.append(next_delegatee)
216+
.append(quantity);
217+
}
198218
Action::SelfNominate {
199219
deposit,
200220
metadata,
@@ -267,6 +287,20 @@ impl<M: Message> Decodable for Action<M> {
267287
quantity: rlp.val_at(2)?,
268288
})
269289
}
290+
ACTION_TAG_REDELEGATE => {
291+
let item_count = rlp.item_count()?;
292+
if item_count != 4 {
293+
return Err(DecoderError::RlpInvalidLength {
294+
expected: 4,
295+
got: item_count,
296+
})
297+
}
298+
Ok(Action::Redelegate {
299+
prev_delegatee: rlp.val_at(1)?,
300+
next_delegatee: rlp.val_at(2)?,
301+
quantity: rlp.val_at(3)?,
302+
})
303+
}
270304
ACTION_TAG_SELF_NOMINATE => {
271305
let item_count = rlp.item_count()?;
272306
if item_count != 3 {

core/src/consensus/stake/mod.rs

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ impl<M: Message> ActionHandler for Stake<M> {
109109
address,
110110
quantity,
111111
} => revoke(state, fee_payer, &address, quantity),
112+
Action::Redelegate {
113+
prev_delegatee,
114+
next_delegatee,
115+
quantity,
116+
} => redelegate(state, fee_payer, &prev_delegatee, &next_delegatee, quantity),
112117
Action::SelfNominate {
113118
deposit,
114119
metadata,
@@ -226,6 +231,43 @@ fn revoke(state: &mut TopLevelState, fee_payer: &Address, delegatee: &Address, q
226231
Ok(())
227232
}
228233

234+
fn redelegate(
235+
state: &mut TopLevelState,
236+
fee_payer: &Address,
237+
prev_delegatee: &Address,
238+
next_delegatee: &Address,
239+
quantity: u64,
240+
) -> StateResult<()> {
241+
let candidates = Candidates::load_from_state(state)?;
242+
if candidates.get_candidate(next_delegatee).is_none() {
243+
return Err(RuntimeError::FailedToHandleCustomAction("Can delegate to who is a candidate".into()).into())
244+
}
245+
246+
let banned = Banned::load_from_state(state)?;
247+
let jailed = Jail::load_from_state(state)?;
248+
assert!(!banned.is_banned(&next_delegatee), "A candidate must not be banned");
249+
assert_eq!(None, jailed.get_prisoner(next_delegatee), "A candidate must not be jailed");
250+
251+
let delegator = StakeAccount::load_from_state(state, fee_payer)?;
252+
let mut delegation = Delegation::load_from_state(state, &fee_payer)?;
253+
254+
delegation.subtract_quantity(*prev_delegatee, quantity)?;
255+
delegation.add_quantity(*next_delegatee, quantity)?;
256+
257+
delegation.save_to_state(state)?;
258+
delegator.save_to_state(state)?;
259+
260+
ctrace!(
261+
ENGINE,
262+
"Redelegated CCS. delegator: {}, prev_delegatee: {}, next_delegatee: {}, quantity: {}",
263+
fee_payer,
264+
prev_delegatee,
265+
next_delegatee,
266+
quantity
267+
);
268+
Ok(())
269+
}
270+
229271
fn self_nominate(
230272
state: &mut TopLevelState,
231273
fee_payer: &Address,
@@ -916,6 +958,269 @@ mod tests {
916958
assert_eq!(state.action_data(&get_delegation_key(&delegator)).unwrap(), None);
917959
}
918960

961+
#[test]
962+
fn can_redelegate_tokens() {
963+
let prev_delegatee_pubkey = Public::random();
964+
let prev_delegatee = public_to_address(&prev_delegatee_pubkey);
965+
let next_delegatee_pubkey = Public::random();
966+
let next_delegatee = public_to_address(&next_delegatee_pubkey);
967+
let delegator_pubkey = Public::random();
968+
let delegator = public_to_address(&delegator_pubkey);
969+
970+
let mut state = helpers::get_temp_state();
971+
let stake = {
972+
let mut genesis_stakes = HashMap::new();
973+
genesis_stakes.insert(delegator, 100);
974+
Stake::<SoloMessage>::new(genesis_stakes)
975+
};
976+
stake.init(&mut state).unwrap();
977+
self_nominate(&mut state, &prev_delegatee, &prev_delegatee_pubkey, 0, 0, 10, b"".to_vec()).unwrap();
978+
self_nominate(&mut state, &next_delegatee, &next_delegatee_pubkey, 0, 0, 10, b"".to_vec()).unwrap();
979+
980+
let action = Action::<SoloMessage>::DelegateCCS {
981+
address: prev_delegatee,
982+
quantity: 50,
983+
};
984+
let result = stake.execute(&action.rlp_bytes(), &mut state, &delegator, &delegator_pubkey);
985+
assert!(result.is_ok());
986+
987+
let action = Action::<SoloMessage>::Redelegate {
988+
prev_delegatee,
989+
next_delegatee,
990+
quantity: 20,
991+
};
992+
let result = stake.execute(&action.rlp_bytes(), &mut state, &delegator, &delegator_pubkey);
993+
assert_eq!(Ok(()), result);
994+
995+
let delegator_account = StakeAccount::load_from_state(&state, &delegator).unwrap();
996+
let delegation = Delegation::load_from_state(&state, &delegator).unwrap();
997+
assert_eq!(delegator_account.balance, 100 - 50);
998+
assert_eq!(delegation.iter().count(), 2);
999+
assert_eq!(delegation.get_quantity(&prev_delegatee), 50 - 20);
1000+
assert_eq!(delegation.get_quantity(&next_delegatee), 20);
1001+
}
1002+
1003+
#[test]
1004+
fn cannot_redelegate_more_than_delegated_tokens() {
1005+
let prev_delegatee_pubkey = Public::random();
1006+
let prev_delegatee = public_to_address(&prev_delegatee_pubkey);
1007+
let next_delegatee_pubkey = Public::random();
1008+
let next_delegatee = public_to_address(&next_delegatee_pubkey);
1009+
let delegator_pubkey = Public::random();
1010+
let delegator = public_to_address(&delegator_pubkey);
1011+
1012+
let mut state = helpers::get_temp_state();
1013+
let stake = {
1014+
let mut genesis_stakes = HashMap::new();
1015+
genesis_stakes.insert(delegator, 100);
1016+
Stake::<SoloMessage>::new(genesis_stakes)
1017+
};
1018+
stake.init(&mut state).unwrap();
1019+
self_nominate(&mut state, &prev_delegatee, &prev_delegatee_pubkey, 0, 0, 10, b"".to_vec()).unwrap();
1020+
self_nominate(&mut state, &next_delegatee, &next_delegatee_pubkey, 0, 0, 10, b"".to_vec()).unwrap();
1021+
1022+
let action = Action::<SoloMessage>::DelegateCCS {
1023+
address: prev_delegatee,
1024+
quantity: 50,
1025+
};
1026+
let result = stake.execute(&action.rlp_bytes(), &mut state, &delegator, &delegator_pubkey);
1027+
assert!(result.is_ok());
1028+
1029+
let action = Action::<SoloMessage>::Redelegate {
1030+
prev_delegatee,
1031+
next_delegatee,
1032+
quantity: 70,
1033+
};
1034+
let result = stake.execute(&action.rlp_bytes(), &mut state, &delegator, &delegator_pubkey);
1035+
assert!(result.is_err());
1036+
1037+
let delegator_account = StakeAccount::load_from_state(&state, &delegator).unwrap();
1038+
let delegation = Delegation::load_from_state(&state, &delegator).unwrap();
1039+
assert_eq!(delegator_account.balance, 100 - 50);
1040+
assert_eq!(delegation.iter().count(), 1);
1041+
assert_eq!(delegation.get_quantity(&prev_delegatee), 50);
1042+
assert_eq!(delegation.get_quantity(&next_delegatee), 0);
1043+
}
1044+
1045+
#[test]
1046+
fn redelegate_all_should_clear_state() {
1047+
let prev_delegatee_pubkey = Public::random();
1048+
let prev_delegatee = public_to_address(&prev_delegatee_pubkey);
1049+
let next_delegatee_pubkey = Public::random();
1050+
let next_delegatee = public_to_address(&next_delegatee_pubkey);
1051+
let delegator_pubkey = Public::random();
1052+
let delegator = public_to_address(&delegator_pubkey);
1053+
1054+
let mut state = helpers::get_temp_state();
1055+
let stake = {
1056+
let mut genesis_stakes = HashMap::new();
1057+
genesis_stakes.insert(delegator, 100);
1058+
Stake::<SoloMessage>::new(genesis_stakes)
1059+
};
1060+
stake.init(&mut state).unwrap();
1061+
self_nominate(&mut state, &prev_delegatee, &prev_delegatee_pubkey, 0, 0, 10, b"".to_vec()).unwrap();
1062+
self_nominate(&mut state, &next_delegatee, &next_delegatee_pubkey, 0, 0, 10, b"".to_vec()).unwrap();
1063+
1064+
let action = Action::<SoloMessage>::DelegateCCS {
1065+
address: prev_delegatee,
1066+
quantity: 50,
1067+
};
1068+
let result = stake.execute(&action.rlp_bytes(), &mut state, &delegator, &delegator_pubkey);
1069+
assert!(result.is_ok());
1070+
1071+
let action = Action::<SoloMessage>::Redelegate {
1072+
prev_delegatee,
1073+
next_delegatee,
1074+
quantity: 50,
1075+
};
1076+
let result = stake.execute(&action.rlp_bytes(), &mut state, &delegator, &delegator_pubkey);
1077+
assert_eq!(Ok(()), result);
1078+
1079+
let delegator_account = StakeAccount::load_from_state(&state, &delegator).unwrap();
1080+
let delegation = Delegation::load_from_state(&state, &delegator).unwrap();
1081+
assert_eq!(delegator_account.balance, 50);
1082+
assert_eq!(delegation.iter().count(), 1);
1083+
assert_eq!(delegation.get_quantity(&prev_delegatee), 0);
1084+
assert_eq!(delegation.get_quantity(&next_delegatee), 50);
1085+
}
1086+
1087+
#[test]
1088+
fn redelegate_only_to_candidate() {
1089+
let prev_delegatee_pubkey = Public::random();
1090+
let prev_delegatee = public_to_address(&prev_delegatee_pubkey);
1091+
let next_delegatee_pubkey = Public::random();
1092+
let next_delegatee = public_to_address(&next_delegatee_pubkey);
1093+
let delegator_pubkey = Public::random();
1094+
let delegator = public_to_address(&delegator_pubkey);
1095+
1096+
let mut state = helpers::get_temp_state();
1097+
let stake = {
1098+
let mut genesis_stakes = HashMap::new();
1099+
genesis_stakes.insert(delegator, 100);
1100+
Stake::<SoloMessage>::new(genesis_stakes)
1101+
};
1102+
stake.init(&mut state).unwrap();
1103+
1104+
self_nominate(&mut state, &prev_delegatee, &prev_delegatee_pubkey, 0, 0, 10, b"".to_vec()).unwrap();
1105+
1106+
let action = Action::<SoloMessage>::DelegateCCS {
1107+
address: prev_delegatee,
1108+
quantity: 40,
1109+
};
1110+
let result = stake.execute(&action.rlp_bytes(), &mut state, &delegator, &delegator_pubkey);
1111+
assert!(result.is_ok());
1112+
1113+
let action = Action::<SoloMessage>::Redelegate {
1114+
prev_delegatee,
1115+
next_delegatee,
1116+
quantity: 50,
1117+
};
1118+
let result = stake.execute(&action.rlp_bytes(), &mut state, &delegator, &delegator_pubkey);
1119+
assert!(result.is_err());
1120+
}
1121+
1122+
#[test]
1123+
fn cannot_redelegate_to_banned_account() {
1124+
let informant_pubkey = Public::random();
1125+
let criminal_pubkey = Public::random();
1126+
let delegator_pubkey = Public::random();
1127+
let criminal = public_to_address(&criminal_pubkey);
1128+
let delegator = public_to_address(&delegator_pubkey);
1129+
let prev_delegatee_pubkey = Public::random();
1130+
let prev_delegatee = public_to_address(&prev_delegatee_pubkey);
1131+
1132+
let mut state = helpers::get_temp_state();
1133+
state.add_balance(&criminal, 1000).unwrap();
1134+
1135+
let stake = {
1136+
let mut genesis_stakes = HashMap::new();
1137+
genesis_stakes.insert(delegator, 100);
1138+
Stake::<SoloMessage>::new(genesis_stakes)
1139+
};
1140+
stake.init(&mut state).unwrap();
1141+
self_nominate(&mut state, &prev_delegatee, &prev_delegatee_pubkey, 0, 0, 10, b"".to_vec()).unwrap();
1142+
self_nominate(&mut state, &criminal, &criminal_pubkey, 100, 0, 10, b"".to_vec()).unwrap();
1143+
1144+
let action = Action::<SoloMessage>::DelegateCCS {
1145+
address: criminal,
1146+
quantity: 40,
1147+
};
1148+
stake.execute(&action.rlp_bytes(), &mut state, &delegator, &delegator_pubkey).unwrap();
1149+
let action = Action::<SoloMessage>::DelegateCCS {
1150+
address: prev_delegatee,
1151+
quantity: 40,
1152+
};
1153+
stake.execute(&action.rlp_bytes(), &mut state, &delegator, &delegator_pubkey).unwrap();
1154+
1155+
let candidates = Candidates::load_from_state(&state).unwrap();
1156+
assert_eq!(candidates.len(), 2);
1157+
1158+
assert_eq!(Ok(()), ban(&mut state, &informant_pubkey, criminal));
1159+
1160+
let banned = Banned::load_from_state(&state).unwrap();
1161+
assert!(banned.is_banned(&criminal));
1162+
1163+
let candidates = Candidates::load_from_state(&state).unwrap();
1164+
assert_eq!(candidates.len(), 1);
1165+
1166+
let action = Action::<SoloMessage>::Redelegate {
1167+
prev_delegatee,
1168+
next_delegatee: criminal,
1169+
quantity: 40,
1170+
};
1171+
let result = stake.execute(&action.rlp_bytes(), &mut state, &delegator, &delegator_pubkey);
1172+
assert!(result.is_err());
1173+
}
1174+
1175+
#[test]
1176+
fn cannot_redelegate_to_jailed_account() {
1177+
let jail_pubkey = Public::random();
1178+
let jail_address = public_to_address(&jail_pubkey);
1179+
let prev_delegatee_pubkey = Public::random();
1180+
let prev_delegatee = public_to_address(&prev_delegatee_pubkey);
1181+
let delegator_pubkey = Public::random();
1182+
let delegator = public_to_address(&delegator_pubkey);
1183+
1184+
let mut state = helpers::get_temp_state();
1185+
state.add_balance(&jail_address, 1000).unwrap();
1186+
1187+
let stake = {
1188+
let mut genesis_stakes = HashMap::new();
1189+
genesis_stakes.insert(delegator, 100);
1190+
Stake::<SoloMessage>::new(genesis_stakes)
1191+
};
1192+
stake.init(&mut state).unwrap();
1193+
self_nominate(&mut state, &prev_delegatee, &prev_delegatee_pubkey, 0, 0, 10, b"".to_vec()).unwrap();
1194+
1195+
let deposit = 200;
1196+
self_nominate(&mut state, &jail_address, &jail_pubkey, deposit, 0, 5, b"".to_vec()).unwrap();
1197+
1198+
let action = Action::<SoloMessage>::DelegateCCS {
1199+
address: prev_delegatee,
1200+
quantity: 40,
1201+
};
1202+
stake.execute(&action.rlp_bytes(), &mut state, &delegator, &delegator_pubkey).unwrap();
1203+
1204+
let candidates = Candidates::load_from_state(&state).unwrap();
1205+
assert_eq!(candidates.len(), 2);
1206+
1207+
let custody_until = 10;
1208+
let released_at = 20;
1209+
let result = jail(&mut state, &[jail_address], custody_until, released_at);
1210+
assert!(result.is_ok());
1211+
1212+
let candidates = Candidates::load_from_state(&state).unwrap();
1213+
assert_eq!(candidates.len(), 1);
1214+
1215+
let action = Action::<SoloMessage>::Redelegate {
1216+
prev_delegatee,
1217+
next_delegatee: jail_address,
1218+
quantity: 40,
1219+
};
1220+
let result = stake.execute(&action.rlp_bytes(), &mut state, &delegator, &delegator_pubkey);
1221+
assert!(result.is_err());
1222+
}
1223+
9191224
#[test]
9201225
fn self_nominate_deposit_test() {
9211226
let address_pubkey = Public::random();

0 commit comments

Comments
 (0)