linera_base/
identifiers.rs

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