linera_base/
identifiers.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Core identifiers used by the Linera protocol.
5
6use 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/// An account owner.
34#[derive(
35    Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd, WitLoad, WitStore, WitType, Allocative,
36)]
37#[cfg_attr(with_testing, derive(test_strategy::Arbitrary))]
38// TODO(#5166) we can be more specific here
39#[cfg_attr(
40    web,
41    derive(tsify::Tsify),
42    tsify(from_wasm_abi, into_wasm_abi, type = "string")
43)]
44pub enum AccountOwner {
45    /// Short addresses reserved for the protocol.
46    Reserved(u8),
47    /// 32-byte account address.
48    Address32(CryptoHash),
49    /// 20-byte account EVM-compatible address.
50    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    /// Returns the default chain address.
65    pub const CHAIN: AccountOwner = AccountOwner::Reserved(0);
66
67    /// Tests if the account is the chain address.
68    pub fn is_chain(&self) -> bool {
69        self == &AccountOwner::CHAIN
70    }
71
72    /// The size of the `AccountOwner`.
73    pub fn size(&self) -> u32 {
74        match self {
75            AccountOwner::Reserved(_) => 1,
76            AccountOwner::Address32(_) => 32,
77            AccountOwner::Address20(_) => 20,
78        }
79    }
80
81    /// Gets the EVM address if possible
82    #[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/// An account.
107#[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    /// The chain of the account.
127    pub chain_id: ChainId,
128    /// The owner of the account.
129    pub owner: AccountOwner,
130}
131
132impl Account {
133    /// Creates a new [`Account`] with the given chain ID and owner.
134    pub fn new(chain_id: ChainId, owner: AccountOwner) -> Self {
135        Self { chain_id, owner }
136    }
137
138    /// Creates an [`Account`] representing the balance shared by a chain's owners.
139    pub fn chain(chain_id: ChainId) -> Self {
140        Account {
141            chain_id,
142            owner: AccountOwner::CHAIN,
143        }
144    }
145
146    /// An address used exclusively for tests
147    #[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/// The unique identifier (UID) of a chain. This is currently computed as the hash value
181/// of a [`ChainDescription`].
182#[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/// The type of the blob.
203/// Should be a 1:1 mapping of the types in `Blob`.
204#[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    /// A generic data blob.
224    #[default]
225    Data,
226    /// A blob containing compressed contract Wasm bytecode.
227    ContractBytecode,
228    /// A blob containing compressed service Wasm bytecode.
229    ServiceBytecode,
230    /// A blob containing compressed EVM bytecode.
231    EvmBytecode,
232    /// A blob containing an application description.
233    ApplicationDescription,
234    /// A blob containing a committee of validators.
235    Committee,
236    /// A blob containing a chain description.
237    ChainDescription,
238}
239
240impl BlobType {
241    /// Returns whether the blob is of [`BlobType::Committee`] variant.
242    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/// A content-addressed blob ID i.e. the hash of the `BlobContent`.
271#[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    /// The type of the blob.
277    pub blob_type: BlobType,
278    /// The hash of the blob.
279    pub hash: CryptoHash,
280}
281
282impl BlobId {
283    /// Creates a new `BlobId` from a `CryptoHash`. This must be a hash of the blob's bytes!
284    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/// Hash of a data blob.
353#[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// TODO(#5166) we can be more specific here (and also more generic)
365#[cfg_attr(web, wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section))]
366const _: &str = "export type ApplicationId = string;";
367
368/// A unique identifier for a user application from a blob.
369#[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    /// The hash of the `ApplicationDescription` this refers to.
374    pub application_description_hash: CryptoHash,
375    #[witty(skip)]
376    #[debug(skip)]
377    #[allocative(skip)]
378    _phantom: PhantomData<A>,
379}
380
381/// A unique identifier for an application.
382#[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    /// The system application.
401    System,
402    /// A user application.
403    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    /// Returns the `ApplicationId`, or `None` if it is `System`.
435    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/// A unique identifier for a module.
490#[derive(Debug, WitLoad, WitStore, WitType, Allocative)]
491#[cfg_attr(with_testing, derive(Default, test_strategy::Arbitrary))]
492pub struct ModuleId<Abi = (), Parameters = (), InstantiationArgument = ()> {
493    /// The hash of the blob containing the contract bytecode.
494    pub contract_blob_hash: CryptoHash,
495    /// The hash of the blob containing the service bytecode.
496    pub service_blob_hash: CryptoHash,
497    /// The virtual machine being used.
498    pub vm_runtime: VmRuntime,
499    #[witty(skip)]
500    #[debug(skip)]
501    _phantom: PhantomData<(Abi, Parameters, InstantiationArgument)>,
502}
503
504/// The name of an event stream.
505#[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/// An event stream ID.
551#[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    /// The application that can add events to this stream.
571    pub application_id: GenericApplicationId,
572    /// The name of this stream: an application can have multiple streams with different names.
573    pub stream_name: StreamName,
574}
575
576impl StreamId {
577    /// Creates a system stream ID with the given name.
578    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/// The result of an `events_from_index`.
587#[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    /// The index of the found event.
604    pub index: u32,
605    /// The event being returned.
606    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/// An event identifier.
637#[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    /// The ID of the chain that generated this event.
653    pub chain_id: ChainId,
654    /// The ID of the stream this event belongs to.
655    pub stream_id: StreamId,
656    /// The event index, i.e. the number of events in the stream before this one.
657    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    /// Turns the stream name into bytes.
668    pub fn into_bytes(self) -> Vec<u8> {
669        self.0
670    }
671}
672
673// Cannot use #[derive(Clone)] because it requires `A: Clone`.
674impl<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    /// Creates a module ID from contract/service hashes and the VM runtime to use.
813    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    /// Specializes a module ID for a given ABI.
827    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    /// Gets the `BlobId` of the contract
839    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    /// Gets the `BlobId` of the service
847    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    /// Gets all bytecode `BlobId`s of the module
855    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    /// Forgets the ABI of a module ID (if any).
868    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
878// Cannot use #[derive(Clone)] because it requires `A: Clone`.
879impl<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    /// Creates an application ID from the application description hash.
969    pub fn new(application_description_hash: CryptoHash) -> Self {
970        ApplicationId {
971            application_description_hash,
972            _phantom: PhantomData,
973        }
974    }
975
976    /// Converts the application ID to the ID of the blob containing the
977    /// `ApplicationDescription`.
978    pub fn description_blob_id(self) -> BlobId {
979        BlobId::new(
980            self.application_description_hash,
981            BlobType::ApplicationDescription,
982        )
983    }
984
985    /// Specializes an application ID for a given ABI.
986    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    /// Forgets the ABI of an application ID (if any).
996    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    /// Returns whether the `ApplicationId` is the one of an EVM application.
1006    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    /// Converts the `ApplicationId` into an Ethereum Address.
1027    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    /// Converts the `ApplicationId` into an Ethereum-compatible 32-byte array.
1034    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    /// Verifies that the way of computing chain IDs doesn't change.
1201    #[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        // Chain-only account.
1277        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        // Account with owner.
1286        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}