Skip to content

Commit

Permalink
Fix backwards compatibility of upgrades-core (#221)
Browse files Browse the repository at this point in the history
  • Loading branch information
frangio authored Nov 13, 2020
1 parent e5352c1 commit 3fe29d1
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 19 deletions.
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"prepublish": "rimraf artifacts cache dist *.tsbuildinfo",
"prepare": "tsc -b && yarn prepare:contracts",
"prepare:contracts": "hardhat compile",
"prepublishOnly": "bash scripts/copy-artifacts.sh",
"test": "tsc -b && hardhat compile --force && ava",
"test:watch": "hardhat compile --force && fgbg 'ava --watch' 'tsc -b --watch'",
"version": "node ../../scripts/bump-changelog.js"
Expand Down
7 changes: 7 additions & 0 deletions packages/core/scripts/copy-artifacts.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash

set -euo pipefail

# copies proxy artifacts to the location they were until 1.3.0 for backwards compatibility with truffle plugin

cp artifacts/contracts/proxy/{AdminUpgradeabilityProxy.sol/AdminUpgradeabilityProxy.json,ProxyAdmin.sol/ProxyAdmin.json} artifacts
18 changes: 9 additions & 9 deletions packages/core/src/validate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import {
getContractVersion,
assertUpgradeSafe,
ValidationOptions,
ValidationLog,
RunValidation,
} from './validate';
import { solcInputOutputDecoder } from './src-decoder';

interface Context {
validations: ValidationLog;
validation: RunValidation;
}

const test = _test as TestInterface<Context>;
Expand All @@ -26,21 +26,21 @@ test.before(async t => {
const solcOutput = buildInfo.output;
const solcInput = buildInfo.input;
const decodeSrc = solcInputOutputDecoder(solcInput, solcOutput);
t.context.validations = [validate(solcOutput, decodeSrc)];
t.context.validation = validate(solcOutput, decodeSrc);
});

function testValid(name: string, valid: boolean) {
test(name, t => {
const version = getContractVersion(t.context.validations, name, 0);
t.is(isUpgradeSafe(t.context.validations, version), valid);
const version = getContractVersion(t.context.validation, name);
t.is(isUpgradeSafe([t.context.validation], version), valid);
});
}

function testOverride(name: string, opts: ValidationOptions, valid: boolean) {
const testName = name.concat(valid ? '_Allowed' : '_NotAllowed');
test(testName, t => {
const version = getContractVersion(t.context.validations, name, 0);
const assertUpgSafe = () => assertUpgradeSafe(t.context.validations, version, opts);
const version = getContractVersion(t.context.validation, name);
const assertUpgSafe = () => assertUpgradeSafe([t.context.validation], version, opts);
if (valid) {
t.notThrows(assertUpgSafe);
} else {
Expand Down Expand Up @@ -83,8 +83,8 @@ testValid('ParentHasEnum', false);
testValid('UsesLibraryWithEnum', false);

test('inherited storage', t => {
const version = getContractVersion(t.context.validations, 'StorageInheritChild', 0);
const layout = getStorageLayout(t.context.validations, version);
const version = getContractVersion(t.context.validation, 'StorageInheritChild');
const layout = getStorageLayout([t.context.validation], version);
t.is(layout.storage.length, 8);
for (let i = 0; i < layout.storage.length; i++) {
t.is(layout.storage[i].label, `v${i}`);
Expand Down
32 changes: 22 additions & 10 deletions packages/core/src/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ import { isNullish } from './utils/is-nullish';
export type ValidationLog = RunValidation[];
export type RunValidation = Record<string, ContractValidation>;

// [email protected] introduced ValidationLog but for compatibility with ^1.0.0
// the functions exported by this module also accept a single RunValidation
type Validations = ValidationLog | RunValidation;

// aliases for backwards compatibility with ^1.0.0
export type Validation = RunValidation;
export type ValidationResult = ContractValidation;

export interface ContractValidation {
version?: Version;
inherit: string[];
Expand Down Expand Up @@ -121,19 +129,21 @@ export function validate(solcOutput: SolcOutput, decodeSrc: SrcDecoder): RunVali
return validation;
}

export function getContractVersion(validations: ValidationLog, contractName: string, compilationRun: number): Version {
const { version } = validations[compilationRun][contractName];
export function getContractVersion(validation: RunValidation, contractName: string): Version {
const { version } = validation[contractName];
if (version === undefined) {
throw new Error(`Contract ${contractName} is abstract`);
}
return version;
}

export function getContractNameAndRunValidation(validations: ValidationLog, version: Version): [string, RunValidation] {
export function getContractNameAndRunValidation(validations: Validations, version: Version): [string, RunValidation] {
const validationLog = Array.isArray(validations) ? validations : [validations];

let runValidation;
let contractName;

for (const validation of validations) {
for (const validation of validationLog) {
contractName = Object.keys(validation).find(
name => validation[name].version?.withMetadata === version.withMetadata,
);
Expand All @@ -150,7 +160,7 @@ export function getContractNameAndRunValidation(validations: ValidationLog, vers
return [contractName, runValidation];
}

export function getStorageLayout(validations: ValidationLog, version: Version): StorageLayout {
export function getStorageLayout(validations: Validations, version: Version): StorageLayout {
const [contractName, runValidation] = getContractNameAndRunValidation(validations, version);
const c = runValidation[contractName];
const layout: StorageLayout = { storage: [], types: {} };
Expand All @@ -161,8 +171,10 @@ export function getStorageLayout(validations: ValidationLog, version: Version):
return layout;
}

export function getUnlinkedBytecode(validations: ValidationLog, bytecode: string): string {
for (const validation of validations) {
export function getUnlinkedBytecode(validations: Validations, bytecode: string): string {
const validationLog = Array.isArray(validations) ? validations : [validations];

for (const validation of validationLog) {
const linkableContracts = Object.keys(validation).filter(name => validation[name].linkReferences.length > 0);

for (const name of linkableContracts) {
Expand All @@ -179,7 +191,7 @@ export function getUnlinkedBytecode(validations: ValidationLog, bytecode: string
return bytecode;
}

export function assertUpgradeSafe(validations: ValidationLog, version: Version, opts: ValidationOptions): void {
export function assertUpgradeSafe(validations: Validations, version: Version, opts: ValidationOptions): void {
const [contractName] = getContractNameAndRunValidation(validations, version);

let errors = getErrors(validations, version);
Expand Down Expand Up @@ -314,15 +326,15 @@ function describeError(e: ValidationError): string {
return log.join('\n ');
}

export function getErrors(validations: ValidationLog, version: Version): ValidationError[] {
export function getErrors(validations: Validations, version: Version): ValidationError[] {
const [contractName, runValidation] = getContractNameAndRunValidation(validations, version);
const c = runValidation[contractName];
return c.errors
.concat(...c.inherit.map(name => runValidation[name].errors))
.concat(...c.libraries.map(name => runValidation[name].errors));
}

export function isUpgradeSafe(validations: ValidationLog, version: Version): boolean {
export function isUpgradeSafe(validations: Validations, version: Version): boolean {
return getErrors(validations, version).length == 0;
}

Expand Down

0 comments on commit 3fe29d1

Please sign in to comment.