1#[cfg(test)]
16mod tests;
17mod validate;
18
19use core::fmt::Display;
20pub use validate::ValidateLayout;
21
22use crate::{
23 serde_hex,
24 utils::{
25 deserialize_from_byte_str,
26 serialize_as_byte_str,
27 },
28};
29use derive_more::From;
30use ink_prelude::collections::btree_map::BTreeMap;
31use ink_primitives::Key;
32use scale::{
33 Decode,
34 Encode,
35};
36use scale_info::{
37 form::{
38 Form,
39 MetaForm,
40 PortableForm,
41 },
42 meta_type,
43 IntoPortable,
44 Registry,
45 TypeInfo,
46};
47use schemars::JsonSchema;
48use serde::{
49 de::{
50 DeserializeOwned,
51 Error,
52 },
53 Deserialize,
54 Serialize,
55};
56
57#[derive(
59 Debug, PartialEq, Eq, PartialOrd, Ord, From, Serialize, Deserialize, JsonSchema,
60)]
61#[serde(bound(
62 serialize = "F::Type: Serialize, F::String: Serialize",
63 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
64))]
65#[serde(rename_all = "camelCase")]
66pub enum Layout<F: Form = MetaForm> {
67 Leaf(LeafLayout<F>),
72 Root(RootLayout<F>),
74 Hash(HashLayout<F>),
78 Array(ArrayLayout<F>),
80 Struct(StructLayout<F>),
82 Enum(EnumLayout<F>),
84}
85
86#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, From, JsonSchema)]
88pub struct LayoutKey {
89 key: Key,
90}
91
92impl serde::Serialize for LayoutKey {
93 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
94 where
95 S: serde::Serializer,
96 {
97 serde_hex::serialize(&self.key.encode(), serializer)
98 }
99}
100
101impl<'de> serde::Deserialize<'de> for LayoutKey {
102 fn deserialize<D>(d: D) -> Result<Self, D::Error>
103 where
104 D: serde::Deserializer<'de>,
105 {
106 let mut arr = [0; 4];
107 serde_hex::deserialize_check_len(d, serde_hex::ExpectedLen::Exact(&mut arr[..]))?;
108 let key = Key::decode(&mut &arr[..]).map_err(|err| {
109 Error::custom(format!("Error decoding layout key: {}", err))
110 })?;
111 Ok(key.into())
112 }
113}
114
115impl<'a> From<&'a Key> for LayoutKey {
116 fn from(key: &'a Key) -> Self {
117 Self { key: *key }
118 }
119}
120
121impl LayoutKey {
122 pub fn new<T>(key: T) -> Self
124 where
125 T: Into<u32>,
126 {
127 Self { key: key.into() }
128 }
129
130 pub fn key(&self) -> &Key {
132 &self.key
133 }
134}
135
136#[derive(
138 Debug, PartialEq, Eq, PartialOrd, Ord, From, Serialize, Deserialize, JsonSchema,
139)]
140#[serde(bound(
141 serialize = "F::Type: Serialize, F::String: Serialize",
142 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
143))]
144pub struct RootLayout<F: Form = MetaForm> {
145 #[schemars(with = "String")]
147 root_key: LayoutKey,
148 layout: Box<Layout<F>>,
150 ty: <F as Form>::Type,
152}
153
154impl IntoPortable for RootLayout {
155 type Output = RootLayout<PortableForm>;
156
157 fn into_portable(self, registry: &mut Registry) -> Self::Output {
158 RootLayout {
159 root_key: self.root_key,
160 layout: Box::new(self.layout.into_portable(registry)),
161 ty: registry.register_type(&self.ty),
162 }
163 }
164}
165
166impl RootLayout<MetaForm> {
167 pub fn new_empty<L>(root_key: LayoutKey, layout: L) -> Self
169 where
170 L: Into<Layout<MetaForm>>,
171 {
172 Self::new::<L>(root_key, layout, meta_type::<()>())
173 }
174}
175
176impl<F> RootLayout<F>
177where
178 F: Form,
179{
180 pub fn new<L>(root_key: LayoutKey, layout: L, ty: <F as Form>::Type) -> Self
182 where
183 L: Into<Layout<F>>,
184 {
185 Self {
186 root_key,
187 layout: Box::new(layout.into()),
188 ty,
189 }
190 }
191
192 pub fn root_key(&self) -> &LayoutKey {
194 &self.root_key
195 }
196
197 pub fn layout(&self) -> &Layout<F> {
199 &self.layout
200 }
201
202 pub fn ty(&self) -> &F::Type {
204 &self.ty
205 }
206}
207
208#[derive(
210 Debug, PartialEq, Eq, PartialOrd, Ord, From, Serialize, Deserialize, JsonSchema,
211)]
212#[serde(bound(
213 serialize = "F::Type: Serialize, F::String: Serialize",
214 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
215))]
216pub struct LeafLayout<F: Form = MetaForm> {
217 #[schemars(with = "String")]
219 key: LayoutKey,
220 ty: <F as Form>::Type,
222}
223
224impl LeafLayout {
225 pub fn from_key<T>(key: LayoutKey) -> Self
227 where
228 T: TypeInfo + 'static,
229 {
230 Self {
231 key,
232 ty: meta_type::<T>(),
233 }
234 }
235}
236
237impl IntoPortable for LeafLayout {
238 type Output = LeafLayout<PortableForm>;
239
240 fn into_portable(self, registry: &mut Registry) -> Self::Output {
241 LeafLayout {
242 key: self.key,
243 ty: registry.register_type(&self.ty),
244 }
245 }
246}
247
248impl IntoPortable for Layout {
249 type Output = Layout<PortableForm>;
250
251 fn into_portable(self, registry: &mut Registry) -> Self::Output {
252 match self {
253 Layout::Leaf(encoded_cell) => {
254 Layout::Leaf(encoded_cell.into_portable(registry))
255 }
256 Layout::Root(encoded_cell) => {
257 Layout::Root(encoded_cell.into_portable(registry))
258 }
259 Layout::Hash(hash_layout) => {
260 Layout::Hash(hash_layout.into_portable(registry))
261 }
262 Layout::Array(array_layout) => {
263 Layout::Array(array_layout.into_portable(registry))
264 }
265 Layout::Struct(struct_layout) => {
266 Layout::Struct(struct_layout.into_portable(registry))
267 }
268 Layout::Enum(enum_layout) => {
269 Layout::Enum(enum_layout.into_portable(registry))
270 }
271 }
272 }
273}
274
275impl<F> LeafLayout<F>
276where
277 F: Form,
278{
279 pub fn key(&self) -> &LayoutKey {
281 &self.key
282 }
283
284 pub fn ty(&self) -> &F::Type {
286 &self.ty
287 }
288
289 pub fn new(key: LayoutKey, ty: <F as Form>::Type) -> Self {
290 Self { key, ty }
291 }
292}
293
294#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)]
298#[serde(bound(
299 serialize = "F::Type: Serialize, F::String: Serialize",
300 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
301))]
302pub struct HashLayout<F: Form = MetaForm> {
303 #[schemars(with = "String")]
305 offset: LayoutKey,
306 strategy: HashingStrategy,
308 layout: Box<Layout<F>>,
310}
311
312impl IntoPortable for HashLayout {
313 type Output = HashLayout<PortableForm>;
314
315 fn into_portable(self, registry: &mut Registry) -> Self::Output {
316 HashLayout {
317 offset: self.offset,
318 strategy: self.strategy,
319 layout: Box::new(self.layout.into_portable(registry)),
320 }
321 }
322}
323
324impl HashLayout {
325 pub fn new<K, L>(offset: K, strategy: HashingStrategy, layout: L) -> Self
327 where
328 K: Into<LayoutKey>,
329 L: Into<Layout>,
330 {
331 Self {
332 offset: offset.into(),
333 strategy,
334 layout: Box::new(layout.into()),
335 }
336 }
337}
338
339impl<F> HashLayout<F>
340where
341 F: Form,
342{
343 pub fn offset(&self) -> &LayoutKey {
345 &self.offset
346 }
347
348 pub fn strategy(&self) -> &HashingStrategy {
350 &self.strategy
351 }
352
353 pub fn layout(&self) -> &Layout<F> {
355 &self.layout
356 }
357}
358
359#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)]
365pub struct HashingStrategy {
366 hasher: CryptoHasher,
368 #[serde(
370 serialize_with = "serialize_as_byte_str",
371 deserialize_with = "deserialize_from_byte_str"
372 )]
373 prefix: Vec<u8>,
374 #[serde(
376 serialize_with = "serialize_as_byte_str",
377 deserialize_with = "deserialize_from_byte_str"
378 )]
379 postfix: Vec<u8>,
380}
381
382impl HashingStrategy {
383 pub fn new(hasher: CryptoHasher, prefix: Vec<u8>, postfix: Vec<u8>) -> Self {
385 Self {
386 hasher,
387 prefix,
388 postfix,
389 }
390 }
391
392 pub fn hasher(&self) -> &CryptoHasher {
394 &self.hasher
395 }
396
397 pub fn prefix(&self) -> &[u8] {
399 &self.prefix
400 }
401
402 pub fn postfix(&self) -> &[u8] {
404 &self.postfix
405 }
406}
407
408#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)]
410pub enum CryptoHasher {
411 Blake2x256,
413 Sha2x256,
415 Keccak256,
417}
418
419#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)]
421#[serde(bound(
422 serialize = "F::Type: Serialize, F::String: Serialize",
423 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
424))]
425#[serde(rename_all = "camelCase")]
426pub struct ArrayLayout<F: Form = MetaForm> {
427 #[schemars(with = "String")]
431 offset: LayoutKey,
432 len: u32,
434 layout: Box<Layout<F>>,
436}
437
438impl ArrayLayout {
439 pub fn new<K, L>(at: K, len: u32, layout: L) -> Self
441 where
442 K: Into<LayoutKey>,
443 L: Into<Layout>,
444 {
445 Self {
446 offset: at.into(),
447 len,
448 layout: Box::new(layout.into()),
449 }
450 }
451}
452
453#[allow(clippy::len_without_is_empty)]
454impl<F> ArrayLayout<F>
455where
456 F: Form,
457{
458 pub fn offset(&self) -> &LayoutKey {
462 &self.offset
463 }
464
465 pub fn len(&self) -> u32 {
467 self.len
468 }
469
470 pub fn layout(&self) -> &Layout<F> {
472 &self.layout
473 }
474}
475
476impl IntoPortable for ArrayLayout {
477 type Output = ArrayLayout<PortableForm>;
478
479 fn into_portable(self, registry: &mut Registry) -> Self::Output {
480 ArrayLayout {
481 offset: self.offset,
482 len: self.len,
483 layout: Box::new(self.layout.into_portable(registry)),
484 }
485 }
486}
487
488#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)]
490#[serde(bound(
491 serialize = "F::Type: Serialize, F::String: Serialize",
492 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
493))]
494pub struct StructLayout<F: Form = MetaForm> {
495 name: F::String,
497 fields: Vec<FieldLayout<F>>,
499}
500
501impl<F> StructLayout<F>
502where
503 F: Form,
504{
505 pub fn new<N, T>(name: N, fields: T) -> Self
507 where
508 N: Into<F::String>,
509 T: IntoIterator<Item = FieldLayout<F>>,
510 {
511 Self {
512 name: name.into(),
513 fields: fields.into_iter().collect(),
514 }
515 }
516
517 pub fn name(&self) -> &F::String {
519 &self.name
520 }
521 pub fn fields(&self) -> &[FieldLayout<F>] {
523 &self.fields
524 }
525}
526
527impl IntoPortable for StructLayout {
528 type Output = StructLayout<PortableForm>;
529
530 fn into_portable(self, registry: &mut Registry) -> Self::Output {
531 StructLayout {
532 name: self.name.to_string(),
533 fields: self
534 .fields
535 .into_iter()
536 .map(|field| field.into_portable(registry))
537 .collect::<Vec<_>>(),
538 }
539 }
540}
541
542#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)]
544#[serde(bound(
545 serialize = "F::Type: Serialize, F::String: Serialize",
546 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
547))]
548pub struct FieldLayout<F: Form = MetaForm> {
549 name: F::String,
551 layout: Layout<F>,
556}
557
558impl<F> FieldLayout<F>
559where
560 F: Form,
561{
562 pub fn new<N, L>(name: N, layout: L) -> Self
564 where
565 N: Into<F::String>,
566 L: Into<Layout<F>>,
567 {
568 Self {
569 name: name.into(),
570 layout: layout.into(),
571 }
572 }
573
574 pub fn name(&self) -> &F::String {
576 &self.name
577 }
578
579 pub fn layout(&self) -> &Layout<F> {
584 &self.layout
585 }
586}
587
588impl IntoPortable for FieldLayout {
589 type Output = FieldLayout<PortableForm>;
590
591 fn into_portable(self, registry: &mut Registry) -> Self::Output {
592 FieldLayout {
593 name: self.name.to_string(),
594 layout: self.layout.into_portable(registry),
595 }
596 }
597}
598
599#[derive(
601 Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
602)]
603pub struct Discriminant(usize);
604
605impl From<usize> for Discriminant {
606 fn from(value: usize) -> Self {
607 Self(value)
608 }
609}
610
611impl Discriminant {
612 pub fn value(&self) -> usize {
614 self.0
615 }
616}
617
618#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)]
620#[serde(bound(
621 serialize = "F::Type: Serialize, F::String: Serialize",
622 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
623))]
624#[serde(rename_all = "camelCase")]
625pub struct EnumLayout<F: Form = MetaForm> {
626 name: F::String,
628 #[schemars(with = "String")]
630 dispatch_key: LayoutKey,
631 variants: BTreeMap<Discriminant, StructLayout<F>>,
633}
634
635impl EnumLayout {
636 pub fn new<N, K, V>(name: N, dispatch_key: K, variants: V) -> Self
638 where
639 N: Into<<MetaForm as Form>::String>,
640 K: Into<LayoutKey>,
641 V: IntoIterator<Item = (Discriminant, StructLayout)>,
642 {
643 Self {
644 name: name.into(),
645 dispatch_key: dispatch_key.into(),
646 variants: variants.into_iter().collect(),
647 }
648 }
649}
650
651impl<F> EnumLayout<F>
652where
653 F: Form,
654{
655 pub fn name(&self) -> &F::String {
657 &self.name
658 }
659
660 pub fn dispatch_key(&self) -> &LayoutKey {
662 &self.dispatch_key
663 }
664
665 pub fn variants(&self) -> &BTreeMap<Discriminant, StructLayout<F>> {
667 &self.variants
668 }
669}
670
671impl IntoPortable for EnumLayout {
672 type Output = EnumLayout<PortableForm>;
673
674 fn into_portable(self, registry: &mut Registry) -> Self::Output {
675 EnumLayout {
676 name: self.name.to_string(),
677 dispatch_key: self.dispatch_key,
678 variants: self
679 .variants
680 .into_iter()
681 .map(|(discriminant, layout)| {
682 (discriminant, layout.into_portable(registry))
683 })
684 .collect(),
685 }
686 }
687}
688
689#[derive(Debug, Clone, PartialEq, Eq)]
691pub enum MetadataError {
692 Collision(String, String),
694}
695
696impl Display for MetadataError {
697 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
698 match self {
699 Self::Collision(prev_path, curr_path) => {
700 write!(
701 f,
702 "storage key collision occurred for `{}`. \
703 The same storage key is occupied by the `{}`.",
704 curr_path,
705 if prev_path.is_empty() {
706 "contract storage"
707 } else {
708 prev_path
709 }
710 )
711 }
712 }
713 }
714}
715
716#[test]
717fn valid_error_message() {
718 assert_eq!(
719 MetadataError::Collision("".to_string(), "Contract.c:".to_string()).to_string(),
720 "storage key collision occurred for `Contract.c:`. \
721 The same storage key is occupied by the `contract storage`."
722 );
723 assert_eq!(
724 MetadataError::Collision("Contract.a:".to_string(), "Contract.c:".to_string())
725 .to_string(),
726 "storage key collision occurred for `Contract.c:`. \
727 The same storage key is occupied by the `Contract.a:`."
728 )
729}