1use std::{
7 fmt::{self, Display},
8 hash::{Hash, Hasher},
9 marker::PhantomData,
10 str::FromStr,
11};
12
13use anyhow::{anyhow, Context};
14use async_graphql::SimpleObject;
15use custom_debug_derive::Debug;
16use linera_witty::{WitLoad, WitStore, WitType};
17use serde::{Deserialize, Deserializer, Serialize, Serializer};
18
19use crate::{
20 bcs_scalar,
21 crypto::{
22 AccountPublicKey, BcsHashable, CryptoError, CryptoHash, Ed25519PublicKey, EvmPublicKey,
23 Secp256k1PublicKey,
24 },
25 data_types::BlockHeight,
26 doc_scalar, hex_debug,
27 vm::VmRuntime,
28};
29
30#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, WitLoad, WitStore, WitType)]
32#[cfg_attr(with_testing, derive(test_strategy::Arbitrary))]
33pub enum AccountOwner {
34 Reserved(u8),
36 Address32(CryptoHash),
38 Address20([u8; 20]),
40}
41
42impl AccountOwner {
43 pub const CHAIN: AccountOwner = AccountOwner::Reserved(0);
45
46 pub fn is_chain(&self) -> bool {
48 self == &AccountOwner::CHAIN
49 }
50}
51
52#[cfg(with_testing)]
53impl From<CryptoHash> for AccountOwner {
54 fn from(address: CryptoHash) -> Self {
55 AccountOwner::Address32(address)
56 }
57}
58
59#[derive(
61 Debug, PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize, WitLoad, WitStore, WitType,
62)]
63pub struct Account {
64 pub chain_id: ChainId,
66 pub owner: AccountOwner,
68}
69
70impl Account {
71 pub fn new(chain_id: ChainId, owner: AccountOwner) -> Self {
73 Self { chain_id, owner }
74 }
75
76 pub fn chain(chain_id: ChainId) -> Self {
78 Account {
79 chain_id,
80 owner: AccountOwner::CHAIN,
81 }
82 }
83}
84
85impl Display for Account {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 write!(f, "{}:{}", self.chain_id, self.owner)
88 }
89}
90
91impl FromStr for Account {
92 type Err = anyhow::Error;
93
94 fn from_str(string: &str) -> Result<Self, Self::Err> {
95 let mut parts = string.splitn(2, ':');
96
97 let chain_id = parts
98 .next()
99 .context(
100 "Expecting an account formatted as `chain-id` or `chain-id:owner-type:address`",
101 )?
102 .parse()?;
103
104 if let Some(owner_string) = parts.next() {
105 let owner = owner_string.parse::<AccountOwner>()?;
106 Ok(Account::new(chain_id, owner))
107 } else {
108 Ok(Account::chain(chain_id))
109 }
110 }
111}
112
113#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Debug, Serialize, Deserialize)]
115pub enum ChainDescription {
116 Root(u32),
118 Child(MessageId),
120}
121
122impl ChainDescription {
123 pub fn is_child(&self) -> bool {
125 matches!(self, ChainDescription::Child(_))
126 }
127}
128
129#[derive(
132 Eq,
133 PartialEq,
134 Ord,
135 PartialOrd,
136 Copy,
137 Clone,
138 Hash,
139 Serialize,
140 Deserialize,
141 WitLoad,
142 WitStore,
143 WitType,
144)]
145#[cfg_attr(with_testing, derive(test_strategy::Arbitrary))]
146#[cfg_attr(with_testing, derive(Default))]
147pub struct ChainId(pub CryptoHash);
148
149#[derive(
152 Eq,
153 PartialEq,
154 Ord,
155 PartialOrd,
156 Clone,
157 Copy,
158 Hash,
159 Debug,
160 Serialize,
161 Deserialize,
162 WitType,
163 WitStore,
164 WitLoad,
165 Default,
166)]
167#[cfg_attr(with_testing, derive(test_strategy::Arbitrary))]
168pub enum BlobType {
169 #[default]
171 Data,
172 ContractBytecode,
174 ServiceBytecode,
176 EvmBytecode,
178 ApplicationDescription,
180 Committee,
182}
183
184impl Display for BlobType {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 write!(f, "{:?}", self)
187 }
188}
189
190impl FromStr for BlobType {
191 type Err = anyhow::Error;
192
193 fn from_str(s: &str) -> Result<Self, Self::Err> {
194 serde_json::from_str(&format!("\"{s}\""))
195 .with_context(|| format!("Invalid BlobType: {}", s))
196 }
197}
198
199#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash, Debug, WitType, WitStore, WitLoad)]
201#[cfg_attr(with_testing, derive(test_strategy::Arbitrary, Default))]
202pub struct BlobId {
203 pub blob_type: BlobType,
205 pub hash: CryptoHash,
207}
208
209impl BlobId {
210 pub fn new(hash: CryptoHash, blob_type: BlobType) -> Self {
212 Self { hash, blob_type }
213 }
214}
215
216impl Display for BlobId {
217 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218 write!(f, "{}:{}", self.blob_type, self.hash)?;
219 Ok(())
220 }
221}
222
223impl FromStr for BlobId {
224 type Err = anyhow::Error;
225
226 fn from_str(s: &str) -> Result<Self, Self::Err> {
227 let parts = s.split(':').collect::<Vec<_>>();
228 if parts.len() == 2 {
229 let blob_type = BlobType::from_str(parts[0]).context("Invalid BlobType!")?;
230 Ok(BlobId {
231 hash: CryptoHash::from_str(parts[1]).context("Invalid hash!")?,
232 blob_type,
233 })
234 } else {
235 Err(anyhow!("Invalid blob ID: {}", s))
236 }
237 }
238}
239
240#[derive(Serialize, Deserialize)]
241#[serde(rename = "BlobId")]
242struct BlobIdHelper {
243 hash: CryptoHash,
244 blob_type: BlobType,
245}
246
247impl Serialize for BlobId {
248 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
249 where
250 S: Serializer,
251 {
252 if serializer.is_human_readable() {
253 serializer.serialize_str(&self.to_string())
254 } else {
255 let helper = BlobIdHelper {
256 hash: self.hash,
257 blob_type: self.blob_type,
258 };
259 helper.serialize(serializer)
260 }
261 }
262}
263
264impl<'a> Deserialize<'a> for BlobId {
265 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
266 where
267 D: Deserializer<'a>,
268 {
269 if deserializer.is_human_readable() {
270 let s = String::deserialize(deserializer)?;
271 Self::from_str(&s).map_err(serde::de::Error::custom)
272 } else {
273 let helper = BlobIdHelper::deserialize(deserializer)?;
274 Ok(BlobId::new(helper.hash, helper.blob_type))
275 }
276 }
277}
278
279#[derive(
281 Eq,
282 PartialEq,
283 Ord,
284 PartialOrd,
285 Copy,
286 Clone,
287 Hash,
288 Debug,
289 Serialize,
290 Deserialize,
291 WitLoad,
292 WitStore,
293 WitType,
294)]
295#[cfg_attr(with_testing, derive(Default, test_strategy::Arbitrary))]
296pub struct MessageId {
297 pub chain_id: ChainId,
299 pub height: BlockHeight,
301 pub index: u32,
303}
304
305#[derive(Debug, WitLoad, WitStore, WitType)]
307#[cfg_attr(with_testing, derive(Default, test_strategy::Arbitrary))]
308pub struct ApplicationId<A = ()> {
309 pub application_description_hash: CryptoHash,
311 #[witty(skip)]
312 #[debug(skip)]
313 _phantom: PhantomData<A>,
314}
315
316#[derive(
318 Eq,
319 PartialEq,
320 Ord,
321 PartialOrd,
322 Copy,
323 Clone,
324 Hash,
325 Debug,
326 Serialize,
327 Deserialize,
328 WitLoad,
329 WitStore,
330 WitType,
331)]
332pub enum GenericApplicationId {
333 System,
335 User(ApplicationId),
337}
338
339impl GenericApplicationId {
340 pub fn user_application_id(&self) -> Option<&ApplicationId> {
342 if let GenericApplicationId::User(app_id) = self {
343 Some(app_id)
344 } else {
345 None
346 }
347 }
348}
349
350impl<A> From<ApplicationId<A>> for AccountOwner {
351 fn from(app_id: ApplicationId<A>) -> Self {
352 AccountOwner::Address32(app_id.application_description_hash)
353 }
354}
355
356impl From<AccountPublicKey> for AccountOwner {
357 fn from(public_key: AccountPublicKey) -> Self {
358 match public_key {
359 AccountPublicKey::Ed25519(public_key) => public_key.into(),
360 AccountPublicKey::Secp256k1(public_key) => public_key.into(),
361 AccountPublicKey::EvmSecp256k1(public_key) => public_key.into(),
362 }
363 }
364}
365
366impl From<ApplicationId> for GenericApplicationId {
367 fn from(application_id: ApplicationId) -> Self {
368 GenericApplicationId::User(application_id)
369 }
370}
371
372impl From<Secp256k1PublicKey> for AccountOwner {
373 fn from(public_key: Secp256k1PublicKey) -> Self {
374 AccountOwner::Address32(CryptoHash::new(&public_key))
375 }
376}
377
378impl From<Ed25519PublicKey> for AccountOwner {
379 fn from(public_key: Ed25519PublicKey) -> Self {
380 AccountOwner::Address32(CryptoHash::new(&public_key))
381 }
382}
383
384impl From<EvmPublicKey> for AccountOwner {
385 fn from(public_key: EvmPublicKey) -> Self {
386 AccountOwner::Address20(alloy_primitives::Address::from_public_key(&public_key.0).into())
387 }
388}
389
390#[derive(Debug, WitLoad, WitStore, WitType)]
392#[cfg_attr(with_testing, derive(Default, test_strategy::Arbitrary))]
393pub struct ModuleId<Abi = (), Parameters = (), InstantiationArgument = ()> {
394 pub contract_blob_hash: CryptoHash,
396 pub service_blob_hash: CryptoHash,
398 pub vm_runtime: VmRuntime,
400 #[witty(skip)]
401 #[debug(skip)]
402 _phantom: PhantomData<(Abi, Parameters, InstantiationArgument)>,
403}
404
405#[derive(
407 Clone,
408 Debug,
409 Eq,
410 Hash,
411 Ord,
412 PartialEq,
413 PartialOrd,
414 Serialize,
415 Deserialize,
416 WitLoad,
417 WitStore,
418 WitType,
419)]
420pub struct ChannelName(
421 #[serde(with = "serde_bytes")]
422 #[debug(with = "hex_debug")]
423 Vec<u8>,
424);
425
426#[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
427pub struct ChannelFullName {
429 pub application_id: ApplicationId,
431 pub name: ChannelName,
433}
434
435impl fmt::Display for ChannelFullName {
436 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
437 let name = hex::encode(&self.name);
438 let app_id = self.application_id;
439 write!(f, "user channel {name} for app {app_id}")
440 }
441}
442
443impl ChannelFullName {
444 pub fn new(name: ChannelName, application_id: ApplicationId) -> Self {
446 Self {
447 application_id,
448 name,
449 }
450 }
451}
452
453#[derive(
455 Clone,
456 Debug,
457 Eq,
458 Hash,
459 Ord,
460 PartialEq,
461 PartialOrd,
462 Serialize,
463 Deserialize,
464 WitLoad,
465 WitStore,
466 WitType,
467)]
468pub struct StreamName(
469 #[serde(with = "serde_bytes")]
470 #[debug(with = "hex_debug")]
471 pub Vec<u8>,
472);
473
474impl<T> From<T> for StreamName
475where
476 T: Into<Vec<u8>>,
477{
478 fn from(name: T) -> Self {
479 StreamName(name.into())
480 }
481}
482
483#[derive(
485 Clone,
486 Debug,
487 Eq,
488 Hash,
489 Ord,
490 PartialEq,
491 PartialOrd,
492 Serialize,
493 Deserialize,
494 WitLoad,
495 WitStore,
496 WitType,
497 SimpleObject,
498)]
499pub struct StreamId {
500 pub application_id: GenericApplicationId,
502 pub stream_name: StreamName,
504}
505
506impl StreamId {
507 pub fn system(name: impl Into<StreamName>) -> Self {
509 StreamId {
510 application_id: GenericApplicationId::System,
511 stream_name: name.into(),
512 }
513 }
514}
515
516#[derive(
518 Debug,
519 PartialEq,
520 Eq,
521 Hash,
522 Clone,
523 Serialize,
524 Deserialize,
525 WitLoad,
526 WitStore,
527 WitType,
528 SimpleObject,
529)]
530pub struct EventId {
531 pub chain_id: ChainId,
533 pub stream_id: StreamId,
535 pub index: u32,
537}
538
539#[derive(
541 Clone,
542 Debug,
543 Eq,
544 Hash,
545 Ord,
546 PartialEq,
547 PartialOrd,
548 Serialize,
549 Deserialize,
550 WitLoad,
551 WitStore,
552 WitType,
553)]
554pub enum Destination {
555 Recipient(ChainId),
557 Subscribers(ChannelName),
559}
560
561impl Destination {
562 pub fn is_channel(&self) -> bool {
564 matches!(self, Destination::Subscribers(_))
565 }
566
567 pub fn recipient(&self) -> Option<ChainId> {
569 match self {
570 Destination::Recipient(chain_id) => Some(*chain_id),
571 Destination::Subscribers(_) => None,
572 }
573 }
574}
575
576impl From<ChainId> for Destination {
577 fn from(chain_id: ChainId) -> Self {
578 Destination::Recipient(chain_id)
579 }
580}
581
582impl From<ChannelName> for Destination {
583 fn from(channel_name: ChannelName) -> Self {
584 Destination::Subscribers(channel_name)
585 }
586}
587
588impl AsRef<[u8]> for ChannelName {
589 fn as_ref(&self) -> &[u8] {
590 &self.0
591 }
592}
593
594impl From<Vec<u8>> for ChannelName {
595 fn from(name: Vec<u8>) -> Self {
596 ChannelName(name)
597 }
598}
599
600impl ChannelName {
601 pub fn into_bytes(self) -> Vec<u8> {
603 self.0
604 }
605}
606
607impl StreamName {
608 pub fn into_bytes(self) -> Vec<u8> {
610 self.0
611 }
612}
613
614impl<Abi, Parameters, InstantiationArgument> Clone
616 for ModuleId<Abi, Parameters, InstantiationArgument>
617{
618 fn clone(&self) -> Self {
619 *self
620 }
621}
622
623impl<Abi, Parameters, InstantiationArgument> Copy
624 for ModuleId<Abi, Parameters, InstantiationArgument>
625{
626}
627
628impl<Abi, Parameters, InstantiationArgument> PartialEq
629 for ModuleId<Abi, Parameters, InstantiationArgument>
630{
631 fn eq(&self, other: &Self) -> bool {
632 let ModuleId {
633 contract_blob_hash,
634 service_blob_hash,
635 vm_runtime,
636 _phantom,
637 } = other;
638 self.contract_blob_hash == *contract_blob_hash
639 && self.service_blob_hash == *service_blob_hash
640 && self.vm_runtime == *vm_runtime
641 }
642}
643
644impl<Abi, Parameters, InstantiationArgument> Eq
645 for ModuleId<Abi, Parameters, InstantiationArgument>
646{
647}
648
649impl<Abi, Parameters, InstantiationArgument> PartialOrd
650 for ModuleId<Abi, Parameters, InstantiationArgument>
651{
652 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
653 Some(self.cmp(other))
654 }
655}
656
657impl<Abi, Parameters, InstantiationArgument> Ord
658 for ModuleId<Abi, Parameters, InstantiationArgument>
659{
660 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
661 let ModuleId {
662 contract_blob_hash,
663 service_blob_hash,
664 vm_runtime,
665 _phantom,
666 } = other;
667 (
668 self.contract_blob_hash,
669 self.service_blob_hash,
670 self.vm_runtime,
671 )
672 .cmp(&(*contract_blob_hash, *service_blob_hash, *vm_runtime))
673 }
674}
675
676impl<Abi, Parameters, InstantiationArgument> Hash
677 for ModuleId<Abi, Parameters, InstantiationArgument>
678{
679 fn hash<H: Hasher>(&self, state: &mut H) {
680 let ModuleId {
681 contract_blob_hash: contract_blob_id,
682 service_blob_hash: service_blob_id,
683 vm_runtime: vm_runtime_id,
684 _phantom,
685 } = self;
686 contract_blob_id.hash(state);
687 service_blob_id.hash(state);
688 vm_runtime_id.hash(state);
689 }
690}
691
692#[derive(Serialize, Deserialize)]
693#[serde(rename = "ModuleId")]
694struct SerializableModuleId {
695 contract_blob_hash: CryptoHash,
696 service_blob_hash: CryptoHash,
697 vm_runtime: VmRuntime,
698}
699
700impl<Abi, Parameters, InstantiationArgument> Serialize
701 for ModuleId<Abi, Parameters, InstantiationArgument>
702{
703 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
704 where
705 S: serde::ser::Serializer,
706 {
707 let serializable_module_id = SerializableModuleId {
708 contract_blob_hash: self.contract_blob_hash,
709 service_blob_hash: self.service_blob_hash,
710 vm_runtime: self.vm_runtime,
711 };
712 if serializer.is_human_readable() {
713 let bytes =
714 bcs::to_bytes(&serializable_module_id).map_err(serde::ser::Error::custom)?;
715 serializer.serialize_str(&hex::encode(bytes))
716 } else {
717 SerializableModuleId::serialize(&serializable_module_id, serializer)
718 }
719 }
720}
721
722impl<'de, Abi, Parameters, InstantiationArgument> Deserialize<'de>
723 for ModuleId<Abi, Parameters, InstantiationArgument>
724{
725 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
726 where
727 D: serde::de::Deserializer<'de>,
728 {
729 if deserializer.is_human_readable() {
730 let s = String::deserialize(deserializer)?;
731 let module_id_bytes = hex::decode(s).map_err(serde::de::Error::custom)?;
732 let serializable_module_id: SerializableModuleId =
733 bcs::from_bytes(&module_id_bytes).map_err(serde::de::Error::custom)?;
734 Ok(ModuleId {
735 contract_blob_hash: serializable_module_id.contract_blob_hash,
736 service_blob_hash: serializable_module_id.service_blob_hash,
737 vm_runtime: serializable_module_id.vm_runtime,
738 _phantom: PhantomData,
739 })
740 } else {
741 let serializable_module_id = SerializableModuleId::deserialize(deserializer)?;
742 Ok(ModuleId {
743 contract_blob_hash: serializable_module_id.contract_blob_hash,
744 service_blob_hash: serializable_module_id.service_blob_hash,
745 vm_runtime: serializable_module_id.vm_runtime,
746 _phantom: PhantomData,
747 })
748 }
749 }
750}
751
752impl ModuleId {
753 pub fn new(
755 contract_blob_hash: CryptoHash,
756 service_blob_hash: CryptoHash,
757 vm_runtime: VmRuntime,
758 ) -> Self {
759 ModuleId {
760 contract_blob_hash,
761 service_blob_hash,
762 vm_runtime,
763 _phantom: PhantomData,
764 }
765 }
766
767 pub fn with_abi<Abi, Parameters, InstantiationArgument>(
769 self,
770 ) -> ModuleId<Abi, Parameters, InstantiationArgument> {
771 ModuleId {
772 contract_blob_hash: self.contract_blob_hash,
773 service_blob_hash: self.service_blob_hash,
774 vm_runtime: self.vm_runtime,
775 _phantom: PhantomData,
776 }
777 }
778
779 pub fn contract_bytecode_blob_id(&self) -> BlobId {
781 match self.vm_runtime {
782 VmRuntime::Wasm => BlobId::new(self.contract_blob_hash, BlobType::ContractBytecode),
783 VmRuntime::Evm => BlobId::new(self.contract_blob_hash, BlobType::EvmBytecode),
784 }
785 }
786
787 pub fn service_bytecode_blob_id(&self) -> BlobId {
789 match self.vm_runtime {
790 VmRuntime::Wasm => BlobId::new(self.service_blob_hash, BlobType::ServiceBytecode),
791 VmRuntime::Evm => BlobId::new(self.contract_blob_hash, BlobType::EvmBytecode),
792 }
793 }
794
795 pub fn bytecode_blob_ids(&self) -> Vec<BlobId> {
797 match self.vm_runtime {
798 VmRuntime::Wasm => vec![
799 BlobId::new(self.contract_blob_hash, BlobType::ContractBytecode),
800 BlobId::new(self.service_blob_hash, BlobType::ServiceBytecode),
801 ],
802 VmRuntime::Evm => vec![BlobId::new(self.contract_blob_hash, BlobType::EvmBytecode)],
803 }
804 }
805}
806
807impl<Abi, Parameters, InstantiationArgument> ModuleId<Abi, Parameters, InstantiationArgument> {
808 pub fn forget_abi(self) -> ModuleId {
810 ModuleId {
811 contract_blob_hash: self.contract_blob_hash,
812 service_blob_hash: self.service_blob_hash,
813 vm_runtime: self.vm_runtime,
814 _phantom: PhantomData,
815 }
816 }
817
818 pub fn just_abi(self) -> ModuleId<Abi> {
820 ModuleId {
821 contract_blob_hash: self.contract_blob_hash,
822 service_blob_hash: self.service_blob_hash,
823 vm_runtime: self.vm_runtime,
824 _phantom: PhantomData,
825 }
826 }
827}
828
829impl<A> Clone for ApplicationId<A> {
831 fn clone(&self) -> Self {
832 *self
833 }
834}
835
836impl<A> Copy for ApplicationId<A> {}
837
838impl<A: PartialEq> PartialEq for ApplicationId<A> {
839 fn eq(&self, other: &Self) -> bool {
840 self.application_description_hash == other.application_description_hash
841 }
842}
843
844impl<A: Eq> Eq for ApplicationId<A> {}
845
846impl<A: PartialOrd> PartialOrd for ApplicationId<A> {
847 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
848 self.application_description_hash
849 .partial_cmp(&other.application_description_hash)
850 }
851}
852
853impl<A: Ord> Ord for ApplicationId<A> {
854 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
855 self.application_description_hash
856 .cmp(&other.application_description_hash)
857 }
858}
859
860impl<A> Hash for ApplicationId<A> {
861 fn hash<H: Hasher>(&self, state: &mut H) {
862 self.application_description_hash.hash(state);
863 }
864}
865
866#[derive(Serialize, Deserialize)]
867#[serde(rename = "ApplicationId")]
868struct SerializableApplicationId {
869 pub application_description_hash: CryptoHash,
870}
871
872impl<A> Serialize for ApplicationId<A> {
873 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
874 where
875 S: serde::ser::Serializer,
876 {
877 if serializer.is_human_readable() {
878 let bytes = bcs::to_bytes(&SerializableApplicationId {
879 application_description_hash: self.application_description_hash,
880 })
881 .map_err(serde::ser::Error::custom)?;
882 serializer.serialize_str(&hex::encode(bytes))
883 } else {
884 SerializableApplicationId::serialize(
885 &SerializableApplicationId {
886 application_description_hash: self.application_description_hash,
887 },
888 serializer,
889 )
890 }
891 }
892}
893
894impl<'de, A> Deserialize<'de> for ApplicationId<A> {
895 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
896 where
897 D: serde::de::Deserializer<'de>,
898 {
899 if deserializer.is_human_readable() {
900 let s = String::deserialize(deserializer)?;
901 let application_id_bytes = hex::decode(s).map_err(serde::de::Error::custom)?;
902 let application_id: SerializableApplicationId =
903 bcs::from_bytes(&application_id_bytes).map_err(serde::de::Error::custom)?;
904 Ok(ApplicationId {
905 application_description_hash: application_id.application_description_hash,
906 _phantom: PhantomData,
907 })
908 } else {
909 let value = SerializableApplicationId::deserialize(deserializer)?;
910 Ok(ApplicationId {
911 application_description_hash: value.application_description_hash,
912 _phantom: PhantomData,
913 })
914 }
915 }
916}
917
918impl ApplicationId {
919 pub fn new(application_description_hash: CryptoHash) -> Self {
921 ApplicationId {
922 application_description_hash,
923 _phantom: PhantomData,
924 }
925 }
926
927 pub fn description_blob_id(self) -> BlobId {
930 BlobId::new(
931 self.application_description_hash,
932 BlobType::ApplicationDescription,
933 )
934 }
935
936 pub fn with_abi<A>(self) -> ApplicationId<A> {
938 ApplicationId {
939 application_description_hash: self.application_description_hash,
940 _phantom: PhantomData,
941 }
942 }
943}
944
945impl<A> ApplicationId<A> {
946 pub fn forget_abi(self) -> ApplicationId {
948 ApplicationId {
949 application_description_hash: self.application_description_hash,
950 _phantom: PhantomData,
951 }
952 }
953}
954
955#[derive(Serialize, Deserialize)]
956#[serde(rename = "AccountOwner")]
957enum SerializableAccountOwner {
958 Reserved(u8),
959 Address32(CryptoHash),
960 Address20([u8; 20]),
961}
962
963impl Serialize for AccountOwner {
964 fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
965 if serializer.is_human_readable() {
966 serializer.serialize_str(&self.to_string())
967 } else {
968 match self {
969 AccountOwner::Reserved(value) => SerializableAccountOwner::Reserved(*value),
970 AccountOwner::Address32(value) => SerializableAccountOwner::Address32(*value),
971 AccountOwner::Address20(value) => SerializableAccountOwner::Address20(*value),
972 }
973 .serialize(serializer)
974 }
975 }
976}
977
978impl<'de> Deserialize<'de> for AccountOwner {
979 fn deserialize<D: serde::de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
980 if deserializer.is_human_readable() {
981 let s = String::deserialize(deserializer)?;
982 let value = Self::from_str(&s).map_err(serde::de::Error::custom)?;
983 Ok(value)
984 } else {
985 let value = SerializableAccountOwner::deserialize(deserializer)?;
986 match value {
987 SerializableAccountOwner::Reserved(value) => Ok(AccountOwner::Reserved(value)),
988 SerializableAccountOwner::Address32(value) => Ok(AccountOwner::Address32(value)),
989 SerializableAccountOwner::Address20(value) => Ok(AccountOwner::Address20(value)),
990 }
991 }
992 }
993}
994
995impl Display for AccountOwner {
996 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
997 match self {
998 AccountOwner::Reserved(value) => {
999 write!(f, "0x{}", hex::encode(&value.to_be_bytes()[..]))?
1000 }
1001 AccountOwner::Address32(value) => write!(f, "0x{}", value)?,
1002 AccountOwner::Address20(value) => write!(f, "0x{}", hex::encode(&value[..]))?,
1003 };
1004
1005 Ok(())
1006 }
1007}
1008
1009impl FromStr for AccountOwner {
1010 type Err = anyhow::Error;
1011
1012 fn from_str(s: &str) -> Result<Self, Self::Err> {
1013 if let Some(s) = s.strip_prefix("0x") {
1014 if s.len() == 64 {
1015 if let Ok(hash) = CryptoHash::from_str(s) {
1016 return Ok(AccountOwner::Address32(hash));
1017 }
1018 } else if s.len() == 40 {
1019 let address = hex::decode(s)?;
1020 if address.len() != 20 {
1021 anyhow::bail!("Invalid address length: {}", s);
1022 }
1023 let address = <[u8; 20]>::try_from(address.as_slice()).unwrap();
1024 return Ok(AccountOwner::Address20(address));
1025 }
1026 if s.len() == 2 {
1027 let bytes = hex::decode(s)?;
1028 if bytes.len() == 1 {
1029 let value = u8::from_be_bytes(bytes.try_into().expect("one byte"));
1030 return Ok(AccountOwner::Reserved(value));
1031 }
1032 }
1033 }
1034 anyhow::bail!("Invalid address value: {}", s);
1035 }
1036}
1037
1038impl Display for ChainId {
1039 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1040 Display::fmt(&self.0, f)
1041 }
1042}
1043
1044impl FromStr for ChainId {
1045 type Err = CryptoError;
1046
1047 fn from_str(s: &str) -> Result<Self, Self::Err> {
1048 Ok(ChainId(CryptoHash::from_str(s)?))
1049 }
1050}
1051
1052impl TryFrom<&[u8]> for ChainId {
1053 type Error = CryptoError;
1054
1055 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
1056 Ok(ChainId(CryptoHash::try_from(value)?))
1057 }
1058}
1059
1060impl fmt::Debug for ChainId {
1061 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
1062 write!(f, "{:?}", self.0)
1063 }
1064}
1065
1066impl From<ChainDescription> for ChainId {
1067 fn from(description: ChainDescription) -> Self {
1068 Self(CryptoHash::new(&description))
1069 }
1070}
1071
1072impl ChainId {
1073 pub fn root(index: u32) -> Self {
1075 Self(CryptoHash::new(&ChainDescription::Root(index)))
1076 }
1077
1078 pub fn child(id: MessageId) -> Self {
1080 Self(CryptoHash::new(&ChainDescription::Child(id)))
1081 }
1082}
1083
1084impl BcsHashable<'_> for ChainDescription {}
1085
1086bcs_scalar!(ApplicationId, "A unique identifier for a user application");
1087doc_scalar!(
1088 GenericApplicationId,
1089 "A unique identifier for a user application or for the system application"
1090);
1091bcs_scalar!(ModuleId, "A unique identifier for an application module");
1092doc_scalar!(ChainDescription, "How to create a chain");
1093doc_scalar!(
1094 ChainId,
1095 "The unique identifier (UID) of a chain. This is currently computed as the hash value of a \
1096 ChainDescription."
1097);
1098doc_scalar!(ChannelName, "The name of a subscription channel");
1099doc_scalar!(StreamName, "The name of an event stream");
1100bcs_scalar!(MessageId, "The index of a message in a chain");
1101doc_scalar!(
1102 Destination,
1103 "The destination of a message, relative to a particular application."
1104);
1105doc_scalar!(
1106 AccountOwner,
1107 "A unique identifier for a user or an application."
1108);
1109doc_scalar!(Account, "An account");
1110doc_scalar!(
1111 BlobId,
1112 "A content-addressed blob ID i.e. the hash of the `BlobContent`"
1113);
1114doc_scalar!(
1115 ChannelFullName,
1116 "A channel name together with its application ID."
1117);
1118
1119#[cfg(test)]
1120mod tests {
1121 use std::str::FromStr as _;
1122
1123 use assert_matches::assert_matches;
1124
1125 use super::{AccountOwner, BlobType, ChainId};
1126
1127 #[test]
1130 fn chain_ids() {
1131 assert_eq!(
1132 &ChainId::root(0).to_string(),
1133 "aee928d4bf3880353b4a3cd9b6f88e6cc6e5ed050860abae439e7782e9b2dfe8"
1134 );
1135 assert_eq!(
1136 &ChainId::root(1).to_string(),
1137 "a3edc33d8e951a1139333be8a4b56646b5598a8f51216e86592d881808972b07"
1138 );
1139 assert_eq!(
1140 &ChainId::root(2).to_string(),
1141 "678e9f66507069d38955b593e93ddf192a23a4087225fd307eadad44e5544ae3"
1142 );
1143 assert_eq!(
1144 &ChainId::root(9).to_string(),
1145 "63620ea465af9e9e0e8e4dd8d21593cc3a719feac5f096df8440f90738f4dbd8"
1146 );
1147 assert_eq!(
1148 &ChainId::root(999).to_string(),
1149 "5487b70625ce71f7ee29154ad32aefa1c526cb483bdb783dea2e1d17bc497844"
1150 );
1151 }
1152
1153 #[test]
1154 fn blob_types() {
1155 assert_eq!("ContractBytecode", BlobType::ContractBytecode.to_string());
1156 assert_eq!(
1157 BlobType::ContractBytecode,
1158 BlobType::from_str("ContractBytecode").unwrap()
1159 );
1160 }
1161
1162 #[test]
1163 fn addresses() {
1164 assert_eq!(&AccountOwner::Reserved(0).to_string(), "0x00");
1165
1166 let address = AccountOwner::from_str("0x10").unwrap();
1167 assert_eq!(address, AccountOwner::Reserved(16));
1168 assert_eq!(address.to_string(), "0x10");
1169
1170 let address = AccountOwner::from_str(
1171 "0x5487b70625ce71f7ee29154ad32aefa1c526cb483bdb783dea2e1d17bc497844",
1172 )
1173 .unwrap();
1174 assert_matches!(address, AccountOwner::Address32(_));
1175 assert_eq!(
1176 address.to_string(),
1177 "0x5487b70625ce71f7ee29154ad32aefa1c526cb483bdb783dea2e1d17bc497844"
1178 );
1179
1180 let address = AccountOwner::from_str("0x6E0ab7F37b667b7228D3a03116Ca21Be83213823").unwrap();
1181 assert_matches!(address, AccountOwner::Address20(_));
1182 assert_eq!(
1183 address.to_string(),
1184 "0x6e0ab7f37b667b7228d3a03116ca21be83213823"
1185 );
1186
1187 assert!(AccountOwner::from_str("0x5487b7").is_err());
1188 assert!(AccountOwner::from_str("0").is_err());
1189 assert!(AccountOwner::from_str(
1190 "5487b70625ce71f7ee29154ad32aefa1c526cb483bdb783dea2e1d17bc497844"
1191 )
1192 .is_err());
1193 }
1194}