diff --git a/Cargo.lock b/Cargo.lock index 8f1eec6842..cc4bb5a9e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -691,6 +691,19 @@ dependencies = [ "serde", ] +[[package]] +name = "bigdecimal" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f31f3af01c5c65a07985c804d3366560e6fa7883d640a122819b14ec327482c" +dependencies = [ + "autocfg", + "libm", + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "bincode" version = "1.3.3" @@ -1203,6 +1216,7 @@ dependencies = [ name = "chain-impl-mockchain" version = "0.1.0" dependencies = [ + "bigdecimal", "cardano-legacy-address", "chain-addr", "chain-core", @@ -1216,6 +1230,7 @@ dependencies = [ "hex", "imhamt", "lazy_static", + "num", "proptest", "quickcheck", "quickcheck_macros", @@ -1297,18 +1312,21 @@ name = "chain-vote" version = "0.1.0" dependencies = [ "base64 0.21.5", + "bigdecimal", "cfg-if 1.0.0", "chain-core", "chain-crypto", "const_format", "criterion", "cryptoxide 0.4.4", + "num", "rand 0.8.5", "rand_chacha 0.3.1", "rand_core 0.6.4", "rayon", "smoke", "thiserror", + "tracing", ] [[package]] @@ -4739,25 +4757,24 @@ dependencies = [ [[package]] name = "num" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ "num-bigint", "num-complex", "num-integer", "num-iter", - "num-rational 0.4.1", + "num-rational 0.4.2", "num-traits", ] [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "autocfg", "num-integer", "num-traits", "serde", @@ -4765,9 +4782,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", "serde", @@ -4786,19 +4803,18 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -4818,11 +4834,10 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg", "num-bigint", "num-integer", "num-traits", @@ -4831,9 +4846,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -7865,9 +7880,9 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -7889,9 +7904,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", @@ -7900,9 +7915,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", diff --git a/src/audit/README.md b/src/audit/README.md index f7f3629579..8746e9421c 100644 --- a/src/audit/README.md +++ b/src/audit/README.md @@ -31,6 +31,21 @@ FRAGMENTS_STORAGE=/tmp/fund9-leader-1/persist/leader-1 ``` + +*Generate encrypted tally with gamma scaling* + + +```bash + +BLOCK0=/tmp/fund9-leader-1/artifacts/block0.bin +FRAGMENTS_STORAGE=/tmp/fund9-leader-1/persist/leader-1 +GAMMA=0.1 +PRECISION=5 + +./target/release/offline --fragments $FRAGMENTS_STORAGE --block0 $BLOCK0 --gamma $GAMMA --precision $PRECISION + +``` + This will create three files: - *ledger_after_tally.json* **(decrypted ledger state after tally)** *should match official results!* - *ledger_before_tally.json* **(encrypted ledger state before tally)** diff --git a/src/audit/src/offline/bin/main.rs b/src/audit/src/offline/bin/main.rs index 39402585d7..9e98b0a9df 100644 --- a/src/audit/src/offline/bin/main.rs +++ b/src/audit/src/offline/bin/main.rs @@ -19,6 +19,7 @@ use lib::offline::{ use chain_core::packer::Codec; use color_eyre::{eyre::Context, Report}; +use std::env; use std::{error::Error, path::PathBuf}; /// @@ -36,6 +37,12 @@ pub struct Args { /// cross reference official results #[clap(short, long)] official_results: Option, + /// Gamma value for Quadratic scaling + #[clap(short, long)] + gamma: Option, + /// Rounding precision for arithmetic + #[clap(short, long)] + precision: Option, } fn main() -> Result<(), Box> { @@ -61,6 +68,16 @@ fn main() -> Result<(), Box> { info!("Audit Tool."); info!("Starting Offline Tally"); + if let Some(gamma) = args.gamma { + const GAMMA: &str = "QUADRATIC_VOTING_GAMMA"; + std::env::set_var(GAMMA, gamma); + } + + if let Some(precision) = args.precision { + const PRECISION: &str = "QUADRATIC_VOTING_PRECISION"; + std::env::set_var(PRECISION, precision); + } + // Load and replay fund fragments from storage let storage_path = PathBuf::from(args.fragments); diff --git a/src/chain-libs/chain-impl-mockchain/Cargo.toml b/src/chain-libs/chain-impl-mockchain/Cargo.toml index 83f4b2b74d..c75ceaaa80 100644 --- a/src/chain-libs/chain-impl-mockchain/Cargo.toml +++ b/src/chain-libs/chain-impl-mockchain/Cargo.toml @@ -32,6 +32,9 @@ criterion = { version = "0.3.0", optional = true } rand = "0.8" cryptoxide = "0.4" tracing.workspace = true +bigdecimal = "0.4.7" +num = "0.4.3" + [features] property-test-api = [ diff --git a/src/chain-libs/chain-impl-mockchain/src/vote/tally.rs b/src/chain-libs/chain-impl-mockchain/src/vote/tally.rs index 7d6a1c2f24..60288505aa 100644 --- a/src/chain-libs/chain-impl-mockchain/src/vote/tally.rs +++ b/src/chain-libs/chain-impl-mockchain/src/vote/tally.rs @@ -3,7 +3,16 @@ use crate::{ value::Value, vote::{Choice, Options}, }; +use bigdecimal::BigDecimal; +use bigdecimal::ToPrimitive; use chain_vote::EncryptedTally; +use num::BigRational; +use num::{BigInt, FromPrimitive}; +use std::num::NonZeroI64; + +use std::str::FromStr; + +use std::env; use std::fmt; use thiserror::Error; @@ -166,7 +175,28 @@ impl TallyResult { } else { let index = choice.as_byte() as usize; - self.results[index] = self.results[index].saturating_add(weight); + const GAMMA: &str = "QUADRATIC_VOTING_GAMMA"; + const PRECISION: &str = "QUADRATIC_VOTING_PRECISION"; + + // Apply quadratic scaling if gamma value specified in env var. Else gamma is 1 and has no effect. + let gamma = f64::from_str(&env::var(GAMMA).unwrap_or(1.0.to_string())).unwrap(); + + let precision = + i64::from_str(&env::var(PRECISION).unwrap_or(1.to_string())).unwrap_or(1); + + let gamma = + BigRational::from_f64(gamma).unwrap_or(BigRational::from_integer(BigInt::from(1))); + let denom = BigDecimal::from_bigint(gamma.denom().clone(), precision); + let numer = BigDecimal::from_bigint(gamma.numer().clone(), precision); + + let stake = BigDecimal::from(weight.0); + + let weight = ((stake.clone() * numer.clone()) / denom) + .round(precision) + .to_u64() + .unwrap_or(weight.0); + + self.results[index] = self.results[index].saturating_add(Weight(weight)); Ok(()) } diff --git a/src/chain-libs/chain-vote/Cargo.toml b/src/chain-libs/chain-vote/Cargo.toml index 6d31e20b84..d5baca7013 100644 --- a/src/chain-libs/chain-vote/Cargo.toml +++ b/src/chain-libs/chain-vote/Cargo.toml @@ -14,6 +14,10 @@ thiserror = "1.0" cryptoxide = "^0.4.2" const_format = "0.2" base64 = "0.21.0" +bigdecimal = "0.4.7" +num = "0.4.3" +tracing = "*" + [dev-dependencies] rand_chacha = "0.3" diff --git a/src/chain-libs/chain-vote/src/tally.rs b/src/chain-libs/chain-vote/src/tally.rs index d28543be17..ec146b10ce 100644 --- a/src/chain-libs/chain-vote/src/tally.rs +++ b/src/chain-libs/chain-vote/src/tally.rs @@ -1,5 +1,8 @@ +use std::num::NonZeroI64; use std::num::NonZeroU64; +use std::str::FromStr; + use crate::GroupElement; use crate::{ committee::*, @@ -9,8 +12,18 @@ use crate::{ TallyOptimizationTable, }; use base64::{engine::general_purpose, Engine as _}; +use num::BigInt; +use num::BigRational; +use num::FromPrimitive; +use tracing::info; + +use bigdecimal::BigDecimal; +use bigdecimal::ToPrimitive; use cryptoxide::blake2b::Blake2b; use cryptoxide::digest::Digest; + +use std::env; + use rand_core::{CryptoRng, RngCore}; /// Secret key for opening vote @@ -155,6 +168,28 @@ impl EncryptedTally { pub fn add(&mut self, ballot: &Ballot, weight: u64) { assert_eq!(ballot.vote().len(), self.r.len()); assert_eq!(ballot.fingerprint(), &self.fingerprint); + + const GAMMA: &str = "QUADRATIC_VOTING_GAMMA"; + const PRECISION: &str = "QUADRATIC_VOTING_PRECISION"; + + // Apply quadratic scaling if gamma value specified in env var. Else gamma is 1 and has no effect. + let gamma = f64::from_str(&env::var(GAMMA).unwrap_or(1.0.to_string())).unwrap(); + let precision = i64::from_str(&env::var(PRECISION).unwrap_or(1.to_string())).unwrap_or(1); + + let gamma = + BigRational::from_f64(gamma).unwrap_or(BigRational::from_integer(BigInt::from(1))); + let denom = BigDecimal::from_bigint(gamma.denom().clone(), precision); + let numer = BigDecimal::from_bigint(gamma.numer().clone(), precision); + + let stake = BigDecimal::from(weight); + + let weight = ((stake.clone() * numer.clone()) / denom) + .round(precision) + .to_u64() + .unwrap_or(weight); + + info!("stake before {:?} stake after {:?}", stake.to_u64(), weight); + for (ri, ci) in self.r.iter_mut().zip(ballot.vote().iter()) { *ri = &*ri + &(ci * weight); }