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