snarkvm_ledger_block/transaction/fee/
mod.rs1mod bytes;
17mod serialize;
18mod string;
19
20use crate::{Input, Output, Transition};
21use console::{
22 network::prelude::*,
23 program::{Argument, Literal, Plaintext},
24 types::{Address, Field, U64},
25};
26use synthesizer_snark::Proof;
27
28#[derive(Clone, PartialEq, Eq)]
29pub struct Fee<N: Network> {
30 transition: Transition<N>,
32 global_state_root: N::StateRoot,
34 proof: Option<Proof<N>>,
36}
37
38impl<N: Network> Fee<N> {
39 pub fn from(transition: Transition<N>, global_state_root: N::StateRoot, proof: Option<Proof<N>>) -> Result<Self> {
41 match transition.is_fee_private() || transition.is_fee_public() {
43 true => Ok(Self::from_unchecked(transition, global_state_root, proof)),
44 false => bail!("Invalid fee transition locator"),
45 }
46 }
47
48 pub const fn from_unchecked(
50 transition: Transition<N>,
51 global_state_root: N::StateRoot,
52 proof: Option<Proof<N>>,
53 ) -> Self {
54 Self { transition, global_state_root, proof }
55 }
56}
57
58impl<N: Network> Fee<N> {
59 #[inline]
61 pub fn is_fee_private(&self) -> bool {
62 self.transition.is_fee_private()
63 }
64
65 #[inline]
67 pub fn is_fee_public(&self) -> bool {
68 self.transition.is_fee_public()
69 }
70}
71
72impl<N: Network> Fee<N> {
73 pub fn is_zero(&self) -> Result<bool> {
75 self.amount().map(|amount| amount.is_zero())
76 }
77
78 pub fn payer(&self) -> Option<Address<N>> {
80 match self.transition.outputs().last() {
82 Some(Output::Future(_, Some(future))) => match future.arguments().first() {
83 Some(Argument::Plaintext(Plaintext::Literal(Literal::Address(address), _))) => Some(*address),
84 _ => None,
85 },
86 _ => None,
87 }
88 }
89
90 pub fn amount(&self) -> Result<U64<N>> {
92 let base_fee_amount = self.base_amount()?;
94 let priority_fee_amount = self.priority_amount()?;
96 Ok(U64::new(base_fee_amount.saturating_add(*priority_fee_amount)))
99 }
100
101 pub fn base_amount(&self) -> Result<U64<N>> {
103 let base_fee_index: usize = match self.transition.outputs().last() {
106 Some(output) => match output {
107 Output::Record(..) => 1,
108 Output::Future(..) => 0,
109 _ => bail!("Unexpected output in fee transition"),
110 },
111 None => bail!("Missing output in fee transition"),
112 };
113 match self.transition.inputs().get(base_fee_index) {
115 Some(Input::Public(_, Some(Plaintext::Literal(Literal::U64(microcredits), _)))) => Ok(*microcredits),
116 _ => bail!("Failed to retrieve the base fee (in microcredits) from the fee transition"),
117 }
118 }
119
120 pub fn priority_amount(&self) -> Result<U64<N>> {
122 let priority_fee_index: usize = match self.transition.outputs().last() {
125 Some(output) => match output {
126 Output::Record(..) => 2,
127 Output::Future(..) => 1,
128 _ => bail!("Unexpected output in fee transition"),
129 },
130 None => bail!("Missing output in fee transition"),
131 };
132 match self.transition.inputs().get(priority_fee_index) {
134 Some(Input::Public(_, Some(Plaintext::Literal(Literal::U64(microcredits), _)))) => Ok(*microcredits),
135 _ => bail!("Failed to retrieve the priority fee (in microcredits) from the fee transition"),
136 }
137 }
138
139 pub fn deployment_or_execution_id(&self) -> Result<Field<N>> {
141 let input_index = match self.transition.outputs().last() {
144 Some(output) => match output {
145 Output::Record(..) => 3,
146 Output::Future(..) => 2,
147 _ => bail!("Unexpected output in fee transition"),
148 },
149 None => bail!("Missing output in fee transition"),
150 };
151 match self.transition.inputs().get(input_index) {
153 Some(Input::Public(_, Some(Plaintext::Literal(Literal::Field(id), _)))) => Ok(*id),
154 _ => bail!("Failed to retrieve the deployment or execution ID from the fee transition"),
155 }
156 }
157
158 pub fn num_finalize_operations(&self) -> usize {
160 match self.is_fee_public() {
162 true => 1,
163 false => 0,
164 }
165 }
166
167 pub fn transition_id(&self) -> &N::TransitionID {
169 self.transition.id()
170 }
171
172 pub const fn transition(&self) -> &Transition<N> {
174 &self.transition
175 }
176
177 pub fn into_transition(self) -> Transition<N> {
179 self.transition
180 }
181
182 pub const fn global_state_root(&self) -> N::StateRoot {
184 self.global_state_root
185 }
186
187 pub const fn proof(&self) -> Option<&Proof<N>> {
189 self.proof.as_ref()
190 }
191}
192
193impl<N: Network> Deref for Fee<N> {
194 type Target = Transition<N>;
195
196 fn deref(&self) -> &Self::Target {
197 &self.transition
198 }
199}
200
201#[cfg(test)]
202pub mod test_helpers {
203 use super::*;
204 use algorithms::snark::varuna::VarunaVersion;
205 use console::types::Field;
206 use ledger_query::Query;
207 use ledger_store::{BlockStore, helpers::memory::BlockMemory};
208 use synthesizer_process::Process;
209
210 use aleo_std::StorageMode;
211 use once_cell::sync::OnceCell;
212
213 type CurrentNetwork = console::network::MainnetV0;
214 type CurrentAleo = circuit::network::AleoV0;
215
216 pub fn sample_fee_private_hardcoded(rng: &mut TestRng) -> Fee<CurrentNetwork> {
218 static INSTANCE: OnceCell<Fee<CurrentNetwork>> = OnceCell::new();
219 INSTANCE
220 .get_or_init(|| {
221 let deployment_or_execution_id = Field::rand(rng);
223 sample_fee_private(deployment_or_execution_id, rng)
225 })
226 .clone()
227 }
228
229 pub fn sample_fee_private(
231 deployment_or_execution_id: Field<CurrentNetwork>,
232 rng: &mut TestRng,
233 ) -> Fee<CurrentNetwork> {
234 let (block, transaction, private_key) = crate::test_helpers::sample_genesis_block_and_components(rng);
236 let credits = transaction.records().next().unwrap().1.clone();
238 let credits = credits.decrypt(&private_key.try_into().unwrap()).unwrap();
240 let base_fee_in_microcredits = 10_000_000;
242 let priority_fee_in_microcredits = 1_000;
244
245 let process = Process::load().unwrap();
247 let authorization = process
249 .authorize_fee_private::<CurrentAleo, _>(
250 &private_key,
251 credits,
252 base_fee_in_microcredits,
253 priority_fee_in_microcredits,
254 deployment_or_execution_id,
255 rng,
256 )
257 .unwrap();
258 let (_, mut trace) = process.execute::<CurrentAleo, _>(authorization, rng).unwrap();
260
261 let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
263 block_store.insert(&FromStr::from_str(&block.to_string()).unwrap()).unwrap();
266
267 trace.prepare(Query::from(block_store)).unwrap();
269 let fee = trace.prove_fee::<CurrentAleo, _>(VarunaVersion::V1, rng).unwrap();
271
272 Fee::from_str(&fee.to_string()).unwrap()
275 }
276
277 pub fn sample_fee_public_hardcoded(rng: &mut TestRng) -> Fee<CurrentNetwork> {
279 static INSTANCE: OnceCell<Fee<CurrentNetwork>> = OnceCell::new();
280 INSTANCE
281 .get_or_init(|| {
282 let deployment_or_execution_id = Field::rand(rng);
284 sample_fee_public(deployment_or_execution_id, rng)
286 })
287 .clone()
288 }
289
290 pub fn sample_fee_public(
292 deployment_or_execution_id: Field<CurrentNetwork>,
293 rng: &mut TestRng,
294 ) -> Fee<CurrentNetwork> {
295 let (block, _, private_key) = crate::test_helpers::sample_genesis_block_and_components(rng);
297 let base_fee = 10_000_000;
299 let priority_fee = 1_000;
301
302 let process = Process::load().unwrap();
304 let authorization = process
306 .authorize_fee_public::<CurrentAleo, _>(
307 &private_key,
308 base_fee,
309 priority_fee,
310 deployment_or_execution_id,
311 rng,
312 )
313 .unwrap();
314 let (_, mut trace) = process.execute::<CurrentAleo, _>(authorization, rng).unwrap();
316
317 let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap();
319 block_store.insert(&FromStr::from_str(&block.to_string()).unwrap()).unwrap();
322
323 trace.prepare(Query::from(block_store)).unwrap();
325 let fee = trace.prove_fee::<CurrentAleo, _>(VarunaVersion::V1, rng).unwrap();
327
328 Fee::from_str(&fee.to_string()).unwrap()
331 }
332}