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 #[debug(with = "hex_debug")]
42 Address20([u8; 20]),
43}
44
45impl AccountOwner {
46 pub const CHAIN: AccountOwner = AccountOwner::Reserved(0);
48
49 pub fn is_chain(&self) -> bool {
51 self == &AccountOwner::CHAIN
52 }
53
54 pub fn size(&self) -> u32 {
56 match self {
57 AccountOwner::Reserved(_) => 1,
58 AccountOwner::Address32(_) => 32,
59 AccountOwner::Address20(_) => 20,
60 }
61 }
62
63 #[cfg(with_revm)]
65 pub fn to_evm_address(&self) -> Option<Address> {
66 match self {
67 AccountOwner::Address20(address) => Some(Address::from(address)),
68 _ => None,
69 }
70 }
71}
72
73#[cfg(with_testing)]
74impl From<CryptoHash> for AccountOwner {
75 fn from(address: CryptoHash) -> Self {
76 AccountOwner::Address32(address)
77 }
78}
79
80#[derive(
82 Debug, PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize, WitLoad, WitStore, WitType,
83)]
84pub struct Account {
85 pub chain_id: ChainId,
87 pub owner: AccountOwner,
89}
90
91impl Account {
92 pub fn new(chain_id: ChainId, owner: AccountOwner) -> Self {
94 Self { chain_id, owner }
95 }
96
97 pub fn chain(chain_id: ChainId) -> Self {
99 Account {
100 chain_id,
101 owner: AccountOwner::CHAIN,
102 }
103 }
104}
105
106impl fmt::Display for Account {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 write!(f, "{}:{}", self.chain_id, self.owner)
109 }
110}
111
112impl std::str::FromStr for Account {
113 type Err = anyhow::Error;
114
115 fn from_str(string: &str) -> Result<Self, Self::Err> {
116 let mut parts = string.splitn(2, ':');
117
118 let chain_id = parts
119 .next()
120 .context(
121 "Expecting an account formatted as `chain-id` or `chain-id:owner-type:address`",
122 )?
123 .parse()?;
124
125 if let Some(owner_string) = parts.next() {
126 let owner = owner_string.parse::<AccountOwner>()?;
127 Ok(Account::new(chain_id, owner))
128 } else {
129 Ok(Account::chain(chain_id))
130 }
131 }
132}
133
134#[derive(
137 Eq,
138 PartialEq,
139 Ord,
140 PartialOrd,
141 Copy,
142 Clone,
143 Hash,
144 Serialize,
145 Deserialize,
146 WitLoad,
147 WitStore,
148 WitType,
149)]
150#[cfg_attr(with_testing, derive(test_strategy::Arbitrary))]
151#[cfg_attr(with_testing, derive(Default))]
152pub struct ChainId(pub CryptoHash);
153
154#[derive(
157 Eq,
158 PartialEq,
159 Ord,
160 PartialOrd,
161 Clone,
162 Copy,
163 Hash,
164 Debug,
165 Serialize,
166 Deserialize,
167 WitType,
168 WitStore,
169 WitLoad,
170 Default,
171)]
172#[cfg_attr(with_testing, derive(test_strategy::Arbitrary))]
173pub enum BlobType {
174 #[default]
176 Data,
177 ContractBytecode,
179 ServiceBytecode,
181 EvmBytecode,
183 ApplicationDescription,
185 Committee,
187 ChainDescription,
189}
190
191impl BlobType {
192 pub fn is_committee_blob(&self) -> bool {
194 match self {
195 BlobType::Data
196 | BlobType::ContractBytecode
197 | BlobType::ServiceBytecode
198 | BlobType::EvmBytecode
199 | BlobType::ApplicationDescription
200 | BlobType::ChainDescription => false,
201 BlobType::Committee => true,
202 }
203 }
204}
205
206impl fmt::Display for BlobType {
207 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208 write!(f, "{:?}", self)
209 }
210}
211
212impl std::str::FromStr for BlobType {
213 type Err = anyhow::Error;
214
215 fn from_str(s: &str) -> Result<Self, Self::Err> {
216 serde_json::from_str(&format!("\"{s}\""))
217 .with_context(|| format!("Invalid BlobType: {}", s))
218 }
219}
220
221#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash, Debug, WitType, WitStore, WitLoad)]
223#[cfg_attr(with_testing, derive(test_strategy::Arbitrary, Default))]
224pub struct BlobId {
225 pub blob_type: BlobType,
227 pub hash: CryptoHash,
229}
230
231impl BlobId {
232 pub fn new(hash: CryptoHash, blob_type: BlobType) -> Self {
234 Self { hash, blob_type }
235 }
236}
237
238impl fmt::Display for BlobId {
239 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
240 write!(f, "{}:{}", self.blob_type, self.hash)?;
241 Ok(())
242 }
243}
244
245impl std::str::FromStr for BlobId {
246 type Err = anyhow::Error;
247
248 fn from_str(s: &str) -> Result<Self, Self::Err> {
249 let parts = s.split(':').collect::<Vec<_>>();
250 if parts.len() == 2 {
251 let blob_type = BlobType::from_str(parts[0]).context("Invalid BlobType!")?;
252 Ok(BlobId {
253 hash: CryptoHash::from_str(parts[1]).context("Invalid hash!")?,
254 blob_type,
255 })
256 } else {
257 Err(anyhow!("Invalid blob ID: {}", s))
258 }
259 }
260}
261
262#[derive(Serialize, Deserialize)]
263#[serde(rename = "BlobId")]
264struct BlobIdHelper {
265 hash: CryptoHash,
266 blob_type: BlobType,
267}
268
269impl Serialize for BlobId {
270 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
271 where
272 S: Serializer,
273 {
274 if serializer.is_human_readable() {
275 serializer.serialize_str(&self.to_string())
276 } else {
277 let helper = BlobIdHelper {
278 hash: self.hash,
279 blob_type: self.blob_type,
280 };
281 helper.serialize(serializer)
282 }
283 }
284}
285
286impl<'a> Deserialize<'a> for BlobId {
287 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
288 where
289 D: Deserializer<'a>,
290 {
291 if deserializer.is_human_readable() {
292 let s = String::deserialize(deserializer)?;
293 Self::from_str(&s).map_err(serde::de::Error::custom)
294 } else {
295 let helper = BlobIdHelper::deserialize(deserializer)?;
296 Ok(BlobId::new(helper.hash, helper.blob_type))
297 }
298 }
299}
300
301#[derive(
303 Eq,
304 PartialEq,
305 Ord,
306 PartialOrd,
307 Copy,
308 Clone,
309 Hash,
310 Debug,
311 Serialize,
312 Deserialize,
313 WitLoad,
314 WitStore,
315 WitType,
316)]
317#[cfg_attr(with_testing, derive(Default, test_strategy::Arbitrary))]
318pub struct MessageId {
319 pub chain_id: ChainId,
321 pub height: BlockHeight,
323 pub index: u32,
325}
326
327#[derive(Debug, WitLoad, WitStore, WitType)]
329#[cfg_attr(with_testing, derive(Default, test_strategy::Arbitrary))]
330pub struct ApplicationId<A = ()> {
331 pub application_description_hash: CryptoHash,
333 #[witty(skip)]
334 #[debug(skip)]
335 _phantom: PhantomData<A>,
336}
337
338#[derive(
340 Eq,
341 PartialEq,
342 Ord,
343 PartialOrd,
344 Copy,
345 Clone,
346 Hash,
347 Debug,
348 Serialize,
349 Deserialize,
350 WitLoad,
351 WitStore,
352 WitType,
353)]
354pub enum GenericApplicationId {
355 System,
357 User(ApplicationId),
359}
360
361impl fmt::Display for GenericApplicationId {
362 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
363 match self {
364 GenericApplicationId::System => Display::fmt("System", f),
365 GenericApplicationId::User(application_id) => {
366 Display::fmt("User:", f)?;
367 Display::fmt(&application_id, f)
368 }
369 }
370 }
371}
372
373impl std::str::FromStr for GenericApplicationId {
374 type Err = anyhow::Error;
375
376 fn from_str(s: &str) -> Result<Self, Self::Err> {
377 if s == "System" {
378 return Ok(GenericApplicationId::System);
379 }
380 if let Some(result) = s.strip_prefix("User:") {
381 let application_id = ApplicationId::from_str(result)?;
382 return Ok(GenericApplicationId::User(application_id));
383 }
384 Err(anyhow!("Invalid parsing of GenericApplicationId"))
385 }
386}
387
388impl GenericApplicationId {
389 pub fn user_application_id(&self) -> Option<&ApplicationId> {
391 if let GenericApplicationId::User(app_id) = self {
392 Some(app_id)
393 } else {
394 None
395 }
396 }
397}
398
399impl<A> From<ApplicationId<A>> for AccountOwner {
400 fn from(app_id: ApplicationId<A>) -> Self {
401 AccountOwner::Address32(app_id.application_description_hash)
402 }
403}
404
405impl From<AccountPublicKey> for AccountOwner {
406 fn from(public_key: AccountPublicKey) -> Self {
407 match public_key {
408 AccountPublicKey::Ed25519(public_key) => public_key.into(),
409 AccountPublicKey::Secp256k1(public_key) => public_key.into(),
410 AccountPublicKey::EvmSecp256k1(public_key) => public_key.into(),
411 }
412 }
413}
414
415impl From<ApplicationId> for GenericApplicationId {
416 fn from(application_id: ApplicationId) -> Self {
417 GenericApplicationId::User(application_id)
418 }
419}
420
421impl From<Secp256k1PublicKey> for AccountOwner {
422 fn from(public_key: Secp256k1PublicKey) -> Self {
423 AccountOwner::Address32(CryptoHash::new(&public_key))
424 }
425}
426
427impl From<Ed25519PublicKey> for AccountOwner {
428 fn from(public_key: Ed25519PublicKey) -> Self {
429 AccountOwner::Address32(CryptoHash::new(&public_key))
430 }
431}
432
433impl From<EvmPublicKey> for AccountOwner {
434 fn from(public_key: EvmPublicKey) -> Self {
435 AccountOwner::Address20(alloy_primitives::Address::from_public_key(&public_key.0).into())
436 }
437}
438
439#[derive(Debug, WitLoad, WitStore, WitType)]
441#[cfg_attr(with_testing, derive(Default, test_strategy::Arbitrary))]
442pub struct ModuleId<Abi = (), Parameters = (), InstantiationArgument = ()> {
443 pub contract_blob_hash: CryptoHash,
445 pub service_blob_hash: CryptoHash,
447 pub vm_runtime: VmRuntime,
449 #[witty(skip)]
450 #[debug(skip)]
451 _phantom: PhantomData<(Abi, Parameters, InstantiationArgument)>,
452}
453
454#[derive(
456 Clone,
457 Debug,
458 Eq,
459 Hash,
460 Ord,
461 PartialEq,
462 PartialOrd,
463 Serialize,
464 Deserialize,
465 WitLoad,
466 WitStore,
467 WitType,
468)]
469pub struct StreamName(
470 #[serde(with = "serde_bytes")]
471 #[debug(with = "hex_debug")]
472 pub Vec<u8>,
473);
474
475impl<T> From<T> for StreamName
476where
477 T: Into<Vec<u8>>,
478{
479 fn from(name: T) -> Self {
480 StreamName(name.into())
481 }
482}
483
484impl fmt::Display for StreamName {
485 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
486 Display::fmt(&hex::encode(&self.0), f)
487 }
488}
489
490impl std::str::FromStr for StreamName {
491 type Err = anyhow::Error;
492
493 fn from_str(s: &str) -> Result<Self, Self::Err> {
494 let vec = hex::decode(s)?;
495 Ok(StreamName(vec))
496 }
497}
498
499#[derive(
501 Clone,
502 Debug,
503 Eq,
504 Hash,
505 Ord,
506 PartialEq,
507 PartialOrd,
508 Serialize,
509 Deserialize,
510 WitLoad,
511 WitStore,
512 WitType,
513 SimpleObject,
514 InputObject,
515)]
516#[graphql(input_name = "StreamIdInput")]
517pub struct StreamId {
518 pub application_id: GenericApplicationId,
520 pub stream_name: StreamName,
522}
523
524impl StreamId {
525 pub fn system(name: impl Into<StreamName>) -> Self {
527 StreamId {
528 application_id: GenericApplicationId::System,
529 stream_name: name.into(),
530 }
531 }
532}
533
534#[derive(
536 Debug,
537 Eq,
538 PartialEq,
539 Ord,
540 PartialOrd,
541 Clone,
542 Hash,
543 Serialize,
544 Deserialize,
545 WitLoad,
546 WitStore,
547 WitType,
548 SimpleObject,
549)]
550pub struct IndexAndEvent {
551 pub index: u32,
553 pub event: Vec<u8>,
555}
556
557impl fmt::Display for StreamId {
558 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
559 Display::fmt(&self.application_id, f)?;
560 Display::fmt(":", f)?;
561 Display::fmt(&self.stream_name, f)
562 }
563}
564
565impl std::str::FromStr for StreamId {
566 type Err = anyhow::Error;
567
568 fn from_str(s: &str) -> Result<Self, Self::Err> {
569 let parts = s.rsplit_once(":");
570 if let Some((part0, part1)) = parts {
571 let application_id =
572 GenericApplicationId::from_str(part0).context("Invalid GenericApplicationId!")?;
573 let stream_name = StreamName::from_str(part1).context("Invalid StreamName!")?;
574 Ok(StreamId {
575 application_id,
576 stream_name,
577 })
578 } else {
579 Err(anyhow!("Invalid blob ID: {}", s))
580 }
581 }
582}
583
584#[derive(
586 Debug,
587 PartialEq,
588 Eq,
589 Hash,
590 Clone,
591 Serialize,
592 Deserialize,
593 WitLoad,
594 WitStore,
595 WitType,
596 SimpleObject,
597)]
598pub struct EventId {
599 pub chain_id: ChainId,
601 pub stream_id: StreamId,
603 pub index: u32,
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
819impl<A> Clone for ApplicationId<A> {
821 fn clone(&self) -> Self {
822 *self
823 }
824}
825
826impl<A> Copy for ApplicationId<A> {}
827
828impl<A: PartialEq> PartialEq for ApplicationId<A> {
829 fn eq(&self, other: &Self) -> bool {
830 self.application_description_hash == other.application_description_hash
831 }
832}
833
834impl<A: Eq> Eq for ApplicationId<A> {}
835
836impl<A: PartialOrd> PartialOrd for ApplicationId<A> {
837 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
838 self.application_description_hash
839 .partial_cmp(&other.application_description_hash)
840 }
841}
842
843impl<A: Ord> Ord for ApplicationId<A> {
844 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
845 self.application_description_hash
846 .cmp(&other.application_description_hash)
847 }
848}
849
850impl<A> Hash for ApplicationId<A> {
851 fn hash<H: Hasher>(&self, state: &mut H) {
852 self.application_description_hash.hash(state);
853 }
854}
855
856#[derive(Serialize, Deserialize)]
857#[serde(rename = "ApplicationId")]
858struct SerializableApplicationId {
859 pub application_description_hash: CryptoHash,
860}
861
862impl<A> Serialize for ApplicationId<A> {
863 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
864 where
865 S: serde::ser::Serializer,
866 {
867 if serializer.is_human_readable() {
868 let bytes = bcs::to_bytes(&SerializableApplicationId {
869 application_description_hash: self.application_description_hash,
870 })
871 .map_err(serde::ser::Error::custom)?;
872 serializer.serialize_str(&hex::encode(bytes))
873 } else {
874 SerializableApplicationId::serialize(
875 &SerializableApplicationId {
876 application_description_hash: self.application_description_hash,
877 },
878 serializer,
879 )
880 }
881 }
882}
883
884impl<'de, A> Deserialize<'de> for ApplicationId<A> {
885 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
886 where
887 D: serde::de::Deserializer<'de>,
888 {
889 if deserializer.is_human_readable() {
890 let s = String::deserialize(deserializer)?;
891 let application_id_bytes = hex::decode(s).map_err(serde::de::Error::custom)?;
892 let application_id: SerializableApplicationId =
893 bcs::from_bytes(&application_id_bytes).map_err(serde::de::Error::custom)?;
894 Ok(ApplicationId {
895 application_description_hash: application_id.application_description_hash,
896 _phantom: PhantomData,
897 })
898 } else {
899 let value = SerializableApplicationId::deserialize(deserializer)?;
900 Ok(ApplicationId {
901 application_description_hash: value.application_description_hash,
902 _phantom: PhantomData,
903 })
904 }
905 }
906}
907
908impl ApplicationId {
909 pub fn new(application_description_hash: CryptoHash) -> Self {
911 ApplicationId {
912 application_description_hash,
913 _phantom: PhantomData,
914 }
915 }
916
917 pub fn description_blob_id(self) -> BlobId {
920 BlobId::new(
921 self.application_description_hash,
922 BlobType::ApplicationDescription,
923 )
924 }
925
926 pub fn with_abi<A>(self) -> ApplicationId<A> {
928 ApplicationId {
929 application_description_hash: self.application_description_hash,
930 _phantom: PhantomData,
931 }
932 }
933}
934
935impl<A> ApplicationId<A> {
936 pub fn forget_abi(self) -> ApplicationId {
938 ApplicationId {
939 application_description_hash: self.application_description_hash,
940 _phantom: PhantomData,
941 }
942 }
943}
944
945#[cfg(with_revm)]
946impl<A> ApplicationId<A> {
947 pub fn evm_address(&self) -> Address {
949 let bytes = self.application_description_hash.as_bytes();
950 let bytes = bytes.0.as_ref();
951 Address::from_slice(&bytes[0..20])
952 }
953
954 pub fn bytes32(&self) -> B256 {
956 *self.application_description_hash.as_bytes()
957 }
958
959 pub fn is_evm(&self) -> bool {
961 let bytes = self.application_description_hash.as_bytes();
962 let bytes = bytes.0.as_ref();
963 for byte in &bytes[20..] {
964 if byte != &0 {
965 return false;
966 }
967 }
968 true
969 }
970}
971
972#[derive(Serialize, Deserialize)]
973#[serde(rename = "AccountOwner")]
974enum SerializableAccountOwner {
975 Reserved(u8),
976 Address32(CryptoHash),
977 Address20([u8; 20]),
978}
979
980impl Serialize for AccountOwner {
981 fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
982 if serializer.is_human_readable() {
983 serializer.serialize_str(&self.to_string())
984 } else {
985 match self {
986 AccountOwner::Reserved(value) => SerializableAccountOwner::Reserved(*value),
987 AccountOwner::Address32(value) => SerializableAccountOwner::Address32(*value),
988 AccountOwner::Address20(value) => SerializableAccountOwner::Address20(*value),
989 }
990 .serialize(serializer)
991 }
992 }
993}
994
995impl<'de> Deserialize<'de> for AccountOwner {
996 fn deserialize<D: serde::de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
997 if deserializer.is_human_readable() {
998 let s = String::deserialize(deserializer)?;
999 let value = Self::from_str(&s).map_err(serde::de::Error::custom)?;
1000 Ok(value)
1001 } else {
1002 let value = SerializableAccountOwner::deserialize(deserializer)?;
1003 match value {
1004 SerializableAccountOwner::Reserved(value) => Ok(AccountOwner::Reserved(value)),
1005 SerializableAccountOwner::Address32(value) => Ok(AccountOwner::Address32(value)),
1006 SerializableAccountOwner::Address20(value) => Ok(AccountOwner::Address20(value)),
1007 }
1008 }
1009 }
1010}
1011
1012impl fmt::Display for AccountOwner {
1013 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1014 match self {
1015 AccountOwner::Reserved(value) => {
1016 write!(f, "0x{}", hex::encode(&value.to_be_bytes()[..]))?
1017 }
1018 AccountOwner::Address32(value) => write!(f, "0x{}", value)?,
1019 AccountOwner::Address20(value) => write!(f, "0x{}", hex::encode(&value[..]))?,
1020 };
1021
1022 Ok(())
1023 }
1024}
1025
1026impl std::str::FromStr for AccountOwner {
1027 type Err = anyhow::Error;
1028
1029 fn from_str(s: &str) -> Result<Self, Self::Err> {
1030 if let Some(s) = s.strip_prefix("0x") {
1031 if s.len() == 64 {
1032 if let Ok(hash) = CryptoHash::from_str(s) {
1033 return Ok(AccountOwner::Address32(hash));
1034 }
1035 } else if s.len() == 40 {
1036 let address = hex::decode(s)?;
1037 if address.len() != 20 {
1038 anyhow::bail!("Invalid address length: {}", s);
1039 }
1040 let address = <[u8; 20]>::try_from(address.as_slice()).unwrap();
1041 return Ok(AccountOwner::Address20(address));
1042 }
1043 if s.len() == 2 {
1044 let bytes = hex::decode(s)?;
1045 if bytes.len() == 1 {
1046 let value = u8::from_be_bytes(bytes.try_into().expect("one byte"));
1047 return Ok(AccountOwner::Reserved(value));
1048 }
1049 }
1050 }
1051 anyhow::bail!("Invalid address value: {}", s);
1052 }
1053}
1054
1055impl fmt::Display for ChainId {
1056 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1057 Display::fmt(&self.0, f)
1058 }
1059}
1060
1061impl std::str::FromStr for ChainId {
1062 type Err = CryptoError;
1063
1064 fn from_str(s: &str) -> Result<Self, Self::Err> {
1065 Ok(ChainId(CryptoHash::from_str(s)?))
1066 }
1067}
1068
1069impl TryFrom<&[u8]> for ChainId {
1070 type Error = CryptoError;
1071
1072 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
1073 Ok(ChainId(CryptoHash::try_from(value)?))
1074 }
1075}
1076
1077impl fmt::Debug for ChainId {
1078 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
1079 write!(f, "{:?}", self.0)
1080 }
1081}
1082
1083impl<'a> From<&'a ChainDescription> for ChainId {
1084 fn from(description: &'a ChainDescription) -> Self {
1085 Self(CryptoHash::new(&BlobContent::new_chain_description(
1086 description,
1087 )))
1088 }
1089}
1090
1091impl From<ChainDescription> for ChainId {
1092 fn from(description: ChainDescription) -> Self {
1093 From::from(&description)
1094 }
1095}
1096
1097bcs_scalar!(ApplicationId, "A unique identifier for a user application");
1098doc_scalar!(
1099 GenericApplicationId,
1100 "A unique identifier for a user application or for the system application"
1101);
1102bcs_scalar!(ModuleId, "A unique identifier for an application module");
1103doc_scalar!(
1104 ChainId,
1105 "The unique identifier (UID) of a chain. This is currently computed as the hash value of a \
1106 ChainDescription."
1107);
1108doc_scalar!(StreamName, "The name of an event stream");
1109bcs_scalar!(MessageId, "The index of a message in a chain");
1110doc_scalar!(
1111 AccountOwner,
1112 "A unique identifier for a user or an application."
1113);
1114doc_scalar!(Account, "An account");
1115doc_scalar!(
1116 BlobId,
1117 "A content-addressed blob ID i.e. the hash of the `BlobContent`"
1118);
1119
1120#[cfg(test)]
1121mod tests {
1122 use std::str::FromStr as _;
1123
1124 use assert_matches::assert_matches;
1125
1126 use super::{AccountOwner, BlobType};
1127 use crate::{
1128 data_types::{Amount, ChainDescription, ChainOrigin, Epoch, InitialChainConfig, Timestamp},
1129 identifiers::{ApplicationId, CryptoHash, GenericApplicationId, StreamId, StreamName},
1130 ownership::ChainOwnership,
1131 };
1132
1133 #[test]
1135 fn chain_id_computing() {
1136 let example_chain_origin = ChainOrigin::Root(0);
1137 let example_chain_config = InitialChainConfig {
1138 epoch: Epoch::ZERO,
1139 ownership: ChainOwnership::single(AccountOwner::Reserved(0)),
1140 balance: Amount::ZERO,
1141 min_active_epoch: Epoch::ZERO,
1142 max_active_epoch: Epoch::ZERO,
1143 application_permissions: Default::default(),
1144 };
1145 let description = ChainDescription::new(
1146 example_chain_origin,
1147 example_chain_config,
1148 Timestamp::from(0),
1149 );
1150 assert_eq!(
1151 description.id().to_string(),
1152 "fe947fddf2735224d01eb9d56580109f2d9d02397dc5ddd748ef9beeb38d9caa"
1153 );
1154 }
1155
1156 #[test]
1157 fn blob_types() {
1158 assert_eq!("ContractBytecode", BlobType::ContractBytecode.to_string());
1159 assert_eq!(
1160 BlobType::ContractBytecode,
1161 BlobType::from_str("ContractBytecode").unwrap()
1162 );
1163 }
1164
1165 #[test]
1166 fn addresses() {
1167 assert_eq!(&AccountOwner::Reserved(0).to_string(), "0x00");
1168
1169 let address = AccountOwner::from_str("0x10").unwrap();
1170 assert_eq!(address, AccountOwner::Reserved(16));
1171 assert_eq!(address.to_string(), "0x10");
1172
1173 let address = AccountOwner::from_str(
1174 "0x5487b70625ce71f7ee29154ad32aefa1c526cb483bdb783dea2e1d17bc497844",
1175 )
1176 .unwrap();
1177 assert_matches!(address, AccountOwner::Address32(_));
1178 assert_eq!(
1179 address.to_string(),
1180 "0x5487b70625ce71f7ee29154ad32aefa1c526cb483bdb783dea2e1d17bc497844"
1181 );
1182
1183 let address = AccountOwner::from_str("0x6E0ab7F37b667b7228D3a03116Ca21Be83213823").unwrap();
1184 assert_matches!(address, AccountOwner::Address20(_));
1185 assert_eq!(
1186 address.to_string(),
1187 "0x6e0ab7f37b667b7228d3a03116ca21be83213823"
1188 );
1189
1190 assert!(AccountOwner::from_str("0x5487b7").is_err());
1191 assert!(AccountOwner::from_str("0").is_err());
1192 assert!(AccountOwner::from_str(
1193 "5487b70625ce71f7ee29154ad32aefa1c526cb483bdb783dea2e1d17bc497844"
1194 )
1195 .is_err());
1196 }
1197
1198 #[test]
1199 fn stream_name() {
1200 let vec = vec![32, 54, 120, 234];
1201 let stream_name1 = StreamName(vec);
1202 let stream_name2 = StreamName::from_str(&format!("{stream_name1}")).unwrap();
1203 assert_eq!(stream_name1, stream_name2);
1204 }
1205
1206 fn test_generic_application_id(application_id: GenericApplicationId) {
1207 let application_id2 = GenericApplicationId::from_str(&format!("{application_id}")).unwrap();
1208 assert_eq!(application_id, application_id2);
1209 }
1210
1211 #[test]
1212 fn generic_application_id() {
1213 test_generic_application_id(GenericApplicationId::System);
1214 let hash = CryptoHash::test_hash("test case");
1215 let application_id = ApplicationId::new(hash);
1216 test_generic_application_id(GenericApplicationId::User(application_id));
1217 }
1218
1219 #[test]
1220 fn stream_id() {
1221 let hash = CryptoHash::test_hash("test case");
1222 let application_id = ApplicationId::new(hash);
1223 let application_id = GenericApplicationId::User(application_id);
1224 let vec = vec![32, 54, 120, 234];
1225 let stream_name = StreamName(vec);
1226
1227 let stream_id1 = StreamId {
1228 application_id,
1229 stream_name,
1230 };
1231 let stream_id2 = StreamId::from_str(&format!("{stream_id1}")).unwrap();
1232 assert_eq!(stream_id1, stream_id2);
1233 }
1234}