Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Script to deploy smart contract changes to a network via governance #4706

Merged
merged 44 commits into from
Aug 25, 2020

Conversation

asaj
Copy link
Contributor

@asaj asaj commented Aug 14, 2020

Description

This PR introduces the make-release script, which reads a backwards compatibility report against the latest release generated by check-version, deploys any changed contracts to the specified network, and outputs a JSON of the proposal that should be submitted to governance in order to promote the release.

This script is part of a series of tools that are intended to make smart contract deployments as easy and safe as possible. These include:

  1. check-deployed: Takes smart-contract source code and confirms that the specified network is running that code. Used to verify that the latest tagged release is in fact what has been released to the network.
  2. check-versions: Takes two branches and checks that, given the smart contract versions and source code of branch A, branch B has correctly set the smart contract versions given the source code changes. Outputs a summary of all smart contract changes between branch A and B. Used to sanity check that the developers' understanding of all smart contract changes from A to B are fully understood, and to specify what contracts need to be upgraded in order to release the contracts in branch B onto a network that is currently running the contracts in branch A.
  3. make-release: Takes the compatibility report output by check-versions, deploys the specified contracts, and outputs a governance proposal. Used to propose a release of the contracts in branch B onto a network that is currently running the contracts in branch A.
  4. verify-release: Takes two branches and a proposal ID, creates a compatibility report with check-versions, and confirms that the on-chain governance proposal is the result of make-release on branches A and B. Used by CELO holders to verify contract upgrade proposals before voting.

Other changes

  • Adds proxies for all linked libraries so that they can be upgraded in the future without having to upgrade contracts that link them. There is some special casing to redeploy all linked libraries with proxies in release 1, which should be removed after the release is made.
  • Adds versions to linked libraries
  • Flag rename in check-versions
  • Use 'Test' suffix to denote test contracts everywhere
  • Don't ignore linked list libraries in contract version check

Tested

First, spin up a ganache network with the contracts/0 release:

git checkout release/contracts/0
yarn devchain generate-tar .tmp/devchain.tar.gz && yarn devchain run-tar .tmp/devchain.tar.gz

Then, populate the initialization data for DowntimeSlasher:

echo '{"DowntimeSlasher": ["0x000000000000000000000000000000000000ce10", 10, 5, 20]}' > initialize_data.json

Finally, run the script:

yarn make-release -a release/contracts/0 -b asaj/make-release -n development -i initialize_data.json -p proposal.json -r report.json

Related issues

-Part of broader work towards #4426

Backwards compatibility

That's the goal!

Copy link
Contributor

@nambrot nambrot left a comment

Choose a reason for hiding this comment

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

Catching up right now, so please forgive any inaccurate statements.

If I understand correctly, the dilemma we are facing is that in master, changes to linked libraries imply that all core contracts that rely on the changes would need to be explicitly upgraded as well?

The solution that you have outlined here is to make all linked libraries proxied, so that changes to libraries can be done via repointing the library proxy instead of having to upgrade the core contracts itself.

I couldn't tell from the PR how exactly you changed the core contracts to access the linked library via proxies vs. directly?

Additionally, would you mind elaborating again why you felt that upgrading all linked libraries was favorable in your opinion? Vs. Having core contracts being able to explicitly rely on possibly older versions of libraries. I assume it is because the invariant of having the source code at a commit represents the contract bytecode in totality on a network?

packages/protocol/scripts/truffle/make-release.ts Outdated Show resolved Hide resolved
packages/protocol/scripts/truffle/make-release.ts Outdated Show resolved Hide resolved
@asaj
Copy link
Contributor Author

asaj commented Aug 17, 2020

If I understand correctly, the dilemma we are facing is that in master, changes to linked libraries imply that all core contracts that rely on the changes would need to be explicitly upgraded as well?

Correct, that is the implication. However, because changes to a library do not cause bytecode changes to any contracts that link that library, our current compatibility tool will not recognize this implication.

The solution that you have outlined here is to make all linked libraries proxied, so that changes to libraries can be done via repointing the library proxy instead of having to upgrade the core contracts itself.

Exactly

I couldn't tell from the PR how exactly you changed the core contracts to access the linked library via proxies vs. directly?

There are two pieces to this.

The first is to ensure changes to all contracts that link libraries, so that the compatibility tool flags them as needing to be upgraded. There is a sanity check in make-release.ts that confirms that all contracts that link libraries are included in the compatibility report.

The second is to deploy the proxied libraries before any of the contracts that link them, and to explicitly link the contracts to those proxied libraries before deploying the contracts.

Additionally, would you mind elaborating again why you felt that upgrading all linked libraries was favorable in your opinion? Vs. Having core contracts being able to explicitly rely on possibly older versions of libraries. I assume it is because the invariant of having the source code at a commit represents the contract bytecode in totality on a network?

Correct, having core contracts rely on older versions of libraries breaks the invariant that the latest release branch represents exactly the contract code that is running on public networks.

@asaj asaj marked this pull request as ready for review August 17, 2020 17:38
Copy link
Contributor

@yorhodes yorhodes left a comment

Choose a reason for hiding this comment

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

forgot to click submit review!

packages/contractkit/src/base.ts Show resolved Hide resolved
import "../Proxy.sol";

/* solhint-disable no-empty-blocks */
contract AddressLinkedListProxy is Proxy {}
Copy link
Contributor

Choose a reason for hiding this comment

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

any reason why these need to be explicitly separate instead of just reusing Proxy?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we wanted separate build artifacts for each. Happy to revisit this question outside the scope of this PR

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, this definitely made things easier when relying on Truffle for contract deployments. I guess going forward we'll be doing upgrade deployments somewhat differently (using this PR's script), but keeping with this convention might still be good to keep things organized, keep track of which contracts are proxied, + we still use Truffle for any dev deployments/new testnets.

*
*/

class ContractDependencies {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think there are some libraries for this like https://github.com/ConsenSys/surya
not suggesting we use it at this stage but good to be aware of in the future
or similarly publishing these scripts as our own npm package for truffle projects

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fortunately our migrations will fail if our hardcoded list of dependencies don't have complete coverage, so it's not as fragile as these sorts of things normally are.

// This makes essentially every contract dependent on Governance.
console.log(`Transferring ownership of ${contractName}Proxy to Governance`)
if (!dryRun) {
await proxy._transferOwnership(addresses.get('Governance'))
Copy link
Contributor

Choose a reason for hiding this comment

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

nit but we could pass in only the governance address to this function and fetch it once outside

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Did it this way to cover the future case where the governance address might change due to a new governance proxy being deployed

packages/protocol/scripts/truffle/make-release.ts Outdated Show resolved Hide resolved
}
// 2. Link dependencies.
const Contract = await artifacts.require(contractName)
await Promise.all(contractDependencies.map((d) => Contract.link(d, addresses.get(d))))
Copy link
Contributor

Choose a reason for hiding this comment

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

does this mutate the artifacts?
do we know if we store the artifacts linked or unlinked?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes I believe it does. They start off unlinked, and then they get linked here

Copy link
Contributor

Choose a reason for hiding this comment

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

On a related note, though not related to this PR directly, have we decided how we're going to be managing artifacts within the release process?

Copy link
Contributor

@m-chrzan m-chrzan left a comment

Choose a reason for hiding this comment

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

Looks awesome! Left some comments, mostly nits.

import "../Proxy.sol";

/* solhint-disable no-empty-blocks */
contract AddressLinkedListProxy is Proxy {}
Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, this definitely made things easier when relying on Truffle for contract deployments. I guess going forward we'll be doing upgrade deployments somewhat differently (using this PR's script), but keeping with this convention might still be good to keep things organized, keep track of which contracts are proxied, + we still use Truffle for any dev deployments/new testnets.

@@ -7,7 +7,7 @@ import "../Attestations.sol";
* but mocks the implementations of the validator set getters. Otherwise we
* couldn't test `request` with the current ganache local testnet.
*/
contract TestAttestations is Attestations {
contract AttestationsTest is Attestations {
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, definitely a major nit, but to me XTest sounds like "file containing tests for X", TestX like "a version of X used for testing". Most of these files I think are the latter, so just wondering why the rename?

Copy link
Contributor Author

@asaj asaj Aug 24, 2020

Choose a reason for hiding this comment

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

Just to be consistent, happy to rename to TestX rather than XTest but would prefer not to block this PR on that

packages/protocol/scripts/bash/check-versions.sh Outdated Show resolved Hide resolved
packages/protocol/scripts/truffle/make-release.ts Outdated Show resolved Hide resolved
}
// 2. Link dependencies.
const Contract = await artifacts.require(contractName)
await Promise.all(contractDependencies.map((d) => Contract.link(d, addresses.get(d))))
Copy link
Contributor

Choose a reason for hiding this comment

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

On a related note, though not related to this PR directly, have we decided how we're going to be managing artifacts within the release process?

contract: `${contractName}Proxy`,
function: '_setImplementation',
args: [contract.address],
value: '0',
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we also want a human readable description like above?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it's sufficiently readable, the problem with the registry repointing is that the registry ID is not human readable

@asaj asaj assigned asaj and unassigned nambrot, yorhodes and m-chrzan Aug 24, 2020
@asaj asaj added the automerge Have PR merge automatically when checks pass label Aug 24, 2020
@mergify mergify bot merged commit 8a8b822 into master Aug 25, 2020
@mergify mergify bot deleted the asaj/make-release branch August 25, 2020 14:16
nambrot added a commit that referenced this pull request Sep 24, 2020
… governance" (#4706) into "oz-prerelease" (#5194)

* Cherry pick 8a8b822

* Resolve merge conflicts from cherry-pick

* Remove double-specification of ICeloVersionedContract

Co-authored-by: Asa Oines <[email protected]>
ewilz pushed a commit that referenced this pull request Sep 29, 2020
… governance" (#4706) into "oz-prerelease" (#5194)

* Cherry pick 8a8b822

* Resolve merge conflicts from cherry-pick

* Remove double-specification of ICeloVersionedContract

Co-authored-by: Asa Oines <[email protected]>
ewilz pushed a commit to ewilz/celo-monorepo that referenced this pull request Sep 29, 2020
…elo-org#4706)

### Description

This PR introduces the `make-release` script, which reads a backwards compatibility report against the latest release generated by `check-version`, deploys any changed contracts to the specified network, and outputs a JSON of the proposal that should be submitted to governance in order to promote the release.

This script is part of a series of tools that are intended to make smart contract deployments as easy and safe as possible. These include:

1. *check-deployed*: Takes smart-contract source code and confirms that the specified network is running that code. Used to verify that the latest tagged release is in fact what has been released to the network.
2. *check-versions*: Takes two branches and checks that, given the smart contract versions and source code of branch A, branch B has correctly set the smart contract versions given the source code changes. Outputs a summary of all smart contract changes between branch A and B. Used to sanity check that the developers' understanding of all smart contract changes from A to B are fully understood, and to specify what contracts need to be upgraded in order to release the contracts in branch B onto a network that is currently running the contracts in branch A.
3. *make-release*: Takes the compatibility report output by *check-versions*, deploys the specified contracts, and outputs a governance proposal. Used to propose a release of the contracts in branch B onto a network that is currently running the contracts in branch A.
4. *verify-release*: Takes two branches and a proposal ID, creates a compatibility report with *check-versions*, and confirms that the on-chain governance proposal is the result of  *make-release* on branches A and B. Used by CELO holders to verify contract upgrade proposals before voting.


## Other changes

- Adds proxies for all linked libraries so that they can be upgraded in the future without having to upgrade contracts that link them. There is some special casing to redeploy all linked libraries with proxies in release 1, which should be removed after the release is made.
- Adds versions to linked libraries
- Flag rename in `check-versions`
- Use 'Test' suffix to denote test contracts everywhere
- Don't ignore linked list libraries in contract version check
  
### Tested
First, spin up a ganache network with the contracts/0 release:
```
git checkout release/contracts/0
yarn devchain generate-tar .tmp/devchain.tar.gz && yarn devchain run-tar .tmp/devchain.tar.gz
```

Then, populate the initialization data for DowntimeSlasher:
```
echo '{"DowntimeSlasher": ["0x000000000000000000000000000000000000ce10", 10, 5, 20]}' > initialize_data.json
```

Finally, run the script:
```
yarn make-release -a release/contracts/0 -b asaj/make-release -n development -i initialize_data.json -p proposal.json -r report.json
```


### Related issues

-Part of broader work towards celo-org#4426

### Backwards compatibility

That's the goal!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
automerge Have PR merge automatically when checks pass
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants