@openzeppelin/upgrades-core 1.9.1
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/CHANGELOG.md +110 -0
- package/artifacts/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.dbg.json +4 -0
- package/artifacts/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.json +80 -0
- package/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json +160 -0
- package/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json +155 -0
- package/artifacts/AdminUpgradeabilityProxy.json +155 -0
- package/artifacts/ProxyAdmin.json +160 -0
- package/artifacts/contracts/proxy/AdminUpgradeabilityProxy.sol/AdminUpgradeabilityProxy.json +155 -0
- package/artifacts/contracts/proxy/ProxyAdmin.sol/ProxyAdmin.json +160 -0
- package/contracts/Initializable.sol +64 -0
- package/dist/ast-dereferencer.d.ts +8 -0
- package/dist/ast-dereferencer.d.ts.map +1 -0
- package/dist/ast-dereferencer.js +29 -0
- package/dist/ast-dereferencer.js.map +1 -0
- package/dist/deployment.d.ts +18 -0
- package/dist/deployment.d.ts.map +1 -0
- package/dist/deployment.js +110 -0
- package/dist/deployment.js.map +1 -0
- package/dist/deployment.test.d.ts +2 -0
- package/dist/deployment.test.d.ts.map +1 -0
- package/dist/deployment.test.js +92 -0
- package/dist/deployment.test.js.map +1 -0
- package/dist/eip-1967.d.ts +7 -0
- package/dist/eip-1967.d.ts.map +1 -0
- package/dist/eip-1967.js +63 -0
- package/dist/eip-1967.js.map +1 -0
- package/dist/error.d.ts +21 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +21 -0
- package/dist/error.js.map +1 -0
- package/dist/impl-store.d.ts +7 -0
- package/dist/impl-store.d.ts.map +1 -0
- package/dist/impl-store.js +94 -0
- package/dist/impl-store.js.map +1 -0
- package/dist/impl-store.test.d.ts +2 -0
- package/dist/impl-store.test.d.ts.map +1 -0
- package/dist/impl-store.test.js +55 -0
- package/dist/impl-store.test.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/levenshtein.d.ts +12 -0
- package/dist/levenshtein.d.ts.map +1 -0
- package/dist/levenshtein.js +89 -0
- package/dist/levenshtein.js.map +1 -0
- package/dist/levenshtein.test.d.ts +2 -0
- package/dist/levenshtein.test.d.ts.map +1 -0
- package/dist/levenshtein.test.js +88 -0
- package/dist/levenshtein.test.js.map +1 -0
- package/dist/link-refs.d.ts +11 -0
- package/dist/link-refs.d.ts.map +1 -0
- package/dist/link-refs.js +36 -0
- package/dist/link-refs.js.map +1 -0
- package/dist/link-refs.test.d.ts +2 -0
- package/dist/link-refs.test.d.ts.map +1 -0
- package/dist/link-refs.test.js +76 -0
- package/dist/link-refs.test.js.map +1 -0
- package/dist/manifest-storage-layout.d.ts +6 -0
- package/dist/manifest-storage-layout.d.ts.map +1 -0
- package/dist/manifest-storage-layout.js +95 -0
- package/dist/manifest-storage-layout.js.map +1 -0
- package/dist/manifest-storage-layout.test.d.ts +2 -0
- package/dist/manifest-storage-layout.test.d.ts.map +1 -0
- package/dist/manifest-storage-layout.test.js +105 -0
- package/dist/manifest-storage-layout.test.js.map +1 -0
- package/dist/manifest.d.ts +36 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +154 -0
- package/dist/manifest.js.map +1 -0
- package/dist/manifest.test.d.ts +2 -0
- package/dist/manifest.test.d.ts.map +1 -0
- package/dist/manifest.test.js +17 -0
- package/dist/manifest.test.js.map +1 -0
- package/dist/provider.d.ts +37 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +74 -0
- package/dist/provider.js.map +1 -0
- package/dist/provider.test.d.ts +2 -0
- package/dist/provider.test.d.ts.map +1 -0
- package/dist/provider.test.js +41 -0
- package/dist/provider.test.js.map +1 -0
- package/dist/scripts/migrate-oz-cli-project.d.ts +108 -0
- package/dist/scripts/migrate-oz-cli-project.d.ts.map +1 -0
- package/dist/scripts/migrate-oz-cli-project.js +321 -0
- package/dist/scripts/migrate-oz-cli-project.js.map +1 -0
- package/dist/scripts/migrate-oz-cli-project.test.d.ts +2 -0
- package/dist/scripts/migrate-oz-cli-project.test.d.ts.map +1 -0
- package/dist/scripts/migrate-oz-cli-project.test.js +60 -0
- package/dist/scripts/migrate-oz-cli-project.test.js.map +1 -0
- package/dist/set-proxy-kind.d.ts +5 -0
- package/dist/set-proxy-kind.d.ts.map +1 -0
- package/dist/set-proxy-kind.js +25 -0
- package/dist/set-proxy-kind.js.map +1 -0
- package/dist/solc-api.d.ts +39 -0
- package/dist/solc-api.d.ts.map +1 -0
- package/dist/solc-api.js +3 -0
- package/dist/solc-api.js.map +1 -0
- package/dist/src-decoder.d.ts +6 -0
- package/dist/src-decoder.d.ts.map +1 -0
- package/dist/src-decoder.js +36 -0
- package/dist/src-decoder.js.map +1 -0
- package/dist/src-decoder.test.d.ts +2 -0
- package/dist/src-decoder.test.d.ts.map +1 -0
- package/dist/src-decoder.test.js +29 -0
- package/dist/src-decoder.test.js.map +1 -0
- package/dist/storage/compare.d.ts +55 -0
- package/dist/storage/compare.d.ts.map +1 -0
- package/dist/storage/compare.js +181 -0
- package/dist/storage/compare.js.map +1 -0
- package/dist/storage/compat.d.ts +4 -0
- package/dist/storage/compat.d.ts.map +1 -0
- package/dist/storage/compat.js +9 -0
- package/dist/storage/compat.js.map +1 -0
- package/dist/storage/extract.d.ts +7 -0
- package/dist/storage/extract.d.ts.map +1 -0
- package/dist/storage/extract.js +86 -0
- package/dist/storage/extract.js.map +1 -0
- package/dist/storage/index.d.ts +8 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +65 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/layout.d.ts +31 -0
- package/dist/storage/layout.d.ts.map +1 -0
- package/dist/storage/layout.js +46 -0
- package/dist/storage/layout.js.map +1 -0
- package/dist/storage/report.d.ts +8 -0
- package/dist/storage/report.d.ts.map +1 -0
- package/dist/storage/report.js +175 -0
- package/dist/storage/report.js.map +1 -0
- package/dist/storage/report.test.d.ts +2 -0
- package/dist/storage/report.test.d.ts.map +1 -0
- package/dist/storage/report.test.js +82 -0
- package/dist/storage/report.test.js.map +1 -0
- package/dist/storage.test.d.ts +2 -0
- package/dist/storage.test.d.ts.map +1 -0
- package/dist/storage.test.js +436 -0
- package/dist/storage.test.js.map +1 -0
- package/dist/stub-provider.d.ts +14 -0
- package/dist/stub-provider.d.ts.map +1 -0
- package/dist/stub-provider.js +128 -0
- package/dist/stub-provider.js.map +1 -0
- package/dist/utils/assert.d.ts +3 -0
- package/dist/utils/assert.d.ts.map +1 -0
- package/dist/utils/assert.js +14 -0
- package/dist/utils/assert.js.map +1 -0
- package/dist/utils/curry.d.ts +6 -0
- package/dist/utils/curry.d.ts.map +1 -0
- package/dist/utils/curry.js +16 -0
- package/dist/utils/curry.js.map +1 -0
- package/dist/utils/debug.d.ts +4 -0
- package/dist/utils/debug.d.ts.map +1 -0
- package/dist/utils/debug.js +8 -0
- package/dist/utils/debug.js.map +1 -0
- package/dist/utils/deep-array.d.ts +3 -0
- package/dist/utils/deep-array.d.ts.map +1 -0
- package/dist/utils/deep-array.js +13 -0
- package/dist/utils/deep-array.js.map +1 -0
- package/dist/utils/deep-array.test.d.ts +2 -0
- package/dist/utils/deep-array.test.d.ts.map +1 -0
- package/dist/utils/deep-array.test.js +23 -0
- package/dist/utils/deep-array.test.js.map +1 -0
- package/dist/utils/function.d.ts +4 -0
- package/dist/utils/function.d.ts.map +1 -0
- package/dist/utils/function.js +43 -0
- package/dist/utils/function.js.map +1 -0
- package/dist/utils/function.test.d.ts +2 -0
- package/dist/utils/function.test.d.ts.map +1 -0
- package/dist/utils/function.test.js +34 -0
- package/dist/utils/function.test.js.map +1 -0
- package/dist/utils/indent.d.ts +2 -0
- package/dist/utils/indent.d.ts.map +1 -0
- package/dist/utils/indent.js +8 -0
- package/dist/utils/indent.js.map +1 -0
- package/dist/utils/is-nullish.d.ts +2 -0
- package/dist/utils/is-nullish.d.ts.map +1 -0
- package/dist/utils/is-nullish.js +8 -0
- package/dist/utils/is-nullish.js.map +1 -0
- package/dist/utils/is-nullish.test.d.ts +2 -0
- package/dist/utils/is-nullish.test.d.ts.map +1 -0
- package/dist/utils/is-nullish.test.js +17 -0
- package/dist/utils/is-nullish.test.js.map +1 -0
- package/dist/utils/is-value-type.d.ts +3 -0
- package/dist/utils/is-value-type.d.ts.map +1 -0
- package/dist/utils/is-value-type.js +8 -0
- package/dist/utils/is-value-type.js.map +1 -0
- package/dist/utils/itemize.d.ts +3 -0
- package/dist/utils/itemize.d.ts.map +1 -0
- package/dist/utils/itemize.js +18 -0
- package/dist/utils/itemize.js.map +1 -0
- package/dist/utils/log.d.ts +4 -0
- package/dist/utils/log.d.ts.map +1 -0
- package/dist/utils/log.js +35 -0
- package/dist/utils/log.js.map +1 -0
- package/dist/utils/make-non-enumerable.d.ts +2 -0
- package/dist/utils/make-non-enumerable.d.ts.map +1 -0
- package/dist/utils/make-non-enumerable.js +9 -0
- package/dist/utils/make-non-enumerable.js.map +1 -0
- package/dist/utils/map-values.d.ts +2 -0
- package/dist/utils/map-values.d.ts.map +1 -0
- package/dist/utils/map-values.js +12 -0
- package/dist/utils/map-values.js.map +1 -0
- package/dist/utils/parse-type-id.d.ts +9 -0
- package/dist/utils/parse-type-id.d.ts.map +1 -0
- package/dist/utils/parse-type-id.js +92 -0
- package/dist/utils/parse-type-id.js.map +1 -0
- package/dist/utils/parse-type-id.test.d.ts +2 -0
- package/dist/utils/parse-type-id.test.d.ts.map +1 -0
- package/dist/utils/parse-type-id.test.js +25 -0
- package/dist/utils/parse-type-id.test.js.map +1 -0
- package/dist/utils/pick.d.ts +2 -0
- package/dist/utils/pick.d.ts.map +1 -0
- package/dist/utils/pick.js +12 -0
- package/dist/utils/pick.js.map +1 -0
- package/dist/utils/type-id.d.ts +4 -0
- package/dist/utils/type-id.d.ts.map +1 -0
- package/dist/utils/type-id.js +65 -0
- package/dist/utils/type-id.js.map +1 -0
- package/dist/validate/compat.d.ts +6 -0
- package/dist/validate/compat.d.ts.map +1 -0
- package/dist/validate/compat.js +4 -0
- package/dist/validate/compat.js.map +1 -0
- package/dist/validate/data.d.ts +14 -0
- package/dist/validate/data.d.ts.map +1 -0
- package/dist/validate/data.js +50 -0
- package/dist/validate/data.js.map +1 -0
- package/dist/validate/error.d.ts +7 -0
- package/dist/validate/error.d.ts.map +1 -0
- package/dist/validate/error.js +78 -0
- package/dist/validate/error.js.map +1 -0
- package/dist/validate/index.d.ts +8 -0
- package/dist/validate/index.d.ts.map +1 -0
- package/dist/validate/index.js +24 -0
- package/dist/validate/index.js.map +1 -0
- package/dist/validate/overrides.d.ts +14 -0
- package/dist/validate/overrides.d.ts.map +1 -0
- package/dist/validate/overrides.js +72 -0
- package/dist/validate/overrides.js.map +1 -0
- package/dist/validate/query.d.ts +15 -0
- package/dist/validate/query.d.ts.map +1 -0
- package/dist/validate/query.js +111 -0
- package/dist/validate/query.js.map +1 -0
- package/dist/validate/query.test.d.ts +2 -0
- package/dist/validate/query.test.d.ts.map +1 -0
- package/dist/validate/query.test.js +48 -0
- package/dist/validate/query.test.js.map +1 -0
- package/dist/validate/run.d.ts +39 -0
- package/dist/validate/run.d.ts.map +1 -0
- package/dist/validate/run.js +199 -0
- package/dist/validate/run.js.map +1 -0
- package/dist/validate.test.d.ts +2 -0
- package/dist/validate.test.d.ts.map +1 -0
- package/dist/validate.test.js +116 -0
- package/dist/validate.test.js.map +1 -0
- package/dist/version.d.ts +9 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +66 -0
- package/dist/version.js.map +1 -0
- package/dist/version.test.d.ts +2 -0
- package/dist/version.test.d.ts.map +1 -0
- package/dist/version.test.js +53 -0
- package/dist/version.test.js.map +1 -0
- package/package.json +58 -0
- package/src/ast-dereferencer.ts +42 -0
- package/src/deployment.test.ts +98 -0
- package/src/deployment.ts +122 -0
- package/src/eip-1967.ts +77 -0
- package/src/error.ts +26 -0
- package/src/impl-store.test.ts +59 -0
- package/src/impl-store.ts +124 -0
- package/src/index.ts +18 -0
- package/src/levenshtein.test.ts +91 -0
- package/src/levenshtein.ts +116 -0
- package/src/link-refs.test.ts +77 -0
- package/src/link-refs.ts +41 -0
- package/src/manifest-storage-layout.test.ts +118 -0
- package/src/manifest-storage-layout.ts +107 -0
- package/src/manifest.test.ts +13 -0
- package/src/manifest.ts +178 -0
- package/src/provider.test.ts +43 -0
- package/src/provider.ts +103 -0
- package/src/scripts/migratable-manifest.test.json +175 -0
- package/src/scripts/migrate-oz-cli-project.test.ts +70 -0
- package/src/scripts/migrate-oz-cli-project.test.ts.md +146 -0
- package/src/scripts/migrate-oz-cli-project.test.ts.snap +0 -0
- package/src/scripts/migrate-oz-cli-project.ts +484 -0
- package/src/scripts/project-file.test.json +25 -0
- package/src/set-proxy-kind.ts +27 -0
- package/src/solc-api.ts +42 -0
- package/src/src-decoder.test.ts +30 -0
- package/src/src-decoder.ts +37 -0
- package/src/storage/compare.ts +249 -0
- package/src/storage/compat.ts +3 -0
- package/src/storage/extract.ts +110 -0
- package/src/storage/index.ts +73 -0
- package/src/storage/layout.ts +88 -0
- package/src/storage/report.test.ts +95 -0
- package/src/storage/report.test.ts.md +154 -0
- package/src/storage/report.test.ts.snap +0 -0
- package/src/storage/report.ts +215 -0
- package/src/storage.test.ts +479 -0
- package/src/storage.test.ts.md +643 -0
- package/src/storage.test.ts.snap +0 -0
- package/src/stub-provider.ts +120 -0
- package/src/utils/assert.ts +9 -0
- package/src/utils/curry.ts +17 -0
- package/src/utils/debug.ts +3 -0
- package/src/utils/deep-array.test.ts +21 -0
- package/src/utils/deep-array.ts +9 -0
- package/src/utils/function.test.ts +37 -0
- package/src/utils/function.ts +53 -0
- package/src/utils/indent.ts +3 -0
- package/src/utils/is-nullish.test.ts +15 -0
- package/src/utils/is-nullish.ts +3 -0
- package/src/utils/is-value-type.ts +5 -0
- package/src/utils/itemize.ts +16 -0
- package/src/utils/log.ts +34 -0
- package/src/utils/make-non-enumerable.ts +4 -0
- package/src/utils/map-values.ts +7 -0
- package/src/utils/parse-type-id.test.ts +22 -0
- package/src/utils/parse-type-id.test.ts.md +264 -0
- package/src/utils/parse-type-id.test.ts.snap +0 -0
- package/src/utils/parse-type-id.ts +104 -0
- package/src/utils/pick.ts +7 -0
- package/src/utils/type-id.ts +57 -0
- package/src/validate/compat.ts +9 -0
- package/src/validate/data.ts +55 -0
- package/src/validate/error.ts +75 -0
- package/src/validate/index.ts +17 -0
- package/src/validate/overrides.ts +93 -0
- package/src/validate/query.test.ts +48 -0
- package/src/validate/query.ts +127 -0
- package/src/validate/run.ts +278 -0
- package/src/validate.test.ts +133 -0
- package/src/version.test.ts +59 -0
- package/src/version.ts +71 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export * from './compat';
|
|
2
|
+
|
|
3
|
+
import { UpgradesError } from '../error';
|
|
4
|
+
import { StorageLayout, getDetailedLayout } from './layout';
|
|
5
|
+
import { StorageOperation, StorageItem, StorageLayoutComparator } from './compare';
|
|
6
|
+
import { LayoutCompatibilityReport } from './report';
|
|
7
|
+
import { ValidationOptions, withValidationDefaults } from '../validate/overrides';
|
|
8
|
+
import { logNote, logWarning } from '../utils/log';
|
|
9
|
+
|
|
10
|
+
export function assertStorageUpgradeSafe(
|
|
11
|
+
original: StorageLayout,
|
|
12
|
+
updated: StorageLayout,
|
|
13
|
+
unsafeAllowCustomTypes: boolean,
|
|
14
|
+
): void;
|
|
15
|
+
|
|
16
|
+
export function assertStorageUpgradeSafe(
|
|
17
|
+
original: StorageLayout,
|
|
18
|
+
updated: StorageLayout,
|
|
19
|
+
opts: Required<ValidationOptions>,
|
|
20
|
+
): void;
|
|
21
|
+
|
|
22
|
+
export function assertStorageUpgradeSafe(
|
|
23
|
+
original: StorageLayout,
|
|
24
|
+
updated: StorageLayout,
|
|
25
|
+
opts: Required<ValidationOptions> | boolean = false,
|
|
26
|
+
): void {
|
|
27
|
+
if (typeof opts === 'boolean') {
|
|
28
|
+
const unsafeAllowCustomTypes = opts;
|
|
29
|
+
opts = withValidationDefaults({ unsafeAllowCustomTypes });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const originalDetailed = getDetailedLayout(original);
|
|
33
|
+
const updatedDetailed = getDetailedLayout(updated);
|
|
34
|
+
const comparator = new StorageLayoutComparator(opts.unsafeAllowCustomTypes, opts.unsafeAllowRenames);
|
|
35
|
+
const report = comparator.compareLayouts(originalDetailed, updatedDetailed);
|
|
36
|
+
|
|
37
|
+
if (comparator.hasAllowedUncheckedCustomTypes) {
|
|
38
|
+
logWarning(`Potentially unsafe deployment`, [
|
|
39
|
+
`You are using \`unsafeAllowCustomTypes\` to force approve structs or enums with missing data.`,
|
|
40
|
+
`Make sure you have manually checked the storage layout for incompatibilities.`,
|
|
41
|
+
]);
|
|
42
|
+
} else if (opts.unsafeAllowCustomTypes) {
|
|
43
|
+
logNote(`\`unsafeAllowCustomTypes\` is no longer necessary. Structs are enums are automatically checked.\n`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!report.pass) {
|
|
47
|
+
throw new StorageUpgradeErrors(report);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
class StorageUpgradeErrors extends UpgradesError {
|
|
52
|
+
constructor(readonly report: LayoutCompatibilityReport) {
|
|
53
|
+
super(`New storage layout is incompatible`, () => report.explain());
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Kept for backwards compatibility and to avoid rewriting tests
|
|
58
|
+
export function getStorageUpgradeErrors(
|
|
59
|
+
original: StorageLayout,
|
|
60
|
+
updated: StorageLayout,
|
|
61
|
+
opts: ValidationOptions = {},
|
|
62
|
+
): StorageOperation<StorageItem>[] {
|
|
63
|
+
try {
|
|
64
|
+
assertStorageUpgradeSafe(original, updated, withValidationDefaults(opts));
|
|
65
|
+
} catch (e) {
|
|
66
|
+
if (e instanceof StorageUpgradeErrors) {
|
|
67
|
+
return e.report.ops;
|
|
68
|
+
} else {
|
|
69
|
+
throw e;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { parseTypeId, ParsedTypeId } from '../utils/parse-type-id';
|
|
2
|
+
|
|
3
|
+
// The interfaces below are generic in the way types are represented (through the parameter `Type`). When stored on
|
|
4
|
+
// disk, the type is represented by a string: the type id. When loaded onto memory to run the storage layout comparisons
|
|
5
|
+
// found in this module, the type id is replaced by its parsed structure together with the corresponding TypeItem, e.g.
|
|
6
|
+
// the struct members if it is a struct type.
|
|
7
|
+
|
|
8
|
+
export interface StorageLayout {
|
|
9
|
+
layoutVersion?: string;
|
|
10
|
+
storage: StorageItem[];
|
|
11
|
+
types: Record<string, TypeItem>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface StorageItem<Type = string> {
|
|
15
|
+
contract: string;
|
|
16
|
+
label: string;
|
|
17
|
+
type: Type;
|
|
18
|
+
src: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface TypeItem<Type = string> {
|
|
22
|
+
label: string;
|
|
23
|
+
members?: TypeItemMembers<Type>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type TypeItemMembers<Type = string> = StructMember<Type>[] | EnumMember[];
|
|
27
|
+
|
|
28
|
+
export interface StructMember<Type = string> {
|
|
29
|
+
label: string;
|
|
30
|
+
type: Type;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type EnumMember = string;
|
|
34
|
+
|
|
35
|
+
export interface ParsedTypeDetailed extends ParsedTypeId {
|
|
36
|
+
item: TypeItem<ParsedTypeDetailed>;
|
|
37
|
+
args?: ParsedTypeDetailed[];
|
|
38
|
+
rets?: ParsedTypeDetailed[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
type Replace<T, K extends string, V> = Omit<T, K> & Record<K, V>;
|
|
42
|
+
|
|
43
|
+
export function getDetailedLayout(layout: StorageLayout): StorageItem<ParsedTypeDetailed>[] {
|
|
44
|
+
const cache: Record<string, ParsedTypeDetailed> = {};
|
|
45
|
+
|
|
46
|
+
return layout.storage.map(parseWithDetails);
|
|
47
|
+
|
|
48
|
+
function parseWithDetails<I extends { type: string }>(item: I): Replace<I, 'type', ParsedTypeDetailed> {
|
|
49
|
+
const parsed = parseTypeId(item.type);
|
|
50
|
+
const withDetails = addDetailsToParsedType(parsed);
|
|
51
|
+
return { ...item, type: withDetails };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function addDetailsToParsedType(parsed: ParsedTypeId): ParsedTypeDetailed {
|
|
55
|
+
if (parsed.id in cache) {
|
|
56
|
+
return cache[parsed.id];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const item = layout.types[parsed.id];
|
|
60
|
+
const detailed: ParsedTypeDetailed = {
|
|
61
|
+
...parsed,
|
|
62
|
+
args: undefined,
|
|
63
|
+
rets: undefined,
|
|
64
|
+
item: {
|
|
65
|
+
...item,
|
|
66
|
+
members: undefined,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// store in cache before recursion below
|
|
71
|
+
cache[parsed.id] = detailed;
|
|
72
|
+
|
|
73
|
+
detailed.args = parsed.args?.map(addDetailsToParsedType);
|
|
74
|
+
detailed.rets = parsed.args?.map(addDetailsToParsedType);
|
|
75
|
+
detailed.item.members =
|
|
76
|
+
item?.members && (isStructMembers(item?.members) ? item.members.map(parseWithDetails) : item?.members);
|
|
77
|
+
|
|
78
|
+
return detailed;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function isEnumMembers<T>(members: TypeItemMembers<T>): members is EnumMember[] {
|
|
83
|
+
return members.length === 0 || typeof members[0] === 'string';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function isStructMembers<T>(members: TypeItemMembers<T>): members is StructMember<T>[] {
|
|
87
|
+
return members.length === 0 || typeof members[0] === 'object';
|
|
88
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import _test, { TestInterface } from 'ava';
|
|
2
|
+
import { ContractDefinition } from 'solidity-ast';
|
|
3
|
+
import { findAll } from 'solidity-ast/utils';
|
|
4
|
+
import { artifacts } from 'hardhat';
|
|
5
|
+
|
|
6
|
+
import { SolcOutput } from '../solc-api';
|
|
7
|
+
import { astDereferencer } from '../ast-dereferencer';
|
|
8
|
+
import { extractStorageLayout } from '../storage/extract';
|
|
9
|
+
import { StorageLayoutComparator } from './compare';
|
|
10
|
+
import { StorageLayout, getDetailedLayout } from './layout';
|
|
11
|
+
|
|
12
|
+
interface Context {
|
|
13
|
+
extractStorageLayout: (contract: string) => ReturnType<typeof extractStorageLayout>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const test = _test as TestInterface<Context>;
|
|
17
|
+
|
|
18
|
+
const dummyDecodeSrc = () => 'file.sol:1';
|
|
19
|
+
|
|
20
|
+
test.before(async t => {
|
|
21
|
+
const buildInfo = await artifacts.getBuildInfo('contracts/test/Storage.sol:Storage1');
|
|
22
|
+
if (buildInfo === undefined) {
|
|
23
|
+
throw new Error('Build info not found');
|
|
24
|
+
}
|
|
25
|
+
const solcOutput: SolcOutput = buildInfo.output;
|
|
26
|
+
const contracts: Record<string, ContractDefinition> = {};
|
|
27
|
+
for (const def of findAll('ContractDefinition', solcOutput.sources['contracts/test/Storage.sol'].ast)) {
|
|
28
|
+
contracts[def.name] = def;
|
|
29
|
+
}
|
|
30
|
+
const deref = astDereferencer(solcOutput);
|
|
31
|
+
t.context.extractStorageLayout = name => extractStorageLayout(contracts[name], dummyDecodeSrc, deref);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
function getReport(original: StorageLayout, updated: StorageLayout) {
|
|
35
|
+
const originalDetailed = getDetailedLayout(original);
|
|
36
|
+
const updatedDetailed = getDetailedLayout(updated);
|
|
37
|
+
const comparator = new StorageLayoutComparator();
|
|
38
|
+
return comparator.compareLayouts(originalDetailed, updatedDetailed);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
test('structs', t => {
|
|
42
|
+
const v1 = t.context.extractStorageLayout('StorageUpgrade_Struct_V1');
|
|
43
|
+
const v2 = t.context.extractStorageLayout('StorageUpgrade_Struct_V2_Bad');
|
|
44
|
+
const report = getReport(v1, v2);
|
|
45
|
+
t.snapshot(report.explain());
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('enums', t => {
|
|
49
|
+
const v1 = t.context.extractStorageLayout('StorageUpgrade_Enum_V1');
|
|
50
|
+
const v2 = t.context.extractStorageLayout('StorageUpgrade_Enum_V2_Bad');
|
|
51
|
+
const report = getReport(v1, v2);
|
|
52
|
+
t.snapshot(report.explain());
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('obvious mismatch', t => {
|
|
56
|
+
const v1 = t.context.extractStorageLayout('StorageUpgrade_ObviousMismatch_V1');
|
|
57
|
+
const v2 = t.context.extractStorageLayout('StorageUpgrade_ObviousMismatch_V2_Bad');
|
|
58
|
+
const report = getReport(v1, v2);
|
|
59
|
+
t.snapshot(report.explain());
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('array', t => {
|
|
63
|
+
const v1 = t.context.extractStorageLayout('StorageUpgrade_Array_V1');
|
|
64
|
+
const v2 = t.context.extractStorageLayout('StorageUpgrade_Array_V2_Bad');
|
|
65
|
+
const report = getReport(v1, v2);
|
|
66
|
+
t.snapshot(report.explain());
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('mapping', t => {
|
|
70
|
+
const v1 = t.context.extractStorageLayout('StorageUpgrade_Mapping_V1');
|
|
71
|
+
const v2 = t.context.extractStorageLayout('StorageUpgrade_Mapping_V2_Bad');
|
|
72
|
+
const report = getReport(v1, v2);
|
|
73
|
+
t.snapshot(report.explain());
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('mapping enum key', t => {
|
|
77
|
+
const v1 = t.context.extractStorageLayout('StorageUpgrade_MappingEnumKey_V1');
|
|
78
|
+
const v2 = t.context.extractStorageLayout('StorageUpgrade_MappingEnumKey_V2_Bad');
|
|
79
|
+
const report = getReport(v1, v2);
|
|
80
|
+
t.snapshot(report.explain());
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('rename', t => {
|
|
84
|
+
const v1 = t.context.extractStorageLayout('StorageUpgrade_Rename_V1');
|
|
85
|
+
const v2 = t.context.extractStorageLayout('StorageUpgrade_Rename_V2');
|
|
86
|
+
const report = getReport(v1, v2);
|
|
87
|
+
t.snapshot(report.explain());
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('replace', t => {
|
|
91
|
+
const v1 = t.context.extractStorageLayout('StorageUpgrade_Replace_V1');
|
|
92
|
+
const v2 = t.context.extractStorageLayout('StorageUpgrade_Replace_V2');
|
|
93
|
+
const report = getReport(v1, v2);
|
|
94
|
+
t.snapshot(report.explain());
|
|
95
|
+
});
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Snapshot report for `src/storage/report.test.ts`
|
|
2
|
+
|
|
3
|
+
The actual snapshot is saved in `report.test.ts.snap`.
|
|
4
|
+
|
|
5
|
+
Generated by [AVA](https://avajs.dev).
|
|
6
|
+
|
|
7
|
+
## structs
|
|
8
|
+
|
|
9
|
+
> Snapshot 1
|
|
10
|
+
|
|
11
|
+
`file.sol:1: Upgraded \`data1\` to an incompatible type␊
|
|
12
|
+
- Bad upgrade from struct StorageUpgrade_Struct_V1.Struct1 to struct StorageUpgrade_Struct_V2_Bad.Struct2Minus␊
|
|
13
|
+
- In struct StorageUpgrade_Struct_V2_Bad.Struct2Minus␊
|
|
14
|
+
- Deleted \`s1\`␊
|
|
15
|
+
> Keep the variable even if unused␊
|
|
16
|
+
␊
|
|
17
|
+
file.sol:1: Upgraded \`data2\` to an incompatible type␊
|
|
18
|
+
- Bad upgrade from struct StorageUpgrade_Struct_V1.Struct1 to struct StorageUpgrade_Struct_V2_Bad.Struct2Plus␊
|
|
19
|
+
- In struct StorageUpgrade_Struct_V2_Bad.Struct2Plus␊
|
|
20
|
+
- Added \`z\`␊
|
|
21
|
+
␊
|
|
22
|
+
␊
|
|
23
|
+
file.sol:1: Upgraded \`m\` to an incompatible type␊
|
|
24
|
+
- In mapping(uint256 => struct StorageUpgrade_Struct_V2_Bad.Struct2Minus)␊
|
|
25
|
+
- Bad upgrade from struct StorageUpgrade_Struct_V1.Struct1 to struct StorageUpgrade_Struct_V2_Bad.Struct2Minus␊
|
|
26
|
+
- In struct StorageUpgrade_Struct_V2_Bad.Struct2Minus␊
|
|
27
|
+
- Deleted \`s1\`␊
|
|
28
|
+
> Keep the variable even if unused␊
|
|
29
|
+
␊
|
|
30
|
+
file.sol:1: Upgraded \`a1\` to an incompatible type␊
|
|
31
|
+
- In struct StorageUpgrade_Struct_V2_Bad.Struct2Minus[10]␊
|
|
32
|
+
- Bad upgrade from struct StorageUpgrade_Struct_V1.Struct1 to struct StorageUpgrade_Struct_V2_Bad.Struct2Minus␊
|
|
33
|
+
- In struct StorageUpgrade_Struct_V2_Bad.Struct2Minus␊
|
|
34
|
+
- Deleted \`s1\`␊
|
|
35
|
+
> Keep the variable even if unused␊
|
|
36
|
+
␊
|
|
37
|
+
file.sol:1: Upgraded \`a2\` to an incompatible type␊
|
|
38
|
+
- In struct StorageUpgrade_Struct_V2_Bad.Struct2Plus[10]␊
|
|
39
|
+
- Bad upgrade from struct StorageUpgrade_Struct_V1.Struct1 to struct StorageUpgrade_Struct_V2_Bad.Struct2Plus␊
|
|
40
|
+
- In struct StorageUpgrade_Struct_V2_Bad.Struct2Plus␊
|
|
41
|
+
- Added \`z\`␊
|
|
42
|
+
␊
|
|
43
|
+
␊
|
|
44
|
+
file.sol:1: Upgraded \`data3\` to an incompatible type␊
|
|
45
|
+
- Bad upgrade from struct StorageUpgrade_Struct_V1.Struct1 to struct StorageUpgrade_Struct_V2_Bad.Struct2Changed␊
|
|
46
|
+
- In struct StorageUpgrade_Struct_V2_Bad.Struct2Changed␊
|
|
47
|
+
- Upgraded \`x\` to an incompatible type␊
|
|
48
|
+
- Bad upgrade from uint256 to string␊
|
|
49
|
+
- Upgraded \`s1\` to an incompatible type␊
|
|
50
|
+
- Bad upgrade from string to uint256␊
|
|
51
|
+
- Upgraded \`inner\` to an incompatible type␊
|
|
52
|
+
- Bad upgrade from struct StorageUpgrade_Struct_V1.StructInner to struct StorageUpgrade_Struct_V2_Bad.StructInnerPlus␊
|
|
53
|
+
- In struct StorageUpgrade_Struct_V2_Bad.StructInnerPlus␊
|
|
54
|
+
- Added \`z\`␊
|
|
55
|
+
`
|
|
56
|
+
|
|
57
|
+
## enums
|
|
58
|
+
|
|
59
|
+
> Snapshot 1
|
|
60
|
+
|
|
61
|
+
`file.sol:1: Upgraded \`data1\` to an incompatible type␊
|
|
62
|
+
- Bad upgrade from enum StorageUpgrade_Enum_V1.Enum1 to enum StorageUpgrade_Enum_V2_Bad.Enum2Delete␊
|
|
63
|
+
- In enum StorageUpgrade_Enum_V2_Bad.Enum2Delete␊
|
|
64
|
+
- Deleted \`B\`␊
|
|
65
|
+
␊
|
|
66
|
+
file.sol:1: Upgraded \`data2\` to an incompatible type␊
|
|
67
|
+
- Bad upgrade from enum StorageUpgrade_Enum_V1.Enum1 to enum StorageUpgrade_Enum_V2_Bad.Enum2Replace␊
|
|
68
|
+
- In enum StorageUpgrade_Enum_V2_Bad.Enum2Replace␊
|
|
69
|
+
- Replaced \`B\` with \`X\`␊
|
|
70
|
+
␊
|
|
71
|
+
file.sol:1: Upgraded \`data3\` to an incompatible type␊
|
|
72
|
+
- Bad upgrade from enum StorageUpgrade_Enum_V1.Enum1 to enum StorageUpgrade_Enum_V2_Bad.Enum2Insert␊
|
|
73
|
+
- In enum StorageUpgrade_Enum_V2_Bad.Enum2Insert␊
|
|
74
|
+
- Inserted \`X\`␊
|
|
75
|
+
␊
|
|
76
|
+
file.sol:1: Upgraded \`data4\` to an incompatible type␊
|
|
77
|
+
- Bad upgrade from enum StorageUpgrade_Enum_V1.Enum1 to enum StorageUpgrade_Enum_V2_Bad.Enum2TooLarge␊
|
|
78
|
+
Different representation sizes`
|
|
79
|
+
|
|
80
|
+
## obvious mismatch
|
|
81
|
+
|
|
82
|
+
> Snapshot 1
|
|
83
|
+
|
|
84
|
+
`file.sol:1: Upgraded \`x1\` to an incompatible type␊
|
|
85
|
+
- Bad upgrade from uint256 to string␊
|
|
86
|
+
␊
|
|
87
|
+
file.sol:1: Upgraded \`s1\` to an incompatible type␊
|
|
88
|
+
- Bad upgrade from struct StorageUpgrade_ObviousMismatch_V1.S to uint256␊
|
|
89
|
+
␊
|
|
90
|
+
file.sol:1: Upgraded \`a1\` to an incompatible type␊
|
|
91
|
+
- Bad upgrade from uint256[] to mapping(uint256 => uint256)`
|
|
92
|
+
|
|
93
|
+
## array
|
|
94
|
+
|
|
95
|
+
> Snapshot 1
|
|
96
|
+
|
|
97
|
+
`file.sol:1: Upgraded \`x1\` to an incompatible type␊
|
|
98
|
+
- Bad array resize from 20 to 15␊
|
|
99
|
+
Size cannot decrease␊
|
|
100
|
+
␊
|
|
101
|
+
file.sol:1: Upgraded \`x2\` to an incompatible type␊
|
|
102
|
+
- Bad array resize from 20 to 25␊
|
|
103
|
+
Size cannot increase here␊
|
|
104
|
+
␊
|
|
105
|
+
file.sol:1: Upgraded \`x3\` to an incompatible type␊
|
|
106
|
+
- Bad upgrade from fixed to dynamic size array␊
|
|
107
|
+
␊
|
|
108
|
+
file.sol:1: Upgraded \`x4\` to an incompatible type␊
|
|
109
|
+
- Bad upgrade from fixed to dynamic size array␊
|
|
110
|
+
␊
|
|
111
|
+
file.sol:1: Upgraded \`m\` to an incompatible type␊
|
|
112
|
+
- In mapping(uint256 => uint256[15])␊
|
|
113
|
+
- Bad array resize from 20 to 15␊
|
|
114
|
+
Size cannot decrease`
|
|
115
|
+
|
|
116
|
+
## mapping
|
|
117
|
+
|
|
118
|
+
> Snapshot 1
|
|
119
|
+
|
|
120
|
+
`file.sol:1: Upgraded \`m1\` to an incompatible type␊
|
|
121
|
+
- In mapping(uint256 => uint256)␊
|
|
122
|
+
- Bad upgrade from uint256[10] to uint256␊
|
|
123
|
+
␊
|
|
124
|
+
file.sol:1: Upgraded \`m2\` to an incompatible type␊
|
|
125
|
+
- In key of mapping(bool => uint256)␊
|
|
126
|
+
- Bad upgrade from undefined to bool`
|
|
127
|
+
|
|
128
|
+
## mapping enum key
|
|
129
|
+
|
|
130
|
+
> Snapshot 1
|
|
131
|
+
|
|
132
|
+
`file.sol:1: Upgraded \`m2\` to an incompatible type␊
|
|
133
|
+
- In key of mapping(enum E2_Bad => uint256)␊
|
|
134
|
+
- Bad upgrade from enum E1 to enum E2_Bad␊
|
|
135
|
+
␊
|
|
136
|
+
file.sol:1: Upgraded \`m3\` to an incompatible type␊
|
|
137
|
+
- In key of mapping(enum E1 => uint256)␊
|
|
138
|
+
- Bad upgrade from uint8 to enum E1␊
|
|
139
|
+
␊
|
|
140
|
+
file.sol:1: Upgraded \`m4\` to an incompatible type␊
|
|
141
|
+
- In key of mapping(uint256 => uint256)␊
|
|
142
|
+
- Bad upgrade from contract Storage1 to uint256`
|
|
143
|
+
|
|
144
|
+
## rename
|
|
145
|
+
|
|
146
|
+
> Snapshot 1
|
|
147
|
+
|
|
148
|
+
'file.sol:1: Renamed `x2` to `renamed`'
|
|
149
|
+
|
|
150
|
+
## replace
|
|
151
|
+
|
|
152
|
+
> Snapshot 1
|
|
153
|
+
|
|
154
|
+
'file.sol:1: Replaced `x2` with `renamed` of incompatible type'
|
|
Binary file
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
import type { BasicOperation } from '../levenshtein';
|
|
4
|
+
import type { ParsedTypeDetailed } from './layout';
|
|
5
|
+
import type { StorageOperation, StorageItem, StorageField, TypeChange, EnumOperation } from './compare';
|
|
6
|
+
import { itemize, itemizeWith } from '../utils/itemize';
|
|
7
|
+
import { indent } from '../utils/indent';
|
|
8
|
+
import { assert } from '../utils/assert';
|
|
9
|
+
|
|
10
|
+
export class LayoutCompatibilityReport {
|
|
11
|
+
readonly pass: boolean;
|
|
12
|
+
|
|
13
|
+
constructor(readonly ops: StorageOperation<StorageItem>[]) {
|
|
14
|
+
this.pass = ops.length === 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
explain(): string {
|
|
18
|
+
const res = [];
|
|
19
|
+
|
|
20
|
+
for (const op of this.ops) {
|
|
21
|
+
const src = 'updated' in op ? op.updated.src : op.original.contract;
|
|
22
|
+
res.push(
|
|
23
|
+
chalk.bold(src) + ':' + indent(explainStorageOperation(op, { kind: 'layout', allowAppend: true }), 2, 1),
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return res.join('\n\n');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface StorageOperationContext {
|
|
32
|
+
kind: 'struct' | 'layout';
|
|
33
|
+
allowAppend: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function explainStorageOperation(op: StorageOperation<StorageField>, ctx: StorageOperationContext): string {
|
|
37
|
+
switch (op.kind) {
|
|
38
|
+
case 'typechange': {
|
|
39
|
+
const basic = explainTypeChange(op.change);
|
|
40
|
+
const details =
|
|
41
|
+
ctx.kind === 'layout' // explain details for layout only
|
|
42
|
+
? new Set(
|
|
43
|
+
getAllTypeChanges(op.change)
|
|
44
|
+
.map(explainTypeChangeDetails)
|
|
45
|
+
.filter((d?: string): d is string => d !== undefined),
|
|
46
|
+
)
|
|
47
|
+
: [];
|
|
48
|
+
return `Upgraded ${label(op.updated)} to an incompatible type\n` + itemize(basic, ...details);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
case 'rename':
|
|
52
|
+
return `Renamed ${label(op.original)} to ${label(op.updated)}`;
|
|
53
|
+
|
|
54
|
+
case 'replace':
|
|
55
|
+
return `Replaced ${label(op.original)} with ${label(op.updated)} of incompatible type`;
|
|
56
|
+
|
|
57
|
+
default: {
|
|
58
|
+
const title = explainBasicOperation(op, t => t.label);
|
|
59
|
+
const hints = [];
|
|
60
|
+
|
|
61
|
+
switch (op.kind) {
|
|
62
|
+
case 'insert': {
|
|
63
|
+
if (ctx.kind === 'struct') {
|
|
64
|
+
if (ctx.allowAppend) {
|
|
65
|
+
hints.push('New struct members should be placed after existing ones');
|
|
66
|
+
} else {
|
|
67
|
+
hints.push('New struct members are not allowed here. Define a new struct');
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
hints.push('New variables should be placed after all existing inherited variables');
|
|
71
|
+
}
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
case 'delete': {
|
|
76
|
+
hints.push('Keep the variable even if unused');
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return title + '\n' + itemizeWith('>', ...hints);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function explainTypeChange(ch: TypeChange): string {
|
|
87
|
+
switch (ch.kind) {
|
|
88
|
+
case 'obvious mismatch':
|
|
89
|
+
case 'struct members':
|
|
90
|
+
case 'enum members':
|
|
91
|
+
return `Bad upgrade ${describeTransition(ch.original, ch.updated)}`;
|
|
92
|
+
|
|
93
|
+
case 'enum resize':
|
|
94
|
+
return `Bad upgrade ${describeTransition(ch.original, ch.updated)}\nDifferent representation sizes`;
|
|
95
|
+
|
|
96
|
+
case 'mapping key':
|
|
97
|
+
return `In key of ${ch.updated.item.label}\n` + itemize(explainTypeChange(ch.inner));
|
|
98
|
+
|
|
99
|
+
case 'mapping value':
|
|
100
|
+
case 'array value':
|
|
101
|
+
return `In ${ch.updated.item.label}\n` + itemize(explainTypeChange(ch.inner));
|
|
102
|
+
|
|
103
|
+
case 'array shrink':
|
|
104
|
+
case 'array grow': {
|
|
105
|
+
assert(ch.original.tail && ch.updated.tail);
|
|
106
|
+
const originalSize = parseInt(ch.original.tail, 10);
|
|
107
|
+
const updatedSize = parseInt(ch.updated.tail, 10);
|
|
108
|
+
const note = ch.kind === 'array shrink' ? 'Size cannot decrease' : 'Size cannot increase here';
|
|
109
|
+
return `Bad array resize from ${originalSize} to ${updatedSize}\n${note}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
case 'array dynamic': {
|
|
113
|
+
assert(ch.original.tail && ch.updated.tail);
|
|
114
|
+
const [originalSize, updatedSize] = ch.original.tail === 'dyn' ? ['dynamic', 'fixed'] : ['fixed', 'dynamic'];
|
|
115
|
+
return `Bad upgrade from ${originalSize} to ${updatedSize} size array`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
case 'missing members': {
|
|
119
|
+
const type = ch.updated.head.replace(/^t_/, ''); // t_struct, t_enum -> struct, enum
|
|
120
|
+
return `Insufficient data to compare ${type}s\nManually assess compatibility, then use option \`unsafeAllowCustomTypes: true\``;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
case 'unknown':
|
|
124
|
+
return `Unknown type ${ch.updated.item.label}`;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function getAllTypeChanges(root: TypeChange): TypeChange[] {
|
|
129
|
+
const list = [root];
|
|
130
|
+
|
|
131
|
+
for (const ch of list) {
|
|
132
|
+
switch (ch.kind) {
|
|
133
|
+
case 'mapping value':
|
|
134
|
+
case 'array value':
|
|
135
|
+
list.push(ch.inner);
|
|
136
|
+
break;
|
|
137
|
+
|
|
138
|
+
case 'struct members': {
|
|
139
|
+
for (const op of ch.ops) {
|
|
140
|
+
if (op.kind === 'typechange') {
|
|
141
|
+
list.push(op.change);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// We mention all other kinds explicitly to review any future new kinds
|
|
148
|
+
case 'obvious mismatch':
|
|
149
|
+
case 'enum members':
|
|
150
|
+
case 'enum resize':
|
|
151
|
+
case 'mapping key':
|
|
152
|
+
case 'array shrink':
|
|
153
|
+
case 'array grow':
|
|
154
|
+
case 'array dynamic':
|
|
155
|
+
case 'missing members':
|
|
156
|
+
case 'unknown':
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return list;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function explainTypeChangeDetails(ch: TypeChange): string | undefined {
|
|
165
|
+
switch (ch.kind) {
|
|
166
|
+
case 'struct members': {
|
|
167
|
+
const { allowAppend } = ch;
|
|
168
|
+
return (
|
|
169
|
+
`In ${ch.updated.item.label}\n` +
|
|
170
|
+
itemize(...ch.ops.map(op => explainStorageOperation(op, { kind: 'struct', allowAppend })))
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
case 'enum members':
|
|
175
|
+
return `In ${ch.updated.item.label}\n` + itemize(...ch.ops.map(explainEnumOperation));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function explainEnumOperation(op: EnumOperation): string {
|
|
180
|
+
switch (op.kind) {
|
|
181
|
+
case 'replace':
|
|
182
|
+
return `Replaced \`${op.original}\` with \`${op.updated}\``;
|
|
183
|
+
|
|
184
|
+
default:
|
|
185
|
+
return explainBasicOperation(op, t => t);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function explainBasicOperation<T>(op: BasicOperation<T>, getName: (t: T) => string): string {
|
|
190
|
+
switch (op.kind) {
|
|
191
|
+
case 'delete':
|
|
192
|
+
return `Deleted \`${getName(op.original)}\``;
|
|
193
|
+
|
|
194
|
+
case 'insert':
|
|
195
|
+
return `Inserted \`${getName(op.updated)}\``;
|
|
196
|
+
|
|
197
|
+
case 'append':
|
|
198
|
+
return `Added \`${getName(op.updated)}\``;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function describeTransition(original: ParsedTypeDetailed, updated: ParsedTypeDetailed): string {
|
|
203
|
+
const originalLabel = original.item.label;
|
|
204
|
+
const updatedLabel = updated.item.label;
|
|
205
|
+
|
|
206
|
+
if (originalLabel === updatedLabel) {
|
|
207
|
+
return `to ${updatedLabel}`;
|
|
208
|
+
} else {
|
|
209
|
+
return `from ${originalLabel} to ${updatedLabel}`;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function label(variable: { label: string }): string {
|
|
214
|
+
return '`' + variable.label + '`';
|
|
215
|
+
}
|