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