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