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