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