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
use alloc::vec::Vec;
use super::{
ByteReader, ByteWriter, Deserializable, DeserializationError, Digest, Felt, Hasher, NoteError,
Serializable, WORD_SIZE, ZERO,
};
use crate::MAX_INPUTS_PER_NOTE;
// NOTE INPUTS
// ================================================================================================
/// A container for note inputs.
///
/// A note can be associated with up to 128 input values. Each value is represented by a single
/// field element. Thus, note input values can contain up to ~1 KB of data.
///
/// All inputs associated with a note can be reduced to a single commitment which is computed by
/// first padding the inputs with ZEROs to the next multiple of 8, and then by computing a
/// sequential hash of the resulting elements.
#[derive(Clone, Debug)]
pub struct NoteInputs {
values: Vec<Felt>,
hash: Digest,
}
impl NoteInputs {
/// Maximum number of input values associated with a single note.
const MAX_INPUTS_PER_NOTE: usize = MAX_INPUTS_PER_NOTE;
// CONSTRUCTOR
// --------------------------------------------------------------------------------------------
/// Returns [NoteInputs] instantiated from the provided values.
///
/// # Errors
/// Returns an error if the number of provided inputs is greater than 128.
pub fn new(values: Vec<Felt>) -> Result<Self, NoteError> {
if values.len() > Self::MAX_INPUTS_PER_NOTE {
return Err(NoteError::too_many_inputs(values.len()));
}
Ok(pad_and_build(values))
}
// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------
/// Returns a commitment to these inputs.
pub fn commitment(&self) -> Digest {
self.hash
}
/// Returns the number of input values.
///
/// The returned value is guaranteed to be smaller than or equal to 128.
pub fn num_values(&self) -> u8 {
debug_assert!(
self.values.len() < 128,
"The constructor should have checked the number of inputs"
);
self.values.len() as u8
}
/// Returns a reference to the input values.
pub fn values(&self) -> &[Felt] {
&self.values
}
/// Returns the note's input formatted to be used with the advice map.
///
/// The format is `input_len || INPUTS || PADDING`, where:
///
/// - input_len is the number of inputs
/// - INPUTS is the variable inputs for the note
/// - PADDING is the optional padding to align the data with a 2WORD boundary
pub fn format_for_advice(&self) -> Vec<Felt> {
// NOTE: keep map in sync with the `note::get_inputs` API procedure
let mut padded = pad_inputs(&self.values);
padded.insert(0, self.num_values().into());
padded
}
}
impl Default for NoteInputs {
fn default() -> Self {
pad_and_build(vec![])
}
}
impl PartialEq for NoteInputs {
fn eq(&self, other: &Self) -> bool {
let NoteInputs { values: inputs, hash: _ } = self;
inputs == &other.values
}
}
impl Eq for NoteInputs {}
// CONVERSION
// ================================================================================================
impl From<NoteInputs> for Vec<Felt> {
fn from(value: NoteInputs) -> Self {
value.values
}
}
impl TryFrom<Vec<Felt>> for NoteInputs {
type Error = NoteError;
fn try_from(value: Vec<Felt>) -> Result<Self, Self::Error> {
NoteInputs::new(value)
}
}
// HELPER FUNCTIONS
// ================================================================================================
/// Returns a vector with built from the provided inputs and padded to the next multiple of 8.
fn pad_inputs(inputs: &[Felt]) -> Vec<Felt> {
const BLOCK_SIZE: usize = WORD_SIZE * 2;
let padded_len = inputs.len().next_multiple_of(BLOCK_SIZE);
let mut padded_inputs = Vec::with_capacity(padded_len);
padded_inputs.extend(inputs.iter());
padded_inputs.resize(padded_len, ZERO);
padded_inputs
}
/// Pad `values` and returns a new `NoteInputs`.
fn pad_and_build(values: Vec<Felt>) -> NoteInputs {
let hash = {
let padded_values = pad_inputs(&values);
Hasher::hash_elements(&padded_values)
};
NoteInputs { values, hash }
}
// SERIALIZATION
// ================================================================================================
impl Serializable for NoteInputs {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
let NoteInputs { values, hash: _hash } = self;
target.write_u8(values.len().try_into().expect("inputs len is not a u8 value"));
target.write_many(values);
}
}
impl Deserializable for NoteInputs {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let num_values = source.read_u8()? as usize;
let values = source.read_many::<Felt>(num_values)?;
Self::new(values).map_err(|v| DeserializationError::InvalidValue(format!("{v}")))
}
}
// TESTS
// ================================================================================================
#[cfg(test)]
mod tests {
use miden_crypto::utils::Deserializable;
use super::{Felt, NoteInputs, Serializable};
#[test]
fn test_input_ordering() {
// inputs are provided in reverse stack order
let inputs = vec![Felt::new(1), Felt::new(2), Felt::new(3)];
// we expect the inputs to be padded to length 16 and to remain in reverse stack order.
let expected_ordering = vec![Felt::new(1), Felt::new(2), Felt::new(3)];
let note_inputs = NoteInputs::new(inputs).expect("note created should succeed");
assert_eq!(&expected_ordering, ¬e_inputs.values);
}
#[test]
fn test_input_serialization() {
let inputs = vec![Felt::new(1), Felt::new(2), Felt::new(3)];
let note_inputs = NoteInputs::new(inputs).unwrap();
let bytes = note_inputs.to_bytes();
let parsed_note_inputs = NoteInputs::read_from_bytes(&bytes).unwrap();
assert_eq!(note_inputs, parsed_note_inputs);
}
}