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