Skip to content
This repository has been archived by the owner on Jan 24, 2022. It is now read-only.

Validate storage layout #92

Closed
wants to merge 21 commits into from
Closed

Conversation

spalladino
Copy link
Contributor

@spalladino spalladino commented Sep 13, 2018

When running zos-push, validate that the storage layout is compatible with the previous version of the contract, and abort push otherwise. All warnings are displayed to the user, eg:

$ zos push
Validating contract AnotherImplV1 before push
Variable 'uint256 value22' in contract ImplV1 was renamed to foo in contracts/mocks/ImplV1.sol:1.
Note that the new variable foo will have the value of value22 after upgrading. If this is not the desired behavior, add a new variable foo at the end of your contract instead.
New variable 'uint256 bar' was added in contract ImplV1 in contracts/mocks/ImplV1.sol:1
This pushes all variables after bar to a higher position in storage, causing the updated contract to read incorrect initial values. Only add new variables at the end of your contract to prevent this issue.

Storage layout is extracted from the contract's AST by the Storage class in lib, and is stored along with other contract data (such as bytecode hash) in the zos.network.json file. It is then retrieved for comparison on the next zos push. The format is a tuple (storage, types), similar to the format discussed in ethereum/solidity#4017, where storage contains the array of variables, with references to the types dictionary with detailed info on each type.

Comparison is handled by the Layout class in lib, and uses a modified levenshtein algorithm for calculating the minimum change between both storages. Changes are identified as additions (either at the end or mid-contract, with additions at the end computed at zero cost for the algorithm), deletions (end or mid-contract), or substitutions (renames, typechanges, or both).

Fixes #37

Pending:

  • Handle changes in complex types (structs)
  • Adding more tests for Layout
  • Improve and refactor CLI output
  • Test CLI output

@spalladino spalladino added status:in-progress status:in-progress Under development, do not merge this PR and removed status:in-progress labels Sep 13, 2018
@facuspagnuolo
Copy link
Contributor

Let's please create some issues for the pending items

facuspagnuolo
facuspagnuolo previously approved these changes Sep 17, 2018
Copy link
Contributor

@facuspagnuolo facuspagnuolo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing job! Congrats @spalladino 👏
Left really minor comments :)

@@ -192,12 +192,13 @@ export default class ZosNetworkFile {
this.setDependency(name, fn(this.getDependency(name)));
}

addContract(alias, instance) {
addContract(alias, instance, extraData = {}) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of using object arguments when those are kind of irrelevant to the rest of the parameters and if they are some type of configuration for the function being called. In this case, note that that extraData has the same importance as the rest of the parameters, we are deconstructing it as is, and it is impossible for the reader to know what data is actually being handled here. It forces the reader to look up for calls to this function to understand how it should be called. It is definitely not a blocker, but it is one of the things we should decide and standardize for the whole codebase.

const [begin, count] = src.split(':');
return sourceCode.substr(begin, count);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd move all this logic outside the controller

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely!

DELETION_COST = 2;

export function compareStorageLayouts(original, updated) {
// TODO: Check cases with empty storage (both for original and updated)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's move this TODO to an issue please

}

// Adapted from https://gist.github.com/andrei-m/982927 by Andrei Mackenzie
function levenshtein(originalStorage, updatedStorage, areEqualFn) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏

const originalType = originalTypes[originalVar.type],
updatedType = updatedTypes[updatedVar.type];

// TODO: Compare complex types (structs and enums)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, let's create another issue for that

? this.getValueTypeInfo(node.valueType)
: this.getTypeInfo(node)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😍

kind: 'elementary',
label: 'function'
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we move this one and the one above to constants?

@spalladino spalladino force-pushed the feature/check-storage-layout branch from 4098342 to 9d69946 Compare September 21, 2018 20:14
@spalladino
Copy link
Contributor Author

@facuspagnuolo I implemented the suggested changes, and added issues #112, #113, and #114 to cover for the missing pieces (all in Backlog for now). The only thing I'm wondering is whether we may want to add a feature toggle to disable these warnings until those issues are finished, though I think we can do that later if we find out that we can't get them ready by the next release.

@spalladino spalladino closed this Sep 24, 2018
@spalladino spalladino changed the base branch from next to master September 24, 2018 20:15
@frangio frangio removed the status:in-progress Under development, do not merge this PR label Jan 31, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Validate that an upgraded contract keeps same storage layout as the original one
3 participants