Skip to content
This repository has been archived by the owner on Jun 3, 2020. It is now read-only.

Commit

Permalink
Merge pull request #272 from tendermint/send-double-sign-err-response
Browse files Browse the repository at this point in the history
Send RemoteSignerError response on double sign (closes #249)
  • Loading branch information
tarcieri authored Jun 21, 2019
2 parents 6a6bada + e9458e6 commit 41ad7dc
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 26 deletions.
7 changes: 7 additions & 0 deletions src/chain/state/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ pub enum StateErrorKind {
SyncError,
}

impl StateError {
/// Get the kind of error
pub fn kind(&self) -> StateErrorKind {
*self.0.kind()
}
}

impl From<Error<StateErrorKind>> for StateError {
fn from(other: Error<StateErrorKind>) -> Self {
StateError(other)
Expand Down
41 changes: 29 additions & 12 deletions src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ pub enum Response {
}

pub trait TendermintRequest: SignableMsg {
// TODO(ismail): this should take an error as an argument:
fn build_response(self) -> Response;
fn build_response(self, error: Option<RemoteError>) -> Response;
}

fn compute_prefix(name: &str) -> (Vec<u8>) {
Expand Down Expand Up @@ -113,19 +112,37 @@ impl Request {
}

impl TendermintRequest for SignVoteRequest {
fn build_response(self) -> Response {
Response::SignedVote(SignedVoteResponse {
vote: self.vote,
err: None,
})
fn build_response(self, error: Option<RemoteError>) -> Response {
let response = if let Some(e) = error {
SignedVoteResponse {
vote: None,
err: Some(e),
}
} else {
SignedVoteResponse {
vote: self.vote,
err: None,
}
};

Response::SignedVote(response)
}
}

impl TendermintRequest for SignProposalRequest {
fn build_response(self) -> Response {
Response::SignedProposal(SignedProposalResponse {
proposal: self.proposal,
err: None,
})
fn build_response(self, error: Option<RemoteError>) -> Response {
let response = if let Some(e) = error {
SignedProposalResponse {
proposal: None,
err: Some(e),
}
} else {
SignedProposalResponse {
proposal: self.proposal,
err: None,
}
};

Response::SignedProposal(response)
}
}
54 changes: 40 additions & 14 deletions src/session.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! A session with a validator node

use crate::{
chain,
chain::{self, state::StateErrorKind},
error::{Error, ErrorKind::*},
prost::Message,
rpc::{Request, Response, TendermintRequest},
Expand All @@ -23,13 +23,16 @@ use std::{
};
use subtle::ConstantTimeEq;
use tendermint::{
amino_types::{PingRequest, PingResponse, PubKeyRequest, PubKeyResponse},
amino_types::{PingRequest, PingResponse, PubKeyRequest, PubKeyResponse, RemoteError},
node,
secret_connection::{self, SecretConnection},
};

/// Encrypted session with a validator node
pub struct Session<Connection> {
/// Remote peer location
peer_addr: String,

/// Chain ID for this session
chain_id: chain::Id,

Expand All @@ -50,7 +53,8 @@ impl Session<SecretConnection<TcpStream>> {
port: u16,
secret_connection_key: &ed25519::Seed,
) -> Result<Self, Error> {
debug!("{}: Connecting to {}:{}...", chain_id, host, port);
let peer_addr = format!("{}:{}", host, port);
debug!("{}: Connecting to {}...", chain_id, &peer_addr);

let socket = TcpStream::connect(format!("{}:{}", host, port))?;
let signer = Ed25519Signer::from(secret_connection_key);
Expand Down Expand Up @@ -81,6 +85,7 @@ impl Session<SecretConnection<TcpStream>> {
}

Ok(Self {
peer_addr,
chain_id,
max_height,
connection,
Expand All @@ -95,16 +100,15 @@ impl Session<UnixConnection<UnixStream>> {
max_height: Option<tendermint::block::Height>,
socket_path: &Path,
) -> Result<Self, Error> {
debug!(
"{}: Connecting to socket at {}...",
chain_id,
socket_path.to_str().unwrap()
);
let peer_addr = socket_path.to_str().unwrap().to_owned();

debug!("{}: Connecting to socket at {}...", chain_id, &peer_addr);

let socket = UnixStream::connect(socket_path)?;
let connection = UnixConnection::new(socket);

Ok(Self {
peer_addr,
chain_id,
max_height,
connection,
Expand All @@ -131,7 +135,10 @@ where
}

let request = Request::read(&mut self.connection)?;
debug!("received request: {:?}", &request);
debug!(
"[{}:{}] received request: {:?}",
&self.chain_id, &self.peer_addr, &request
);

let response = match request {
Request::SignProposal(req) => self.sign(req)?,
Expand All @@ -141,7 +148,10 @@ where
Request::ShowPublicKey(ref req) => self.get_public_key(req)?,
};

debug!("sending response: {:?}", &response);
debug!(
"[{}:{}] sending response: {:?}",
&self.chain_id, &self.peer_addr, &response
);

let mut buf = vec![];

Expand All @@ -167,7 +177,23 @@ where
if let Some(request_state) = request.consensus_state() {
// TODO(tarcieri): better handle `PoisonError`?
let mut chain_state = chain.state.lock().unwrap();
chain_state.update_consensus_state(request_state.clone())?;

if let Err(e) = chain_state.update_consensus_state(request_state.clone()) {
// Report double signing error back to the validator
if e.kind() == StateErrorKind::DoubleSign {
let height = request.height().unwrap();

warn!(
"[{}:{}] attempt to double sign at height: {}",
&self.chain_id, &self.peer_addr, height
);

let remote_err = RemoteError::double_sign(height);
return Ok(request.build_response(Some(remote_err)));
} else {
return Err(e.into());
}
}
}

if let Some(max_height) = self.max_height {
Expand All @@ -193,7 +219,7 @@ where
self.log_signing_request(&request);
request.set_signature(&sig);

Ok(request.build_response())
Ok(request.build_response(None))
}

/// Reply to a ping request
Expand Down Expand Up @@ -225,8 +251,8 @@ where
.unwrap_or_else(|| "Unknown".to_owned());

info!(
"[{}] signed {:?} at height: {}",
self.chain_id, msg_type, height
"[{}@{}] signed {:?} at height: {}",
&self.chain_id, &self.peer_addr, msg_type, height
);
}
}
23 changes: 23 additions & 0 deletions tendermint-rs/src/amino_types/remote_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,26 @@ pub struct RemoteError {
#[prost(string, tag = "2")]
pub description: String,
}

/// Error codes for remote signer failures
// TODO(tarcieri): add these to Tendermint. See corresponding TODO here:
// <https://github.com/tendermint/tendermint/blob/master/privval/errors.go>
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(i32)]
pub enum RemoteErrorCode {
/// Generic error code useful when the others don't apply
RemoteSignerError = 1,

/// Double signing detected
DoubleSignError = 2,
}

impl RemoteError {
/// Create a new double signing error with the given message
pub fn double_sign(height: i64) -> Self {
RemoteError {
code: RemoteErrorCode::DoubleSignError as i32,
description: format!("double signing requested at height: {}", height),
}
}
}

0 comments on commit 41ad7dc

Please sign in to comment.