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