use primitives::{
algebra::{
elliptic_curve::{BaseFieldElement, Curve25519Ristretto as EC, Point, Scalar},
field::Bit,
BoxedUint,
},
utils::codec::bincode_io,
};
use crate::{
circuit::latest::{
AlgebraicType,
BitPlaintextBinaryOp,
BitPlaintextUnaryOp,
BitShareBinaryOp,
BitShareUnaryOp,
Circuit,
Constant,
FieldPlaintextBinaryOp,
FieldPlaintextUnaryOp,
FieldShareBinaryOp,
FieldShareUnaryOp,
FieldType,
Gate,
Input,
PointPlaintextBinaryOp,
PointPlaintextUnaryOp,
PointShareBinaryOp,
PointShareUnaryOp,
Slice,
},
config::{DefaultConfig as C, MpcFieldElement},
};
fn all_gates() -> Vec<Gate<C>> {
let scalar = || Scalar::<EC>::from(1u64);
let base_field = || BaseFieldElement::<EC>::from(1u64);
let mpc_field = || MpcFieldElement::<C>::from(1u64);
let bit = || Bit::from(false);
let point = || Point::<EC>::identity();
let exp = || BoxedUint::from(vec![1u64]);
vec![
Gate::Input(Input::Plaintext {
algebraic_type: AlgebraicType::BaseField,
batch_size: 1,
}),
Gate::Input(Input::SecretPlaintext {
inputer: 0,
algebraic_type: AlgebraicType::ScalarField,
batch_size: 1,
}),
Gate::Input(Input::Share {
algebraic_type: AlgebraicType::Point,
batch_size: 1,
}),
Gate::Constant(Constant::Scalar(scalar())),
Gate::Constant(Constant::ScalarBatch(vec![scalar()])),
Gate::Constant(Constant::BaseField(base_field())),
Gate::Constant(Constant::BaseFieldBatch(vec![base_field()])),
Gate::Constant(Constant::MpcField(mpc_field())),
Gate::Constant(Constant::MpcFieldBatch(vec![mpc_field()])),
Gate::Constant(Constant::Bit(bit())),
Gate::Constant(Constant::BitBatch(vec![bit()])),
Gate::Constant(Constant::Point(point())),
Gate::Constant(Constant::PointBatch(vec![point()])),
Gate::Random {
algebraic_type: AlgebraicType::BaseField,
batch_size: 1,
},
Gate::Random {
algebraic_type: AlgebraicType::ScalarField,
batch_size: 1,
},
Gate::Random {
algebraic_type: AlgebraicType::Point,
batch_size: 1,
},
Gate::Random {
algebraic_type: AlgebraicType::Bit,
batch_size: 1,
},
Gate::Random {
algebraic_type: AlgebraicType::MpcField,
batch_size: 1,
},
Gate::FieldShareUnaryOp {
x: 0,
op: FieldShareUnaryOp::Neg,
},
Gate::FieldShareUnaryOp {
x: 0,
op: FieldShareUnaryOp::MulInverse,
},
Gate::FieldShareUnaryOp {
x: 0,
op: FieldShareUnaryOp::Open,
},
Gate::FieldShareUnaryOp {
x: 0,
op: FieldShareUnaryOp::IsZero,
},
Gate::FieldShareBinaryOp {
x: 0,
y: 1,
op: FieldShareBinaryOp::Add,
},
Gate::FieldShareBinaryOp {
x: 0,
y: 1,
op: FieldShareBinaryOp::Mul,
},
Gate::BatchSummation { x: 0 },
Gate::BitShareUnaryOp {
x: 0,
op: BitShareUnaryOp::Not,
},
Gate::BitShareUnaryOp {
x: 0,
op: BitShareUnaryOp::Open,
},
Gate::BitShareBinaryOp {
x: 0,
y: 1,
op: BitShareBinaryOp::Xor,
},
Gate::BitShareBinaryOp {
x: 0,
y: 1,
op: BitShareBinaryOp::Or,
},
Gate::BitShareBinaryOp {
x: 0,
y: 1,
op: BitShareBinaryOp::And,
},
Gate::PointShareUnaryOp {
p: 0,
op: PointShareUnaryOp::Neg,
},
Gate::PointShareUnaryOp {
p: 0,
op: PointShareUnaryOp::Open,
},
Gate::PointShareUnaryOp {
p: 0,
op: PointShareUnaryOp::IsZero,
},
Gate::PointShareBinaryOp {
p: 0,
y: 1,
op: PointShareBinaryOp::Add,
},
Gate::PointShareBinaryOp {
p: 0,
y: 1,
op: PointShareBinaryOp::ScalarMul,
},
Gate::FieldPlaintextUnaryOp {
x: 0,
op: FieldPlaintextUnaryOp::Neg,
},
Gate::FieldPlaintextUnaryOp {
x: 0,
op: FieldPlaintextUnaryOp::MulInverse,
},
Gate::FieldPlaintextUnaryOp {
x: 0,
op: FieldPlaintextUnaryOp::BitExtract {
little_endian_bit_idx: 0,
signed: false,
},
},
Gate::FieldPlaintextUnaryOp {
x: 0,
op: FieldPlaintextUnaryOp::Sqrt,
},
Gate::FieldPlaintextUnaryOp {
x: 0,
op: FieldPlaintextUnaryOp::Pow { exp: exp() },
},
Gate::FieldPlaintextBinaryOp {
x: 0,
y: 1,
op: FieldPlaintextBinaryOp::Add,
},
Gate::FieldPlaintextBinaryOp {
x: 0,
y: 1,
op: FieldPlaintextBinaryOp::Mul,
},
Gate::FieldPlaintextBinaryOp {
x: 0,
y: 1,
op: FieldPlaintextBinaryOp::EuclDiv,
},
Gate::FieldPlaintextBinaryOp {
x: 0,
y: 1,
op: FieldPlaintextBinaryOp::Mod,
},
Gate::FieldPlaintextBinaryOp {
x: 0,
y: 1,
op: FieldPlaintextBinaryOp::Gt,
},
Gate::FieldPlaintextBinaryOp {
x: 0,
y: 1,
op: FieldPlaintextBinaryOp::Ge,
},
Gate::FieldPlaintextBinaryOp {
x: 0,
y: 1,
op: FieldPlaintextBinaryOp::Eq,
},
Gate::FieldPlaintextBinaryOp {
x: 0,
y: 1,
op: FieldPlaintextBinaryOp::Xor,
},
Gate::FieldPlaintextBinaryOp {
x: 0,
y: 1,
op: FieldPlaintextBinaryOp::Or,
},
Gate::BitPlaintextUnaryOp {
x: 0,
op: BitPlaintextUnaryOp::Not,
},
Gate::BitPlaintextBinaryOp {
x: 0,
y: 1,
op: BitPlaintextBinaryOp::Xor,
},
Gate::BitPlaintextBinaryOp {
x: 0,
y: 1,
op: BitPlaintextBinaryOp::Or,
},
Gate::BitPlaintextBinaryOp {
x: 0,
y: 1,
op: BitPlaintextBinaryOp::And,
},
Gate::PointPlaintextUnaryOp {
p: 0,
op: PointPlaintextUnaryOp::Neg,
},
Gate::PointPlaintextBinaryOp {
p: 0,
y: 1,
op: PointPlaintextBinaryOp::Add,
},
Gate::PointPlaintextBinaryOp {
p: 0,
y: 1,
op: PointPlaintextBinaryOp::ScalarMul,
},
Gate::DaBit {
field_type: FieldType::BaseField,
batch_size: 1,
},
Gate::DaBit {
field_type: FieldType::ScalarField,
batch_size: 1,
},
Gate::DaBit {
field_type: FieldType::MpcField,
batch_size: 1,
},
Gate::GetDaBitFieldShare { x: 0 },
Gate::GetDaBitSharedBit { x: 0 },
Gate::BaseFieldPow { x: 0, exp: exp() },
Gate::BitPlaintextToField {
x: 0,
field_type: FieldType::BaseField,
},
Gate::BitPlaintextToField {
x: 0,
field_type: FieldType::ScalarField,
},
Gate::BitPlaintextToField {
x: 0,
field_type: FieldType::MpcField,
},
Gate::FieldPlaintextToBit { x: 0 },
Gate::ExtractFromBatch {
x: 0,
slice: Slice::single(0),
},
Gate::ExtractFromBatch {
x: 0,
slice: Slice::range(0, 2, 1).expect("valid slice"),
},
Gate::ExtractFromBatch {
x: 0,
slice: Slice::range2d(0, 2, 2, 1, 1).expect("valid slice"),
},
Gate::ExtractFromBatch {
x: 0,
slice: {
let mut s = Slice::empty();
s.append(Slice::single(0));
s
},
},
Gate::CollectToBatch { wires: vec![0, 1] },
Gate::PointFromPlaintextCoordinates { wires: vec![0, 1] },
Gate::PlaintextPointToCoordinates { point: 0 },
Gate::PlaintextKeccakF1600 { x: 0 },
Gate::CompressPlaintextPoint { point: 0 },
Gate::KeyRecoveryPlaintextComputeErrors {
d_minus_one: 0,
syndromes: 1,
},
Gate::AesGcmKeyStream {
round_keys: 0,
iv: 1,
n_ciphertext_blocks: 1,
},
Gate::GhashPowersOfH {
h: 0,
n_ciphertext_blocks: 1,
},
Gate::Ghash {
x: 0,
powers_of_h: 1,
},
Gate::AesKeySchedule { key: 0 },
]
}
fn sample_circuit() -> Circuit<C> {
let mut circuit = Circuit::new();
let x = circuit
.add_gate(Gate::Input(Input::SecretPlaintext {
inputer: 0,
algebraic_type: AlgebraicType::ScalarField,
batch_size: 1,
}))
.expect("valid gate");
let y = circuit
.add_gate(Gate::Input(Input::SecretPlaintext {
inputer: 1,
algebraic_type: AlgebraicType::ScalarField,
batch_size: 1,
}))
.expect("valid gate");
let z = circuit
.add_gate(Gate::FieldShareBinaryOp {
x,
y,
op: FieldShareBinaryOp::Add,
})
.expect("valid gate");
circuit.add_output(z).expect("valid output");
circuit
}
const EXPECTED_FINGERPRINT: &str =
"637127a1095d3982e133952770b4d277e9aa8ab02894647079715eac8189bc2e";
#[test]
fn wire_format_fingerprint() {
let bytes = bincode_io::serialize(&(sample_circuit(), all_gates()))
.expect("circuit/gate bincode serialization");
let fingerprint = blake3::hash(&bytes).to_hex().to_string();
assert_eq!(
fingerprint, EXPECTED_FINGERPRINT,
"\n\n`latest::Circuit`/`Gate` wire format changed. See module docs for the required \
steps (bump CIRCUIT_FORMAT_VERSION, freeze old::vN, update EXPECTED_FINGERPRINT).\n"
);
}
#[cfg(feature = "dump-fixtures")]
#[test]
fn dump_fixture() {
let bytes = bincode_io::serialize(&(sample_circuit(), all_gates()))
.expect("circuit/gate bincode serialization");
let path = concat!(
env!("CARGO_MANIFEST_DIR"),
"/src/circuit/latest/testdata/circuit.bin"
);
std::fs::write(path, &bytes).unwrap_or_else(|e| panic!("write {path}: {e}"));
}
#[test]
fn fixture_round_trips() {
let fixture = include_bytes!("testdata/circuit.bin");
let (circuit, gates): (Circuit<C>, Vec<Gate<C>>) =
bincode_io::deserialize(fixture).expect("fixture must still deserialize");
let bytes =
bincode_io::serialize(&(circuit, gates)).expect("circuit/gate bincode serialization");
assert_eq!(
bytes, fixture,
"\n\nRe-encoding the decoded `testdata/circuit.bin` fixture didn't reproduce the original \
bytes -- decode/encode are no longer inverses for `latest::Circuit`/`Gate`. See module \
docs.\n"
);
}
const EXPECTED_JSON_FINGERPRINT: &str =
"3427f0c1c8668a6b482b52897bccfb5fefaf6315e7eccad8c92f28856dab1ee8";
#[test]
#[ignore = "informational only - run explicitly in CI as a soft warning, see doc comment above"]
fn field_names_fingerprint() {
let json = serde_json::to_string(&(sample_circuit(), all_gates()))
.expect("circuit/gate json serialization");
let fingerprint = blake3::hash(json.as_bytes()).to_hex().to_string();
assert_eq!(
fingerprint, EXPECTED_JSON_FINGERPRINT,
"\n\nA `Gate`/op variant or `Circuit` field was renamed. Harmless for `bincode`; update \
EXPECTED_JSON_FINGERPRINT if intentional.\n"
);
}