Skip to content

Commit

Permalink
#393: add generation of votes; refactor commit
Browse files Browse the repository at this point in the history
  • Loading branch information
andrey-kuprianov committed Jul 20, 2020
1 parent 4991c0d commit 56a11d8
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 64 deletions.
110 changes: 63 additions & 47 deletions mbt-utils/src/commit.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,71 @@
use gumdrop::Options;
use serde::Deserialize;
use simple_error::*;
use signatory::{
ed25519,
signature::{ Signature as _, Signer }
};
use signatory_dalek::Ed25519Signer;
use tendermint::{
amino_types, block, lite, vote,
signature::Signature };
use tendermint::{ block, lite };

use crate::{Generator, Validator, Header, helpers::*};
use crate::{Generator, Validator, Header, Vote, helpers::*};

#[derive(Debug, Options, Deserialize)]
pub struct Commit {
#[options(help = "header (required)", parse(try_from_str = "parse_as::<Header>"))]
pub header: Option<Header>,
#[options(help = "votes in this commit (default: from header)",
parse(try_from_str = "parse_as::<Vec<Vote>>"))]
pub votes: Option<Vec<Vote>>,
#[options(help = "commit round (default: 1)")]
pub round: Option<u64>,
pub round: Option<u64>
}

impl Commit {
pub fn new(header: &Header) -> Self {
Commit {
header: Some(header.clone()),
round: None,
votes: None
}
}
set_option!(round, u64);
}

pub fn header(&self) -> Result<&Header, SimpleError> {
match &self.header{
None => bail!("commit header is missing"),
Some(h) => Ok(h)
}
}

pub fn votes(&self) -> Result<&Vec<Vote>, SimpleError> {
match &self.votes{
None => bail!("commit votes is missing"),
Some(vs) => Ok(vs)
}
}
pub fn generate_default_votes(mut self) -> Result<(), SimpleError> {
let header = self.header()?;
let val_to_vote = |(i, v): (usize, &Validator)| -> Vote {
Vote::new(v, header)
.index(&(i as u64))
};
let votes = header.validators
.as_ref()
.unwrap()
.iter()
.enumerate()
.map(val_to_vote)
.collect();
self.votes = Some(votes);
Ok(())
}

pub fn vote_of(&self, val: &Validator) -> Result<&Vote, SimpleError> {
let vote = require_with!(self.votes()?.iter().find(|v| *v.validator.as_ref().unwrap() == *val), "can't find vote by validator");
Ok(vote)
}

pub fn vote_at(&self, index: usize) -> Result<&Vote, SimpleError> {
let vote = require_with!(self.votes()?.get(index), "can't find vote by index");
Ok(vote)
}
}

impl std::str::FromStr for Commit {
type Err = SimpleError;
Expand All @@ -47,53 +83,33 @@ impl Generator<block::Commit> for Commit {
Commit {
header: choose_from(&self.header, &other.header),
round: choose_from(&self.round, &other.round),
votes: choose_from(&self.votes, &other.votes)
}
}

fn generate(&self) -> Result<block::Commit, SimpleError> {
if self.header.is_none() {
bail!("header is missing")
}
let header = self.header.as_ref().unwrap();
let header = match &self.header{
None => bail!("failed to generate commit: header is missing"),
Some(h) => h
};
let votes = match &self.votes{
None => bail!("failed to generate commit: votes are missing"),
Some(vs) => vs
};
let block_header = header.generate()?;
let block_id = block::Id::new(lite::Header::hash(&block_header), None);

let val_sign = |(i, v): (usize, &Validator)| -> Result<block::CommitSig, SimpleError> {
let validator = v.generate()?;
let signer: Ed25519Signer = v.get_signer()?;
let vote = vote::Vote {
vote_type: vote::Type::Precommit,
height: block_header.height,
round: choose_or(self.round, 1),
block_id: Some(block_id.clone()),
timestamp: block_header.time,
validator_address: validator.address,
validator_index: i as u64,
signature: Signature::Ed25519(
try_with!(ed25519::Signature::from_bytes(&[0_u8; ed25519::SIGNATURE_SIZE]), "failed to construct empty ed25519 signature"),
),
};
let signed_vote = vote::SignedVote::new(
amino_types::vote::Vote::from(&vote),
block_header.chain_id.as_str(),
validator.address,
vote.signature
);
let sign_bytes = signed_vote.sign_bytes();
let vote_to_sig = |v: &Vote| -> Result<block::CommitSig, SimpleError> {
let vote = v.generate()?;
Ok(block::CommitSig::BlockIDFlagCommit {
validator_address: validator.address,
timestamp: block_header.time,
signature: Signature::Ed25519(try_with!(signer.try_sign(sign_bytes.as_slice()), "failed to sign using ed25519 signature")),
validator_address: vote.validator_address,
timestamp: vote.timestamp,
signature: vote.signature,
})
};
let sigs = header.validators
.as_ref()
.unwrap()
.iter()
.enumerate()
.map(val_sign)
let sigs = votes.iter()
.map(vote_to_sig)
.collect::<Result<Vec<block::CommitSig>, SimpleError>>()?;

let commit = block::Commit {
height: block_header.height,
round: choose_or(self.round, 1),
Expand Down
4 changes: 2 additions & 2 deletions mbt-utils/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ impl Header {
time: None,
}
}
set_option!(next_validators, &[Validator], next_validators.to_vec());
set_option!(chain_id, &str, chain_id.to_string());
set_option!(next_validators, &[Validator], Some(next_validators.to_vec()));
set_option!(chain_id, &str, Some(chain_id.to_string()));
set_option!(height, u64);
set_option!(time, Time);
}
Expand Down
8 changes: 4 additions & 4 deletions mbt-utils/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ use std::io::{self, Read};
#[macro_export]
macro_rules! set_option {
($name:ident, $t:ty) => {
pub fn $name(&mut self, $name: $t) -> &mut Self {
self.$name = Some($name);
pub fn $name(mut self, $name: &$t) -> Self {
self.$name = Some($name.clone());
self
}
};
($name:ident, $t:ty, $val:expr) => {
pub fn $name(&mut self, $name: $t) -> &mut Self {
self.$name = Some($val);
pub fn $name(mut self, $name: &$t) -> Self {
self.$name = $val;
self
}
};
Expand Down
10 changes: 7 additions & 3 deletions mbt-utils/src/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ pub struct Validator {
pub proposer_priority: Option<i64>,
}




impl Validator {
pub fn new(id: &str) -> Self {
Validator {
Expand Down Expand Up @@ -66,6 +63,13 @@ impl std::str::FromStr for Validator {
}
}

impl std::cmp::PartialEq for Validator {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl std::cmp::Eq for Validator {}

impl Generator<validator::Info> for Validator {
fn merge_with_default(&self, default: &Self) -> Self {
Validator {
Expand Down
69 changes: 61 additions & 8 deletions mbt-utils/src/vote.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
use gumdrop::Options;
use serde::Deserialize;
use simple_error::*;
use tendermint::{Time};
use crate::{Validator, Header, helpers::*};
use tendermint::{
Time, vote, block, lite, amino_types,
signature::Signature };
use signatory::{
ed25519,
signature::{ Signature as _, Signer }
};
use crate::{Generator, Validator, Header, helpers::*};

#[derive(Debug, Options, Deserialize, Clone)]
pub struct Vote {
Expand All @@ -24,19 +30,19 @@ pub struct Vote {
}

impl Vote {
pub fn new(validator: &Validator) -> Self {
pub fn new(validator: &Validator, header: &Header) -> Self {
Vote {
validator: Some(validator.clone()),
index: None,
header: None,
header: Some(header.clone()),
precommit: None,
height: None,
time: None,
round: None
}
}
set_option!(index, u64);
set_option!(precommit, ());
set_option!(precommit, bool, if *precommit {Some(())} else {None});
set_option!(height, u64);
set_option!(time, Time);
set_option!(round, u64);
Expand All @@ -45,10 +51,57 @@ impl Vote {
impl std::str::FromStr for Vote {
type Err = SimpleError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let vote = match parse_as::<Vote>(s) {
Ok(input) => input,
Err(_) => Vote::new(&parse_as::<Validator>(s)?)
parse_as::<Vote>(s)
}
}

impl Generator<vote::Vote> for Vote {
fn merge_with_default(&self, default: &Self) -> Self {
Vote {
validator: choose_from(&self.validator, &default.validator),
index: choose_from(&self.index, &default.index),
header: choose_from(&self.header, &default.header),
precommit: choose_from(&self.precommit, &default.precommit),
height: choose_from(&self.height, &default.height),
time: choose_from(&self.time, &default.time),
round: choose_from(&self.round, &default.round)
}
}

fn generate(&self) -> Result<vote::Vote, SimpleError> {
let validator = match &self.validator {
None => bail!("failed to generate vote: validator is missing"),
Some(v) => v
};
let header = match &self.header {
None => bail!("failed to generate vote: header is missing"),
Some(h) => h
};
let signer = validator.get_signer()?;
let block_validator = validator.generate()?;
let block_header = header.generate()?;
let block_id = block::Id::new(lite::Header::hash(&block_header), None);
let val_index = header.validators.as_ref().unwrap().iter().enumerate().find(|(_,v)| **v == *validator);
let mut vote = vote::Vote {
vote_type: if self.precommit.is_some() { vote::Type::Precommit } else { vote::Type::Prevote },
height: block_header.height,
round: choose_or(self.round, 1),
block_id: Some(block_id.clone()),
timestamp: block_header.time,
validator_address: block_validator.address,
validator_index: choose_or(self.index, if let Some(i) = val_index { i.0 as u64 } else { 0 }),
signature: Signature::Ed25519(
try_with!(ed25519::Signature::from_bytes(&[0_u8; ed25519::SIGNATURE_SIZE]), "failed to construct empty ed25519 signature"),
),
};
let signed_vote = vote::SignedVote::new(
amino_types::vote::Vote::from(&vote),
block_header.chain_id.as_str(),
vote.validator_address,
vote.signature
);
let sign_bytes = signed_vote.sign_bytes();
vote.signature = Signature::Ed25519(try_with!(signer.try_sign(sign_bytes.as_slice()), "failed to sign using ed25519 signature"));
Ok(vote)
}
}

0 comments on commit 56a11d8

Please sign in to comment.