Skip to content
This repository was archived by the owner on Nov 6, 2020. It is now read-only.

Tendermint epoch transitions#6085

Merged
keorn merged 18 commits into
openethereum:masterfrom
feynmanliang:tendermint-epoch-transitions
Jul 26, 2017
Merged

Tendermint epoch transitions#6085
keorn merged 18 commits into
openethereum:masterfrom
feynmanliang:tendermint-epoch-transitions

Conversation

@feynmanliang
Copy link
Copy Markdown
Contributor

Resolves #6046

@parity-cla-bot
Copy link
Copy Markdown

It looks like @feynmanliang hasn'signed our Contributor License Agreement, yet.

The purpose of a CLA is to ensure that the guardian of a project's outputs has the necessary ownership or grants of rights over all contributions to allow them to distribute under the chosen licence.
Wikipedia

You can read and sign our full Contributor License Agreement at the following URL: https://cla.parity.io

Once you've signed, plesae reply to this thread with [clabot:check] to prove it.

Many thanks,

Parity Technologies CLA Bot

@feynmanliang
Copy link
Copy Markdown
Contributor Author

[clabot:check]

@parity-cla-bot
Copy link
Copy Markdown

It looks like @feynmanliang signed our Contributor License Agreement. 👍

Many thanks,

Parity Technologies CLA Bot

@gavofyork
Copy link
Copy Markdown
Contributor

many thanks for the contribution!

@gavofyork gavofyork added A0-pleasereview 🤓 Pull request needs code review. M4-core ⛓ Core client code / Rust. labels Jul 19, 2017
@gavofyork gavofyork requested review from keorn and rphmeier July 19, 2017 08:16
@rphmeier
Copy link
Copy Markdown
Contributor

@feynmanliang Thanks! It's almost there (and sorry for being a bit ambiguous about this), but there are just a couple things missing:

  • There should be an EpochVerifier which has a verify_light function that checks a block's seal under the validator set
  • The EpochVerifier should also have a check_finality_proof which does the same.
  • The validator set proof should be combined with the block header as a finality proof, and in conjunction can be used to produce an EpochVerifier. Look at how this is done in AuthorityRound for guidance.

How to check the seal:
In tendermint blocks have instant finality, so any block which is correctly signed is finalized. as such, only each block can only finalize itself, unlike authority round where any block can finalize an unbounded amount of others.

For checking the seal of a header, take a look at the verify_block_family function of Tendermint. The basic steps are:

  • iterate over header.seal.get(2) (fail if None) as UntrustedRlp
  • extract the signatures from each Precommit
  • The block is sealed if the number of unique signers is a majority of the epoch set, and unsealed otherwise.

@rphmeier rphmeier added A3-inprogress ⏳ Pull request is in progress. No review needed at this stage. and removed A0-pleasereview 🤓 Pull request needs code review. labels Jul 19, 2017
Comment thread ethcore/src/engines/tendermint/mod.rs Outdated
}

fn check_finality_proof(&self, proof: &[u8]) -> Option<Vec<H256>> {
panic!("Tendermint blocks have instant finality, so `check_finality_proof` should never get called.")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tendermint blocks do have instant finality, so the "proof" here would just be an encoded header (which we check for seal validity).

the way warp sync for PoA works is by providing a list of validator set changes along with proofs of finality of the change. A change is signalled, and the old validator set approves the signal by finalizing the block where the signal occurred. From this proved list of validator set handoffs we can get to the most recent finalized block with full security. check_finality_proof is the way we check that signals were finalized, so it can't panic.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread ethcore/src/engines/tendermint/mod.rs Outdated
let ref header_signatures_field = header.seal().get(2).ok_or(BlockError::InvalidSeal)?;
for rlp in UntrustedRlp::new(header_signatures_field).iter() {
let signature = rlp.as_val()?; // TODO: why is `rlp` have type ()?
signatures.insert(signature);
Copy link
Copy Markdown
Contributor

@rphmeier rphmeier Jul 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should also ensure that
a) each signature is a valid signature of header.bare_hash()
b) the public key recovered from each signature corresponds to an address in self.subchain_validators

I would recommend making the hashset store only those Addresses as opposed to the signatures themselves.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a little bit lost here. Looking at verify_block_family, it pattern matches on first converting the Header into either a Proposal or VoteStep. Then for Proposals it checks if the proposer is an authority in the parent block and is the proposer of the current proposal, and for VoteStep it does the counting of signatures.

Are we to assume here that the Header will always correspond to a VoteStep?

Comment thread ethcore/src/engines/tendermint/mod.rs Outdated
match self.validators.epoch_set(first, self, signal_number, set_proof) {
Ok((list, _)) => {
// Tendermint blocks have instant finality, so no need to check finality proof
ConstructedVerifier::Trusted(Box::new(EpochVerifier {
Copy link
Copy Markdown
Contributor

@rphmeier rphmeier Jul 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should still return Unconfirmed if some block hash is returned by epoch_set, and finality proofs may be checked (but not here). The "proof" passed here may be invalid, so this is also something of a verification function under the consensus logic.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 will add the logic for checking finality of the block hash returned by epoch_set.

Does something need to be done wrt checking validity of the "proof" passed here, or is it sufficient to have that happen at https://github.com/paritytech/parity/blob/master/ethcore/src/snapshot/consensus/authority.rs?utf8=%E2%9C%93#L199?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should decode correctly into the two portions (signal proof and finality proof), and each portion should be checked. signal proof is checked if epoch_set returns Ok, and finality_proof will be checked at that point long as we return ConstructedVerifier::Unconfirmed from epoch_verifier

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the finality proof for tendermint just ::rlp::encode(header)?

Comment thread ethcore/src/engines/tendermint/mod.rs Outdated
let mut signatures = HashSet::new();
let ref header_signatures_field = header.seal().get(2).ok_or(BlockError::InvalidSeal)?;
for rlp in UntrustedRlp::new(header_signatures_field).iter() {
let signature = rlp.as_val()?; // TODO: why is `rlp` have type ()?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't understand this comment. rlp has type UntrustedRlp

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, this was left over from my debugging >.<

@rphmeier
Copy link
Copy Markdown
Contributor

Some comments I just made are shown as outdated, but they are still relevant

@feynmanliang
Copy link
Copy Markdown
Contributor Author

@rphmeier thank you for your comments and helping me through this!

Comment thread ethcore/src/engines/tendermint/mod.rs Outdated
use spec::CommonParams;
use engines::{Engine, Seal, EngineError};
use engines::{Engine, Seal, EngineError, ConstructedVerifier};
use engines::authority_round::{combine_proofs, destructure_proofs};
Copy link
Copy Markdown
Contributor

@rphmeier rphmeier Jul 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could the tendermint module get its own versions of these functions (as the engines will be split into seperate crates in the future)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

}

fn destructure_proofs(combined: &[u8]) -> Result<(BlockNumber, &[u8], &[u8]), Error> {
pub fn destructure_proofs(combined: &[u8]) -> Result<(BlockNumber, &[u8], &[u8]), Error> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if these are kept private it gives us more freedom to upgrade their internal behavior

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@rphmeier rphmeier added A5-grumble 🔥 Pull request has minor issues that must be addressed before merging. and removed A3-inprogress ⏳ Pull request is in progress. No review needed at this stage. labels Jul 22, 2017
@rphmeier
Copy link
Copy Markdown
Contributor

LGTM except for combine_proofs/destructure_proofs grumble. I'll also check out the branch and make sure everything's working before merging. Adding tests for the tendermint::EpochVerifier behavior would be good too.

@feynmanliang
Copy link
Copy Markdown
Contributor Author

Copied {combine,destructure}_proofs over; I'm happy to do the tests (can you point me to a reference of what these might look like?).

}

let n = addresses.len();
let threshold = self.subchain_validators.len() * 2/3;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a majority (i.e. 1/2) or 2/3 (for Byzantine fault tolerance)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tendermint requires 2/3

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


let n = addresses.len();
let threshold = self.subchain_validators.len() * 2/3;
if n > threshold {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it matter if it's ">" or ">="? I'd imagine in the case of three subchain_validators we'd want verification to pass when we get 2 of them

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should always be >. In the case of 3 subchain validators you would actually need all 3 to be honest. With an amount of byzantine players f, we have to have at least 3f + 1 players total for safety.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@rphmeier
Copy link
Copy Markdown
Contributor

One way you could test is by making EpochVerifier generic over a Recover trait (with fn recover(&self, H520) -> Result<Address, Error> -- in the actual engine this would be instantiated with an implementation that actually does the recovery, but in a test you can mock it with some custom mapping. Then you can test behavior of EpochVerifier<MockRecover> to ensure that check_finality_proof and verify_light work correctly.

@feynmanliang
Copy link
Copy Markdown
Contributor Author

@rphmeier I added a recover closure parameter to EpochVerifier's constructor so I could stub it out in the tests.

Comment thread ethcore/src/engines/tendermint/mod.rs Outdated

struct EpochVerifier {
subchain_validators: SimpleList,
recover: Box<Fn(&Signature, &Message) -> Result<Address, Error> + Send + Sync>,
Copy link
Copy Markdown
Contributor

@rphmeier rphmeier Jul 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this a generic parameter instead or even one thing in cfg(test) and another in cfg(not(test))? Doing several virtual calls per header verification will probably be a performance hit we don't have to take during ancient block download after warp sync.

Copy link
Copy Markdown
Contributor Author

@feynmanliang feynmanliang Jul 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we might need a virtual here if we want to have the signatures used in the mock recover to be grabbed from the environment. The alternative would be to hard code those addresses (which should remain fairly stable as long as res/tendermint.json isn't modified) and signatures (which will change whenever vote_info.sha3() or tap.sign change). I'll push a commit for this and see what you think

Comment thread ethcore/src/engines/tendermint/mod.rs Outdated

struct EpochVerifier {
subchain_validators: SimpleList,
recover: Box<Fn(&Signature, &Message) -> Result<Address, Error> + Send + Sync>,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@feynmanliang

Try

struct EpochVerifier<F> 
    where F: Fn(&Signature, &Message) -> Result<Address, Error> + Send + Sync 
{
    subchain_validators: SimpleList,
    recover: F,
}

and just don't box the closures when you instantiate EpochVerifier.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment thread ethcore/src/engines/tendermint/mod.rs Outdated

struct EpochVerifier {
struct EpochVerifier<F>
where F: Fn(&Signature, &Message) -> Result<Address, Error> + Send + Sync {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: brace on next line, unindented

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also the where clause should be indented one level, not two

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment thread ethcore/src/engines/tendermint/mod.rs Outdated

impl super::EpochVerifier for EpochVerifier {
impl <F> super::EpochVerifier for EpochVerifier<F>
where F: Fn(&Signature, &Message) -> Result<Address, Error> + Send + Sync {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: brace on next line, unindented

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@rphmeier rphmeier added A8-looksgood 🦄 Pull request is reviewed well. and removed A5-grumble 🔥 Pull request has minor issues that must be addressed before merging. labels Jul 25, 2017
@rphmeier
Copy link
Copy Markdown
Contributor

@feynmanliang

LGTM, just will ask for another quick look-over by @keorn

@gavofyork
Copy link
Copy Markdown
Contributor

@keorn ?

@keorn keorn merged commit 5eb8cea into openethereum:master Jul 26, 2017
@feynmanliang feynmanliang deleted the tendermint-epoch-transitions branch July 26, 2017 16:12
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

A8-looksgood 🦄 Pull request is reviewed well. M4-core ⛓ Core client code / Rust.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants