Skip to main content

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