1use 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#[derive(
35 Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd, WitLoad, WitStore, WitType, Allocative,
36)]
37#[cfg_attr(with_testing, derive(test_strategy::Arbitrary))]
38#[cfg_attr(
40 web,
41 derive(tsify::Tsify),
42 tsify(from_wasm_abi, into_wasm_abi, type = "string")
43)]
44pub enum AccountOwner {
45 Reserved(u8),
47 Address32(CryptoHash),
49 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 pub const CHAIN: AccountOwner = AccountOwner::Reserved(0);
66
67 pub fn is_chain(&self) -> bool {
69 self == &AccountOwner::CHAIN
70 }
71
72 pub fn size(&self) -> u32 {
74 match self {
75 AccountOwner::Reserved(_) => 1,
76 AccountOwner::Address32(_) => 32,
77 AccountOwner::Address20(_) => 20,
78 }
79 }
80
81 #[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 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#[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 pub chain_id: ChainId,
133 pub owner: AccountOwner,
135}
136
137impl Account {
138 pub fn new(chain_id: ChainId, owner: AccountOwner) -> Self {
140 Self { chain_id, owner }
141 }
142
143 pub fn chain(chain_id: ChainId) -> Self {
145 Account {
146 chain_id,
147 owner: AccountOwner::CHAIN,
148 }
149 }
150
151 #[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#[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#[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 #[default]
230 Data,
231 ContractBytecode,
233 ServiceBytecode,
235 EvmBytecode,
237 ApplicationDescription,
239 Committee,
241 ChainDescription,
243}
244
245impl BlobType {
246 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#[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 pub blob_type: BlobType,
283 pub hash: CryptoHash,
285}
286
287impl BlobId {
288 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#[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#[cfg_attr(web, wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section))]
371const _: &str = "export type ApplicationId = string;";
372
373#[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 pub application_description_hash: CryptoHash,
380 #[witty(skip)]
381 #[debug(skip)]
382 #[allocative(skip)]
383 _phantom: PhantomData<A>,
384}
385
386#[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 System,
407 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 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#[derive(Debug, WitLoad, WitStore, WitType, Allocative)]
491#[cfg_attr(with_testing, derive(Default, test_strategy::Arbitrary))]
492pub struct ModuleId<Abi = (), Parameters = (), InstantiationArgument = ()> {
493 pub contract_blob_hash: CryptoHash,
495 pub service_blob_hash: CryptoHash,
497 pub vm_runtime: VmRuntime,
499 #[witty(skip)]
500 #[debug(skip)]
501 _phantom: PhantomData<(Abi, Parameters, InstantiationArgument)>,
502}
503
504#[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#[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 pub application_id: GenericApplicationId,
571 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 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#[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 pub index: u32,
645 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#[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 pub chain_id: ChainId,
694 pub stream_id: StreamId,
696 pub index: u32,
698}
699
700impl StreamName {
701 pub fn into_bytes(self) -> Vec<u8> {
703 self.0
704 }
705}
706
707impl<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 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 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 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 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 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 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
912impl<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 pub fn new(application_description_hash: CryptoHash) -> Self {
1004 ApplicationId {
1005 application_description_hash,
1006 _phantom: PhantomData,
1007 }
1008 }
1009
1010 pub fn description_blob_id(self) -> BlobId {
1013 BlobId::new(
1014 self.application_description_hash,
1015 BlobType::ApplicationDescription,
1016 )
1017 }
1018
1019 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 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 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 pub fn bytes32(&self) -> B256 {
1049 *self.application_description_hash.as_bytes()
1050 }
1051
1052 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 #[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 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 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}