@@ -23,9 +23,10 @@ use std::ops::Deref;
2323use std:: sync:: Arc ;
2424
2525use ccrypto:: Blake ;
26- use ckey:: { recover, Address } ;
27- use cstate:: { ActionHandler , StateResult , TopLevelState } ;
26+ use ckey:: { public_to_address , recover, Address , Signature } ;
27+ use cstate:: { ActionHandler , StateResult , TopLevelState , TopState } ;
2828use ctypes:: errors:: { RuntimeError , SyntaxError } ;
29+ use ctypes:: util:: unexpected:: Mismatch ;
2930use ctypes:: { CommonParams , Header } ;
3031use primitives:: H256 ;
3132use rlp:: { Decodable , UntrustedRlp } ;
@@ -114,8 +115,10 @@ impl ActionHandler for Stake {
114115 }
115116 }
116117 Action :: ChangeParams {
117- ..
118- } => unimplemented ! ( ) ,
118+ metadata_seq,
119+ params,
120+ signatures,
121+ } => change_params ( state, metadata_seq, * params, & signatures) ,
119122 }
120123 }
121124
@@ -215,6 +218,40 @@ pub fn get_stakes(state: &TopLevelState) -> StateResult<HashMap<Address, u64>> {
215218 Ok ( result)
216219}
217220
221+ fn change_params (
222+ state : & mut TopLevelState ,
223+ metadata_seq : u64 ,
224+ params : CommonParams ,
225+ signatures : & [ Signature ] ,
226+ ) -> StateResult < ( ) > {
227+ // Update state first because the signature validation is more expensive.
228+ state. update_params ( metadata_seq, params) ?;
229+
230+ let action = Action :: ChangeParams {
231+ metadata_seq,
232+ params : params. into ( ) ,
233+ signatures : vec ! [ ] ,
234+ } ;
235+ let encoded_action = H256 :: blake ( rlp:: encode ( & action) ) ;
236+ let stakes = get_stakes ( state) ?;
237+ let signed_stakes = signatures. iter ( ) . try_fold ( 0 , |sum, signature| {
238+ let public = recover ( signature, & encoded_action) . unwrap_or_else ( |err| {
239+ unreachable ! ( "The transaction with an invalid signature cannot pass the verification: {}" , err) ;
240+ } ) ;
241+ let address = public_to_address ( & public) ;
242+ stakes. get ( & address) . map ( |stake| sum + stake) . ok_or_else ( || RuntimeError :: SignatureOfInvalidAccount ( address) )
243+ } ) ?;
244+ let total_stakes: u64 = stakes. values ( ) . sum ( ) ;
245+ if total_stakes / 2 >= signed_stakes {
246+ return Err ( RuntimeError :: InsufficientStakes ( Mismatch {
247+ expected : total_stakes,
248+ found : signed_stakes,
249+ } )
250+ . into ( ) )
251+ }
252+ Ok ( ( ) )
253+ }
254+
218255#[ cfg( test) ]
219256mod tests {
220257 use super :: action_data:: get_account_key;
0 commit comments