1414// You should have received a copy of the GNU Affero General Public License
1515// along with this program. If not, see <https://www.gnu.org/licenses/>.
1616
17+ use std:: sync:: Arc ;
18+
1719use ccrypto:: Blake ;
1820use ckey:: { recover, Address , Signature } ;
21+ use client:: ConsensusClient ;
22+ use consensus:: vote_collector:: Message ;
23+ use consensus:: ValidatorSet ;
1924use ctypes:: errors:: SyntaxError ;
2025use ctypes:: CommonParams ;
2126use primitives:: { Bytes , H256 } ;
@@ -25,10 +30,11 @@ const ACTION_TAG_TRANSFER_CCS: u8 = 1;
2530const ACTION_TAG_DELEGATE_CCS : u8 = 2 ;
2631const ACTION_TAG_REVOKE : u8 = 3 ;
2732const ACTION_TAG_SELF_NOMINATE : u8 = 4 ;
33+ const ACTION_TAG_REPORT_DOUBLE_VOTE : u8 = 5 ;
2834const ACTION_TAG_CHANGE_PARAMS : u8 = 0xFF ;
2935
3036#[ derive( Debug , PartialEq ) ]
31- pub enum Action {
37+ pub enum Action < M : Message > {
3238 TransferCCS {
3339 address : Address ,
3440 quantity : u64 ,
@@ -50,10 +56,19 @@ pub enum Action {
5056 params : Box < CommonParams > ,
5157 signatures : Vec < Signature > ,
5258 } ,
59+ ReportDoubleVote {
60+ message1 : M ,
61+ message2 : M ,
62+ } ,
5363}
5464
55- impl Action {
56- pub fn verify ( & self , current_params : & CommonParams ) -> Result < ( ) , SyntaxError > {
65+ impl < M : Message > Action < M > {
66+ pub fn verify (
67+ & self ,
68+ current_params : & CommonParams ,
69+ client : Option < Arc < ConsensusClient > > ,
70+ validators : Option < Arc < ValidatorSet > > ,
71+ ) -> Result < ( ) , SyntaxError > {
5772 match self {
5873 Action :: TransferCCS {
5974 ..
@@ -89,7 +104,7 @@ impl Action {
89104 ) ) )
90105 }
91106 params. verify ( ) . map_err ( SyntaxError :: InvalidCustomAction ) ?;
92- let action = Action :: ChangeParams {
107+ let action = Action :: < M > :: ChangeParams {
93108 metadata_seq : * metadata_seq,
94109 params : params. clone ( ) ,
95110 signatures : vec ! [ ] ,
@@ -102,12 +117,64 @@ impl Action {
102117 } ) ?;
103118 }
104119 }
120+ Action :: ReportDoubleVote {
121+ message1,
122+ message2,
123+ } => {
124+ if message1 == message2 {
125+ return Err ( SyntaxError :: InvalidCustomAction ( String :: from ( "Messages are duplicated" ) ) )
126+ }
127+ if message1. round ( ) != message2. round ( ) {
128+ return Err ( SyntaxError :: InvalidCustomAction ( String :: from (
129+ "The messages are from two different voting rounds" ,
130+ ) ) )
131+ }
132+
133+ let signer_idx1 = message1. signer_index ( ) ;
134+ let signer_idx2 = message2. signer_index ( ) ;
135+
136+ if signer_idx1 != signer_idx2 {
137+ return Err ( SyntaxError :: InvalidCustomAction ( format ! (
138+ "Two messages have different signer indexes: {}, {}" ,
139+ signer_idx1, signer_idx2
140+ ) ) )
141+ }
142+
143+ assert_eq ! (
144+ message1. height( ) ,
145+ message2. height( ) ,
146+ "Heights of both messages must be same because message1.round() == message2.round()"
147+ ) ;
148+ let signed_block_height = message1. height ( ) ;
149+ let ( client, validators) = (
150+ client. expect ( "Client should be initialized" ) ,
151+ validators. expect ( "ValidatorSet should be initialized" ) ,
152+ ) ;
153+ if signed_block_height == 0 {
154+ return Err ( SyntaxError :: InvalidCustomAction ( String :: from (
155+ "Double vote on the genesis block does not make sense" ,
156+ ) ) )
157+ }
158+ let parent_hash = client
159+ . block_header ( & ( signed_block_height - 1 ) . into ( ) )
160+ . ok_or_else ( || {
161+ SyntaxError :: InvalidCustomAction ( format ! (
162+ "Cannot get header from the height {}" ,
163+ signed_block_height
164+ ) )
165+ } ) ?
166+ . hash ( ) ;
167+ let signer = validators. get ( & parent_hash, signer_idx1) ;
168+ if message1. verify ( & signer) != Ok ( true ) || message2. verify ( & signer) != Ok ( true ) {
169+ return Err ( SyntaxError :: InvalidCustomAction ( String :: from ( "Schnorr signature verification fails" ) ) )
170+ }
171+ }
105172 }
106173 Ok ( ( ) )
107174 }
108175}
109176
110- impl Encodable for Action {
177+ impl < M : Message > Encodable for Action < M > {
111178 fn rlp_append ( & self , s : & mut RlpStream ) {
112179 match self {
113180 Action :: TransferCCS {
@@ -147,11 +214,17 @@ impl Encodable for Action {
147214 s. append ( signature) ;
148215 }
149216 }
217+ Action :: ReportDoubleVote {
218+ message1,
219+ message2,
220+ } => {
221+ s. begin_list ( 3 ) . append ( & ACTION_TAG_REPORT_DOUBLE_VOTE ) . append ( message1) . append ( message2) ;
222+ }
150223 } ;
151224 }
152225}
153226
154- impl Decodable for Action {
227+ impl < M : Message > Decodable for Action < M > {
155228 fn decode ( rlp : & UntrustedRlp ) -> Result < Self , DecoderError > {
156229 let tag = rlp. val_at ( 0 ) ?;
157230 match tag {
@@ -224,13 +297,33 @@ impl Decodable for Action {
224297 signatures,
225298 } )
226299 }
300+ ACTION_TAG_REPORT_DOUBLE_VOTE => {
301+ let item_count = rlp. item_count ( ) ?;
302+ if item_count != 3 {
303+ return Err ( DecoderError :: RlpIncorrectListLen {
304+ expected : 3 ,
305+ got : item_count,
306+ } )
307+ }
308+ let message1 = rlp. val_at ( 1 ) ?;
309+ let message2 = rlp. val_at ( 2 ) ?;
310+ Ok ( Action :: ReportDoubleVote {
311+ message1,
312+ message2,
313+ } )
314+ }
227315 _ => Err ( DecoderError :: Custom ( "Unexpected Tendermint Stake Action Type" ) ) ,
228316 }
229317 }
230318}
231319
232320#[ cfg( test) ]
233321mod tests {
322+ use super :: * ;
323+ use ccrypto:: blake256;
324+ use ckey:: sign_schnorr;
325+ use client:: TestBlockChainClient ;
326+ use consensus:: solo:: SoloMessage ;
234327 use rlp:: rlp_encode_and_decode_test;
235328
236329 use super :: * ;
@@ -247,7 +340,7 @@ mod tests {
247340 expected: 4 ,
248341 got: 3 ,
249342 } ) ,
250- UntrustedRlp :: new( & rlp:: encode( & action) ) . as_val:: <Action >( )
343+ UntrustedRlp :: new( & rlp:: encode( & action) ) . as_val:: <Action >:: <_> ( )
251344 ) ;
252345 }
253346
0 commit comments