Skip to content

Commit

Permalink
Move voting structure serialization to mockchain
Browse files Browse the repository at this point in the history
The chain-vote library is the algorithm implementation, it should not
be constrained by vote plan size limits that are imposed by
the blockchain format. It's wrong to implement serialization there.
  • Loading branch information
Mikhail Zabaluev committed Oct 20, 2020
1 parent 076a040 commit 6624a61
Show file tree
Hide file tree
Showing 11 changed files with 208 additions and 181 deletions.
4 changes: 2 additions & 2 deletions chain-impl-mockchain/src/certificate/vote_plan.rs
Expand Up @@ -13,7 +13,7 @@ use chain_core::{
property,
};
use chain_crypto::{digest::DigestOf, Blake2b256, Verification};
use chain_vote::{MemberPublicKey, MEMBER_PUBLIC_KEY_BYTES_LEN};
use chain_vote::MemberPublicKey;
use std::ops::Deref;
use typed_bytes::{ByteArray, ByteBuilder};

Expand Down Expand Up @@ -442,7 +442,7 @@ impl Readable for VotePlan {
let member_keys_len = buf.get_u8()?;
let mut committee_public_keys = Vec::new();
for _ in 0..member_keys_len {
let key_buf = buf.get_slice(MEMBER_PUBLIC_KEY_BYTES_LEN)?;
let key_buf = buf.get_slice(MemberPublicKey::BYTES_LEN)?;
committee_public_keys.push(MemberPublicKey::from_bytes(key_buf).ok_or_else(|| {
ReadError::StructureInvalid("invalid public key format".to_string())
})?);
Expand Down
31 changes: 24 additions & 7 deletions chain-impl-mockchain/src/certificate/vote_tally.rs
Expand Up @@ -90,12 +90,21 @@ impl VoteTally {

match &self.payload {
VoteTallyPayload::Public => bb,
VoteTallyPayload::Private { shares } => bb
.u8(shares.inner.len().try_into().unwrap())
.fold(shares.inner.iter(), |bb, s| {
bb.u8(s.len().try_into().unwrap())
.fold(s.iter(), |bb, s| bb.bytes(s.serialize().as_ref()))
}),
VoteTallyPayload::Private { shares } => {
bb.u8(shares.inner.len().try_into().unwrap())
.fold(shares.inner.iter(), |bb, s| {
// Shares per proposal, n_members x n_options
let n_members = s.len().try_into().unwrap();
if n_members == 0 {
bb.u8(0).u8(0)
} else {
let n_options = s[0].options().try_into().unwrap();
bb.u8(n_members)
.u8(n_options)
.fold(s.iter(), |bb, s| bb.bytes(&s.to_bytes()))
}
})
}
}
}

Expand Down Expand Up @@ -237,9 +246,17 @@ impl Readable for VoteTally {
let mut proposals = Vec::with_capacity(proposals_number);
for _i in 0..proposals_number {
let shares_number = buf.get_u8()? as usize;
let options_number = buf.get_u8()? as usize;
let share_bytes = TallyDecryptShare::bytes_len(options_number);
let mut shares = Vec::with_capacity(shares_number);
for _j in 0..shares_number {
shares.push(TallyDecryptShare::read(buf)?);
let s_buf = buf.get_slice(share_bytes)?;
let share = TallyDecryptShare::from_bytes(s_buf).ok_or_else(|| {
ReadError::StructureInvalid(
"invalid decrypt share structure".to_owned(),
)
})?;
shares.push(share);
}
proposals.push(shares.into_boxed_slice());
}
Expand Down
2 changes: 1 addition & 1 deletion chain-impl-mockchain/src/ledger/recovery.rs
Expand Up @@ -954,7 +954,7 @@ fn unpack_committee_public_keys<R: BufRead>(
let size = codec.get_u8()?;
let mut result = Vec::new();
for _ in 0..size {
let buf = codec.get_bytes(chain_vote::MEMBER_PUBLIC_KEY_BYTES_LEN)?;
let buf = codec.get_bytes(chain_vote::MemberPublicKey::BYTES_LEN)?;
let key = chain_vote::MemberPublicKey::from_bytes(&buf).ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
Expand Down
48 changes: 40 additions & 8 deletions chain-impl-mockchain/src/vote/payload.rs
@@ -1,6 +1,7 @@
use crate::vote::Choice;
use chain_core::mempack::{ReadBuf, ReadError};
use chain_vote::{Ciphertext, EncryptedVote, ProofOfCorrectVote, CIPHERTEXT_BYTES_LEN};
use chain_vote::shvzk;
use chain_vote::{Ciphertext, EncryptedVote, ProofOfCorrectVote, Scalar};
use std::convert::{TryFrom, TryInto as _};
use std::hash::Hash;
use thiserror::Error;
Expand Down Expand Up @@ -71,13 +72,15 @@ impl Payload {
Self::Private {
encrypted_vote,
proof,
} => {
let bb = bb.iter8(encrypted_vote, |bb, ct| {
} => bb
.iter8(encrypted_vote, |bb, ct| {
let buffer = ct.to_bytes();
bb.bytes(&buffer)
});
proof.serialize_in(bb)
}
})
.fold(proof.ibas(), |bb, iba| bb.bytes(&iba.to_bytes()))
.fold(proof.ds(), |bb, d| bb.bytes(&d.to_bytes()))
.fold(proof.zwvs(), |bb, zwv| bb.bytes(&zwv.to_bytes()))
.bytes(&proof.r().to_bytes()),
}
}

Expand All @@ -93,12 +96,41 @@ impl Payload {
let len: usize = buf.get_u8()? as usize;
let mut cypher_texts: Vec<Ciphertext> = Vec::new();
for _ in 0..len {
let ct_buf = buf.get_slice(CIPHERTEXT_BYTES_LEN)?;
let ct_buf = buf.get_slice(Ciphertext::BYTES_LEN)?;
cypher_texts.push(Ciphertext::from_bytes(ct_buf).ok_or_else(|| {
ReadError::StructureInvalid("Invalid private vote".to_string())
})?);
}
let proof = ProofOfCorrectVote::read(buf)?;
let bits = len.next_power_of_two().trailing_zeros() as usize;
let mut ibas = Vec::with_capacity(bits);
for _ in 0..bits {
let elem_buf = buf.get_slice(shvzk::IBA::BYTES_LEN)?;
let iba = shvzk::IBA::from_bytes(elem_buf).ok_or_else(|| {
ReadError::StructureInvalid("Invalid IBA component".to_string())
})?;
ibas.push(iba);
}
let mut bs = Vec::with_capacity(bits);
for _ in 0..bits {
let elem_buf = buf.get_slice(Ciphertext::BYTES_LEN)?;
let ciphertext = Ciphertext::from_bytes(elem_buf).ok_or_else(|| {
ReadError::StructureInvalid("Invalid encoded ciphertext".to_string())
})?;
bs.push(ciphertext);
}
let mut zwvs = Vec::with_capacity(bits);
for _ in 0..bits {
let elem_buf = buf.get_slice(shvzk::ZWV::BYTES_LEN)?;
let zwv = shvzk::ZWV::from_bytes(elem_buf).ok_or_else(|| {
ReadError::StructureInvalid("Invalid ZWV component".to_string())
})?;
zwvs.push(zwv);
}
let r_buf = buf.get_slice(Scalar::BYTES_LEN)?;
let r = Scalar::from_bytes(r_buf).ok_or_else(|| {
ReadError::StructureInvalid("Invalid Proof encoded R scalar".to_string())
})?;
let proof = ProofOfCorrectVote::from_parts(ibas, bs, zwvs, r);
Ok(Self::Private {
encrypted_vote: cypher_texts,
proof,
Expand Down
2 changes: 0 additions & 2 deletions chain-vote/Cargo.toml
Expand Up @@ -14,8 +14,6 @@ zerocaf = { version = "0.2", optional = true }

criterion = { version = "0.3", optional = true }
rand_chacha = { version = "0.2", optional = true }
typed-bytes = { path = "../typed-bytes" }
chain-ser = { path = "../chain-ser" }

[dev-dependencies]
rand_chacha = "0.2"
Expand Down
12 changes: 7 additions & 5 deletions chain-vote/src/commitment.rs
@@ -1,9 +1,7 @@
use crate::gang::{GroupElement, Scalar, GROUP_ELEMENT_BYTES_LEN};
use crate::gang::{GroupElement, Scalar};
use rand_core::{CryptoRng, RngCore};
use std::ops::{Add, Mul};

pub const COMMITMENT_BYTES_LEN: usize = GROUP_ELEMENT_BYTES_LEN;

/// Pedersen commitment
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Commitment {
Expand Down Expand Up @@ -35,6 +33,8 @@ pub struct Open {
}

impl Commitment {
pub const BYTES_LEN: usize = GroupElement::BYTES_LEN;

pub fn new_open(ck: &CommitmentKey, o: &Open) -> Self {
let c = GroupElement::generator() * &o.m + &ck.h * &o.r;
Commitment { c }
Expand All @@ -53,9 +53,11 @@ impl Commitment {
Validity::Invalid
}
}
pub fn to_bytes(&self) -> Vec<u8> {
self.c.to_bytes().to_vec()

pub fn to_bytes(&self) -> [u8; Self::BYTES_LEN] {
self.c.to_bytes()
}

pub fn from_bytes(buf: &[u8]) -> Option<Self> {
Some(Self {
c: GroupElement::from_bytes(buf)?,
Expand Down
8 changes: 4 additions & 4 deletions chain-vote/src/committee.rs
@@ -1,12 +1,10 @@
use crate::commitment::CommitmentKey;
use crate::gang::{GroupElement, Scalar};
use crate::gargamel::{PublicKey, SecretKey, PUBLIC_KEY_BYTES_LEN};
use crate::gargamel::{PublicKey, SecretKey};
use crate::hybrid;
use crate::math::Polynomial;
use rand_core::{CryptoRng, RngCore};

pub const MEMBER_PUBLIC_KEY_BYTES_LEN: usize = PUBLIC_KEY_BYTES_LEN;

/// Committee member election secret key
#[derive(Clone)]
pub struct MemberSecretKey(pub(crate) SecretKey);
Expand Down Expand Up @@ -128,12 +126,14 @@ impl MemberSecretKey {
}

pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
let sk = Scalar::from_slice(bytes)?;
let sk = Scalar::from_bytes(bytes)?;
Some(Self(SecretKey { sk }))
}
}

impl MemberPublicKey {
pub const BYTES_LEN: usize = PublicKey::BYTES_LEN;

pub fn to_bytes(&self) -> Vec<u8> {
self.0.to_bytes()
}
Expand Down
26 changes: 9 additions & 17 deletions chain-vote/src/gang/p256k1.rs
Expand Up @@ -9,9 +9,6 @@ pub struct Scalar(IScalar);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GroupElement(Point);

/// Size of the byte representation of `GroupElement`.
pub const GROUP_ELEMENT_BYTES_LEN: usize = 65;

#[allow(clippy::derive_hash_xor_eq)]
impl Hash for GroupElement {
fn hash<H: Hasher>(&self, state: &mut H) {
Expand All @@ -27,6 +24,9 @@ impl Hash for Scalar {
}

impl GroupElement {
/// Size of the byte representation of `GroupElement`.
pub const BYTES_LEN: usize = 65;

pub fn generator() -> Self {
GroupElement(Point::generator())
}
Expand All @@ -50,8 +50,8 @@ impl GroupElement {
self.0.normalize()
}

pub fn to_bytes(&self) -> [u8; GROUP_ELEMENT_BYTES_LEN] {
let mut bytes = [0u8; GROUP_ELEMENT_BYTES_LEN];
pub fn to_bytes(&self) -> [u8; Self::BYTES_LEN] {
let mut bytes = [0u8; Self::BYTES_LEN];
match self.0.to_affine() {
None => (),
Some(pa) => {
Expand Down Expand Up @@ -103,6 +103,8 @@ impl GroupElement {
}

impl Scalar {
pub const BYTES_LEN: usize = 32;

/// additive identity
pub fn zero() -> Self {
Scalar(IScalar::zero())
Expand All @@ -127,21 +129,11 @@ impl Scalar {
self.0 = &self.0 + IScalar::one()
}

/*
pub fn from_random_bytes(slice: [u8; 64]) -> Self {
Scalar(IScalar::init_from_wide_bytes(slice))
}
*/

pub fn from_bytes(bytes: &[u8; 32]) -> Option<Self> {
IScalar::from_bytes(bytes).map(Scalar)
}

pub fn to_bytes(&self) -> [u8; 32] {
pub fn to_bytes(&self) -> [u8; Self::BYTES_LEN] {
self.0.to_bytes()
}

pub fn from_slice(slice: &[u8]) -> Option<Self> {
pub fn from_bytes(slice: &[u8]) -> Option<Self> {
IScalar::from_slice(slice).map(Scalar)
}

Expand Down
22 changes: 12 additions & 10 deletions chain-vote/src/gargamel.rs
@@ -1,13 +1,9 @@
#![allow(dead_code)]

use crate::gang::{GroupElement, Scalar, GROUP_ELEMENT_BYTES_LEN};
use crate::gang::{GroupElement, Scalar};
use rand_core::{CryptoRng, RngCore};
use std::ops::{Add, Mul};

pub const PUBLIC_KEY_BYTES_LEN: usize = GROUP_ELEMENT_BYTES_LEN;
/// Size of the byte representation of `Ciphertext`.
pub const CIPHERTEXT_BYTES_LEN: usize = GROUP_ELEMENT_BYTES_LEN * 2;

// ElGamal Ciphertext
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PublicKey {
Expand All @@ -32,9 +28,12 @@ pub struct Ciphertext {
}

impl PublicKey {
pub const BYTES_LEN: usize = GroupElement::BYTES_LEN;

pub fn to_bytes(&self) -> Vec<u8> {
self.pk.to_bytes().to_vec()
}

pub fn from_bytes(buf: &[u8]) -> Option<Self> {
Some(Self {
pk: GroupElement::from_bytes(buf)?,
Expand All @@ -49,7 +48,7 @@ impl SecretKey {
}

pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
Scalar::from_slice(bytes).map(|sk| Self { sk })
Scalar::from_bytes(bytes).map(|sk| Self { sk })
}
}

Expand All @@ -67,6 +66,9 @@ impl Keypair {
}

impl Ciphertext {
/// Size of the byte representation of `Ciphertext`.
pub const BYTES_LEN: usize = GroupElement::BYTES_LEN * 2;

/// the zero ciphertext
pub fn zero() -> Self {
Ciphertext {
Expand All @@ -76,16 +78,16 @@ impl Ciphertext {
}

pub fn to_bytes(&self) -> Vec<u8> {
let mut r = Vec::with_capacity(CIPHERTEXT_BYTES_LEN);
let mut r = Vec::with_capacity(Self::BYTES_LEN);
r.extend_from_slice(self.e1.to_bytes().as_ref());
r.extend_from_slice(self.e2.to_bytes().as_ref());
debug_assert_eq!(r.len(), CIPHERTEXT_BYTES_LEN);
debug_assert_eq!(r.len(), Self::BYTES_LEN);
r
}

pub fn from_bytes(slice: &[u8]) -> Option<Ciphertext> {
let e1 = GroupElement::from_bytes(&slice[..GROUP_ELEMENT_BYTES_LEN])?;
let e2 = GroupElement::from_bytes(&slice[GROUP_ELEMENT_BYTES_LEN..])?;
let e1 = GroupElement::from_bytes(&slice[..GroupElement::BYTES_LEN])?;
let e2 = GroupElement::from_bytes(&slice[GroupElement::BYTES_LEN..])?;
Some(Ciphertext { e1, e2 })
}

Expand Down

0 comments on commit 6624a61

Please sign in to comment.