use primitives::{
algebra::{
elliptic_curve::{BaseFieldElement, Curve25519Ristretto as EC, Point, Scalar},
field::{subfield_element::Mersenne107Element, Bit},
BoxedUint,
},
utils::codec::bincode_io,
};
use crate::circuit::old::v2::{
circuit::Circuit,
gate::Gate,
ops::{
BitPlaintextBinaryOp,
BitPlaintextUnaryOp,
BitShareBinaryOp,
BitShareUnaryOp,
Constant,
FieldPlaintextBinaryOp,
FieldPlaintextUnaryOp,
FieldShareBinaryOp,
FieldShareUnaryOp,
Input,
PointPlaintextBinaryOp,
PointPlaintextUnaryOp,
PointShareBinaryOp,
PointShareUnaryOp,
},
AlgebraicType,
FieldType,
Slice,
};
fn all_gates() -> Vec<Gate<EC>> {
let scalar = || Scalar::<EC>::from(1u64);
let base_field = || BaseFieldElement::<EC>::from(1u64);
let mersenne107 = || Mersenne107Element::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::Mersenne107(mersenne107())),
Gate::Constant(Constant::Mersenne107Batch(vec![mersenne107()])),
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::Mersenne107,
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::Mersenne107,
},
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<EC> {
Circuit {
ops: vec![
Gate::Input(Input::SecretPlaintext {
inputer: 0,
algebraic_type: AlgebraicType::ScalarField,
batch_size: 1,
}),
Gate::Input(Input::SecretPlaintext {
inputer: 1,
algebraic_type: AlgebraicType::ScalarField,
batch_size: 1,
}),
Gate::FieldShareBinaryOp {
x: 0,
y: 1,
op: FieldShareBinaryOp::Add,
},
],
output_gates: vec![2],
}
}
const EXPECTED_FINGERPRINT: &str =
"5fc39dda67753f0d21ea85c75d21c33d649933f761133ca2740aaf27965916f4";
#[test]
fn wire_format_matches_frozen_v2() {
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`old::v2` wire format changed -- it must never change. Revert whatever touched \
`old::v2::gate`/`ops`/`circuit::Circuit`; note that v2's `AlgebraicType`, `Slice`, and \
`GateIndex` are type aliases to `latest::mod`/`latest::slice`, so a change there breaks \
v2 too. Do not update EXPECTED_FINGERPRINT.\n"
);
}
#[test]
fn fixture_round_trips() {
let fixture = include_bytes!("testdata/circuit.bin");
let (circuit, gates): (Circuit<EC>, Vec<Gate<EC>>) =
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 -- `old::v2` must never change. Revert whatever touched it.\n"
);
}