use alloc::vec::Vec;
use crate::account::AccountCode;
use crate::crypto::utils::{bytes_to_elements_with_padding, padded_elements_to_bytes};
use crate::utils::serde::{
ByteReader,
ByteWriter,
Deserializable,
DeserializationError,
Serializable,
};
use crate::{Felt, Hasher, WORD_SIZE, Word};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AccountCodeUpgrade {
code: AccountCode,
}
impl AccountCodeUpgrade {
const ADVICE_MAP_KEY_DOMAIN: Felt = Felt::new_unchecked(0x02_0002);
pub fn new(code: AccountCode) -> Self {
Self { code }
}
pub fn advice_map_key(new_code_commitment: Word) -> Word {
Hasher::hash_elements_in_domain(
new_code_commitment.as_elements(),
Self::ADVICE_MAP_KEY_DOMAIN,
)
}
pub fn code(&self) -> &AccountCode {
&self.code
}
pub fn commitment(&self) -> Word {
self.code.commitment()
}
pub fn into_code(self) -> AccountCode {
self.code
}
pub fn to_elements(&self) -> Vec<Felt> {
let mut elements = bytes_to_elements_with_padding(&self.code.to_bytes());
elements.resize(elements.len().next_multiple_of(WORD_SIZE), Felt::ZERO);
elements
}
pub fn to_advice_map_entry(&self) -> (Word, Vec<Felt>) {
(Self::advice_map_key(self.commitment()), self.to_elements())
}
pub fn try_from_elements(elements: &[Felt]) -> Result<Self, DeserializationError> {
let packed_len = elements
.iter()
.rposition(|element| *element != Felt::ZERO)
.map_or(0, |last_packed_idx| last_packed_idx + 1);
let bytes = padded_elements_to_bytes(&elements[..packed_len]).ok_or_else(|| {
DeserializationError::InvalidValue("encoded account code is not padded".into())
})?;
AccountCode::read_from_bytes(&bytes).map(Self::new)
}
}
impl Serializable for AccountCodeUpgrade {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.code.write_into(target);
}
fn get_size_hint(&self) -> usize {
self.code.get_size_hint()
}
}
impl Deserializable for AccountCodeUpgrade {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
AccountCode::read_from(source).map(Self::new)
}
}
#[cfg(test)]
mod tests {
use super::AccountCodeUpgrade;
use crate::WORD_SIZE;
use crate::account::AccountCode;
#[test]
fn advice_map_entry_roundtrips() -> anyhow::Result<()> {
let upgrade = AccountCodeUpgrade::new(AccountCode::mock());
let (key, elements) = upgrade.to_advice_map_entry();
assert_eq!(key, AccountCodeUpgrade::advice_map_key(upgrade.commitment()));
assert_eq!(elements.len() % WORD_SIZE, 0);
assert_eq!(AccountCodeUpgrade::try_from_elements(&elements)?, upgrade);
Ok(())
}
}