Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mvp commit validation #26

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/amino_types/vote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ impl block::ParseHeight for CanonicalVote {
}

impl CanonicalVote {
fn new(vote: Vote, chain_id: &str) -> CanonicalVote {
pub fn new(vote: Vote, chain_id: &str) -> CanonicalVote {
CanonicalVote {
vote_type: vote.vote_type,
chain_id: chain_id.to_string(),
Expand Down
18 changes: 18 additions & 0 deletions src/block/id.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::parts;
use crate::{
amino_types,
error::Error,
hash::{Algorithm, Hash},
};
Expand Down Expand Up @@ -49,6 +50,23 @@ impl Id {
result.truncate(PREFIX_LENGTH);
result
}

/// Convert to amino form
pub fn to_amino(&self) -> amino_types::block_id::BlockId {
amino_types::block_id::BlockId {
hash: self.hash.to_vec(),
parts_header: Some(match &self.parts {
Some(p) => amino_types::block_id::PartsSetHeader {
total: p.total as i64, //XXX: careful integer overflow
hash: p.hash.to_vec(),
},
None => amino_types::block_id::PartsSetHeader {
total: 0,
hash: Vec::new(),
},
}),
}
}
}

// TODO: match gaia serialization? e.g `D2F5991B98D708FD2C25AA2BEBED9358F24177DE:1:C37A55FB95E9`
Expand Down
83 changes: 83 additions & 0 deletions src/commit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//! Tendermint commits

use crate::{amino_types, block, validator, vote};
use prost::Message;
use signatory::Verifier;
use signatory_dalek::Ed25519Verifier;

use amino_types::vote::CanonicalVote;

/// TODO return Result<(), Error>
/// NOTE: This assumes the vals correspond directly to the votes in the last_commit
pub fn verify_commit(chain_id: &str, last_commit: block::LastCommit, vals: validator::Set) -> bool {
let precommits_vec = last_commit.precommits.into_vec();
let vals_vec = vals.into_vec();
if precommits_vec.len() != vals_vec.len() {
return false;
}

if vals_vec.len() == 0 {
return false;
}

// populate these as we iterate through
let mut signed_power = 0;
let mut total_power = 0;

// populate these from the first non-empty vote
let mut height = 0;
let mut round = 0;

for (val, opt_vote) in vals_vec.into_iter().zip(precommits_vec.into_iter()) {
let val_power = val.voting_power.value();
total_power += val_power;

if let Some(v) = opt_vote {
if height == 0 {
height = v.height.value();
round = v.round;
}

if height != v.height.value() || round != v.round {
return false;
}

if v.vote_type != vote::Type::Precommit {
return false;
}

// just skip different block ids
if v.block_id != last_commit.block_id {
continue;
}

// no validation we can do on the timestamp

// check the vote matches the validator
if v.validator_address != val.pub_key.id() {
return false;
}

let mut sign_bytes: Vec<u8> = Vec::new();
let canonical_vote = CanonicalVote::new(v.to_amino(), chain_id);
let result = canonical_vote.encode_length_delimited(&mut sign_bytes);
if let Err(r) = result {
println!("ERROR {}", r);
return false;
}

// verify the signature
// TODO: abstract over Ed25519
let pub_key = val.pub_key.ed25519().unwrap();
let sig_verifier = Ed25519Verifier::from(&pub_key);
let result = sig_verifier.verify(&sign_bytes, &v.signature.ed25519().unwrap());
if let Err(r) = result {
println!("ERROR {}", r);
return false;
}

signed_power += val_power
}
}
signed_power * 3 > total_power * 2
}
8 changes: 8 additions & 0 deletions src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ impl Hash {
Hash::Null => None,
}
}

/// Convert the hash to a vector of bytes or else an empty vector
pub fn to_vec(&self) -> Vec<u8> {
match self {
Hash::Sha256(h) => h.to_vec(),
Hash::Null => Vec::new(),
}
}
}

impl Debug for Hash {
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub mod amino_types;
pub mod block;
pub mod chain;
pub mod channel;
pub mod commit;
pub mod config;
pub mod consensus;
pub mod evidence;
Expand Down
9 changes: 9 additions & 0 deletions src/public_key.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Public keys used in Tendermint networks

use crate::account::Id;
use crate::error::{Error, ErrorKind};
use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer};
use signatory::{ecdsa::curve::secp256k1, ed25519};
Expand Down Expand Up @@ -54,6 +55,14 @@ impl PublicKey {
}
}

/// Get public key address
pub fn id(self) -> Id {
match self {
PublicKey::Ed25519(pk) => Id::from(pk),
PublicKey::Secp256k1(pk) => Id::from(pk),
}
}

/// Serialize this key as raw bytes
pub fn as_bytes(self) -> Vec<u8> {
match self {
Expand Down
7 changes: 7 additions & 0 deletions src/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ impl Signature {
Signature::Ed25519(_) => Algorithm::Ed25519,
}
}

/// Get Ed25519 signature
pub fn ed25519(&self) -> Option<signatory::ed25519::Signature> {
match self {
Signature::Ed25519(sig) => Some(sig.clone()),
}
}
}

impl<'de> Deserialize<'de> for Signature {
Expand Down
5 changes: 5 additions & 0 deletions src/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ impl Set {
Set { validators: vals }
}

/// Return the underlying vector of validator Info
pub fn into_vec(self) -> Vec<Info> {
self.validators
}

/// Compute the Merkle root of the validator set
pub fn hash(self) -> merkle::Hash {
// We need to get from Vec<Info> to &[&[u8]] so we can call simple_hash_from_byte_slices.
Expand Down
19 changes: 18 additions & 1 deletion src/vote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
mod power;

pub use self::power::Power;
use crate::{account, block, Signature, Time};
use crate::{account, amino_types, block, Signature, Time};
use signatory::Signature as SignatureTrait;
use {
crate::serializers,
serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer},
Expand Down Expand Up @@ -49,6 +50,22 @@ pub struct Vote {
pub signature: Signature,
}

impl Vote {
/// Convert to amino_types vote
pub fn to_amino(&self) -> amino_types::vote::Vote {
amino_types::vote::Vote {
vote_type: self.vote_type.to_u8() as u32,
height: self.height.value() as i64, // XXX: careful integer overflow
round: self.round as i64, // XXX: careful integer overflow
block_id: Some(self.block_id.to_amino()), // TODO: fill in this to_amino method
timestamp: Some(amino_types::TimeMsg::from(self.timestamp)),
validator_address: self.validator_address.as_bytes().to_vec(),
validator_index: self.validator_index as i64,
signature: self.signature.ed25519().unwrap().as_slice().to_vec(), // TODO: support more than ed25519
}
}
}

impl Vote {
/// Is this vote a prevote?
pub fn is_prevote(&self) -> bool {
Expand Down