concordium_base/
transactions.rs

1//! Definition of transactions and other transaction-like messages, together
2//! with their serialization, signing, and similar auxiliary methods.
3
4// Various old deprecated protocol features are still exposed in this module.
5// This also prevents deprecation warnings from deprecated enum variants that
6// trigger due to usage of the variants in derived implementations.
7#![allow(deprecated)]
8
9use crate::{
10    base::{
11        AccountThreshold, AggregateSigPairing, AmountFraction, BakerAggregationVerifyKey,
12        BakerElectionVerifyKey, BakerKeyPairs, BakerSignatureVerifyKey, ContractAddress,
13        CredentialRegistrationID, DelegationTarget, Energy, Nonce, OpenStatus, UrlText,
14    },
15    common::{
16        self,
17        cbor::{CborDecoder, CborDeserialize, CborEncoder, CborSerializationResult, CborSerialize},
18        types::{Amount, KeyIndex, KeyPair, Timestamp, TransactionSignature, TransactionTime, *},
19        Buffer, Deserial, Get, ParseResult, Put, ReadBytesExt, SerdeDeserialize, SerdeSerialize,
20        Serial, Serialize,
21    },
22    constants::*,
23    encrypted_transfers::types::{EncryptedAmountTransferData, SecToPubAmountTransferData},
24    hashes,
25    id::types::{
26        AccountAddress, AccountCredentialMessage, AccountKeys, CredentialDeploymentInfo,
27        CredentialPublicKeys, VerifyKey,
28    },
29    protocol_level_tokens::TokenOperationsPayload,
30    random_oracle::RandomOracle,
31    smart_contracts, updates,
32};
33use concordium_contracts_common as concordium_std;
34use concordium_std::SignatureThreshold;
35use derive_more::*;
36use rand::{CryptoRng, Rng};
37use sha2::Digest;
38use std::{collections::BTreeMap, marker::PhantomData};
39use thiserror::Error;
40
41#[derive(SerdeSerialize, SerdeDeserialize, Serial, Debug, Clone, Eq, PartialEq, AsRef, Into)]
42#[serde(transparent)]
43/// A data that was registered on the chain.
44pub struct Memo {
45    #[serde(with = "crate::internal::byte_array_hex")]
46    #[size_length = 2]
47    bytes: Vec<u8>,
48}
49
50impl CborSerialize for Memo {
51    fn serialize<C: CborEncoder>(&self, encoder: C) -> CborSerializationResult<()> {
52        encoder.encode_bytes(&self.bytes)
53    }
54}
55
56impl CborDeserialize for Memo {
57    fn deserialize<C: CborDecoder>(decoder: C) -> CborSerializationResult<Self>
58    where
59        Self: Sized,
60    {
61        let bytes = decoder.decode_bytes()?;
62
63        Ok(Self { bytes })
64    }
65}
66
67/// An error used to signal that an object was too big to be converted.
68#[derive(Display, Error, Debug)]
69pub struct TooBig;
70
71impl TryFrom<Vec<u8>> for Memo {
72    type Error = TooBig;
73
74    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
75        if value.len() <= crate::constants::MAX_MEMO_SIZE {
76            Ok(Self { bytes: value })
77        } else {
78            Err(TooBig)
79        }
80    }
81}
82
83impl Deserial for Memo {
84    fn deserial<R: crate::common::ReadBytesExt>(source: &mut R) -> ParseResult<Self> {
85        let len: u16 = source.get()?;
86        anyhow::ensure!(
87            usize::from(len) <= crate::constants::MAX_MEMO_SIZE,
88            "Memo too big.."
89        );
90        let bytes = crate::common::deserial_bytes(source, len.into())?;
91        Ok(Memo { bytes })
92    }
93}
94
95#[derive(SerdeSerialize, SerdeDeserialize, Debug, Clone, Copy, PartialEq, Eq, Hash, Display)]
96#[serde(rename_all = "camelCase")]
97// Since all variants are fieldless, the default JSON serialization will convert
98// all the variants to simple strings.
99/// Types of account transactions.
100pub enum TransactionType {
101    /// Deploy a Wasm module.
102    DeployModule,
103    /// Initialize a smart contract instance.
104    InitContract,
105    /// Update a smart contract instance.
106    Update,
107    /// Transfer CCD from an account to another.
108    Transfer,
109
110    /// Register an account as a baker.
111    #[deprecated(
112        since = "5.0.1",
113        note = "baking is changed in protocol 4, use ConfigureBaker or ConfigureDelegation instead"
114    )]
115    AddBaker,
116
117    /// Remove an account as a baker.
118    #[deprecated(
119        since = "5.0.1",
120        note = "baking is changed in protocol 4, use ConfigureBaker or ConfigureDelegation instead"
121    )]
122    RemoveBaker,
123
124    /// Update the staked amount.
125    #[deprecated(
126        since = "5.0.1",
127        note = "baking is changed in protocol 4, use ConfigureBaker or ConfigureDelegation instead"
128    )]
129    UpdateBakerStake,
130
131    /// Update whether the baker automatically restakes earnings.
132    #[deprecated(
133        since = "5.0.1",
134        note = "baking is changed in protocol 4, use ConfigureBaker or ConfigureDelegation instead"
135    )]
136    UpdateBakerRestakeEarnings,
137
138    /// Update baker keys
139    #[deprecated(
140        since = "5.0.1",
141        note = "baking is changed in protocol 4, use ConfigureBaker or ConfigureDelegation instead"
142    )]
143    UpdateBakerKeys,
144
145    /// Update given credential keys
146    UpdateCredentialKeys,
147
148    /// Transfer encrypted amount.
149    #[deprecated(
150        since = "5.0.1",
151        note = "encrypted transfers are deprecated and partially removed since protocol version 7"
152    )]
153    EncryptedAmountTransfer,
154
155    /// Transfer from public to encrypted balance of the same account.
156    #[deprecated(
157        since = "5.0.1",
158        note = "encrypted transfers are deprecated and partially removed since protocol version 7"
159    )]
160    TransferToEncrypted,
161
162    /// Transfer from encrypted to public balance of the same account.
163    TransferToPublic,
164    /// Transfer a CCD with a release schedule.
165    TransferWithSchedule,
166    /// Update the account's credentials.
167    UpdateCredentials,
168    /// Register some data on the chain.
169    RegisterData,
170    /// Same as transfer but with a memo field.
171    TransferWithMemo,
172
173    /// Same as encrypted transfer, but with a memo.
174    #[deprecated(
175        since = "5.0.1",
176        note = "encrypted transfers are deprecated and partially removed since protocol version 7"
177    )]
178    EncryptedAmountTransferWithMemo,
179
180    /// Same as transfer with schedule, but with an added memo.
181    TransferWithScheduleAndMemo,
182    ///  Configure an account's baker.
183    ConfigureBaker,
184    ///  Configure an account's stake delegation.
185    ConfigureDelegation,
186    /// Token update transaction. Introduced in Concordium protocol version 9.
187    TokenUpdate,
188}
189
190/// An error that occurs when trying to convert
191/// an invalid i32 tag to a [TransactionType].
192#[derive(Debug, Error)]
193#[error("{0} is not a valid TransactionType tag.")]
194pub struct TransactionTypeConversionError(pub i32);
195
196impl TryFrom<i32> for TransactionType {
197    type Error = TransactionTypeConversionError;
198
199    fn try_from(value: i32) -> Result<Self, Self::Error> {
200        Ok(match value {
201            0 => Self::DeployModule,
202            1 => Self::InitContract,
203            2 => Self::Update,
204            3 => Self::Transfer,
205            4 => Self::AddBaker,
206            5 => Self::RemoveBaker,
207            6 => Self::UpdateBakerStake,
208            7 => Self::UpdateBakerRestakeEarnings,
209            8 => Self::UpdateBakerKeys,
210            9 => Self::UpdateCredentialKeys,
211            10 => Self::EncryptedAmountTransfer,
212            11 => Self::TransferToEncrypted,
213            12 => Self::TransferToPublic,
214            13 => Self::TransferWithSchedule,
215            14 => Self::UpdateCredentials,
216            15 => Self::RegisterData,
217            16 => Self::TransferWithMemo,
218            17 => Self::EncryptedAmountTransferWithMemo,
219            18 => Self::TransferWithScheduleAndMemo,
220            19 => Self::ConfigureBaker,
221            20 => Self::ConfigureDelegation,
222            21 => Self::TokenUpdate,
223            n => return Err(TransactionTypeConversionError(n)),
224        })
225    }
226}
227
228#[derive(
229    Debug, Copy, Clone, Serial, SerdeSerialize, SerdeDeserialize, Into, From, Display, Eq, PartialEq,
230)]
231#[serde(transparent)]
232/// Type safe wrapper to record the size of the transaction payload.
233pub struct PayloadSize {
234    pub(crate) size: u32,
235}
236
237impl Deserial for PayloadSize {
238    fn deserial<R: ReadBytesExt>(source: &mut R) -> ParseResult<Self> {
239        let size: u32 = source.get()?;
240        anyhow::ensure!(
241            size <= MAX_PAYLOAD_SIZE,
242            "Size of the payload exceeds maximum allowed."
243        );
244        Ok(PayloadSize { size })
245    }
246}
247
248#[derive(Debug, Clone, Serialize, SerdeSerialize, SerdeDeserialize)]
249#[serde(rename_all = "camelCase")]
250/// Header of an account transaction that contains basic data to check whether
251/// the sender and the transaction is valid.
252pub struct TransactionHeader {
253    /// Sender account of the transaction.
254    pub sender: AccountAddress,
255    /// Sequence number of the transaction.
256    pub nonce: Nonce,
257    /// Maximum amount of energy the transaction can take to execute.
258    pub energy_amount: Energy,
259    /// Size of the transaction payload. This is used to deserialize the
260    /// payload.
261    pub payload_size: PayloadSize,
262    /// Latest time the transaction can be included in a block.
263    pub expiry: TransactionTime,
264}
265
266#[derive(Debug, Clone, SerdeSerialize, SerdeDeserialize, Into, AsRef)]
267#[serde(transparent)]
268/// An account transaction payload that has not yet been deserialized.
269/// This is a simple wrapper around [`Vec<u8>`](Vec) with bespoke serialization.
270pub struct EncodedPayload {
271    #[serde(with = "crate::internal::byte_array_hex")]
272    pub(crate) payload: Vec<u8>,
273}
274
275#[derive(Debug, Error)]
276#[error("The given byte array of size {actual}B exceeds maximum payload size {max}B")]
277pub struct ExceedsPayloadSize {
278    pub actual: usize,
279    pub max: u32,
280}
281
282impl TryFrom<Vec<u8>> for EncodedPayload {
283    type Error = ExceedsPayloadSize;
284
285    fn try_from(payload: Vec<u8>) -> Result<Self, Self::Error> {
286        let actual = payload.len();
287        if actual
288            .try_into()
289            .map_or(false, |x: u32| x <= MAX_PAYLOAD_SIZE)
290        {
291            Ok(Self { payload })
292        } else {
293            Err(ExceedsPayloadSize {
294                actual,
295                max: MAX_PAYLOAD_SIZE,
296            })
297        }
298    }
299}
300
301impl EncodedPayload {
302    /// Attempt to decode the [`EncodedPayload`] into a structured [`Payload`].
303    /// This also checks that all data is used, i.e., that there are no
304    /// remaining trailing bytes.
305    pub fn decode(&self) -> ParseResult<Payload> {
306        let mut source = std::io::Cursor::new(&self.payload);
307        let payload = source.get()?;
308        // ensure payload length matches the stated size.
309        let consumed = source.position();
310        anyhow::ensure!(
311            consumed == self.payload.len() as u64,
312            "Payload length information is inaccurate: {} bytes of input remaining.",
313            self.payload.len() as u64 - consumed
314        );
315        Ok(payload)
316    }
317}
318
319/// This serial instance does not have an inverse. It needs a context with the
320/// length.
321impl Serial for EncodedPayload {
322    fn serial<B: Buffer>(&self, out: &mut B) {
323        out.write_all(&self.payload)
324            .expect("Writing to buffer should succeed.");
325    }
326}
327
328/// Parse an encoded payload of specified length.
329pub fn get_encoded_payload<R: ReadBytesExt>(
330    source: &mut R,
331    len: PayloadSize,
332) -> ParseResult<EncodedPayload> {
333    // The use of deserial_bytes is safe here (no execessive allocations) because
334    // payload_size is limited
335    let payload = crate::common::deserial_bytes(source, u32::from(len) as usize)?;
336    Ok(EncodedPayload { payload })
337}
338
339/// A helper trait so that we can treat payload and encoded payload in the same
340/// place.
341pub trait PayloadLike {
342    /// Encode the transaction payload by serializing.
343    fn encode(&self) -> EncodedPayload;
344    /// Encode the payload directly to a buffer. This will in general be more
345    /// efficient than `encode`. However this will only matter if serialization
346    /// was to be done in a tight loop.
347    fn encode_to_buffer<B: Buffer>(&self, out: &mut B);
348}
349
350impl PayloadLike for EncodedPayload {
351    fn encode(&self) -> EncodedPayload {
352        self.clone()
353    }
354
355    fn encode_to_buffer<B: Buffer>(&self, out: &mut B) {
356        out.write_all(&self.payload)
357            .expect("Writing to buffer is always safe.");
358    }
359}
360
361#[derive(Debug, Clone, SerdeDeserialize, SerdeSerialize)]
362#[serde(rename_all = "camelCase")]
363/// An account transaction signed and paid for by a sender account.
364/// The payload type is a generic parameter to support two kinds of payloads,
365/// a fully deserialized [Payload] type, and an [EncodedPayload]. The latter is
366/// useful since deserialization of some types of payloads is expensive. It is
367/// thus useful to delay deserialization until after we have checked signatures
368/// and the sender account information.
369pub struct AccountTransaction<PayloadType> {
370    pub signature: TransactionSignature,
371    pub header: TransactionHeader,
372    pub payload: PayloadType,
373}
374
375impl<P: PayloadLike> Serial for AccountTransaction<P> {
376    fn serial<B: Buffer>(&self, out: &mut B) {
377        out.put(&self.signature);
378        out.put(&self.header);
379        self.payload.encode_to_buffer(out)
380    }
381}
382
383impl Deserial for AccountTransaction<EncodedPayload> {
384    fn deserial<R: ReadBytesExt>(source: &mut R) -> ParseResult<Self> {
385        let signature = source.get()?;
386        let header: TransactionHeader = source.get()?;
387        let payload = get_encoded_payload(source, header.payload_size)?;
388        Ok(AccountTransaction {
389            signature,
390            header,
391            payload,
392        })
393    }
394}
395
396impl Deserial for AccountTransaction<Payload> {
397    fn deserial<R: ReadBytesExt>(source: &mut R) -> ParseResult<Self> {
398        let signature = source.get()?;
399        let header: TransactionHeader = source.get()?;
400        let payload_len = u64::from(u32::from(header.payload_size));
401        let mut limited = <&mut R as std::io::Read>::take(source, payload_len);
402        let payload = limited.get()?;
403        // ensure payload length matches the stated size.
404        anyhow::ensure!(
405            limited.limit() == 0,
406            "Payload length information is inaccurate: {} bytes of input remaining.",
407            limited.limit()
408        );
409        Ok(AccountTransaction {
410            signature,
411            header,
412            payload,
413        })
414    }
415}
416
417impl<P: PayloadLike> AccountTransaction<P> {
418    /// Verify signature on the transaction given the public keys.
419    pub fn verify_transaction_signature(&self, keys: &impl HasAccountAccessStructure) -> bool {
420        let hash = compute_transaction_sign_hash(&self.header, &self.payload);
421        verify_signature_transaction_sign_hash(keys, &hash, &self.signature)
422    }
423}
424
425/// Marker for `BakerKeysPayload` indicating the proofs contained in
426/// `BakerKeysPayload` have been generated for an `AddBaker` transaction.
427#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
428pub enum AddBakerKeysMarker {}
429
430/// Marker for `BakerKeysPayload` indicating the proofs contained in
431/// `BakerKeysPayload` have been generated for an `UpdateBakerKeys` transaction.
432#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
433pub enum UpdateBakerKeysMarker {}
434
435/// Marker for `ConfigureBakerKeysPayload` indicating the proofs contained in
436/// `ConfigureBaker` have been generated for an `ConfigureBaker` transaction.
437#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
438pub enum ConfigureBakerKeysMarker {}
439
440#[derive(Debug, Clone, SerdeDeserialize, SerdeSerialize)]
441#[serde(rename_all = "camelCase")]
442/// Auxiliary type that contains public keys and proof of ownership of those
443/// keys. This is used in the `AddBaker` and `UpdateBakerKeys` transaction
444/// types.
445/// The proofs are either constructed for `AddBaker` or `UpdateBakerKeys` and
446/// the generic `V` is used as a marker to distinguish this in the type. See the
447/// markers: `AddBakerKeysMarker` and `UpdateBakerKeysMarker`.
448pub struct BakerKeysPayload<V> {
449    #[serde(skip)] // use default when deserializing
450    phantom: PhantomData<V>,
451    /// New public key for participating in the election lottery.
452    pub election_verify_key: BakerElectionVerifyKey,
453    /// New public key for verifying this baker's signatures.
454    pub signature_verify_key: BakerSignatureVerifyKey,
455    /// New public key for verifying this baker's signature on finalization
456    /// records.
457    pub aggregation_verify_key: BakerAggregationVerifyKey,
458    /// Proof of knowledge of the secret key corresponding to the signature
459    /// verification key.
460    pub proof_sig: crate::eddsa_ed25519::Ed25519DlogProof,
461    /// Proof of knowledge of the election secret key.
462    pub proof_election: crate::eddsa_ed25519::Ed25519DlogProof,
463    /// Proof of knowledge of the secret key for signing finalization
464    /// records.
465    pub proof_aggregation: crate::aggregate_sig::Proof<AggregateSigPairing>,
466}
467
468/// Baker keys payload containing proofs construct for a `AddBaker` transaction.
469pub type BakerAddKeysPayload = BakerKeysPayload<AddBakerKeysMarker>;
470/// Baker keys payload containing proofs construct for a `UpdateBakerKeys`
471/// transaction.
472pub type BakerUpdateKeysPayload = BakerKeysPayload<UpdateBakerKeysMarker>;
473
474/// Baker keys payload containing proofs construct for a `ConfigureBaker`
475/// transaction.
476pub type ConfigureBakerKeysPayload = BakerKeysPayload<ConfigureBakerKeysMarker>;
477
478impl<T> BakerKeysPayload<T> {
479    /// Construct a BakerKeysPayload taking a prefix for the challenge.
480    fn new_payload<R: Rng + CryptoRng>(
481        baker_keys: &BakerKeyPairs,
482        sender: AccountAddress,
483        challenge_prefix: &[u8],
484        csprng: &mut R,
485    ) -> Self {
486        let mut challenge = challenge_prefix.to_vec();
487
488        sender.serial(&mut challenge);
489        baker_keys.election_verify.serial(&mut challenge);
490        baker_keys.signature_verify.serial(&mut challenge);
491        baker_keys.aggregation_verify.serial(&mut challenge);
492
493        let proof_election = crate::eddsa_ed25519::prove_dlog_ed25519(
494            csprng,
495            &mut RandomOracle::domain(&challenge),
496            &baker_keys.election_verify.verify_key,
497            &baker_keys.election_sign.sign_key,
498        );
499        let proof_sig = crate::eddsa_ed25519::prove_dlog_ed25519(
500            csprng,
501            &mut RandomOracle::domain(&challenge),
502            &baker_keys.signature_verify.verify_key,
503            &baker_keys.signature_sign.sign_key,
504        );
505        let proof_aggregation = baker_keys
506            .aggregation_sign
507            .prove(csprng, &mut RandomOracle::domain(&challenge));
508
509        BakerKeysPayload {
510            phantom: PhantomData,
511            election_verify_key: baker_keys.election_verify.clone(),
512            signature_verify_key: baker_keys.signature_verify.clone(),
513            aggregation_verify_key: baker_keys.aggregation_verify.clone(),
514            proof_sig,
515            proof_election,
516            proof_aggregation,
517        }
518    }
519}
520
521impl BakerAddKeysPayload {
522    /// Construct a BakerKeysPayload with proofs for adding a baker.
523    pub fn new<T: Rng + CryptoRng>(
524        baker_keys: &BakerKeyPairs,
525        sender: AccountAddress,
526        csprng: &mut T,
527    ) -> Self {
528        BakerKeysPayload::new_payload(baker_keys, sender, b"addBaker", csprng)
529    }
530}
531
532impl BakerUpdateKeysPayload {
533    /// Construct a BakerKeysPayload with proofs for updating baker keys.
534    pub fn new<T: Rng + CryptoRng>(
535        baker_keys: &BakerKeyPairs,
536        sender: AccountAddress,
537        csprng: &mut T,
538    ) -> Self {
539        BakerKeysPayload::new_payload(baker_keys, sender, b"updateBakerKeys", csprng)
540    }
541}
542
543impl ConfigureBakerKeysPayload {
544    /// Construct a BakerKeysPayload with proofs for updating baker keys.
545    pub fn new<T: Rng + CryptoRng>(
546        baker_keys: &BakerKeyPairs,
547        sender: AccountAddress,
548        csprng: &mut T,
549    ) -> Self {
550        BakerKeysPayload::new_payload(baker_keys, sender, b"configureBaker", csprng)
551    }
552}
553
554#[derive(Debug, Clone, SerdeDeserialize, SerdeSerialize)]
555#[serde(rename_all = "camelCase")]
556/// Payload of the `AddBaker` transaction. This transaction registers the
557/// account as a baker.
558pub struct AddBakerPayload {
559    /// The keys with which the baker registered.
560    #[serde(flatten)]
561    pub keys: BakerAddKeysPayload,
562    /// Initial baking stake.
563    pub baking_stake: Amount,
564    /// Whether to add earnings to the stake automatically or not.
565    pub restake_earnings: bool,
566}
567
568#[derive(Debug, Clone, SerdeDeserialize, SerdeSerialize)]
569#[serde(rename_all = "camelCase")]
570/// Data needed to initialize a smart contract.
571pub struct InitContractPayload {
572    /// Deposit this amount of CCD.
573    pub amount: Amount,
574    /// Reference to the module from which to initialize the instance.
575    pub mod_ref: concordium_contracts_common::ModuleReference,
576    /// Name of the contract in the module.
577    pub init_name: smart_contracts::OwnedContractName,
578    /// Message to invoke the initialization method with.
579    pub param: smart_contracts::OwnedParameter,
580}
581
582impl InitContractPayload {
583    /// Get the size of the payload in number of bytes.
584    pub fn size(&self) -> usize {
585        8 + // Amount
586        32 + // Module reference
587        2 + // Init name size
588        self.init_name.as_contract_name().get_chain_name().len() +
589        2 + // Parameter size
590        self.param.as_ref().len()
591    }
592}
593
594#[derive(Debug, Clone, SerdeDeserialize, SerdeSerialize)]
595#[serde(rename_all = "camelCase")]
596/// Data needed to update a smart contract instance.
597pub struct UpdateContractPayload {
598    /// Send the given amount of CCD together with the message to the
599    /// contract instance.
600    pub amount: Amount,
601    /// Address of the contract instance to invoke.
602    pub address: ContractAddress,
603    /// Name of the method to invoke on the contract.
604    pub receive_name: smart_contracts::OwnedReceiveName,
605    /// Message to send to the contract instance.
606    pub message: smart_contracts::OwnedParameter,
607}
608
609impl UpdateContractPayload {
610    /// Get the size of the payload in number of bytes.
611    pub fn size(&self) -> usize {
612        8 + // Amount
613        16 + // Contract address
614        2 + // Receive name size
615        self.receive_name.as_receive_name().get_chain_name().len() +
616        2 + // Parameter size
617        self.message.as_ref().len()
618    }
619}
620
621#[derive(Debug, Clone, SerdeDeserialize, SerdeSerialize, Default)]
622#[serde(rename_all = "camelCase")]
623/// Payload for configuring a baker. The different constructors cover
624/// the different common cases.
625/// The [Default] implementation produces an empty configure that will have no
626/// effects.
627pub struct ConfigureBakerPayload {
628    /// The equity capital of the baker
629    pub capital: Option<Amount>,
630    /// Whether the baker's earnings are restaked
631    pub restake_earnings: Option<bool>,
632    /// Whether the pool is open for delegators
633    pub open_for_delegation: Option<OpenStatus>,
634    /// The key/proof pairs to verify the baker.
635    pub keys_with_proofs: Option<ConfigureBakerKeysPayload>,
636    /// The URL referencing the baker's metadata.
637    pub metadata_url: Option<UrlText>,
638    /// The commission the pool owner takes on transaction fees.
639    pub transaction_fee_commission: Option<AmountFraction>,
640    /// The commission the pool owner takes on baking rewards.
641    pub baking_reward_commission: Option<AmountFraction>,
642    /// The commission the pool owner takes on finalization rewards.
643    pub finalization_reward_commission: Option<AmountFraction>,
644    /// Whether the account should be suspended or not.
645    pub suspend: Option<bool>,
646}
647
648impl ConfigureBakerPayload {
649    pub fn new() -> Self {
650        Self::default()
651    }
652
653    /// Construct a new payload to remove a baker.
654    pub fn new_remove_baker() -> Self {
655        Self {
656            capital: Some(Amount::from_micro_ccd(0)),
657            ..Self::new()
658        }
659    }
660
661    /// Set the new baker capital.
662    pub fn set_capital(&mut self, amount: Amount) -> &mut Self {
663        self.capital = Some(amount);
664        self
665    }
666
667    /// Set whether or not earnings are automatically added to the stake.
668    pub fn set_restake_earnings(&mut self, restake_earnings: bool) -> &mut Self {
669        self.restake_earnings = Some(restake_earnings);
670        self
671    }
672
673    /// Update the delegation status of the pool.
674    pub fn set_open_for_delegation(&mut self, open_for_delegation: OpenStatus) -> &mut Self {
675        self.open_for_delegation = Some(open_for_delegation);
676        self
677    }
678
679    /// Add keys to the payload. This will construct proofs of validity and
680    /// insert the public keys into the payload.
681    pub fn add_keys<T: Rng + CryptoRng>(
682        &mut self,
683        baker_keys: &BakerKeyPairs,
684        sender: AccountAddress,
685        csprng: &mut T,
686    ) -> &mut Self {
687        let keys_with_proofs =
688            BakerKeysPayload::new_payload(baker_keys, sender, b"configureBaker", csprng);
689        self.keys_with_proofs = Some(keys_with_proofs);
690        self
691    }
692
693    /// Add metadata URL to the payload.
694    pub fn set_metadata_url(&mut self, metadata_url: UrlText) -> &mut Self {
695        self.metadata_url = Some(metadata_url);
696        self
697    }
698
699    /// Set a new transaction fee commission.
700    pub fn set_transaction_fee_commission(
701        &mut self,
702        transaction_fee_commission: AmountFraction,
703    ) -> &mut Self {
704        self.transaction_fee_commission = Some(transaction_fee_commission);
705        self
706    }
707
708    /// Set a new baking reward commission.
709    pub fn set_baking_reward_commission(
710        &mut self,
711        baking_reward_commission: AmountFraction,
712    ) -> &mut Self {
713        self.baking_reward_commission = Some(baking_reward_commission);
714        self
715    }
716
717    /// Set a new finalization reward commission.
718    pub fn set_finalization_reward_commission(
719        &mut self,
720        finalization_reward_commission: AmountFraction,
721    ) -> &mut Self {
722        self.finalization_reward_commission = Some(finalization_reward_commission);
723        self
724    }
725
726    pub fn set_suspend(&mut self, suspend: bool) -> &mut Self {
727        self.suspend = Some(suspend);
728        self
729    }
730}
731
732#[derive(Debug, Clone, SerdeDeserialize, SerdeSerialize, Default)]
733#[serde(rename_all = "camelCase")]
734/// Payload for configuring delegation. The [Default] implementation produces an
735/// empty configuration that will not change anything.
736pub struct ConfigureDelegationPayload {
737    /// The capital delegated to the pool.
738    pub capital: Option<Amount>,
739    /// Whether the delegator's earnings are restaked.
740    pub restake_earnings: Option<bool>,
741    /// The target of the delegation.
742    pub delegation_target: Option<DelegationTarget>,
743}
744
745impl ConfigureDelegationPayload {
746    /// Construct a new payload that has all the options unset.
747    pub fn new() -> Self {
748        Self::default()
749    }
750
751    /// Construct a new payload to remove a delegation.
752    pub fn new_remove_delegation() -> Self {
753        Self {
754            capital: Some(Amount::from_micro_ccd(0)),
755            ..Self::new()
756        }
757    }
758
759    pub fn set_capital(&mut self, amount: Amount) -> &mut Self {
760        self.capital = Some(amount);
761        self
762    }
763
764    pub fn set_restake_earnings(&mut self, restake_earnings: bool) -> &mut Self {
765        self.restake_earnings = Some(restake_earnings);
766        self
767    }
768
769    pub fn set_delegation_target(&mut self, target: DelegationTarget) -> &mut Self {
770        self.delegation_target = Some(target);
771        self
772    }
773}
774
775#[derive(SerdeSerialize, SerdeDeserialize, Serial, Debug, Clone, AsRef, Into, AsMut)]
776#[serde(transparent)]
777/// A data that was registered on the chain.
778pub struct RegisteredData {
779    #[serde(with = "crate::internal::byte_array_hex")]
780    #[size_length = 2]
781    bytes: Vec<u8>,
782}
783
784/// Registered data is too large.
785#[derive(Debug, Error, Copy, Clone)]
786#[error("Data is too large to be registered ({actual_size}).")]
787pub struct TooLargeError {
788    actual_size: usize,
789}
790
791impl TryFrom<Vec<u8>> for RegisteredData {
792    type Error = TooLargeError;
793
794    fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
795        let actual_size = bytes.len();
796        if actual_size <= crate::constants::MAX_REGISTERED_DATA_SIZE {
797            Ok(RegisteredData { bytes })
798        } else {
799            Err(TooLargeError { actual_size })
800        }
801    }
802}
803
804impl From<[u8; 32]> for RegisteredData {
805    fn from(data: [u8; 32]) -> Self {
806        Self {
807            bytes: data.to_vec(),
808        }
809    }
810}
811
812impl<M> From<crate::hashes::HashBytes<M>> for RegisteredData {
813    fn from(data: crate::hashes::HashBytes<M>) -> Self {
814        Self {
815            bytes: data.as_ref().to_vec(),
816        }
817    }
818}
819
820impl Deserial for RegisteredData {
821    fn deserial<R: crate::common::ReadBytesExt>(source: &mut R) -> ParseResult<Self> {
822        let len: u16 = source.get()?;
823        anyhow::ensure!(
824            usize::from(len) <= crate::constants::MAX_REGISTERED_DATA_SIZE,
825            "Data too big to register."
826        );
827        let bytes = crate::common::deserial_bytes(source, len.into())?;
828        Ok(RegisteredData { bytes })
829    }
830}
831
832/// Mapping of credential indices to account credentials with proofs.
833/// This structure is used when sending transactions that update credentials.
834pub type AccountCredentialsMap = BTreeMap<
835    CredentialIndex,
836    CredentialDeploymentInfo<
837        crate::id::constants::IpPairing,
838        crate::id::constants::ArCurve,
839        crate::id::constants::AttributeKind,
840    >,
841>;
842
843#[derive(Debug, Clone, SerdeDeserialize, SerdeSerialize)]
844#[serde(rename_all = "camelCase")]
845/// Payload of an account transaction.
846pub enum Payload {
847    /// Deploy a Wasm module with the given source.
848    DeployModule {
849        #[serde(rename = "mod")]
850        module: smart_contracts::WasmModule,
851    },
852    /// Initialize a new smart contract instance.
853    InitContract {
854        #[serde(flatten)]
855        payload: InitContractPayload,
856    },
857    /// Update a smart contract instance by invoking a specific function.
858    Update {
859        #[serde(flatten)]
860        payload: UpdateContractPayload,
861    },
862    /// Transfer CCD to an account.
863    Transfer {
864        /// Address to send to.
865        to_address: AccountAddress,
866        /// Amount to send.
867        amount: Amount,
868    },
869    /// Register the sender account as a baker.
870    AddBaker {
871        #[serde(flatten)]
872        payload: Box<AddBakerPayload>,
873    },
874    /// Deregister the account as a baker.
875    RemoveBaker,
876    /// Update baker's stake.
877    UpdateBakerStake {
878        /// The new stake.
879        stake: Amount,
880    },
881    /// Modify whether to add earnings to the baker stake automatically or not.
882    UpdateBakerRestakeEarnings {
883        /// New value of the flag.
884        restake_earnings: bool,
885    },
886    /// Update the baker's keys.
887    UpdateBakerKeys {
888        #[serde(flatten)]
889        payload: Box<BakerUpdateKeysPayload>,
890    },
891    /// Update signing keys of a specific credential.
892    UpdateCredentialKeys {
893        /// Id of the credential whose keys are to be updated.
894        cred_id: CredentialRegistrationID,
895        /// The new public keys.
896        keys: CredentialPublicKeys,
897    },
898    /// Transfer an encrypted amount.
899    #[deprecated(
900        since = "5.0.1",
901        note = "encrypted transfers are deprecated and partially removed since protocol version 7"
902    )]
903    EncryptedAmountTransfer {
904        /// The recepient's address.
905        to: AccountAddress,
906        /// The (encrypted) amount to transfer and proof of correctness of
907        /// accounting.
908        data: Box<EncryptedAmountTransferData<EncryptedAmountsCurve>>,
909    },
910    /// Transfer from public to encrypted balance of the sender account.
911    #[deprecated(
912        since = "5.0.1",
913        note = "encrypted transfers are deprecated and partially removed since protocol version 7"
914    )]
915    TransferToEncrypted {
916        /// The amount to transfer.
917        amount: Amount,
918    },
919    /// Transfer an amount from encrypted to the public balance of the account.
920    TransferToPublic {
921        /// The amount to transfer and proof of correctness of accounting.
922        #[serde(flatten)]
923        data: Box<SecToPubAmountTransferData<EncryptedAmountsCurve>>,
924    },
925    /// Transfer an amount with schedule.
926    TransferWithSchedule {
927        /// The recepient.
928        to: AccountAddress,
929        /// The release schedule. This can be at most 255 elements.
930        schedule: Vec<(Timestamp, Amount)>,
931    },
932    /// Update the account's credentials.
933    UpdateCredentials {
934        /// New credentials to add.
935        new_cred_infos: AccountCredentialsMap,
936        /// Ids of credentials to remove.
937        remove_cred_ids: Vec<CredentialRegistrationID>,
938        /// The new account threshold.
939        new_threshold: AccountThreshold,
940    },
941    /// Register the given data on the chain.
942    RegisterData {
943        /// The data to register.
944        data: RegisteredData,
945    },
946    /// Transfer CCD to an account with an additional memo.
947    TransferWithMemo {
948        /// Address to send to.
949        to_address: AccountAddress,
950        /// Memo to include in the transfer.
951        memo: Memo,
952        /// Amount to send.
953        amount: Amount,
954    },
955    /// Transfer an encrypted amount.
956    #[deprecated(
957        since = "5.0.1",
958        note = "encrypted transfers are deprecated and partially removed since protocol version 7"
959    )]
960    EncryptedAmountTransferWithMemo {
961        /// The recepient's address.
962        to: AccountAddress,
963        /// Memo to include in the transfer.
964        memo: Memo,
965        /// The (encrypted) amount to transfer and proof of correctness of
966        /// accounting.
967        data: Box<EncryptedAmountTransferData<EncryptedAmountsCurve>>,
968    },
969    /// Transfer an amount with schedule.
970    TransferWithScheduleAndMemo {
971        /// The recepient.
972        to: AccountAddress,
973        /// Memo to include in the transfer.
974        memo: Memo,
975        /// The release schedule. This can be at most 255 elements.
976        schedule: Vec<(Timestamp, Amount)>,
977    },
978    /// Configure a baker on an account.
979    ConfigureBaker {
980        #[serde(flatten)]
981        data: Box<ConfigureBakerPayload>,
982    },
983    ///  Configure an account's stake delegation.
984    ConfigureDelegation {
985        #[serde(flatten)]
986        data: ConfigureDelegationPayload,
987    },
988    /// Token update operations
989    TokenUpdate {
990        #[serde(flatten)]
991        payload: TokenOperationsPayload,
992    },
993}
994
995impl Payload {
996    /// Resolve the [TransactionType] corresponding to the variant of the
997    /// Payload.
998    pub fn transaction_type(&self) -> TransactionType {
999        match self {
1000            Payload::DeployModule { .. } => TransactionType::DeployModule,
1001            Payload::InitContract { .. } => TransactionType::InitContract,
1002            Payload::Update { .. } => TransactionType::Update,
1003            Payload::Transfer { .. } => TransactionType::Transfer,
1004            Payload::AddBaker { .. } => TransactionType::AddBaker,
1005            Payload::RemoveBaker { .. } => TransactionType::RemoveBaker,
1006            Payload::UpdateBakerStake { .. } => TransactionType::UpdateBakerStake,
1007            Payload::UpdateBakerRestakeEarnings { .. } => {
1008                TransactionType::UpdateBakerRestakeEarnings
1009            }
1010            Payload::UpdateBakerKeys { .. } => TransactionType::UpdateBakerKeys,
1011            Payload::UpdateCredentialKeys { .. } => TransactionType::UpdateCredentialKeys,
1012            Payload::EncryptedAmountTransfer { .. } => TransactionType::EncryptedAmountTransfer,
1013            Payload::TransferToEncrypted { .. } => TransactionType::TransferToEncrypted,
1014            Payload::TransferToPublic { .. } => TransactionType::TransferToPublic,
1015            Payload::TransferWithSchedule { .. } => TransactionType::TransferWithSchedule,
1016            Payload::UpdateCredentials { .. } => TransactionType::UpdateCredentials,
1017            Payload::RegisterData { .. } => TransactionType::RegisterData,
1018            Payload::TransferWithMemo { .. } => TransactionType::TransferWithMemo,
1019            Payload::EncryptedAmountTransferWithMemo { .. } => {
1020                TransactionType::EncryptedAmountTransferWithMemo
1021            }
1022            Payload::TransferWithScheduleAndMemo { .. } => {
1023                TransactionType::TransferWithScheduleAndMemo
1024            }
1025            Payload::ConfigureBaker { .. } => TransactionType::ConfigureBaker,
1026            Payload::ConfigureDelegation { .. } => TransactionType::ConfigureDelegation,
1027            Payload::TokenUpdate { .. } => TransactionType::TokenUpdate,
1028        }
1029    }
1030}
1031
1032impl Serial for Payload {
1033    fn serial<B: Buffer>(&self, out: &mut B) {
1034        match &self {
1035            Payload::DeployModule { module } => {
1036                out.put(&0u8);
1037                out.put(module);
1038            }
1039            Payload::InitContract { payload } => {
1040                out.put(&1u8);
1041                out.put(payload)
1042            }
1043            Payload::Update { payload } => {
1044                out.put(&2u8);
1045                out.put(payload)
1046            }
1047            Payload::Transfer { to_address, amount } => {
1048                out.put(&3u8);
1049                out.put(to_address);
1050                out.put(amount);
1051            }
1052            Payload::AddBaker { payload } => {
1053                out.put(&4u8);
1054                out.put(payload);
1055            }
1056            Payload::RemoveBaker => {
1057                out.put(&5u8);
1058            }
1059            Payload::UpdateBakerStake { stake } => {
1060                out.put(&6u8);
1061                out.put(stake);
1062            }
1063            Payload::UpdateBakerRestakeEarnings { restake_earnings } => {
1064                out.put(&7u8);
1065                out.put(restake_earnings);
1066            }
1067            Payload::UpdateBakerKeys { payload } => {
1068                out.put(&8u8);
1069                out.put(payload)
1070            }
1071            Payload::UpdateCredentialKeys { cred_id, keys } => {
1072                out.put(&13u8);
1073                out.put(cred_id);
1074                out.put(keys);
1075            }
1076            Payload::EncryptedAmountTransfer { to, data } => {
1077                out.put(&16u8);
1078                out.put(to);
1079                out.put(data);
1080            }
1081            Payload::TransferToEncrypted { amount } => {
1082                out.put(&17u8);
1083                out.put(amount);
1084            }
1085            Payload::TransferToPublic { data } => {
1086                out.put(&18u8);
1087                out.put(data);
1088            }
1089            Payload::TransferWithSchedule { to, schedule } => {
1090                out.put(&19u8);
1091                out.put(to);
1092                out.put(&(schedule.len() as u8));
1093                crate::common::serial_vector_no_length(schedule, out);
1094            }
1095            Payload::UpdateCredentials {
1096                new_cred_infos,
1097                remove_cred_ids,
1098                new_threshold,
1099            } => {
1100                out.put(&20u8);
1101                out.put(&(new_cred_infos.len() as u8));
1102                crate::common::serial_map_no_length(new_cred_infos, out);
1103                out.put(&(remove_cred_ids.len() as u8));
1104                crate::common::serial_vector_no_length(remove_cred_ids, out);
1105                out.put(new_threshold);
1106            }
1107            Payload::RegisterData { data } => {
1108                out.put(&21u8);
1109                out.put(data);
1110            }
1111            Payload::TransferWithMemo {
1112                to_address,
1113                memo,
1114                amount,
1115            } => {
1116                out.put(&22u8);
1117                out.put(to_address);
1118                out.put(memo);
1119                out.put(amount);
1120            }
1121            Payload::EncryptedAmountTransferWithMemo { to, memo, data } => {
1122                out.put(&23u8);
1123                out.put(to);
1124                out.put(memo);
1125                out.put(data);
1126            }
1127            Payload::TransferWithScheduleAndMemo { to, memo, schedule } => {
1128                out.put(&24u8);
1129                out.put(to);
1130                out.put(memo);
1131                out.put(&(schedule.len() as u8));
1132                crate::common::serial_vector_no_length(schedule, out);
1133            }
1134            Payload::ConfigureBaker { data } => {
1135                out.put(&25u8);
1136                let set_if = |n, b| if b { 1u16 << n } else { 0 };
1137                let bitmap: u16 = set_if(0, data.capital.is_some())
1138                    | set_if(1, data.restake_earnings.is_some())
1139                    | set_if(2, data.open_for_delegation.is_some())
1140                    | set_if(3, data.keys_with_proofs.is_some())
1141                    | set_if(4, data.metadata_url.is_some())
1142                    | set_if(5, data.transaction_fee_commission.is_some())
1143                    | set_if(6, data.baking_reward_commission.is_some())
1144                    | set_if(7, data.finalization_reward_commission.is_some())
1145                    | set_if(8, data.suspend.is_some());
1146                out.put(&bitmap);
1147                if let Some(capital) = &data.capital {
1148                    out.put(capital);
1149                }
1150                if let Some(restake_earnings) = &data.restake_earnings {
1151                    out.put(restake_earnings);
1152                }
1153                if let Some(open_for_delegation) = &data.open_for_delegation {
1154                    out.put(open_for_delegation);
1155                }
1156                if let Some(keys_with_proofs) = &data.keys_with_proofs {
1157                    // this is serialized manually since the serialization in Haskell is not
1158                    // consistent with the serialization of baker add
1159                    // transactions. The order of fields is different.
1160                    out.put(&keys_with_proofs.election_verify_key);
1161                    out.put(&keys_with_proofs.proof_election);
1162                    out.put(&keys_with_proofs.signature_verify_key);
1163                    out.put(&keys_with_proofs.proof_sig);
1164                    out.put(&keys_with_proofs.aggregation_verify_key);
1165                    out.put(&keys_with_proofs.proof_aggregation);
1166                }
1167                if let Some(metadata_url) = &data.metadata_url {
1168                    out.put(metadata_url);
1169                }
1170                if let Some(transaction_fee_commission) = &data.transaction_fee_commission {
1171                    out.put(transaction_fee_commission);
1172                }
1173                if let Some(baking_reward_commission) = &data.baking_reward_commission {
1174                    out.put(baking_reward_commission);
1175                }
1176                if let Some(finalization_reward_commission) = &data.finalization_reward_commission {
1177                    out.put(finalization_reward_commission);
1178                }
1179                if let Some(suspend) = &data.suspend {
1180                    out.put(suspend);
1181                }
1182            }
1183            Payload::ConfigureDelegation {
1184                data:
1185                    ConfigureDelegationPayload {
1186                        capital,
1187                        restake_earnings,
1188                        delegation_target,
1189                    },
1190            } => {
1191                out.put(&26u8);
1192                let set_if = |n, b| if b { 1u16 << n } else { 0 };
1193                let bitmap: u16 = set_if(0, capital.is_some())
1194                    | set_if(1, restake_earnings.is_some())
1195                    | set_if(2, delegation_target.is_some());
1196                out.put(&bitmap);
1197                if let Some(capital) = capital {
1198                    out.put(capital);
1199                }
1200                if let Some(restake_earnings) = restake_earnings {
1201                    out.put(restake_earnings);
1202                }
1203                if let Some(delegation_target) = delegation_target {
1204                    out.put(delegation_target);
1205                }
1206            }
1207            Payload::TokenUpdate { payload } => {
1208                out.put(&27u8);
1209                out.put(&payload.token_id);
1210                out.put(&payload.operations);
1211            }
1212        }
1213    }
1214}
1215
1216impl Deserial for Payload {
1217    fn deserial<R: ReadBytesExt>(source: &mut R) -> ParseResult<Self> {
1218        let tag: u8 = source.get()?;
1219        match tag {
1220            0 => {
1221                let module = source.get()?;
1222                Ok(Payload::DeployModule { module })
1223            }
1224            1 => {
1225                let payload = source.get()?;
1226                Ok(Payload::InitContract { payload })
1227            }
1228            2 => {
1229                let payload = source.get()?;
1230                Ok(Payload::Update { payload })
1231            }
1232            3 => {
1233                let to_address = source.get()?;
1234                let amount = source.get()?;
1235                Ok(Payload::Transfer { to_address, amount })
1236            }
1237            4 => {
1238                let payload_data = source.get()?;
1239                Ok(Payload::AddBaker {
1240                    payload: Box::new(payload_data),
1241                })
1242            }
1243            5 => Ok(Payload::RemoveBaker),
1244            6 => {
1245                let stake = source.get()?;
1246                Ok(Payload::UpdateBakerStake { stake })
1247            }
1248            7 => {
1249                let restake_earnings = source.get()?;
1250                Ok(Payload::UpdateBakerRestakeEarnings { restake_earnings })
1251            }
1252            8 => {
1253                let payload_data = source.get()?;
1254                Ok(Payload::UpdateBakerKeys {
1255                    payload: Box::new(payload_data),
1256                })
1257            }
1258            13 => {
1259                let cred_id = source.get()?;
1260                let keys = source.get()?;
1261                Ok(Payload::UpdateCredentialKeys { cred_id, keys })
1262            }
1263            16 => {
1264                let to = source.get()?;
1265                let data = source.get()?;
1266                Ok(Payload::EncryptedAmountTransfer { to, data })
1267            }
1268            17 => {
1269                let amount = source.get()?;
1270                Ok(Payload::TransferToEncrypted { amount })
1271            }
1272            18 => {
1273                let data_data = source.get()?;
1274                Ok(Payload::TransferToPublic {
1275                    data: Box::new(data_data),
1276                })
1277            }
1278            19 => {
1279                let to = source.get()?;
1280                let len: u8 = source.get()?;
1281                let schedule = crate::common::deserial_vector_no_length(source, len.into())?;
1282                Ok(Payload::TransferWithSchedule { to, schedule })
1283            }
1284            20 => {
1285                let cred_infos_len: u8 = source.get()?;
1286                let new_cred_infos =
1287                    crate::common::deserial_map_no_length(source, cred_infos_len.into())?;
1288                let remove_cred_ids_len: u8 = source.get()?;
1289                let remove_cred_ids =
1290                    crate::common::deserial_vector_no_length(source, remove_cred_ids_len.into())?;
1291                let new_threshold = source.get()?;
1292                Ok(Payload::UpdateCredentials {
1293                    new_cred_infos,
1294                    remove_cred_ids,
1295                    new_threshold,
1296                })
1297            }
1298            21 => {
1299                let data = source.get()?;
1300                Ok(Payload::RegisterData { data })
1301            }
1302            22 => {
1303                let to_address = source.get()?;
1304                let memo = source.get()?;
1305                let amount = source.get()?;
1306                Ok(Payload::TransferWithMemo {
1307                    to_address,
1308                    memo,
1309                    amount,
1310                })
1311            }
1312            23 => {
1313                let to = source.get()?;
1314                let memo = source.get()?;
1315                let data = source.get()?;
1316                Ok(Payload::EncryptedAmountTransferWithMemo { to, memo, data })
1317            }
1318            24 => {
1319                let to = source.get()?;
1320                let memo = source.get()?;
1321                let len: u8 = source.get()?;
1322                let schedule = crate::common::deserial_vector_no_length(source, len.into())?;
1323                Ok(Payload::TransferWithScheduleAndMemo { to, memo, schedule })
1324            }
1325            25 => {
1326                let bitmap: u16 = source.get()?;
1327                let mut capital = None;
1328                let mut restake_earnings = None;
1329                let mut open_for_delegation = None;
1330                let mut keys_with_proofs = None;
1331                let mut metadata_url = None;
1332                let mut transaction_fee_commission = None;
1333                let mut baking_reward_commission = None;
1334                let mut finalization_reward_commission = None;
1335                let mut suspend = None;
1336                if bitmap & 1 != 0 {
1337                    capital = Some(source.get()?);
1338                }
1339                if bitmap & (1 << 1) != 0 {
1340                    restake_earnings = Some(source.get()?);
1341                }
1342                if bitmap & (1 << 2) != 0 {
1343                    open_for_delegation = Some(source.get()?);
1344                }
1345                if bitmap & (1 << 3) != 0 {
1346                    // this is serialized manually since the serialization in Haskell is not
1347                    // consistent with the serialization of baker add
1348                    // transactions. The order of fields is different.
1349                    let election_verify_key = source.get()?;
1350                    let proof_election = source.get()?;
1351                    let signature_verify_key = source.get()?;
1352                    let proof_sig = source.get()?;
1353                    let aggregation_verify_key = source.get()?;
1354                    let proof_aggregation = source.get()?;
1355                    keys_with_proofs = Some(BakerKeysPayload {
1356                        phantom: PhantomData,
1357                        election_verify_key,
1358                        signature_verify_key,
1359                        aggregation_verify_key,
1360                        proof_sig,
1361                        proof_election,
1362                        proof_aggregation,
1363                    });
1364                }
1365                if bitmap & (1 << 4) != 0 {
1366                    metadata_url = Some(source.get()?);
1367                }
1368                if bitmap & (1 << 5) != 0 {
1369                    transaction_fee_commission = Some(source.get()?);
1370                }
1371                if bitmap & (1 << 6) != 0 {
1372                    baking_reward_commission = Some(source.get()?);
1373                }
1374                if bitmap & (1 << 7) != 0 {
1375                    finalization_reward_commission = Some(source.get()?);
1376                }
1377                if bitmap & (1 << 8) != 0 {
1378                    suspend = Some(source.get()?);
1379                }
1380                let data = Box::new(ConfigureBakerPayload {
1381                    capital,
1382                    restake_earnings,
1383                    open_for_delegation,
1384                    keys_with_proofs,
1385                    metadata_url,
1386                    transaction_fee_commission,
1387                    baking_reward_commission,
1388                    finalization_reward_commission,
1389                    suspend,
1390                });
1391                Ok(Payload::ConfigureBaker { data })
1392            }
1393            26 => {
1394                let mut data = ConfigureDelegationPayload::default();
1395                let bitmap: u16 = source.get()?;
1396                anyhow::ensure!(
1397                    bitmap & 0b111 == bitmap,
1398                    "Incorrect bitmap for configure delegation."
1399                );
1400                if bitmap & 1 != 0 {
1401                    data.capital = Some(source.get()?);
1402                }
1403                if bitmap & (1 << 1) != 0 {
1404                    data.restake_earnings = Some(source.get()?);
1405                }
1406                if bitmap & (1 << 2) != 0 {
1407                    data.delegation_target = Some(source.get()?);
1408                }
1409                Ok(Payload::ConfigureDelegation { data })
1410            }
1411            27 => {
1412                let token_id = source.get()?;
1413                let operations = source.get()?;
1414                let payload = TokenOperationsPayload {
1415                    token_id,
1416                    operations,
1417                };
1418                Ok(Payload::TokenUpdate { payload })
1419            }
1420            _ => {
1421                anyhow::bail!("Unsupported transaction payload tag {}", tag)
1422            }
1423        }
1424    }
1425}
1426
1427impl PayloadLike for Payload {
1428    fn encode(&self) -> EncodedPayload {
1429        let payload = crate::common::to_bytes(&self);
1430        EncodedPayload { payload }
1431    }
1432
1433    fn encode_to_buffer<B: Buffer>(&self, out: &mut B) {
1434        out.put(&self)
1435    }
1436}
1437
1438impl EncodedPayload {
1439    pub fn size(&self) -> PayloadSize {
1440        let size = self.payload.len() as u32;
1441        PayloadSize { size }
1442    }
1443}
1444
1445/// Compute the transaction sign hash from an encoded payload and header.
1446pub fn compute_transaction_sign_hash(
1447    header: &TransactionHeader,
1448    payload: &impl PayloadLike,
1449) -> hashes::TransactionSignHash {
1450    let mut hasher = sha2::Sha256::new();
1451    hasher.put(header);
1452    payload.encode_to_buffer(&mut hasher);
1453    hashes::HashBytes::new(hasher.result())
1454}
1455
1456/// Abstraction of private keys.
1457pub trait TransactionSigner {
1458    /// Sign the specified transaction hash, allocating and returning the
1459    /// signatures.
1460    fn sign_transaction_hash(
1461        &self,
1462        hash_to_sign: &hashes::TransactionSignHash,
1463    ) -> TransactionSignature;
1464}
1465
1466/// A signing implementation that knows the number of keys up-front.
1467pub trait ExactSizeTransactionSigner: TransactionSigner {
1468    /// Return the number of keys that the signer will sign with.
1469    /// This must match what [TransactionSigner::sign_transaction_hash] returns.
1470    fn num_keys(&self) -> u32;
1471}
1472
1473impl<S: TransactionSigner> TransactionSigner for std::sync::Arc<S> {
1474    fn sign_transaction_hash(
1475        &self,
1476        hash_to_sign: &hashes::TransactionSignHash,
1477    ) -> TransactionSignature {
1478        self.as_ref().sign_transaction_hash(hash_to_sign)
1479    }
1480}
1481
1482impl<S: ExactSizeTransactionSigner> ExactSizeTransactionSigner for std::sync::Arc<S> {
1483    fn num_keys(&self) -> u32 {
1484        self.as_ref().num_keys()
1485    }
1486}
1487
1488impl<S: TransactionSigner> TransactionSigner for std::rc::Rc<S> {
1489    fn sign_transaction_hash(
1490        &self,
1491        hash_to_sign: &hashes::TransactionSignHash,
1492    ) -> TransactionSignature {
1493        self.as_ref().sign_transaction_hash(hash_to_sign)
1494    }
1495}
1496
1497impl<S: ExactSizeTransactionSigner> ExactSizeTransactionSigner for std::rc::Rc<S> {
1498    fn num_keys(&self) -> u32 {
1499        self.as_ref().num_keys()
1500    }
1501}
1502
1503/// This signs with the first `threshold` credentials and for each
1504/// credential with the first threshold keys for that credential.
1505impl TransactionSigner for AccountKeys {
1506    fn sign_transaction_hash(
1507        &self,
1508        hash_to_sign: &hashes::TransactionSignHash,
1509    ) -> TransactionSignature {
1510        let iter = self
1511            .keys
1512            .iter()
1513            .take(usize::from(u8::from(self.threshold)))
1514            .map(|(k, v)| {
1515                (k, {
1516                    let num = u8::from(v.threshold);
1517                    v.keys.iter().take(num.into())
1518                })
1519            });
1520        let mut signatures = BTreeMap::<CredentialIndex, BTreeMap<KeyIndex, _>>::new();
1521        for (ci, cred_keys) in iter {
1522            let cred_sigs = cred_keys
1523                .into_iter()
1524                .map(|(ki, kp)| (*ki, kp.sign(hash_to_sign.as_ref()).into()))
1525                .collect::<BTreeMap<_, _>>();
1526            signatures.insert(*ci, cred_sigs);
1527        }
1528        TransactionSignature { signatures }
1529    }
1530}
1531
1532impl ExactSizeTransactionSigner for AccountKeys {
1533    fn num_keys(&self) -> u32 {
1534        self.keys
1535            .values()
1536            .take(usize::from(u8::from(self.threshold)))
1537            .map(|v| u32::from(u8::from(v.threshold)))
1538            .sum::<u32>()
1539    }
1540}
1541
1542impl<'a, X: TransactionSigner> TransactionSigner for &'a X {
1543    fn sign_transaction_hash(
1544        &self,
1545        hash_to_sign: &hashes::TransactionSignHash,
1546    ) -> TransactionSignature {
1547        (*self).sign_transaction_hash(hash_to_sign)
1548    }
1549}
1550
1551impl<'a, X: ExactSizeTransactionSigner> ExactSizeTransactionSigner for &'a X {
1552    fn num_keys(&self) -> u32 {
1553        (*self).num_keys()
1554    }
1555}
1556
1557impl TransactionSigner for BTreeMap<CredentialIndex, BTreeMap<KeyIndex, KeyPair>> {
1558    fn sign_transaction_hash(
1559        &self,
1560        hash_to_sign: &hashes::TransactionSignHash,
1561    ) -> TransactionSignature {
1562        let mut signatures = BTreeMap::<CredentialIndex, BTreeMap<KeyIndex, _>>::new();
1563        for (ci, cred_keys) in self {
1564            let cred_sigs = cred_keys
1565                .iter()
1566                .map(|(ki, kp)| (*ki, kp.sign(hash_to_sign.as_ref()).into()))
1567                .collect::<BTreeMap<_, _>>();
1568            signatures.insert(*ci, cred_sigs);
1569        }
1570        TransactionSignature { signatures }
1571    }
1572}
1573
1574impl ExactSizeTransactionSigner for BTreeMap<CredentialIndex, BTreeMap<KeyIndex, KeyPair>> {
1575    fn num_keys(&self) -> u32 {
1576        self.values().map(|v| v.len() as u32).sum::<u32>()
1577    }
1578}
1579
1580/// Sign the header and payload, construct the transaction, and return it.
1581pub fn sign_transaction<S: TransactionSigner, P: PayloadLike>(
1582    signer: &S,
1583    header: TransactionHeader,
1584    payload: P,
1585) -> AccountTransaction<P> {
1586    let hash_to_sign = compute_transaction_sign_hash(&header, &payload);
1587    let signature = signer.sign_transaction_hash(&hash_to_sign);
1588    AccountTransaction {
1589        signature,
1590        header,
1591        payload,
1592    }
1593}
1594
1595/// Implementations of this trait are structures which can produce public keys
1596/// with which transaction signatures can be verified.
1597pub trait HasAccountAccessStructure {
1598    /// The number of credentials that must sign a transaction.
1599    fn threshold(&self) -> AccountThreshold;
1600    /// Access a credential at the provided index.
1601    fn credential_keys(&self, idx: CredentialIndex) -> Option<&CredentialPublicKeys>;
1602}
1603
1604#[derive(PartialEq, Eq, Debug, Clone, concordium_std::Serialize)]
1605/// The most straighforward account access structure is a map of public keys
1606/// with the account threshold.
1607pub struct AccountAccessStructure {
1608    /// Keys indexed by credential.
1609    #[concordium(size_length = 1)]
1610    pub keys: BTreeMap<CredentialIndex, CredentialPublicKeys>,
1611    /// The number of credentials that needed to sign a transaction.
1612    pub threshold: AccountThreshold,
1613}
1614
1615/// Input parameter containing indices, signature thresholds,
1616/// and public keys for creating a new `AccountAccessStructure`.
1617pub type AccountStructure<'a> = &'a [(
1618    CredentialIndex,
1619    SignatureThreshold,
1620    &'a [(KeyIndex, ed25519_dalek::VerifyingKey)],
1621)];
1622
1623impl AccountAccessStructure {
1624    /// Generate a new [`AccountAccessStructure`] for the thresholds, public
1625    /// keys, and key indices specified in the input. If there are duplicate
1626    /// indices then later ones override the previous ones.
1627    pub fn new(account_threshold: AccountThreshold, structure: AccountStructure) -> Self {
1628        let mut map: BTreeMap<CredentialIndex, CredentialPublicKeys> = BTreeMap::new();
1629
1630        for credential_structure in structure {
1631            let mut inner_map: BTreeMap<KeyIndex, VerifyKey> = BTreeMap::new();
1632
1633            for key_structure in credential_structure.2 {
1634                inner_map.insert(
1635                    key_structure.0,
1636                    VerifyKey::Ed25519VerifyKey(key_structure.1),
1637                );
1638            }
1639
1640            map.insert(
1641                credential_structure.0,
1642                CredentialPublicKeys {
1643                    keys: inner_map,
1644                    threshold: credential_structure.1,
1645                },
1646            );
1647        }
1648
1649        AccountAccessStructure {
1650            keys: map,
1651            threshold: account_threshold,
1652        }
1653    }
1654
1655    /// Generate a new [`AccountAccessStructure`] with a single credential and
1656    /// public key, at credential and key indices 0.
1657    pub fn singleton(public_key: ed25519_dalek::VerifyingKey) -> Self {
1658        Self::new(
1659            AccountThreshold::ONE,
1660            &[(0.into(), SignatureThreshold::ONE, &[(0.into(), public_key)])],
1661        )
1662    }
1663}
1664
1665impl From<&AccountKeys> for AccountAccessStructure {
1666    fn from(value: &AccountKeys) -> Self {
1667        Self {
1668            threshold: value.threshold,
1669            keys: value
1670                .keys
1671                .iter()
1672                .map(|(k, v)| {
1673                    (
1674                        *k,
1675                        CredentialPublicKeys {
1676                            keys: v
1677                                .keys
1678                                .iter()
1679                                .map(|(ki, kp)| {
1680                                    (
1681                                        *ki,
1682                                        VerifyKey::Ed25519VerifyKey(kp.as_ref().verifying_key()),
1683                                    )
1684                                })
1685                                .collect(),
1686                            threshold: v.threshold,
1687                        },
1688                    )
1689                })
1690                .collect(),
1691        }
1692    }
1693}
1694
1695impl HasAccountAccessStructure for AccountAccessStructure {
1696    fn threshold(&self) -> AccountThreshold {
1697        self.threshold
1698    }
1699
1700    fn credential_keys(&self, idx: CredentialIndex) -> Option<&CredentialPublicKeys> {
1701        self.keys.get(&idx)
1702    }
1703}
1704
1705// The serial and deserial implementations must match the serialization of
1706// `AccountInformation` in Haskell.
1707impl Serial for AccountAccessStructure {
1708    fn serial<B: Buffer>(&self, out: &mut B) {
1709        (self.keys.len() as u8).serial(out);
1710        for (k, v) in self.keys.iter() {
1711            k.serial(out);
1712            v.serial(out);
1713        }
1714        self.threshold.serial(out)
1715    }
1716}
1717
1718// The serial and deserial implementations must match the serialization of
1719// `AccountInformation` in Haskell.
1720impl Deserial for AccountAccessStructure {
1721    fn deserial<R: ReadBytesExt>(source: &mut R) -> ParseResult<Self> {
1722        let len = u8::deserial(source)?;
1723        let keys = common::deserial_map_no_length(source, len.into())?;
1724        let threshold = source.get()?;
1725        Ok(Self { threshold, keys })
1726    }
1727}
1728
1729impl AccountAccessStructure {
1730    /// Return the total number of keys present in the access structure.
1731    pub fn num_keys(&self) -> u32 {
1732        self.keys.values().map(|m| m.keys.len() as u32).sum()
1733    }
1734}
1735
1736/// Verify a signature on the transaction sign hash. This is a low-level
1737/// operation that is useful to avoid recomputing the transaction hash.
1738pub fn verify_signature_transaction_sign_hash(
1739    keys: &impl HasAccountAccessStructure,
1740    hash: &hashes::TransactionSignHash,
1741    signature: &TransactionSignature,
1742) -> bool {
1743    verify_data_signature(keys, hash, &signature.signatures)
1744}
1745
1746/// Verify a signature on the provided data with respect to the account access
1747/// structure.
1748///
1749/// This is not the same as verifying a signature on a serialized transaction.
1750/// Transaction signature verification is a different protocol that first
1751/// involves hashing the message.
1752pub fn verify_data_signature<T: ?Sized + AsRef<[u8]>>(
1753    keys: &impl HasAccountAccessStructure,
1754    data: &T,
1755    signatures: &BTreeMap<CredentialIndex, BTreeMap<KeyIndex, Signature>>,
1756) -> bool {
1757    if usize::from(u8::from(keys.threshold())) > signatures.len() {
1758        return false;
1759    }
1760    // There are enough signatures.
1761    for (&ci, cred_sigs) in signatures.iter() {
1762        if let Some(cred_keys) = keys.credential_keys(ci) {
1763            if usize::from(u8::from(cred_keys.threshold)) > cred_sigs.len() {
1764                return false;
1765            }
1766            for (&ki, sig) in cred_sigs {
1767                if let Some(pk) = cred_keys.get(ki) {
1768                    if !pk.verify(data, sig) {
1769                        return false;
1770                    }
1771                } else {
1772                    return false;
1773                }
1774            }
1775        } else {
1776            return false;
1777        }
1778    }
1779    true
1780}
1781
1782#[derive(Debug, Clone)]
1783/// A block item are data items that are transmitted on the network either as
1784/// separate messages, or as part of blocks. They are the only user-generated
1785/// (as opposed to protocol-generated) message.
1786pub enum BlockItem<PayloadType> {
1787    /// Account transactions are messages which are signed and paid for by an
1788    /// account.
1789    AccountTransaction(AccountTransaction<PayloadType>),
1790    /// Credential deployments create new accounts. They are not paid for
1791    /// directly by the sender. Instead, bakers are rewarded by the protocol for
1792    /// including them.
1793    CredentialDeployment(
1794        Box<
1795            AccountCredentialMessage<
1796                crate::id::constants::IpPairing,
1797                crate::id::constants::ArCurve,
1798                crate::id::constants::AttributeKind,
1799            >,
1800        >,
1801    ),
1802    UpdateInstruction(updates::UpdateInstruction),
1803}
1804
1805impl<PayloadType> From<AccountTransaction<PayloadType>> for BlockItem<PayloadType> {
1806    fn from(at: AccountTransaction<PayloadType>) -> Self {
1807        Self::AccountTransaction(at)
1808    }
1809}
1810
1811impl<PayloadType>
1812    From<
1813        AccountCredentialMessage<
1814            crate::id::constants::IpPairing,
1815            crate::id::constants::ArCurve,
1816            crate::id::constants::AttributeKind,
1817        >,
1818    > for BlockItem<PayloadType>
1819{
1820    fn from(
1821        at: AccountCredentialMessage<
1822            crate::id::constants::IpPairing,
1823            crate::id::constants::ArCurve,
1824            crate::id::constants::AttributeKind,
1825        >,
1826    ) -> Self {
1827        Self::CredentialDeployment(Box::new(at))
1828    }
1829}
1830
1831impl<PayloadType> From<updates::UpdateInstruction> for BlockItem<PayloadType> {
1832    fn from(ui: updates::UpdateInstruction) -> Self {
1833        Self::UpdateInstruction(ui)
1834    }
1835}
1836
1837impl<PayloadType> BlockItem<PayloadType> {
1838    /// Compute the hash of the block item that identifies the block item on the
1839    /// chain.
1840    pub fn hash(&self) -> hashes::TransactionHash
1841    where
1842        BlockItem<PayloadType>: Serial,
1843    {
1844        let mut hasher = sha2::Sha256::new();
1845        hasher.put(&self);
1846        hashes::HashBytes::new(hasher.result())
1847    }
1848}
1849
1850impl<V> Serial for BakerKeysPayload<V> {
1851    fn serial<B: Buffer>(&self, out: &mut B) {
1852        out.put(&self.election_verify_key);
1853        out.put(&self.signature_verify_key);
1854        out.put(&self.aggregation_verify_key);
1855        out.put(&self.proof_sig);
1856        out.put(&self.proof_election);
1857        out.put(&self.proof_aggregation);
1858    }
1859}
1860
1861impl<V> Deserial for BakerKeysPayload<V> {
1862    fn deserial<R: ReadBytesExt>(source: &mut R) -> ParseResult<Self> {
1863        let election_verify_key = source.get()?;
1864        let signature_verify_key = source.get()?;
1865        let aggregation_verify_key = source.get()?;
1866        let proof_sig = source.get()?;
1867        let proof_election = source.get()?;
1868        let proof_aggregation = source.get()?;
1869        Ok(Self {
1870            phantom: PhantomData,
1871            election_verify_key,
1872            signature_verify_key,
1873            aggregation_verify_key,
1874            proof_sig,
1875            proof_election,
1876            proof_aggregation,
1877        })
1878    }
1879}
1880
1881impl Serial for AddBakerPayload {
1882    fn serial<B: Buffer>(&self, out: &mut B) {
1883        out.put(&self.keys);
1884        out.put(&self.baking_stake);
1885        out.put(&self.restake_earnings);
1886    }
1887}
1888
1889impl Deserial for AddBakerPayload {
1890    fn deserial<R: ReadBytesExt>(source: &mut R) -> ParseResult<Self> {
1891        let keys = source.get()?;
1892        let baking_stake = source.get()?;
1893        let restake_earnings = source.get()?;
1894        Ok(Self {
1895            keys,
1896            baking_stake,
1897            restake_earnings,
1898        })
1899    }
1900}
1901
1902impl Serial for InitContractPayload {
1903    fn serial<B: Buffer>(&self, out: &mut B) {
1904        out.put(&self.amount);
1905        out.put(&self.mod_ref);
1906        out.put(&self.init_name);
1907        out.put(&self.param);
1908    }
1909}
1910
1911impl Deserial for InitContractPayload {
1912    fn deserial<R: ReadBytesExt>(source: &mut R) -> ParseResult<Self> {
1913        let amount = source.get()?;
1914        let mod_ref = source.get()?;
1915        let init_name = source.get()?;
1916        let param = source.get()?;
1917        Ok(InitContractPayload {
1918            amount,
1919            mod_ref,
1920            init_name,
1921            param,
1922        })
1923    }
1924}
1925
1926impl Serial for UpdateContractPayload {
1927    fn serial<B: Buffer>(&self, out: &mut B) {
1928        out.put(&self.amount);
1929        out.put(&self.address);
1930        out.put(&self.receive_name);
1931        out.put(&self.message);
1932    }
1933}
1934
1935impl Deserial for UpdateContractPayload {
1936    fn deserial<R: ReadBytesExt>(source: &mut R) -> ParseResult<Self> {
1937        let amount = source.get()?;
1938        let address = source.get()?;
1939        let receive_name = source.get()?;
1940        let message = source.get()?;
1941        Ok(UpdateContractPayload {
1942            amount,
1943            address,
1944            receive_name,
1945            message,
1946        })
1947    }
1948}
1949
1950impl<P: PayloadLike> Serial for BlockItem<P> {
1951    fn serial<B: Buffer>(&self, out: &mut B) {
1952        match &self {
1953            BlockItem::AccountTransaction(at) => {
1954                out.put(&0u8);
1955                out.put(at)
1956            }
1957            BlockItem::CredentialDeployment(acdi) => {
1958                out.put(&1u8);
1959                out.put(acdi);
1960            }
1961            BlockItem::UpdateInstruction(ui) => {
1962                out.put(&2u8);
1963                out.put(ui);
1964            }
1965        }
1966    }
1967}
1968
1969impl Deserial for BlockItem<EncodedPayload> {
1970    fn deserial<R: ReadBytesExt>(source: &mut R) -> ParseResult<Self> {
1971        let tag: u8 = source.get()?;
1972        match tag {
1973            0 => {
1974                let at = source.get()?;
1975                Ok(BlockItem::AccountTransaction(at))
1976            }
1977            1 => {
1978                let acdi = source.get()?;
1979                Ok(BlockItem::CredentialDeployment(acdi))
1980            }
1981            2 => {
1982                let ui = source.get()?;
1983                Ok(BlockItem::UpdateInstruction(ui))
1984            }
1985            _ => anyhow::bail!("Unsupported block item type: {}.", tag),
1986        }
1987    }
1988}
1989
1990/// Energy costs of transactions.
1991pub mod cost {
1992    use crate::id::types::CredentialType;
1993
1994    use super::*;
1995
1996    /// The B constant for NRG assignment. This scales the effect of the number
1997    /// of signatures on the energy.
1998    pub const A: u64 = 100;
1999
2000    /// The A constant for NRG assignment. This scales the effect of transaction
2001    /// size on the energy.
2002    pub const B: u64 = 1;
2003
2004    /// Base cost of a transaction is the minimum cost that accounts for
2005    /// transaction size and signature checking. In addition to base cost
2006    /// each transaction has a transaction-type specific cost.
2007    pub fn base_cost(transaction_size: u64, num_signatures: u32) -> Energy {
2008        Energy::from(B * transaction_size + A * u64::from(num_signatures))
2009    }
2010
2011    /// Additional cost of a normal, account to account, transfer.
2012    pub const SIMPLE_TRANSFER: Energy = Energy { energy: 300 };
2013
2014    /// Additional cost of a transaction consisting of protocol level token
2015    /// operations
2016    pub const PLT_OPERATIONS_TRANSACTIONS: Energy = Energy { energy: 300 };
2017
2018    /// Additional cost of a PLT transfer
2019    pub const PLT_TRANSFER: Energy = Energy { energy: 100 };
2020
2021    /// Additional cost of a PLT mint
2022    pub const PLT_MINT: Energy = Energy { energy: 50 };
2023
2024    /// Additional cost of a PLT burn
2025    pub const PLT_BURN: Energy = Energy { energy: 50 };
2026
2027    /// Additional cost of a PLT allow or deny list update
2028    pub const PLT_LIST_UPDATE: Energy = Energy { energy: 50 };
2029
2030    /// Additional cost of a PLT pause
2031    pub const PLT_PAUSE: Energy = Energy { energy: 50 };
2032
2033    /// Additional cost of an encrypted transfer.
2034    #[deprecated(
2035        since = "5.0.1",
2036        note = "encrypted transfers are deprecated and partially removed since protocol version 7"
2037    )]
2038    pub const ENCRYPTED_TRANSFER: Energy = Energy { energy: 27000 };
2039
2040    /// Additional cost of a transfer from public to encrypted balance.
2041    #[deprecated(
2042        since = "5.0.1",
2043        note = "encrypted transfers are deprecated and partially removed since protocol version 7"
2044    )]
2045    pub const TRANSFER_TO_ENCRYPTED: Energy = Energy { energy: 600 };
2046
2047    /// Additional cost of a transfer from encrypted to public balance.
2048    pub const TRANSFER_TO_PUBLIC: Energy = Energy { energy: 14850 };
2049
2050    /// Cost of a scheduled transfer, parametrized by the number of releases.
2051    pub fn scheduled_transfer(num_releases: u16) -> Energy {
2052        Energy::from(u64::from(num_releases) * (300 + 64))
2053    }
2054
2055    /// Additional cost of registering the account as a baker.
2056    pub const ADD_BAKER: Energy = Energy { energy: 4050 };
2057
2058    /// Additional cost of updating baker's keys.
2059    pub const UPDATE_BAKER_KEYS: Energy = Energy { energy: 4050 };
2060
2061    /// Additional cost of updating the baker's stake, either increasing or
2062    /// lowering it.
2063    pub const UPDATE_BAKER_STAKE: Energy = Energy { energy: 300 };
2064
2065    /// Additional cost of updating the baker's restake flag.
2066    pub const UPDATE_BAKER_RESTAKE: Energy = Energy { energy: 300 };
2067
2068    /// Additional cost of removing a baker.
2069    pub const REMOVE_BAKER: Energy = Energy { energy: 300 };
2070
2071    /// Additional cost of updating existing credential keys. Parametrised by
2072    /// amount of existing credentials and new keys. Due to the way the
2073    /// accounts are stored a new copy of all credentials will be created,
2074    /// so we need to account for that storage increase.
2075    pub fn update_credential_keys(num_credentials_before: u16, num_keys: u16) -> Energy {
2076        Energy {
2077            energy: 500u64 * u64::from(num_credentials_before) + 100 * u64::from(num_keys),
2078        }
2079    }
2080
2081    /// Additional cost of updating account's credentials, parametrized by
2082    /// - the number of credentials on the account before the update
2083    /// - list of keys of credentials to be added.
2084    pub fn update_credentials(num_credentials_before: u16, num_keys: &[u16]) -> Energy {
2085        UPDATE_CREDENTIALS_BASE + update_credentials_variable(num_credentials_before, num_keys)
2086    }
2087
2088    /// Additional cost of registering a piece of data.
2089    pub const REGISTER_DATA: Energy = Energy { energy: 300 };
2090
2091    /// Additional cost of configuring a baker if new keys are registered.
2092    pub const CONFIGURE_BAKER_WITH_KEYS: Energy = Energy { energy: 4050 };
2093
2094    /// Additional cost of configuring a baker if new keys are **not**
2095    /// registered.
2096    pub const CONFIGURE_BAKER_WITHOUT_KEYS: Energy = Energy { energy: 300 };
2097
2098    /// Additional cost of configuring delegation.
2099    pub const CONFIGURE_DELEGATION: Energy = Energy { energy: 300 };
2100
2101    /// Additional cost of deploying a smart contract module, parametrized by
2102    /// the size of the module, which is defined to be the size of
2103    /// the binary `.wasm` file that is sent as part of the transaction.
2104    pub fn deploy_module(module_size: u64) -> Energy {
2105        Energy::from(module_size / 10)
2106    }
2107
2108    /// There is a non-trivial amount of lookup
2109    /// that needs to be done before we can start any checking. This ensures
2110    /// that those lookups are not a problem. If the credential updates are
2111    /// genuine then this cost is going to be negligible compared to
2112    /// verifying the credential.
2113    const UPDATE_CREDENTIALS_BASE: Energy = Energy { energy: 500 };
2114
2115    /// Additional cost of deploying a credential of the given type and with the
2116    /// given number of keys.
2117    pub fn deploy_credential(ty: CredentialType, num_keys: u16) -> Energy {
2118        match ty {
2119            CredentialType::Initial => Energy::from(1000 + 100 * u64::from(num_keys)),
2120            CredentialType::Normal => Energy::from(54000 + 100 * u64::from(num_keys)),
2121        }
2122    }
2123
2124    /// Helper function. This together with [`UPDATE_CREDENTIALS_BASE`]
2125    /// determines the cost of updating credentials on an account.
2126    fn update_credentials_variable(num_credentials_before: u16, num_keys: &[u16]) -> Energy {
2127        // the 500 * num_credentials_before is to account for transactions which do
2128        // nothing, e.g., don't add don't remove, and don't update the
2129        // threshold. These still have a cost since the way the accounts are
2130        // stored it will update the stored account data, which does take up
2131        // quite a bit of space per credential.
2132        let energy: u64 = 500 * u64::from(num_credentials_before)
2133            + num_keys
2134                .iter()
2135                .map(|&nk| u64::from(deploy_credential(CredentialType::Normal, nk)))
2136                .sum::<u64>();
2137        Energy::from(energy)
2138    }
2139}
2140
2141/// High level wrappers for making transactions with minimal user input.
2142/// These wrappers handle encoding, setting energy costs when those are fixed
2143/// for transaction.
2144/// See also the [send] module above which combines construction with signing.
2145pub mod construct {
2146    use super::*;
2147    use crate::common::upward::Upward;
2148    use crate::{
2149        common::cbor,
2150        protocol_level_tokens::{RawCbor, TokenId, TokenOperation, TokenOperations},
2151    };
2152
2153    /// A transaction that is prepared to be signed.
2154    /// The serde instance serializes the structured payload and skips
2155    /// serializing the encoded one.
2156    #[derive(Debug, Clone, SerdeSerialize)]
2157    #[serde(rename_all = "camelCase")]
2158    pub struct PreAccountTransaction {
2159        pub header: TransactionHeader,
2160        /// The payload.
2161        pub payload: Payload,
2162        /// The encoded payload. This is already serialized payload that is
2163        /// constructed during construction of the prepared transaction
2164        /// since we need it to compute the cost.
2165        #[serde(skip_serializing)]
2166        pub encoded: EncodedPayload,
2167        /// Hash of the transaction to sign.
2168        pub hash_to_sign: hashes::TransactionSignHash,
2169    }
2170
2171    impl PreAccountTransaction {
2172        /// Sign the transaction with the provided signer. Note that this signer
2173        /// must match the account address and the number of keys that
2174        /// were used in construction, otherwise the transaction will be
2175        /// invalid.
2176        pub fn sign(self, signer: &impl TransactionSigner) -> AccountTransaction<EncodedPayload> {
2177            sign_transaction(signer, self.header, self.encoded)
2178        }
2179    }
2180
2181    /// Serialize only the header and payload, so that this can be deserialized
2182    /// as a transaction body.
2183    impl Serial for PreAccountTransaction {
2184        fn serial<B: Buffer>(&self, out: &mut B) {
2185            self.header.serial(out);
2186            self.encoded.serial(out);
2187        }
2188    }
2189
2190    impl Deserial for PreAccountTransaction {
2191        fn deserial<R: ReadBytesExt>(source: &mut R) -> ParseResult<Self> {
2192            let header: TransactionHeader = source.get()?;
2193            let encoded = get_encoded_payload(source, header.payload_size)?;
2194            let payload = encoded.decode()?;
2195            let hash_to_sign = compute_transaction_sign_hash(&header, &encoded);
2196            Ok(Self {
2197                header,
2198                payload,
2199                encoded,
2200                hash_to_sign,
2201            })
2202        }
2203    }
2204
2205    /// Helper structure to store the intermediate state of a transaction.
2206    /// The problem this helps solve is that to compute the exact energy
2207    /// requirements for the transaction we need to know its exact size when
2208    /// serialized. For some we could compute this manually, but in general it
2209    /// is less error prone to serialize and get the length. To avoid doing
2210    /// double work we first serialize with a dummy `energy_amount` value, then
2211    /// in the [TransactionBuilder::finalize] method we compute the correct
2212    /// energy amount and overwrite it in the transaction, before signing
2213    /// it.
2214    /// This is deliberately made private so that the inconsistent internal
2215    /// state does not leak.
2216    struct TransactionBuilder {
2217        header: TransactionHeader,
2218        payload: Payload,
2219        encoded: EncodedPayload,
2220    }
2221
2222    /// Size of a transaction header. This is currently always 60 bytes.
2223    /// Future chain updates might revise this, but this is a big change so this
2224    /// is expected to change seldomly.
2225    pub const TRANSACTION_HEADER_SIZE: u64 = 32 + 8 + 8 + 4 + 8;
2226
2227    impl TransactionBuilder {
2228        pub fn new(
2229            sender: AccountAddress,
2230            nonce: Nonce,
2231            expiry: TransactionTime,
2232            payload: Payload,
2233        ) -> Self {
2234            let encoded = payload.encode();
2235            let header = TransactionHeader {
2236                sender,
2237                nonce,
2238                energy_amount: 0.into(),
2239                payload_size: encoded.size(),
2240                expiry,
2241            };
2242            Self {
2243                header,
2244                payload,
2245                encoded,
2246            }
2247        }
2248
2249        #[inline]
2250        fn size(&self) -> u64 {
2251            TRANSACTION_HEADER_SIZE + u64::from(u32::from(self.header.payload_size))
2252        }
2253
2254        #[inline]
2255        pub fn construct(mut self, f: impl FnOnce(u64) -> Energy) -> PreAccountTransaction {
2256            let size = self.size();
2257            self.header.energy_amount = f(size);
2258            let hash_to_sign = compute_transaction_sign_hash(&self.header, &self.encoded);
2259            PreAccountTransaction {
2260                header: self.header,
2261                payload: self.payload,
2262                encoded: self.encoded,
2263                hash_to_sign,
2264            }
2265        }
2266    }
2267
2268    /// Construct a native coin (CCD) transfer transaction.
2269    pub fn transfer(
2270        num_sigs: u32,
2271        sender: AccountAddress,
2272        nonce: Nonce,
2273        expiry: TransactionTime,
2274        receiver: AccountAddress,
2275        amount: Amount,
2276    ) -> PreAccountTransaction {
2277        let payload = Payload::Transfer {
2278            to_address: receiver,
2279            amount,
2280        };
2281        make_transaction(
2282            sender,
2283            nonce,
2284            expiry,
2285            GivenEnergy::Add {
2286                num_sigs,
2287                energy: cost::SIMPLE_TRANSFER,
2288            },
2289            payload,
2290        )
2291    }
2292
2293    /// Construct a native coin (CCD) transfer transaction with a memo.
2294    pub fn transfer_with_memo(
2295        num_sigs: u32,
2296        sender: AccountAddress,
2297        nonce: Nonce,
2298        expiry: TransactionTime,
2299        receiver: AccountAddress,
2300        amount: Amount,
2301        memo: Memo,
2302    ) -> PreAccountTransaction {
2303        let payload = Payload::TransferWithMemo {
2304            to_address: receiver,
2305            memo,
2306            amount,
2307        };
2308        make_transaction(
2309            sender,
2310            nonce,
2311            expiry,
2312            GivenEnergy::Add {
2313                num_sigs,
2314                energy: cost::SIMPLE_TRANSFER,
2315            },
2316            payload,
2317        )
2318    }
2319
2320    /// Additional cost of token operations transaction
2321    fn token_operations_txn_energy(operations: &TokenOperations) -> Energy {
2322        cost::PLT_OPERATIONS_TRANSACTIONS
2323            + operations
2324                .operations
2325                .iter()
2326                .map(|op| match op {
2327                    Upward::Known(TokenOperation::Transfer(_)) => cost::PLT_TRANSFER,
2328                    Upward::Known(TokenOperation::Mint(_)) => cost::PLT_MINT,
2329                    Upward::Known(TokenOperation::Burn(_)) => cost::PLT_BURN,
2330                    Upward::Known(TokenOperation::AddAllowList(_))
2331                    | Upward::Known(TokenOperation::RemoveAllowList(_))
2332                    | Upward::Known(TokenOperation::AddDenyList(_))
2333                    | Upward::Known(TokenOperation::RemoveDenyList(_)) => cost::PLT_LIST_UPDATE,
2334                    Upward::Known(TokenOperation::Pause(_))
2335                    | Upward::Known(TokenOperation::Unpause(_)) => cost::PLT_PAUSE,
2336                    Upward::Unknown(_) => Default::default(),
2337                })
2338                .sum()
2339    }
2340
2341    /// Construct a protocol level token update transaction consisting of the
2342    /// token update operations encoded in the given CBOR.
2343    ///
2344    /// Update operations can be created using the functions in
2345    /// [`operations`](crate::protocol_level_tokens::operations).
2346    pub fn token_update_operations(
2347        num_sigs: u32,
2348        sender: AccountAddress,
2349        nonce: Nonce,
2350        expiry: TransactionTime,
2351        token_id: TokenId,
2352        operations: TokenOperations,
2353    ) -> CborSerializationResult<PreAccountTransaction> {
2354        let energy = token_operations_txn_energy(&operations);
2355        let operations = RawCbor::from(cbor::cbor_encode(&operations)?);
2356
2357        let payload = Payload::TokenUpdate {
2358            payload: TokenOperationsPayload {
2359                token_id,
2360                operations,
2361            },
2362        };
2363        Ok(make_transaction(
2364            sender,
2365            nonce,
2366            expiry,
2367            GivenEnergy::Add { num_sigs, energy },
2368            payload,
2369        ))
2370    }
2371
2372    /// Make an encrypted transfer. The payload can be constructed using
2373    /// [`make_transfer_data`](crate::encrypted_transfers::make_transfer_data).
2374    #[deprecated(
2375        since = "5.0.1",
2376        note = "encrypted transfers are deprecated and partially removed since protocol version 7"
2377    )]
2378    pub fn encrypted_transfer(
2379        num_sigs: u32,
2380        sender: AccountAddress,
2381        nonce: Nonce,
2382        expiry: TransactionTime,
2383        receiver: AccountAddress,
2384        data: EncryptedAmountTransferData<EncryptedAmountsCurve>,
2385    ) -> PreAccountTransaction {
2386        let payload = Payload::EncryptedAmountTransfer {
2387            to: receiver,
2388            data: Box::new(data),
2389        };
2390        make_transaction(
2391            sender,
2392            nonce,
2393            expiry,
2394            GivenEnergy::Add {
2395                num_sigs,
2396                energy: cost::ENCRYPTED_TRANSFER,
2397            },
2398            payload,
2399        )
2400    }
2401
2402    /// Make an encrypted transfer with a memo.
2403    /// The payload can be constructed using
2404    /// [make_transfer_data](crate::encrypted_transfers::make_transfer_data).
2405    #[deprecated(
2406        since = "5.0.1",
2407        note = "encrypted transfers are deprecated and partially removed since protocol version 7"
2408    )]
2409    pub fn encrypted_transfer_with_memo(
2410        num_sigs: u32,
2411        sender: AccountAddress,
2412        nonce: Nonce,
2413        expiry: TransactionTime,
2414        receiver: AccountAddress,
2415        data: EncryptedAmountTransferData<EncryptedAmountsCurve>,
2416        memo: Memo,
2417    ) -> PreAccountTransaction {
2418        // FIXME: This payload could be returned as well since it is only borrowed.
2419        let payload = Payload::EncryptedAmountTransferWithMemo {
2420            to: receiver,
2421            memo,
2422            data: Box::new(data),
2423        };
2424        make_transaction(
2425            sender,
2426            nonce,
2427            expiry,
2428            GivenEnergy::Add {
2429                num_sigs,
2430                energy: cost::ENCRYPTED_TRANSFER,
2431            },
2432            payload,
2433        )
2434    }
2435
2436    /// Transfer the given amount from public to encrypted balance of the given
2437    /// account.
2438    #[deprecated(
2439        since = "5.0.1",
2440        note = "encrypted transfers are deprecated and partially removed since protocol version 7"
2441    )]
2442    pub fn transfer_to_encrypted(
2443        num_sigs: u32,
2444        sender: AccountAddress,
2445        nonce: Nonce,
2446        expiry: TransactionTime,
2447        amount: Amount,
2448    ) -> PreAccountTransaction {
2449        let payload = Payload::TransferToEncrypted { amount };
2450        make_transaction(
2451            sender,
2452            nonce,
2453            expiry,
2454            GivenEnergy::Add {
2455                num_sigs,
2456                energy: cost::TRANSFER_TO_ENCRYPTED,
2457            },
2458            payload,
2459        )
2460    }
2461
2462    /// Transfer the given amount from encrypted to public balance of the given
2463    /// account. The payload may be constructed using
2464    /// [`make_sec_to_pub_transfer_data`][anchor]
2465    ///
2466    /// [anchor]: crate::encrypted_transfers::make_sec_to_pub_transfer_data
2467    pub fn transfer_to_public(
2468        num_sigs: u32,
2469        sender: AccountAddress,
2470        nonce: Nonce,
2471        expiry: TransactionTime,
2472        data: SecToPubAmountTransferData<EncryptedAmountsCurve>,
2473    ) -> PreAccountTransaction {
2474        // FIXME: This payload could be returned as well since it is only borrowed.
2475        let payload = Payload::TransferToPublic {
2476            data: Box::new(data),
2477        };
2478        make_transaction(
2479            sender,
2480            nonce,
2481            expiry,
2482            GivenEnergy::Add {
2483                num_sigs,
2484                energy: cost::TRANSFER_TO_PUBLIC,
2485            },
2486            payload,
2487        )
2488    }
2489
2490    /// Construct a transfer with schedule transaction, sending to the given
2491    /// account.
2492    pub fn transfer_with_schedule(
2493        num_sigs: u32,
2494        sender: AccountAddress,
2495        nonce: Nonce,
2496        expiry: TransactionTime,
2497        receiver: AccountAddress,
2498        schedule: Vec<(Timestamp, Amount)>,
2499    ) -> PreAccountTransaction {
2500        let num_releases = schedule.len() as u16;
2501        let payload = Payload::TransferWithSchedule {
2502            to: receiver,
2503            schedule,
2504        };
2505        make_transaction(
2506            sender,
2507            nonce,
2508            expiry,
2509            GivenEnergy::Add {
2510                num_sigs,
2511                energy: cost::scheduled_transfer(num_releases),
2512            },
2513            payload,
2514        )
2515    }
2516
2517    /// Construct a transfer with schedule and memo transaction, sending to the
2518    /// given account.
2519    pub fn transfer_with_schedule_and_memo(
2520        num_sigs: u32,
2521        sender: AccountAddress,
2522        nonce: Nonce,
2523        expiry: TransactionTime,
2524        receiver: AccountAddress,
2525        schedule: Vec<(Timestamp, Amount)>,
2526        memo: Memo,
2527    ) -> PreAccountTransaction {
2528        let num_releases = schedule.len() as u16;
2529        let payload = Payload::TransferWithScheduleAndMemo {
2530            to: receiver,
2531            memo,
2532            schedule,
2533        };
2534        make_transaction(
2535            sender,
2536            nonce,
2537            expiry,
2538            GivenEnergy::Add {
2539                num_sigs,
2540                energy: cost::scheduled_transfer(num_releases),
2541            },
2542            payload,
2543        )
2544    }
2545
2546    /// Register the sender account as a baker.
2547    ///
2548    /// **Note that this transaction only applies to protocol versions 1-3.**
2549    /// Use [`configure_baker`](Self::configure_baker) instead for protocols
2550    /// after 4.
2551    #[deprecated(
2552        since = "2.0.0",
2553        note = "This transaction only applies to protocol versions 1-3. Use configure_baker \
2554                instead."
2555    )]
2556    #[doc(hidden)]
2557    pub fn add_baker(
2558        num_sigs: u32,
2559        sender: AccountAddress,
2560        nonce: Nonce,
2561        expiry: TransactionTime,
2562        baking_stake: Amount,
2563        restake_earnings: bool,
2564        keys: BakerAddKeysPayload,
2565    ) -> PreAccountTransaction {
2566        let payload = Payload::AddBaker {
2567            payload: Box::new(AddBakerPayload {
2568                keys,
2569                baking_stake,
2570                restake_earnings,
2571            }),
2572        };
2573        make_transaction(
2574            sender,
2575            nonce,
2576            expiry,
2577            GivenEnergy::Add {
2578                num_sigs,
2579                energy: cost::ADD_BAKER,
2580            },
2581            payload,
2582        )
2583    }
2584
2585    /// Update keys of the baker associated with the sender account.
2586    ///
2587    /// **Note that this transaction only applies to protocol versions 1-3.**
2588    /// Use [`configure_baker`](Self::configure_baker) instead for protocols
2589    /// after 4.
2590    #[deprecated(
2591        since = "2.0.0",
2592        note = "This transaction only applies to protocol versions 1-3. Use configure_baker \
2593                instead."
2594    )]
2595    #[doc(hidden)]
2596    pub fn update_baker_keys(
2597        num_sigs: u32,
2598        sender: AccountAddress,
2599        nonce: Nonce,
2600        expiry: TransactionTime,
2601        keys: BakerUpdateKeysPayload,
2602    ) -> PreAccountTransaction {
2603        // FIXME: This payload could be returned as well since it is only borrowed.
2604        let payload = Payload::UpdateBakerKeys {
2605            payload: Box::new(keys),
2606        };
2607        make_transaction(
2608            sender,
2609            nonce,
2610            expiry,
2611            GivenEnergy::Add {
2612                num_sigs,
2613                energy: cost::UPDATE_BAKER_KEYS,
2614            },
2615            payload,
2616        )
2617    }
2618
2619    /// Deregister the account as a baker.
2620    ///
2621    /// **Note that this transaction only applies to protocol versions 1-3.**
2622    /// Use [`configure_baker`](Self::configure_baker) instead for protocols
2623    /// after 4.
2624    #[deprecated(
2625        since = "2.0.0",
2626        note = "This transaction only applies to protocol versions 1-3. Use configure_baker \
2627                instead."
2628    )]
2629    #[doc(hidden)]
2630    pub fn remove_baker(
2631        num_sigs: u32,
2632        sender: AccountAddress,
2633        nonce: Nonce,
2634        expiry: TransactionTime,
2635    ) -> PreAccountTransaction {
2636        // FIXME: This payload could be returned as well since it is only borrowed.
2637        let payload = Payload::RemoveBaker;
2638        make_transaction(
2639            sender,
2640            nonce,
2641            expiry,
2642            GivenEnergy::Add {
2643                num_sigs,
2644                energy: cost::REMOVE_BAKER,
2645            },
2646            payload,
2647        )
2648    }
2649
2650    /// Update the amount the account stakes for being a baker.
2651    ///
2652    /// **Note that this transaction only applies to protocol versions 1-3.**
2653    /// Use [`configure_baker`](Self::configure_baker) instead for protocols
2654    /// after 4.
2655    #[deprecated(
2656        since = "2.0.0",
2657        note = "This transaction only applies to protocol versions 1-3. Use configure_baker \
2658                instead."
2659    )]
2660    #[doc(hidden)]
2661    pub fn update_baker_stake(
2662        num_sigs: u32,
2663        sender: AccountAddress,
2664        nonce: Nonce,
2665        expiry: TransactionTime,
2666        new_stake: Amount,
2667    ) -> PreAccountTransaction {
2668        // FIXME: This payload could be returned as well since it is only borrowed.
2669        let payload = Payload::UpdateBakerStake { stake: new_stake };
2670        make_transaction(
2671            sender,
2672            nonce,
2673            expiry,
2674            GivenEnergy::Add {
2675                num_sigs,
2676                energy: cost::UPDATE_BAKER_STAKE,
2677            },
2678            payload,
2679        )
2680    }
2681
2682    /// Update whether the earnings are automatically added to the baker's stake
2683    /// or not.
2684    ///
2685    /// **Note that this transaction only applies to protocol versions 1-3.**
2686    /// Use [`configure_baker`](Self::configure_baker) instead for protocols
2687    /// after 4.
2688    #[deprecated(
2689        since = "2.0.0",
2690        note = "This transaction only applies to protocol versions 1-3. Use configure_baker \
2691                instead."
2692    )]
2693    #[doc(hidden)]
2694    pub fn update_baker_restake_earnings(
2695        num_sigs: u32,
2696        sender: AccountAddress,
2697        nonce: Nonce,
2698        expiry: TransactionTime,
2699        restake_earnings: bool,
2700    ) -> PreAccountTransaction {
2701        // FIXME: This payload could be returned as well since it is only borrowed.
2702        let payload = Payload::UpdateBakerRestakeEarnings { restake_earnings };
2703        make_transaction(
2704            sender,
2705            nonce,
2706            expiry,
2707            GivenEnergy::Add {
2708                num_sigs,
2709                energy: cost::UPDATE_BAKER_RESTAKE,
2710            },
2711            payload,
2712        )
2713    }
2714
2715    /// Construct a transction to register the given piece of data.
2716    pub fn register_data(
2717        num_sigs: u32,
2718        sender: AccountAddress,
2719        nonce: Nonce,
2720        expiry: TransactionTime,
2721        data: RegisteredData,
2722    ) -> PreAccountTransaction {
2723        let payload = Payload::RegisterData { data };
2724        make_transaction(
2725            sender,
2726            nonce,
2727            expiry,
2728            GivenEnergy::Add {
2729                num_sigs,
2730                energy: cost::REGISTER_DATA,
2731            },
2732            payload,
2733        )
2734    }
2735
2736    /// Deploy the given Wasm module. The module is given as a binary source,
2737    /// and no processing is done to the module.
2738    pub fn deploy_module(
2739        num_sigs: u32,
2740        sender: AccountAddress,
2741        nonce: Nonce,
2742        expiry: TransactionTime,
2743        module: smart_contracts::WasmModule,
2744    ) -> PreAccountTransaction {
2745        let module_size = module.source.size();
2746        let payload = Payload::DeployModule { module };
2747        make_transaction(
2748            sender,
2749            nonce,
2750            expiry,
2751            GivenEnergy::Add {
2752                num_sigs,
2753                energy: cost::deploy_module(module_size),
2754            },
2755            payload,
2756        )
2757    }
2758
2759    /// Initialize a smart contract, giving it the given amount of energy for
2760    /// execution. The unique parameters are
2761    /// - `energy` -- the amount of energy that can be used for contract
2762    ///   execution. The base energy amount for transaction verification will be
2763    ///   added to this cost.
2764    pub fn init_contract(
2765        num_sigs: u32,
2766        sender: AccountAddress,
2767        nonce: Nonce,
2768        expiry: TransactionTime,
2769        payload: InitContractPayload,
2770        energy: Energy,
2771    ) -> PreAccountTransaction {
2772        let payload = Payload::InitContract { payload };
2773        make_transaction(
2774            sender,
2775            nonce,
2776            expiry,
2777            GivenEnergy::Add { num_sigs, energy },
2778            payload,
2779        )
2780    }
2781
2782    /// Update a smart contract intance, giving it the given amount of energy
2783    /// for execution. The unique parameters are
2784    /// - `energy` -- the amount of energy that can be used for contract
2785    ///   execution. The base energy amount for transaction verification will be
2786    ///   added to this cost.
2787    pub fn update_contract(
2788        num_sigs: u32,
2789        sender: AccountAddress,
2790        nonce: Nonce,
2791        expiry: TransactionTime,
2792        payload: UpdateContractPayload,
2793        energy: Energy,
2794    ) -> PreAccountTransaction {
2795        let payload = Payload::Update { payload };
2796        make_transaction(
2797            sender,
2798            nonce,
2799            expiry,
2800            GivenEnergy::Add { num_sigs, energy },
2801            payload,
2802        )
2803    }
2804
2805    /// Configure the account as a baker. Only valid for protocol version 4 and
2806    /// up.
2807    pub fn configure_baker(
2808        num_sigs: u32,
2809        sender: AccountAddress,
2810        nonce: Nonce,
2811        expiry: TransactionTime,
2812        payload: ConfigureBakerPayload,
2813    ) -> PreAccountTransaction {
2814        let energy = if payload.keys_with_proofs.is_some() {
2815            cost::CONFIGURE_BAKER_WITH_KEYS
2816        } else {
2817            cost::CONFIGURE_BAKER_WITHOUT_KEYS
2818        };
2819        let payload = Payload::ConfigureBaker {
2820            data: Box::new(payload),
2821        };
2822        make_transaction(
2823            sender,
2824            nonce,
2825            expiry,
2826            GivenEnergy::Add { num_sigs, energy },
2827            payload,
2828        )
2829    }
2830
2831    /// Configure the account as a delegator. Only valid for protocol version 4
2832    /// and up.
2833    pub fn configure_delegation(
2834        num_sigs: u32,
2835        sender: AccountAddress,
2836        nonce: Nonce,
2837        expiry: TransactionTime,
2838        payload: ConfigureDelegationPayload,
2839    ) -> PreAccountTransaction {
2840        let payload = Payload::ConfigureDelegation { data: payload };
2841        make_transaction(
2842            sender,
2843            nonce,
2844            expiry,
2845            GivenEnergy::Add {
2846                num_sigs,
2847                energy: cost::CONFIGURE_DELEGATION,
2848            },
2849            payload,
2850        )
2851    }
2852
2853    /// Construct a transaction to update keys of a single credential on an
2854    /// account. The transaction specific arguments are
2855    ///
2856    /// - `num_existing_credentials` - the number of existing credentials on the
2857    ///   account. This will affect the estimated transaction cost. It is safe
2858    ///   to over-approximate this.
2859    /// - `cred_id` - `credId` of a credential whose keys are to be updated.
2860    /// - `keys` - the new keys associated with the credential.
2861    pub fn update_credential_keys(
2862        num_sigs: u32,
2863        sender: AccountAddress,
2864        nonce: Nonce,
2865        expiry: TransactionTime,
2866        num_existing_credentials: u16,
2867        cred_id: CredentialRegistrationID,
2868        keys: CredentialPublicKeys,
2869    ) -> PreAccountTransaction {
2870        let num_cred_keys = keys.keys.len() as u16;
2871        let payload = Payload::UpdateCredentialKeys { cred_id, keys };
2872        make_transaction(
2873            sender,
2874            nonce,
2875            expiry,
2876            GivenEnergy::Add {
2877                energy: cost::update_credential_keys(num_existing_credentials, num_cred_keys),
2878                num_sigs,
2879            },
2880            payload,
2881        )
2882    }
2883
2884    /// Construct a transaction to update credentials on an account.
2885    /// The transaction specific arguments are
2886    ///
2887    /// - `num_existing_credentials` - the number of existing credentials on the
2888    ///   account. This will affect the estimated transaction cost. It is safe
2889    ///   to over-approximate this.
2890    /// - `new_credentials` - the new credentials to be deployed to the account
2891    ///   with the desired indices. The credential with index 0 cannot be
2892    ///   replaced.
2893    /// - `remove_credentials` - the list of credentials, by `credId`'s, to be
2894    ///   removed
2895    /// - `new_threshold` - the new account threshold.
2896    #[allow(clippy::too_many_arguments)]
2897    pub fn update_credentials(
2898        num_sigs: u32,
2899        sender: AccountAddress,
2900        nonce: Nonce,
2901        expiry: TransactionTime,
2902        num_existing_credentials: u16,
2903        new_credentials: AccountCredentialsMap,
2904        remove_credentials: Vec<CredentialRegistrationID>,
2905        new_threshold: AccountThreshold,
2906    ) -> PreAccountTransaction {
2907        let num_cred_keys = new_credentials
2908            .values()
2909            .map(|v| v.values.cred_key_info.keys.len() as u16)
2910            .collect::<Vec<_>>();
2911        let payload = Payload::UpdateCredentials {
2912            new_cred_infos: new_credentials,
2913            remove_cred_ids: remove_credentials,
2914            new_threshold,
2915        };
2916        make_transaction(
2917            sender,
2918            nonce,
2919            expiry,
2920            GivenEnergy::Add {
2921                energy: cost::update_credentials(num_existing_credentials, &num_cred_keys),
2922                num_sigs,
2923            },
2924            payload,
2925        )
2926    }
2927
2928    /// An upper bound on the amount of energy to spend on a transaction.
2929    /// Transaction costs have two components, one is based on the size of the
2930    /// transaction and the number of signatures, and then there is a
2931    /// transaction specific one. This construction helps handle the fixed
2932    /// costs and allows the user to focus only on the transaction specific
2933    /// ones. The most important case for this are smart contract
2934    /// initialisations and updates.
2935    pub enum GivenEnergy {
2936        /// Use this exact amount of energy.
2937        Absolute(Energy),
2938        /// Add the given amount of energy to the base amount.
2939        /// The base amount covers transaction size and signature checking.
2940        Add { energy: Energy, num_sigs: u32 },
2941    }
2942
2943    /// A convenience wrapper around `sign_transaction` that construct the
2944    /// transaction and signs it. Compared to transaction-type-specific wrappers
2945    /// above this allows selecting the amount of energy
2946    pub fn make_transaction(
2947        sender: AccountAddress,
2948        nonce: Nonce,
2949        expiry: TransactionTime,
2950        energy: GivenEnergy,
2951        payload: Payload,
2952    ) -> PreAccountTransaction {
2953        let builder = TransactionBuilder::new(sender, nonce, expiry, payload);
2954        let cost = |size| match energy {
2955            GivenEnergy::Absolute(energy) => energy,
2956            GivenEnergy::Add { num_sigs, energy } => cost::base_cost(size, num_sigs) + energy,
2957        };
2958        builder.construct(cost)
2959    }
2960}
2961
2962/// High level wrappers for making transactions with minimal user input.
2963/// These wrappers handle encoding, setting energy costs when those are fixed
2964/// for transaction.
2965pub mod send {
2966    use super::*;
2967    use crate::protocol_level_tokens::{TokenId, TokenOperations};
2968
2969    /// Construct a native coin (CCD) transfer transaction.
2970    pub fn transfer(
2971        signer: &impl ExactSizeTransactionSigner,
2972        sender: AccountAddress,
2973        nonce: Nonce,
2974        expiry: TransactionTime,
2975        receiver: AccountAddress,
2976        amount: Amount,
2977    ) -> AccountTransaction<EncodedPayload> {
2978        construct::transfer(signer.num_keys(), sender, nonce, expiry, receiver, amount).sign(signer)
2979    }
2980
2981    /// Construct a native coin (CCD) transfer transaction with a memo.
2982    pub fn transfer_with_memo(
2983        signer: &impl ExactSizeTransactionSigner,
2984        sender: AccountAddress,
2985        nonce: Nonce,
2986        expiry: TransactionTime,
2987        receiver: AccountAddress,
2988        amount: Amount,
2989        memo: Memo,
2990    ) -> AccountTransaction<EncodedPayload> {
2991        construct::transfer_with_memo(
2992            signer.num_keys(),
2993            sender,
2994            nonce,
2995            expiry,
2996            receiver,
2997            amount,
2998            memo,
2999        )
3000        .sign(signer)
3001    }
3002
3003    /// Construct and sign a protocol level token update transaction consisting
3004    /// of the token update operations encoded in the given CBOR.
3005    ///
3006    /// Update operations can be created using the functions in
3007    /// [`operations`](crate::protocol_level_tokens::operations).
3008    pub fn token_update_operations(
3009        signer: &impl ExactSizeTransactionSigner,
3010        sender: AccountAddress,
3011        nonce: Nonce,
3012        expiry: TransactionTime,
3013        token_id: TokenId,
3014        operations: TokenOperations,
3015    ) -> CborSerializationResult<AccountTransaction<EncodedPayload>> {
3016        Ok(construct::token_update_operations(
3017            signer.num_keys(),
3018            sender,
3019            nonce,
3020            expiry,
3021            token_id,
3022            operations,
3023        )?
3024        .sign(signer))
3025    }
3026
3027    /// Make an encrypted transfer. The payload can be constructed using
3028    /// [`make_transfer_data`](crate::encrypted_transfers::make_transfer_data).
3029    #[deprecated(
3030        since = "5.0.1",
3031        note = "encrypted transfers are deprecated and partially removed since protocol version 7"
3032    )]
3033    pub fn encrypted_transfer(
3034        signer: &impl ExactSizeTransactionSigner,
3035        sender: AccountAddress,
3036        nonce: Nonce,
3037        expiry: TransactionTime,
3038        receiver: AccountAddress,
3039        data: EncryptedAmountTransferData<EncryptedAmountsCurve>,
3040    ) -> AccountTransaction<EncodedPayload> {
3041        construct::encrypted_transfer(signer.num_keys(), sender, nonce, expiry, receiver, data)
3042            .sign(signer)
3043    }
3044
3045    /// Make an encrypted transfer with a memo.
3046    /// The payload can be constructed using
3047    /// [`make_transfer_data`](crate::encrypted_transfers::make_transfer_data).
3048    #[deprecated(
3049        since = "5.0.1",
3050        note = "encrypted transfers are deprecated and partially removed since protocol version 7"
3051    )]
3052    pub fn encrypted_transfer_with_memo(
3053        signer: &impl ExactSizeTransactionSigner,
3054        sender: AccountAddress,
3055        nonce: Nonce,
3056        expiry: TransactionTime,
3057        receiver: AccountAddress,
3058        data: EncryptedAmountTransferData<EncryptedAmountsCurve>,
3059        memo: Memo,
3060    ) -> AccountTransaction<EncodedPayload> {
3061        construct::encrypted_transfer_with_memo(
3062            signer.num_keys(),
3063            sender,
3064            nonce,
3065            expiry,
3066            receiver,
3067            data,
3068            memo,
3069        )
3070        .sign(signer)
3071    }
3072
3073    /// Transfer the given amount from public to encrypted balance of the given
3074    /// account.
3075    #[deprecated(
3076        since = "5.0.1",
3077        note = "encrypted transfers are deprecated and partially removed since protocol version 7"
3078    )]
3079    pub fn transfer_to_encrypted(
3080        signer: &impl ExactSizeTransactionSigner,
3081        sender: AccountAddress,
3082        nonce: Nonce,
3083        expiry: TransactionTime,
3084        amount: Amount,
3085    ) -> AccountTransaction<EncodedPayload> {
3086        construct::transfer_to_encrypted(signer.num_keys(), sender, nonce, expiry, amount)
3087            .sign(signer)
3088    }
3089
3090    /// Transfer the given amount from encrypted to public balance of the given
3091    /// account.
3092    /// The payload may be constructed using
3093    /// [`make_sec_to_pub_transfer_data`][anchor]
3094    ///
3095    /// [anchor]: crate::encrypted_transfers::make_sec_to_pub_transfer_data
3096    pub fn transfer_to_public(
3097        signer: &impl ExactSizeTransactionSigner,
3098        sender: AccountAddress,
3099        nonce: Nonce,
3100        expiry: TransactionTime,
3101        data: SecToPubAmountTransferData<EncryptedAmountsCurve>,
3102    ) -> AccountTransaction<EncodedPayload> {
3103        construct::transfer_to_public(signer.num_keys(), sender, nonce, expiry, data).sign(signer)
3104    }
3105
3106    /// Construct a transfer with schedule transaction, sending to the given
3107    /// account.
3108    pub fn transfer_with_schedule(
3109        signer: &impl ExactSizeTransactionSigner,
3110        sender: AccountAddress,
3111        nonce: Nonce,
3112        expiry: TransactionTime,
3113        receiver: AccountAddress,
3114        schedule: Vec<(Timestamp, Amount)>,
3115    ) -> AccountTransaction<EncodedPayload> {
3116        construct::transfer_with_schedule(
3117            signer.num_keys(),
3118            sender,
3119            nonce,
3120            expiry,
3121            receiver,
3122            schedule,
3123        )
3124        .sign(signer)
3125    }
3126
3127    /// Construct a transfer with schedule and memo transaction, sending to the
3128    /// given account.
3129    pub fn transfer_with_schedule_and_memo(
3130        signer: &impl ExactSizeTransactionSigner,
3131        sender: AccountAddress,
3132        nonce: Nonce,
3133        expiry: TransactionTime,
3134        receiver: AccountAddress,
3135        schedule: Vec<(Timestamp, Amount)>,
3136        memo: Memo,
3137    ) -> AccountTransaction<EncodedPayload> {
3138        construct::transfer_with_schedule_and_memo(
3139            signer.num_keys(),
3140            sender,
3141            nonce,
3142            expiry,
3143            receiver,
3144            schedule,
3145            memo,
3146        )
3147        .sign(signer)
3148    }
3149
3150    /// Register the sender account as a baker.
3151    ///
3152    /// **Note that this transaction only applies to protocol versions 1-3.**
3153    /// Use [`configure_baker`](Self::configure_baker) instead for protocols
3154    /// after 4.
3155    #[deprecated(
3156        since = "2.0.0",
3157        note = "This transaction only applies to protocol versions 1-3. Use configure_baker \
3158                instead."
3159    )]
3160    #[doc(hidden)]
3161    pub fn add_baker(
3162        signer: &impl ExactSizeTransactionSigner,
3163        sender: AccountAddress,
3164        nonce: Nonce,
3165        expiry: TransactionTime,
3166        baking_stake: Amount,
3167        restake_earnings: bool,
3168        keys: BakerAddKeysPayload,
3169    ) -> AccountTransaction<EncodedPayload> {
3170        construct::add_baker(
3171            signer.num_keys(),
3172            sender,
3173            nonce,
3174            expiry,
3175            baking_stake,
3176            restake_earnings,
3177            keys,
3178        )
3179        .sign(signer)
3180    }
3181
3182    /// Update keys of the baker associated with the sender account.
3183    ///
3184    /// **Note that this transaction only applies to protocol versions 1-3.**
3185    /// Use [`configure_baker`](Self::configure_baker) instead for protocols
3186    /// after 4.
3187    #[deprecated(
3188        since = "2.0.0",
3189        note = "This transaction only applies to protocol versions 1-3. Use configure_baker \
3190                instead."
3191    )]
3192    #[doc(hidden)]
3193    pub fn update_baker_keys(
3194        signer: &impl ExactSizeTransactionSigner,
3195        sender: AccountAddress,
3196        nonce: Nonce,
3197        expiry: TransactionTime,
3198        keys: BakerUpdateKeysPayload,
3199    ) -> AccountTransaction<EncodedPayload> {
3200        construct::update_baker_keys(signer.num_keys(), sender, nonce, expiry, keys).sign(signer)
3201    }
3202
3203    /// Deregister the account as a baker.
3204    ///
3205    /// **Note that this transaction only applies to protocol versions 1-3.**
3206    /// Use [`configure_baker`](Self::configure_baker) instead for protocols
3207    /// after 4.
3208    #[deprecated(
3209        since = "2.0.0",
3210        note = "This transaction only applies to protocol versions 1-3. Use configure_baker \
3211                instead."
3212    )]
3213    #[doc(hidden)]
3214    pub fn remove_baker(
3215        signer: &impl ExactSizeTransactionSigner,
3216        sender: AccountAddress,
3217        nonce: Nonce,
3218        expiry: TransactionTime,
3219    ) -> AccountTransaction<EncodedPayload> {
3220        construct::remove_baker(signer.num_keys(), sender, nonce, expiry).sign(signer)
3221    }
3222
3223    /// Update the amount the account stakes for being a baker.
3224    #[deprecated(
3225        since = "2.0.0",
3226        note = "This transaction only applies to protocol versions 1-3. Use configure_baker \
3227                instead."
3228    )]
3229    #[doc(hidden)]
3230    pub fn update_baker_stake(
3231        signer: &impl ExactSizeTransactionSigner,
3232        sender: AccountAddress,
3233        nonce: Nonce,
3234        expiry: TransactionTime,
3235        new_stake: Amount,
3236    ) -> AccountTransaction<EncodedPayload> {
3237        construct::update_baker_stake(signer.num_keys(), sender, nonce, expiry, new_stake)
3238            .sign(signer)
3239    }
3240
3241    /// Update whether the earnings are automatically added to the baker's stake
3242    /// or not.
3243    #[deprecated(
3244        since = "2.0.0",
3245        note = "This transaction only applies to protocol versions 1-3. Use configure_baker \
3246                instead."
3247    )]
3248    #[doc(hidden)]
3249    pub fn update_baker_restake_earnings(
3250        signer: &impl ExactSizeTransactionSigner,
3251        sender: AccountAddress,
3252        nonce: Nonce,
3253        expiry: TransactionTime,
3254        restake_earnings: bool,
3255    ) -> AccountTransaction<EncodedPayload> {
3256        construct::update_baker_restake_earnings(
3257            signer.num_keys(),
3258            sender,
3259            nonce,
3260            expiry,
3261            restake_earnings,
3262        )
3263        .sign(signer)
3264    }
3265
3266    /// Configure the account as a baker. Only valid for protocol version 4 and
3267    /// up.
3268    pub fn configure_baker(
3269        signer: &impl ExactSizeTransactionSigner,
3270        sender: AccountAddress,
3271        nonce: Nonce,
3272        expiry: TransactionTime,
3273        payload: ConfigureBakerPayload,
3274    ) -> AccountTransaction<EncodedPayload> {
3275        construct::configure_baker(signer.num_keys(), sender, nonce, expiry, payload).sign(signer)
3276    }
3277
3278    /// Configure the account as a delegator. Only valid for protocol version 4
3279    /// and up.
3280    pub fn configure_delegation(
3281        signer: &impl ExactSizeTransactionSigner,
3282        sender: AccountAddress,
3283        nonce: Nonce,
3284        expiry: TransactionTime,
3285        payload: ConfigureDelegationPayload,
3286    ) -> AccountTransaction<EncodedPayload> {
3287        construct::configure_delegation(signer.num_keys(), sender, nonce, expiry, payload)
3288            .sign(signer)
3289    }
3290
3291    /// Construct a transaction to update keys of a single credential on an
3292    /// account. The transaction specific arguments are
3293    ///
3294    /// - `num_existing_credentials` - the number of existing credentials on the
3295    ///   account. This will affect the estimated transaction cost. It is safe
3296    ///   to over-approximate this.
3297    /// - `cred_id` - `credId` of a credential whose keys are to be updated.
3298    /// - `keys` - the new keys associated with the credential.
3299    pub fn update_credential_keys(
3300        signer: &impl ExactSizeTransactionSigner,
3301        sender: AccountAddress,
3302        nonce: Nonce,
3303        expiry: TransactionTime,
3304        num_existing_credentials: u16,
3305        cred_id: CredentialRegistrationID,
3306        keys: CredentialPublicKeys,
3307    ) -> AccountTransaction<EncodedPayload> {
3308        construct::update_credential_keys(
3309            signer.num_keys(),
3310            sender,
3311            nonce,
3312            expiry,
3313            num_existing_credentials,
3314            cred_id,
3315            keys,
3316        )
3317        .sign(signer)
3318    }
3319
3320    /// Construct a transaction to update credentials on an account.
3321    /// The transaction specific arguments are
3322    ///
3323    /// - `num_existing_credentials` - the number of existing credentials on the
3324    ///   account. This will affect the estimated transaction cost. It is safe
3325    ///   to over-approximate this.
3326    /// - `new_credentials` - the new credentials to be deployed to the account
3327    ///   with the desired indices. The credential with index 0 cannot be
3328    ///   replaced.
3329    /// - `remove_credentials` - the list of credentials, by `credId`'s, to be
3330    ///   removed
3331    /// - `new_threshold` - the new account threshold.
3332    #[allow(clippy::too_many_arguments)]
3333    pub fn update_credentials(
3334        signer: &impl ExactSizeTransactionSigner,
3335        sender: AccountAddress,
3336        nonce: Nonce,
3337        expiry: TransactionTime,
3338        num_existing_credentials: u16,
3339        new_credentials: AccountCredentialsMap,
3340        remove_credentials: Vec<CredentialRegistrationID>,
3341        new_threshold: AccountThreshold,
3342    ) -> AccountTransaction<EncodedPayload> {
3343        construct::update_credentials(
3344            signer.num_keys(),
3345            sender,
3346            nonce,
3347            expiry,
3348            num_existing_credentials,
3349            new_credentials,
3350            remove_credentials,
3351            new_threshold,
3352        )
3353        .sign(signer)
3354    }
3355
3356    /// Construct a transction to register the given piece of data.
3357    pub fn register_data(
3358        signer: &impl ExactSizeTransactionSigner,
3359        sender: AccountAddress,
3360        nonce: Nonce,
3361        expiry: TransactionTime,
3362        data: RegisteredData,
3363    ) -> AccountTransaction<EncodedPayload> {
3364        construct::register_data(signer.num_keys(), sender, nonce, expiry, data).sign(signer)
3365    }
3366
3367    /// Deploy the given Wasm module. The module is given as a binary source,
3368    /// and no processing is done to the module.
3369    pub fn deploy_module(
3370        signer: &impl ExactSizeTransactionSigner,
3371        sender: AccountAddress,
3372        nonce: Nonce,
3373        expiry: TransactionTime,
3374        module: smart_contracts::WasmModule,
3375    ) -> AccountTransaction<EncodedPayload> {
3376        construct::deploy_module(signer.num_keys(), sender, nonce, expiry, module).sign(signer)
3377    }
3378
3379    /// Initialize a smart contract, giving it the given amount of energy for
3380    /// execution. The unique parameters are
3381    /// - `energy` -- the amount of energy that can be used for contract
3382    ///   execution. The base energy amount for transaction verification will be
3383    ///   added to this cost.
3384    pub fn init_contract(
3385        signer: &impl ExactSizeTransactionSigner,
3386        sender: AccountAddress,
3387        nonce: Nonce,
3388        expiry: TransactionTime,
3389        payload: InitContractPayload,
3390        energy: Energy,
3391    ) -> AccountTransaction<EncodedPayload> {
3392        construct::init_contract(signer.num_keys(), sender, nonce, expiry, payload, energy)
3393            .sign(signer)
3394    }
3395
3396    /// Update a smart contract intance, giving it the given amount of energy
3397    /// for execution. The unique parameters are
3398    /// - `energy` -- the amount of energy that can be used for contract
3399    ///   execution. The base energy amount for transaction verification will be
3400    ///   added to this cost.
3401    pub fn update_contract(
3402        signer: &impl ExactSizeTransactionSigner,
3403        sender: AccountAddress,
3404        nonce: Nonce,
3405        expiry: TransactionTime,
3406        payload: UpdateContractPayload,
3407        energy: Energy,
3408    ) -> AccountTransaction<EncodedPayload> {
3409        construct::update_contract(signer.num_keys(), sender, nonce, expiry, payload, energy)
3410            .sign(signer)
3411    }
3412
3413    #[derive(Debug, Copy, Clone)]
3414    /// An upper bound on the amount of energy to spend on a transaction.
3415    /// Transaction costs have two components, one is based on the size of the
3416    /// transaction and the number of signatures, and then there is a
3417    /// transaction specific one. This construction helps handle the fixed
3418    /// costs and allows the user to focus only on the transaction specific
3419    /// ones. The most important case for this are smart contract
3420    /// initialisations and updates.
3421    pub enum GivenEnergy {
3422        /// Use this exact amount of energy.
3423        Absolute(Energy),
3424        /// Add the given amount of energy to the base amount.
3425        /// The base amount covers transaction size and signature checking.
3426        Add(Energy),
3427    }
3428
3429    /// A convenience wrapper around `sign_transaction` that construct the
3430    /// transaction and signs it. Compared to transaction-type-specific wrappers
3431    /// above this allows selecting the amount of energy
3432    pub fn make_and_sign_transaction(
3433        signer: &impl ExactSizeTransactionSigner,
3434        sender: AccountAddress,
3435        nonce: Nonce,
3436        expiry: TransactionTime,
3437        energy: GivenEnergy,
3438        payload: Payload,
3439    ) -> AccountTransaction<EncodedPayload> {
3440        match energy {
3441            GivenEnergy::Absolute(energy) => construct::make_transaction(
3442                sender,
3443                nonce,
3444                expiry,
3445                construct::GivenEnergy::Absolute(energy),
3446                payload,
3447            )
3448            .sign(signer),
3449            GivenEnergy::Add(energy) => construct::make_transaction(
3450                sender,
3451                nonce,
3452                expiry,
3453                construct::GivenEnergy::Add {
3454                    energy,
3455                    num_sigs: signer.num_keys(),
3456                },
3457                payload,
3458            )
3459            .sign(signer),
3460        }
3461    }
3462}
3463
3464#[cfg(test)]
3465mod tests {
3466    use crate::{
3467        hashes::TransactionSignHash,
3468        id::types::{SignatureThreshold, VerifyKey},
3469    };
3470    use rand::Rng;
3471    use std::convert::TryFrom;
3472
3473    use super::*;
3474    #[test]
3475    fn test_transaction_signature_check() {
3476        let mut rng = rand::thread_rng();
3477        let mut keys = BTreeMap::<CredentialIndex, BTreeMap<KeyIndex, KeyPair>>::new();
3478        let bound: usize = rng.gen_range(1..20);
3479        for _ in 0..bound {
3480            let c_idx = CredentialIndex::from(rng.gen::<u8>());
3481            if keys.get(&c_idx).is_none() {
3482                let inner_bound: usize = rng.gen_range(1..20);
3483                let mut cred_keys = BTreeMap::new();
3484                for _ in 0..inner_bound {
3485                    let k_idx = KeyIndex::from(rng.gen::<u8>());
3486                    cred_keys.insert(k_idx, KeyPair::generate(&mut rng));
3487                }
3488                keys.insert(c_idx, cred_keys);
3489            }
3490        }
3491        let hash = TransactionSignHash::new(rng.gen());
3492        let sig = keys.sign_transaction_hash(&hash);
3493        let threshold =
3494            AccountThreshold::try_from(rng.gen_range(1..(keys.len() + 1) as u8)).unwrap();
3495        let pub_keys = keys
3496            .iter()
3497            .map(|(&ci, keys)| {
3498                let threshold =
3499                    SignatureThreshold::try_from(rng.gen_range(1..keys.len() + 1) as u8).unwrap();
3500                let keys = keys
3501                    .iter()
3502                    .map(|(&ki, kp)| (ki, VerifyKey::from(kp)))
3503                    .collect();
3504                (ci, CredentialPublicKeys { keys, threshold })
3505            })
3506            .collect::<BTreeMap<_, _>>();
3507        let mut access_structure = AccountAccessStructure {
3508            threshold,
3509            keys: pub_keys,
3510        };
3511        assert!(
3512            verify_signature_transaction_sign_hash(&access_structure, &hash, &sig),
3513            "Transaction signature must validate."
3514        );
3515
3516        access_structure.threshold = AccountThreshold::try_from((keys.len() + 1) as u8).unwrap();
3517
3518        assert!(
3519            !verify_signature_transaction_sign_hash(&access_structure, &hash, &sig),
3520            "Transaction signature must not validate with invalid threshold."
3521        );
3522    }
3523}