Skip to main content

midnight_ledger/
structure.rs

1// This file is part of midnight-ledger.
2// Copyright (C) 2025 Midnight Foundation
3// SPDX-License-Identifier: Apache-2.0
4// Licensed under the Apache License, Version 2.0 (the "License");
5// You may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7// http://www.apache.org/licenses/LICENSE-2.0
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14use crate::annotation::NightAnn;
15use crate::dust::DUST_GENERATION_INFO_SIZE;
16use crate::dust::DUST_SPEND_PIS;
17use crate::dust::DUST_SPEND_PROOF_SIZE;
18use crate::dust::{DustActions, DustParameters, DustState, INITIAL_DUST_PARAMETERS};
19use crate::error::FeeCalculationError;
20use crate::error::InvariantViolation;
21use crate::error::MalformedTransaction;
22use crate::verify::ProofVerificationMode;
23use base_crypto::BinaryHashRepr;
24use base_crypto::cost_model::RunningCost;
25use base_crypto::cost_model::price_adjustment_function;
26use base_crypto::cost_model::{CostDuration, FeePrices, FixedPoint, SyntheticCost};
27use base_crypto::hash::HashOutput;
28use base_crypto::hash::PERSISTENT_HASH_BYTES;
29use base_crypto::hash::persistent_hash;
30use base_crypto::repr::MemWrite;
31use base_crypto::signatures::Signature;
32use base_crypto::signatures::{SigningKey, VerifyingKey};
33use base_crypto::time::{Duration, Timestamp};
34use coin_structure::coin::NIGHT;
35use coin_structure::coin::PublicAddress;
36use coin_structure::coin::UserAddress;
37use coin_structure::coin::{Commitment, Nonce, ShieldedTokenType, TokenType, UnshieldedTokenType};
38use coin_structure::contract::ContractAddress;
39use derive_where::derive_where;
40use fake::Dummy;
41use introspection_derive::Introspection;
42use onchain_runtime::context::{BlockContext, CallContext, ClaimedContractCallsValue};
43use onchain_runtime::state::ChargedState;
44use onchain_runtime::state::ContractOperation;
45use onchain_runtime::state::{ContractMaintenanceAuthority, ContractState, EntryPointBuf};
46use onchain_runtime::transcript::Transcript;
47use rand::{CryptoRng, Rng};
48use serde::{Deserialize, Serialize};
49use serialize::{
50    self, Deserializable, Serializable, Tagged, tag_enforcement_test, tagged_serialize,
51};
52use sha2::{Digest, Sha256};
53use std::collections::BTreeSet;
54use std::fmt::Debug;
55use std::fmt::Display;
56use std::fmt::{self, Formatter};
57use std::hash::Hash;
58use std::io::{Read, Write};
59use std::iter::once;
60use std::marker::PhantomData;
61use std::ops::Deref;
62use storage::Storable;
63use storage::arena::{ArenaHash, ArenaKey, Sp};
64use storage::db::DB;
65use storage::db::InMemoryDB;
66use storage::merkle_patricia_trie::Annotation;
67use storage::storable::Loader;
68use storage::storage::Map;
69use storage::storage::{HashMap, HashSet, TimeFilterMap};
70use transient_crypto::commitment::{Pedersen, PedersenRandomness, PureGeneratorPedersen};
71use transient_crypto::curve::FR_BYTES;
72use transient_crypto::curve::Fr;
73use transient_crypto::proofs::KeyLocation;
74use transient_crypto::proofs::VerifierKey;
75use transient_crypto::proofs::{Proof, ProofPreimage};
76use transient_crypto::repr::{FieldRepr, FromFieldRepr};
77use zswap::ZSWAP_TREE_HEIGHT;
78use zswap::error::MalformedOffer;
79use zswap::{Input, Offer as ZswapOffer, Output, Transient};
80
81/// A trait for things that can fit into `Signature` shaped holes
82pub trait SignatureKind<D: DB>: Ord + Storable<D> + Debug + Tagged + 'static {
83    /// The type of the `Signature` shaped thing
84    type Signature<T>: Ord + Serializable + Deserializable + Storable<D> + Debug + Tagged;
85
86    /// Verify a signature against a message
87    fn signature_verify<T>(msg: &[u8], key: VerifyingKey, signature: &Self::Signature<T>) -> bool;
88
89    fn sign<R: Rng + CryptoRng, T>(
90        sk: &SigningKey,
91        rng: &mut R,
92        msg: &[u8],
93    ) -> <Self as SignatureKind<D>>::Signature<T>;
94}
95
96impl<D: DB> SignatureKind<D> for () {
97    type Signature<T> = ();
98
99    fn signature_verify<T>(_msg: &[u8], _key: VerifyingKey, _signature: &()) -> bool {
100        true
101    }
102
103    fn sign<R: Rng + CryptoRng, T>(_: &SigningKey, _: &mut R, _: &[u8]) {}
104}
105
106impl<D: DB> SignatureKind<D> for Signature {
107    type Signature<T> = Signature;
108
109    fn signature_verify<T>(msg: &[u8], key: VerifyingKey, signature: &Signature) -> bool {
110        key.verify(msg, signature)
111    }
112
113    fn sign<R: Rng + CryptoRng, T>(sk: &SigningKey, rng: &mut R, msg: &[u8]) -> Signature {
114        sk.sign(rng, msg)
115    }
116}
117
118pub trait BindingKind<S: SignatureKind<D>, P: ProofKind<D>, D: DB>: Sync + Send {
119    fn when_sealed(
120        f: Result<impl Fn() -> Result<(), MalformedTransaction<D>>, MalformedTransaction<D>>,
121    ) -> Result<(), MalformedTransaction<D>>;
122}
123
124impl<S: SignatureKind<D>, P: ProofKind<D>, D: DB> BindingKind<S, P, D> for Pedersen {
125    fn when_sealed(
126        f: Result<impl Fn() -> Result<(), MalformedTransaction<D>>, MalformedTransaction<D>>,
127    ) -> Result<(), MalformedTransaction<D>> {
128        f?;
129        Ok(())
130    }
131}
132
133impl<S: SignatureKind<D>, P: ProofKind<D>, D: DB> BindingKind<S, P, D> for PedersenRandomness {
134    fn when_sealed(
135        f: Result<impl Fn() -> Result<(), MalformedTransaction<D>>, MalformedTransaction<D>>,
136    ) -> Result<(), MalformedTransaction<D>> {
137        f?;
138        Ok(())
139    }
140}
141
142impl<S: SignatureKind<D>, P: ProofKind<D>, D: DB> BindingKind<S, P, D> for PureGeneratorPedersen {
143    fn when_sealed(
144        f: Result<impl Fn() -> Result<(), MalformedTransaction<D>>, MalformedTransaction<D>>,
145    ) -> Result<(), MalformedTransaction<D>> {
146        f?()
147    }
148}
149
150pub trait PedersenDowngradeable<D: DB>:
151    Into<Pedersen> + Clone + PartialEq + Eq + PartialOrd + Ord + Serialize
152{
153    fn downgrade(&self) -> Pedersen;
154
155    #[allow(clippy::result_large_err)]
156    fn valid(&self, _challenge_pre: &[u8]) -> Result<(), MalformedTransaction<D>>;
157}
158
159pub trait PedersenUpgradeable<D: DB>:
160    Into<Pedersen> + Clone + PartialEq + Eq + PartialOrd + Ord + Serialize + PedersenDowngradeable<D>
161{
162    fn upgrade<R: Rng + CryptoRng>(
163        &self,
164        rng: &mut R,
165        challenge_pre: &[u8],
166    ) -> PureGeneratorPedersen;
167}
168
169impl<D: DB> PedersenDowngradeable<D> for PureGeneratorPedersen {
170    fn downgrade(&self) -> Pedersen {
171        Pedersen::from(self.clone())
172    }
173
174    fn valid(&self, challenge_pre: &[u8]) -> Result<(), MalformedTransaction<D>> {
175        if PureGeneratorPedersen::valid(self, challenge_pre) {
176            Ok(())
177        } else {
178            Err(MalformedTransaction::<D>::InvalidSchnorrProof)
179        }
180    }
181}
182
183impl<D: DB> PedersenUpgradeable<D> for PureGeneratorPedersen {
184    fn upgrade<R: Rng + CryptoRng>(
185        &self,
186        _rng: &mut R,
187        _challenge_pre: &[u8],
188    ) -> PureGeneratorPedersen {
189        self.clone()
190    }
191}
192
193impl<D: DB> PedersenDowngradeable<D> for PedersenRandomness {
194    fn downgrade(&self) -> Pedersen {
195        Pedersen::from(*self)
196    }
197
198    fn valid(&self, _challenge_pre: &[u8]) -> Result<(), MalformedTransaction<D>> {
199        Ok(())
200    }
201}
202
203impl<D: DB> PedersenUpgradeable<D> for PedersenRandomness {
204    fn upgrade<R: Rng + CryptoRng>(
205        &self,
206        rng: &mut R,
207        challenge_pre: &[u8],
208    ) -> PureGeneratorPedersen {
209        PureGeneratorPedersen::new_from(rng, self, challenge_pre)
210    }
211}
212
213impl<D: DB> PedersenDowngradeable<D> for Pedersen {
214    fn downgrade(&self) -> Pedersen {
215        *self
216    }
217
218    fn valid(&self, _challenge_pre: &[u8]) -> Result<(), MalformedTransaction<D>> {
219        Ok(())
220    }
221}
222
223#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Storable)]
224#[storable(base)]
225#[tag = "proof-preimage-versioned"]
226#[non_exhaustive]
227pub enum ProofPreimageVersioned {
228    V2(std::sync::Arc<ProofPreimage>),
229}
230
231impl Serializable for ProofPreimageVersioned {
232    fn serialize(&self, writer: &mut impl Write) -> std::io::Result<()> {
233        match self {
234            ProofPreimageVersioned::V2(preimage) => {
235                Serializable::serialize(&1u8, writer)?;
236                preimage.serialize(writer)
237            }
238        }
239    }
240    fn serialized_size(&self) -> usize {
241        match self {
242            ProofPreimageVersioned::V2(preimage) => preimage.serialized_size() + 1,
243        }
244    }
245}
246
247impl Deserializable for ProofPreimageVersioned {
248    fn deserialize(reader: &mut impl Read, recursion_depth: u32) -> std::io::Result<Self> {
249        let discrim = Deserializable::deserialize(reader, recursion_depth)?;
250        match discrim {
251            0u8 => Err(std::io::Error::new(
252                std::io::ErrorKind::InvalidData,
253                format!("invalid old discriminant for ProofPreimageVersioned: {discrim}"),
254            )),
255            1 => Ok(ProofPreimageVersioned::V2(std::sync::Arc::new(
256                ProofPreimage::deserialize(reader, recursion_depth)?,
257            ))),
258            _ => Err(std::io::Error::new(
259                std::io::ErrorKind::InvalidData,
260                format!("unknown discriminant for ProofPreimageVersioned: {discrim}"),
261            )),
262        }
263    }
264}
265
266impl Tagged for ProofPreimageVersioned {
267    fn tag() -> std::borrow::Cow<'static, str> {
268        "proof-preimage-versioned".into()
269    }
270    fn tag_unique_factor() -> String {
271        format!("[[],{}]", ProofPreimage::tag())
272    }
273}
274
275tag_enforcement_test!(ProofPreimageVersioned);
276
277impl ProofPreimageVersioned {
278    pub fn key_location(&self) -> &KeyLocation {
279        match self {
280            ProofPreimageVersioned::V2(ppi) => &ppi.key_location,
281        }
282    }
283}
284
285#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Storable, Introspection)]
286#[storable(base)]
287#[tag = "proof-versioned"]
288#[non_exhaustive]
289pub enum ProofVersioned {
290    V2(Proof),
291}
292
293impl Serializable for ProofVersioned {
294    fn serialize(&self, writer: &mut impl Write) -> std::io::Result<()> {
295        match self {
296            ProofVersioned::V2(proof) => {
297                Serializable::serialize(&1u8, writer)?;
298                proof.serialize(writer)
299            }
300        }
301    }
302    fn serialized_size(&self) -> usize {
303        match self {
304            ProofVersioned::V2(proof) => proof.serialized_size() + 1,
305        }
306    }
307}
308
309impl Deserializable for ProofVersioned {
310    fn deserialize(reader: &mut impl Read, recursion_depth: u32) -> std::io::Result<Self> {
311        let discrim = Deserializable::deserialize(reader, recursion_depth)?;
312        match discrim {
313            0u8 => Err(std::io::Error::new(
314                std::io::ErrorKind::InvalidData,
315                format!("invalid old discriminant for ProofVersioned: {discrim}"),
316            )),
317            1 => Ok(ProofVersioned::V2(Proof::deserialize(
318                reader,
319                recursion_depth,
320            )?)),
321            _ => Err(std::io::Error::new(
322                std::io::ErrorKind::InvalidData,
323                format!("unknown discriminant for ProofVersioned: {discrim}"),
324            )),
325        }
326    }
327}
328
329impl Tagged for ProofVersioned {
330    fn tag() -> std::borrow::Cow<'static, str> {
331        "proof-versioned".into()
332    }
333    fn tag_unique_factor() -> String {
334        format!("[[],{}]", Proof::tag())
335    }
336}
337
338tag_enforcement_test!(ProofVersioned);
339
340#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serializable, Storable)]
341#[tag = "proof"]
342#[storable(base)]
343pub struct ProofMarker;
344
345#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serializable, Storable)]
346#[tag = "proof-preimage"]
347#[storable(base)]
348pub struct ProofPreimageMarker;
349
350pub trait ProofKind<D: DB>: Ord + Storable<D> + Serializable + Deserializable + Tagged {
351    type Pedersen: PedersenDowngradeable<D> + Serializable + Deserializable + Tagged;
352    type Proof: Clone
353        + PartialEq
354        + Eq
355        + PartialOrd
356        + Ord
357        + Serializable
358        + Deserializable
359        + Tagged
360        + Storable<D>;
361    type LatestProof: Clone
362        + PartialEq
363        + Eq
364        + Ord
365        + Serializable
366        + Deserializable
367        + Hash
368        + Storable<D>
369        + Tagged
370        + Into<Self::Proof>;
371    fn zswap_well_formed(
372        offer: &zswap::Offer<Self::LatestProof, D>,
373        segment: u16,
374    ) -> Result<Pedersen, MalformedOffer>;
375    fn zswap_claim_well_formed(
376        claim: &zswap::AuthorizedClaim<Self::LatestProof>,
377    ) -> Result<(), MalformedOffer>;
378    #[allow(clippy::result_large_err)]
379    fn proof_verify(
380        op: &ContractOperation,
381        proof: &Self::Proof,
382        pis: Vec<Fr>,
383        call: &ContractCall<Self, D>,
384        mode: ProofVerificationMode,
385    ) -> Result<(), MalformedTransaction<D>>;
386    #[allow(clippy::result_large_err)]
387    fn latest_proof_verify(
388        op: &VerifierKey,
389        proof: &Self::LatestProof,
390        pis: Vec<Fr>,
391        mode: ProofVerificationMode,
392    ) -> Result<(), MalformedTransaction<D>>;
393    /// Provides the transaction size, real for proven transactions, and crudely
394    /// estimated for unproven.
395    fn estimated_tx_size<
396        S: SignatureKind<D>,
397        B: Storable<D> + PedersenDowngradeable<D> + Serializable,
398    >(
399        tx: &Transaction<S, Self, B, D>,
400    ) -> usize;
401}
402
403impl From<Proof> for ProofVersioned {
404    fn from(proof: Proof) -> Self {
405        Self::V2(proof)
406    }
407}
408
409impl<D: DB> ProofKind<D> for ProofMarker {
410    type Pedersen = PureGeneratorPedersen;
411    type Proof = ProofVersioned;
412    type LatestProof = Proof;
413    fn zswap_well_formed(
414        offer: &zswap::Offer<Self::LatestProof, D>,
415        segment: u16,
416    ) -> Result<Pedersen, MalformedOffer> {
417        offer.well_formed(segment)
418    }
419    fn zswap_claim_well_formed(
420        claim: &zswap::AuthorizedClaim<Self::LatestProof>,
421    ) -> Result<(), MalformedOffer> {
422        claim.well_formed()
423    }
424    #[cfg(not(feature = "proof-verifying"))]
425    fn proof_verify(
426        _op: &ContractOperation,
427        _proof: &Self::Proof,
428        _pis: Vec<Fr>,
429        _call: &ContractCall<Self, D>,
430        _mode: ProofVerificationMode,
431    ) -> Result<(), MalformedTransaction<D>> {
432        Ok(())
433    }
434    #[cfg(feature = "proof-verifying")]
435    fn proof_verify(
436        op: &ContractOperation,
437        proof: &Self::Proof,
438        pis: Vec<Fr>,
439        call: &ContractCall<Self, D>,
440        mode: ProofVerificationMode,
441    ) -> Result<(), MalformedTransaction<D>> {
442        use transient_crypto::proofs::PARAMS_VERIFIER;
443
444        let vk = match &op.v2 {
445            Some(vk) => vk,
446            None => {
447                warn!("missing verifier key");
448                return Err(MalformedTransaction::<D>::VerifierKeyNotPresent {
449                    address: call.address,
450                    operation: call.entry_point.clone(),
451                });
452            }
453        };
454
455        if op.v2.is_some() && !matches!(proof, ProofVersioned::V2(_)) {
456            return Err(MalformedTransaction::<D>::UnsupportedProofVersion {
457                op_version: "V2".to_string(),
458            });
459        }
460
461        match proof {
462            ProofVersioned::V2(proof) => match mode {
463                #[cfg(feature = "mock-verify")]
464                ProofVerificationMode::CalibratedMock => vk
465                    .mock_verify(pis.into_iter())
466                    .map_err(MalformedTransaction::<D>::InvalidProof),
467                _ => vk
468                    .verify(&PARAMS_VERIFIER, proof, pis.into_iter())
469                    .map_err(MalformedTransaction::<D>::InvalidProof),
470            },
471        }
472    }
473    #[allow(clippy::result_large_err)]
474    fn latest_proof_verify(
475        vk: &VerifierKey,
476        proof: &Self::LatestProof,
477        pis: Vec<Fr>,
478        mode: ProofVerificationMode,
479    ) -> Result<(), MalformedTransaction<D>> {
480        use transient_crypto::proofs::PARAMS_VERIFIER;
481
482        match mode {
483            #[cfg(feature = "mock-verify")]
484            ProofVerificationMode::CalibratedMock => vk
485                .mock_verify(pis.into_iter())
486                .map_err(MalformedTransaction::<D>::InvalidProof),
487            _ => vk
488                .verify(&PARAMS_VERIFIER, proof, pis.into_iter())
489                .map_err(MalformedTransaction::<D>::InvalidProof),
490        }
491    }
492    fn estimated_tx_size<
493        S: SignatureKind<D>,
494        B: Storable<D> + PedersenDowngradeable<D> + Serializable,
495    >(
496        tx: &Transaction<S, Self, B, D>,
497    ) -> usize {
498        tx.serialized_size()
499    }
500}
501
502impl From<ProofPreimage> for ProofPreimageVersioned {
503    fn from(proof: ProofPreimage) -> Self {
504        Self::V2(std::sync::Arc::new(proof))
505    }
506}
507
508impl<D: DB> ProofKind<D> for ProofPreimageMarker {
509    type Pedersen = PedersenRandomness;
510    type Proof = ProofPreimageVersioned;
511    type LatestProof = ProofPreimage;
512    fn zswap_well_formed(
513        offer: &zswap::Offer<Self::LatestProof, D>,
514        segment: u16,
515    ) -> Result<Pedersen, MalformedOffer> {
516        offer.well_formed(segment)
517    }
518    fn zswap_claim_well_formed(
519        _: &zswap::AuthorizedClaim<Self::LatestProof>,
520    ) -> Result<(), MalformedOffer> {
521        Ok(())
522    }
523    fn proof_verify(
524        _: &ContractOperation,
525        _: &Self::Proof,
526        _: Vec<Fr>,
527        _: &ContractCall<Self, D>,
528        _: ProofVerificationMode,
529    ) -> Result<(), MalformedTransaction<D>> {
530        Ok(())
531    }
532    #[allow(clippy::result_large_err)]
533    fn latest_proof_verify(
534        _: &VerifierKey,
535        _: &Self::LatestProof,
536        _: Vec<Fr>,
537        _: ProofVerificationMode,
538    ) -> Result<(), MalformedTransaction<D>> {
539        Ok(())
540    }
541    fn estimated_tx_size<
542        S: SignatureKind<D>,
543        B: Storable<D> + PedersenDowngradeable<D> + Serializable,
544    >(
545        tx: &Transaction<S, Self, B, D>,
546    ) -> usize {
547        <()>::estimated_tx_size(&tx.erase_proofs())
548    }
549}
550
551impl<D: DB> ProofKind<D> for () {
552    type Pedersen = Pedersen;
553    type Proof = ();
554    type LatestProof = ();
555    fn zswap_well_formed(
556        offer: &zswap::Offer<Self::LatestProof, D>,
557        segment: u16,
558    ) -> Result<Pedersen, MalformedOffer> {
559        offer.well_formed(segment)
560    }
561    fn zswap_claim_well_formed(
562        _: &zswap::AuthorizedClaim<Self::LatestProof>,
563    ) -> Result<(), MalformedOffer> {
564        Ok(())
565    }
566    fn proof_verify(
567        _: &ContractOperation,
568        _: &Self::Proof,
569        _: Vec<Fr>,
570        _: &ContractCall<Self, D>,
571        _: ProofVerificationMode,
572    ) -> Result<(), MalformedTransaction<D>> {
573        Ok(())
574    }
575    #[allow(clippy::result_large_err)]
576    fn latest_proof_verify(
577        _: &VerifierKey,
578        _: &Self::LatestProof,
579        _: Vec<Fr>,
580        _: ProofVerificationMode,
581    ) -> Result<(), MalformedTransaction<D>> {
582        Ok(())
583    }
584    fn estimated_tx_size<
585        S: SignatureKind<D>,
586        B: Storable<D> + PedersenDowngradeable<D> + Serializable,
587    >(
588        tx: &Transaction<S, Self, B, D>,
589    ) -> usize {
590        let size = tx.serialized_size();
591        let calls = tx.calls().count();
592        let dust_spends = tx
593            .intents()
594            .map(|(_, intent)| {
595                intent
596                    .dust_actions
597                    .as_ref()
598                    .map(|da| da.spends.len())
599                    .unwrap_or(0)
600            })
601            .sum::<usize>();
602        let (zswap_inputs, zswap_outputs) = if let Transaction::Standard(stx) = tx {
603            let transients = stx.transients().count();
604            (
605                stx.inputs().count() + transients,
606                stx.outputs().count() + transients,
607            )
608        } else {
609            (0, 0)
610        };
611        size + calls * PROOF_SIZE
612            + zswap_inputs * zswap::INPUT_PROOF_SIZE
613            + zswap_outputs * zswap::OUTPUT_PROOF_SIZE
614            + dust_spends * DUST_SPEND_PROOF_SIZE
615    }
616}
617
618#[derive(Clone, Debug, PartialEq, Serializable, Storable)]
619#[storable(base)]
620#[tag = "output-instruction-shielded[v2]"]
621pub struct OutputInstructionShielded {
622    pub amount: u128,
623    pub target_key: coin_structure::coin::PublicKey,
624}
625tag_enforcement_test!(OutputInstructionShielded);
626
627#[derive(Copy, Clone, Debug, PartialEq, Eq, Serializable, Storable)]
628#[storable(base)]
629#[tag = "claim-kind[v1]"]
630pub enum ClaimKind {
631    Reward,
632    CardanoBridge,
633}
634tag_enforcement_test!(ClaimKind);
635
636impl Display for ClaimKind {
637    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
638        match self {
639            ClaimKind::Reward => write!(f, "rewards claim"),
640            ClaimKind::CardanoBridge => write!(f, "cardano bridge claim"),
641        }
642    }
643}
644
645impl rand::distributions::Distribution<ClaimKind> for rand::distributions::Standard {
646    fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> ClaimKind {
647        let b: bool = rng.r#gen();
648        if b {
649            ClaimKind::Reward
650        } else {
651            ClaimKind::CardanoBridge
652        }
653    }
654}
655
656#[derive(Clone, Debug, PartialEq, Serializable, Storable)]
657#[storable(base)]
658#[tag = "output-instruction-unshielded[v1]"]
659pub struct OutputInstructionUnshielded {
660    pub amount: u128,
661    pub target_address: UserAddress,
662    pub nonce: Nonce,
663}
664tag_enforcement_test!(OutputInstructionUnshielded);
665
666impl OutputInstructionUnshielded {
667    pub fn to_hash_data(self, tt: UnshieldedTokenType) -> Vec<u8> {
668        let mut data = Vec::new();
669        data.extend(b"midnight:hash-output-instruction-unshielded:");
670        Serializable::serialize(&tt, &mut data).expect("In-memory serialization should succeed");
671        Serializable::serialize(&self.amount, &mut data)
672            .expect("In-memory serialization should succeed");
673        Serializable::serialize(&self.target_address, &mut data)
674            .expect("In-memory serialization should succeed");
675        Serializable::serialize(&self.nonce, &mut data)
676            .expect("In-memory serialization should succeed");
677        data
678    }
679
680    pub fn mk_intent_hash(self, tt: UnshieldedTokenType) -> IntentHash {
681        IntentHash(persistent_hash(&self.to_hash_data(tt)))
682    }
683}
684
685#[derive(Clone, Debug, PartialEq, Serializable)]
686#[tag = "cnight-generates-dust-action-type"]
687pub enum CNightGeneratesDustActionType {
688    Create,
689    Destroy,
690}
691tag_enforcement_test!(CNightGeneratesDustActionType);
692
693#[derive(Clone, Debug, PartialEq, Serializable)]
694#[tag = "cnight-generates-dust-event[v1]"]
695pub struct CNightGeneratesDustEvent {
696    pub value: u128,
697    pub owner: crate::dust::DustPublicKey,
698    pub time: Timestamp,
699    pub action: CNightGeneratesDustActionType,
700    pub nonce: crate::dust::InitialNonce,
701}
702tag_enforcement_test!(CNightGeneratesDustEvent);
703
704#[derive(Clone, Debug, PartialEq, Serializable, Storable)]
705#[storable(base)]
706#[tag = "cardano-bridge[v1]"]
707pub struct CardanoBridge {
708    pub amount: u128,
709    pub target_address: UserAddress,
710    pub nonce: Nonce,
711}
712tag_enforcement_test!(CardanoBridge);
713
714#[derive(Clone, Debug, PartialEq, Serializable, Storable)]
715#[tag = "system-transaction[v6]"]
716#[storable(base)]
717#[non_exhaustive]
718// TODO: Getting `Box` to serialize is a pain right now. Revisit later.
719#[allow(clippy::large_enum_variant)]
720pub enum SystemTransaction {
721    OverwriteParameters(LedgerParameters),
722    DistributeNight(ClaimKind, Vec<OutputInstructionUnshielded>),
723    PayBlockRewardsToTreasury {
724        amount: u128,
725    },
726    PayFromTreasuryShielded {
727        outputs: Vec<OutputInstructionShielded>,
728        nonce: HashOutput,
729        token_type: ShieldedTokenType,
730    },
731    PayFromTreasuryUnshielded {
732        outputs: Vec<OutputInstructionUnshielded>,
733        token_type: UnshieldedTokenType,
734    },
735    DistributeReserve(u128),
736    CNightGeneratesDustUpdate {
737        events: Vec<CNightGeneratesDustEvent>,
738    },
739}
740tag_enforcement_test!(SystemTransaction);
741
742#[derive(Storable)]
743#[derive_where(Clone, PartialEq, Eq)]
744#[storable(db = D)]
745pub struct SegIntent<D: DB>(u16, ErasedIntent<D>);
746
747impl<D: DB> SegIntent<D> {
748    pub fn into_inner(&self) -> (u16, ErasedIntent<D>) {
749        (self.0, self.1.clone())
750    }
751}
752
753#[derive(Storable)]
754#[tag = "unshielded-offer[v1]"]
755#[derive_where(Clone, PartialEq, Eq, PartialOrd, Ord; S)]
756#[storable(db = D)]
757pub struct UnshieldedOffer<S: SignatureKind<D>, D: DB> {
758    pub inputs: storage::storage::Array<UtxoSpend, D>,
759    // This will soon become the following with the introduction of Dust
760    // tokenomics:
761    // outputs: Vec<Either<UtxoOutput, GeneratingUtxoOutput>>,
762    pub outputs: storage::storage::Array<UtxoOutput, D>,
763    // Note that for S = (), this has a fixed point of ().
764    // This signs the intent, and the segment ID
765    pub signatures: storage::storage::Array<S::Signature<SegIntent<D>>, D>,
766}
767tag_enforcement_test!(UnshieldedOffer<(), InMemoryDB>);
768
769impl<S: SignatureKind<D>, D: DB> fmt::Debug for UnshieldedOffer<S, D> {
770    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
771        f.debug_struct("UnshieldedOffer")
772            .field("inputs", &self.inputs)
773            .field("outputs", &self.outputs)
774            .field(
775                "signatures",
776                &format_args!("[{} signatures]", self.signatures.len()),
777            )
778            .finish()
779    }
780}
781
782impl<S: SignatureKind<D>, D: DB> UnshieldedOffer<S, D> {
783    pub fn add_signatures(
784        &mut self,
785        signatures: Vec<<S as SignatureKind<D>>::Signature<SegIntent<D>>>,
786    ) {
787        for signature in signatures {
788            self.signatures = self.signatures.push(signature);
789        }
790    }
791
792    pub fn erase_signatures(&self) -> UnshieldedOffer<(), D>
793    where
794        UnshieldedOffer<S, D>: Clone,
795    {
796        UnshieldedOffer {
797            inputs: self.inputs.clone(),
798            outputs: self.outputs.clone(),
799            signatures: vec![].into(),
800        }
801    }
802}
803
804#[derive(
805    Debug,
806    Default,
807    Copy,
808    Clone,
809    Hash,
810    PartialEq,
811    Eq,
812    PartialOrd,
813    Ord,
814    FieldRepr,
815    FromFieldRepr,
816    BinaryHashRepr,
817    Serializable,
818    Storable,
819    Dummy,
820)]
821#[storable(base)]
822#[tag = "intent-hash"]
823pub struct IntentHash(pub HashOutput);
824tag_enforcement_test!(IntentHash);
825
826impl<D: DB> ErasedIntent<D> {
827    pub fn intent_hash(&self, segment_id: u16) -> IntentHash {
828        IntentHash(persistent_hash(&self.data_to_sign(segment_id)))
829    }
830}
831
832impl rand::distributions::Distribution<IntentHash> for rand::distributions::Standard {
833    fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> IntentHash {
834        IntentHash(rng.r#gen())
835    }
836}
837
838pub type ErasedIntent<D> = Intent<(), (), Pedersen, D>;
839
840#[derive(Storable)]
841#[tag = "intent[v6]"]
842#[derive_where(Clone, PartialEq, Eq; S, B, P)]
843#[storable(db = D)]
844pub struct Intent<S: SignatureKind<D>, P: ProofKind<D>, B: Storable<D>, D: DB> {
845    pub guaranteed_unshielded_offer: Option<Sp<UnshieldedOffer<S, D>, D>>,
846    pub fallible_unshielded_offer: Option<Sp<UnshieldedOffer<S, D>, D>>,
847    pub actions: storage::storage::Array<ContractAction<P, D>, D>,
848    pub dust_actions: Option<Sp<DustActions<S, P, D>, D>>,
849    pub ttl: Timestamp,
850    pub binding_commitment: B,
851}
852tag_enforcement_test!(Intent<(), (), Pedersen, InMemoryDB>);
853
854impl<S: SignatureKind<D>, P: ProofKind<D>, B: Storable<D>, D: DB> Intent<S, P, B, D> {
855    pub fn challenge_pre_for(&self, segment_id: u16) -> Vec<u8> {
856        let mut data = ContractAction::challenge_pre_for(Vec::from(&self.actions).as_slice());
857        let _ = Serializable::serialize(&segment_id, &mut data);
858
859        data
860    }
861}
862
863impl<S: SignatureKind<D> + Debug, P: ProofKind<D>, B: Storable<D>, D: DB> fmt::Debug
864    for Intent<S, P, B, D>
865{
866    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
867        let mut debug_struct = f.debug_struct("Intent");
868        debug_struct
869            .field(
870                "guaranteed_unshielded_offer",
871                &self.guaranteed_unshielded_offer,
872            )
873            .field("fallible_unshielded_offer", &self.fallible_unshielded_offer)
874            .field("actions", &self.actions)
875            .field("ttl", &self.ttl);
876        debug_struct.field("dust_actions", &self.dust_actions);
877        debug_struct
878            .field("binding_commitment", &Symbol("<binding commitment>"))
879            .finish()
880    }
881}
882
883fn to_hash_data<D: DB>(intent: Intent<(), (), Pedersen, D>, mut data: Vec<u8>) -> Vec<u8> {
884    Serializable::serialize(&intent.guaranteed_unshielded_offer, &mut data)
885        .expect("In-memory serialization should succeed");
886    Serializable::serialize(&intent.fallible_unshielded_offer, &mut data)
887        .expect("In-memory serialization should succeed");
888    Serializable::serialize(&intent.actions, &mut data)
889        .expect("In-memory serialization should succeed");
890    Serializable::serialize(&intent.ttl, &mut data)
891        .expect("In-memory serialization should succeed");
892    Serializable::serialize(&intent.binding_commitment, &mut data)
893        .expect("In-memory serialization should succeed");
894    data
895}
896
897impl<
898    S: SignatureKind<D>,
899    P: ProofKind<D>,
900    B: Storable<D> + PedersenDowngradeable<D> + Serializable,
901    D: DB,
902> Intent<S, P, B, D>
903{
904    pub fn erase_proofs(&self) -> Intent<S, (), Pedersen, D>
905    where
906        UnshieldedOffer<S, D>: Clone,
907    {
908        Intent {
909            guaranteed_unshielded_offer: self.guaranteed_unshielded_offer.clone(),
910            fallible_unshielded_offer: self.fallible_unshielded_offer.clone(),
911            actions: self
912                .actions
913                .clone()
914                .iter()
915                .map(|x| x.erase_proof())
916                .collect(),
917            dust_actions: self
918                .dust_actions
919                .as_ref()
920                .map(|act| Sp::new(act.erase_proofs())),
921            ttl: self.ttl,
922            binding_commitment: self.binding_commitment.downgrade(),
923        }
924    }
925
926    pub fn erase_signatures(&self) -> Intent<(), P, B, D>
927    where
928        UnshieldedOffer<S, D>: Clone,
929    {
930        Intent {
931            guaranteed_unshielded_offer: self
932                .guaranteed_unshielded_offer
933                .clone()
934                .map(|x| Sp::new(x.erase_signatures())),
935            fallible_unshielded_offer: self
936                .fallible_unshielded_offer
937                .clone()
938                .map(|x| Sp::new(x.erase_signatures())),
939            actions: self.actions.clone(),
940            dust_actions: self
941                .dust_actions
942                .as_ref()
943                .map(|act| Sp::new(act.erase_signatures())),
944            ttl: self.ttl,
945            binding_commitment: self.binding_commitment.clone(),
946        }
947    }
948
949    pub fn guaranteed_inputs(&self) -> Vec<UtxoSpend> {
950        self.guaranteed_unshielded_offer
951            .clone()
952            .map(|guo| guo.inputs.clone())
953            .unwrap_or_default()
954            .into()
955    }
956
957    pub fn fallible_inputs(&self) -> Vec<UtxoSpend> {
958        self.fallible_unshielded_offer
959            .clone()
960            .map(|fuo| fuo.inputs.clone())
961            .unwrap_or_default()
962            .into()
963    }
964
965    pub fn guaranteed_outputs(&self) -> Vec<UtxoOutput> {
966        self.guaranteed_unshielded_offer
967            .clone()
968            .map(|guo| guo.outputs.clone())
969            .unwrap_or_default()
970            .into()
971    }
972
973    pub fn fallible_outputs(&self) -> Vec<UtxoOutput> {
974        self.fallible_unshielded_offer
975            .clone()
976            .map(|fuo| fuo.outputs.clone())
977            .unwrap_or_default()
978            .into()
979    }
980}
981
982impl<D: DB> Intent<(), (), Pedersen, D> {
983    pub fn data_to_sign(&self, segment_id: u16) -> Vec<u8> {
984        let mut data = Vec::new();
985        data.extend(b"midnight:hash-intent:");
986        Serializable::serialize(&segment_id, &mut data)
987            .expect("In-memory serialization should succeed");
988        to_hash_data::<D>(self.clone(), data)
989    }
990}
991
992#[derive(Storable)]
993#[derive_where(Debug, Clone, PartialEq, Eq)]
994#[storable(db = D)]
995#[tag = "replay-protection-state[v1]"]
996#[must_use]
997pub struct ReplayProtectionState<D: DB> {
998    pub time_filter_map: TimeFilterMap<HashSet<IntentHash, D>, D>,
999}
1000tag_enforcement_test!(ReplayProtectionState<InMemoryDB>);
1001
1002impl<D: DB> ReplayProtectionState<D> {
1003    pub fn new() -> ReplayProtectionState<D> {
1004        ReplayProtectionState {
1005            time_filter_map: TimeFilterMap::new(),
1006        }
1007    }
1008}
1009
1010impl<D: DB> Default for ReplayProtectionState<D> {
1011    fn default() -> Self {
1012        Self::new()
1013    }
1014}
1015
1016#[derive(Clone, Debug, PartialEq, Eq, Serializable, Serialize, Deserialize)]
1017#[tag = "transaction-cost-model[v4]"]
1018pub struct TransactionCostModel {
1019    pub runtime_cost_model: onchain_runtime::cost_model::CostModel,
1020    pub parallelism_factor: u64,
1021    pub baseline_cost: RunningCost,
1022}
1023
1024impl TransactionCostModel {
1025    pub(crate) fn cell_read(&self, size: u64) -> RunningCost {
1026        self.runtime_cost_model.read_cell(size, true)
1027    }
1028    fn cell_write(&self, size: u64, overwrite: bool) -> RunningCost {
1029        RunningCost {
1030            bytes_written: size,
1031            bytes_deleted: if overwrite { size } else { 0 },
1032            ..RunningCost::ZERO
1033        }
1034    }
1035    fn cell_delete(&self, size: u64) -> RunningCost {
1036        RunningCost {
1037            bytes_deleted: size,
1038            ..RunningCost::ZERO
1039        }
1040    }
1041    pub(crate) fn map_index(&self, log_size: usize) -> RunningCost {
1042        self.runtime_cost_model.read_map(log_size, true)
1043    }
1044    pub(crate) fn proof_verify(&self, size: usize) -> RunningCost {
1045        let time = self.runtime_cost_model.proof_verify_constant
1046            + self.runtime_cost_model.proof_verify_coeff_size * size;
1047        RunningCost::compute(time)
1048    }
1049    fn map_insert(&self, log_size: usize, overwrite: bool) -> RunningCost {
1050        let layers = log_size.div_ceil(4);
1051        self.cell_write(PERSISTENT_HASH_BYTES as u64 * 16, true) * layers as u64
1052            + self.cell_write(PERSISTENT_HASH_BYTES as u64, overwrite)
1053    }
1054    fn map_remove(&self, log_size: usize, guaranteed_present: bool) -> RunningCost {
1055        if guaranteed_present {
1056            self.map_insert(log_size, true)
1057        } else {
1058            self.map_insert(log_size, true) + self.map_index(log_size)
1059        }
1060    }
1061    fn time_filter_map_lookup(&self) -> RunningCost {
1062        // TODO: This is a good approximation, but not accurate.
1063        self.map_index(8) * 2u64
1064    }
1065    fn time_filter_map_insert(&self, overwrite: bool) -> RunningCost {
1066        // Two map insertions, the 'set' and the 'time_map'.
1067        self.map_insert(8, overwrite) * 2u64
1068    }
1069    fn merkle_tree_index(&self, log_size: usize) -> RunningCost {
1070        self.runtime_cost_model.read_bmt(log_size, true)
1071    }
1072    fn merkle_tree_insert_no_rehash(&self, log_size: usize, overwrite: bool) -> RunningCost {
1073        // Cost all but the last layer as 'overwrites' -- this isn't strictly
1074        // true, but it captures the spirit here, in that these trees are sized
1075        // specifically for the protocol to *fill* them. Therefore, we
1076        // essentially consider everything except the leaf to already be
1077        // 'reserved'.
1078        let raw_writes = self.cell_write(FR_BYTES as u64, overwrite);
1079        let raw_overwrites = self.cell_write((FR_BYTES * 3 + 2) as u64, true) * log_size as u64;
1080        raw_writes + raw_overwrites
1081    }
1082    fn merkle_tree_insert_unamortized(&self, log_size: usize, overwrite: bool) -> RunningCost {
1083        let rehashes = RunningCost::compute(self.runtime_cost_model.transient_hash * log_size);
1084        rehashes + self.merkle_tree_insert_no_rehash(log_size, overwrite)
1085    }
1086    fn merkle_tree_insert_amortized(&self, log_size: usize, overwrite: bool) -> RunningCost {
1087        // The amortization turns n writes into a tree of depth d from
1088        // nd hashes to n log_2 n + d hashes. That means that the hashes we cost *per insert*
1089        // isn't constant.
1090        //
1091        // Assuming 16 insertions per rehash, and d=32, that's 16 * 4 + 32 = 96 total hashes, or
1092        // 6 hashes per insertion
1093        const EXPECTED_AMORTIZE_HASHES: usize = 6;
1094        let rehashes =
1095            RunningCost::compute(self.runtime_cost_model.transient_hash * EXPECTED_AMORTIZE_HASHES);
1096        rehashes + self.merkle_tree_insert_no_rehash(log_size, overwrite)
1097    }
1098    fn tree_copy<T: Storable<D>, D: DB>(&self, value: Sp<T, D>) -> RunningCost {
1099        self.runtime_cost_model.tree_copy(value)
1100    }
1101    fn stack_setup_cost<D: DB>(&self, transcript: &Transcript<D>) -> RunningCost {
1102        // There's an additional cost of processing a transcript coming from initializing effects
1103        // and context stack variables. This accounts for them.
1104        // The bulk of this is MPT insertions to build up various sets that sit in these values.
1105        // To unify these, we first capture a list of element numbers and element sizes, assuming
1106        // they are constant by container, and then reduce that to a running cost using map
1107        // insert VM operations.
1108        const EXPECTED_COM_INDICES: usize = 16;
1109        let eff = &transcript.effects;
1110        // (amount, key length)
1111        let maps = [
1112            (EXPECTED_COM_INDICES, PERSISTENT_HASH_BYTES),
1113            (eff.claimed_nullifiers.size(), PERSISTENT_HASH_BYTES),
1114            (eff.claimed_shielded_receives.size(), PERSISTENT_HASH_BYTES),
1115            (eff.claimed_shielded_spends.size(), PERSISTENT_HASH_BYTES),
1116            (
1117                eff.claimed_contract_calls.size(),
1118                PERSISTENT_HASH_BYTES * 2 + FR_BYTES + 8,
1119            ),
1120            (eff.shielded_mints.size(), PERSISTENT_HASH_BYTES),
1121            (eff.unshielded_mints.size(), PERSISTENT_HASH_BYTES),
1122            (eff.unshielded_inputs.size(), PERSISTENT_HASH_BYTES + 1),
1123            (eff.unshielded_outputs.size(), PERSISTENT_HASH_BYTES + 1),
1124            (
1125                eff.claimed_unshielded_spends.size(),
1126                PERSISTENT_HASH_BYTES * 2 + 1,
1127            ),
1128        ];
1129        let time = maps
1130            .iter()
1131            .map(|(n, key_len)| {
1132                self.runtime_cost_model.ins_map_constant
1133                    + self.runtime_cost_model.ins_map_coeff_container_log_size
1134                        * n.next_power_of_two().ilog2() as u64
1135                    + self.runtime_cost_model.ins_map_coeff_key_size * *key_len
1136            })
1137            .sum();
1138        RunningCost::compute(time)
1139    }
1140}
1141tag_enforcement_test!(TransactionCostModel);
1142
1143pub const INITIAL_TRANSACTION_COST_MODEL: TransactionCostModel = TransactionCostModel {
1144    runtime_cost_model: onchain_runtime::cost_model::INITIAL_COST_MODEL,
1145    parallelism_factor: 4,
1146    baseline_cost: RunningCost {
1147        compute_time: CostDuration::from_picoseconds(100_000_000),
1148        read_time: CostDuration::ZERO,
1149        bytes_written: 0,
1150        bytes_deleted: 0,
1151    },
1152};
1153
1154#[derive(Clone, Debug, PartialEq, Eq, Serializable)]
1155#[cfg_attr(feature = "fixed-point-custom-serde", derive(Serialize, Deserialize))]
1156#[tag = "transaction-limits[v2]"]
1157pub struct TransactionLimits {
1158    pub transaction_byte_limit: u64,
1159    pub time_to_dismiss_per_byte: CostDuration,
1160    pub min_time_to_dismiss: CostDuration,
1161    pub block_limits: SyntheticCost,
1162    /// The minimum amount of Night withdrawable from block rewards, as a
1163    /// multiple of the amount which would be able to pay the theoretical fees
1164    /// for the withdrawal when Dust reaches its cap.
1165    #[cfg_attr(
1166        feature = "fixed-point-custom-serde",
1167        serde(with = "base_crypto::cost_model::fixed_point_custom_serde")
1168    )]
1169    pub block_withdrawal_minimum_multiple: FixedPoint,
1170}
1171tag_enforcement_test!(TransactionLimits);
1172
1173pub const INITIAL_LIMITS: TransactionLimits = TransactionLimits {
1174    transaction_byte_limit: 1 << 20, // 1 MiB
1175    time_to_dismiss_per_byte: CostDuration::from_picoseconds(2_000_000),
1176    min_time_to_dismiss: CostDuration::from_picoseconds(15_000_000_000),
1177    block_limits: SyntheticCost {
1178        read_time: CostDuration::SECOND,
1179        compute_time: CostDuration::SECOND,
1180        block_usage: 200_000,
1181        bytes_written: 50_000,
1182        bytes_churned: 1_000_000,
1183    },
1184    block_withdrawal_minimum_multiple: FixedPoint::from_u64_div(1, 2),
1185};
1186
1187#[derive(Clone, Debug, PartialEq, Eq, Serializable, Storable)]
1188#[cfg_attr(
1189    feature = "fixed-point-custom-serde",
1190    derive(serde::Serialize, serde::Deserialize)
1191)]
1192#[tag = "ledger-parameters[v5]"]
1193#[storable(base)]
1194pub struct LedgerParameters {
1195    pub cost_model: TransactionCostModel,
1196    pub limits: TransactionLimits,
1197    pub dust: DustParameters,
1198    pub fee_prices: FeePrices,
1199    pub global_ttl: Duration,
1200    // Valid range of 0..1
1201    #[cfg_attr(
1202        feature = "fixed-point-custom-serde",
1203        serde(with = "base_crypto::cost_model::fixed_point_custom_serde")
1204    )]
1205    pub cost_dimension_min_ratio: FixedPoint,
1206    #[cfg_attr(
1207        feature = "fixed-point-custom-serde",
1208        serde(with = "base_crypto::cost_model::fixed_point_custom_serde")
1209    )]
1210    pub price_adjustment_a_parameter: FixedPoint,
1211    // Note: This is equivalent to `c_to_m_bridge_fee_percent` in the spec
1212    // Valid range of 0..10_000
1213    pub cardano_to_midnight_bridge_fee_basis_points: u32,
1214    // Note: This is denominated in STARs (atomic night units)
1215    pub c_to_m_bridge_min_amount: u128,
1216}
1217tag_enforcement_test!(LedgerParameters);
1218
1219impl LedgerParameters {
1220    /// The maximum price adjustment per block with the current parameters, as a multiplicative
1221    /// factor (that is: 1.1 would indicate a 10% adjustment). Will always return the positive (>1)
1222    /// adjustment factor. Note that negative adjustments are the additive inverse (1.1 has a
1223    /// corresponding 0.9 downward adjustment), *not* the multiplicative as might reasonably be
1224    /// assumed.
1225    pub fn max_price_adjustment(&self) -> FixedPoint {
1226        price_adjustment_function(FixedPoint::ONE, self.price_adjustment_a_parameter)
1227            + FixedPoint::ONE
1228    }
1229
1230    pub fn min_claimable_rewards(&self) -> u128 {
1231        let Ok(synthetic_fee) =
1232            Transaction::ClaimRewards::<Signature, ProofMarker, PureGeneratorPedersen, InMemoryDB>(
1233                ClaimRewardsTransaction {
1234                    network_id: "phantom-value".into(),
1235                    value: u128::MAX,
1236                    owner: Default::default(),
1237                    nonce: Default::default(),
1238                    signature: Default::default(),
1239                    kind: ClaimKind::Reward,
1240                },
1241            )
1242            .cost(self, true)
1243        else {
1244            return u128::MAX;
1245        };
1246        let real_dust_fee = synthetic_fee
1247            .normalize(self.limits.block_limits)
1248            .map(|norm| self.fee_prices.overall_cost(&norm))
1249            .unwrap_or(FixedPoint::MAX);
1250        let night_dust_fp = FixedPoint::from_u64_div(
1251            self.dust.night_dust_ratio,
1252            (SPECKS_PER_DUST / STARS_PER_NIGHT) as u64,
1253        );
1254        let night_to_pay_at_cap = real_dust_fee / night_dust_fp;
1255        let min_night_fp = night_to_pay_at_cap * self.limits.block_withdrawal_minimum_multiple;
1256        min_night_fp.into_atomic_units(STARS_PER_NIGHT)
1257    }
1258}
1259
1260pub const INITIAL_PARAMETERS: LedgerParameters = LedgerParameters {
1261    cost_model: INITIAL_TRANSACTION_COST_MODEL,
1262    limits: INITIAL_LIMITS,
1263    dust: INITIAL_DUST_PARAMETERS,
1264    fee_prices: FeePrices {
1265        overall_price: FixedPoint::from_u64_div(10, 1),
1266        read_factor: FixedPoint::ONE,
1267        compute_factor: FixedPoint::ONE,
1268        block_usage_factor: FixedPoint::ONE,
1269        write_factor: FixedPoint::ONE,
1270    },
1271    global_ttl: Duration::from_secs(3600),
1272    cardano_to_midnight_bridge_fee_basis_points: 500,
1273    cost_dimension_min_ratio: FixedPoint::from_u64_div(1, 4),
1274    price_adjustment_a_parameter: FixedPoint::from_u64_div(100, 1),
1275    c_to_m_bridge_min_amount: 1000,
1276};
1277
1278#[derive(Storable)]
1279#[storable(db = D)]
1280#[derive_where(Clone; S, B, P)]
1281#[tag = "transaction[v9]"]
1282// TODO: Getting `Box` to serialize is a pain right now. Revisit later.
1283#[allow(clippy::large_enum_variant)]
1284pub enum Transaction<S: SignatureKind<D>, P: ProofKind<D>, B: Storable<D>, D: DB> {
1285    Standard(StandardTransaction<S, P, B, D>),
1286    ClaimRewards(ClaimRewardsTransaction<S, D>),
1287}
1288tag_enforcement_test!(Transaction<(), (), Pedersen, InMemoryDB>);
1289
1290#[derive_where(Debug, Clone)]
1291pub struct VerifiedTransaction<D: DB> {
1292    pub(crate) inner: Transaction<(), (), Pedersen, D>,
1293    pub(crate) hash: TransactionHash,
1294}
1295
1296impl<D: DB> Deref for VerifiedTransaction<D> {
1297    type Target = Transaction<(), (), Pedersen, D>;
1298    fn deref(&self) -> &Self::Target {
1299        &self.inner
1300    }
1301}
1302
1303impl<S: SignatureKind<D> + Debug, P: ProofKind<D> + Debug, B: Storable<D> + Debug, D: DB> Debug
1304    for Transaction<S, P, B, D>
1305{
1306    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1307        match self {
1308            Transaction::Standard(stx) => stx.fmt(f),
1309            Transaction::ClaimRewards(cm) => cm.fmt(f),
1310        }
1311    }
1312}
1313
1314impl<
1315    S: SignatureKind<D>,
1316    P: ProofKind<D>,
1317    B: Storable<D> + PedersenDowngradeable<D> + Serializable,
1318    D: DB,
1319> Transaction<S, P, B, D>
1320{
1321    pub fn erase_proofs(&self) -> Transaction<S, (), Pedersen, D> {
1322        match self {
1323            Transaction::Standard(StandardTransaction {
1324                network_id,
1325                intents,
1326                guaranteed_coins,
1327                fallible_coins,
1328                binding_randomness,
1329                ..
1330            }) => Transaction::Standard(StandardTransaction {
1331                network_id: network_id.clone(),
1332                intents: intents
1333                    .iter()
1334                    .map(|seg_intent| {
1335                        (
1336                            *seg_intent.deref().0.deref(),
1337                            seg_intent.deref().1.deref().erase_proofs(),
1338                        )
1339                    })
1340                    .collect(),
1341                guaranteed_coins: guaranteed_coins
1342                    .as_ref()
1343                    .map(|x| Sp::new(ZswapOffer::erase_proofs(x.deref()))),
1344                fallible_coins: fallible_coins
1345                    .iter()
1346                    .map(|sp| (*sp.deref().0.deref(), sp.deref().1.deref().erase_proofs()))
1347                    .collect(),
1348                binding_randomness: *binding_randomness,
1349            }),
1350            Transaction::ClaimRewards(claim) => Transaction::ClaimRewards(claim.clone()),
1351        }
1352    }
1353
1354    pub fn erase_signatures(&self) -> Transaction<(), P, B, D> {
1355        match self {
1356            Transaction::Standard(StandardTransaction {
1357                network_id,
1358                intents,
1359                guaranteed_coins,
1360                fallible_coins,
1361                binding_randomness,
1362                ..
1363            }) => Transaction::Standard(StandardTransaction {
1364                network_id: network_id.clone(),
1365                intents: intents
1366                    .iter()
1367                    .map(|sp| {
1368                        (
1369                            *sp.deref().0.deref(),
1370                            sp.deref().1.deref().erase_signatures(),
1371                        )
1372                    })
1373                    .collect(),
1374                guaranteed_coins: guaranteed_coins.clone(),
1375                fallible_coins: fallible_coins.clone(),
1376                binding_randomness: *binding_randomness,
1377            }),
1378            Transaction::ClaimRewards(claim) => Transaction::ClaimRewards(claim.erase_signatures()),
1379        }
1380    }
1381
1382    #[instrument(skip(self, other))]
1383    pub fn merge(&self, other: &Self) -> Result<Self, MalformedTransaction<D>> {
1384        use Transaction as T;
1385        match (self, other) {
1386            (T::Standard(stx1), T::Standard(stx2)) => {
1387                if stx1.network_id != stx2.network_id {
1388                    return Err(MalformedTransaction::InvalidNetworkId {
1389                        expected: stx1.network_id.clone(),
1390                        found: stx2.network_id.clone(),
1391                    });
1392                }
1393                let res = Transaction::Standard(StandardTransaction {
1394                    network_id: stx1.network_id.clone(),
1395                    intents: stx1
1396                        .intents
1397                        .clone()
1398                        .into_iter()
1399                        .chain(stx2.intents.clone())
1400                        // Would be nicer to add Entry for HashMap and use that
1401                        .try_fold(HashMap::new(), |mut acc, (k, v)| {
1402                            if acc.contains_key(&k) {
1403                                Err(MalformedTransaction::<D>::IntentSegmentIdCollision(k))
1404                            } else {
1405                                acc = acc.insert(k, v);
1406                                Ok(acc)
1407                            }
1408                        })?,
1409                    guaranteed_coins: match (
1410                        stx1.guaranteed_coins.as_ref(),
1411                        stx2.guaranteed_coins.as_ref(),
1412                    ) {
1413                        (Some(gc1), Some(gc2)) => Some(Sp::new(
1414                            gc1.merge(gc2).map_err(MalformedTransaction::<D>::Zswap)?,
1415                        )),
1416                        (Some(gc1), None) => Some(gc1.clone()),
1417                        (None, Some(gc2)) => Some(gc2.clone()),
1418                        (None, None) => None,
1419                    },
1420                    fallible_coins: {
1421                        let mut result: std::collections::BTreeMap<
1422                            u16,
1423                            ZswapOffer<P::LatestProof, D>,
1424                        > = stx1.fallible_coins.clone().into_iter().collect();
1425                        for (key, offer2) in stx2.fallible_coins.clone() {
1426                            match result.get(&key) {
1427                                Some(offer1) => {
1428                                    result.insert(key, offer1.merge(&offer2)?);
1429                                }
1430                                None => {
1431                                    result.insert(key, offer2.clone());
1432                                }
1433                            }
1434                        }
1435
1436                        result.into_iter().collect()
1437                    },
1438                    binding_randomness: stx1.binding_randomness + stx2.binding_randomness,
1439                });
1440                debug!("transaction merged");
1441                Ok(res)
1442            }
1443            _ => Err(MalformedTransaction::<D>::CantMergeTypes),
1444        }
1445    }
1446
1447    pub fn identifiers(&'_ self) -> impl Iterator<Item = TransactionIdentifier> + '_ {
1448        let mut res = Vec::new();
1449        match self {
1450            Transaction::Standard(stx) => res.extend(
1451                stx.inputs()
1452                    .map(|i| i.value_commitment)
1453                    .chain(stx.outputs().map(|o| o.value_commitment))
1454                    .chain(stx.transients().map(|io| io.value_commitment_input))
1455                    .chain(stx.transients().map(|io| io.value_commitment_output))
1456                    .chain(
1457                        stx.intents()
1458                            .map(|(_, intent)| intent.binding_commitment.downgrade()),
1459                    )
1460                    .map(TransactionIdentifier::Merged),
1461            ),
1462            Transaction::ClaimRewards(claim) => res.push(TransactionIdentifier::Unique(
1463                OutputInstructionUnshielded {
1464                    amount: claim.value,
1465                    target_address: claim.owner.clone().into(),
1466                    nonce: claim.nonce,
1467                }
1468                .clone()
1469                .mk_intent_hash(NIGHT)
1470                .0,
1471            )),
1472        }
1473        res.into_iter()
1474    }
1475
1476    pub fn actions(&self) -> impl Iterator<Item = (u16, ContractAction<P, D>)> {
1477        match self {
1478            Transaction::Standard(stx) => stx.actions().collect(),
1479            _ => Vec::new(),
1480        }
1481        .into_iter()
1482    }
1483
1484    pub fn deploys(&self) -> impl Iterator<Item = (u16, ContractDeploy<D>)> {
1485        match self {
1486            Transaction::Standard(stx) => stx.deploys().collect(),
1487            _ => Vec::new(),
1488        }
1489        .into_iter()
1490    }
1491
1492    pub fn updates(&'_ self) -> impl Iterator<Item = (u16, MaintenanceUpdate<D>)> {
1493        match self {
1494            Transaction::Standard(stx) => stx.updates().collect(),
1495            _ => Vec::new(),
1496        }
1497        .into_iter()
1498    }
1499
1500    pub fn calls(&'_ self) -> impl Iterator<Item = (u16, ContractCall<P, D>)> {
1501        match self {
1502            Transaction::Standard(stx) => stx.calls().collect(),
1503            _ => Vec::new(),
1504        }
1505        .into_iter()
1506    }
1507
1508    pub fn intents(&'_ self) -> impl Iterator<Item = (u16, Intent<S, P, B, D>)> {
1509        match self {
1510            Transaction::Standard(stx) => stx.intents().collect(),
1511            _ => Vec::new(),
1512        }
1513        .into_iter()
1514    }
1515
1516    pub fn has_identifier(&self, ident: &TransactionIdentifier) -> bool {
1517        self.identifiers().any(|ident2| ident == &ident2)
1518    }
1519}
1520
1521impl<S: SignatureKind<D>, P: ProofKind<D>, B: Storable<D>, D: DB> Transaction<S, P, B, D>
1522where
1523    Transaction<S, P, B, D>: Serializable,
1524{
1525    pub fn segments(&self) -> Vec<u16> {
1526        match self {
1527            Self::Standard(stx) => stx.segments(),
1528            Self::ClaimRewards(_) => Vec::new(),
1529        }
1530    }
1531}
1532
1533impl<S: SignatureKind<D>, P: ProofKind<D>, B: Storable<D>, D: DB> Intent<S, P, B, D> {
1534    pub fn calls(&self) -> impl Iterator<Item = &ContractCall<P, D>> {
1535        self.actions.iter_deref().filter_map(|cd| match cd {
1536            ContractAction::Call(upd) => Some(&**upd),
1537            _ => None,
1538        })
1539    }
1540
1541    pub fn calls_owned(&self) -> Vec<ContractCall<P, D>>
1542    where
1543        ContractCall<P, D>: Clone,
1544    {
1545        self.actions
1546            .iter_deref()
1547            .filter_map(|cd| match cd {
1548                ContractAction::Call(upd) => Some((**upd).clone()),
1549                _ => None,
1550            })
1551            .collect()
1552    }
1553}
1554
1555#[derive(Storable)]
1556#[storable(db = D)]
1557#[derive_where(Clone, Debug; S, P, B)]
1558#[tag = "standard-transaction[v9]"]
1559pub struct StandardTransaction<S: SignatureKind<D>, P: ProofKind<D>, B: Storable<D>, D: DB> {
1560    pub network_id: String,
1561    pub intents: HashMap<u16, Intent<S, P, B, D>, D>,
1562    pub guaranteed_coins: Option<Sp<ZswapOffer<P::LatestProof, D>, D>>,
1563    pub fallible_coins: HashMap<u16, ZswapOffer<P::LatestProof, D>, D>,
1564    pub binding_randomness: PedersenRandomness,
1565}
1566tag_enforcement_test!(StandardTransaction<(), (), Pedersen, InMemoryDB>);
1567
1568impl<S: SignatureKind<D>, P: ProofKind<D> + Serializable + Deserializable, B: Storable<D>, D: DB>
1569    StandardTransaction<S, P, B, D>
1570{
1571    pub fn actions(&self) -> impl Iterator<Item = (u16, ContractAction<P, D>)> {
1572        self.intents
1573            .clone()
1574            .into_iter()
1575            .flat_map(|(segment_id, intent)| {
1576                Vec::from(&intent.actions)
1577                    .into_iter()
1578                    .map(move |act| (segment_id, act))
1579            })
1580    }
1581
1582    pub fn deploys(&self) -> impl Iterator<Item = (u16, ContractDeploy<D>)> {
1583        self.intents
1584            .clone()
1585            .into_iter()
1586            .flat_map(|(segment_id, intent)| {
1587                Vec::from(&intent.actions)
1588                    .into_iter()
1589                    .map(move |act| (segment_id, act))
1590            })
1591            .filter_map(|(segment_id, action)| match action {
1592                ContractAction::Deploy(d) => Some((segment_id, (*d).clone())),
1593                _ => None,
1594            })
1595    }
1596
1597    pub fn updates(&self) -> impl Iterator<Item = (u16, MaintenanceUpdate<D>)> {
1598        self.intents
1599            .clone()
1600            .into_iter()
1601            .flat_map(|(segment_id, intent)| {
1602                Vec::from(&intent.actions)
1603                    .into_iter()
1604                    .map(move |act| (segment_id, act))
1605            })
1606            .filter_map(|(segment_id, action)| match action {
1607                ContractAction::Maintain(upd) => Some((segment_id, upd)),
1608                _ => None,
1609            })
1610    }
1611
1612    pub fn calls(&self) -> impl Iterator<Item = (u16, ContractCall<P, D>)> {
1613        self.intents
1614            .clone()
1615            .into_iter()
1616            .flat_map(|(segment_id, intent)| {
1617                Vec::from(&intent.actions)
1618                    .into_iter()
1619                    .filter_map(move |action| {
1620                        if let ContractAction::Call(upd) = action {
1621                            Some((segment_id, (*upd).clone()))
1622                        } else {
1623                            None
1624                        }
1625                    })
1626            })
1627    }
1628
1629    pub fn intents(&'_ self) -> impl Iterator<Item = (u16, Intent<S, P, B, D>)> {
1630        self.intents.clone().into_iter()
1631    }
1632
1633    pub fn inputs(
1634        &self,
1635    ) -> impl Iterator<Item = Input<<P as ProofKind<D>>::LatestProof, D>> + use<'_, S, P, B, D>
1636    {
1637        self.guaranteed_inputs().chain(self.fallible_inputs())
1638    }
1639
1640    pub fn guaranteed_inputs(
1641        &self,
1642    ) -> impl Iterator<Item = Input<<P as ProofKind<D>>::LatestProof, D>> + use<'_, S, P, B, D>
1643    {
1644        self.guaranteed_coins
1645            .iter()
1646            .flat_map(|gc| Vec::from(&gc.inputs).into_iter())
1647    }
1648
1649    pub fn fallible_inputs(
1650        &self,
1651    ) -> impl Iterator<Item = Input<<P as ProofKind<D>>::LatestProof, D>> {
1652        self.fallible_coins
1653            .iter()
1654            .flat_map(|sp| Vec::from(&sp.1.inputs).into_iter())
1655    }
1656
1657    pub fn outputs(
1658        &self,
1659    ) -> impl Iterator<Item = Output<<P as ProofKind<D>>::LatestProof, D>> + use<'_, S, P, B, D>
1660    {
1661        self.guaranteed_outputs().chain(self.fallible_outputs())
1662    }
1663
1664    pub fn guaranteed_outputs(
1665        &self,
1666    ) -> impl Iterator<Item = Output<<P as ProofKind<D>>::LatestProof, D>> + use<'_, S, P, B, D>
1667    {
1668        self.guaranteed_coins
1669            .iter()
1670            .flat_map(|gc| Vec::from(&gc.outputs).into_iter())
1671    }
1672
1673    pub fn fallible_outputs(
1674        &'_ self,
1675    ) -> impl Iterator<Item = Output<<P as ProofKind<D>>::LatestProof, D>> {
1676        self.fallible_coins
1677            .iter()
1678            .flat_map(|sp| Vec::from(&sp.1.outputs).into_iter())
1679    }
1680
1681    pub fn transients(
1682        &self,
1683    ) -> impl Iterator<Item = Transient<<P as ProofKind<D>>::LatestProof, D>> + use<'_, S, P, B, D>
1684    {
1685        self.guaranteed_transients()
1686            .chain(self.fallible_transients())
1687    }
1688
1689    pub fn guaranteed_transients(
1690        &self,
1691    ) -> impl Iterator<Item = Transient<<P as ProofKind<D>>::LatestProof, D>> + use<'_, S, P, B, D>
1692    {
1693        self.guaranteed_coins
1694            .iter()
1695            .flat_map(|offer| Vec::from(&offer.transient).into_iter())
1696    }
1697
1698    pub fn fallible_transients(
1699        &self,
1700    ) -> impl Iterator<Item = Transient<<P as ProofKind<D>>::LatestProof, D>> + use<'_, S, P, B, D>
1701    {
1702        self.fallible_coins
1703            .iter()
1704            .flat_map(|offer| Vec::from(&offer.1.transient).into_iter())
1705    }
1706
1707    pub fn segments(&self) -> Vec<u16> {
1708        let mut segments = once(0)
1709            .chain(self.intents.iter().map(|seg_intent| *seg_intent.0))
1710            .chain(self.fallible_coins.iter().map(|seg_offer| *seg_offer.0))
1711            .collect::<Vec<_>>();
1712        segments.sort();
1713        segments.dedup();
1714        segments
1715    }
1716}
1717
1718type ErasedClaimRewardsTransaction<D> = ClaimRewardsTransaction<(), D>;
1719
1720#[derive(Storable)]
1721#[derive_where(Clone, PartialEq, Eq; S)]
1722#[storable(db = D)]
1723#[tag = "claim-rewards-transaction[v1]"]
1724pub struct ClaimRewardsTransaction<S: SignatureKind<D>, D: DB> {
1725    pub network_id: String,
1726    pub value: u128,
1727    pub owner: VerifyingKey,
1728    pub nonce: Nonce,
1729    pub signature: S::Signature<ErasedClaimRewardsTransaction<D>>,
1730    pub kind: ClaimKind,
1731}
1732tag_enforcement_test!(ClaimRewardsTransaction<(), InMemoryDB>);
1733
1734impl<S: SignatureKind<D>, D: DB> ClaimRewardsTransaction<S, D> {
1735    pub fn add_signature(&self, signature: Signature) -> ClaimRewardsTransaction<Signature, D> {
1736        ClaimRewardsTransaction {
1737            network_id: self.network_id.clone(),
1738            value: self.value,
1739            owner: self.owner.clone(),
1740            nonce: self.nonce,
1741            signature,
1742            kind: self.kind,
1743        }
1744    }
1745
1746    pub fn erase_signatures(&self) -> ErasedClaimRewardsTransaction<D> {
1747        ClaimRewardsTransaction {
1748            network_id: self.network_id.clone(),
1749            value: self.value,
1750            owner: self.owner.clone(),
1751            nonce: self.nonce,
1752            signature: (),
1753            kind: self.kind,
1754        }
1755    }
1756}
1757
1758impl<D: DB> ClaimRewardsTransaction<(), D> {
1759    pub fn data_to_sign(&self) -> Vec<u8> {
1760        let mut data = Vec::new();
1761        data.extend(b"midnight:sig-claim_rewards_transaction:");
1762        Self::to_hash_data(self.clone(), data)
1763    }
1764
1765    pub fn to_hash_data(rewards: ClaimRewardsTransaction<(), D>, mut data: Vec<u8>) -> Vec<u8> {
1766        Serializable::serialize(&rewards.value, &mut data)
1767            .expect("In-memory serialization should succeed");
1768        Serializable::serialize(&rewards.owner, &mut data)
1769            .expect("In-memory serialization should succeed");
1770        Serializable::serialize(&rewards.nonce, &mut data)
1771            .expect("In-memory serialization should succeed");
1772        data
1773    }
1774}
1775
1776impl<S: SignatureKind<D>, D: DB> Debug for ClaimRewardsTransaction<S, D> {
1777    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
1778        write!(
1779            formatter,
1780            "<rewards of {} of Night for recipient {:?}>",
1781            self.value, self.owner
1782        )
1783    }
1784}
1785
1786pub(crate) const MIN_PROOF_SIZE: usize = DUST_SPEND_PROOF_SIZE;
1787pub(crate) const PROOF_SIZE: usize = zswap::INPUT_PROOF_SIZE;
1788// Retrieved from zswap key size. Unfortunately varies with circuits, this
1789// should be an upper bound.
1790pub(crate) const VERIFIER_KEY_SIZE: usize = 2875;
1791pub(crate) const EXPECTED_CONTRACT_DEPTH: usize = 32;
1792pub(crate) const EXPECTED_UTXO_DEPTH: usize = 32;
1793pub(crate) const EXPECTED_GENERATION_DEPTH: usize = 32;
1794pub(crate) const EXPECTED_OPERATIONS_DEPTH: usize = 8;
1795pub(crate) const EXPECTED_TOKEN_TYPE_DEPTH: usize = 8;
1796pub(crate) const EXPECTED_TIME_FILTER_MAP_DEPTH: usize = 8;
1797pub(crate) const FRESH_DUST_COMMITMENT_HASHES: usize = 6;
1798
1799impl<
1800    S: SignatureKind<D>,
1801    P: ProofKind<D>,
1802    B: Storable<D> + PedersenDowngradeable<D> + Serializable,
1803    D: DB,
1804> Transaction<S, P, B, D>
1805where
1806    Transaction<S, P, B, D>: Serializable,
1807{
1808    pub fn fees_with_margin(
1809        &self,
1810        params: &LedgerParameters,
1811        margin: usize,
1812    ) -> Result<u128, FeeCalculationError> {
1813        let synthetic = self.cost(params, false)?;
1814        let normalized = synthetic
1815            .normalize(params.limits.block_limits)
1816            .ok_or(FeeCalculationError::BlockLimitExceeded)?;
1817        let fees_fixed_point = params.fee_prices.overall_cost(&normalized);
1818        let margin_fees = fees_fixed_point * params.max_price_adjustment().powi(margin as i32);
1819        Ok(margin_fees.into_atomic_units(SPECKS_PER_DUST))
1820    }
1821
1822    pub fn fees(
1823        &self,
1824        params: &LedgerParameters,
1825        enforce_time_to_dismiss: bool,
1826    ) -> Result<u128, FeeCalculationError> {
1827        let synthetic = self.cost(params, enforce_time_to_dismiss)?;
1828        let normalized = synthetic
1829            .normalize(params.limits.block_limits)
1830            .ok_or(FeeCalculationError::BlockLimitExceeded)?;
1831        let fees_fixed_point = params.fee_prices.overall_cost(&normalized);
1832        Ok(fees_fixed_point.into_atomic_units(SPECKS_PER_DUST))
1833    }
1834
1835    pub fn validation_cost(&self, model: &TransactionCostModel) -> SyntheticCost {
1836        match self {
1837            Transaction::Standard(stx) => {
1838                let vk_reads = self
1839                    .calls()
1840                    .map(|(_, call)| (call.address, call.entry_point))
1841                    .collect::<BTreeSet<_>>()
1842                    .len();
1843                let mut cost = model.baseline_cost;
1844                cost += (model.cell_read(VERIFIER_KEY_SIZE as u64)
1845                    + model.map_index(EXPECTED_CONTRACT_DEPTH)
1846                    + model.map_index(EXPECTED_OPERATIONS_DEPTH))
1847                    * vk_reads;
1848                let offers = stx
1849                    .guaranteed_coins
1850                    .iter()
1851                    .map(|o| (**o).clone())
1852                    .chain(stx.fallible_coins.values())
1853                    .collect::<Vec<_>>();
1854                let zswap_inputs = offers
1855                    .iter()
1856                    .map(|offer| offer.inputs.len() + offer.transient.len())
1857                    .sum::<usize>();
1858                let zswap_outputs = offers
1859                    .iter()
1860                    .map(|offer| offer.outputs.len() + offer.transient.len())
1861                    .sum::<usize>();
1862                cost += model.proof_verify(zswap::INPUT_PIS) * zswap_inputs;
1863                cost += model.proof_verify(zswap::OUTPUT_PIS) * zswap_outputs;
1864                for intent in stx.intents.values() {
1865                    // Binding commitment check
1866                    cost.compute_time += model.runtime_cost_model.pedersen_valid;
1867                    // Unshielded offer validation
1868                    cost.compute_time += intent
1869                        .guaranteed_unshielded_offer
1870                        .iter()
1871                        .chain(intent.fallible_unshielded_offer.iter())
1872                        .map(|o| o.signatures.len())
1873                        .sum::<usize>()
1874                        * model.runtime_cost_model.signature_verify_constant;
1875                    for action in intent.actions.iter() {
1876                        match &*action {
1877                            ContractAction::Call(call) => {
1878                                cost.compute_time += model.runtime_cost_model.verifier_key_load;
1879                                cost += model
1880                                    .proof_verify(call.public_inputs(Default::default()).len());
1881                            }
1882                            ContractAction::Maintain(upd) => {
1883                                cost.compute_time +=
1884                                    model.runtime_cost_model.signature_verify_constant
1885                                        * upd.signatures.len();
1886                            }
1887                            _ => {}
1888                        }
1889                    }
1890                    if let Some(dust_actions) = intent.dust_actions {
1891                        cost += model.proof_verify(DUST_SPEND_PIS) * dust_actions.spends.len();
1892                        cost.compute_time += model.runtime_cost_model.signature_verify_constant
1893                            * dust_actions.registrations.len();
1894                    }
1895                }
1896                // Compute time for Pedersen check
1897                cost.compute_time += offers.iter().map(|o| o.deltas.len()).sum::<usize>()
1898                    * (model.runtime_cost_model.hash_to_curve + model.runtime_cost_model.ec_mul);
1899                cost.compute_time += model.runtime_cost_model.ec_mul;
1900                let mut res = SyntheticCost::from(cost);
1901                res.block_usage = self.est_size() as u64;
1902                res
1903            }
1904            Transaction::ClaimRewards(_) => (RunningCost {
1905                compute_time: model.runtime_cost_model.signature_verify_constant,
1906                ..RunningCost::ZERO
1907            } + model.baseline_cost)
1908                .into(),
1909        }
1910    }
1911
1912    fn est_size(&self) -> usize {
1913        P::estimated_tx_size(self)
1914    }
1915
1916    pub fn application_cost(&self, model: &TransactionCostModel) -> (SyntheticCost, SyntheticCost) {
1917        let mut g_cost = model.baseline_cost;
1918        let mut f_cost = RunningCost::ZERO;
1919        for (_, intent) in self.intents() {
1920            // Replay protection state update
1921            g_cost +=
1922                model.time_filter_map_lookup() + model.cell_read(PERSISTENT_HASH_BYTES as u64);
1923            g_cost += model.time_filter_map_insert(false)
1924                + model.cell_write(PERSISTENT_HASH_BYTES as u64, false);
1925            // utxo processing
1926            for (cost, offer) in [
1927                (&mut g_cost, intent.guaranteed_unshielded_offer.as_ref()),
1928                (&mut f_cost, intent.fallible_unshielded_offer.as_ref()),
1929            ]
1930            .into_iter()
1931            .filter_map(|(c, o)| o.map(|o| (c, o)))
1932            {
1933                let inputs = offer.inputs.len();
1934                let outputs = offer.outputs.len();
1935                // UTXO membership test
1936                *cost += model.map_index(EXPECTED_UTXO_DEPTH) * inputs;
1937                // UTXO removal
1938                *cost += (model.map_remove(EXPECTED_UTXO_DEPTH, true)
1939                    + model.cell_delete(UTXO_SIZE as u64))
1940                    * inputs;
1941                // UTXO insertion
1942                *cost += (model.map_insert(EXPECTED_UTXO_DEPTH, false)
1943                    + model.cell_write(UTXO_SIZE as u64, false))
1944                    * outputs;
1945                let night_inputs = offer.inputs.iter().filter(|i| i.type_ == NIGHT).count();
1946                let night_outputs = offer.outputs.iter().filter(|o| o.type_ == NIGHT).count();
1947                // Generating dtime update
1948                *cost += (model.merkle_tree_insert_unamortized(32, false)
1949                    + model.cell_write(DUST_GENERATION_INFO_SIZE as u64, false))
1950                    * night_inputs;
1951                // Night generates Dust address table read
1952                *cost += (model.map_index(EXPECTED_UTXO_DEPTH) + model.cell_read(FR_BYTES as u64))
1953                    * night_outputs;
1954                // Generation tree insertion & first-free update
1955                *cost += (model.cell_read(8) + model.cell_write(8, true)) * night_outputs;
1956                *cost += (model.merkle_tree_insert_amortized(32, false)
1957                    + model.cell_write(DUST_GENERATION_INFO_SIZE as u64, false))
1958                    * night_outputs;
1959                // Night indicies insertion
1960                *cost += (model.map_insert(EXPECTED_UTXO_DEPTH, false)
1961                    + model.cell_write(8, false))
1962                    * night_outputs;
1963                // Commitment merkle tree insertion & first-free update
1964                *cost += (model.cell_read(8) + model.cell_write(8, true)) * night_outputs;
1965                *cost += (model.merkle_tree_insert_amortized(EXPECTED_UTXO_DEPTH, false)
1966                    + model.cell_write(FR_BYTES as u64, false))
1967                    * night_outputs;
1968                // Commitment computation
1969                *cost += RunningCost::compute(
1970                    model.runtime_cost_model.transient_hash
1971                        * FRESH_DUST_COMMITMENT_HASHES
1972                        * night_outputs,
1973                );
1974            }
1975            let dust_spends = intent
1976                .dust_actions
1977                .as_ref()
1978                .map(|a| a.spends.len())
1979                .unwrap_or(0);
1980            // Nullifier membership test
1981            g_cost += model.map_index(32) * dust_spends;
1982            // Nullifier set insertion
1983            g_cost += (model.map_insert(EXPECTED_UTXO_DEPTH, false)
1984                + model.cell_write(FR_BYTES as u64, false))
1985                * dust_spends;
1986            // Commitment merkle tree insertion & first-free update
1987            g_cost += (model.cell_read(8) + model.cell_write(8, true)) * dust_spends;
1988            g_cost += (model.merkle_tree_insert_amortized(EXPECTED_UTXO_DEPTH, false)
1989                + model.cell_write(FR_BYTES as u64, false))
1990                * dust_spends;
1991            // Dust Merkle roots lookup
1992            g_cost += model.time_filter_map_lookup() * 2u64;
1993            for reg in intent
1994                .dust_actions
1995                .iter()
1996                .flat_map(|a| a.registrations.iter())
1997            {
1998                // Update the dust address registration table
1999                if reg.dust_address.is_some() {
2000                    g_cost += model.map_insert(EXPECTED_GENERATION_DEPTH, false)
2001                        + model.cell_write(FR_BYTES as u64, false);
2002                } else {
2003                    g_cost += model.map_remove(EXPECTED_GENERATION_DEPTH, true)
2004                        + model.cell_delete(FR_BYTES as u64);
2005                }
2006                // For each guaranteed night input with a matching address in
2007                // the intent, we read its ctime, and check it in the
2008                // generation indicies table.
2009                if reg.dust_address.is_some() {
2010                    let night_inputs = intent
2011                        .guaranteed_unshielded_offer
2012                        .iter()
2013                        .flat_map(|o| o.inputs.iter())
2014                        .filter(|i| i.owner == reg.night_key && i.type_ == NIGHT)
2015                        .count();
2016                    // Night indicies set check
2017                    g_cost +=
2018                        (model.map_index(EXPECTED_UTXO_DEPTH) + model.cell_read(8)) * night_inputs;
2019                    // Generation tree index
2020                    g_cost += (model.merkle_tree_index(EXPECTED_UTXO_DEPTH)
2021                        + model.cell_read(DUST_GENERATION_INFO_SIZE as u64))
2022                        * night_inputs;
2023                }
2024            }
2025            // Contract actions
2026            for action in intent.actions.iter() {
2027                match &*action {
2028                    ContractAction::Call(call) => {
2029                        let base_cost = if call.guaranteed_transcript.is_some() {
2030                            &mut g_cost
2031                        } else {
2032                            &mut f_cost
2033                        };
2034                        // Fetch / store state
2035                        *base_cost += model.map_index(EXPECTED_CONTRACT_DEPTH)
2036                            + model.map_insert(EXPECTED_CONTRACT_DEPTH, true)
2037                            + model.map_index(1)
2038                            + model.map_insert(1, true);
2039                        // Declared transcript costs
2040                        //
2041                        // NOTE: This is taken at face-value here. During
2042                        // execution, the declared cost (A `RunningCost`) is
2043                        // checked against the real cost at each operation, and
2044                        // aborted if it exceeds it (with the exception of
2045                        // `bytes_deleted`, which is checked after completion,
2046                        // and must be *at least* as declared).
2047                        for (cost, transcript) in [
2048                            (&mut g_cost, call.guaranteed_transcript.as_ref()),
2049                            (&mut f_cost, call.fallible_transcript.as_ref()),
2050                        ]
2051                        .into_iter()
2052                        .filter_map(|(c, t)| t.map(|t| (c, t)))
2053                        {
2054                            *cost += transcript.gas;
2055                            // VM stack setup / destroy cost
2056                            // Left out of scope here to avoid going to deep into
2057                            // stack structure.
2058                            *cost += model.stack_setup_cost(transcript);
2059                        }
2060                    }
2061                    ContractAction::Deploy(deploy) => {
2062                        // Contract exists check
2063                        f_cost += model.map_index(EXPECTED_CONTRACT_DEPTH);
2064                        // Contract insert
2065                        f_cost += model.map_insert(EXPECTED_CONTRACT_DEPTH, false)
2066                            + model.tree_copy(Sp::new(deploy.initial_state.clone()));
2067                    }
2068                    ContractAction::Maintain(upd) => {
2069                        // Contract state fetch
2070                        f_cost += model.map_index(EXPECTED_CONTRACT_DEPTH);
2071                        // Maintainance update counter update
2072                        f_cost += model.map_index(1) * 2u64
2073                            + model.map_insert(1, true) * 2u64
2074                            + model.cell_read(8)
2075                            + model.cell_write(8, true);
2076                        // Carrying out the updates
2077                        for part in upd.updates.iter() {
2078                            match &*part {
2079                                SingleUpdate::ReplaceAuthority(auth) => {
2080                                    f_cost += model.tree_copy::<_, D>(Sp::new(auth.clone()))
2081                                        + model.map_insert(1, true)
2082                                }
2083                                SingleUpdate::VerifierKeyRemove(..) => {
2084                                    f_cost += model.map_remove(EXPECTED_OPERATIONS_DEPTH, true)
2085                                        + model.cell_delete(VERIFIER_KEY_SIZE as u64)
2086                                        + model.map_insert(1, true)
2087                                }
2088                                SingleUpdate::VerifierKeyInsert(..) => {
2089                                    f_cost += model.map_insert(EXPECTED_OPERATIONS_DEPTH, false)
2090                                        + model.cell_write(VERIFIER_KEY_SIZE as u64, false)
2091                                        + model.map_insert(1, true)
2092                                }
2093                            }
2094                        }
2095                        // Inserting the new state
2096                        f_cost += model.map_insert(EXPECTED_CONTRACT_DEPTH, true);
2097                    }
2098                }
2099            }
2100        }
2101        match self {
2102            Transaction::Standard(stx) => {
2103                let offers = stx
2104                    .guaranteed_coins
2105                    .iter()
2106                    .map(|o| (0, (**o).clone()))
2107                    .chain(
2108                        stx.fallible_coins
2109                            .iter()
2110                            .map(|pair| (*pair.0, (*pair.1).clone())),
2111                    );
2112                for (segment, offer) in offers {
2113                    let mut offer_cost = RunningCost::ZERO;
2114                    let inputs = offer.inputs.len() + offer.transient.len();
2115                    let outputs = offer.outputs.len() + offer.transient.len();
2116                    // Roots set test
2117                    offer_cost += model.map_index(EXPECTED_TIME_FILTER_MAP_DEPTH) * inputs;
2118                    // Nullifier set test
2119                    offer_cost += model.map_index(EXPECTED_UTXO_DEPTH) * inputs;
2120                    // Nullifier set insertion
2121                    offer_cost += (model.map_insert(EXPECTED_UTXO_DEPTH, false)
2122                        + model.cell_write(PERSISTENT_HASH_BYTES as u64, false))
2123                        * inputs;
2124                    // Commitment set test
2125                    offer_cost += model.map_index(EXPECTED_UTXO_DEPTH) * outputs;
2126                    // Commitment set insertion
2127                    offer_cost += model.map_insert(EXPECTED_UTXO_DEPTH, false) * outputs;
2128                    // First free update
2129                    offer_cost += (model.cell_read(8) + model.cell_write(8, true)) * outputs;
2130                    // Merkle tree insertion
2131                    offer_cost +=
2132                        model.merkle_tree_insert_amortized(EXPECTED_UTXO_DEPTH, false) * outputs;
2133                    if segment == 0 {
2134                        g_cost += offer_cost;
2135                    } else {
2136                        f_cost += offer_cost;
2137                        // Because we also try to apply it in the guaranteed segment.
2138                        g_cost.compute_time += offer_cost.compute_time;
2139                    }
2140                }
2141            }
2142            Transaction::ClaimRewards(_) => {
2143                // Claim check
2144                g_cost += model.map_index(EXPECTED_UTXO_DEPTH);
2145                // Claim update
2146                g_cost += model.map_insert(EXPECTED_UTXO_DEPTH, true);
2147                // Replay protection update
2148                g_cost +=
2149                    model.time_filter_map_lookup() + model.cell_read(PERSISTENT_HASH_BYTES as u64);
2150                g_cost += model.time_filter_map_insert(false)
2151                    + model.cell_write(PERSISTENT_HASH_BYTES as u64, false);
2152                // Utxo update
2153                g_cost += model.map_insert(EXPECTED_UTXO_DEPTH, false);
2154            }
2155        }
2156        (g_cost.into(), (g_cost + f_cost).into())
2157    }
2158
2159    pub fn time_to_dismiss(&self, model: &TransactionCostModel) -> CostDuration {
2160        let mut validation_cost = self.validation_cost(model);
2161        validation_cost.compute_time = validation_cost.compute_time / model.parallelism_factor;
2162        let guaranteed_cost = self.application_cost(model).0;
2163        let cost_to_dismiss = guaranteed_cost + validation_cost;
2164        CostDuration::max(cost_to_dismiss.compute_time, cost_to_dismiss.read_time)
2165    }
2166
2167    pub fn cost(
2168        &self,
2169        params: &LedgerParameters,
2170        enforce_time_to_dismiss: bool,
2171    ) -> Result<SyntheticCost, FeeCalculationError> {
2172        let mut validation_cost = self.validation_cost(&params.cost_model);
2173        validation_cost.compute_time =
2174            validation_cost.compute_time / params.cost_model.parallelism_factor;
2175        let (guaranteed_cost, application_cost) = self.application_cost(&params.cost_model);
2176        let cost_to_dismiss = guaranteed_cost + validation_cost;
2177        let time_to_dismiss = CostDuration::max(
2178            params.limits.time_to_dismiss_per_byte * self.est_size() as u64,
2179            params.limits.min_time_to_dismiss,
2180        );
2181        if enforce_time_to_dismiss && cost_to_dismiss.max_time() > time_to_dismiss {
2182            return Err(FeeCalculationError::OutsideTimeToDismiss {
2183                time_to_dismiss: cost_to_dismiss.max_time(),
2184                allowed_time_to_dismiss: time_to_dismiss,
2185                size: self.est_size() as u64,
2186            });
2187        }
2188        Ok(validation_cost + application_cost)
2189    }
2190}
2191
2192impl<S: SignatureKind<D>, P: ProofKind<D>, B: Serializable + Tagged + Storable<D>, D: DB>
2193    Transaction<S, P, B, D>
2194{
2195    pub fn transaction_hash(&self) -> TransactionHash {
2196        let mut hasher = Sha256::new();
2197        tagged_serialize(self, &mut hasher).expect("In-memory serialization must succeed");
2198        TransactionHash(HashOutput(hasher.finalize().into()))
2199    }
2200}
2201
2202impl SystemTransaction {
2203    pub fn transaction_hash(&self) -> TransactionHash {
2204        let mut hasher = Sha256::new();
2205        tagged_serialize(self, &mut hasher).expect("In-memory serialization must succeed");
2206        TransactionHash(HashOutput(hasher.finalize().into()))
2207    }
2208
2209    pub fn cost(&self, params: &LedgerParameters) -> SyntheticCost {
2210        use SystemTransaction::*;
2211        let model = &params.cost_model;
2212        match self {
2213            OverwriteParameters(new_params) => params
2214                .cost_model
2215                .cell_write(new_params.serialized_size() as u64, true),
2216            DistributeNight(_, outputs) => {
2217                let mut cost = RunningCost::ZERO;
2218                // Replay protection state update
2219                cost +=
2220                    model.time_filter_map_lookup() + model.cell_read(PERSISTENT_HASH_BYTES as u64);
2221                cost += model.time_filter_map_insert(false)
2222                    + model.cell_write(PERSISTENT_HASH_BYTES as u64, false);
2223                // map insertion, either bridge_receiving or unclaimed_block_rewards
2224                cost += model.map_insert(EXPECTED_GENERATION_DEPTH, false);
2225                cost += model.cell_write(16, false);
2226                // Commitment merkle tree insertion & first-free update
2227                cost += model.cell_read(8) + model.cell_write(8, true);
2228                cost += model.merkle_tree_insert_amortized(EXPECTED_UTXO_DEPTH, false)
2229                    + model.cell_write(FR_BYTES as u64, false);
2230                // Commitment computation
2231                cost += RunningCost::compute(
2232                    model.runtime_cost_model.transient_hash * FRESH_DUST_COMMITMENT_HASHES,
2233                );
2234                // n offers
2235                cost * outputs.len()
2236            }
2237            PayBlockRewardsToTreasury { .. } => {
2238                let mut cost = RunningCost::ZERO;
2239                cost += model.cell_read(16) + model.cell_write(16, true);
2240                cost += model.map_index(EXPECTED_TOKEN_TYPE_DEPTH);
2241                cost += model.map_insert(EXPECTED_TOKEN_TYPE_DEPTH, true);
2242                cost
2243            }
2244            PayFromTreasuryShielded { outputs, .. } => {
2245                let mut cost = RunningCost::ZERO;
2246                // Zswap UTXO creation
2247                // commitment hash
2248                cost += RunningCost::compute(model.runtime_cost_model.transient_hash);
2249                // merkle tree insertion
2250                cost += model.merkle_tree_insert_amortized(ZSWAP_TREE_HEIGHT as usize, false);
2251                //set insertion
2252                cost += model.map_insert(EXPECTED_UTXO_DEPTH, false)
2253                    + model.cell_write(PERSISTENT_HASH_BYTES as u64, false);
2254                cost = cost * outputs.len();
2255                // treasury subtraction
2256                cost += model.cell_read(16) + model.cell_write(16, true);
2257                cost += model.map_index(EXPECTED_TOKEN_TYPE_DEPTH);
2258                cost += model.map_insert(EXPECTED_TOKEN_TYPE_DEPTH, true);
2259                cost
2260            }
2261            PayFromTreasuryUnshielded { outputs, .. } => {
2262                let mut cost = RunningCost::ZERO;
2263                // Replay protection state update
2264                cost +=
2265                    model.time_filter_map_lookup() + model.cell_read(PERSISTENT_HASH_BYTES as u64);
2266                cost += model.time_filter_map_insert(false)
2267                    + model.cell_write(PERSISTENT_HASH_BYTES as u64, false);
2268                // Unshielded UTXO creation
2269                cost += model.map_insert(EXPECTED_UTXO_DEPTH, false);
2270                cost += model.cell_write(UTXO_SIZE as u64, false);
2271                cost = cost * outputs.len();
2272                // treasury subtraction
2273                cost += model.cell_read(16) + model.cell_write(16, true);
2274                cost += model.map_index(EXPECTED_TOKEN_TYPE_DEPTH);
2275                cost += model.map_insert(EXPECTED_TOKEN_TYPE_DEPTH, true);
2276                // Commitment merkle tree insertion & first-free update
2277                cost += model.cell_read(8) + model.cell_write(8, true);
2278                cost += model.merkle_tree_insert_amortized(EXPECTED_UTXO_DEPTH, false)
2279                    + model.cell_write(FR_BYTES as u64, false);
2280                // Commitment computation
2281                cost += RunningCost::compute(
2282                    model.runtime_cost_model.transient_hash * FRESH_DUST_COMMITMENT_HASHES,
2283                );
2284                cost
2285            }
2286            DistributeReserve(..) => {
2287                // changing two pool balances
2288                let cost = model.cell_read(16) + model.cell_write(16, true);
2289                cost * 2u64
2290            }
2291            CNightGeneratesDustUpdate { events } => {
2292                let creates = events
2293                    .iter()
2294                    .filter(|e| e.action == CNightGeneratesDustActionType::Create)
2295                    .count();
2296                let destroys = events.len() - creates;
2297                let mut ccost = RunningCost::ZERO;
2298                // Night generates Dust address table read
2299                ccost += model.map_index(EXPECTED_UTXO_DEPTH) + model.cell_read(FR_BYTES as u64);
2300                // Generation tree insertion & first-free update
2301                ccost += model.cell_read(8) + model.cell_write(8, true);
2302                ccost += model.merkle_tree_insert_amortized(32, false)
2303                    + model.cell_write(DUST_GENERATION_INFO_SIZE as u64, false);
2304                // Night indicies insertion
2305                ccost += model.map_insert(EXPECTED_UTXO_DEPTH, false) + model.cell_write(8, false);
2306                // Commitment merkle tree insertion & first-free update
2307                ccost += model.cell_read(8) + model.cell_write(8, true);
2308                ccost += model.merkle_tree_insert_amortized(EXPECTED_UTXO_DEPTH, false)
2309                    + model.cell_write(FR_BYTES as u64, false);
2310                // Commitment computation
2311                ccost += RunningCost::compute(
2312                    model.runtime_cost_model.transient_hash * FRESH_DUST_COMMITMENT_HASHES,
2313                );
2314                let mut dcost = RunningCost::ZERO;
2315                // Dtime update
2316                dcost += model.map_index(EXPECTED_GENERATION_DEPTH);
2317                dcost += model.merkle_tree_index(EXPECTED_GENERATION_DEPTH);
2318                dcost += model.merkle_tree_insert_unamortized(32, true);
2319                ccost * creates + dcost * destroys
2320            }
2321        }
2322        .into()
2323    }
2324}
2325
2326#[derive(
2327    Debug,
2328    Default,
2329    Copy,
2330    Clone,
2331    PartialEq,
2332    Eq,
2333    PartialOrd,
2334    Ord,
2335    Hash,
2336    FieldRepr,
2337    FromFieldRepr,
2338    Serializable,
2339    Dummy,
2340    Storable,
2341)]
2342#[storable(base)]
2343#[tag = "transaction-hash"]
2344pub struct TransactionHash(pub HashOutput);
2345tag_enforcement_test!(TransactionHash);
2346
2347impl Serialize for TransactionHash {
2348    fn serialize<S: serde::ser::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
2349        let mut bytes = Vec::new();
2350        <Self as Serializable>::serialize(self, &mut bytes).map_err(serde::ser::Error::custom)?;
2351        ser.serialize_bytes(&bytes)
2352    }
2353}
2354
2355impl<'de> Deserialize<'de> for TransactionHash {
2356    fn deserialize<D: serde::de::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
2357        de.deserialize_bytes(BorshVisitor(PhantomData))
2358    }
2359}
2360
2361#[cfg(feature = "proptest")]
2362impl rand::distributions::Distribution<ContractCall<(), InMemoryDB>>
2363    for rand::distributions::Standard
2364{
2365    fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> ContractCall<(), InMemoryDB> {
2366        ContractCall {
2367            address: rng.r#gen(),
2368            entry_point: rng.r#gen(),
2369            guaranteed_transcript: None, // rng.r#gen(), TODO WG
2370            fallible_transcript: None,   // rng.r#gen(), TODO WG
2371            communication_commitment: rng.r#gen(),
2372            proof: (),
2373        }
2374    }
2375}
2376
2377#[derive(Storable)]
2378#[derive_where(Clone, PartialEq, Eq; P)]
2379#[storable(db = D)]
2380#[tag = "contract-call[v3]"]
2381pub struct ContractCall<P: ProofKind<D>, D: DB> {
2382    pub address: ContractAddress,
2383    pub entry_point: EntryPointBuf,
2384    // nb: Vector is *not* sorted
2385    pub guaranteed_transcript: Option<Sp<Transcript<D>, D>>,
2386    pub fallible_transcript: Option<Sp<Transcript<D>, D>>,
2387
2388    pub communication_commitment: Fr,
2389    pub proof: P::Proof,
2390}
2391tag_enforcement_test!(ContractCall<(), InMemoryDB>);
2392
2393impl<P: ProofKind<D>, D: DB> Debug for ContractCall<P, D> {
2394    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
2395        formatter
2396            .debug_map()
2397            .entry(&Symbol("contract"), &self.address)
2398            .entry(&Symbol("entry_point"), &self.entry_point)
2399            .entry(
2400                &Symbol("guaranteed_transcript"),
2401                &self.guaranteed_transcript,
2402            )
2403            .entry(&Symbol("fallible_transcript"), &self.fallible_transcript)
2404            .entry(&Symbol("communication_commitment"), &Symbol("<commitment>"))
2405            .entry(&Symbol("proof"), &Symbol("<proof>"))
2406            .finish()
2407    }
2408}
2409
2410impl<P: ProofKind<D>, D: DB> ContractCall<P, D> {
2411    pub fn context(
2412        self,
2413        block: &BlockContext,
2414        intent: &Intent<(), (), Pedersen, D>,
2415        state: ContractState<D>,
2416        com_indices: &Map<Commitment, u64>,
2417    ) -> CallContext<D> {
2418        let caller = intent
2419            .actions
2420            .iter_deref()
2421            .find_map(|action| match action {
2422                ContractAction::Call(caller) if caller.clone().calls(&self.erase_proof()) => {
2423                    Some(PublicAddress::Contract(caller.address))
2424                }
2425                _ => None,
2426            })
2427            .or_else(|| {
2428                let mut owners = intent
2429                    .guaranteed_unshielded_offer
2430                    .iter()
2431                    .chain(intent.fallible_unshielded_offer.iter())
2432                    .flat_map(|o| o.inputs.iter())
2433                    .map(|i| i.owner.clone());
2434                let owner = owners.next()?;
2435                if owners.all(|owner2| owner == owner2) {
2436                    Some(PublicAddress::User(UserAddress::from(owner)))
2437                } else {
2438                    None
2439                }
2440            });
2441        CallContext {
2442            own_address: self.address,
2443            tblock: block.tblock,
2444            tblock_err: block.tblock_err,
2445            parent_block_hash: block.parent_block_hash,
2446            caller,
2447            balance: state.balance,
2448            com_indices: com_indices.clone(),
2449            last_block_time: block.last_block_time,
2450        }
2451    }
2452
2453    pub fn calls(&self, callee: &ContractCall<P, D>) -> bool {
2454        self.calls_with_seq(callee).is_some()
2455    }
2456
2457    pub fn calls_with_seq(&self, callee: &ContractCall<P, D>) -> Option<(bool, u64)> {
2458        let calls: Vec<((u64, ContractAddress, HashOutput, Fr), bool)> = self
2459            .guaranteed_transcript
2460            .iter()
2461            .map(|x| (x, true))
2462            .chain(self.fallible_transcript.iter().map(|x| (x, false)))
2463            .flat_map(|(t, guaranteed)| {
2464                let ccs: HashSet<ClaimedContractCallsValue, D> =
2465                    t.deref().effects.claimed_contract_calls.clone();
2466                ccs.iter()
2467                    .map(|x| ((*x).deref().into_inner(), guaranteed))
2468                    .collect::<Vec<_>>()
2469                    .clone()
2470            })
2471            .collect();
2472        calls
2473            .into_iter()
2474            .find_map(|((seq, addr, ep, cc), guaranteed)| {
2475                (addr == callee.address
2476                    && ep == callee.entry_point.ep_hash()
2477                    && cc == callee.communication_commitment)
2478                    .then_some((guaranteed, seq))
2479            })
2480    }
2481
2482    pub fn erase_proof(&self) -> ContractCall<(), D> {
2483        ContractCall {
2484            address: self.address,
2485            entry_point: self.entry_point.clone(),
2486            guaranteed_transcript: self.guaranteed_transcript.clone(),
2487            fallible_transcript: self.fallible_transcript.clone(),
2488            communication_commitment: self.communication_commitment,
2489            proof: (),
2490        }
2491    }
2492}
2493
2494#[derive(Storable)]
2495#[derive_where(Clone, PartialEq, Eq)]
2496#[storable(db = D)]
2497#[tag = "contract-deploy[v4]"]
2498pub struct ContractDeploy<D: DB> {
2499    pub initial_state: ContractState<D>,
2500    pub nonce: HashOutput,
2501}
2502tag_enforcement_test!(ContractDeploy<InMemoryDB>);
2503
2504impl<D: DB> Debug for ContractDeploy<D> {
2505    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
2506        formatter.write_str("Deploy ")?;
2507        self.initial_state.fmt(formatter)
2508    }
2509}
2510
2511impl<D: DB> ContractDeploy<D> {
2512    pub fn address(&self) -> ContractAddress {
2513        let mut writer = Sha256::new();
2514        tagged_serialize(self, &mut writer).expect("In-memory serialization should succeed");
2515        ContractAddress(HashOutput(writer.finalize().into()))
2516    }
2517}
2518
2519#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2520#[non_exhaustive]
2521pub enum ContractOperationVersion {
2522    V3,
2523}
2524
2525impl Serializable for ContractOperationVersion {
2526    fn serialize(&self, writer: &mut impl Write) -> Result<(), std::io::Error> {
2527        use ContractOperationVersion as V;
2528        match self {
2529            V::V3 => Serializable::serialize(&2u8, writer),
2530        }
2531    }
2532
2533    fn serialized_size(&self) -> usize {
2534        use ContractOperationVersion as V;
2535        match self {
2536            V::V3 => 1,
2537        }
2538    }
2539}
2540
2541impl Tagged for ContractOperationVersion {
2542    fn tag() -> std::borrow::Cow<'static, str> {
2543        "contract-operation-version".into()
2544    }
2545    fn tag_unique_factor() -> String {
2546        "u8".into()
2547    }
2548}
2549tag_enforcement_test!(ContractOperationVersion);
2550
2551impl Deserializable for ContractOperationVersion {
2552    fn deserialize(reader: &mut impl Read, _recursion_depth: u32) -> Result<Self, std::io::Error> {
2553        use ContractOperationVersion as V;
2554        let mut disc = vec![0u8; 1];
2555        reader.read_exact(&mut disc)?;
2556        match disc[0] {
2557            0u8..=1u8 => Err(std::io::Error::new(
2558                std::io::ErrorKind::InvalidData,
2559                format!("Invalid old discriminant {}", disc[0]),
2560            )),
2561            2u8 => Ok(V::V3),
2562            _ => Err(std::io::Error::new(
2563                std::io::ErrorKind::InvalidData,
2564                format!("Unknown discriminant {}", disc[0]),
2565            )),
2566        }
2567    }
2568}
2569
2570impl ContractOperationVersion {
2571    pub(crate) fn has(&self, co: &ContractOperation) -> bool {
2572        use ContractOperationVersion as V;
2573        match self {
2574            V::V3 => co.v2.is_some(),
2575        }
2576    }
2577    pub(crate) fn rm_from(&self, co: &mut ContractOperation) {
2578        use ContractOperationVersion as V;
2579        match self {
2580            V::V3 => co.v2 = None,
2581        }
2582    }
2583}
2584
2585#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2586#[non_exhaustive]
2587pub enum ContractOperationVersionedVerifierKey {
2588    V3(transient_crypto::proofs::VerifierKey),
2589}
2590
2591impl ContractOperationVersionedVerifierKey {
2592    pub(crate) fn as_version(&self) -> ContractOperationVersion {
2593        use ContractOperationVersion as V;
2594        use ContractOperationVersionedVerifierKey as VK;
2595        match self {
2596            VK::V3(_) => V::V3,
2597        }
2598    }
2599
2600    pub(crate) fn insert_into(&self, co: &mut ContractOperation) {
2601        use ContractOperationVersionedVerifierKey as VK;
2602        match self {
2603            VK::V3(vk) => co.v2 = Some(vk.clone()),
2604        }
2605    }
2606}
2607
2608impl Serializable for ContractOperationVersionedVerifierKey {
2609    fn serialize(&self, writer: &mut impl Write) -> Result<(), std::io::Error> {
2610        use ContractOperationVersionedVerifierKey as VK;
2611        match self {
2612            VK::V3(vk) => {
2613                Serializable::serialize(&2u8, writer)?;
2614                Serializable::serialize(vk, writer)
2615            }
2616        }
2617    }
2618
2619    fn serialized_size(&self) -> usize {
2620        use ContractOperationVersionedVerifierKey as VK;
2621        match self {
2622            VK::V3(vk) => 1 + Serializable::serialized_size(vk),
2623        }
2624    }
2625}
2626
2627impl Tagged for ContractOperationVersionedVerifierKey {
2628    fn tag() -> std::borrow::Cow<'static, str> {
2629        "contract-operation-versioned-verifier-key".into()
2630    }
2631    fn tag_unique_factor() -> String {
2632        format!("[[],[],{}]", transient_crypto::proofs::VerifierKey::tag())
2633    }
2634}
2635tag_enforcement_test!(ContractOperationVersionedVerifierKey);
2636
2637impl Deserializable for ContractOperationVersionedVerifierKey {
2638    fn deserialize(reader: &mut impl Read, recursion_depth: u32) -> Result<Self, std::io::Error> {
2639        use ContractOperationVersionedVerifierKey as VK;
2640        let mut disc = vec![0u8; 1];
2641        reader.read_exact(&mut disc)?;
2642        match disc[0] {
2643            0u8..=1u8 => Err(std::io::Error::new(
2644                std::io::ErrorKind::InvalidData,
2645                format!("Invalid old discriminant {}", disc[0]),
2646            )),
2647            2u8 => Ok(VK::V3(Deserializable::deserialize(
2648                reader,
2649                recursion_depth,
2650            )?)),
2651            _ => Err(std::io::Error::new(
2652                std::io::ErrorKind::InvalidData,
2653                format!("Unknown discriminant {}", disc[0]),
2654            )),
2655        }
2656    }
2657}
2658
2659#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serializable, Storable)]
2660#[storable(base)]
2661#[tag = "maintenance-update-single-update[v1]"]
2662pub enum SingleUpdate {
2663    /// Replaces the authority for this contract.
2664    /// Any subsequent updates in this update sequence are still carried out.
2665    ReplaceAuthority(ContractMaintenanceAuthority),
2666    /// Removes a verifier key associated with a given version and entry point.
2667    VerifierKeyRemove(EntryPointBuf, ContractOperationVersion),
2668    /// Inserts a new verifier key under a given version and entry point.
2669    /// This operations *does not* replace existing keys, which must first be
2670    /// explicitly removed.
2671    VerifierKeyInsert(EntryPointBuf, ContractOperationVersionedVerifierKey),
2672}
2673tag_enforcement_test!(SingleUpdate);
2674
2675#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serializable, Storable, Debug)]
2676#[storable(base)]
2677#[tag = "maintenance-update-signatures-value[v1]"]
2678// This type exists solely to work nicely with storage. It's the tuple of `(index, signature)` for the elements of `MaintenanceUpdate::signatures`
2679pub struct SignaturesValue(pub u32, pub Signature);
2680tag_enforcement_test!(SignaturesValue);
2681
2682impl SignaturesValue {
2683    pub fn into_inner(&self) -> (u32, Signature) {
2684        (self.0, self.1.clone())
2685    }
2686}
2687
2688#[derive(Storable)]
2689#[derive_where(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2690#[tag = "contract-maintenance-update[v1]"]
2691#[storable(db = D)]
2692pub struct MaintenanceUpdate<D: DB> {
2693    pub address: ContractAddress,
2694    pub updates: storage::storage::Array<SingleUpdate, D>,
2695    pub counter: u32,
2696    pub signatures: storage::storage::Array<SignaturesValue, D>,
2697}
2698tag_enforcement_test!(MaintenanceUpdate<InMemoryDB>);
2699
2700impl<D: DB> Debug for MaintenanceUpdate<D> {
2701    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2702        f.debug_struct("MaintenanceUpdate")
2703            .field("address", &self.address)
2704            .field("updates", &self.updates)
2705            .field("counter", &self.counter)
2706            .field("signatures", &self.signatures)
2707            .finish()
2708    }
2709}
2710
2711impl<D: DB> MaintenanceUpdate<D> {
2712    pub fn data_to_sign(&self) -> Vec<u8> {
2713        let mut data = Vec::new();
2714        data.extend(b"midnight:contract-update:");
2715        Serializable::serialize(&self.address, &mut data)
2716            .expect("In-memory serialization should succeed");
2717        Serializable::serialize(&self.updates, &mut data)
2718            .expect("In-memory serialization should succeed");
2719        Serializable::serialize(&self.counter, &mut data)
2720            .expect("In-memory serialization should succeed");
2721        data
2722    }
2723}
2724
2725#[derive(Storable)]
2726#[storable(db = D)]
2727#[tag = "contract-action[v6]"]
2728#[derive_where(Clone, PartialEq, Eq; P)]
2729pub enum ContractAction<P: ProofKind<D>, D: DB> {
2730    Call(#[storable(child)] Sp<ContractCall<P, D>, D>),
2731    Deploy(Sp<ContractDeploy<D>, D>),
2732    Maintain(MaintenanceUpdate<D>),
2733}
2734tag_enforcement_test!(ContractAction<(), InMemoryDB>);
2735
2736impl<P: ProofKind<D>, D: DB> From<ContractCall<P, D>> for ContractAction<P, D> {
2737    fn from(call: ContractCall<P, D>) -> Self {
2738        ContractAction::Call(Sp::new(call))
2739    }
2740}
2741
2742impl<P: ProofKind<D>, D: DB> From<ContractDeploy<D>> for ContractAction<P, D> {
2743    fn from(deploy: ContractDeploy<D>) -> Self {
2744        ContractAction::Deploy(Sp::new(deploy))
2745    }
2746}
2747
2748impl<P: ProofKind<D>, D: DB> From<MaintenanceUpdate<D>> for ContractAction<P, D> {
2749    fn from(upd: MaintenanceUpdate<D>) -> Self {
2750        ContractAction::Maintain(upd)
2751    }
2752}
2753
2754impl<P: ProofKind<D>, D: DB> Debug for ContractAction<P, D> {
2755    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
2756        match self {
2757            ContractAction::Call(call) => call.fmt(formatter),
2758            ContractAction::Deploy(deploy) => deploy.fmt(formatter),
2759            ContractAction::Maintain(upd) => upd.fmt(formatter),
2760        }
2761    }
2762}
2763
2764impl<P: ProofKind<D>, D: DB> ContractAction<P, D> {
2765    pub fn erase_proof(&self) -> ContractAction<(), D> {
2766        match self {
2767            ContractAction::Call(call) => ContractAction::Call(Sp::new(call.erase_proof())),
2768            ContractAction::Deploy(deploy) => ContractAction::Deploy(deploy.clone()),
2769            ContractAction::Maintain(upd) => ContractAction::Maintain(upd.clone()),
2770        }
2771    }
2772
2773    pub fn erase_proofs(actions: Vec<&ContractAction<P, D>>) -> Vec<ContractAction<(), D>> {
2774        actions
2775            .into_iter()
2776            .map(ContractAction::erase_proof)
2777            .collect()
2778    }
2779
2780    pub fn challenge_pre_for(calls: &[ContractAction<P, D>]) -> Vec<u8> {
2781        let mut data = Vec::new();
2782        for cd in calls.iter() {
2783            match cd {
2784                ContractAction::Call(call) => {
2785                    data.push(0u8);
2786                    let _ = Serializable::serialize(&call.address, &mut data);
2787                    let _ = Serializable::serialize(&call.entry_point, &mut data);
2788
2789                    let _ = Serializable::serialize(&call.guaranteed_transcript, &mut data);
2790
2791                    let _ = Serializable::serialize(&call.fallible_transcript, &mut data);
2792                }
2793                ContractAction::Deploy(deploy) => {
2794                    data.push(1u8);
2795                    let _ = Serializable::serialize(&deploy, &mut data);
2796                }
2797                ContractAction::Maintain(upd) => {
2798                    data.push(2u8);
2799                    let _ = Serializable::serialize(&upd, &mut data);
2800                }
2801            }
2802        }
2803        data
2804    }
2805}
2806
2807#[derive(Copy, Clone, Debug, PartialEq, Eq, Serializable)]
2808#[tag = "transcation-id[v1]"]
2809pub enum TransactionIdentifier {
2810    Merged(Pedersen),
2811    Unique(HashOutput),
2812}
2813tag_enforcement_test!(TransactionIdentifier);
2814
2815impl Serialize for TransactionIdentifier {
2816    fn serialize<S: serde::ser::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
2817        let mut bytes = Vec::new();
2818        <Self as Serializable>::serialize(self, &mut bytes).map_err(serde::ser::Error::custom)?;
2819        ser.serialize_bytes(&bytes)
2820    }
2821}
2822
2823impl<'de> Deserialize<'de> for TransactionIdentifier {
2824    fn deserialize<D: serde::de::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
2825        de.deserialize_bytes(BorshVisitor(PhantomData))
2826    }
2827}
2828
2829#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serializable, Storable)]
2830#[tag = "unshielded-utxo[v1]"]
2831#[storable(base)]
2832pub struct Utxo {
2833    pub value: u128,
2834    pub owner: UserAddress,
2835    pub type_: UnshieldedTokenType,
2836    pub intent_hash: IntentHash,
2837    pub output_no: u32,
2838}
2839tag_enforcement_test!(Utxo);
2840
2841pub(crate) const UTXO_SIZE: usize = 16 + PERSISTENT_HASH_BYTES * 3 + 4;
2842
2843impl From<UtxoSpend> for Utxo {
2844    fn from(x: UtxoSpend) -> Self {
2845        Utxo {
2846            value: x.value,
2847            owner: UserAddress::from(x.owner),
2848            type_: x.type_,
2849            intent_hash: x.intent_hash,
2850            output_no: x.output_no,
2851        }
2852    }
2853}
2854
2855#[derive(Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Serializable, Storable)]
2856#[storable(base)]
2857#[tag = "unshielded-utxo-output[v1]"]
2858pub struct UtxoOutput {
2859    pub value: u128,
2860    pub owner: UserAddress,
2861    pub type_: UnshieldedTokenType,
2862}
2863tag_enforcement_test!(UtxoOutput);
2864
2865impl From<Utxo> for UtxoOutput {
2866    fn from(value: Utxo) -> Self {
2867        UtxoOutput {
2868            value: value.value,
2869            owner: value.owner,
2870            type_: value.type_,
2871        }
2872    }
2873}
2874
2875#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serializable, Storable)]
2876#[storable(base)]
2877#[tag = "unshielded-utxo-spend"]
2878pub struct UtxoSpend {
2879    pub value: u128,
2880    pub owner: VerifyingKey,
2881    pub type_: UnshieldedTokenType,
2882    pub intent_hash: IntentHash,
2883    pub output_no: u32,
2884}
2885tag_enforcement_test!(UtxoSpend);
2886
2887#[derive(PartialOrd, Ord, Debug, Clone, PartialEq, Eq, Hash, Serializable, Storable)]
2888#[storable(base)]
2889#[tag = "utxo-metadata[v1]"]
2890pub struct UtxoMeta {
2891    pub ctime: Timestamp,
2892}
2893tag_enforcement_test!(UtxoMeta);
2894
2895#[derive(Storable, PartialOrd, Ord)]
2896#[derive_where(Debug, Clone, PartialEq, Eq, Hash)]
2897#[storable(db = D)]
2898#[tag = "unshielded-utxo-state[v2]"]
2899#[must_use]
2900pub struct UtxoState<D: DB> {
2901    pub utxos: HashMap<Utxo, UtxoMeta, D, NightAnn>,
2902}
2903tag_enforcement_test!(UtxoState<InMemoryDB>);
2904
2905impl Annotation<Utxo> for NightAnn {
2906    fn from_value(utxo: &Utxo) -> Self {
2907        NightAnn {
2908            size: 1,
2909            value: if utxo.type_ == NIGHT { utxo.value } else { 0 },
2910        }
2911    }
2912}
2913
2914impl<D: DB> Annotation<(Sp<Utxo, D>, Sp<UtxoMeta, D>)> for NightAnn {
2915    fn from_value(tuple: &(Sp<Utxo, D>, Sp<UtxoMeta, D>)) -> Self {
2916        Self::from_value(tuple.0.deref())
2917    }
2918}
2919
2920impl<D: DB> Annotation<(Sp<Utxo, D>, Sp<(), D>)> for NightAnn {
2921    fn from_value(tuple: &(Sp<Utxo, D>, Sp<(), D>)) -> Self {
2922        Self::from_value(tuple.0.deref())
2923    }
2924}
2925
2926impl Annotation<u128> for NightAnn {
2927    fn from_value(value: &u128) -> Self {
2928        NightAnn {
2929            size: 1,
2930            value: *value,
2931        }
2932    }
2933}
2934
2935impl<D: DB> Annotation<ContractState<D>> for NightAnn {
2936    fn from_value(state: &ContractState<D>) -> Self {
2937        NightAnn {
2938            size: 1,
2939            value: state
2940                .balance
2941                .get(&TokenType::Unshielded(NIGHT))
2942                .map(|sp_value| *sp_value)
2943                .unwrap_or(0),
2944        }
2945    }
2946}
2947
2948impl<D: DB> UtxoState<D> {
2949    pub fn insert(&self, utxo: Utxo, meta: UtxoMeta) -> Self {
2950        UtxoState {
2951            utxos: self.utxos.insert(utxo, meta),
2952        }
2953    }
2954
2955    pub fn remove(&self, utxo: &Utxo) -> Self {
2956        UtxoState {
2957            utxos: self.utxos.remove(utxo),
2958        }
2959    }
2960}
2961
2962impl<D: DB> Default for UtxoState<D> {
2963    fn default() -> Self {
2964        Self {
2965            utxos: HashMap::new(),
2966        }
2967    }
2968}
2969
2970#[derive(Storable)]
2971#[derive_where(Clone, Debug, PartialEq, Eq)]
2972#[storable(db = D)]
2973#[tag = "ledger-state[v13]"]
2974#[must_use]
2975pub struct LedgerState<D: DB> {
2976    pub network_id: String,
2977    #[storable(child)]
2978    pub parameters: Sp<LedgerParameters, D>,
2979    pub locked_pool: u128,
2980    pub bridge_receiving: Map<UserAddress, u128, D>,
2981    pub reserve_pool: u128,
2982    pub block_reward_pool: u128,
2983    pub unclaimed_block_rewards: Map<UserAddress, u128, D, NightAnn>,
2984    pub treasury: Map<TokenType, u128, D>,
2985    #[storable(child)]
2986    pub zswap: Sp<zswap::ledger::State<D>, D>,
2987    pub contract: Map<ContractAddress, ContractState<D>, D, NightAnn>,
2988    #[storable(child)]
2989    pub utxo: Sp<UtxoState<D>, D>,
2990    pub replay_protection: Sp<ReplayProtectionState<D>, D>,
2991    #[storable(child)]
2992    pub dust: Sp<DustState<D>, D>,
2993}
2994tag_enforcement_test!(LedgerState<InMemoryDB>);
2995
2996/// The maximum rewardable supply of NIGHT atomic units. 24 billion NIGHT
2997/// with an atomic unit at 10^-6.
2998#[allow(clippy::inconsistent_digit_grouping)]
2999pub const MAX_SUPPLY: u128 = 24_000_000_000 * STARS_PER_NIGHT;
3000pub const STARS_PER_NIGHT: u128 = 1_000_000;
3001pub const SPECKS_PER_DUST: u128 = 1_000_000_000_000_000;
3002
3003impl<D: DB> LedgerState<D> {
3004    /// Constructs a new ledger state with default parameters.
3005    pub fn new(network_id: impl Into<String>) -> Self {
3006        Self::with_genesis_settings(network_id, INITIAL_PARAMETERS, 0, MAX_SUPPLY, 0)
3007            .expect("default state should be legal")
3008    }
3009
3010    /// Constructs a new ledger state with given genesis parameterisation.
3011    ///
3012    /// From the ledger's perspective, this includes the network ID, initial parameters, as well as
3013    /// three system-controlled pools:
3014    /// - The locked pool, which represents funds locked on Midnight due to being circulating as
3015    ///   cNIGHT on Cardano
3016    /// - The reserve pool, which represents funds set aside as a reward for rewarding protocol
3017    ///   participation
3018    /// - The treasury pool, which holds funds to be controlled by governance for strategic use (at
3019    ///   genesis, this should equal the Illiquid Circulating Supply, or ICS on Cardano, which
3020    ///   represents tokens locked on Cardano, which at genesis is *only* the treasury.
3021    pub fn with_genesis_settings(
3022        network_id: impl Into<String>,
3023        parameters: LedgerParameters,
3024        locked_pool: u128,
3025        reserve_pool: u128,
3026        treasury: u128,
3027    ) -> Result<Self, InvariantViolation> {
3028        let result = LedgerState {
3029            network_id: network_id.into(),
3030            parameters: Sp::new(parameters),
3031            locked_pool,
3032            bridge_receiving: Map::new(),
3033            reserve_pool,
3034            treasury: [(TokenType::Unshielded(NIGHT), treasury)]
3035                .into_iter()
3036                .collect(),
3037            block_reward_pool: 0,
3038            unclaimed_block_rewards: Map::new(),
3039            zswap: Sp::new(zswap::ledger::State::new()),
3040            contract: Map::new(),
3041            utxo: Sp::new(UtxoState::default()),
3042            replay_protection: Sp::new(ReplayProtectionState::default()),
3043            dust: Sp::new(DustState::default()),
3044        };
3045        result.check_night_balance_invariant()?;
3046        Ok(result)
3047    }
3048
3049    pub fn state_hash(&self) -> ArenaHash<D::Hasher> {
3050        Sp::new(self.clone()).hash()
3051    }
3052
3053    pub fn index(&self, address: ContractAddress) -> Option<ContractState<D>> {
3054        self.contract.get(&address).cloned()
3055    }
3056
3057    pub fn update_index(
3058        &self,
3059        address: ContractAddress,
3060        state: ChargedState<D>,
3061        balance: storage::storage::HashMap<TokenType, u128, D>,
3062    ) -> Self {
3063        let mut new_ledger_state = self.clone();
3064        let contract = new_ledger_state
3065            .contract
3066            .get(&address)
3067            .cloned()
3068            .unwrap_or_default();
3069        let new_contract = ContractState {
3070            data: state,
3071            balance,
3072            ..contract
3073        };
3074        new_ledger_state.contract = new_ledger_state.contract.insert(address, new_contract);
3075        new_ledger_state
3076    }
3077}
3078
3079pub(crate) struct Symbol(pub(crate) &'static str);
3080
3081impl Debug for Symbol {
3082    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
3083        formatter.write_str(self.0)
3084    }
3085}
3086
3087struct BorshVisitor<T>(PhantomData<T>);
3088
3089impl<T: Deserializable> serde::de::Visitor<'_> for BorshVisitor<T> {
3090    type Value = T;
3091    fn expecting(&self, fmt: &mut Formatter) -> fmt::Result {
3092        write!(fmt, "Borsh-serialized {}", std::any::type_name::<T>())
3093    }
3094    fn visit_bytes<E: serde::de::Error>(self, mut v: &[u8]) -> Result<Self::Value, E> {
3095        Deserializable::deserialize(&mut v, 0).map_err(serde::de::Error::custom)
3096    }
3097}
3098
3099pub const FEE_TOKEN: TokenType = TokenType::Dust;
3100
3101#[cfg(test)]
3102mod tests {
3103    use storage::db::InMemoryDB;
3104
3105    use super::*;
3106
3107    #[test]
3108    fn test_max_price_adjustment() {
3109        let adj = f64::from(INITIAL_PARAMETERS.max_price_adjustment());
3110        assert!(1.045 <= adj);
3111        assert!(adj <= 1.047);
3112    }
3113
3114    #[test]
3115    fn test_genesis_state() {
3116        assert_eq!(
3117            LedgerState::<InMemoryDB>::with_genesis_settings(
3118                "local-test",
3119                INITIAL_PARAMETERS,
3120                10_000_000_000_000_000,
3121                10_000_000_000_000_000,
3122                10_000_000_000_000_000,
3123            ),
3124            Err(InvariantViolation::NightBalance(30_000_000_000_000_000))
3125        );
3126        assert_eq!(
3127            LedgerState::<InMemoryDB>::with_genesis_settings(
3128                "local-test",
3129                INITIAL_PARAMETERS,
3130                1_000_000_000_000_000,
3131                1_000_000_000_000_000,
3132                1_000_000_000_000_000,
3133            ),
3134            Err(InvariantViolation::NightBalance(3_000_000_000_000_000))
3135        );
3136        assert!(
3137            LedgerState::<InMemoryDB>::with_genesis_settings(
3138                "local-test",
3139                INITIAL_PARAMETERS,
3140                10_000_000_000_000_000,
3141                10_000_000_000_000_000,
3142                4_000_000_000_000_000,
3143            )
3144            .is_ok()
3145        );
3146    }
3147
3148    #[test]
3149    fn test_state_serialized() {
3150        let state = LedgerState::<InMemoryDB>::new("local-test");
3151        let mut ser = Vec::new();
3152        serialize::tagged_serialize(&state, &mut ser).unwrap();
3153        let _ = serialize::tagged_deserialize::<LedgerState<InMemoryDB>>(&ser[..]).unwrap();
3154    }
3155}