Merge pull request #3187 from ethereum/secure-upgrade

Save proxy addresses and storage layout for upgrade. (Secure Upgrade Part1)
pull/5370/head
David Disu 2 years ago committed by GitHub
commit bbb0df2def
  1. 41
      apps/remix-ide/src/blockchain/blockchain.js
  2. 4
      libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts
  3. 1
      libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx

@ -170,7 +170,7 @@ export class Blockchain extends Plugin {
} }
const continueCb = (error, continueTxExecution, cancelCb) => { continueTxExecution() } const continueCb = (error, continueTxExecution, cancelCb) => { continueTxExecution() }
const promptCb = (okCb, cancelCb) => { okCb() } const promptCb = (okCb, cancelCb) => { okCb() }
const finalCb = (error, txResult, address, returnValue) => { const finalCb = async (error, txResult, address, returnValue) => {
if (error) { if (error) {
const log = logBuilder(error) const log = logBuilder(error)
@ -179,6 +179,7 @@ export class Blockchain extends Plugin {
} }
if (networkInfo.name === 'VM') this.config.set('vm/proxy', address) if (networkInfo.name === 'VM') this.config.set('vm/proxy', address)
else this.config.set(`${networkInfo.name}/${networkInfo.currentFork}/${networkInfo.id}/proxy`, address) else this.config.set(`${networkInfo.name}/${networkInfo.currentFork}/${networkInfo.id}/proxy`, address)
await this.saveDeployedContractStorageLayout(implementationContractObject, address, networkInfo)
_paq.push(['trackEvent', 'blockchain', 'Deploy With Proxy', 'Proxy deployment successful']) _paq.push(['trackEvent', 'blockchain', 'Deploy With Proxy', 'Proxy deployment successful'])
this.call('udapp', 'addInstance', addressToString(address), implementationContractObject.abi, implementationContractObject.name) this.call('udapp', 'addInstance', addressToString(address), implementationContractObject.abi, implementationContractObject.name)
} }
@ -209,25 +210,61 @@ export class Blockchain extends Plugin {
async runUpgradeTx (proxyAddress, data, newImplementationContractObject) { async runUpgradeTx (proxyAddress, data, newImplementationContractObject) {
const args = { useCall: false, data, to: proxyAddress } const args = { useCall: false, data, to: proxyAddress }
let networkInfo
const confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => { const confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => {
// continue using original authorization given by user // continue using original authorization given by user
networkInfo = network
continueTxExecution(null) continueTxExecution(null)
} }
const continueCb = (error, continueTxExecution, cancelCb) => { continueTxExecution() } const continueCb = (error, continueTxExecution, cancelCb) => { continueTxExecution() }
const promptCb = (okCb, cancelCb) => { okCb() } const promptCb = (okCb, cancelCb) => { okCb() }
const finalCb = (error, txResult, address, returnValue) => { const finalCb = async (error, txResult, address, returnValue) => {
if (error) { if (error) {
const log = logBuilder(error) const log = logBuilder(error)
_paq.push(['trackEvent', 'blockchain', 'Upgrade With Proxy', 'Upgrade failed']) _paq.push(['trackEvent', 'blockchain', 'Upgrade With Proxy', 'Upgrade failed'])
return this.call('terminal', 'logHtml', log) return this.call('terminal', 'logHtml', log)
} }
await this.saveDeployedContractStorageLayout(newImplementationContractObject, proxyAddress, networkInfo)
_paq.push(['trackEvent', 'blockchain', 'Upgrade With Proxy', 'Upgrade Successful']) _paq.push(['trackEvent', 'blockchain', 'Upgrade With Proxy', 'Upgrade Successful'])
this.call('udapp', 'addInstance', addressToString(proxyAddress), newImplementationContractObject.abi, newImplementationContractObject.name) this.call('udapp', 'addInstance', addressToString(proxyAddress), newImplementationContractObject.abi, newImplementationContractObject.name)
} }
this.runTx(args, confirmationCb, continueCb, promptCb, finalCb) this.runTx(args, confirmationCb, continueCb, promptCb, finalCb)
} }
async saveDeployedContractStorageLayout (contractObject, proxyAddress, networkInfo) {
const { contractName, implementationAddress, contract } = contractObject
const hasPreviousDeploys = await this.call('fileManager', 'exists', `.deploys/upgradeable-contracts/${networkInfo.name}/UUPS.json`)
// TODO: make deploys folder read only.
if (hasPreviousDeploys) {
const deployments = await this.call('fileManager', 'readFile', `.deploys/upgradeable-contracts/${networkInfo.name}/UUPS.json`)
const parsedDeployments = JSON.parse(deployments)
parsedDeployments.deployments[proxyAddress] = {
date: new Date().toISOString(),
contractName: contractName,
fork: networkInfo.currentFork,
implementationAddress: implementationAddress,
layout: contract.object.storageLayout
}
await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkInfo.name}/UUPS.json`, JSON.stringify(parsedDeployments, null, 2))
} else {
await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkInfo.name}/UUPS.json`, JSON.stringify({
id: networkInfo.id,
network: networkInfo.name,
deployments: {
[proxyAddress]: {
date: new Date().toISOString(),
contractName: contractName,
fork: networkInfo.currentFork,
implementationAddress: implementationAddress,
layout: contract.object.storageLayout
}
}
}, null, 2))
}
}
async getEncodedFunctionHex (args, funABI) { async getEncodedFunctionHex (args, funABI) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
txFormat.encodeFunctionCall(args, funABI, (error, data) => { txFormat.encodeFunctionCall(args, funABI, (error, data) => {

@ -100,6 +100,8 @@ export class OpenZeppelinProxy extends Plugin {
} }
// re-use implementation contract's ABI for UI display in udapp and change name to proxy name. // re-use implementation contract's ABI for UI display in udapp and change name to proxy name.
implementationContractObject.contractName = implementationContractObject.name
implementationContractObject.implementationAddress = implAddress
implementationContractObject.name = proxyName implementationContractObject.name = proxyName
this.blockchain.deployProxy(data, implementationContractObject) this.blockchain.deployProxy(data, implementationContractObject)
} }
@ -116,6 +118,8 @@ export class OpenZeppelinProxy extends Plugin {
dataHex: fnData.replace('0x', '') dataHex: fnData.replace('0x', '')
} }
// re-use implementation contract's ABI for UI display in udapp and change name to proxy name. // re-use implementation contract's ABI for UI display in udapp and change name to proxy name.
newImplementationContractObject.contractName = newImplementationContractObject.name
newImplementationContractObject.implementationAddress = newImplAddress
newImplementationContractObject.name = proxyName newImplementationContractObject.name = proxyName
this.blockchain.upgradeProxy(proxyAddress, newImplAddress, data, newImplementationContractObject) this.blockchain.upgradeProxy(proxyAddress, newImplAddress, data, newImplementationContractObject)
} }

@ -1,7 +1,6 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import React, { useEffect, useRef, useState } from 'react' import React, { useEffect, useRef, useState } from 'react'
import * as remixLib from '@remix-project/remix-lib' import * as remixLib from '@remix-project/remix-lib'
import Web3 from 'web3'
import { ContractGUIProps } from '../types' import { ContractGUIProps } from '../types'
import { CopyToClipboard } from '@remix-ui/clipboard' import { CopyToClipboard } from '@remix-ui/clipboard'
import { CustomTooltip } from '@remix-ui/helper' import { CustomTooltip } from '@remix-ui/helper'

Loading…
Cancel
Save