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

Introduce baseline types required to implement validator set updates #273

Merged
merged 49 commits into from
Aug 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
d6d0433
Add validator set update vext mod
sug0 Aug 3, 2022
8eb6e03
Add a TODO
sug0 Aug 3, 2022
2dec131
Add tiny-keccak as a dep
sug0 Aug 4, 2022
a30cf4f
Validator set update vote extension
sug0 Aug 4, 2022
e060784
Return a list of validator addresses and voting powers
sug0 Aug 4, 2022
0732bb8
Merge branch 'eth-bridge-integration' into tiago/ethbridge/valset-upd…
sug0 Aug 4, 2022
a46cce1
Normalize the voting powers to 2^32
sug0 Aug 4, 2022
b6a3c5d
Merge branch 'eth-bridge-integration' into tiago/ethbridge/valset-upd…
sug0 Aug 4, 2022
61030d8
WIP: Get keccak hash of validator set update vote extension
sug0 Aug 4, 2022
26ab743
Encode test
sug0 Aug 4, 2022
27016cd
Get keccak hash of validator set update vote extension
sug0 Aug 4, 2022
16c4d4f
Remove AbiEncodePacked
sug0 Aug 4, 2022
32b002e
Remove TODO
sug0 Aug 4, 2022
ce38f19
Add VextDigest
sug0 Aug 4, 2022
c14c8f2
WIP: Ethereum address wrapper type
sug0 Aug 4, 2022
944f8c2
Add Borsh serialization for the Ethereum address wrapper
sug0 Aug 5, 2022
791cce8
WIP: VextDigest decompress
sug0 Aug 5, 2022
768eec1
Decompress validator set update vote extension digest
sug0 Aug 5, 2022
8305590
Fix make clippy-abci-plus-plus
sug0 Aug 5, 2022
b0ff8a5
Sign stub
sug0 Aug 5, 2022
52b6d57
Add VoteExtension type
sug0 Aug 5, 2022
5abd8e7
Add VoteExtensionDigest
sug0 Aug 5, 2022
dc02085
Rename EncodedData to HexString
sug0 Aug 5, 2022
20499f5
Add test docstring
sug0 Aug 5, 2022
4f205f5
Add abi_params() stub
sug0 Aug 5, 2022
dfa21a9
Make Signed use a generic serialization method
sug0 Aug 5, 2022
9e1e32d
WIP: Sign Vext
sug0 Aug 5, 2022
d904238
Make tag mod private
sug0 Aug 5, 2022
a8ae428
Fix docstrings
sug0 Aug 5, 2022
9e473ef
Tighten trait constraints on Signed
sug0 Aug 5, 2022
e09944c
Improve Signed docstring
sug0 Aug 5, 2022
20cba91
Fix typo in tag enum
sug0 Aug 5, 2022
6bb7b41
WIP: Verify validator set update vote extension signature
sug0 Aug 8, 2022
4a78619
Refactor voting powers
sug0 Aug 8, 2022
adb9c8e
Ethereum keccak hash of signed message
sug0 Aug 8, 2022
d0381ab
Remove smart contract version from the hash calc
sug0 Aug 8, 2022
afc35d7
Sign validator set update
sug0 Aug 8, 2022
69c4fb5
Verify signature of validator set update
sug0 Aug 8, 2022
a5a2cde
Add VoteExtensionDigest::get_protocol_txs()
sug0 Aug 8, 2022
d72e25e
Rename get_protocol_txs to into_protocol_txs
sug0 Aug 8, 2022
6e4a437
Add a TODO
sug0 Aug 8, 2022
c7f940f
Revert "Add VoteExtensionDigest::get_protocol_txs()"
sug0 Aug 9, 2022
0e13722
Update Cargo.lock
sug0 Aug 9, 2022
3ee213d
Manually implement Eq for Signed
sug0 Aug 9, 2022
01d0423
Update shared/src/types/vote_extensions/validator_set_update.rs
sug0 Aug 9, 2022
445bbfd
Remove duplicated type def
sug0 Aug 9, 2022
3f440f9
Use KeccakHash wrapper type instead of raw array
sug0 Aug 9, 2022
97021f8
Improve Signed API
sug0 Aug 9, 2022
6b442ca
Improve docstring of a Vext field
sug0 Aug 9, 2022
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions shared/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ tendermint-proto = {git = "https://github.com/heliaxdev/tendermint-rs", rev = "9
tendermint-proto-abci = {package = "tendermint-proto", git = "https://github.com/heliaxdev/tendermint-rs", branch = "murisi/jsfix", optional = true}
tendermint-stable = {package = "tendermint", git = "https://github.com/heliaxdev/tendermint-rs", branch = "murisi/jsfix", optional = true}
thiserror = "1.0.30"
tiny-keccak = "2.0.2"
tracing = "0.1.30"
wasmer = {version = "=2.2.0", optional = true}
wasmer-cache = {version = "=2.2.0", optional = true}
Expand Down
3 changes: 2 additions & 1 deletion shared/src/proto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ pub mod generated;
mod types;

pub use types::{
Dkg, Error, Intent, IntentGossipMessage, IntentId, Signed, SignedTxData, Tx,
Dkg, Error, Intent, IntentGossipMessage, IntentId, Signed, SignedSerialize,
SignedTxData, Tx,
};

#[cfg(test)]
Expand Down
96 changes: 58 additions & 38 deletions shared/src/proto/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::collections::HashMap;
use std::convert::{TryFrom, TryInto};
use std::fmt::Display;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;

use borsh::schema::{Declaration, Definition};
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
Expand Down Expand Up @@ -51,54 +52,69 @@ pub struct SignedTxData {
pub sig: common::Signature,
}

/// A generic signed data wrapper for Borsh encode-able data.
/// A serialization method to provide to [`Signed`], such
/// that we may sign serialized data.
pub trait SignedSerialize<T> {
/// A byte vector containing the serialized data.
type Output: AsRef<[u8]>;

/// Encodes `data` as a byte vector,
/// with some arbitrary serialization method.
fn serialize(data: &T) -> Self::Output;
}

/// Tag type that indicates we should use [`BorshSerialize`]
/// to sign data in a [`Signed`] wrapper.
#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
pub struct SerializeWithBorsh;

impl<T: BorshSerialize> SignedSerialize<T> for SerializeWithBorsh {
type Output = Vec<u8>;

fn serialize(data: &T) -> Vec<u8> {
data.try_to_vec()
.expect("Encoding data for signing shouldn't fail")
}
}

/// A generic signed data wrapper for serialize-able types.
///
/// The default serialization method is [`BorshSerialize`].
#[derive(
Clone, Debug, BorshSerialize, BorshDeserialize, Serialize, Deserialize,
)]
pub struct Signed<T: BorshSerialize + BorshDeserialize> {
pub struct Signed<T, S = SerializeWithBorsh> {
Copy link
Contributor

Choose a reason for hiding this comment

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

It'd be good if we could bound S here (if it's possible?) so that it's impossible to construct a Signed with an invalid tag, something like

pub struct Signed<T, S: SignedSerialize<T> = SerializeWithBorsh>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we already tighten Signed at the impl block starting on line 148. you can only verify signatures of Signed instances with a tag which implements SignedSerialize. you can't use the struct itself to instantiate new values, since it has a private field, but you can use Signed::new_from instead. regardless, we hit the same wall, this instance can't do jack if S does not implement SignedSerialize.

tl;dr I don't think this is an issue

/// Arbitrary data to be signed
pub data: T,
/// The signature of the data
pub sig: common::Signature,
/// The method to serialize the data with,
/// before it being signed
_serialization: PhantomData<S>,
}

impl<T> PartialEq for Signed<T>
where
T: BorshSerialize + BorshDeserialize + PartialEq,
{
impl<S, T: Eq> Eq for Signed<T, S> {}

impl<S, T: PartialEq> PartialEq for Signed<T, S> {
fn eq(&self, other: &Self) -> bool {
self.data == other.data && self.sig == other.sig
}
}

impl<T> Eq for Signed<T> where
T: BorshSerialize + BorshDeserialize + Eq + PartialEq
{
}

impl<T> Hash for Signed<T>
where
T: BorshSerialize + BorshDeserialize + Hash,
{
impl<S, T: Hash> Hash for Signed<T, S> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.data.hash(state);
self.sig.hash(state);
}
}

impl<T> PartialOrd for Signed<T>
where
T: BorshSerialize + BorshDeserialize + PartialOrd,
{
impl<S, T: PartialOrd> PartialOrd for Signed<T, S> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.data.partial_cmp(&other.data)
}
}

impl<T> BorshSchema for Signed<T>
where
T: BorshSerialize + BorshDeserialize + BorshSchema,
{
impl<S, T: BorshSchema> BorshSchema for Signed<T, S> {
fn add_definitions_recursively(
definitions: &mut HashMap<Declaration, Definition>,
) {
Expand All @@ -117,17 +133,24 @@ where
}
}

impl<T> Signed<T>
where
T: BorshSerialize + BorshDeserialize,
{
/// Initialize a new signed data.
impl<T, S> Signed<T, S> {
/// Initialize a new [`Signed`] instance from an existing signature.
#[inline]
pub fn new_from(data: T, sig: common::Signature) -> Self {
Self {
data,
sig,
_serialization: PhantomData,
}
}
}

impl<T, S: SignedSerialize<T>> Signed<T, S> {
/// Initialize a new [`Signed`] instance.
pub fn new(keypair: &common::SecretKey, data: T) -> Self {
let to_sign = data
.try_to_vec()
.expect("Encoding data for signing shouldn't fail");
let sig = common::SigScheme::sign(keypair, &to_sign);
Self { data, sig }
let to_sign = S::serialize(&data);
let sig = common::SigScheme::sign(keypair, to_sign.as_ref());
Self::new_from(data, sig)
}

/// Verify that the data has been signed by the secret key
Expand All @@ -136,11 +159,8 @@ where
&self,
pk: &common::PublicKey,
) -> std::result::Result<(), VerifySigError> {
let bytes = self
.data
.try_to_vec()
.expect("Encoding data for verifying signature shouldn't fail");
common::SigScheme::verify_signature_raw(pk, &bytes, &self.sig)
let bytes = S::serialize(&self.data);
common::SigScheme::verify_signature_raw(pk, bytes.as_ref(), &self.sig)
}
}

Expand Down
1 change: 1 addition & 0 deletions shared/src/types/ethereum_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ impl From<u64> for Uint {
Eq,
PartialOrd,
Ord,
Hash,
BorshSerialize,
BorshDeserialize,
BorshSchema,
Expand Down
6 changes: 5 additions & 1 deletion shared/src/types/transaction/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ mod protocol_txs {
use crate::proto::Tx;
use crate::types::key::*;
use crate::types::transaction::{EllipticCurve, TxError, TxType};
use crate::types::vote_extensions::ethereum_events;
use crate::types::vote_extensions::{
ethereum_events, validator_set_update,
};

const TX_NEW_DKG_KP_WASM: &str = "tx_update_dkg_session_keypair.wasm";

Expand Down Expand Up @@ -79,6 +81,8 @@ mod protocol_txs {
NewDkgKeypair(Tx),
/// Ethereum events contained in vote extensions
EthereumEvents(ethereum_events::VextDigest),
/// Validator set updates contained in vote extensions
ValidatorSetUpdate(validator_set_update::VextDigest),
}

impl ProtocolTxType {
Expand Down
47 changes: 24 additions & 23 deletions shared/src/types/vote_extensions.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
//! This module contains types necessary for processing vote extensions.

pub mod ethereum_events;
pub mod validator_set_update;

// TODO: add a `VoteExtension` type
//
// ```ignore
// pub struct VoteExtension {
// pub ethereum_events: Signed<ethereum_events::Vext>,
// pub validator_set_update: Option<validator_set_update::EthSignedVext>,
// }
// ```
use crate::proto::Signed;

// TODO: add a `VoteExtensionDigest` type; this will contain
// the values to be proposed, for a quorum of Ethereum events
// vote extensions, and a separate quorum of validator set update
// vote extensions
//
// ```ignore
// pub struct VoteExtensionDigest {
// pub ethereum_events: ethereum_events::VextDigest,
// pub validator_set_update: Option<validator_set_update::VextDigest>,
// }
// ```
//
// from a `VoteExtensionDigest` we yield two signed `ProtocolTxType` values,
// one of `ProtocolTxType::EthereumEvents` and the other of
// `ProtocolTxType::ValidatorSetUpdate`
/// This type represents the data we pass to the extension of
/// a vote at the PreCommit phase of Tendermint.
pub struct VoteExtension {
/// Vote extension data related with Ethereum events.
pub ethereum_events: Signed<ethereum_events::Vext>,
/// Vote extension data related with validator set updates.
pub validator_set_update: Option<validator_set_update::SignedVext>,
}

/// The digest of the signatures from different validators
/// in [`VoteExtension`] instances.
///
/// From a [`VoteExtensionDigest`] we yield two signed
/// [`crate::types::transaction::protocol::ProtocolTxType`] transactions:
/// - A `ProtocolTxType::EthereumEvents` tx, and
/// - A `ProtocolTxType::ValidatorSetUpdate` tx
pub struct VoteExtensionDigest {
/// The digest of Ethereum events vote extension signatures.
pub ethereum_events: ethereum_events::VextDigest,
/// The digest of validator set updates vote extension signatures.
pub validator_set_update: Option<validator_set_update::VextDigest>,
}
2 changes: 1 addition & 1 deletion shared/src/types/vote_extensions/ethereum_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ impl VextDigest {
// of crate versions changing and such
ext.ethereum_events.sort();

let signed = Signed { data: ext, sig };
let signed = Signed::new_from(ext, sig);
extensions.push(signed);
}
extensions
Expand Down
Loading