use std::ops::{Index, IndexMut};
use derive_more::derive::{Add, AddAssign, Sub, SubAssign};
use primitives::correlated_randomness::bundler::BundleConsumer;
use serde::{Deserialize, Serialize};
use crate::{
circuit::{
AlgebraicType,
BitShareBinaryOp,
Circuit,
FieldShareBinaryOp,
FieldShareUnaryOp,
FieldType,
Gate,
GateExt,
Input,
PointShareBinaryOp,
PointShareUnaryOp,
ShareOrPlaintext,
},
config::MpcConfig,
preprocessing::iterator::PreprocessingIterator,
};
pub(crate) const AES_S_BOX_N_NETWORK_ROUNDS: usize = 8;
pub(crate) const AES_S_BOX_N_TRIPLES: usize = 34;
pub(crate) const SEMI_HONEST_PACKED_LANES: usize = 64;
#[derive(
Debug,
Copy,
Clone,
Default,
PartialEq,
Eq,
Add,
AddAssign,
Sub,
SubAssign,
Serialize,
Deserialize,
)]
#[repr(C)]
pub struct FieldCircuitPreprocessing {
pub singlets: usize,
pub triples: usize,
pub dabits: usize,
}
impl FieldCircuitPreprocessing {
pub fn componentwise_max(self, other: Self) -> Self {
Self {
singlets: self.singlets.max(other.singlets),
triples: self.triples.max(other.triples),
dabits: self.dabits.max(other.dabits),
}
}
}
#[derive(
Debug,
Copy,
Clone,
Default,
PartialEq,
Eq,
Add,
AddAssign,
Sub,
SubAssign,
Serialize,
Deserialize,
)]
#[repr(C)]
pub struct CircuitPreprocessing {
pub bit_singlets: usize,
pub bit_triples: usize,
pub base_field: FieldCircuitPreprocessing,
pub scalar: FieldCircuitPreprocessing,
pub mpc_field: FieldCircuitPreprocessing,
}
impl CircuitPreprocessing {
pub fn componentwise_max(self, other: Self) -> Self {
Self {
bit_singlets: self.bit_singlets.max(other.bit_singlets),
bit_triples: self.bit_triples.max(other.bit_triples),
base_field: self.base_field.componentwise_max(other.base_field),
scalar: self.scalar.componentwise_max(other.scalar),
mpc_field: self.mpc_field.componentwise_max(other.mpc_field),
}
}
}
impl Index<FieldType> for CircuitPreprocessing {
type Output = FieldCircuitPreprocessing;
fn index(&self, index: FieldType) -> &Self::Output {
match index {
FieldType::BaseField => &self.base_field,
FieldType::ScalarField => &self.scalar,
FieldType::MpcField => &self.mpc_field,
}
}
}
impl IndexMut<FieldType> for CircuitPreprocessing {
fn index_mut(&mut self, index: FieldType) -> &mut Self::Output {
match index {
FieldType::BaseField => &mut self.base_field,
FieldType::ScalarField => &mut self.scalar,
FieldType::MpcField => &mut self.mpc_field,
}
}
}
impl<C: MpcConfig> BundleConsumer for Circuit<C> {
type Iterator = PreprocessingIterator<C>;
fn required_preprocessing(&self) -> CircuitPreprocessing {
let mut circuit_preprocessing = CircuitPreprocessing::default();
for gate in self.iter_gates_ext() {
self.add_to_required_preprocessing(gate, &mut circuit_preprocessing);
}
circuit_preprocessing
}
}
impl<C: MpcConfig> Circuit<C> {
pub fn add_to_required_preprocessing(
&self,
gate: &GateExt<C>,
circuit_preprocessing: &mut CircuitPreprocessing,
) {
let batch_size = gate.output.get_batch_size() as usize;
match &gate.gate {
Gate::Input(Input::SecretPlaintext { algebraic_type, .. })
| Gate::Random { algebraic_type, .. } => match algebraic_type {
AlgebraicType::ScalarField | AlgebraicType::Point => {
circuit_preprocessing.scalar.singlets += batch_size;
}
AlgebraicType::BaseField => {
circuit_preprocessing.base_field.singlets += batch_size;
}
AlgebraicType::Bit => {
circuit_preprocessing.bit_singlets += batch_size;
}
AlgebraicType::MpcField => {
circuit_preprocessing.mpc_field.singlets += batch_size;
}
},
Gate::FieldShareUnaryOp { op, .. } => {
let field_type = gate.output.get_field_type_unchecked();
match op {
FieldShareUnaryOp::MulInverse | FieldShareUnaryOp::IsZero => {
circuit_preprocessing[field_type].triples += batch_size;
circuit_preprocessing[field_type].singlets += batch_size;
}
FieldShareUnaryOp::Open | FieldShareUnaryOp::Neg => (),
}
}
Gate::FieldShareBinaryOp { op, y, .. } => match op {
FieldShareBinaryOp::Mul => {
let field_type = gate.output.get_field_type_unchecked();
if self.gate_output_unchecked(*y).get_form() == ShareOrPlaintext::Share {
circuit_preprocessing[field_type].triples += batch_size;
}
}
FieldShareBinaryOp::Add => (),
},
Gate::PointShareUnaryOp { op, .. } => match op {
PointShareUnaryOp::IsZero => {
circuit_preprocessing.scalar.triples += batch_size;
circuit_preprocessing.scalar.singlets += batch_size;
}
PointShareUnaryOp::Open | PointShareUnaryOp::Neg => (),
},
Gate::PointShareBinaryOp { op, p, y, .. } => match op {
PointShareBinaryOp::ScalarMul => {
if self.gate_output_unchecked(*p).get_form() == ShareOrPlaintext::Share
&& self.gate_output_unchecked(*y).get_form() == ShareOrPlaintext::Share
{
circuit_preprocessing.scalar.triples += batch_size;
}
}
PointShareBinaryOp::Add => (),
},
Gate::BitShareBinaryOp { op, y, .. } => match op {
BitShareBinaryOp::And | BitShareBinaryOp::Or => {
if self.gate_output_unchecked(*y).get_form() == ShareOrPlaintext::Share {
circuit_preprocessing.bit_triples += batch_size;
}
}
BitShareBinaryOp::Xor => (),
},
Gate::BaseFieldPow { .. } => {
unimplemented!("Removed from the Bundler, need to choose exponent to set it back.")
}
Gate::DaBit { field_type, .. } => {
circuit_preprocessing[*field_type].dabits += batch_size
}
Gate::Input(_)
| Gate::Constant { .. }
| Gate::BatchSummation { .. }
| Gate::BitShareUnaryOp { .. }
| Gate::FieldPlaintextUnaryOp { .. }
| Gate::FieldPlaintextBinaryOp { .. }
| Gate::BitPlaintextUnaryOp { .. }
| Gate::BitPlaintextBinaryOp { .. }
| Gate::PointPlaintextUnaryOp { .. }
| Gate::PointPlaintextBinaryOp { .. }
| Gate::GetDaBitFieldShare { .. }
| Gate::GetDaBitSharedBit { .. }
| Gate::BitPlaintextToField { .. }
| Gate::FieldPlaintextToBit { .. }
| Gate::ExtractFromBatch { .. }
| Gate::CollectToBatch { .. }
| Gate::PointFromPlaintextCoordinates { .. }
| Gate::PlaintextPointToCoordinates { .. }
| Gate::PlaintextKeccakF1600 { .. }
| Gate::CompressPlaintextPoint { .. }
| Gate::KeyRecoveryPlaintextComputeErrors { .. }
| Gate::Ghash { .. } => (),
#[cfg(any(test, feature = "dev"))]
Gate::AesKeySchedule { key, .. } => {
let key_length = self.gate_ext_unchecked(*key).output.batch_size;
circuit_preprocessing.bit_triples += n_triples_aes_key_schedule(key_length as usize)
.expect("Something went wrong with Circuit::add_to_required_preprocessing for Gate::AesKeySchedule")
}
Gate::AesGcmKeyStream {
round_keys,
n_ciphertext_blocks,
..
} => {
let round_keys_length = self.gate_ext_unchecked(*round_keys).output.batch_size;
circuit_preprocessing.bit_triples +=
n_triples_aes_gcm_key_stream(round_keys_length as usize, *n_ciphertext_blocks)
.map(|n| n + AES_S_BOX_N_TRIPLES * 12 * (SEMI_HONEST_PACKED_LANES - 1))
.expect("Something went wrong with Circuit::add_to_required_preprocessing for Gate::AesGcmKeyStream")
}
Gate::GhashPowersOfH {
n_ciphertext_blocks,
..
} => {
circuit_preprocessing.bit_triples +=
(*n_ciphertext_blocks as usize - 1) * n_triples_gf2_128_multiply()
}
};
}
}
pub fn n_triples_aes_key_schedule(security_level: usize) -> Option<usize> {
let n_sub_bytes_calls = match security_level {
128 => Some(10),
192 => Some(8),
256 => Some(13),
_ => None,
};
n_sub_bytes_calls.map(|n_calls| 4 * AES_S_BOX_N_TRIPLES * n_calls)
}
pub fn n_triples_aes_gcm_key_stream(
round_keys_length: usize,
n_ciphertext_blocks: u32,
) -> Option<usize> {
let n_rounds = match round_keys_length {
1408 => Some(10),
1664 => Some(12),
1920 => Some(14),
_ => None,
};
n_rounds.map(|n| {
AES_S_BOX_N_TRIPLES
* (12
+ 4 * (1 + n_ciphertext_blocks as usize)
+ (n - 1) * 16 * (1 + n_ciphertext_blocks as usize))
})
}
pub fn n_triples_gf2_128_multiply() -> usize {
128 * 128
}
#[cfg(test)]
mod tests {
use crate::circuit::preprocessing::{CircuitPreprocessing, FieldCircuitPreprocessing};
#[test]
fn test_circuit_preprocessing_add() {
let a = CircuitPreprocessing {
bit_singlets: 0,
bit_triples: 1,
base_field: FieldCircuitPreprocessing {
singlets: 3,
triples: 4,
dabits: 2,
},
scalar: FieldCircuitPreprocessing {
singlets: 1,
triples: 2,
dabits: 1,
},
mpc_field: FieldCircuitPreprocessing {
singlets: 0,
triples: 0,
dabits: 0,
},
};
let b = CircuitPreprocessing {
bit_singlets: 3,
bit_triples: 4,
base_field: FieldCircuitPreprocessing {
singlets: 0,
triples: 5,
dabits: 3,
},
scalar: FieldCircuitPreprocessing {
singlets: 2,
triples: 3,
dabits: 2,
},
mpc_field: FieldCircuitPreprocessing {
singlets: 3,
triples: 2,
dabits: 0,
},
};
let c = a + b;
assert_eq!(c.scalar.singlets, 3);
assert_eq!(c.scalar.triples, 5);
assert_eq!(c.base_field.singlets, 3);
assert_eq!(c.base_field.triples, 9);
assert_eq!(c.bit_singlets, 3);
assert_eq!(c.bit_triples, 5);
assert_eq!(c.mpc_field.dabits, 0);
assert_eq!(c.mpc_field.singlets, 3);
assert_eq!(c.mpc_field.triples, 2);
assert_eq!(c.scalar.dabits, 3);
assert_eq!(c.base_field.dabits, 5);
}
}