@openzeppelin/upgrades-core 1.18.0 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/levenshtein.d.ts +4 -1
  2. package/dist/levenshtein.d.ts.map +1 -1
  3. package/dist/levenshtein.js +1 -1
  4. package/dist/levenshtein.js.map +1 -1
  5. package/dist/provider.d.ts.map +1 -1
  6. package/dist/provider.js +2 -0
  7. package/dist/provider.js.map +1 -1
  8. package/dist/storage/compare.d.ts +21 -3
  9. package/dist/storage/compare.d.ts.map +1 -1
  10. package/dist/storage/compare.js +80 -17
  11. package/dist/storage/compare.js.map +1 -1
  12. package/dist/storage/gap.d.ts +18 -0
  13. package/dist/storage/gap.d.ts.map +1 -0
  14. package/dist/storage/gap.js +29 -0
  15. package/dist/storage/gap.js.map +1 -0
  16. package/dist/storage/report-gap.test.d.ts +2 -0
  17. package/dist/storage/report-gap.test.d.ts.map +1 -0
  18. package/dist/storage/report-gap.test.js +106 -0
  19. package/dist/storage/report-gap.test.js.map +1 -0
  20. package/dist/storage/report-rename-retype.test.js +24 -0
  21. package/dist/storage/report-rename-retype.test.js.map +1 -1
  22. package/dist/storage/report.d.ts +1 -1
  23. package/dist/storage/report.d.ts.map +1 -1
  24. package/dist/storage/report.js +59 -10
  25. package/dist/storage/report.js.map +1 -1
  26. package/dist/storage.test.js +421 -2
  27. package/dist/storage.test.js.map +1 -1
  28. package/dist/validate/data.js +25 -5
  29. package/dist/validate/data.js.map +1 -1
  30. package/package.json +3 -3
  31. package/src/levenshtein.ts +4 -2
  32. package/src/provider.ts +2 -0
  33. package/src/storage/compare.ts +88 -18
  34. package/src/storage/gap.ts +26 -0
  35. package/src/storage/report-gap.test.ts +119 -0
  36. package/src/storage/report-gap.test.ts.md +59 -0
  37. package/src/storage/report-gap.test.ts.snap +0 -0
  38. package/src/storage/report-rename-retype.test.ts +27 -0
  39. package/src/storage/report-rename-retype.test.ts.md +7 -0
  40. package/src/storage/report-rename-retype.test.ts.snap +0 -0
  41. package/src/storage/report.test.ts.md +1 -4
  42. package/src/storage/report.test.ts.snap +0 -0
  43. package/src/storage/report.ts +83 -12
  44. package/src/storage.test.ts +466 -2
  45. package/src/storage.test.ts.snap +0 -0
  46. package/src/validate/data.ts +1 -1
  47. package/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json +0 -160
@@ -18,7 +18,6 @@ Generated by [AVA](https://avajs.dev).
18
18
  - Bad upgrade from struct StorageUpgrade_Struct_V1.Struct1 to struct StorageUpgrade_Struct_V2_Bad.Struct2Plus␊
19
19
  - In struct StorageUpgrade_Struct_V2_Bad.Struct2Plus␊
20
20
  - Added \`z\`␊
21
- ␊
22
21
  ␊
23
22
  file.sol:1: Upgraded \`m\` to an incompatible type␊
24
23
  - In mapping(uint256 => struct StorageUpgrade_Struct_V2_Bad.Struct2Minus)␊
@@ -39,7 +38,6 @@ Generated by [AVA](https://avajs.dev).
39
38
  - Bad upgrade from struct StorageUpgrade_Struct_V1.Struct1 to struct StorageUpgrade_Struct_V2_Bad.Struct2Plus␊
40
39
  - In struct StorageUpgrade_Struct_V2_Bad.Struct2Plus␊
41
40
  - Added \`z\`␊
42
- ␊
43
41
  ␊
44
42
  file.sol:1: Upgraded \`data3\` to an incompatible type␊
45
43
  - Bad upgrade from struct StorageUpgrade_Struct_V1.Struct1 to struct StorageUpgrade_Struct_V2_Bad.Struct2Changed␊
@@ -51,8 +49,7 @@ Generated by [AVA](https://avajs.dev).
51
49
  - Upgraded \`inner\` to an incompatible type␊
52
50
  - Bad upgrade from struct StorageUpgrade_Struct_V1.StructInner to struct StorageUpgrade_Struct_V2_Bad.StructInnerPlus␊
53
51
  - In struct StorageUpgrade_Struct_V2_Bad.StructInnerPlus␊
54
- - Added \`z\`␊
55
- `
52
+ - Added \`z\``
56
53
 
57
54
  ## enums
58
55
 
Binary file
@@ -2,10 +2,20 @@ import _chalk from 'chalk';
2
2
 
3
3
  import type { BasicOperation } from '../levenshtein';
4
4
  import type { ParsedTypeDetailed } from './layout';
5
- import type { StorageOperation, StorageItem, StorageField, TypeChange, EnumOperation, LayoutChange } from './compare';
5
+ import {
6
+ StorageOperation,
7
+ StorageItem,
8
+ StorageField,
9
+ TypeChange,
10
+ EnumOperation,
11
+ LayoutChange,
12
+ storageFieldEnd,
13
+ storageFieldBegin,
14
+ } from './compare';
6
15
  import { itemize, itemizeWith } from '../utils/itemize';
7
16
  import { indent } from '../utils/indent';
8
17
  import { assert } from '../utils/assert';
18
+ import { isGap } from './gap';
9
19
 
10
20
  export class LayoutCompatibilityReport {
11
21
  constructor(readonly ops: StorageOperation<StorageItem>[]) {}
@@ -42,10 +52,41 @@ interface StorageOperationContext {
42
52
  allowAppend: boolean;
43
53
  }
44
54
 
55
+ interface WithHints {
56
+ title: string;
57
+ hints: string[];
58
+ }
59
+
60
+ function getExpectedGapSize(original: StorageField, updated: StorageField) {
61
+ const origEnd = storageFieldEnd(original);
62
+ const updatedStart = storageFieldBegin(updated);
63
+ const origNumBytes = original.type.item.numberOfBytes;
64
+ const origTail = original.type.tail;
65
+
66
+ if (origEnd === undefined || updatedStart === undefined || origNumBytes === undefined || origTail === undefined) {
67
+ return undefined;
68
+ }
69
+
70
+ const bytesPerItem = BigInt(origNumBytes) / BigInt(parseInt(origTail, 10));
71
+ const expectedSizeBytes = origEnd - updatedStart;
72
+
73
+ return expectedSizeBytes / bytesPerItem;
74
+ }
75
+
76
+ function suggestGapSize(original: StorageField, updated: StorageField): string {
77
+ const expectedSize = getExpectedGapSize(original, updated);
78
+ if (expectedSize !== undefined) {
79
+ return `Set __gap array to size ${expectedSize}`;
80
+ } else {
81
+ return '';
82
+ }
83
+ }
84
+
45
85
  function explainStorageOperation(op: StorageOperation<StorageField>, ctx: StorageOperationContext): string {
46
86
  switch (op.kind) {
87
+ case 'shrinkgap':
47
88
  case 'typechange': {
48
- const basic = explainTypeChange(op.change);
89
+ const basic = explainTypeChange(op.change, op.original, op.updated);
49
90
  const details =
50
91
  ctx.kind === 'layout' // explain details for layout only
51
92
  ? new Set(
@@ -54,8 +95,19 @@ function explainStorageOperation(op: StorageOperation<StorageField>, ctx: Storag
54
95
  .filter((d?: string): d is string => d !== undefined),
55
96
  )
56
97
  : [];
57
- return `Upgraded ${label(op.updated)} to an incompatible type\n` + itemize(basic, ...details);
98
+ const hints = [];
99
+ if (isGap(op.original) && op.change.kind === 'array shrink') {
100
+ hints.push(suggestGapSize(op.original, op.updated));
101
+ }
102
+ return printWithHints({
103
+ title: `Upgraded ${label(op.updated)} to an incompatible type\n` + itemize(basic, ...details),
104
+ hints,
105
+ });
58
106
  }
107
+
108
+ case 'finishgap':
109
+ return `Converted end of storage gap ${label(op.original)} to ${label(op.updated)}`;
110
+
59
111
  case 'rename':
60
112
  return `Renamed ${label(op.original)} to ${label(op.updated)}`;
61
113
 
@@ -63,11 +115,17 @@ function explainStorageOperation(op: StorageOperation<StorageField>, ctx: Storag
63
115
  return `Replaced ${label(op.original)} with ${label(op.updated)} of incompatible type`;
64
116
 
65
117
  case 'layoutchange': {
66
- return (
118
+ const title =
67
119
  `Layout ${op.change.uncertain ? 'could have changed' : 'changed'} for ${label(op.updated)} ` +
68
120
  `(${op.original.type.item.label} -> ${op.updated.type.item.label})\n` +
69
- describeLayoutTransition(op.change)
70
- ).trimEnd();
121
+ describeLayoutTransition(op.change);
122
+ const hints = [];
123
+
124
+ if (isGap(op.original)) {
125
+ hints.push(suggestGapSize(op.original, op.updated));
126
+ }
127
+
128
+ return printWithHints({ title, hints });
71
129
  }
72
130
 
73
131
  default: {
@@ -94,12 +152,16 @@ function explainStorageOperation(op: StorageOperation<StorageField>, ctx: Storag
94
152
  }
95
153
  }
96
154
 
97
- return title + '\n' + itemizeWith('>', ...hints);
155
+ return printWithHints({ title, hints });
98
156
  }
99
157
  }
100
158
  }
101
159
 
102
- function explainTypeChange(ch: TypeChange): string {
160
+ function printWithHints(result: WithHints): string {
161
+ return (result.title + '\n' + itemizeWith('>', ...result.hints)).trimEnd();
162
+ }
163
+
164
+ function explainTypeChange(ch: TypeChange, original: StorageField, updated: StorageField): string {
103
165
  switch (ch.kind) {
104
166
  case 'visibility change':
105
167
  return `Bad upgrade ${describeTransition(ch.original, ch.updated)}\nDifferent visibility`;
@@ -113,19 +175,28 @@ function explainTypeChange(ch: TypeChange): string {
113
175
  return `Bad upgrade ${describeTransition(ch.original, ch.updated)}\nDifferent representation sizes`;
114
176
 
115
177
  case 'mapping key':
116
- return `In key of ${ch.updated.item.label}\n` + itemize(explainTypeChange(ch.inner));
178
+ return `In key of ${ch.updated.item.label}\n` + itemize(explainTypeChange(ch.inner, original, updated));
117
179
 
118
180
  case 'mapping value':
119
181
  case 'array value':
120
- return `In ${ch.updated.item.label}\n` + itemize(explainTypeChange(ch.inner));
182
+ return `In ${ch.updated.item.label}\n` + itemize(explainTypeChange(ch.inner, original, updated));
121
183
 
122
184
  case 'array shrink':
123
185
  case 'array grow': {
124
186
  assert(ch.original.tail && ch.updated.tail);
125
187
  const originalSize = parseInt(ch.original.tail, 10);
126
188
  const updatedSize = parseInt(ch.updated.tail, 10);
127
- const note = ch.kind === 'array shrink' ? 'Size cannot decrease' : 'Size cannot increase here';
128
- return `Bad array resize from ${originalSize} to ${updatedSize}\n${note}`;
189
+
190
+ if (isGap(original)) {
191
+ const note =
192
+ ch.kind === 'array shrink'
193
+ ? 'Size decrease must match with corresponding variable inserts'
194
+ : 'Size cannot increase';
195
+ return `Bad storage gap resize from ${originalSize} to ${updatedSize}\n${note}`;
196
+ } else {
197
+ const note = ch.kind === 'array shrink' ? 'Size cannot decrease' : 'Size cannot increase here';
198
+ return `Bad array resize from ${originalSize} to ${updatedSize}\n${note}`;
199
+ }
129
200
  }
130
201
 
131
202
  case 'array dynamic': {
@@ -65,7 +65,7 @@ test('storage upgrade delete', t => {
65
65
  const v2 = t.context.extractStorageLayout('StorageUpgrade_Delete_V2');
66
66
  const comparison = getStorageUpgradeErrors(v1, v2);
67
67
  t.like(comparison, {
68
- length: 1,
68
+ length: 2,
69
69
  0: {
70
70
  kind: 'delete',
71
71
  original: {
@@ -76,6 +76,23 @@ test('storage upgrade delete', t => {
76
76
  },
77
77
  },
78
78
  },
79
+ 1: {
80
+ kind: 'layoutchange',
81
+ original: {
82
+ label: 'x2',
83
+ type: {
84
+ id: 't_address',
85
+ },
86
+ slot: '1',
87
+ },
88
+ updated: {
89
+ label: 'x2',
90
+ type: {
91
+ id: 't_address',
92
+ },
93
+ slot: '0',
94
+ },
95
+ },
79
96
  });
80
97
  });
81
98
 
@@ -172,6 +189,7 @@ test('storage upgrade with structs', t => {
172
189
  t.deepEqual(getStorageUpgradeErrors(v1, v2_Ok), []);
173
190
 
174
191
  const v2_Bad = t.context.extractStorageLayout('StorageUpgrade_Struct_V2_Bad');
192
+
175
193
  t.like(getStorageUpgradeErrors(v1, v2_Bad), {
176
194
  length: 6,
177
195
  0: {
@@ -179,7 +197,7 @@ test('storage upgrade with structs', t => {
179
197
  change: {
180
198
  kind: 'struct members',
181
199
  ops: {
182
- length: 2,
200
+ length: 3,
183
201
  0: { kind: 'delete' },
184
202
  },
185
203
  },
@@ -474,6 +492,452 @@ test('storage upgrade with embedded enum inside struct type', t => {
474
492
  t.deepEqual(getStorageUpgradeErrors(v1, v2), []);
475
493
  });
476
494
 
495
+ test('storage upgrade with gap', t => {
496
+ const v1 = t.context.extractStorageLayout('StorageUpgrade_Gap_V1');
497
+ const v2_Ok = t.context.extractStorageLayout('StorageUpgrade_Gap_V2_Ok');
498
+
499
+ const v2_Bad1 = t.context.extractStorageLayout('StorageUpgrade_Gap_V2_Bad1');
500
+ const v2_Bad2 = t.context.extractStorageLayout('StorageUpgrade_Gap_V2_Bad2');
501
+ const v2_Bad3 = t.context.extractStorageLayout('StorageUpgrade_Gap_V2_Bad3');
502
+ const v2_Bad4 = t.context.extractStorageLayout('StorageUpgrade_Gap_V2_Bad4');
503
+ const v2_Bad5 = t.context.extractStorageLayout('StorageUpgrade_Gap_V2_Bad5');
504
+
505
+ t.deepEqual(getStorageUpgradeErrors(v1, v2_Ok), []);
506
+
507
+ t.like(getStorageUpgradeErrors(v1, v2_Bad1), {
508
+ length: 2,
509
+ 0: {
510
+ kind: 'layoutchange',
511
+ change: {
512
+ slot: {
513
+ from: '2',
514
+ to: '3',
515
+ },
516
+ },
517
+ original: { label: '__gap' },
518
+ updated: { label: '__gap' },
519
+ },
520
+ 1: {
521
+ kind: 'layoutchange',
522
+ change: {
523
+ slot: {
524
+ from: '50',
525
+ to: '51',
526
+ },
527
+ },
528
+ original: { label: 'z' },
529
+ updated: { label: 'z' },
530
+ },
531
+ });
532
+
533
+ t.like(getStorageUpgradeErrors(v1, v2_Bad2), {
534
+ length: 2,
535
+ 0: {
536
+ kind: 'delete',
537
+ original: { label: 'b' },
538
+ },
539
+ 1: {
540
+ kind: 'typechange',
541
+ change: { kind: 'array grow' },
542
+ },
543
+ });
544
+
545
+ t.like(getStorageUpgradeErrors(v1, v2_Bad3), {
546
+ length: 2,
547
+ 0: {
548
+ kind: 'typechange',
549
+ change: { kind: 'array shrink' },
550
+ },
551
+ 1: {
552
+ kind: 'layoutchange',
553
+ change: {
554
+ slot: {
555
+ from: '50',
556
+ to: '49',
557
+ },
558
+ },
559
+ original: { label: 'z' },
560
+ updated: { label: 'z' },
561
+ },
562
+ });
563
+
564
+ t.like(getStorageUpgradeErrors(v1, v2_Bad4), {
565
+ length: 2,
566
+ 0: {
567
+ kind: 'typechange',
568
+ change: { kind: 'array shrink' },
569
+ },
570
+ 1: {
571
+ kind: 'layoutchange',
572
+ change: {
573
+ slot: {
574
+ from: '50',
575
+ to: '49',
576
+ },
577
+ },
578
+ original: { label: 'z' },
579
+ updated: { label: 'z' },
580
+ },
581
+ });
582
+
583
+ t.like(getStorageUpgradeErrors(v1, v2_Bad5), {
584
+ length: 2,
585
+ 0: {
586
+ kind: 'typechange',
587
+ change: { kind: 'array shrink' },
588
+ original: { label: '__gap' },
589
+ updated: { label: '__gap' },
590
+ },
591
+ 1: {
592
+ kind: 'layoutchange',
593
+ change: {
594
+ slot: {
595
+ from: '50',
596
+ to: '51',
597
+ },
598
+ },
599
+ original: { label: 'z' },
600
+ updated: { label: 'z' },
601
+ },
602
+ });
603
+ });
604
+
605
+ test('storage upgrade with bytes32 gap', t => {
606
+ const v1 = t.context.extractStorageLayout('StorageUpgrade_Bytes32Gap_V1');
607
+ const v2_Ok = t.context.extractStorageLayout('StorageUpgrade_Bytes32Gap_V2_Ok');
608
+
609
+ const v2_Bad1 = t.context.extractStorageLayout('StorageUpgrade_Bytes32Gap_V2_Bad1');
610
+ const v2_Bad2 = t.context.extractStorageLayout('StorageUpgrade_Bytes32Gap_V2_Bad2');
611
+ const v2_Bad3 = t.context.extractStorageLayout('StorageUpgrade_Bytes32Gap_V2_Bad3');
612
+ const v2_Bad4 = t.context.extractStorageLayout('StorageUpgrade_Bytes32Gap_V2_Bad4');
613
+ const v2_Bad5 = t.context.extractStorageLayout('StorageUpgrade_Bytes32Gap_V2_Bad5');
614
+
615
+ t.deepEqual(getStorageUpgradeErrors(v1, v2_Ok), []);
616
+
617
+ t.like(getStorageUpgradeErrors(v1, v2_Bad1), {
618
+ length: 2,
619
+ 0: {
620
+ kind: 'layoutchange',
621
+ change: {
622
+ slot: {
623
+ from: '2',
624
+ to: '3',
625
+ },
626
+ },
627
+ original: { label: '__gap' },
628
+ updated: { label: '__gap' },
629
+ },
630
+ 1: {
631
+ kind: 'layoutchange',
632
+ change: {
633
+ slot: {
634
+ from: '50',
635
+ to: '51',
636
+ },
637
+ },
638
+ original: { label: 'z' },
639
+ updated: { label: 'z' },
640
+ },
641
+ });
642
+
643
+ t.like(getStorageUpgradeErrors(v1, v2_Bad2), {
644
+ length: 2,
645
+ 0: {
646
+ kind: 'delete',
647
+ original: { label: 'b' },
648
+ },
649
+ 1: {
650
+ kind: 'typechange',
651
+ change: { kind: 'array grow' },
652
+ },
653
+ });
654
+
655
+ t.like(getStorageUpgradeErrors(v1, v2_Bad3), {
656
+ length: 2,
657
+ 0: {
658
+ kind: 'typechange',
659
+ change: { kind: 'array shrink' },
660
+ },
661
+ 1: {
662
+ kind: 'layoutchange',
663
+ change: {
664
+ slot: {
665
+ from: '50',
666
+ to: '49',
667
+ },
668
+ },
669
+ original: { label: 'z' },
670
+ updated: { label: 'z' },
671
+ },
672
+ });
673
+
674
+ t.like(getStorageUpgradeErrors(v1, v2_Bad4), {
675
+ length: 2,
676
+ 0: {
677
+ kind: 'typechange',
678
+ change: { kind: 'array shrink' },
679
+ },
680
+ 1: {
681
+ kind: 'layoutchange',
682
+ change: {
683
+ slot: {
684
+ from: '50',
685
+ to: '49',
686
+ },
687
+ },
688
+ original: { label: 'z' },
689
+ updated: { label: 'z' },
690
+ },
691
+ });
692
+
693
+ t.like(getStorageUpgradeErrors(v1, v2_Bad5), {
694
+ length: 2,
695
+ 0: {
696
+ kind: 'typechange',
697
+ change: { kind: 'array shrink' },
698
+ original: { label: '__gap' },
699
+ updated: { label: '__gap' },
700
+ },
701
+ 1: {
702
+ kind: 'layoutchange',
703
+ change: {
704
+ slot: {
705
+ from: '50',
706
+ to: '51',
707
+ },
708
+ },
709
+ original: { label: 'z' },
710
+ updated: { label: 'z' },
711
+ },
712
+ });
713
+ });
714
+
715
+ test('storage upgrade with multiple items consuming a gap', t => {
716
+ const v1 = t.context.extractStorageLayout('StorageUpgrade_MultiConsumeGap_V1');
717
+ const v2_Ok = t.context.extractStorageLayout('StorageUpgrade_MultiConsumeGap_V2_Ok');
718
+
719
+ t.deepEqual(getStorageUpgradeErrors(v1, v2_Ok), []);
720
+ });
721
+
722
+ test('storage upgrade that consumes gap and inherited gap', t => {
723
+ const v1a = t.context.extractStorageLayout('StorageUpgrade_EndGap_V1a');
724
+ const v1b = t.context.extractStorageLayout('StorageUpgrade_EndGap_V1b');
725
+ const v1c = t.context.extractStorageLayout('StorageUpgrade_EndGap_V1c');
726
+ const v2a = t.context.extractStorageLayout('StorageUpgrade_EndGap_V2a');
727
+ const v2b = t.context.extractStorageLayout('StorageUpgrade_EndGap_V2b');
728
+
729
+ t.deepEqual(getStorageUpgradeErrors(v1a, v2a), []);
730
+
731
+ t.like(getStorageUpgradeErrors(v1b, v2a), {
732
+ length: 1,
733
+ 0: {
734
+ kind: 'delete',
735
+ original: { label: '__gap' },
736
+ },
737
+ });
738
+
739
+ t.like(getStorageUpgradeErrors(v1c, v2a), {
740
+ length: 2,
741
+ 0: {
742
+ kind: 'delete',
743
+ original: { label: '__gap' },
744
+ },
745
+ 1: {
746
+ kind: 'delete',
747
+ original: { label: 'b' },
748
+ },
749
+ });
750
+
751
+ t.deepEqual(getStorageUpgradeErrors(v1b, v2b), []);
752
+ });
753
+
754
+ test('storage upgrade that consumes gap and inherited gap with smaller data type', t => {
755
+ const v1a = t.context.extractStorageLayout('StorageUpgrade_EndGap_Uint128_V1a');
756
+ const v1b = t.context.extractStorageLayout('StorageUpgrade_EndGap_Uint128_V1b');
757
+ const v1c = t.context.extractStorageLayout('StorageUpgrade_EndGap_Uint128_V1c');
758
+ const v2a = t.context.extractStorageLayout('StorageUpgrade_EndGap_Uint128_V2a');
759
+ const v2b = t.context.extractStorageLayout('StorageUpgrade_EndGap_Uint128_V2b');
760
+
761
+ t.deepEqual(getStorageUpgradeErrors(v1a, v2a), []);
762
+
763
+ t.like(getStorageUpgradeErrors(v1b, v2a), {
764
+ length: 1,
765
+ 0: {
766
+ kind: 'delete',
767
+ original: { label: '__gap' },
768
+ },
769
+ });
770
+
771
+ t.like(getStorageUpgradeErrors(v1c, v2a), {
772
+ length: 2,
773
+ 0: {
774
+ kind: 'delete',
775
+ original: { label: '__gap' },
776
+ },
777
+ 1: {
778
+ kind: 'delete',
779
+ original: { label: 'b' },
780
+ },
781
+ });
782
+
783
+ t.deepEqual(getStorageUpgradeErrors(v1b, v2b), []);
784
+ });
785
+
786
+ test('storage upgrade with different typed gaps', t => {
787
+ const uint256gap_address_v1 = t.context.extractStorageLayout('StorageUpgrade_Uint256Gap_Address_V1');
788
+ const uint256gap_address_v2 = t.context.extractStorageLayout('StorageUpgrade_Uint256Gap_Address_V2');
789
+
790
+ const address_v1 = t.context.extractStorageLayout('StorageUpgrade_AddressGap_V1');
791
+ const address_v2 = t.context.extractStorageLayout('StorageUpgrade_AddressGap_V2');
792
+
793
+ const uint128_v1 = t.context.extractStorageLayout('StorageUpgrade_Uint128Gap_V1');
794
+ const uint128_v2_ok = t.context.extractStorageLayout('StorageUpgrade_Uint128Gap_V2_Ok');
795
+ const uint128_v2b_ok = t.context.extractStorageLayout('StorageUpgrade_Uint128Gap_V2b_Ok');
796
+ const uint128_v2_bad = t.context.extractStorageLayout('StorageUpgrade_Uint128Gap_V2_Bad');
797
+
798
+ const bool_v1 = t.context.extractStorageLayout('StorageUpgrade_BoolGap_V1');
799
+ const bool_v2_ok = t.context.extractStorageLayout('StorageUpgrade_BoolGap_V2_Ok');
800
+ const bool_v2_bad = t.context.extractStorageLayout('StorageUpgrade_BoolGap_V2_Bad');
801
+
802
+ t.deepEqual(getStorageUpgradeErrors(uint256gap_address_v1, uint256gap_address_v2), []);
803
+ t.deepEqual(getStorageUpgradeErrors(address_v1, address_v2), []);
804
+
805
+ t.deepEqual(getStorageUpgradeErrors(uint128_v1, uint128_v2_ok), []);
806
+ t.deepEqual(getStorageUpgradeErrors(uint128_v1, uint128_v2b_ok), []);
807
+
808
+ t.like(getStorageUpgradeErrors(uint128_v1, uint128_v2_bad), {
809
+ length: 2,
810
+ 0: {
811
+ kind: 'layoutchange',
812
+ change: {
813
+ slot: {
814
+ from: '1',
815
+ to: '2',
816
+ },
817
+ },
818
+ original: { label: '__gap' },
819
+ updated: { label: '__gap' },
820
+ },
821
+ 1: {
822
+ kind: 'layoutchange',
823
+ change: {
824
+ slot: {
825
+ from: '26',
826
+ to: '27',
827
+ },
828
+ },
829
+ original: { label: 'z' },
830
+ updated: { label: 'z' },
831
+ },
832
+ });
833
+
834
+ t.deepEqual(getStorageUpgradeErrors(bool_v1, bool_v2_ok), []);
835
+ t.like(getStorageUpgradeErrors(bool_v1, bool_v2_bad), {
836
+ length: 2,
837
+ 0: {
838
+ kind: 'typechange',
839
+ change: { kind: 'array shrink' },
840
+ original: { label: '__gap' },
841
+ updated: { label: '__gap' },
842
+ },
843
+ 1: {
844
+ kind: 'typechange',
845
+ change: { kind: 'obvious mismatch' },
846
+ original: { label: 'z' },
847
+ updated: { label: 'z' },
848
+ },
849
+ });
850
+ });
851
+
852
+ test('storage upgrade with one element gap', t => {
853
+ const v1 = t.context.extractStorageLayout('StorageUpgrade_Gap_One_Element_V1');
854
+ const v2_Ok = t.context.extractStorageLayout('StorageUpgrade_Gap_One_Element_V2_Ok');
855
+ const v2b_Ok = t.context.extractStorageLayout('StorageUpgrade_Gap_One_Element_V2b_Ok');
856
+ const v2_Bad = t.context.extractStorageLayout('StorageUpgrade_Gap_One_Element_V2_Bad');
857
+
858
+ t.deepEqual(getStorageUpgradeErrors(v1, v2_Ok), []);
859
+ t.deepEqual(getStorageUpgradeErrors(v1, v2b_Ok), []);
860
+ t.like(getStorageUpgradeErrors(v1, v2_Bad), {
861
+ length: 2,
862
+ 0: {
863
+ kind: 'layoutchange',
864
+ change: {
865
+ slot: {
866
+ from: '2',
867
+ to: '3',
868
+ },
869
+ },
870
+ original: { label: '__gap' },
871
+ updated: { label: '__gap' },
872
+ },
873
+ 1: {
874
+ kind: 'layoutchange',
875
+ change: {
876
+ slot: {
877
+ from: '3',
878
+ to: '4',
879
+ },
880
+ },
881
+ original: { label: 'z' },
882
+ updated: { label: 'z' },
883
+ },
884
+ });
885
+ });
886
+
887
+ test('storage upgrade with bool non-array named as __gap', t => {
888
+ const v1 = t.context.extractStorageLayout('StorageUpgrade_Gap_Bool_Not_Array_V1');
889
+ const v2_Bad = t.context.extractStorageLayout('StorageUpgrade_Gap_Bool_Not_Array_V2_Bad');
890
+
891
+ t.like(getStorageUpgradeErrors(v1, v2_Bad), {
892
+ length: 1,
893
+ 0: {
894
+ kind: 'rename',
895
+ original: { label: '__gap' },
896
+ updated: { label: 'c' },
897
+ },
898
+ });
899
+ });
900
+
901
+ test('storage upgrade with uint256 non-array named as __gap', t => {
902
+ const v1 = t.context.extractStorageLayout('StorageUpgrade_Gap_Uint256_Not_Array_V1');
903
+ const v2_Bad = t.context.extractStorageLayout('StorageUpgrade_Gap_Uint256_Not_Array_V2_Bad');
904
+
905
+ t.like(getStorageUpgradeErrors(v1, v2_Bad), {
906
+ length: 1,
907
+ 0: {
908
+ kind: 'rename',
909
+ original: { label: '__gap' },
910
+ updated: { label: 'c' },
911
+ },
912
+ });
913
+ });
914
+
915
+ test('storage upgrade with struct gap', t => {
916
+ const v1 = t.context.extractStorageLayout('StorageUpgrade_StructGap_V1');
917
+ const v2_Ok = t.context.extractStorageLayout('StorageUpgrade_StructGap_V2_Ok');
918
+ const v2_Bad = t.context.extractStorageLayout('StorageUpgrade_StructGap_V2_Bad');
919
+
920
+ t.deepEqual(getStorageUpgradeErrors(v1, v2_Ok), []);
921
+ t.like(getStorageUpgradeErrors(v1, v2_Bad), {
922
+ length: 3,
923
+ 0: {
924
+ kind: 'typechange',
925
+ original: { label: 'store_mapping' },
926
+ updated: { label: 'store_mapping' },
927
+ },
928
+ 1: {
929
+ kind: 'typechange',
930
+ original: { label: 'store_fixed_array' },
931
+ updated: { label: 'store_fixed_array' },
932
+ },
933
+ 2: {
934
+ kind: 'typechange',
935
+ original: { label: 'store_dynamic_array' },
936
+ updated: { label: 'store_dynamic_array' },
937
+ },
938
+ });
939
+ });
940
+
477
941
  function stabilizeStorageLayout(layout: StorageLayout) {
478
942
  return {
479
943
  storage: layout.storage.map(s => ({ ...s, type: stabilizeTypeIdentifier(s.type) })),
Binary file
@@ -1,4 +1,4 @@
1
- import versions from 'compare-versions';
1
+ import * as versions from 'compare-versions';
2
2
 
3
3
  import { ValidationRunData } from './run';
4
4