1#![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)]
41pub struct Memo {
43 #[serde(with = "crate::internal::byte_array_hex")]
44 #[size_length = 2]
45 bytes: Vec<u8>,
46}
47
48#[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")]
78pub enum TransactionType {
82 DeployModule,
84 InitContract,
86 Update,
88 Transfer,
90
91 #[deprecated(
93 since = "5.0.1",
94 note = "baking is changed in protocol 4, use ConfigureBaker or ConfigureDelegation instead"
95 )]
96 AddBaker,
97
98 #[deprecated(
100 since = "5.0.1",
101 note = "baking is changed in protocol 4, use ConfigureBaker or ConfigureDelegation instead"
102 )]
103 RemoveBaker,
104
105 #[deprecated(
107 since = "5.0.1",
108 note = "baking is changed in protocol 4, use ConfigureBaker or ConfigureDelegation instead"
109 )]
110 UpdateBakerStake,
111
112 #[deprecated(
114 since = "5.0.1",
115 note = "baking is changed in protocol 4, use ConfigureBaker or ConfigureDelegation instead"
116 )]
117 UpdateBakerRestakeEarnings,
118
119 #[deprecated(
121 since = "5.0.1",
122 note = "baking is changed in protocol 4, use ConfigureBaker or ConfigureDelegation instead"
123 )]
124 UpdateBakerKeys,
125
126 UpdateCredentialKeys,
128
129 #[deprecated(
131 since = "5.0.1",
132 note = "encrypted transfers are deprecated and partially removed since protocol version 7"
133 )]
134 EncryptedAmountTransfer,
135
136 #[deprecated(
138 since = "5.0.1",
139 note = "encrypted transfers are deprecated and partially removed since protocol version 7"
140 )]
141 TransferToEncrypted,
142
143 TransferToPublic,
145 TransferWithSchedule,
147 UpdateCredentials,
149 RegisterData,
151 TransferWithMemo,
153
154 #[deprecated(
156 since = "5.0.1",
157 note = "encrypted transfers are deprecated and partially removed since protocol version 7"
158 )]
159 EncryptedAmountTransferWithMemo,
160
161 TransferWithScheduleAndMemo,
163 ConfigureBaker,
165 ConfigureDelegation,
167}
168
169#[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)]
210pub 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")]
228pub struct TransactionHeader {
231 pub sender: AccountAddress,
233 pub nonce: Nonce,
235 pub energy_amount: Energy,
237 pub payload_size: PayloadSize,
240 pub expiry: TransactionTime,
242}
243
244#[derive(Debug, Clone, SerdeSerialize, SerdeDeserialize, Into, AsRef)]
245#[serde(transparent)]
246pub 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 pub fn decode(&self) -> ParseResult<Payload> {
284 let mut source = std::io::Cursor::new(&self.payload);
285 let payload = source.get()?;
286 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
297impl 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
306pub fn get_encoded_payload<R: ReadBytesExt>(
308 source: &mut R,
309 len: PayloadSize,
310) -> ParseResult<EncodedPayload> {
311 let payload = crate::common::deserial_bytes(source, u32::from(len) as usize)?;
314 Ok(EncodedPayload { payload })
315}
316
317pub trait PayloadLike {
320 fn encode(&self) -> EncodedPayload;
322 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")]
339pub 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 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 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#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
404pub enum AddBakerKeysMarker {}
405
406#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
409pub enum UpdateBakerKeysMarker {}
410
411#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
414pub enum ConfigureBakerKeysMarker {}
415
416#[derive(Debug, Clone, SerdeDeserialize, SerdeSerialize)]
417#[serde(rename_all = "camelCase")]
418pub struct BakerKeysPayload<V> {
425 #[serde(skip)] phantom: PhantomData<V>,
427 pub election_verify_key: BakerElectionVerifyKey,
429 pub signature_verify_key: BakerSignatureVerifyKey,
431 pub aggregation_verify_key: BakerAggregationVerifyKey,
434 pub proof_sig: crate::eddsa_ed25519::Ed25519DlogProof,
437 pub proof_election: crate::eddsa_ed25519::Ed25519DlogProof,
439 pub proof_aggregation: crate::aggregate_sig::Proof<AggregateSigPairing>,
442}
443
444pub type BakerAddKeysPayload = BakerKeysPayload<AddBakerKeysMarker>;
446pub type BakerUpdateKeysPayload = BakerKeysPayload<UpdateBakerKeysMarker>;
449
450pub type ConfigureBakerKeysPayload = BakerKeysPayload<ConfigureBakerKeysMarker>;
453
454impl<T> BakerKeysPayload<T> {
455 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 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 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 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")]
532pub struct AddBakerPayload {
535 #[serde(flatten)]
537 pub keys: BakerAddKeysPayload,
538 pub baking_stake: Amount,
540 pub restake_earnings: bool,
542}
543
544#[derive(Debug, Clone, SerdeDeserialize, SerdeSerialize)]
545#[serde(rename_all = "camelCase")]
546pub struct InitContractPayload {
548 pub amount: Amount,
550 pub mod_ref: concordium_contracts_common::ModuleReference,
552 pub init_name: smart_contracts::OwnedContractName,
554 pub param: smart_contracts::OwnedParameter,
556}
557
558impl InitContractPayload {
559 pub fn size(&self) -> usize {
561 8 + 32 + 2 + self.init_name.as_contract_name().get_chain_name().len() +
565 2 + self.param.as_ref().len()
567 }
568}
569
570#[derive(Debug, Clone, SerdeDeserialize, SerdeSerialize)]
571#[serde(rename_all = "camelCase")]
572pub struct UpdateContractPayload {
574 pub amount: Amount,
577 pub address: ContractAddress,
579 pub receive_name: smart_contracts::OwnedReceiveName,
581 pub message: smart_contracts::OwnedParameter,
583}
584
585impl UpdateContractPayload {
586 pub fn size(&self) -> usize {
588 8 + 16 + 2 + self.receive_name.as_receive_name().get_chain_name().len() +
592 2 + self.message.as_ref().len()
594 }
595}
596
597#[derive(Debug, Clone, SerdeDeserialize, SerdeSerialize, Default)]
598#[serde(rename_all = "camelCase")]
599pub struct ConfigureBakerPayload {
604 pub capital: Option<Amount>,
606 pub restake_earnings: Option<bool>,
608 pub open_for_delegation: Option<OpenStatus>,
610 pub keys_with_proofs: Option<ConfigureBakerKeysPayload>,
612 pub metadata_url: Option<UrlText>,
614 pub transaction_fee_commission: Option<AmountFraction>,
616 pub baking_reward_commission: Option<AmountFraction>,
618 pub finalization_reward_commission: Option<AmountFraction>,
620 pub suspend: Option<bool>,
622}
623
624impl ConfigureBakerPayload {
625 pub fn new() -> Self { Self::default() }
626
627 pub fn new_remove_baker() -> Self {
629 Self {
630 capital: Some(Amount::from_micro_ccd(0)),
631 ..Self::new()
632 }
633 }
634
635 pub fn set_capital(&mut self, amount: Amount) -> &mut Self {
637 self.capital = Some(amount);
638 self
639 }
640
641 pub fn set_restake_earnings(&mut self, restake_earnings: bool) -> &mut Self {
643 self.restake_earnings = Some(restake_earnings);
644 self
645 }
646
647 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 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 pub fn set_metadata_url(&mut self, metadata_url: UrlText) -> &mut Self {
669 self.metadata_url = Some(metadata_url);
670 self
671 }
672
673 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 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 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")]
708pub struct ConfigureDelegationPayload {
711 pub capital: Option<Amount>,
713 pub restake_earnings: Option<bool>,
715 pub delegation_target: Option<DelegationTarget>,
717}
718
719impl ConfigureDelegationPayload {
720 pub fn new() -> Self { Self::default() }
722
723 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)]
749pub struct RegisteredData {
751 #[serde(with = "crate::internal::byte_array_hex")]
752 #[size_length = 2]
753 bytes: Vec<u8>,
754}
755
756#[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
804pub 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")]
817pub enum Payload {
819 DeployModule {
821 #[serde(rename = "mod")]
822 module: smart_contracts::WasmModule,
823 },
824 InitContract {
826 #[serde(flatten)]
827 payload: InitContractPayload,
828 },
829 Update {
831 #[serde(flatten)]
832 payload: UpdateContractPayload,
833 },
834 Transfer {
836 to_address: AccountAddress,
838 amount: Amount,
840 },
841 AddBaker {
843 #[serde(flatten)]
844 payload: Box<AddBakerPayload>,
845 },
846 RemoveBaker,
848 UpdateBakerStake {
850 stake: Amount,
852 },
853 UpdateBakerRestakeEarnings {
855 restake_earnings: bool,
857 },
858 UpdateBakerKeys {
860 #[serde(flatten)]
861 payload: Box<BakerUpdateKeysPayload>,
862 },
863 UpdateCredentialKeys {
865 cred_id: CredentialRegistrationID,
867 keys: CredentialPublicKeys,
869 },
870 #[deprecated(
872 since = "5.0.1",
873 note = "encrypted transfers are deprecated and partially removed since protocol version 7"
874 )]
875 EncryptedAmountTransfer {
876 to: AccountAddress,
878 data: Box<EncryptedAmountTransferData<EncryptedAmountsCurve>>,
881 },
882 #[deprecated(
884 since = "5.0.1",
885 note = "encrypted transfers are deprecated and partially removed since protocol version 7"
886 )]
887 TransferToEncrypted {
888 amount: Amount,
890 },
891 TransferToPublic {
893 #[serde(flatten)]
895 data: Box<SecToPubAmountTransferData<EncryptedAmountsCurve>>,
896 },
897 TransferWithSchedule {
899 to: AccountAddress,
901 schedule: Vec<(Timestamp, Amount)>,
903 },
904 UpdateCredentials {
906 new_cred_infos: AccountCredentialsMap,
908 remove_cred_ids: Vec<CredentialRegistrationID>,
910 new_threshold: AccountThreshold,
912 },
913 RegisterData {
915 data: RegisteredData,
917 },
918 TransferWithMemo {
920 to_address: AccountAddress,
922 memo: Memo,
924 amount: Amount,
926 },
927 #[deprecated(
929 since = "5.0.1",
930 note = "encrypted transfers are deprecated and partially removed since protocol version 7"
931 )]
932 EncryptedAmountTransferWithMemo {
933 to: AccountAddress,
935 memo: Memo,
937 data: Box<EncryptedAmountTransferData<EncryptedAmountsCurve>>,
940 },
941 TransferWithScheduleAndMemo {
943 to: AccountAddress,
945 memo: Memo,
947 schedule: Vec<(Timestamp, Amount)>,
949 },
950 ConfigureBaker {
952 #[serde(flatten)]
953 data: Box<ConfigureBakerPayload>,
954 },
955 ConfigureDelegation {
957 #[serde(flatten)]
958 data: ConfigureDelegationPayload,
959 },
960}
961
962impl Payload {
963 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 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 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
1395pub 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
1406pub trait TransactionSigner {
1408 fn sign_transaction_hash(
1411 &self,
1412 hash_to_sign: &hashes::TransactionSignHash,
1413 ) -> TransactionSignature;
1414}
1415
1416pub trait ExactSizeTransactionSigner: TransactionSigner {
1418 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
1449impl 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
1522pub 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
1537pub trait HasAccountAccessStructure {
1540 fn threshold(&self) -> AccountThreshold;
1542 fn credential_keys(&self, idx: CredentialIndex) -> Option<&CredentialPublicKeys>;
1544}
1545
1546#[derive(PartialEq, Eq, Debug, Clone, concordium_std::Serialize)]
1547pub struct AccountAccessStructure {
1550 #[concordium(size_length = 1)]
1552 pub keys: BTreeMap<CredentialIndex, CredentialPublicKeys>,
1553 pub threshold: AccountThreshold,
1555}
1556
1557pub type AccountStructure<'a> = &'a [(
1560 CredentialIndex,
1561 SignatureThreshold,
1562 &'a [(KeyIndex, ed25519_dalek::VerifyingKey)],
1563)];
1564
1565impl AccountAccessStructure {
1566 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 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
1640impl 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
1653impl 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 pub fn num_keys(&self) -> u32 { self.keys.values().map(|m| m.keys.len() as u32).sum() }
1667}
1668
1669pub 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
1679pub 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 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)]
1716pub enum BlockItem<PayloadType> {
1720 AccountTransaction(AccountTransaction<PayloadType>),
1723 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 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
1918pub mod cost {
1920 use crate::id::types::CredentialType;
1921
1922 use super::*;
1923
1924 pub const A: u64 = 100;
1927
1928 pub const B: u64 = 1;
1931
1932 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 pub const SIMPLE_TRANSFER: Energy = Energy { energy: 300 };
1941
1942 #[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 #[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 pub const TRANSFER_TO_PUBLIC: Energy = Energy { energy: 14850 };
1958
1959 pub fn scheduled_transfer(num_releases: u16) -> Energy {
1961 Energy::from(u64::from(num_releases) * (300 + 64))
1962 }
1963
1964 pub const ADD_BAKER: Energy = Energy { energy: 4050 };
1966
1967 pub const UPDATE_BAKER_KEYS: Energy = Energy { energy: 4050 };
1969
1970 pub const UPDATE_BAKER_STAKE: Energy = Energy { energy: 300 };
1973
1974 pub const UPDATE_BAKER_RESTAKE: Energy = Energy { energy: 300 };
1976
1977 pub const REMOVE_BAKER: Energy = Energy { energy: 300 };
1979
1980 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 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 pub const REGISTER_DATA: Energy = Energy { energy: 300 };
1999
2000 pub const CONFIGURE_BAKER_WITH_KEYS: Energy = Energy { energy: 4050 };
2002
2003 pub const CONFIGURE_BAKER_WITHOUT_KEYS: Energy = Energy { energy: 300 };
2006
2007 pub const CONFIGURE_DELEGATION: Energy = Energy { energy: 300 };
2009
2010 pub fn deploy_module(module_size: u64) -> Energy { Energy::from(module_size / 10) }
2014
2015 const UPDATE_CREDENTIALS_BASE: Energy = Energy { energy: 500 };
2021
2022 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 fn update_credentials_variable(num_credentials_before: u16, num_keys: &[u16]) -> Energy {
2034 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
2048pub mod construct {
2053 use super::*;
2054
2055 #[derive(Debug, Clone, SerdeSerialize)]
2059 #[serde(rename_all = "camelCase")]
2060 pub struct PreAccountTransaction {
2061 pub header: TransactionHeader,
2062 pub payload: Payload,
2064 #[serde(skip_serializing)]
2068 pub encoded: EncodedPayload,
2069 pub hash_to_sign: hashes::TransactionSignHash,
2071 }
2072
2073 impl PreAccountTransaction {
2074 pub fn sign(self, signer: &impl TransactionSigner) -> AccountTransaction<EncodedPayload> {
2079 sign_transaction(signer, self.header, self.encoded)
2080 }
2081 }
2082
2083 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 struct TransactionBuilder {
2119 header: TransactionHeader,
2120 payload: Payload,
2121 encoded: EncodedPayload,
2122 }
2123
2124 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 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 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 #[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 #[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 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 #[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 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 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 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 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 #[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 #[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 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 #[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 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 #[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 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 #[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 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 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 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 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 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 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 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 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 #[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 pub enum GivenEnergy {
2786 Absolute(Energy),
2788 Add { energy: Energy, num_sigs: u32 },
2791 }
2792
2793 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
2812pub mod send {
2816 use super::*;
2817
2818 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 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 #[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 #[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 #[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 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 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 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 #[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 #[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 #[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 #[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 #[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 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 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 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 #[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 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 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 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 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 pub enum GivenEnergy {
3247 Absolute(Energy),
3249 Add(Energy),
3252 }
3253
3254 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}