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