pbc_zk_core/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
#![doc = include_str!("../README.md")]

mod sbi;
mod secret_binary;

use pbc_abi::abi_model::TypeSpec;
use std::io::{Read, Write};

use pbc_abi::{
    create_type_spec::{NamedTypeLookup, NamedTypeSpecs},
    CreateTypeSpec,
};
pub use sbi::FromToBits;
#[cfg(doc)]
pub use sbi::Sbi;
#[cfg(not(doc))]
use sbi::Sbi;
#[allow(unused_imports)] // Reexport
pub use secret_binary::*;

/// A secret-shared [`bool`] value.
pub type Sbu1 = bool;

/// A secret-shared [`bool`] value. Deprecated, prefer [`Sbu1`].
#[deprecated(note = "Use `pbc_zk::Sbu1` instead. Change made to improve type consistency.")]
pub type Sbi1 = Sbu1;

macro_rules! sbi_impl {
    ($SelfT:ident, $InnerT:ty) => {
        #[doc = concat!("A [`", stringify!($InnerT), "`] secret-shared using a binary field.")]
        ///
        #[doc = concat!("Can be used similarly to normal/public [`", stringify!($InnerT), "`] values, with the restriction that any public [`", stringify!($InnerT), "`] must be upgraded to an [`", stringify!($SelfT), "`] before usage.")]
        ///
        /// For example:
        ///
        /// ```
        /// # use pbc_zk_core::*;
        #[doc = concat!("let x = ", stringify!($SelfT), "::from(1);")]
        #[doc = concat!("let y = ", stringify!($SelfT), "::from(9);")]
        #[doc = concat!("assert_eq!(x + y, ", stringify!($SelfT), "::from(10));")]
        /// ```
        ///
        /// ## Information Flow Control
        ///
        /// Variables in REAL must satisfy some [information
        /// flow](https://en.wikipedia.org/wiki/Information_flow_(information_theory))
        /// requirements, which states that it must not be possible to infer the value of
        /// a secret variable. This basic requirement then results in a bunch of type constraints:
        ///
        /// - `Sbi` values cannot be downcasted to public integers.
        /// - While-statements on `Sbi` values is impossible.
        /// - If-statements on `Sbi` values has some additional limitations in the branches:
        ///   * Can only assign secret-shared values.
        ///   * Cannot call functions.
        ///   * Cannot use while-statements.
        ///   * Cannot use public if-statements.
        ///
        /// Here are some examples of disallowed data flows:
        ///
        /// ```compile_fail
        /// # use pbc_zk_core::*;
        #[doc = concat!("let x: ", stringify!($SelfT), " = ", stringify!($SelfT), "::from(9);")]
        #[doc = concat!("let y: ", stringify!($InnerT), "   = x;       // Fails: Secrets cannot become public")]
        #[doc = concat!("let z: ", stringify!($SelfT), " = x + 10;  // Fails: Missing cast")]
        /// ```
        ///
        /// ## Representation
        ///
        /// The true value of instances of this type is secret-shared across several computation nodes,
        /// and certain operations require network communication, which may result in a significantly slower
        /// operation than on their public equivalents.
        ///
        #[doc = concat!("[`", stringify!($SelfT), "`]")]
        /// uses a binary-field secret-sharing, which allows operations on bits to be done
        /// efficiently, but arithmetic operations needs to be implemented using the bit
        /// operations, which can be slow.
        ///
        /// Here is a rough overview of the theoretic performance. `N` is the type length in bits. Lower numbers are better:
        ///
        /// | Operation | Rounds<br>(#Networking Packets) | Multiplications<br>(Size of Networking Packets)|
        /// | --------- | ------ | --------------- |
        /// | [`BitXor`](std::ops::BitXor) | `0` | `0` |
        /// | [`Shl`](std::ops::Shl) / [`Shr`](std::ops::Shr) | `0` | `0` |
        /// | [`BitAnd`](std::ops::BitAnd) | `1` | `N` |
        /// | [`BitOr`](std::ops::BitOr) | `1` | `N` |
        /// | If-expression return value | `1` | `2*N` |
        /// | [`PartialEq`](PartialEq) / [`Eq`](Eq) | `log2(N)` | `N-1` |
        /// | [`Add`](std::ops::Add) / [`Sub`](std::ops::BitXor) | `N` | `2*N` |
        /// | [`Ord`](Ord) (lt, gt) | `N` | `2*N` |
        /// | [`Mul`](std::ops::Mul) | `N` squared | `2*N` squared |
        ///
        /// ### Example
        ///
        /// Consider the following program:
        ///
        /// ```rust
        /// # use pbc_zk_core::*;
        /// fn max(a: Sbi32, b: Sbi32) -> Sbi32 {
        ///     if a < b { b } else { a }
        /// }
        /// ```
        ///
        /// One execution of `max` would involve `33` rounds (`32` from `<` and `1` from `if`) and `128` multiplications (`64` from `<` and `64` from `if`).
        pub type $SelfT = Sbi<$InnerT>;
    };
}

sbi_impl! { Sbi8, i8 }
sbi_impl! { Sbi16, i16 }
sbi_impl! { Sbi32, i32 }
sbi_impl! { Sbi64, i64 }
sbi_impl! { Sbi128, i128 }
sbi_impl! { Sbu8, u8 }
sbi_impl! { Sbu16, u16 }
sbi_impl! { Sbu32, u32 }
sbi_impl! { Sbu64, u64 }
sbi_impl! { Sbu128, u128 }

/// Required for secret-shared values.
/// Secret variables are serialized like their public counterparts using the
/// [State serialization format](https://partisiablockchain.gitlab.io/documentation/smart-contracts/smart-contract-binary-formats.html#state-binary-format).
pub trait SecretBinary {
    /// Deserialization method for a secret.
    fn secret_read_from<T: Read>(reader: &mut T) -> Self;
    /// Serialization method for a secret.
    fn secret_write_to<T: Write>(&self, writer: &mut T) -> std::io::Result<()>;
}
pub use crate::SecretBinary as Secret;

/// Required for secret-shared values. Used to determine the size of secret-shared inputs.
pub trait SecretBinaryFixedSize {
    /// The bitsize of the type.
    const BITS: u32;
}

macro_rules! impl_secretbinary_delegate_to_read_write_state {
    ($($type:ty)*) => {
        $(
            #[doc = "Implementation of the [`SecretBinary`] trait for [`"]
            #[doc = stringify!($type)]
            #[doc = "`]."]
            impl SecretBinary for $type {
                fn secret_read_from<ReadT: Read>(reader: &mut ReadT) -> Self {
                    <Self as pbc_traits::ReadWriteState>::state_read_from(reader)
                }

                fn secret_write_to<WriteT: Write>(&self, writer: &mut WriteT) -> std::io::Result<()> {
                    <Self as pbc_traits::ReadWriteState>::state_write_to(self, writer)
                }
            }

            impl SecretBinaryFixedSize for $type {
                const BITS: u32 = <$type>::BITS as u32;
            }
        )*
    }
}

impl_secretbinary_delegate_to_read_write_state! {
    i8
    i16
    i32
    i64
    i128
    u8
    u16
    u32
    u64
    u128
}

/// The output is n implementations of [`CreateTypeSpec`] that simply write the type as a string
/// and fill the ordinal in the [`CreateTypeSpec::__ty_spec_write`] vector output.
macro_rules! impl_createtypespec_for_type {
    ($($type:ty, $val:ident)*) => {
        $(
            #[doc = "Implementation of the [`CreateTypeSpec`] trait for [`"]
            #[doc = stringify!($type)]
            #[doc = "`]."]
            impl CreateTypeSpec for $type {

                #[doc = concat!("Constant string [`", stringify!($type), "`].")]
                fn __ty_name() -> String {
                    format!("{}", stringify!($type).to_string())
                }

                #[doc = concat!("Ordinal is `", stringify!($val), "`, as defined in [ABI Spec](https://partisiablockchain.gitlab.io/documentation/smart-contracts/smart-contract-binary-formats.html).")]
                fn __ty_identifier() -> String {
                    Self::__ty_name()
                }

                fn __ty_generate_spec(
                    _named_type_index_lookup: &mut NamedTypeLookup,
                    _named_type_specs: &mut NamedTypeSpecs,)
                -> TypeSpec {
                    TypeSpec::$val
                }
            }
        )*
    }
}

// Sbi types are mapped to their public counterparts.
impl_createtypespec_for_type!(
    Sbu8,   U8
    Sbu16,  U16
    Sbu32,  U32
    Sbu64,  U64
    Sbu128, U128
    Sbi8,   I8
    Sbi16,  I16
    Sbi32,  I32
    Sbi64,  I64
    Sbi128, I128
);

/// Implementation of [`SecretBinary`] for arrays of arbitrary sizes and types.
impl<const LEN: usize, ElementT: SecretBinary + Sized> SecretBinary for [ElementT; LEN] {
    fn secret_read_from<T: Read>(reader: &mut T) -> Self {
        let mut data: [std::mem::MaybeUninit<ElementT>; LEN] =
            unsafe { std::mem::MaybeUninit::uninit().assume_init() };
        for element_addr in &mut data[..] {
            element_addr.write(<ElementT as SecretBinary>::secret_read_from(reader));
        }
        data.map(|x| unsafe { x.assume_init() })
    }

    fn secret_write_to<T: Write>(&self, writer: &mut T) -> std::io::Result<()> {
        for elem in self {
            <ElementT as SecretBinary>::secret_write_to(elem, writer)?;
        }
        Ok(())
    }
}

impl SecretBinary for Vec<Sbu8> {
    fn secret_read_from<T: Read>(_reader: &mut T) -> Self {
        panic!("Deserialization of Vec<Sbu8> not supported.")
    }

    fn secret_write_to<T: Write>(&self, _writer: &mut T) -> std::io::Result<()> {
        panic!("Serialization of Vec<Sbu8> not supported.")
    }
}

impl SecretBinary for Vec<Sbi8> {
    fn secret_read_from<T: Read>(_reader: &mut T) -> Self {
        panic!("Deserialization of Vec<Sbi8> not supported.")
    }

    fn secret_write_to<T: Write>(&self, _writer: &mut T) -> std::io::Result<()> {
        panic!("Serialization of Vec<Sbi8> not supported.")
    }
}