@openzeppelin/upgrades-core 1.20.5 → 1.21.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/artifacts/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.dbg.json +1 -1
- package/artifacts/@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol/BeaconProxy.dbg.json +1 -1
- package/artifacts/@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol/UpgradeableBeacon.dbg.json +1 -1
- package/artifacts/build-info.json +151 -34
- package/dist/error.d.ts +1 -1
- package/dist/error.d.ts.map +1 -1
- package/dist/levenshtein.d.ts +4 -4
- package/dist/levenshtein.d.ts.map +1 -1
- package/dist/manifest.js +3 -3
- package/dist/manifest.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/scripts/migrate-oz-cli-project.d.ts +1 -1
- package/dist/scripts/migrate-oz-cli-project.d.ts.map +1 -1
- package/dist/solc-api.d.ts +1 -1
- package/dist/solc-api.d.ts.map +1 -1
- package/dist/src-decoder.d.ts +1 -1
- package/dist/src-decoder.d.ts.map +1 -1
- package/dist/src-decoder.js +1 -1
- package/dist/src-decoder.js.map +1 -1
- package/dist/storage/compare.d.ts +7 -7
- package/dist/storage/compare.d.ts.map +1 -1
- package/dist/storage/layout.d.ts +4 -4
- package/dist/storage/layout.d.ts.map +1 -1
- package/dist/storage/report.js +8 -5
- package/dist/storage/report.js.map +1 -1
- package/dist/utils/deep-array.d.ts +1 -1
- package/dist/utils/deep-array.d.ts.map +1 -1
- package/dist/validate/compat.d.ts +4 -4
- package/dist/validate/compat.d.ts.map +1 -1
- package/dist/validate/data.d.ts +4 -4
- package/dist/validate/data.d.ts.map +1 -1
- package/dist/validate/query.d.ts.map +1 -1
- package/dist/validate/query.js +12 -8
- package/dist/validate/query.js.map +1 -1
- package/dist/validate/run.d.ts +7 -2
- package/dist/validate/run.d.ts.map +1 -1
- package/dist/validate/run.js +119 -26
- package/dist/validate/run.js.map +1 -1
- package/dist/validate/run.test.d.ts +2 -0
- package/dist/validate/run.test.d.ts.map +1 -0
- package/dist/validate/run.test.js +43 -0
- package/dist/validate/run.test.js.map +1 -0
- package/dist/validate-ignore-errors.test.d.ts +2 -0
- package/dist/validate-ignore-errors.test.d.ts.map +1 -0
- package/dist/validate-ignore-errors.test.js +123 -0
- package/dist/validate-ignore-errors.test.js.map +1 -0
- package/package.json +2 -2
- package/src/provider.ts +2 -0
- package/src/src-decoder.ts +1 -1
- package/src/storage/report.ts +9 -5
- package/src/validate/query.ts +15 -11
- package/src/validate/run.test.ts +49 -0
- package/src/validate/run.ts +190 -31
- package/src/validate-ignore-errors.test.ts +137 -0
package/src/validate/run.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Node } from 'solidity-ast/node';
|
|
2
|
-
import { isNodeType, findAll } from 'solidity-ast/utils';
|
|
3
|
-
import type { ContractDefinition } from 'solidity-ast';
|
|
2
|
+
import { isNodeType, findAll, ASTDereferencer } from 'solidity-ast/utils';
|
|
3
|
+
import type { ContractDefinition, FunctionDefinition } from 'solidity-ast';
|
|
4
4
|
|
|
5
5
|
import { SolcOutput, SolcBytecode } from '../solc-api';
|
|
6
6
|
import { SrcDecoder } from '../src-decoder';
|
|
@@ -68,6 +68,26 @@ interface ValidationErrorOpcode extends ValidationErrorBase {
|
|
|
68
68
|
kind: 'delegatecall' | 'selfdestruct';
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
interface OpcodePattern {
|
|
72
|
+
kind: 'delegatecall' | 'selfdestruct';
|
|
73
|
+
pattern: RegExp;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const OPCODES = {
|
|
77
|
+
delegatecall: {
|
|
78
|
+
kind: 'delegatecall',
|
|
79
|
+
pattern: /^t_function_baredelegatecall_/,
|
|
80
|
+
},
|
|
81
|
+
selfdestruct: {
|
|
82
|
+
kind: 'selfdestruct',
|
|
83
|
+
pattern: /^t_function_selfdestruct_/,
|
|
84
|
+
},
|
|
85
|
+
} as const;
|
|
86
|
+
|
|
87
|
+
export function isOpcodeError(error: ValidationErrorBase): error is ValidationErrorOpcode {
|
|
88
|
+
return error.kind === 'delegatecall' || error.kind === 'selfdestruct';
|
|
89
|
+
}
|
|
90
|
+
|
|
71
91
|
interface ValidationErrorUpgradeability extends ValidationErrorBase {
|
|
72
92
|
kind: 'missing-public-upgradeto';
|
|
73
93
|
}
|
|
@@ -84,34 +104,49 @@ function* execall(re: RegExp, text: string) {
|
|
|
84
104
|
}
|
|
85
105
|
}
|
|
86
106
|
|
|
87
|
-
function getAllowed(node: Node): string[] {
|
|
107
|
+
function getAllowed(node: Node, reachable: boolean): string[] {
|
|
88
108
|
if ('documentation' in node) {
|
|
109
|
+
const tag = `oz-upgrades-unsafe-allow${reachable ? '-reachable' : ''}`;
|
|
89
110
|
const doc = typeof node.documentation === 'string' ? node.documentation : node.documentation?.text ?? '';
|
|
111
|
+
return getAnnotationArgs(doc, tag);
|
|
112
|
+
} else {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
90
116
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
117
|
+
/**
|
|
118
|
+
* Get args from the doc string matching the given tag
|
|
119
|
+
*/
|
|
120
|
+
export function getAnnotationArgs(doc: string, tag: string) {
|
|
121
|
+
const result: string[] = [];
|
|
122
|
+
for (const { groups } of execall(
|
|
123
|
+
/^\s*(?:@(?<title>\w+)(?::(?<tag>[a-z][a-z-]*))? )?(?<args>(?:(?!^\s*@\w+)[^])*)/m,
|
|
124
|
+
doc,
|
|
125
|
+
)) {
|
|
126
|
+
if (groups && groups.title === 'custom' && groups.tag === tag) {
|
|
127
|
+
const trimmedArgs = groups.args.trim();
|
|
128
|
+
if (trimmedArgs.length > 0) {
|
|
129
|
+
result.push(...trimmedArgs.split(/\s+/));
|
|
98
130
|
}
|
|
99
131
|
}
|
|
132
|
+
}
|
|
100
133
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
134
|
+
result.forEach(arg => {
|
|
135
|
+
if (!(errorKinds as readonly string[]).includes(arg)) {
|
|
136
|
+
throw new Error(`NatSpec: ${tag} argument not recognized: ${arg}`);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
106
139
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function skipCheckReachable(error: string, node: Node): boolean {
|
|
144
|
+
return getAllowed(node, true).includes(error);
|
|
111
145
|
}
|
|
112
146
|
|
|
113
147
|
function skipCheck(error: string, node: Node): boolean {
|
|
114
|
-
|
|
148
|
+
// skip both allow and allow-reachable errors in the lexical scope
|
|
149
|
+
return getAllowed(node, false).includes(error) || getAllowed(node, true).includes(error);
|
|
115
150
|
}
|
|
116
151
|
|
|
117
152
|
function getFullyQualifiedName(source: string, contractName: string) {
|
|
@@ -163,7 +198,7 @@ export function validate(solcOutput: SolcOutput, decodeSrc: SrcDecoder, solcVers
|
|
|
163
198
|
validation[key].src = decodeSrc(contractDef);
|
|
164
199
|
validation[key].errors = [
|
|
165
200
|
...getConstructorErrors(contractDef, decodeSrc),
|
|
166
|
-
...getOpcodeErrors(contractDef, decodeSrc),
|
|
201
|
+
...getOpcodeErrors(contractDef, deref, decodeSrc),
|
|
167
202
|
...getStateVariableErrors(contractDef, decodeSrc),
|
|
168
203
|
// TODO: add linked libraries support
|
|
169
204
|
// https://github.com/OpenZeppelin/openzeppelin-upgrades/issues/52
|
|
@@ -206,27 +241,151 @@ function* getConstructorErrors(contractDef: ContractDefinition, decodeSrc: SrcDe
|
|
|
206
241
|
}
|
|
207
242
|
}
|
|
208
243
|
|
|
209
|
-
function* getOpcodeErrors(
|
|
210
|
-
|
|
244
|
+
function* getOpcodeErrors(
|
|
245
|
+
contractDef: ContractDefinition,
|
|
246
|
+
deref: ASTDereferencer,
|
|
247
|
+
decodeSrc: SrcDecoder,
|
|
248
|
+
): Generator<ValidationErrorOpcode> {
|
|
249
|
+
yield* getContractOpcodeErrors(contractDef, deref, decodeSrc, OPCODES.delegatecall, 'main');
|
|
250
|
+
yield* getContractOpcodeErrors(contractDef, deref, decodeSrc, OPCODES.selfdestruct, 'main');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Whether this node is being visited as part of a main contract, or as an inherited contract or function
|
|
255
|
+
*/
|
|
256
|
+
type Scope = 'main' | 'inherited';
|
|
257
|
+
|
|
258
|
+
function* getContractOpcodeErrors(
|
|
259
|
+
contractDef: ContractDefinition,
|
|
260
|
+
deref: ASTDereferencer,
|
|
261
|
+
decodeSrc: SrcDecoder,
|
|
262
|
+
opcode: OpcodePattern,
|
|
263
|
+
scope: Scope,
|
|
264
|
+
visitedNodeIds = new Map<number, Scope>(),
|
|
265
|
+
): Generator<ValidationErrorOpcode> {
|
|
266
|
+
if (wasVisited(contractDef.id, scope, visitedNodeIds)) {
|
|
267
|
+
// We return early here, but the errors from this node were emitted when the node was marked as visited.
|
|
268
|
+
return;
|
|
269
|
+
} else {
|
|
270
|
+
visitedNodeIds.set(contractDef.id, scope);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
yield* getFunctionOpcodeErrors(contractDef, deref, decodeSrc, opcode, scope, visitedNodeIds);
|
|
274
|
+
yield* getInheritedContractOpcodeErrors(contractDef, deref, decodeSrc, opcode, visitedNodeIds);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function wasVisited(key: number, scope: Scope, visitedNodeIds: Map<number, Scope>) {
|
|
278
|
+
return (
|
|
279
|
+
(scope === 'inherited' && visitedNodeIds.has(key)) || // looking up an inherited contract, and it was previously checked as a main contract OR inherited contract
|
|
280
|
+
(scope === 'main' && visitedNodeIds.get(key) === 'main') // looking up a main contract, and it was previously checked as a main contract
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function* getFunctionOpcodeErrors(
|
|
285
|
+
contractOrFunctionDef: ContractDefinition | FunctionDefinition,
|
|
286
|
+
deref: ASTDereferencer,
|
|
287
|
+
decodeSrc: SrcDecoder,
|
|
288
|
+
opcode: OpcodePattern,
|
|
289
|
+
scope: Scope,
|
|
290
|
+
visitedNodeIds: Map<number, Scope>,
|
|
291
|
+
): Generator<ValidationErrorOpcode> {
|
|
292
|
+
const parentContractDef = getParentDefinition(deref, contractOrFunctionDef);
|
|
293
|
+
if (parentContractDef === undefined || !skipCheck(opcode.kind, parentContractDef)) {
|
|
294
|
+
yield* getDirectFunctionOpcodeErrors(contractOrFunctionDef, decodeSrc, opcode, scope);
|
|
295
|
+
}
|
|
296
|
+
if (parentContractDef === undefined || !skipCheckReachable(opcode.kind, parentContractDef)) {
|
|
297
|
+
yield* getReferencedFunctionOpcodeErrors(contractOrFunctionDef, deref, decodeSrc, opcode, scope, visitedNodeIds);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function* getDirectFunctionOpcodeErrors(
|
|
302
|
+
contractOrFunctionDef: ContractDefinition | FunctionDefinition,
|
|
303
|
+
decodeSrc: SrcDecoder,
|
|
304
|
+
opcode: OpcodePattern,
|
|
305
|
+
scope: Scope,
|
|
306
|
+
) {
|
|
307
|
+
for (const fnCall of findAll(
|
|
308
|
+
'FunctionCall',
|
|
309
|
+
contractOrFunctionDef,
|
|
310
|
+
node => skipCheck(opcode.kind, node) || (scope === 'inherited' && isInternalFunction(node)),
|
|
311
|
+
)) {
|
|
211
312
|
const fn = fnCall.expression;
|
|
212
|
-
if (fn.typeDescriptions.typeIdentifier?.match(
|
|
313
|
+
if (fn.typeDescriptions.typeIdentifier?.match(opcode.pattern)) {
|
|
213
314
|
yield {
|
|
214
|
-
kind:
|
|
315
|
+
kind: opcode.kind,
|
|
215
316
|
src: decodeSrc(fnCall),
|
|
216
317
|
};
|
|
217
318
|
}
|
|
218
319
|
}
|
|
219
|
-
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function* getReferencedFunctionOpcodeErrors(
|
|
323
|
+
contractOrFunctionDef: ContractDefinition | FunctionDefinition,
|
|
324
|
+
deref: ASTDereferencer,
|
|
325
|
+
decodeSrc: SrcDecoder,
|
|
326
|
+
opcode: OpcodePattern,
|
|
327
|
+
scope: Scope,
|
|
328
|
+
visitedNodeIds: Map<number, Scope>,
|
|
329
|
+
) {
|
|
330
|
+
for (const fnCall of findAll(
|
|
331
|
+
'FunctionCall',
|
|
332
|
+
contractOrFunctionDef,
|
|
333
|
+
node => skipCheckReachable(opcode.kind, node) || (scope === 'inherited' && isInternalFunction(node)),
|
|
334
|
+
)) {
|
|
220
335
|
const fn = fnCall.expression;
|
|
221
|
-
if (fn.
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
336
|
+
if ('referencedDeclaration' in fn && fn.referencedDeclaration && fn.referencedDeclaration > 0) {
|
|
337
|
+
// non-positive references refer to built-in functions
|
|
338
|
+
const referencedNode = tryDerefFunction(deref, fn.referencedDeclaration);
|
|
339
|
+
if (referencedNode !== undefined) {
|
|
340
|
+
if (!wasVisited(referencedNode.id, scope, visitedNodeIds)) {
|
|
341
|
+
visitedNodeIds.set(referencedNode.id, scope);
|
|
342
|
+
yield* getFunctionOpcodeErrors(referencedNode, deref, decodeSrc, opcode, scope, visitedNodeIds);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
226
345
|
}
|
|
227
346
|
}
|
|
228
347
|
}
|
|
229
348
|
|
|
349
|
+
function tryDerefFunction(deref: ASTDereferencer, referencedDeclaration: number): FunctionDefinition | undefined {
|
|
350
|
+
try {
|
|
351
|
+
return deref(['FunctionDefinition'], referencedDeclaration);
|
|
352
|
+
} catch (e: any) {
|
|
353
|
+
if (!e.message.includes('No node with id')) {
|
|
354
|
+
throw e;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function* getInheritedContractOpcodeErrors(
|
|
360
|
+
contractDef: ContractDefinition,
|
|
361
|
+
deref: ASTDereferencer,
|
|
362
|
+
decodeSrc: SrcDecoder,
|
|
363
|
+
opcode: OpcodePattern,
|
|
364
|
+
visitedNodeIds: Map<number, Scope>,
|
|
365
|
+
) {
|
|
366
|
+
if (!skipCheckReachable(opcode.kind, contractDef)) {
|
|
367
|
+
for (const base of contractDef.baseContracts) {
|
|
368
|
+
const referencedContract = deref('ContractDefinition', base.baseName.referencedDeclaration);
|
|
369
|
+
yield* getContractOpcodeErrors(referencedContract, deref, decodeSrc, opcode, 'inherited', visitedNodeIds);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function getParentDefinition(deref: ASTDereferencer, contractOrFunctionDef: ContractDefinition | FunctionDefinition) {
|
|
375
|
+
const parentNode = deref(['ContractDefinition', 'SourceUnit'], contractOrFunctionDef.scope);
|
|
376
|
+
if (parentNode.nodeType === 'ContractDefinition') {
|
|
377
|
+
return parentNode;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function isInternalFunction(node: Node) {
|
|
382
|
+
return (
|
|
383
|
+
node.nodeType === 'FunctionDefinition' &&
|
|
384
|
+
node.kind !== 'constructor' && // do not consider constructors as internal, because they are always called by children contracts' constructors
|
|
385
|
+
(node.visibility === 'internal' || node.visibility === 'private')
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
|
|
230
389
|
function* getStateVariableErrors(
|
|
231
390
|
contractDef: ContractDefinition,
|
|
232
391
|
decodeSrc: SrcDecoder,
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import _test, { TestFn } from 'ava';
|
|
2
|
+
import { artifacts } from 'hardhat';
|
|
3
|
+
|
|
4
|
+
import { validate, getContractVersion, assertUpgradeSafe, ValidationOptions, RunValidation } from './validate';
|
|
5
|
+
import { solcInputOutputDecoder } from './src-decoder';
|
|
6
|
+
|
|
7
|
+
interface Context {
|
|
8
|
+
validation: RunValidation;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const test = _test as TestFn<Context>;
|
|
12
|
+
|
|
13
|
+
test.before(async t => {
|
|
14
|
+
const contracts = [
|
|
15
|
+
'contracts/test/ignore-errors/SafeContract.sol:SafeContract',
|
|
16
|
+
'contracts/test/ignore-errors/SafeContractWithFreeFunctionCall.sol:SafeContractWithFreeFunctionCall',
|
|
17
|
+
'contracts/test/ignore-errors/SafeContractWithLibraryCall.sol:SafeContractWithLibraryCall',
|
|
18
|
+
'contracts/test/ignore-errors/SafeContractWithLibraryImport.sol:SafeContractWithLibraryImport',
|
|
19
|
+
'contracts/test/ignore-errors/SafeContractWithLibraryUsingFor.sol:SafeContractWithLibraryUsingFor',
|
|
20
|
+
'contracts/test/ignore-errors/SafeContractWithTransitiveLibraryCall.sol:SafeContractWithTransitiveLibraryCall',
|
|
21
|
+
'contracts/test/ignore-errors/SafeContractWithParentCall.sol:SafeContractWithParentCall',
|
|
22
|
+
'contracts/test/ignore-errors/RiskyParentContract.sol:RiskyParentContract',
|
|
23
|
+
'contracts/test/ignore-errors/UnsafeContract.sol:UnsafeContract',
|
|
24
|
+
'contracts/test/ignore-errors/UnsafeContractWithFreeFunctionCall.sol:UnsafeContractWithFreeFunctionCall',
|
|
25
|
+
'contracts/test/ignore-errors/UnsafeContractWithLibraryCall.sol:UnsafeContractWithLibraryCall',
|
|
26
|
+
'contracts/test/ignore-errors/UnsafeContractWithLibraryUsingFor.sol:UnsafeContractWithLibraryUsingFor',
|
|
27
|
+
'contracts/test/ignore-errors/UnsafeContractWithTransitiveLibraryCall.sol:UnsafeContractWithTransitiveLibraryCall',
|
|
28
|
+
'contracts/test/ignore-errors/UnsafeContractWithParentCall.sol:UnsafeContractWithParentCall',
|
|
29
|
+
'contracts/test/ignore-errors/UnsafeContractWithInheritedParent.sol:UnsafeContractWithInheritedParent',
|
|
30
|
+
'contracts/test/ignore-errors/UnsafeContractWithInheritedTransitiveParent.sol:UnsafeContractWithInheritedTransitiveParent',
|
|
31
|
+
'contracts/test/ignore-errors/UnsafeAllow.sol:UnsafeAllow',
|
|
32
|
+
'contracts/test/ignore-errors/AllowReachable.sol:AllowReachable',
|
|
33
|
+
'contracts/test/ignore-errors/UnsafeAllowReachableDifferentOpcode.sol:UnsafeAllowReachableDifferentOpcode',
|
|
34
|
+
'contracts/test/ignore-errors/UnsafeAllowParent.sol:UnsafeAllowParent',
|
|
35
|
+
'contracts/test/ignore-errors/AllowReachableParent.sol:AllowReachableParent',
|
|
36
|
+
'contracts/test/ignore-errors/UnsafeAllowReachableParentDifferentOpcode.sol:UnsafeAllowReachableParentDifferentOpcode',
|
|
37
|
+
'contracts/test/ignore-errors/AllowChildInheritedTransitive.sol:AllowChildInheritedTransitive',
|
|
38
|
+
'contracts/test/ignore-errors/AllowChild.sol:AllowChild',
|
|
39
|
+
'contracts/test/ignore-errors/AllowChildCallTransitive.sol:AllowChildCallTransitive',
|
|
40
|
+
'contracts/test/ignore-errors/AllowChildSelfReachable.sol:AllowChildSelfReachable',
|
|
41
|
+
'contracts/test/ignore-errors/SafeRecursion.sol:SafeRecursion',
|
|
42
|
+
'contracts/test/ignore-errors/UnsafeRecursion.sol:UnsafeRecursion',
|
|
43
|
+
'contracts/test/ignore-errors/Constructors.sol:UnsafeChild1',
|
|
44
|
+
'contracts/test/ignore-errors/Constructors.sol:UnsafeChild2',
|
|
45
|
+
'contracts/test/ignore-errors/Constructors.sol:UnsafeChild3',
|
|
46
|
+
'contracts/test/ignore-errors/Constructors.sol:UnsafeChild4',
|
|
47
|
+
'contracts/test/ignore-errors/Constructors.sol:AllowChild5',
|
|
48
|
+
'contracts/test/ignore-errors/Constructors.sol:AllowChild6',
|
|
49
|
+
'contracts/test/ignore-errors/Constructors.sol:UnsafeAllowChild7',
|
|
50
|
+
'contracts/test/ignore-errors/Constructors.sol:AllowReachableChild8',
|
|
51
|
+
'contracts/test/ignore-errors/Constructors.sol:UnsafeChild9',
|
|
52
|
+
// 'contracts/test/ignore-errors/Modifiers.sol:ModifierNotUsed',
|
|
53
|
+
'contracts/test/ignore-errors/Modifiers.sol:ModifierUsed',
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
t.context.validation = {} as RunValidation;
|
|
57
|
+
for (const contract of contracts) {
|
|
58
|
+
const buildInfo = await artifacts.getBuildInfo(contract);
|
|
59
|
+
if (buildInfo === undefined) {
|
|
60
|
+
throw new Error(`Build info not found for contract ${contract}`);
|
|
61
|
+
}
|
|
62
|
+
const solcOutput = buildInfo.output;
|
|
63
|
+
const solcInput = buildInfo.input;
|
|
64
|
+
const decodeSrc = solcInputOutputDecoder(solcInput, solcOutput);
|
|
65
|
+
Object.assign(t.context.validation, validate(solcOutput, decodeSrc));
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
function testValid(name: string, kind: ValidationOptions['kind'], valid: boolean) {
|
|
70
|
+
testOverride(name, kind, {}, valid);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function testOverride(name: string, kind: ValidationOptions['kind'], opts: ValidationOptions, valid: boolean) {
|
|
74
|
+
const optKeys = Object.keys(opts);
|
|
75
|
+
const describeOpts = optKeys.length > 0 ? '(' + optKeys.join(', ') + ')' : '';
|
|
76
|
+
const testName = [valid ? 'accepts' : 'rejects', kind, name, describeOpts].join(' ');
|
|
77
|
+
test(testName, t => {
|
|
78
|
+
const version = getContractVersion(t.context.validation, name);
|
|
79
|
+
const assertUpgSafe = () => assertUpgradeSafe([t.context.validation], version, { kind, ...opts });
|
|
80
|
+
if (valid) {
|
|
81
|
+
t.notThrows(assertUpgSafe);
|
|
82
|
+
} else {
|
|
83
|
+
t.throws(assertUpgSafe);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
testValid('SafeContract', 'transparent', true);
|
|
89
|
+
testValid('SafeContractWithFreeFunctionCall', 'transparent', true);
|
|
90
|
+
testValid('SafeContractWithLibraryCall', 'transparent', true);
|
|
91
|
+
testValid('SafeContractWithLibraryImport', 'transparent', true);
|
|
92
|
+
testValid('SafeContractWithLibraryUsingFor', 'transparent', true);
|
|
93
|
+
testValid('SafeContractWithTransitiveLibraryCall', 'transparent', true);
|
|
94
|
+
testValid('SafeContractWithParentCall', 'transparent', true);
|
|
95
|
+
|
|
96
|
+
testValid('RiskyParentContract', 'transparent', false);
|
|
97
|
+
|
|
98
|
+
testValid('UnsafeContract', 'transparent', false);
|
|
99
|
+
testValid('UnsafeContractWithFreeFunctionCall', 'transparent', false);
|
|
100
|
+
testValid('UnsafeContractWithLibraryCall', 'transparent', false);
|
|
101
|
+
testValid('UnsafeContractWithLibraryUsingFor', 'transparent', false);
|
|
102
|
+
testValid('UnsafeContractWithTransitiveLibraryCall', 'transparent', false);
|
|
103
|
+
testValid('UnsafeContractWithParentCall', 'transparent', false);
|
|
104
|
+
testValid('UnsafeContractWithInheritedParent', 'transparent', false);
|
|
105
|
+
testValid('UnsafeContractWithInheritedTransitiveParent', 'transparent', false);
|
|
106
|
+
|
|
107
|
+
testValid('UnsafeAllow', 'transparent', false);
|
|
108
|
+
testValid('AllowReachable', 'transparent', true);
|
|
109
|
+
testValid('UnsafeAllowReachableDifferentOpcode', 'transparent', false);
|
|
110
|
+
|
|
111
|
+
testValid('UnsafeAllowParent', 'transparent', false);
|
|
112
|
+
testValid('AllowReachableParent', 'transparent', true);
|
|
113
|
+
testValid('UnsafeAllowReachableParentDifferentOpcode', 'transparent', false);
|
|
114
|
+
|
|
115
|
+
testValid('AllowChildInheritedTransitive', 'transparent', true);
|
|
116
|
+
testValid('AllowChild', 'transparent', true);
|
|
117
|
+
testValid('AllowChildCallTransitive', 'transparent', true);
|
|
118
|
+
|
|
119
|
+
testValid('AllowChildSelfReachable', 'transparent', true);
|
|
120
|
+
|
|
121
|
+
testValid('SafeRecursion', 'transparent', true);
|
|
122
|
+
testValid('UnsafeRecursion', 'transparent', false);
|
|
123
|
+
|
|
124
|
+
testValid('UnsafeChild1', 'transparent', false);
|
|
125
|
+
testValid('UnsafeChild2', 'transparent', false);
|
|
126
|
+
testValid('UnsafeChild3', 'transparent', false);
|
|
127
|
+
testValid('UnsafeChild4', 'transparent', false);
|
|
128
|
+
testValid('AllowChild5', 'transparent', true);
|
|
129
|
+
testValid('AllowChild6', 'transparent', true);
|
|
130
|
+
testValid('UnsafeAllowChild7', 'transparent', false);
|
|
131
|
+
testValid('AllowReachableChild8', 'transparent', true);
|
|
132
|
+
testValid('UnsafeChild9', 'transparent', false);
|
|
133
|
+
|
|
134
|
+
// TODO: do not throw an error in this case
|
|
135
|
+
// testValid('ModifierNotUsed', 'transparent', true);
|
|
136
|
+
|
|
137
|
+
testValid('ModifierUsed', 'transparent', false);
|