zkinterface_bellman 1.3.2

Bellman circuit construction and proving system for zkInterface
use std::path::Path;
use std::marker::PhantomData;

use zkinterface::{ConstraintSystem, Witness, Variables, KeyValue, StatementBuilder, Sink, WorkspaceSink, BilinearConstraint};
use bellman as bl;
use bellman::{Variable, Index, LinearCombination, SynthesisError};
use ff::PrimeField;
use super::export::{write_scalar, to_zkif_constraint};
use std::mem;

const DEFAULT_CONSTRAINTS_PER_MESSAGE: usize = 100000;


pub struct ZkifCS<Scalar: PrimeField> {
    pub constraints_per_message: usize,

    statement: StatementBuilder<WorkspaceSink>,
    constraints: ConstraintSystem,
    proving: bool,
    witness: Vec<u8>,
    phantom: PhantomData<Scalar>,
}

impl<Scalar: PrimeField> ZkifCS<Scalar> {
    /// Must call finish() to finalize the files in the workspace.
    pub fn new(workspace: impl AsRef<Path>, proving: bool) -> Self {
        let sink = WorkspaceSink::new(workspace).unwrap();
        let statement = StatementBuilder::new(sink);

        ZkifCS {
            constraints_per_message: DEFAULT_CONSTRAINTS_PER_MESSAGE,
            statement,
            constraints: ConstraintSystem::default(),
            proving,
            witness: vec![],
            phantom: PhantomData,
        }
    }

    pub fn finish(mut self, name: &str) -> zkinterface::Result<()> {
        if self.constraints.constraints.len() > 0 {
            self.statement.push_constraints(self.constraints)?;
        }

        if self.proving {
            let wit = Witness {
                assigned_variables: Variables {
                    variable_ids: self.statement.header.list_witness_ids(),
                    values: Some(self.witness.clone()),
                }
            };
            self.statement.push_witness(wit)?;
        }

        let negative_one = Scalar::one().neg();
        let mut field_maximum = Vec::<u8>::new();
        write_scalar(&negative_one, &mut field_maximum);

        self.statement.header.field_maximum = Some(field_maximum);
        self.statement.header.configuration = Some(vec![
            KeyValue {
                key: "name".to_string(),
                text: Some(name.to_string()),
                data: None,
                number: 0,
            }]);
        self.statement.finish_header()
    }

    fn push_constraint(&mut self, co: BilinearConstraint) -> zkinterface::Result<()> {
        self.constraints.constraints.push(co);

        if self.constraints.constraints.len() >= self.constraints_per_message {
            let cs = mem::replace(&mut self.constraints, ConstraintSystem::default());
            self.statement.push_constraints(cs)?;
        }
        Ok(())
    }
}

impl<Scalar: PrimeField> bl::ConstraintSystem<Scalar> for ZkifCS<Scalar> {
    type Root = Self;

    fn alloc<F, A, AR>(&mut self, _annotation: A, f: F) -> Result<Variable, SynthesisError>
        where F: FnOnce() -> Result<Scalar, SynthesisError>,
              A: FnOnce() -> AR, AR: Into<String>
    {
        let zkid = self.statement.allocate_var();
        if self.proving {
            let fr = f()?;
            write_scalar(&fr, &mut self.witness);
        }
        Ok(Variable::new_unchecked(Index::Aux(zkid as usize)))
    }

    fn alloc_input<F, A, AR>(&mut self, _annotation: A, f: F) -> Result<Variable, SynthesisError>
        where F: FnOnce() -> Result<Scalar, SynthesisError>,
              A: FnOnce() -> AR, AR: Into<String>
    {
        let value = f()?;
        let mut encoded = vec![];
        write_scalar(&value, &mut encoded);

        let zkid = self.statement.allocate_instance_var(&encoded);
        Ok(Variable::new_unchecked(Index::Input(zkid as usize)))
    }

    fn enforce<A, AR, LA, LB, LC>(&mut self, _annotation: A, a: LA, b: LB, c: LC)
        where A: FnOnce() -> AR, AR: Into<String>,
              LA: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
              LB: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
              LC: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>
    {
        let a = a(LinearCombination::zero());
        let b = b(LinearCombination::zero());
        let c = c(LinearCombination::zero());

        let co = to_zkif_constraint(a, b, c);
        self.push_constraint(co).unwrap();
    }

    fn push_namespace<NR, N>(&mut self, _name_fn: N) where NR: Into<String>, N: FnOnce() -> NR {}

    fn pop_namespace(&mut self) {}

    fn get_root(&mut self) -> &mut Self::Root {
        self
    }
}


#[test]
fn test_zkif_cs() -> zkinterface::Result<()> {
    use std::path::Path;
    use std::fs::remove_dir_all;
    use bellman::ConstraintSystem as BLCS;
    use bls12_381::Scalar;
    use zkinterface::{Messages, CircuitHeader, ConstraintSystem, Workspace};
    use zkinterface::consumers::simulator::Simulator;

    let dir = Path::new("local/test/");
    let _ = remove_dir_all(dir);

    let mut cs = ZkifCS::<Scalar>::new(dir, true);

    // Create 10 constraints to store in chunks of 4.
    cs.constraints_per_message = 4;
    let n_constraints = 10;

    let (xv, yv) = (10, 11);

    let one = ZkifCS::<Scalar>::one();
    let x = cs.alloc_input(
        || "x", || Ok(Scalar::from(xv)))?;
    let y = cs.alloc(
        || "y", || Ok(Scalar::from(yv)))?;
    let z = cs.alloc(
        || "z", || Ok(Scalar::from(xv * (xv + yv))))?;

    for i in 0..n_constraints {
        cs.enforce(
            || "constraint",
            |zero| zero + x,
            |zero| zero + x + y + (Scalar::from(i), one),
            |zero| zero + z + (Scalar::from(i * xv), one),
        );
    }

    cs.finish("test")?;

    let ws = Workspace::from_dir(dir)?;

    let mut simulator = Simulator::default();
    for msg in ws.iter_messages() {
        simulator.ingest_message(&msg);
    }
    assert_eq!(simulator.get_violations().len(), 0);

    let messages = ws.read_all_messages();
    let expected = Messages {
        circuit_headers: vec![CircuitHeader {
            instance_variables: Variables {
                variable_ids: vec![1],
                values: Some(vec![10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
            },
            free_variable_id: 4,
            field_maximum: Some(vec![0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115]),
            configuration: Some(vec![KeyValue { key: "name".to_string(), text: Some("test".to_string()), data: None, number: 0 }]),
        }],

        constraint_systems: vec![
            // First chunk of 4 constraints.
            ConstraintSystem {
                constraints: vec![
                    BilinearConstraint { linear_combination_a: Variables { variable_ids: vec![1], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_b: Variables { variable_ids: vec![1, 2, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_c: Variables { variable_ids: vec![3, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } },
                    BilinearConstraint { linear_combination_a: Variables { variable_ids: vec![1], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_b: Variables { variable_ids: vec![1, 2, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_c: Variables { variable_ids: vec![3, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } },
                    BilinearConstraint { linear_combination_a: Variables { variable_ids: vec![1], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_b: Variables { variable_ids: vec![1, 2, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_c: Variables { variable_ids: vec![3, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } },
                    BilinearConstraint { linear_combination_a: Variables { variable_ids: vec![1], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_b: Variables { variable_ids: vec![1, 2, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_c: Variables { variable_ids: vec![3, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } },
                ]
            },
            // Second chunk of 4 constraints.
            ConstraintSystem {
                constraints: vec![
                    BilinearConstraint { linear_combination_a: Variables { variable_ids: vec![1], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_b: Variables { variable_ids: vec![1, 2, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_c: Variables { variable_ids: vec![3, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } },
                    BilinearConstraint { linear_combination_a: Variables { variable_ids: vec![1], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_b: Variables { variable_ids: vec![1, 2, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_c: Variables { variable_ids: vec![3, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } },
                    BilinearConstraint { linear_combination_a: Variables { variable_ids: vec![1], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_b: Variables { variable_ids: vec![1, 2, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_c: Variables { variable_ids: vec![3, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } },
                    BilinearConstraint { linear_combination_a: Variables { variable_ids: vec![1], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_b: Variables { variable_ids: vec![1, 2, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_c: Variables { variable_ids: vec![3, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } },
                ]
            },
            // Final chunk of 2 constraints.
            ConstraintSystem {
                constraints: vec![
                    BilinearConstraint { linear_combination_a: Variables { variable_ids: vec![1], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_b: Variables { variable_ids: vec![1, 2, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_c: Variables { variable_ids: vec![3, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } },
                    BilinearConstraint { linear_combination_a: Variables { variable_ids: vec![1], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_b: Variables { variable_ids: vec![1, 2, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_c: Variables { variable_ids: vec![3, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } },
                ]
            }
        ],

        witnesses: vec![Witness {
            assigned_variables: Variables {
                variable_ids: vec![2, 3],
                values: Some(vec![
                    11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                    210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
            }
        }],
    };

    assert_eq!(messages, expected);

    Ok(())
}