sinsemilla::chip.rs: Implement witness_message_* APIs.

witness_message() witnesses a full message given a bitstring.

The other two APIs, witness_message_piece_bitstring() and
witness_message_piece_field(), both witness a message piece, i.e.
part of a message that fits within a single base field element.

witness_message_piece_bitstring() takes in a bitstring, while
witness_message_piece_field() takes in a field element. In the
latter case, the number of words encoded must be specified.
This commit is contained in:
therealyingtong 2021-06-19 11:26:16 +08:00
parent 74e617b46d
commit 7cddc9b587
1 changed files with 62 additions and 3 deletions

View File

@ -128,7 +128,26 @@ impl SinsemillaInstructions<pallas::Affine, { sinsemilla::K }, { sinsemilla::C }
mut layouter: impl Layouter<pallas::Base>,
message: Vec<Option<bool>>,
) -> Result<Self::Message, Error> {
todo!()
// Message must be composed of `K`-bit words.
assert_eq!(message.len() % sinsemilla::K, 0);
// Message must have at most `sinsemilla::C` words.
assert!(message.len() / sinsemilla::K <= sinsemilla::C);
// Message piece must be at most `ceil(pallas::Base::NUM_BITS / sinsemilla::K)` bits
let piece_num_words = pallas::Base::NUM_BITS as usize / sinsemilla::K;
let pieces: Result<Vec<_>, _> = message
.chunks(piece_num_words * sinsemilla::K)
.enumerate()
.map(|(i, piece)| -> Result<Self::MessagePiece, Error> {
self.witness_message_piece_bitstring(
layouter.namespace(|| format!("message piece {}", i)),
piece,
)
})
.collect();
pieces.map(|pieces| pieces.into())
}
#[allow(non_snake_case)]
@ -137,7 +156,34 @@ impl SinsemillaInstructions<pallas::Affine, { sinsemilla::K }, { sinsemilla::C }
layouter: impl Layouter<pallas::Base>,
message_piece: &[Option<bool>],
) -> Result<Self::MessagePiece, Error> {
todo!()
// Message must be composed of `K`-bit words.
assert_eq!(message_piece.len() % sinsemilla::K, 0);
let num_words = message_piece.len() / sinsemilla::K;
// Message piece must be at most `ceil(C::Base::NUM_BITS / sinsemilla::K)` bits
let piece_max_num_words = pallas::Base::NUM_BITS as usize / sinsemilla::K;
assert!(num_words <= piece_max_num_words as usize);
// Closure to parse a bitstring (little-endian) into a base field element.
let to_base_field = |bits: &[Option<bool>]| -> Option<pallas::Base> {
assert!(bits.len() <= pallas::Base::NUM_BITS as usize);
let bits: Option<Vec<bool>> = bits.iter().cloned().collect();
let bytes: Option<Vec<u8>> = bits.map(|bits| {
// Pad bits to 256 bits
let pad_len = 256 - bits.len();
let mut bits = bits;
bits.extend_from_slice(&vec![false; pad_len]);
bits.chunks_exact(8)
.map(|byte| byte.iter().rev().fold(0u8, |acc, bit| acc * 2 + *bit as u8))
.collect()
});
bytes.map(|bytes| pallas::Base::from_bytes(&bytes.try_into().unwrap()).unwrap())
};
let piece_value = to_base_field(message_piece);
self.witness_message_piece_field(layouter, piece_value, num_words)
}
fn witness_message_piece_field(
@ -146,7 +192,20 @@ impl SinsemillaInstructions<pallas::Affine, { sinsemilla::K }, { sinsemilla::C }
field_elem: Option<pallas::Base>,
num_words: usize,
) -> Result<Self::MessagePiece, Error> {
todo!()
let config = self.config().clone();
let cell = layouter.assign_region(
|| "witness message piece",
|mut region| {
region.assign_advice(
|| "witness message piece",
config.bits,
0,
|| field_elem.ok_or(Error::SynthesisError),
)
},
)?;
Ok(MessagePiece::new(cell, field_elem, num_words))
}
#[allow(non_snake_case)]