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