Skip to content

Commit a8fc0fd

Browse files
committed
Implement report_double_vote custom action
1 parent c02f93c commit a8fc0fd

File tree

11 files changed

+244
-44
lines changed

11 files changed

+244
-44
lines changed

core/src/consensus/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub use self::solo::Solo;
3434
pub use self::tendermint::{Tendermint, TendermintParams, TimeGapParams};
3535
pub use self::validator_set::validator_list::RoundRobinValidator;
3636
pub use self::validator_set::ValidatorSet;
37+
pub use self::vote_collector::Message;
3738

3839
use std::fmt;
3940
use std::sync::{Arc, Weak};

core/src/consensus/solo/mod.rs

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,18 @@ mod params;
1818

1919
use std::sync::Arc;
2020

21-
use ckey::Address;
21+
use ckey::{Address, Error as KeyError, Public, SchnorrSignature};
2222
use cstate::{ActionHandler, HitHandler};
2323
use ctypes::{CommonParams, Header};
24+
use primitives::H256;
25+
use rlp::{Decodable, DecoderError, Encodable, RlpStream, UntrustedRlp};
2426

2527
use self::params::SoloParams;
2628
use super::stake;
2729
use super::{ConsensusEngine, Seal};
2830
use crate::block::{ExecutedBlock, IsBlock};
2931
use crate::codechain_machine::CodeChainMachine;
30-
use crate::consensus::{EngineError, EngineType};
32+
use crate::consensus::{EngineError, EngineType, Message};
3133
use crate::error::Error;
3234

3335
/// A consensus engine which does not provide any consensus mechanism.
@@ -37,14 +39,61 @@ pub struct Solo {
3739
action_handlers: Vec<Arc<ActionHandler>>,
3840
}
3941

42+
#[derive(Debug, PartialEq, Eq, Clone, Hash, Default)]
43+
pub struct SoloMessage {}
44+
45+
impl Encodable for SoloMessage {
46+
fn rlp_append(&self, s: &mut RlpStream) {
47+
s.append_empty_data();
48+
}
49+
}
50+
51+
impl Decodable for SoloMessage {
52+
fn decode(_rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
53+
Ok(SoloMessage {})
54+
}
55+
}
56+
57+
impl Message for SoloMessage {
58+
type Round = bool;
59+
60+
fn signature(&self) -> SchnorrSignature {
61+
SchnorrSignature::random()
62+
}
63+
64+
fn signer_index(&self) -> usize {
65+
Default::default()
66+
}
67+
68+
fn block_hash(&self) -> Option<H256> {
69+
None
70+
}
71+
72+
fn round(&self) -> &bool {
73+
&false
74+
}
75+
76+
fn height(&self) -> u64 {
77+
0
78+
}
79+
80+
fn is_broadcastable(&self) -> bool {
81+
false
82+
}
83+
84+
fn verify(&self, _signer_public: &Public) -> Result<bool, KeyError> {
85+
Ok(true)
86+
}
87+
}
88+
4089
impl Solo {
4190
/// Returns new instance of Solo over the given state machine.
4291
pub fn new(params: SoloParams, machine: CodeChainMachine) -> Self {
4392
let mut action_handlers: Vec<Arc<ActionHandler>> = Vec::new();
4493
if params.enable_hit_handler {
4594
action_handlers.push(Arc::new(HitHandler::new()));
4695
}
47-
action_handlers.push(Arc::new(stake::Stake::new(params.genesis_stakes.clone())));
96+
action_handlers.push(Arc::new(stake::Stake::<SoloMessage>::new(params.genesis_stakes.clone())));
4897

4998
Solo {
5099
params,

core/src/consensus/stake/actions.rs

Lines changed: 98 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,13 @@
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+
1719
use ccrypto::Blake;
1820
use ckey::{recover, Address, Signature};
21+
use client::ConsensusClient;
22+
use consensus::vote_collector::Message;
23+
use consensus::ValidatorSet;
1924
use ctypes::errors::SyntaxError;
2025
use ctypes::CommonParams;
2126
use primitives::{Bytes, H256};
@@ -25,10 +30,11 @@ const ACTION_TAG_TRANSFER_CCS: u8 = 1;
2530
const ACTION_TAG_DELEGATE_CCS: u8 = 2;
2631
const ACTION_TAG_REVOKE: u8 = 3;
2732
const ACTION_TAG_SELF_NOMINATE: u8 = 4;
33+
const ACTION_TAG_REPORT_DOUBLE_VOTE: u8 = 5;
2834
const 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)]
233319
mod 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

Comments
 (0)