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}\"")).with_context(|| format!("Invalid BlobType: {s}"))
271 }
272}
273
274#[derive(
276 Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash, Debug, WitType, WitStore, WitLoad, Allocative,
277)]
278#[cfg_attr(with_testing, derive(test_strategy::Arbitrary, Default))]
279pub struct BlobId {
280 pub blob_type: BlobType,
282 pub hash: CryptoHash,
284}
285
286impl BlobId {
287 pub fn new(hash: CryptoHash, blob_type: BlobType) -> Self {
289 Self { hash, blob_type }
290 }
291}
292
293impl fmt::Display for BlobId {
294 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295 write!(f, "{}:{}", self.blob_type, self.hash)?;
296 Ok(())
297 }
298}
299
300impl std::str::FromStr for BlobId {
301 type Err = anyhow::Error;
302
303 fn from_str(s: &str) -> Result<Self, Self::Err> {
304 let parts = s.split(':').collect::<Vec<_>>();
305 if parts.len() == 2 {
306 let blob_type = BlobType::from_str(parts[0]).context("Invalid BlobType!")?;
307 Ok(BlobId {
308 hash: CryptoHash::from_str(parts[1]).context("Invalid hash!")?,
309 blob_type,
310 })
311 } else {
312 Err(anyhow!("Invalid blob ID: {s}"))
313 }
314 }
315}
316
317#[derive(Serialize, Deserialize)]
318#[serde(rename = "BlobId")]
319struct BlobIdHelper {
320 hash: CryptoHash,
321 blob_type: BlobType,
322}
323
324impl Serialize for BlobId {
325 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
326 where
327 S: Serializer,
328 {
329 if serializer.is_human_readable() {
330 serializer.serialize_str(&self.to_string())
331 } else {
332 let helper = BlobIdHelper {
333 hash: self.hash,
334 blob_type: self.blob_type,
335 };
336 helper.serialize(serializer)
337 }
338 }
339}
340
341impl<'a> Deserialize<'a> for BlobId {
342 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
343 where
344 D: Deserializer<'a>,
345 {
346 if deserializer.is_human_readable() {
347 let s = String::deserialize(deserializer)?;
348 Self::from_str(&s).map_err(serde::de::Error::custom)
349 } else {
350 let helper = BlobIdHelper::deserialize(deserializer)?;
351 Ok(BlobId::new(helper.hash, helper.blob_type))
352 }
353 }
354}
355
356#[derive(
358 Eq, Hash, PartialEq, Debug, Serialize, Deserialize, Clone, Copy, WitType, WitLoad, WitStore,
359)]
360pub struct DataBlobHash(pub CryptoHash);
361
362impl From<DataBlobHash> for BlobId {
363 fn from(hash: DataBlobHash) -> BlobId {
364 BlobId::new(hash.0, BlobType::Data)
365 }
366}
367
368#[cfg_attr(web, wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section))]
370const _: &str = "export type ApplicationId = string;";
371
372#[derive(Debug, WitLoad, WitStore, WitType, Allocative)]
374#[cfg_attr(with_testing, derive(Default, test_strategy::Arbitrary))]
375#[allocative(bound = "A")]
376pub struct ApplicationId<A = ()> {
377 pub application_description_hash: CryptoHash,
379 #[witty(skip)]
380 #[debug(skip)]
381 #[allocative(skip)]
382 phantom: PhantomData<A>,
383}
384
385#[derive(
387 Eq,
388 PartialEq,
389 Ord,
390 PartialOrd,
391 Copy,
392 Clone,
393 Hash,
394 Debug,
395 Serialize,
396 Deserialize,
397 WitLoad,
398 WitStore,
399 WitType,
400 Allocative,
401)]
402#[cfg_attr(web, derive(tsify::Tsify), tsify(from_wasm_abi, into_wasm_abi))]
403pub enum GenericApplicationId {
404 System,
406 User(ApplicationId),
408}
409
410impl fmt::Display for GenericApplicationId {
411 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
412 match self {
413 GenericApplicationId::System => Display::fmt("System", f),
414 GenericApplicationId::User(application_id) => {
415 Display::fmt("User:", f)?;
416 Display::fmt(&application_id, f)
417 }
418 }
419 }
420}
421
422impl std::str::FromStr for GenericApplicationId {
423 type Err = anyhow::Error;
424
425 fn from_str(s: &str) -> Result<Self, Self::Err> {
426 if s == "System" {
427 return Ok(GenericApplicationId::System);
428 }
429 if let Some(result) = s.strip_prefix("User:") {
430 let application_id = ApplicationId::from_str(result)?;
431 return Ok(GenericApplicationId::User(application_id));
432 }
433 Err(anyhow!("Invalid parsing of GenericApplicationId"))
434 }
435}
436
437impl<A> From<ApplicationId<A>> for AccountOwner {
438 fn from(app_id: ApplicationId<A>) -> Self {
439 AccountOwner::Address32(app_id.application_description_hash)
440 }
441}
442
443impl From<AccountPublicKey> for AccountOwner {
444 fn from(public_key: AccountPublicKey) -> Self {
445 match public_key {
446 AccountPublicKey::Ed25519(public_key) => public_key.into(),
447 AccountPublicKey::Secp256k1(public_key) => public_key.into(),
448 AccountPublicKey::EvmSecp256k1(public_key) => public_key.into(),
449 }
450 }
451}
452
453impl From<ApplicationId> for GenericApplicationId {
454 fn from(application_id: ApplicationId) -> Self {
455 GenericApplicationId::User(application_id)
456 }
457}
458
459impl From<Secp256k1PublicKey> for AccountOwner {
460 fn from(public_key: Secp256k1PublicKey) -> Self {
461 AccountOwner::Address32(CryptoHash::new(&public_key))
462 }
463}
464
465impl From<Ed25519PublicKey> for AccountOwner {
466 fn from(public_key: Ed25519PublicKey) -> Self {
467 AccountOwner::Address32(CryptoHash::new(&public_key))
468 }
469}
470
471impl From<EvmPublicKey> for AccountOwner {
472 fn from(public_key: EvmPublicKey) -> Self {
473 AccountOwner::Address20(alloy_primitives::Address::from_public_key(&public_key.0).into())
474 }
475}
476
477#[derive(Debug, WitLoad, WitStore, WitType, Allocative)]
479#[cfg_attr(with_testing, derive(Default, test_strategy::Arbitrary))]
480pub struct ModuleId<Abi = (), Parameters = (), InstantiationArgument = ()> {
481 pub contract_blob_hash: CryptoHash,
483 pub service_blob_hash: CryptoHash,
485 pub vm_runtime: VmRuntime,
487 #[witty(skip)]
488 #[debug(skip)]
489 phantom: PhantomData<(Abi, Parameters, InstantiationArgument)>,
490}
491
492#[derive(
495 Clone,
496 Debug,
497 Eq,
498 Hash,
499 Ord,
500 PartialEq,
501 PartialOrd,
502 Serialize,
503 Deserialize,
504 WitLoad,
505 WitStore,
506 WitType,
507 Allocative,
508)]
509pub struct StreamName(
510 #[serde(with = "serde_bytes")]
511 #[debug(with = "hex_debug")]
512 pub Vec<u8>,
513);
514
515impl<T> From<T> for StreamName
516where
517 T: Into<Vec<u8>>,
518{
519 fn from(name: T) -> Self {
520 StreamName(name.into())
521 }
522}
523
524impl fmt::Display for StreamName {
525 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
526 Display::fmt(&hex::encode(&self.0), f)
527 }
528}
529
530impl std::str::FromStr for StreamName {
531 type Err = anyhow::Error;
532
533 fn from_str(s: &str) -> Result<Self, Self::Err> {
534 let vec = hex::decode(s)?;
535 Ok(StreamName(vec))
536 }
537}
538
539#[derive(
541 Clone,
542 Debug,
543 Eq,
544 Hash,
545 Ord,
546 PartialEq,
547 PartialOrd,
548 WitLoad,
549 WitStore,
550 WitType,
551 SimpleObject,
552 InputObject,
553 Allocative,
554)]
555#[graphql(input_name = "StreamIdInput")]
556pub struct StreamId {
557 pub application_id: GenericApplicationId,
559 pub stream_name: StreamName,
561}
562
563impl serde::Serialize for StreamId {
564 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
565 where
566 S: serde::ser::Serializer,
567 {
568 if serializer.is_human_readable() {
569 serializer.serialize_str(&self.to_string())
570 } else {
571 use serde::ser::SerializeStruct;
572 let mut state = serializer.serialize_struct("StreamId", 2)?;
573 state.serialize_field("application_id", &self.application_id)?;
574 state.serialize_field("stream_name", &self.stream_name)?;
575 state.end()
576 }
577 }
578}
579
580impl<'de> serde::Deserialize<'de> for StreamId {
581 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
582 where
583 D: serde::de::Deserializer<'de>,
584 {
585 if deserializer.is_human_readable() {
586 let s = String::deserialize(deserializer)?;
587 Self::from_str(&s).map_err(serde::de::Error::custom)
588 } else {
589 #[derive(serde::Deserialize)]
590 #[serde(rename = "StreamId")]
591 struct StreamIdHelper {
592 application_id: GenericApplicationId,
593 stream_name: StreamName,
594 }
595 let helper = StreamIdHelper::deserialize(deserializer)?;
596 Ok(StreamId {
597 application_id: helper.application_id,
598 stream_name: helper.stream_name,
599 })
600 }
601 }
602}
603
604impl StreamId {
605 pub fn system(name: impl Into<StreamName>) -> Self {
607 StreamId {
608 application_id: GenericApplicationId::System,
609 stream_name: name.into(),
610 }
611 }
612}
613
614#[derive(
616 Debug,
617 Eq,
618 PartialEq,
619 Ord,
620 PartialOrd,
621 Clone,
622 Hash,
623 Serialize,
624 Deserialize,
625 WitLoad,
626 WitStore,
627 WitType,
628 SimpleObject,
629)]
630pub struct IndexAndEvent {
631 pub index: u32,
633 pub event: Vec<u8>,
635}
636
637impl fmt::Display for StreamId {
638 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
639 Display::fmt(&self.application_id, f)?;
640 Display::fmt(":", f)?;
641 Display::fmt(&self.stream_name, f)
642 }
643}
644
645impl std::str::FromStr for StreamId {
646 type Err = anyhow::Error;
647
648 fn from_str(s: &str) -> Result<Self, Self::Err> {
649 let parts = s.rsplit_once(":");
650 if let Some((part0, part1)) = parts {
651 let application_id =
652 GenericApplicationId::from_str(part0).context("Invalid GenericApplicationId!")?;
653 let stream_name = StreamName::from_str(part1).context("Invalid StreamName!")?;
654 Ok(StreamId {
655 application_id,
656 stream_name,
657 })
658 } else {
659 Err(anyhow!("Invalid blob ID: {s}"))
660 }
661 }
662}
663
664#[derive(
666 Debug,
667 PartialEq,
668 Eq,
669 Hash,
670 Clone,
671 Serialize,
672 Deserialize,
673 WitLoad,
674 WitStore,
675 WitType,
676 SimpleObject,
677 Allocative,
678)]
679pub struct EventId {
680 pub chain_id: ChainId,
682 pub stream_id: StreamId,
684 pub index: u32,
686}
687
688impl StreamName {
689 pub fn into_bytes(self) -> Vec<u8> {
691 self.0
692 }
693}
694
695impl<Abi, Parameters, InstantiationArgument> Clone
697 for ModuleId<Abi, Parameters, InstantiationArgument>
698{
699 fn clone(&self) -> Self {
700 *self
701 }
702}
703
704impl<Abi, Parameters, InstantiationArgument> Copy
705 for ModuleId<Abi, Parameters, InstantiationArgument>
706{
707}
708
709impl<Abi, Parameters, InstantiationArgument> PartialEq
710 for ModuleId<Abi, Parameters, InstantiationArgument>
711{
712 fn eq(&self, other: &Self) -> bool {
713 let ModuleId {
714 contract_blob_hash,
715 service_blob_hash,
716 vm_runtime,
717 phantom: _,
718 } = other;
719 self.contract_blob_hash == *contract_blob_hash
720 && self.service_blob_hash == *service_blob_hash
721 && self.vm_runtime == *vm_runtime
722 }
723}
724
725impl<Abi, Parameters, InstantiationArgument> Eq
726 for ModuleId<Abi, Parameters, InstantiationArgument>
727{
728}
729
730impl<Abi, Parameters, InstantiationArgument> PartialOrd
731 for ModuleId<Abi, Parameters, InstantiationArgument>
732{
733 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
734 Some(self.cmp(other))
735 }
736}
737
738impl<Abi, Parameters, InstantiationArgument> Ord
739 for ModuleId<Abi, Parameters, InstantiationArgument>
740{
741 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
742 let ModuleId {
743 contract_blob_hash,
744 service_blob_hash,
745 vm_runtime,
746 phantom: _,
747 } = other;
748 (
749 self.contract_blob_hash,
750 self.service_blob_hash,
751 self.vm_runtime,
752 )
753 .cmp(&(*contract_blob_hash, *service_blob_hash, *vm_runtime))
754 }
755}
756
757impl<Abi, Parameters, InstantiationArgument> Hash
758 for ModuleId<Abi, Parameters, InstantiationArgument>
759{
760 fn hash<H: Hasher>(&self, state: &mut H) {
761 let ModuleId {
762 contract_blob_hash: contract_blob_id,
763 service_blob_hash: service_blob_id,
764 vm_runtime: vm_runtime_id,
765 phantom: _,
766 } = self;
767 contract_blob_id.hash(state);
768 service_blob_id.hash(state);
769 vm_runtime_id.hash(state);
770 }
771}
772
773#[derive(Serialize, Deserialize)]
774#[serde(rename = "ModuleId")]
775struct SerializableModuleId {
776 contract_blob_hash: CryptoHash,
777 service_blob_hash: CryptoHash,
778 vm_runtime: VmRuntime,
779}
780
781impl<Abi, Parameters, InstantiationArgument> Serialize
782 for ModuleId<Abi, Parameters, InstantiationArgument>
783{
784 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
785 where
786 S: serde::ser::Serializer,
787 {
788 let serializable_module_id = SerializableModuleId {
789 contract_blob_hash: self.contract_blob_hash,
790 service_blob_hash: self.service_blob_hash,
791 vm_runtime: self.vm_runtime,
792 };
793 if serializer.is_human_readable() {
794 let bytes =
795 bcs::to_bytes(&serializable_module_id).map_err(serde::ser::Error::custom)?;
796 serializer.serialize_str(&hex::encode(bytes))
797 } else {
798 SerializableModuleId::serialize(&serializable_module_id, serializer)
799 }
800 }
801}
802
803impl<'de, Abi, Parameters, InstantiationArgument> Deserialize<'de>
804 for ModuleId<Abi, Parameters, InstantiationArgument>
805{
806 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
807 where
808 D: serde::de::Deserializer<'de>,
809 {
810 if deserializer.is_human_readable() {
811 let s = String::deserialize(deserializer)?;
812 let module_id_bytes = hex::decode(s).map_err(serde::de::Error::custom)?;
813 let serializable_module_id: SerializableModuleId =
814 bcs::from_bytes(&module_id_bytes).map_err(serde::de::Error::custom)?;
815 Ok(ModuleId {
816 contract_blob_hash: serializable_module_id.contract_blob_hash,
817 service_blob_hash: serializable_module_id.service_blob_hash,
818 vm_runtime: serializable_module_id.vm_runtime,
819 phantom: PhantomData,
820 })
821 } else {
822 let serializable_module_id = SerializableModuleId::deserialize(deserializer)?;
823 Ok(ModuleId {
824 contract_blob_hash: serializable_module_id.contract_blob_hash,
825 service_blob_hash: serializable_module_id.service_blob_hash,
826 vm_runtime: serializable_module_id.vm_runtime,
827 phantom: PhantomData,
828 })
829 }
830 }
831}
832
833impl ModuleId {
834 pub fn new(
836 contract_blob_hash: CryptoHash,
837 service_blob_hash: CryptoHash,
838 vm_runtime: VmRuntime,
839 ) -> Self {
840 ModuleId {
841 contract_blob_hash,
842 service_blob_hash,
843 vm_runtime,
844 phantom: PhantomData,
845 }
846 }
847
848 pub fn with_abi<Abi, Parameters, InstantiationArgument>(
850 self,
851 ) -> ModuleId<Abi, Parameters, InstantiationArgument> {
852 ModuleId {
853 contract_blob_hash: self.contract_blob_hash,
854 service_blob_hash: self.service_blob_hash,
855 vm_runtime: self.vm_runtime,
856 phantom: PhantomData,
857 }
858 }
859
860 pub fn contract_bytecode_blob_id(&self) -> BlobId {
862 match self.vm_runtime {
863 VmRuntime::Wasm => BlobId::new(self.contract_blob_hash, BlobType::ContractBytecode),
864 VmRuntime::Evm => BlobId::new(self.contract_blob_hash, BlobType::EvmBytecode),
865 }
866 }
867
868 pub fn service_bytecode_blob_id(&self) -> BlobId {
870 match self.vm_runtime {
871 VmRuntime::Wasm => BlobId::new(self.service_blob_hash, BlobType::ServiceBytecode),
872 VmRuntime::Evm => BlobId::new(self.contract_blob_hash, BlobType::EvmBytecode),
873 }
874 }
875
876 pub fn bytecode_blob_ids(&self) -> Vec<BlobId> {
878 match self.vm_runtime {
879 VmRuntime::Wasm => vec![
880 BlobId::new(self.contract_blob_hash, BlobType::ContractBytecode),
881 BlobId::new(self.service_blob_hash, BlobType::ServiceBytecode),
882 ],
883 VmRuntime::Evm => vec![BlobId::new(self.contract_blob_hash, BlobType::EvmBytecode)],
884 }
885 }
886}
887
888impl<Abi, Parameters, InstantiationArgument> ModuleId<Abi, Parameters, InstantiationArgument> {
889 pub fn forget_abi(self) -> ModuleId {
891 ModuleId {
892 contract_blob_hash: self.contract_blob_hash,
893 service_blob_hash: self.service_blob_hash,
894 vm_runtime: self.vm_runtime,
895 phantom: PhantomData,
896 }
897 }
898}
899
900impl<A> Clone for ApplicationId<A> {
902 fn clone(&self) -> Self {
903 *self
904 }
905}
906
907impl<A> Copy for ApplicationId<A> {}
908
909impl<A: PartialEq> PartialEq for ApplicationId<A> {
910 fn eq(&self, other: &Self) -> bool {
911 self.application_description_hash == other.application_description_hash
912 }
913}
914
915impl<A: Eq> Eq for ApplicationId<A> {}
916
917impl<A: PartialOrd> PartialOrd for ApplicationId<A> {
918 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
919 self.application_description_hash
920 .partial_cmp(&other.application_description_hash)
921 }
922}
923
924impl<A: Ord> Ord for ApplicationId<A> {
925 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
926 self.application_description_hash
927 .cmp(&other.application_description_hash)
928 }
929}
930
931impl<A> Hash for ApplicationId<A> {
932 fn hash<H: Hasher>(&self, state: &mut H) {
933 self.application_description_hash.hash(state);
934 }
935}
936
937#[derive(Serialize, Deserialize)]
938#[serde(rename = "ApplicationId")]
939struct SerializableApplicationId {
940 pub application_description_hash: CryptoHash,
941}
942
943impl<A> Serialize for ApplicationId<A> {
944 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
945 where
946 S: serde::ser::Serializer,
947 {
948 if serializer.is_human_readable() {
949 let bytes = bcs::to_bytes(&SerializableApplicationId {
950 application_description_hash: self.application_description_hash,
951 })
952 .map_err(serde::ser::Error::custom)?;
953 serializer.serialize_str(&hex::encode(bytes))
954 } else {
955 SerializableApplicationId::serialize(
956 &SerializableApplicationId {
957 application_description_hash: self.application_description_hash,
958 },
959 serializer,
960 )
961 }
962 }
963}
964
965impl<'de, A> Deserialize<'de> for ApplicationId<A> {
966 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
967 where
968 D: serde::de::Deserializer<'de>,
969 {
970 if deserializer.is_human_readable() {
971 let s = String::deserialize(deserializer)?;
972 let application_id_bytes = hex::decode(s).map_err(serde::de::Error::custom)?;
973 let application_id: SerializableApplicationId =
974 bcs::from_bytes(&application_id_bytes).map_err(serde::de::Error::custom)?;
975 Ok(ApplicationId {
976 application_description_hash: application_id.application_description_hash,
977 phantom: PhantomData,
978 })
979 } else {
980 let value = SerializableApplicationId::deserialize(deserializer)?;
981 Ok(ApplicationId {
982 application_description_hash: value.application_description_hash,
983 phantom: PhantomData,
984 })
985 }
986 }
987}
988
989impl ApplicationId {
990 pub fn new(application_description_hash: CryptoHash) -> Self {
992 ApplicationId {
993 application_description_hash,
994 phantom: PhantomData,
995 }
996 }
997
998 pub fn description_blob_id(self) -> BlobId {
1001 BlobId::new(
1002 self.application_description_hash,
1003 BlobType::ApplicationDescription,
1004 )
1005 }
1006
1007 pub fn with_abi<A>(self) -> ApplicationId<A> {
1009 ApplicationId {
1010 application_description_hash: self.application_description_hash,
1011 phantom: PhantomData,
1012 }
1013 }
1014}
1015
1016impl<A> ApplicationId<A> {
1017 pub fn forget_abi(self) -> ApplicationId {
1019 ApplicationId {
1020 application_description_hash: self.application_description_hash,
1021 phantom: PhantomData,
1022 }
1023 }
1024}
1025
1026#[cfg(with_revm)]
1027impl<A> ApplicationId<A> {
1028 pub fn evm_address(&self) -> Address {
1030 let bytes = self.application_description_hash.as_bytes();
1031 let bytes = bytes.0.as_ref();
1032 Address::from_slice(&bytes[0..20])
1033 }
1034
1035 pub fn bytes32(&self) -> B256 {
1037 *self.application_description_hash.as_bytes()
1038 }
1039
1040 pub fn is_evm(&self) -> bool {
1042 let bytes = self.application_description_hash.as_bytes();
1043 let bytes = bytes.0.as_ref();
1044 for byte in &bytes[20..] {
1045 if byte != &0 {
1046 return false;
1047 }
1048 }
1049 true
1050 }
1051}
1052
1053#[derive(Serialize, Deserialize)]
1054#[serde(rename = "AccountOwner")]
1055enum SerializableAccountOwner {
1056 Reserved(u8),
1057 Address32(CryptoHash),
1058 Address20([u8; 20]),
1059}
1060
1061impl Serialize for AccountOwner {
1062 fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1063 if serializer.is_human_readable() {
1064 serializer.serialize_str(&self.to_string())
1065 } else {
1066 match self {
1067 AccountOwner::Reserved(value) => SerializableAccountOwner::Reserved(*value),
1068 AccountOwner::Address32(value) => SerializableAccountOwner::Address32(*value),
1069 AccountOwner::Address20(value) => SerializableAccountOwner::Address20(*value),
1070 }
1071 .serialize(serializer)
1072 }
1073 }
1074}
1075
1076impl<'de> Deserialize<'de> for AccountOwner {
1077 fn deserialize<D: serde::de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1078 if deserializer.is_human_readable() {
1079 let s = String::deserialize(deserializer)?;
1080 let value = Self::from_str(&s).map_err(serde::de::Error::custom)?;
1081 Ok(value)
1082 } else {
1083 let value = SerializableAccountOwner::deserialize(deserializer)?;
1084 match value {
1085 SerializableAccountOwner::Reserved(value) => Ok(AccountOwner::Reserved(value)),
1086 SerializableAccountOwner::Address32(value) => Ok(AccountOwner::Address32(value)),
1087 SerializableAccountOwner::Address20(value) => Ok(AccountOwner::Address20(value)),
1088 }
1089 }
1090 }
1091}
1092
1093impl fmt::Display for AccountOwner {
1094 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1095 match self {
1096 AccountOwner::Reserved(value) => {
1097 write!(f, "0x{}", hex::encode(&value.to_be_bytes()[..]))?
1098 }
1099 AccountOwner::Address32(value) => write!(f, "0x{value}")?,
1100 AccountOwner::Address20(value) => write!(f, "0x{}", hex::encode(&value[..]))?,
1101 };
1102
1103 Ok(())
1104 }
1105}
1106
1107impl std::str::FromStr for AccountOwner {
1108 type Err = anyhow::Error;
1109
1110 fn from_str(s: &str) -> Result<Self, Self::Err> {
1111 if let Some(s) = s.strip_prefix("0x") {
1112 if s.len() == 64 {
1113 if let Ok(hash) = CryptoHash::from_str(s) {
1114 return Ok(AccountOwner::Address32(hash));
1115 }
1116 } else if s.len() == 40 {
1117 let address = hex::decode(s)?;
1118 if address.len() != 20 {
1119 anyhow::bail!("Invalid address length: {s}");
1120 }
1121 let address = <[u8; 20]>::try_from(address.as_slice()).unwrap();
1122 return Ok(AccountOwner::Address20(address));
1123 }
1124 if s.len() == 2 {
1125 let bytes = hex::decode(s)?;
1126 if bytes.len() == 1 {
1127 let value = u8::from_be_bytes(bytes.try_into().expect("one byte"));
1128 return Ok(AccountOwner::Reserved(value));
1129 }
1130 }
1131 }
1132 anyhow::bail!("Invalid address value: {s}");
1133 }
1134}
1135
1136impl fmt::Display for ChainId {
1137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1138 Display::fmt(&self.0, f)
1139 }
1140}
1141
1142impl std::str::FromStr for ChainId {
1143 type Err = CryptoError;
1144
1145 fn from_str(s: &str) -> Result<Self, Self::Err> {
1146 Ok(ChainId(CryptoHash::from_str(s)?))
1147 }
1148}
1149
1150impl TryFrom<&[u8]> for ChainId {
1151 type Error = CryptoError;
1152
1153 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
1154 Ok(ChainId(CryptoHash::try_from(value)?))
1155 }
1156}
1157
1158impl fmt::Debug for ChainId {
1159 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
1160 write!(f, "{:?}", self.0)
1161 }
1162}
1163
1164impl<'a> From<&'a ChainDescription> for ChainId {
1165 fn from(description: &'a ChainDescription) -> Self {
1166 Self(CryptoHash::new(&BlobContent::new_chain_description(
1167 description,
1168 )))
1169 }
1170}
1171
1172impl From<ChainDescription> for ChainId {
1173 fn from(description: ChainDescription) -> Self {
1174 From::from(&description)
1175 }
1176}
1177
1178bcs_scalar!(ApplicationId, "A unique identifier for a user application");
1179doc_scalar!(DataBlobHash, "Hash of a Data Blob");
1180doc_scalar!(
1181 GenericApplicationId,
1182 "A unique identifier for a user application or for the system application"
1183);
1184bcs_scalar!(ModuleId, "A unique identifier for an application module");
1185doc_scalar!(
1186 ChainId,
1187 "The unique identifier (UID) of a chain. This is currently computed as the hash value of a \
1188 ChainDescription."
1189);
1190doc_scalar!(StreamName, "The name of an event stream");
1191
1192doc_scalar!(
1193 AccountOwner,
1194 "A unique identifier for a user or an application."
1195);
1196doc_scalar!(Account, "An account");
1197doc_scalar!(
1198 BlobId,
1199 "A content-addressed blob ID i.e. the hash of the `BlobContent`"
1200);
1201
1202#[cfg(test)]
1203mod tests {
1204 use std::str::FromStr as _;
1205
1206 use assert_matches::assert_matches;
1207
1208 use super::{AccountOwner, BlobType};
1209 use crate::{
1210 data_types::{Amount, ChainDescription, ChainOrigin, Epoch, InitialChainConfig, Timestamp},
1211 identifiers::{ApplicationId, CryptoHash, GenericApplicationId, StreamId, StreamName},
1212 ownership::ChainOwnership,
1213 };
1214
1215 #[test]
1217 fn chain_id_computing() {
1218 let example_chain_origin = ChainOrigin::Root(0);
1219 let example_chain_config = InitialChainConfig {
1220 epoch: Epoch::ZERO,
1221 ownership: ChainOwnership::single(AccountOwner::Reserved(0)),
1222 balance: Amount::ZERO,
1223 min_active_epoch: Epoch::ZERO,
1224 max_active_epoch: Epoch::ZERO,
1225 application_permissions: Default::default(),
1226 };
1227 let description = ChainDescription::new(
1228 example_chain_origin,
1229 example_chain_config,
1230 Timestamp::from(0),
1231 );
1232 assert_eq!(
1233 description.id().to_string(),
1234 "0b87a7cba23cf0d634a1f8eace084acb8fa110b8017db71a7f1bb159e4c752dd"
1235 );
1236 }
1237
1238 #[test]
1239 fn blob_types() {
1240 assert_eq!("ContractBytecode", BlobType::ContractBytecode.to_string());
1241 assert_eq!(
1242 BlobType::ContractBytecode,
1243 BlobType::from_str("ContractBytecode").unwrap()
1244 );
1245 }
1246
1247 #[test]
1248 fn addresses() {
1249 assert_eq!(&AccountOwner::Reserved(0).to_string(), "0x00");
1250 assert_eq!(AccountOwner::from_str("0x00").unwrap(), AccountOwner::CHAIN);
1251
1252 let address = AccountOwner::from_str("0x10").unwrap();
1253 assert_eq!(address, AccountOwner::Reserved(16));
1254 assert_eq!(address.to_string(), "0x10");
1255
1256 let address = AccountOwner::from_str(
1257 "0x5487b70625ce71f7ee29154ad32aefa1c526cb483bdb783dea2e1d17bc497844",
1258 )
1259 .unwrap();
1260 assert_matches!(address, AccountOwner::Address32(_));
1261 assert_eq!(
1262 address.to_string(),
1263 "0x5487b70625ce71f7ee29154ad32aefa1c526cb483bdb783dea2e1d17bc497844"
1264 );
1265
1266 let address = AccountOwner::from_str("0x6E0ab7F37b667b7228D3a03116Ca21Be83213823").unwrap();
1267 assert_matches!(address, AccountOwner::Address20(_));
1268 assert_eq!(
1269 address.to_string(),
1270 "0x6e0ab7f37b667b7228d3a03116ca21be83213823"
1271 );
1272
1273 assert!(AccountOwner::from_str("0x5487b7").is_err());
1274 assert!(AccountOwner::from_str("0").is_err());
1275 assert!(AccountOwner::from_str(
1276 "5487b70625ce71f7ee29154ad32aefa1c526cb483bdb783dea2e1d17bc497844"
1277 )
1278 .is_err());
1279 }
1280
1281 #[test]
1282 fn accounts() {
1283 use super::{Account, ChainId};
1284
1285 const CHAIN: &str = "76e3a8c7b2449e6bc238642ac68b4311a809cb57328bea0a1ef9122f08a0053d";
1286 const OWNER: &str = "0x5487b70625ce71f7ee29154ad32aefa1c526cb483bdb783dea2e1d17bc497844";
1287
1288 let chain_id = ChainId::from_str(CHAIN).unwrap();
1289 let owner = AccountOwner::from_str(OWNER).unwrap();
1290
1291 let account = Account::from_str(CHAIN).unwrap();
1293 assert_eq!(
1294 account,
1295 Account::from_str(&format!("0x00@{CHAIN}")).unwrap()
1296 );
1297 assert_eq!(account, Account::chain(chain_id));
1298 assert_eq!(account.to_string(), format!("0x00@{CHAIN}"));
1299
1300 let account = Account::from_str(&format!("{OWNER}@{CHAIN}")).unwrap();
1302 assert_eq!(account, Account::new(chain_id, owner));
1303 assert_eq!(account.to_string(), format!("{OWNER}@{CHAIN}"));
1304 }
1305
1306 #[test]
1307 fn stream_name() {
1308 let vec = vec![32, 54, 120, 234];
1309 let stream_name1 = StreamName(vec);
1310 let stream_name2 = StreamName::from_str(&format!("{stream_name1}")).unwrap();
1311 assert_eq!(stream_name1, stream_name2);
1312 }
1313
1314 fn test_generic_application_id(application_id: GenericApplicationId) {
1315 let application_id2 = GenericApplicationId::from_str(&format!("{application_id}")).unwrap();
1316 assert_eq!(application_id, application_id2);
1317 }
1318
1319 #[test]
1320 fn generic_application_id() {
1321 test_generic_application_id(GenericApplicationId::System);
1322 let hash = CryptoHash::test_hash("test case");
1323 let application_id = ApplicationId::new(hash);
1324 test_generic_application_id(GenericApplicationId::User(application_id));
1325 }
1326
1327 #[test]
1328 fn stream_id() {
1329 let hash = CryptoHash::test_hash("test case");
1330 let application_id = ApplicationId::new(hash);
1331 let application_id = GenericApplicationId::User(application_id);
1332 let vec = vec![32, 54, 120, 234];
1333 let stream_name = StreamName(vec);
1334
1335 let stream_id1 = StreamId {
1336 application_id,
1337 stream_name,
1338 };
1339 let stream_id2 = StreamId::from_str(&format!("{stream_id1}")).unwrap();
1340 assert_eq!(stream_id1, stream_id2);
1341 }
1342
1343 #[test]
1344 fn ed25519_public_key_to_account_owner_known_vector() {
1345 use crate::crypto::Ed25519PublicKey;
1346 let pubkey_bytes: [u8; 32] = [
1368 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1369 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
1370 0x1d, 0x1e, 0x1f, 0x20,
1371 ];
1372 let pubkey = Ed25519PublicKey(pubkey_bytes);
1373 let owner = AccountOwner::from(pubkey);
1374 assert_eq!(
1378 owner.to_string(),
1379 "0xeacee5344cbec9569e836f95029d476c700f4f5bc007c71c0752c73fba149043",
1380 "Ed25519 owner derivation drifted; verify intentional before updating"
1381 );
1382 }
1383}