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