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 IntoPortable,
38 Registry,
39 TypeInfo,
40 form::{
41 Form,
42 MetaForm,
43 PortableForm,
44 },
45 meta_type,
46};
47use schemars::JsonSchema;
48use serde::{
49 Deserialize,
50 Serialize,
51 de::{
52 DeserializeOwned,
53 Error,
54 },
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[..])
109 .map_err(|err| Error::custom(format!("Error decoding layout key: {err}")))?;
110 Ok(key.into())
111 }
112}
113
114impl<'a> From<&'a Key> for LayoutKey {
115 fn from(key: &'a Key) -> Self {
116 Self { key: *key }
117 }
118}
119
120impl LayoutKey {
121 pub fn new<T>(key: T) -> Self
123 where
124 T: Into<u32>,
125 {
126 Self { key: key.into() }
127 }
128
129 pub fn key(&self) -> &Key {
131 &self.key
132 }
133}
134
135#[derive(
137 Debug, PartialEq, Eq, PartialOrd, Ord, From, Serialize, Deserialize, JsonSchema,
138)]
139#[serde(bound(
140 serialize = "F::Type: Serialize, F::String: Serialize",
141 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
142))]
143pub struct RootLayout<F: Form = MetaForm> {
144 #[schemars(with = "String")]
146 root_key: LayoutKey,
147 layout: Box<Layout<F>>,
149 ty: <F as Form>::Type,
151}
152
153impl IntoPortable for RootLayout {
154 type Output = RootLayout<PortableForm>;
155
156 fn into_portable(self, registry: &mut Registry) -> Self::Output {
157 RootLayout {
158 root_key: self.root_key,
159 layout: Box::new(self.layout.into_portable(registry)),
160 ty: registry.register_type(&self.ty),
161 }
162 }
163}
164
165impl RootLayout<MetaForm> {
166 pub fn new_empty<L>(root_key: LayoutKey, layout: L) -> Self
168 where
169 L: Into<Layout<MetaForm>>,
170 {
171 Self::new::<L>(root_key, layout, meta_type::<()>())
172 }
173}
174
175impl<F> RootLayout<F>
176where
177 F: Form,
178{
179 pub fn new<L>(root_key: LayoutKey, layout: L, ty: <F as Form>::Type) -> Self
181 where
182 L: Into<Layout<F>>,
183 {
184 Self {
185 root_key,
186 layout: Box::new(layout.into()),
187 ty,
188 }
189 }
190
191 pub fn root_key(&self) -> &LayoutKey {
193 &self.root_key
194 }
195
196 pub fn layout(&self) -> &Layout<F> {
198 &self.layout
199 }
200
201 pub fn ty(&self) -> &F::Type {
203 &self.ty
204 }
205}
206
207#[derive(
209 Debug, PartialEq, Eq, PartialOrd, Ord, From, Serialize, Deserialize, JsonSchema,
210)]
211#[serde(bound(
212 serialize = "F::Type: Serialize, F::String: Serialize",
213 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
214))]
215pub struct LeafLayout<F: Form = MetaForm> {
216 #[schemars(with = "String")]
218 key: LayoutKey,
219 ty: <F as Form>::Type,
221}
222
223impl LeafLayout {
224 pub fn from_key<T>(key: LayoutKey) -> Self
226 where
227 T: TypeInfo + 'static,
228 {
229 Self {
230 key,
231 ty: meta_type::<T>(),
232 }
233 }
234}
235
236impl IntoPortable for LeafLayout {
237 type Output = LeafLayout<PortableForm>;
238
239 fn into_portable(self, registry: &mut Registry) -> Self::Output {
240 LeafLayout {
241 key: self.key,
242 ty: registry.register_type(&self.ty),
243 }
244 }
245}
246
247impl IntoPortable for Layout {
248 type Output = Layout<PortableForm>;
249
250 fn into_portable(self, registry: &mut Registry) -> Self::Output {
251 match self {
252 Layout::Leaf(encoded_cell) => {
253 Layout::Leaf(encoded_cell.into_portable(registry))
254 }
255 Layout::Root(encoded_cell) => {
256 Layout::Root(encoded_cell.into_portable(registry))
257 }
258 Layout::Hash(hash_layout) => {
259 Layout::Hash(hash_layout.into_portable(registry))
260 }
261 Layout::Array(array_layout) => {
262 Layout::Array(array_layout.into_portable(registry))
263 }
264 Layout::Struct(struct_layout) => {
265 Layout::Struct(struct_layout.into_portable(registry))
266 }
267 Layout::Enum(enum_layout) => {
268 Layout::Enum(enum_layout.into_portable(registry))
269 }
270 }
271 }
272}
273
274impl<F> LeafLayout<F>
275where
276 F: Form,
277{
278 pub fn key(&self) -> &LayoutKey {
280 &self.key
281 }
282
283 pub fn ty(&self) -> &F::Type {
285 &self.ty
286 }
287
288 pub fn new(key: LayoutKey, ty: <F as Form>::Type) -> Self {
289 Self { key, ty }
290 }
291}
292
293#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)]
297#[serde(bound(
298 serialize = "F::Type: Serialize, F::String: Serialize",
299 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
300))]
301pub struct HashLayout<F: Form = MetaForm> {
302 #[schemars(with = "String")]
304 offset: LayoutKey,
305 strategy: HashingStrategy,
307 layout: Box<Layout<F>>,
309}
310
311impl IntoPortable for HashLayout {
312 type Output = HashLayout<PortableForm>;
313
314 fn into_portable(self, registry: &mut Registry) -> Self::Output {
315 HashLayout {
316 offset: self.offset,
317 strategy: self.strategy,
318 layout: Box::new(self.layout.into_portable(registry)),
319 }
320 }
321}
322
323impl HashLayout {
324 pub fn new<K, L>(offset: K, strategy: HashingStrategy, layout: L) -> Self
326 where
327 K: Into<LayoutKey>,
328 L: Into<Layout>,
329 {
330 Self {
331 offset: offset.into(),
332 strategy,
333 layout: Box::new(layout.into()),
334 }
335 }
336}
337
338impl<F> HashLayout<F>
339where
340 F: Form,
341{
342 pub fn offset(&self) -> &LayoutKey {
344 &self.offset
345 }
346
347 pub fn strategy(&self) -> &HashingStrategy {
349 &self.strategy
350 }
351
352 pub fn layout(&self) -> &Layout<F> {
354 &self.layout
355 }
356}
357
358#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)]
364pub struct HashingStrategy {
365 hasher: CryptoHasher,
367 #[serde(
369 serialize_with = "serialize_as_byte_str",
370 deserialize_with = "deserialize_from_byte_str"
371 )]
372 prefix: Vec<u8>,
373 #[serde(
375 serialize_with = "serialize_as_byte_str",
376 deserialize_with = "deserialize_from_byte_str"
377 )]
378 postfix: Vec<u8>,
379}
380
381impl HashingStrategy {
382 pub fn new(hasher: CryptoHasher, prefix: Vec<u8>, postfix: Vec<u8>) -> Self {
384 Self {
385 hasher,
386 prefix,
387 postfix,
388 }
389 }
390
391 pub fn hasher(&self) -> &CryptoHasher {
393 &self.hasher
394 }
395
396 pub fn prefix(&self) -> &[u8] {
398 &self.prefix
399 }
400
401 pub fn postfix(&self) -> &[u8] {
403 &self.postfix
404 }
405}
406
407#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)]
409pub enum CryptoHasher {
410 Blake2x256,
412 Sha2x256,
414 Keccak256,
416}
417
418#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)]
420#[serde(bound(
421 serialize = "F::Type: Serialize, F::String: Serialize",
422 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
423))]
424#[serde(rename_all = "camelCase")]
425pub struct ArrayLayout<F: Form = MetaForm> {
426 #[schemars(with = "String")]
430 offset: LayoutKey,
431 len: u32,
433 layout: Box<Layout<F>>,
435}
436
437impl ArrayLayout {
438 pub fn new<K, L>(at: K, len: u32, layout: L) -> Self
440 where
441 K: Into<LayoutKey>,
442 L: Into<Layout>,
443 {
444 Self {
445 offset: at.into(),
446 len,
447 layout: Box::new(layout.into()),
448 }
449 }
450}
451
452#[allow(clippy::len_without_is_empty)]
453impl<F> ArrayLayout<F>
454where
455 F: Form,
456{
457 pub fn offset(&self) -> &LayoutKey {
461 &self.offset
462 }
463
464 pub fn len(&self) -> u32 {
466 self.len
467 }
468
469 pub fn layout(&self) -> &Layout<F> {
471 &self.layout
472 }
473}
474
475impl IntoPortable for ArrayLayout {
476 type Output = ArrayLayout<PortableForm>;
477
478 fn into_portable(self, registry: &mut Registry) -> Self::Output {
479 ArrayLayout {
480 offset: self.offset,
481 len: self.len,
482 layout: Box::new(self.layout.into_portable(registry)),
483 }
484 }
485}
486
487#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)]
489#[serde(bound(
490 serialize = "F::Type: Serialize, F::String: Serialize",
491 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
492))]
493pub struct StructLayout<F: Form = MetaForm> {
494 name: F::String,
496 fields: Vec<FieldLayout<F>>,
498}
499
500impl<F> StructLayout<F>
501where
502 F: Form,
503{
504 pub fn new<N, T>(name: N, fields: T) -> Self
506 where
507 N: Into<F::String>,
508 T: IntoIterator<Item = FieldLayout<F>>,
509 {
510 Self {
511 name: name.into(),
512 fields: fields.into_iter().collect(),
513 }
514 }
515
516 pub fn name(&self) -> &F::String {
518 &self.name
519 }
520 pub fn fields(&self) -> &[FieldLayout<F>] {
522 &self.fields
523 }
524}
525
526impl IntoPortable for StructLayout {
527 type Output = StructLayout<PortableForm>;
528
529 fn into_portable(self, registry: &mut Registry) -> Self::Output {
530 StructLayout {
531 name: self.name.to_string(),
532 fields: self
533 .fields
534 .into_iter()
535 .map(|field| field.into_portable(registry))
536 .collect::<Vec<_>>(),
537 }
538 }
539}
540
541#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)]
543#[serde(bound(
544 serialize = "F::Type: Serialize, F::String: Serialize",
545 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
546))]
547pub struct FieldLayout<F: Form = MetaForm> {
548 name: F::String,
550 layout: Layout<F>,
555}
556
557impl<F> FieldLayout<F>
558where
559 F: Form,
560{
561 pub fn new<N, L>(name: N, layout: L) -> Self
563 where
564 N: Into<F::String>,
565 L: Into<Layout<F>>,
566 {
567 Self {
568 name: name.into(),
569 layout: layout.into(),
570 }
571 }
572
573 pub fn name(&self) -> &F::String {
575 &self.name
576 }
577
578 pub fn layout(&self) -> &Layout<F> {
583 &self.layout
584 }
585}
586
587impl IntoPortable for FieldLayout {
588 type Output = FieldLayout<PortableForm>;
589
590 fn into_portable(self, registry: &mut Registry) -> Self::Output {
591 FieldLayout {
592 name: self.name.to_string(),
593 layout: self.layout.into_portable(registry),
594 }
595 }
596}
597
598#[derive(
600 Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
601)]
602pub struct Discriminant(usize);
603
604impl From<usize> for Discriminant {
605 fn from(value: usize) -> Self {
606 Self(value)
607 }
608}
609
610impl Discriminant {
611 pub fn value(&self) -> usize {
613 self.0
614 }
615}
616
617#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)]
619#[serde(bound(
620 serialize = "F::Type: Serialize, F::String: Serialize",
621 deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
622))]
623#[serde(rename_all = "camelCase")]
624pub struct EnumLayout<F: Form = MetaForm> {
625 name: F::String,
627 #[schemars(with = "String")]
629 dispatch_key: LayoutKey,
630 variants: BTreeMap<Discriminant, StructLayout<F>>,
632}
633
634impl EnumLayout {
635 pub fn new<N, K, V>(name: N, dispatch_key: K, variants: V) -> Self
637 where
638 N: Into<<MetaForm as Form>::String>,
639 K: Into<LayoutKey>,
640 V: IntoIterator<Item = (Discriminant, StructLayout)>,
641 {
642 Self {
643 name: name.into(),
644 dispatch_key: dispatch_key.into(),
645 variants: variants.into_iter().collect(),
646 }
647 }
648}
649
650impl<F> EnumLayout<F>
651where
652 F: Form,
653{
654 pub fn name(&self) -> &F::String {
656 &self.name
657 }
658
659 pub fn dispatch_key(&self) -> &LayoutKey {
661 &self.dispatch_key
662 }
663
664 pub fn variants(&self) -> &BTreeMap<Discriminant, StructLayout<F>> {
666 &self.variants
667 }
668}
669
670impl IntoPortable for EnumLayout {
671 type Output = EnumLayout<PortableForm>;
672
673 fn into_portable(self, registry: &mut Registry) -> Self::Output {
674 EnumLayout {
675 name: self.name.to_string(),
676 dispatch_key: self.dispatch_key,
677 variants: self
678 .variants
679 .into_iter()
680 .map(|(discriminant, layout)| {
681 (discriminant, layout.into_portable(registry))
682 })
683 .collect(),
684 }
685 }
686}
687
688#[derive(Debug, Clone, PartialEq, Eq)]
690pub enum MetadataError {
691 Collision(String, String),
693}
694
695impl Display for MetadataError {
696 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
697 match self {
698 Self::Collision(prev_path, curr_path) => {
699 write!(
700 f,
701 "storage key collision occurred for `{}`. \
702 The same storage key is occupied by the `{}`.",
703 curr_path,
704 if prev_path.is_empty() {
705 "contract storage"
706 } else {
707 prev_path
708 }
709 )
710 }
711 }
712 }
713}
714
715#[test]
716fn valid_error_message() {
717 assert_eq!(
718 MetadataError::Collision("".to_string(), "Contract.c:".to_string()).to_string(),
719 "storage key collision occurred for `Contract.c:`. \
720 The same storage key is occupied by the `contract storage`."
721 );
722 assert_eq!(
723 MetadataError::Collision("Contract.a:".to_string(), "Contract.c:".to_string())
724 .to_string(),
725 "storage key collision occurred for `Contract.c:`. \
726 The same storage key is occupied by the `Contract.a:`."
727 )
728}