use alloc::string::ToString;
use alloc::vec::Vec;
use crate::asset::AssetId;
use crate::batch::BatchKernel;
use crate::constants::MIN_PROOF_SECURITY_LEVEL;
use crate::crypto::SequentialCommit;
use crate::errors::ProtocolConfigError;
use crate::transaction::TransactionKernel;
use crate::utils::serde::{
ByteReader,
ByteWriter,
Deserializable,
DeserializationError,
Serializable,
};
use crate::{Felt, Word};
mod kernel_config;
pub use kernel_config::KernelConfig;
mod next_protocol_config;
pub use next_protocol_config::NextProtocolConfig;
mod proof_verification;
pub use proof_verification::{ProofSecurityPolicy, ProofVerificationConfig};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProtocolConfig {
fee_asset_id: AssetId,
tx_kernel: KernelConfig,
batch_kernel: KernelConfig,
block_kernel: KernelConfig,
proof_verification: ProofVerificationConfig,
}
impl ProtocolConfig {
const MINIMUM_SECURITY_BITS: u8 = {
assert!(MIN_PROOF_SECURITY_LEVEL <= u8::MAX as u32);
MIN_PROOF_SECURITY_LEVEL as u8
};
pub fn new(
fee_asset_id: AssetId,
tx_kernel: KernelConfig,
batch_kernel: KernelConfig,
block_kernel: KernelConfig,
proof_verification: ProofVerificationConfig,
) -> Result<Self, ProtocolConfigError> {
if !fee_asset_id.composition().is_fungible() {
return Err(ProtocolConfigError::FeeAssetMustBeFungible(fee_asset_id.composition()));
}
Ok(Self {
fee_asset_id,
tx_kernel,
batch_kernel,
block_kernel,
proof_verification,
})
}
pub fn current(fee_asset_id: AssetId) -> Result<Self, ProtocolConfigError> {
let tx_kernel = KernelConfig::new(
TransactionKernel::main().hash(),
TransactionKernel::PROCEDURES.to_vec(),
)?;
let batch_kernel = KernelConfig::new(BatchKernel::main().hash(), Vec::new())?;
let block_kernel = KernelConfig::new(Word::empty(), Vec::new())?;
let security_policy = ProofSecurityPolicy::new(Word::empty(), Self::MINIMUM_SECURITY_BITS)?;
let proof_verification =
ProofVerificationConfig::new(Word::empty(), Word::empty(), security_policy);
Self::new(fee_asset_id, tx_kernel, batch_kernel, block_kernel, proof_verification)
}
pub fn fee_asset_id(&self) -> AssetId {
self.fee_asset_id
}
pub fn tx_kernel(&self) -> &KernelConfig {
&self.tx_kernel
}
pub fn batch_kernel(&self) -> &KernelConfig {
&self.batch_kernel
}
pub fn block_kernel(&self) -> &KernelConfig {
&self.block_kernel
}
pub fn proof_verification(&self) -> &ProofVerificationConfig {
&self.proof_verification
}
pub fn to_commitment(&self) -> Word {
<Self as SequentialCommit>::to_commitment(self)
}
pub fn to_elements(&self) -> Vec<Felt> {
<Self as SequentialCommit>::to_elements(self)
}
}
impl SequentialCommit for ProtocolConfig {
type Commitment = Word;
fn to_elements(&self) -> Vec<Felt> {
let fee_asset_id = self.fee_asset_id.to_word();
let tx_kernel = self.tx_kernel.to_commitment();
let batch_kernel = self.batch_kernel.to_commitment();
let block_kernel = self.block_kernel.to_commitment();
let proof_verification = self.proof_verification.to_commitment();
[
fee_asset_id.as_elements(),
tx_kernel.as_elements(),
batch_kernel.as_elements(),
block_kernel.as_elements(),
proof_verification.as_elements(),
Word::empty().as_elements(),
]
.concat()
}
}
impl Serializable for ProtocolConfig {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
let Self {
fee_asset_id,
tx_kernel,
batch_kernel,
block_kernel,
proof_verification,
} = self;
fee_asset_id.write_into(target);
tx_kernel.write_into(target);
batch_kernel.write_into(target);
block_kernel.write_into(target);
proof_verification.write_into(target);
}
}
impl Deserializable for ProtocolConfig {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let fee_asset_id = source.read()?;
let tx_kernel = source.read()?;
let batch_kernel = source.read()?;
let block_kernel = source.read()?;
let proof_verification = source.read()?;
Self::new(fee_asset_id, tx_kernel, batch_kernel, block_kernel, proof_verification)
.map_err(|err| DeserializationError::InvalidValue(err.to_string()))
}
}
#[cfg(test)]
mod tests {
use assert_matches::assert_matches;
use super::*;
use crate::account::AccountId;
use crate::asset::{AssetClass, AssetComposition};
use crate::testing::account_id::{
ACCOUNT_ID_PRIVATE_NON_FUNGIBLE_FAUCET,
ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET,
};
fn fee_asset_id() -> AssetId {
let faucet_id = AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET)
.expect("test faucet ID should be valid");
AssetId::new_fungible(faucet_id)
}
#[test]
fn to_elements_is_pipeable() {
let config = ProtocolConfig::current(fee_asset_id()).unwrap();
assert_eq!(config.to_elements().len(), 24);
}
#[test]
fn current_commits_to_the_linked_tx_kernel() {
let config = ProtocolConfig::current(fee_asset_id()).unwrap();
assert_eq!(config.tx_kernel().main_proc(), TransactionKernel::main().hash());
assert_eq!(config.tx_kernel().kernel_procs(), TransactionKernel::PROCEDURES);
}
#[test]
fn new_rejects_non_fungible_fee_asset() {
let faucet_id = AccountId::try_from(ACCOUNT_ID_PRIVATE_NON_FUNGIBLE_FAUCET)
.expect("test faucet ID should be valid");
let fee_asset_id =
AssetId::new(AssetClass::default(), faucet_id, AssetComposition::None).unwrap();
let error = ProtocolConfig::new(
fee_asset_id,
KernelConfig::dummy(),
KernelConfig::dummy(),
KernelConfig::dummy(),
ProofVerificationConfig::new(
Word::empty(),
Word::empty(),
ProofSecurityPolicy::new(Word::empty(), 96).unwrap(),
),
)
.unwrap_err();
assert_matches!(error, ProtocolConfigError::FeeAssetMustBeFungible(AssetComposition::None));
}
#[test]
fn serde_round_trip() -> anyhow::Result<()> {
let config = ProtocolConfig::current(fee_asset_id())?;
let deserialized = ProtocolConfig::read_from_bytes(&config.to_bytes())
.map_err(|err| anyhow::anyhow!("{err}"))?;
assert_eq!(config, deserialized);
Ok(())
}
}