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,62 @@ 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 ! (
144+ message1. height( ) == message2. height( ) ,
145+ "Heights of both messages must be same because message1.round() == message2.round()"
146+ ) ;
147+ let signed_block_height = message1. height ( ) ;
148+ let ( client, validators) = (
149+ client. expect ( "Client should be initialized" ) ,
150+ validators. expect ( "ValidatorSet should be initialized" ) ,
151+ ) ;
152+ if signed_block_height == 0 {
153+ return Err ( SyntaxError :: InvalidCustomAction ( String :: from (
154+ "Double vote on the genesis block does not make sense" ,
155+ ) ) )
156+ }
157+ let parent_header = client. block_header ( & ( signed_block_height - 1 ) . into ( ) ) . ok_or_else ( || {
158+ SyntaxError :: InvalidCustomAction ( format ! (
159+ "Cannot get header from the height {}" ,
160+ signed_block_height
161+ ) )
162+ } ) ?;
163+ let parent_hash = parent_header. hash ( ) ;
164+
165+ let signer = validators. get ( & parent_hash, signer_idx1) ;
166+ if message1. verify ( & signer) != Ok ( true ) || message2. verify ( & signer) != Ok ( true ) {
167+ return Err ( SyntaxError :: InvalidCustomAction ( String :: from ( "Schnorr signature verification fails" ) ) )
168+ }
169+ }
105170 }
106171 Ok ( ( ) )
107172 }
108173}
109174
110- impl Encodable for Action {
175+ impl < M : Message > Encodable for Action < M > {
111176 fn rlp_append ( & self , s : & mut RlpStream ) {
112177 match self {
113178 Action :: TransferCCS {
@@ -147,11 +212,17 @@ impl Encodable for Action {
147212 s. append ( signature) ;
148213 }
149214 }
215+ Action :: ReportDoubleVote {
216+ message1,
217+ message2,
218+ } => {
219+ s. begin_list ( 3 ) . append ( & ACTION_TAG_REPORT_DOUBLE_VOTE ) . append ( message1) . append ( message2) ;
220+ }
150221 } ;
151222 }
152223}
153224
154- impl Decodable for Action {
225+ impl < M : Message > Decodable for Action < M > {
155226 fn decode ( rlp : & UntrustedRlp ) -> Result < Self , DecoderError > {
156227 let tag = rlp. val_at ( 0 ) ?;
157228 match tag {
@@ -224,13 +295,33 @@ impl Decodable for Action {
224295 signatures,
225296 } )
226297 }
298+ ACTION_TAG_REPORT_DOUBLE_VOTE => {
299+ let item_count = rlp. item_count ( ) ?;
300+ if item_count != 3 {
301+ return Err ( DecoderError :: RlpIncorrectListLen {
302+ expected : 3 ,
303+ got : item_count,
304+ } )
305+ }
306+ let message1 = rlp. val_at ( 1 ) ?;
307+ let message2 = rlp. val_at ( 2 ) ?;
308+ Ok ( Action :: ReportDoubleVote {
309+ message1,
310+ message2,
311+ } )
312+ }
227313 _ => Err ( DecoderError :: Custom ( "Unexpected Tendermint Stake Action Type" ) ) ,
228314 }
229315 }
230316}
231317
232318#[ cfg( test) ]
233319mod tests {
320+ use super :: * ;
321+ use ccrypto:: blake256;
322+ use ckey:: sign_schnorr;
323+ use client:: TestBlockChainClient ;
324+ use consensus:: solo:: SoloMessage ;
234325 use rlp:: rlp_encode_and_decode_test;
235326
236327 use super :: * ;
@@ -247,7 +338,7 @@ mod tests {
247338 expected: 4 ,
248339 got: 3 ,
249340 } ) ,
250- UntrustedRlp :: new( & rlp:: encode( & action) ) . as_val:: <Action >( )
341+ UntrustedRlp :: new( & rlp:: encode( & action) ) . as_val:: <Action >:: <_> ( )
251342 ) ;
252343 }
253344
0 commit comments