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