@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.
- package/dist/levenshtein.d.ts +4 -1
- package/dist/levenshtein.d.ts.map +1 -1
- package/dist/levenshtein.js +1 -1
- package/dist/levenshtein.js.map +1 -1
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +2 -0
- package/dist/provider.js.map +1 -1
- package/dist/storage/compare.d.ts +21 -3
- package/dist/storage/compare.d.ts.map +1 -1
- package/dist/storage/compare.js +80 -17
- package/dist/storage/compare.js.map +1 -1
- package/dist/storage/gap.d.ts +18 -0
- package/dist/storage/gap.d.ts.map +1 -0
- package/dist/storage/gap.js +29 -0
- package/dist/storage/gap.js.map +1 -0
- package/dist/storage/report-gap.test.d.ts +2 -0
- package/dist/storage/report-gap.test.d.ts.map +1 -0
- package/dist/storage/report-gap.test.js +106 -0
- package/dist/storage/report-gap.test.js.map +1 -0
- package/dist/storage/report-rename-retype.test.js +24 -0
- package/dist/storage/report-rename-retype.test.js.map +1 -1
- package/dist/storage/report.d.ts +1 -1
- package/dist/storage/report.d.ts.map +1 -1
- package/dist/storage/report.js +59 -10
- package/dist/storage/report.js.map +1 -1
- package/dist/storage.test.js +421 -2
- package/dist/storage.test.js.map +1 -1
- package/dist/validate/data.js +25 -5
- package/dist/validate/data.js.map +1 -1
- package/package.json +3 -3
- package/src/levenshtein.ts +4 -2
- package/src/provider.ts +2 -0
- package/src/storage/compare.ts +88 -18
- package/src/storage/gap.ts +26 -0
- package/src/storage/report-gap.test.ts +119 -0
- package/src/storage/report-gap.test.ts.md +59 -0
- package/src/storage/report-gap.test.ts.snap +0 -0
- package/src/storage/report-rename-retype.test.ts +27 -0
- package/src/storage/report-rename-retype.test.ts.md +7 -0
- package/src/storage/report-rename-retype.test.ts.snap +0 -0
- package/src/storage/report.test.ts.md +1 -4
- package/src/storage/report.test.ts.snap +0 -0
- package/src/storage/report.ts +83 -12
- package/src/storage.test.ts +466 -2
- package/src/storage.test.ts.snap +0 -0
- package/src/validate/data.ts +1 -1
- 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
|
package/src/storage/report.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
155
|
+
return printWithHints({ title, hints });
|
|
98
156
|
}
|
|
99
157
|
}
|
|
100
158
|
}
|
|
101
159
|
|
|
102
|
-
function
|
|
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
|
-
|
|
128
|
-
|
|
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': {
|
package/src/storage.test.ts
CHANGED
|
@@ -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:
|
|
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:
|
|
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) })),
|
package/src/storage.test.ts.snap
CHANGED
|
Binary file
|
package/src/validate/data.ts
CHANGED