mod bytes;
mod serialize;
mod string;
use crate::{Input, Output, Transition};
use console::{
network::prelude::*,
program::{Argument, Literal, Plaintext},
types::{Address, Field, U64},
};
use snarkvm_synthesizer_snark::Proof;
#[derive(Clone, PartialEq, Eq)]
pub struct Fee<N: Network> {
transition: Transition<N>,
global_state_root: N::StateRoot,
proof: Option<Proof<N>>,
}
impl<N: Network> Fee<N> {
pub fn from(transition: Transition<N>, global_state_root: N::StateRoot, proof: Option<Proof<N>>) -> Result<Self> {
match transition.is_fee_private() || transition.is_fee_public() {
true => Ok(Self::from_unchecked(transition, global_state_root, proof)),
false => bail!("Invalid fee transition locator"),
}
}
pub const fn from_unchecked(
transition: Transition<N>,
global_state_root: N::StateRoot,
proof: Option<Proof<N>>,
) -> Self {
Self { transition, global_state_root, proof }
}
}
impl<N: Network> Fee<N> {
#[inline]
pub fn is_fee_private(&self) -> bool {
self.transition.is_fee_private()
}
#[inline]
pub fn is_fee_public(&self) -> bool {
self.transition.is_fee_public()
}
}
impl<N: Network> Fee<N> {
pub fn is_zero(&self) -> Result<bool> {
self.amount().map(|amount| amount.is_zero())
}
pub fn payer(&self) -> Option<Address<N>> {
match self.transition.outputs().last() {
Some(Output::Future(_, Some(future))) => match future.arguments().first() {
Some(Argument::Plaintext(Plaintext::Literal(Literal::Address(address), _))) => Some(*address),
_ => None,
},
_ => None,
}
}
pub fn amount(&self) -> Result<U64<N>> {
let base_fee_amount = self.base_amount()?;
let priority_fee_amount = self.priority_amount()?;
Ok(U64::new(base_fee_amount.saturating_add(*priority_fee_amount)))
}
pub fn base_amount(&self) -> Result<U64<N>> {
let base_fee_index: usize = match self.transition.outputs().last() {
Some(output) => match output {
Output::Record(..) => 1,
Output::Future(..) => 0,
_ => bail!("Unexpected output in fee transition"),
},
None => bail!("Missing output in fee transition"),
};
match self.transition.inputs().get(base_fee_index) {
Some(Input::Public(_, Some(Plaintext::Literal(Literal::U64(microcredits), _)))) => Ok(*microcredits),
_ => bail!("Failed to retrieve the base fee (in microcredits) from the fee transition"),
}
}
pub fn priority_amount(&self) -> Result<U64<N>> {
let priority_fee_index: usize = match self.transition.outputs().last() {
Some(output) => match output {
Output::Record(..) => 2,
Output::Future(..) => 1,
_ => bail!("Unexpected output in fee transition"),
},
None => bail!("Missing output in fee transition"),
};
match self.transition.inputs().get(priority_fee_index) {
Some(Input::Public(_, Some(Plaintext::Literal(Literal::U64(microcredits), _)))) => Ok(*microcredits),
_ => bail!("Failed to retrieve the priority fee (in microcredits) from the fee transition"),
}
}
pub fn deployment_or_execution_id(&self) -> Result<Field<N>> {
let input_index = match self.transition.outputs().last() {
Some(output) => match output {
Output::Record(..) => 3,
Output::Future(..) => 2,
_ => bail!("Unexpected output in fee transition"),
},
None => bail!("Missing output in fee transition"),
};
match self.transition.inputs().get(input_index) {
Some(Input::Public(_, Some(Plaintext::Literal(Literal::Field(id), _)))) => Ok(*id),
_ => bail!("Failed to retrieve the deployment or execution ID from the fee transition"),
}
}
pub fn num_finalize_operations(&self) -> usize {
match self.is_fee_public() {
true => 1,
false => 0,
}
}
pub fn transition_id(&self) -> &N::TransitionID {
self.transition.id()
}
pub const fn transition(&self) -> &Transition<N> {
&self.transition
}
pub fn into_transition(self) -> Transition<N> {
self.transition
}
pub const fn global_state_root(&self) -> N::StateRoot {
self.global_state_root
}
pub const fn proof(&self) -> Option<&Proof<N>> {
self.proof.as_ref()
}
}
impl<N: Network> Deref for Fee<N> {
type Target = Transition<N>;
fn deref(&self) -> &Self::Target {
&self.transition
}
}
#[cfg(test)]
pub mod test_helpers {
use super::*;
use console::types::Field;
use snarkvm_algorithms::snark::varuna::VarunaVersion;
use snarkvm_ledger_query::Query;
use snarkvm_ledger_store::{BlockStore, helpers::memory::BlockMemory};
use snarkvm_synthesizer_process::Process;
use aleo_std::StorageMode;
use std::sync::OnceLock;
type CurrentNetwork = console::network::MainnetV0;
type CurrentAleo = snarkvm_circuit::network::AleoV0;
pub fn sample_fee_private_hardcoded(rng: &mut TestRng) -> Fee<CurrentNetwork> {
static INSTANCE: OnceLock<Fee<CurrentNetwork>> = OnceLock::new();
INSTANCE
.get_or_init(|| {
let deployment_or_execution_id = Field::rand(rng);
sample_fee_private(deployment_or_execution_id, rng)
})
.clone()
}
pub fn sample_fee_private(
deployment_or_execution_id: Field<CurrentNetwork>,
rng: &mut TestRng,
) -> Fee<CurrentNetwork> {
let (block, transaction, private_key) = crate::test_helpers::sample_genesis_block_and_components(rng);
let credits = transaction.records().next().unwrap().1.clone();
let credits = credits.decrypt(&private_key.try_into().unwrap()).unwrap();
let base_fee_in_microcredits = 10_000_000;
let priority_fee_in_microcredits = 1_000;
let process = Process::load().unwrap();
let authorization = process
.authorize_fee_private::<CurrentAleo, _>(
&private_key,
credits,
base_fee_in_microcredits,
priority_fee_in_microcredits,
deployment_or_execution_id,
rng,
)
.unwrap();
let (_, mut trace) = process.execute::<CurrentAleo, _>(authorization, rng).unwrap();
let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
block_store.insert(&FromStr::from_str(&block.to_string()).unwrap()).unwrap();
trace.prepare(&Query::from(block_store)).unwrap();
let fee = trace.prove_fee::<CurrentAleo, _>(VarunaVersion::V1, rng).unwrap();
Fee::from_str(&fee.to_string()).unwrap()
}
pub fn sample_fee_public_hardcoded(rng: &mut TestRng) -> Fee<CurrentNetwork> {
static INSTANCE: OnceLock<Fee<CurrentNetwork>> = OnceLock::new();
INSTANCE
.get_or_init(|| {
let deployment_or_execution_id = Field::rand(rng);
sample_fee_public(deployment_or_execution_id, rng)
})
.clone()
}
pub fn sample_fee_public(
deployment_or_execution_id: Field<CurrentNetwork>,
rng: &mut TestRng,
) -> Fee<CurrentNetwork> {
let (block, _, private_key) = crate::test_helpers::sample_genesis_block_and_components(rng);
let base_fee = 10_000_000;
let priority_fee = 1_000;
let process = Process::load().unwrap();
let authorization = process
.authorize_fee_public::<CurrentAleo, _>(
&private_key,
base_fee,
priority_fee,
deployment_or_execution_id,
rng,
)
.unwrap();
let (_, mut trace) = process.execute::<CurrentAleo, _>(authorization, rng).unwrap();
let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
block_store.insert(&FromStr::from_str(&block.to_string()).unwrap()).unwrap();
trace.prepare(&Query::from(block_store)).unwrap();
let fee = trace.prove_fee::<CurrentAleo, _>(VarunaVersion::V1, rng).unwrap();
Fee::from_str(&fee.to_string()).unwrap()
}
}