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