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