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