@@ -115,6 +115,16 @@ impl ActionHandler for Stake {
115115 Err ( RuntimeError :: FailedToHandleCustomAction ( "DelegateCCS is disabled" . to_string ( ) ) . into ( ) )
116116 }
117117 }
118+ Action :: Revoke {
119+ address,
120+ quantity,
121+ } => {
122+ if self . enable_delegations {
123+ revoke ( state, sender, & address, quantity)
124+ } else {
125+ Err ( RuntimeError :: FailedToHandleCustomAction ( "DelegateCCS is disabled" . to_string ( ) ) . into ( ) )
126+ }
127+ }
118128 Action :: ChangeParams {
119129 metadata_seq,
120130 params,
@@ -133,6 +143,9 @@ impl ActionHandler for Stake {
133143 Action :: DelegateCCS {
134144 ..
135145 } => Ok ( ( ) ) ,
146+ Action :: Revoke {
147+ ..
148+ } => Ok ( ( ) ) ,
136149 Action :: ChangeParams {
137150 metadata_seq,
138151 params,
@@ -208,6 +221,19 @@ fn delegate_ccs(
208221 Ok ( ( ) )
209222}
210223
224+ fn revoke ( state : & mut TopLevelState , sender : & Address , delegatee : & Address , quantity : u64 ) -> StateResult < ( ) > {
225+ let mut delegator = StakeAccount :: load_from_state ( state, sender) ?;
226+ let mut delegation = Delegation :: load_from_state ( state, & sender) ?;
227+
228+ delegator. add_balance ( quantity) ?;
229+ delegation. subtract_quantity ( * delegatee, quantity) ?;
230+ // delegation does not touch stakeholders
231+
232+ delegation. save_to_state ( state) ?;
233+ delegator. save_to_state ( state) ?;
234+ Ok ( ( ) )
235+ }
236+
211237pub fn get_stakes ( state : & TopLevelState ) -> StateResult < HashMap < Address , u64 > > {
212238 let stakeholders = Stakeholders :: load_from_state ( state) ?;
213239 let mut result = HashMap :: new ( ) ;
@@ -279,6 +305,7 @@ mod tests {
279305 use super :: * ;
280306
281307 use ckey:: { public_to_address, Public } ;
308+ use consensus:: stake:: action_data:: get_delegation_key;
282309 use consensus:: validator_set:: new_validator_set;
283310 use cstate:: tests:: helpers;
284311 use cstate:: TopStateView ;
@@ -552,4 +579,110 @@ mod tests {
552579 let result = stake. execute ( & action. rlp_bytes ( ) , & mut state, & delegator) ;
553580 assert ! ( result. is_err( ) ) ;
554581 }
582+
583+ #[ test]
584+ fn can_revoke_delegated_tokens ( ) {
585+ let delegatee_public = Public :: random ( ) ;
586+ let delegatee = public_to_address ( & delegatee_public) ;
587+ let delegator = Address :: random ( ) ;
588+
589+ let mut state = helpers:: get_temp_state ( ) ;
590+ let stake = {
591+ let mut genesis_stakes = HashMap :: new ( ) ;
592+ genesis_stakes. insert ( delegatee, 100 ) ;
593+ genesis_stakes. insert ( delegator, 100 ) ;
594+ Stake :: new ( genesis_stakes, new_validator_set ( vec ! [ delegatee_public] ) )
595+ } ;
596+ stake. init ( & mut state) . unwrap ( ) ;
597+
598+ let action = Action :: DelegateCCS {
599+ address : delegatee,
600+ quantity : 50 ,
601+ } ;
602+ let result = stake. execute ( & action. rlp_bytes ( ) , & mut state, & delegator) ;
603+ assert ! ( result. is_ok( ) ) ;
604+
605+ let action = Action :: Revoke {
606+ address : delegatee,
607+ quantity : 20 ,
608+ } ;
609+ let result = stake. execute ( & action. rlp_bytes ( ) , & mut state, & delegator) ;
610+ assert_eq ! ( Ok ( ( ) ) , result) ;
611+
612+ let delegator_account = StakeAccount :: load_from_state ( & state, & delegator) . unwrap ( ) ;
613+ let delegation = Delegation :: load_from_state ( & state, & delegator) . unwrap ( ) ;
614+ assert_eq ! ( delegator_account. balance, 100 - 50 + 20 ) ;
615+ assert_eq ! ( delegation. iter( ) . count( ) , 1 ) ;
616+ assert_eq ! ( delegation. get_quantity( & delegatee) , 50 - 20 ) ;
617+ }
618+
619+ #[ test]
620+ fn cannot_revoke_more_than_delegated_tokens ( ) {
621+ let delegatee_public = Public :: random ( ) ;
622+ let delegatee = public_to_address ( & delegatee_public) ;
623+ let delegator = Address :: random ( ) ;
624+
625+ let mut state = helpers:: get_temp_state ( ) ;
626+ let stake = {
627+ let mut genesis_stakes = HashMap :: new ( ) ;
628+ genesis_stakes. insert ( delegatee, 100 ) ;
629+ genesis_stakes. insert ( delegator, 100 ) ;
630+ Stake :: new ( genesis_stakes, new_validator_set ( vec ! [ delegatee_public] ) )
631+ } ;
632+ stake. init ( & mut state) . unwrap ( ) ;
633+
634+ let action = Action :: DelegateCCS {
635+ address : delegatee,
636+ quantity : 50 ,
637+ } ;
638+ let result = stake. execute ( & action. rlp_bytes ( ) , & mut state, & delegator) ;
639+ assert ! ( result. is_ok( ) ) ;
640+
641+ let action = Action :: Revoke {
642+ address : delegatee,
643+ quantity : 70 ,
644+ } ;
645+ let result = stake. execute ( & action. rlp_bytes ( ) , & mut state, & delegator) ;
646+ assert ! ( result. is_err( ) ) ;
647+
648+ let delegator_account = StakeAccount :: load_from_state ( & state, & delegator) . unwrap ( ) ;
649+ let delegation = Delegation :: load_from_state ( & state, & delegator) . unwrap ( ) ;
650+ assert_eq ! ( delegator_account. balance, 100 - 50 ) ;
651+ assert_eq ! ( delegation. iter( ) . count( ) , 1 ) ;
652+ assert_eq ! ( delegation. get_quantity( & delegatee) , 50 ) ;
653+ }
654+
655+ #[ test]
656+ fn revoke_all_should_clear_state ( ) {
657+ let delegatee_public = Public :: random ( ) ;
658+ let delegatee = public_to_address ( & delegatee_public) ;
659+ let delegator = Address :: random ( ) ;
660+
661+ let mut state = helpers:: get_temp_state ( ) ;
662+ let stake = {
663+ let mut genesis_stakes = HashMap :: new ( ) ;
664+ genesis_stakes. insert ( delegatee, 100 ) ;
665+ genesis_stakes. insert ( delegator, 100 ) ;
666+ Stake :: new ( genesis_stakes, new_validator_set ( vec ! [ delegatee_public] ) )
667+ } ;
668+ stake. init ( & mut state) . unwrap ( ) ;
669+
670+ let action = Action :: DelegateCCS {
671+ address : delegatee,
672+ quantity : 50 ,
673+ } ;
674+ let result = stake. execute ( & action. rlp_bytes ( ) , & mut state, & delegator) ;
675+ assert ! ( result. is_ok( ) ) ;
676+
677+ let action = Action :: Revoke {
678+ address : delegatee,
679+ quantity : 50 ,
680+ } ;
681+ let result = stake. execute ( & action. rlp_bytes ( ) , & mut state, & delegator) ;
682+ assert_eq ! ( Ok ( ( ) ) , result) ;
683+
684+ let delegator_account = StakeAccount :: load_from_state ( & state, & delegator) . unwrap ( ) ;
685+ assert_eq ! ( delegator_account. balance, 100 ) ;
686+ assert_eq ! ( state. action_data( & get_delegation_key( & delegator) ) . unwrap( ) , None ) ;
687+ }
555688}
0 commit comments