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