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