Skip to content
Merged
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
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,25 @@ All notable changes to this workspace will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this workspace adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

# 0.5.8

## Added
- `VotingDb::setup_bundles` now persists bundle note identity hashes, and
`VotingDb::build_governance_pczt`, `VotingDb::precompute_delegation_pir`,
and `VotingDb::build_and_prove_delegation` reject same-position note
substitutions for bundles set up under 0.5.8 or later. Bundles persisted by
earlier releases retain the prior position-only check until they are
re-setup.

## Fixed
- Delegation proof storage now checks proof-derived public inputs against the
PCZT-derived values stored during `VotingDb::build_governance_pczt`, and
stores the proof, public inputs, and round phase atomically.
- `VotingDb::setup_bundles` now persists all bundle rows in a single
transaction.
- Avoided dropping the Hyper/Tokio transport runtime from inside an active Tokio
context.

# 0.5.7

## Fixed
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion zcash_voting/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "zcash_voting"
version = "0.5.7"
version = "0.5.8"
Comment thread
greg0x marked this conversation as resolved.
edition = "2021"
description = "Client-side library for Zcash shielded voting: ZKP delegation and vote-commitment proofs (Halo 2), ElGamal encryption, governance PCZT construction, Merkle witness generation, and SQLite round-state persistence."
license = "MIT OR Apache-2.0"
Expand Down
63 changes: 58 additions & 5 deletions zcash_voting/src/http_transport.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#[cfg(feature = "client-tree-sync")]
use std::future::Future;

use anyhow::{Context, Result};
use bytes::Bytes;
use http::{Method, Request};
Expand Down Expand Up @@ -26,16 +29,13 @@ struct HyperResponse {
pub struct HyperTransport {
client: HyperClient,
#[cfg(feature = "client-tree-sync")]
runtime: tokio::runtime::Runtime,
runtime: BlockingRuntime,
}

impl HyperTransport {
pub fn new() -> Self {
#[cfg(feature = "client-tree-sync")]
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.expect("create tree-sync HTTP runtime");
let runtime = BlockingRuntime::new();
let mut connector = HttpConnector::new();
connector.enforce_http(false);
let https = hyper_rustls::HttpsConnectorBuilder::new()
Expand Down Expand Up @@ -99,6 +99,41 @@ impl Default for HyperTransport {
}
}

#[cfg(feature = "client-tree-sync")]
struct BlockingRuntime {
inner: Option<tokio::runtime::Runtime>,
}

#[cfg(feature = "client-tree-sync")]
impl BlockingRuntime {
fn new() -> Self {
Self {
inner: Some(
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.expect("create tree-sync HTTP runtime"),
),
}
}

fn block_on<F: Future>(&self, future: F) -> F::Output {
self.inner
.as_ref()
.expect("tree-sync HTTP runtime is unavailable")
.block_on(future)
}
}

#[cfg(feature = "client-tree-sync")]
impl Drop for BlockingRuntime {
fn drop(&mut self) {
if let Some(runtime) = self.inner.take() {
runtime.shutdown_background();
}
}
}

#[cfg(feature = "client-pir")]
impl pir_client::Transport for HyperTransport {
fn get<'a>(&'a self, url: &'a str) -> pir_client::TransportFuture<'a> {
Expand Down Expand Up @@ -148,3 +183,21 @@ impl vote_commitment_tree_client::transport::Transport for HyperTransport {
})
}
}

#[cfg(all(test, feature = "client-tree-sync"))]
mod tests {
use super::BlockingRuntime;

#[test]
fn blocking_runtime_drop_does_not_panic_inside_tokio_context() {
let outer = tokio::runtime::Runtime::new().unwrap();
let result = std::panic::catch_unwind(|| {
outer.block_on(async {
let runtime = BlockingRuntime::new();
drop(runtime);
});
});

assert!(result.is_ok());
}
}
81 changes: 80 additions & 1 deletion zcash_voting/src/storage/migrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,31 @@ use rusqlite::Connection;

use crate::VotingError;

const CURRENT_VERSION: u32 = 7;
const CURRENT_VERSION: u32 = 8;

fn column_exists(conn: &Connection, table: &str, column: &str) -> Result<bool, VotingError> {
let mut stmt = conn
.prepare(&format!("PRAGMA table_info({table})"))
.map_err(|e| VotingError::Internal {
message: format!("failed to inspect {table} columns: {e}"),
})?;
let columns = stmt
.query_map([], |row| row.get::<_, String>(1))
.map_err(|e| VotingError::Internal {
message: format!("failed to query {table} columns: {e}"),
})?;

for name in columns {
let name = name.map_err(|e| VotingError::Internal {
message: format!("failed to read {table} column name: {e}"),
})?;
if name == column {
return Ok(true);
}
}

Ok(false)
}

pub fn migrate(conn: &Connection) -> Result<(), VotingError> {
let version: u32 = conn
Expand Down Expand Up @@ -191,6 +215,23 @@ pub fn migrate(conn: &Connection) -> Result<(), VotingError> {
})?;
}

if version < 8 {
Comment thread
greg0x marked this conversation as resolved.
// v8: persist full bundle note identity hashes so later workflow steps
// can reject same-position note substitutions. Fresh DBs and drop-all
// migrations already get the column from 001_init.sql, so guard the
// ALTER for those paths.
if !column_exists(conn, "bundles", "note_identity_hashes_blob")? {
conn.execute_batch("ALTER TABLE bundles ADD COLUMN note_identity_hashes_blob BLOB;")
.map_err(|e| VotingError::Internal {
message: format!("migration to version 8 failed: {}", e),
})?;
}
conn.pragma_update(None, "user_version", 8)
.map_err(|e| VotingError::Internal {
message: format!("failed to update database version: {}", e),
})?;
}

let final_version: u32 = conn
.pragma_query_value(None, "user_version", |r| r.get(0))
.map_err(|e| VotingError::Internal {
Expand All @@ -212,6 +253,22 @@ pub fn migrate(conn: &Connection) -> Result<(), VotingError> {
#[cfg(test)]
mod tests {
use super::*;
use crate::storage::queries;
use crate::VotingRoundParams;

fn v7_schema() -> String {
include_str!("migrations/001_init.sql").replace(" note_identity_hashes_blob BLOB,\n", "")
}

fn test_params() -> VotingRoundParams {
VotingRoundParams {
vote_round_id: "test-round".to_string(),
snapshot_height: 1000,
ea_pk: vec![0xEA; 32],
nc_root: vec![0xAA; 32],
nullifier_imt_root: vec![0xBB; 32],
}
}

#[test]
fn test_migrate_fresh_database() {
Expand All @@ -236,6 +293,28 @@ mod tests {
assert_eq!(version, CURRENT_VERSION);
}

#[test]
fn test_migrate_from_v7_preserves_existing_bundles() {
let conn = Connection::open_in_memory().unwrap();
conn.execute_batch(&v7_schema()).unwrap();
queries::insert_round(&conn, "wallet", &test_params(), None).unwrap();
queries::insert_bundle(&conn, "test-round", "wallet", 0, &[1]).unwrap();
conn.pragma_update(None, "user_version", 7).unwrap();

migrate(&conn).unwrap();

let (positions, identity_hashes): (Vec<u8>, Option<Vec<u8>>) = conn
.query_row(
"SELECT note_positions_blob, note_identity_hashes_blob FROM bundles
WHERE round_id = 'test-round' AND wallet_id = 'wallet' AND bundle_index = 0",
[],
|row| Ok((row.get(0)?, row.get(1)?)),
)
.unwrap();
assert_eq!(positions, 1u64.to_le_bytes().to_vec());
assert_eq!(identity_hashes, None);
}

#[test]
fn test_tables_created() {
let conn = Connection::open_in_memory().unwrap();
Expand Down
1 change: 1 addition & 0 deletions zcash_voting/src/storage/migrations/001_init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ CREATE TABLE bundles (
wallet_id TEXT NOT NULL DEFAULT '',
bundle_index INTEGER NOT NULL,
note_positions_blob BLOB,
note_identity_hashes_blob BLOB,
van_comm_rand BLOB,
dummy_nullifiers BLOB,
rho_signed BLOB,
Expand Down
2 changes: 1 addition & 1 deletion zcash_voting/src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ mod tests {
let version: u32 = conn
.pragma_query_value(None, "user_version", |r| r.get(0))
.unwrap();
assert_eq!(version, 7);
assert_eq!(version, 8);
}

#[test]
Expand Down
Loading
Loading