Commit 16efedcd authored by Kelvin Fichter's avatar Kelvin Fichter Committed by Kelvin Fichter

feat: reintroduce whitelist

parent f38b8000
/* Imports: External */
import { Contract, ContractFactory, Wallet } from 'ethers'
import { ethers } from 'hardhat'
import chai, { expect } from 'chai'
import { solidity } from 'ethereum-waffle'
import { predeploys } from '@eth-optimism/contracts'
/* Imports: Internal */
import { OptimismEnv } from './shared/env'
import { l2Provider } from './shared/utils'
chai.use(solidity)
describe('Whitelist', async () => {
const initialAmount = 1000
const tokenName = 'OVM Test'
const tokenDecimals = 8
const TokenSymbol = 'OVM'
let Factory__ERC20: ContractFactory
let env: OptimismEnv
before(async () => {
env = await OptimismEnv.new()
Factory__ERC20 = await ethers.getContractFactory('ERC20', env.l2Wallet)
})
describe('when the whitelist is disabled', () => {
it('should be able to deploy a contract', async () => {
await expect(
l2Provider.send('eth_call', [
Factory__ERC20.getDeployTransaction(
initialAmount,
tokenName,
tokenDecimals,
TokenSymbol
),
'latest',
{
[predeploys.OVM_DeployerWhitelist]: {
state: {
['0x0000000000000000000000000000000000000000000000000000000000000000']:
'0x0000000000000000000000000000000000000000000000000000000000000000',
},
},
},
])
).to.not.be.reverted
})
})
describe('when the whitelist is enabled', () => {
const sender = '0x' + '22'.repeat(20)
it('should fail if the user is not whitelisted', async () => {
await expect(
l2Provider.send('eth_call', [
{
...Factory__ERC20.getDeployTransaction(
initialAmount,
tokenName,
tokenDecimals,
TokenSymbol
),
from: sender,
},
'latest',
{
[predeploys.OVM_DeployerWhitelist]: {
state: {
// Set an owner but don't allow this user to deploy
// Owner here is address(1) instead of address(0)
['0x0000000000000000000000000000000000000000000000000000000000000000']:
'0x0000000000000000000000000000000000000000000000000000000000000001',
},
},
},
])
).to.be.revertedWith(`deployer address not whitelisted: ${sender}`)
})
it('should succeed if the user is whitelisted', async () => {
await expect(
l2Provider.send('eth_call', [
{
...Factory__ERC20.getDeployTransaction(
initialAmount,
tokenName,
tokenDecimals,
TokenSymbol
),
from: sender,
},
'latest',
{
[predeploys.OVM_DeployerWhitelist]: {
state: {
// Set an owner
['0x0000000000000000000000000000000000000000000000000000000000000000']:
'0x0000000000000000000000000000000000000000000000000000000000000001',
// See https://docs.soliditylang.org/en/v0.8.7/internals/layout_in_storage.html for
// reference on how the correct storage slot should be set.
// Whitelist mapping is located at storage slot 1.
// whitelist[address] will be located at:
// keccak256(uint256(address) . uint256(1)))
[ethers.utils.keccak256(
'0x' +
// uint256(address)
'0000000000000000000000002222222222222222222222222222222222222222' +
// uint256(1)
'0000000000000000000000000000000000000000000000000000000000000001'
)]:
'0x0000000000000000000000000000000000000000000000000000000000000001', // Boolean (1)
},
},
},
])
).to.not.be.reverted
})
})
})
...@@ -18,6 +18,7 @@ package vm ...@@ -18,6 +18,7 @@ package vm
import ( import (
"bytes" "bytes"
"fmt"
"math/big" "math/big"
"sync/atomic" "sync/atomic"
"time" "time"
...@@ -25,6 +26,10 @@ import ( ...@@ -25,6 +26,10 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rollup/dump"
"github.com/ethereum/go-ethereum/rollup/rcfg"
"github.com/ethereum/go-ethereum/rollup/util"
"golang.org/x/crypto/sha3"
) )
// emptyCodeHash is used by create to ensure deployment is disallowed to already // emptyCodeHash is used by create to ensure deployment is disallowed to already
...@@ -414,6 +419,20 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, ...@@ -414,6 +419,20 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, common.Address{}, gas, ErrInsufficientBalance return nil, common.Address{}, gas, ErrInsufficientBalance
} }
if rcfg.UsingOVM {
// Make sure the creator address should be able to deploy.
if !evm.AddressWhitelisted(caller.Address()) {
// Try to encode this error as a Solidity error message so it's more clear to end-users
// what's going on when a contract creation fails.
solerr := fmt.Errorf("deployer address not whitelisted: %s", caller.Address().Hex())
ret, err := util.EncodeSolidityError(solerr)
if err != nil {
// If we're unable to properly encode the error then just return the original message.
return nil, common.Address{}, gas, solerr
}
return ret, common.Address{}, gas, errExecutionReverted
}
}
nonce := evm.StateDB.GetNonce(caller.Address()) nonce := evm.StateDB.GetNonce(caller.Address())
evm.StateDB.SetNonce(caller.Address(), nonce+1) evm.StateDB.SetNonce(caller.Address(), nonce+1)
...@@ -499,3 +518,23 @@ func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment * ...@@ -499,3 +518,23 @@ func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *
// ChainConfig returns the environment's chain configuration // ChainConfig returns the environment's chain configuration
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
func (evm *EVM) AddressWhitelisted(addr common.Address) bool {
// First check if the owner is address(0), which implicitly disables the whitelist.
ownerKey := common.Hash{}
owner := evm.StateDB.GetState(dump.OvmWhitelistAddress, ownerKey)
if (owner == common.Hash{}) {
return true
}
// Next check if the user is whitelisted by resolving the position where the
// true/false value would be.
position := common.Big1
hasher := sha3.NewLegacyKeccak256()
hasher.Write(common.LeftPadBytes(addr.Bytes(), 32))
hasher.Write(common.LeftPadBytes(position.Bytes(), 32))
digest := hasher.Sum(nil)
key := common.BytesToHash(digest)
isWhitelisted := evm.StateDB.GetState(dump.OvmWhitelistAddress, key)
return isWhitelisted != common.Hash{}
}
...@@ -6,3 +6,4 @@ import ( ...@@ -6,3 +6,4 @@ import (
var OvmEthAddress = common.HexToAddress("0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000") var OvmEthAddress = common.HexToAddress("0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000")
var OvmFeeWallet = common.HexToAddress("0x4200000000000000000000000000000000000011") var OvmFeeWallet = common.HexToAddress("0x4200000000000000000000000000000000000011")
var OvmWhitelistAddress = common.HexToAddress("0x4200000000000000000000000000000000000002")
package util
import (
"fmt"
"strings"
"github.com/ethereum/go-ethereum/accounts/abi"
)
var codec abi.ABI
func init() {
const abidata = `
[
{
"type": "function",
"name": "Error",
"constant": true,
"inputs": [
{
"name": "msg",
"type": "string"
}
],
"outputs": []
}
]
`
var err error
codec, err = abi.JSON(strings.NewReader(abidata))
if err != nil {
panic(fmt.Errorf("unable to create abi decoder: %v", err))
}
}
// EncodeSolidityError generates an abi-encoded error message.
func EncodeSolidityError(err error) ([]byte, error) {
return codec.Pack("Error", err.Error())
}
...@@ -35,7 +35,8 @@ services: ...@@ -35,7 +35,8 @@ services:
DEPLOYER_PRIVATE_KEY: "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" DEPLOYER_PRIVATE_KEY: "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
SEQUENCER_PRIVATE_KEY: "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" SEQUENCER_PRIVATE_KEY: "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"
GAS_PRICE_ORACLE_OWNER: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" GAS_PRICE_ORACLE_OWNER: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
WHITELIST_OWNER: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" # setting the whitelist owner to address(0) disables the whitelist
WHITELIST_OWNER: "0x0000000000000000000000000000000000000000"
L1_FEE_WALLET_ADDRESS: "0x391716d440c151c42cdf1c95c1d83a5427bca52c" L1_FEE_WALLET_ADDRESS: "0x391716d440c151c42cdf1c95c1d83a5427bca52c"
# skip compilation when run in docker-compose, since the contracts # skip compilation when run in docker-compose, since the contracts
# were already compiled in the builder step # were already compiled in the builder step
......
...@@ -10,10 +10,16 @@ import { makeStateDump } from '../src/make-dump' ...@@ -10,10 +10,16 @@ import { makeStateDump } from '../src/make-dump'
const outfile = path.join(outdir, 'state-dump.latest.json') const outfile = path.join(outdir, 'state-dump.latest.json')
mkdirp.sync(outdir) mkdirp.sync(outdir)
// Basic warning so users know that the whitelist will be disabled if the owner is the zero address.
if (process.env.WHITELIST_OWNER === '0x' + '00'.repeat(20)) {
console.log(
'WARNING: whitelist owner is address(0), whitelist will be disabled'
)
}
const dump = await makeStateDump({ const dump = await makeStateDump({
whitelistConfig: { whitelistConfig: {
owner: process.env.WHITELIST_OWNER, owner: process.env.WHITELIST_OWNER,
allowArbitraryContractDeployment: true,
}, },
gasPriceOracleConfig: { gasPriceOracleConfig: {
owner: process.env.GAS_PRICE_ORACLE_OWNER, owner: process.env.GAS_PRICE_ORACLE_OWNER,
......
...@@ -12,12 +12,20 @@ pragma solidity >0.5.0 <0.8.0; ...@@ -12,12 +12,20 @@ pragma solidity >0.5.0 <0.8.0;
*/ */
contract OVM_DeployerWhitelist { contract OVM_DeployerWhitelist {
/**********
* Events *
**********/
event OwnerChanged(address oldOwner, address newOwner);
event WhitelistStatusChanged(address deployer, bool whitelisted);
event WhitelistDisabled(address oldOwner);
/********************** /**********************
* Contract Constants * * Contract Constants *
**********************/ **********************/
bool public initialized; // WARNING: When owner is set to address(0), the whitelist is disabled.
bool public allowArbitraryDeployment;
address public owner; address public owner;
mapping (address => bool) public whitelist; mapping (address => bool) public whitelist;
...@@ -42,26 +50,6 @@ contract OVM_DeployerWhitelist { ...@@ -42,26 +50,6 @@ contract OVM_DeployerWhitelist {
* Public Functions * * Public Functions *
********************/ ********************/
/**
* Initializes the whitelist.
* @param _owner Address of the owner for this contract.
* @param _allowArbitraryDeployment Whether or not to allow arbitrary contract deployment.
*/
function initialize(
address _owner,
bool _allowArbitraryDeployment
)
external
{
if (initialized == true) {
return;
}
initialized = true;
allowArbitraryDeployment = _allowArbitraryDeployment;
owner = _owner;
}
/** /**
* Adds or removes an address from the deployment whitelist. * Adds or removes an address from the deployment whitelist.
* @param _deployer Address to update permissions for. * @param _deployer Address to update permissions for.
...@@ -75,6 +63,7 @@ contract OVM_DeployerWhitelist { ...@@ -75,6 +63,7 @@ contract OVM_DeployerWhitelist {
onlyOwner onlyOwner
{ {
whitelist[_deployer] = _isWhitelisted; whitelist[_deployer] = _isWhitelisted;
emit WhitelistStatusChanged(_deployer, _isWhitelisted);
} }
/** /**
...@@ -87,20 +76,16 @@ contract OVM_DeployerWhitelist { ...@@ -87,20 +76,16 @@ contract OVM_DeployerWhitelist {
public public
onlyOwner onlyOwner
{ {
owner = _owner; // Prevent users from setting the whitelist owner to address(0) except via
} // enableArbitraryContractDeployment. If you want to burn the whitelist owner, send it to any
// other address that doesn't have a corresponding knowable private key.
require(
_owner != address(0),
"OVM_DeployerWhitelist: whitelist can only be disabled via enableArbitraryContractDeployment"
);
/** emit OwnerChanged(owner, _owner);
* Updates the arbitrary deployment flag. owner = _owner;
* @param _allowArbitraryDeployment Whether or not to allow arbitrary contract deployment.
*/
function setAllowArbitraryDeployment(
bool _allowArbitraryDeployment
)
public
onlyOwner
{
allowArbitraryDeployment = _allowArbitraryDeployment;
} }
/** /**
...@@ -110,8 +95,8 @@ contract OVM_DeployerWhitelist { ...@@ -110,8 +95,8 @@ contract OVM_DeployerWhitelist {
external external
onlyOwner onlyOwner
{ {
setAllowArbitraryDeployment(true); emit WhitelistDisabled(owner);
setOwner(address(0)); owner = address(0);
} }
/** /**
...@@ -123,14 +108,11 @@ contract OVM_DeployerWhitelist { ...@@ -123,14 +108,11 @@ contract OVM_DeployerWhitelist {
address _deployer address _deployer
) )
external external
view
returns ( returns (
bool bool
) )
{ {
return ( return (owner == address(0) || whitelist[_deployer]);
initialized == false
|| allowArbitraryDeployment == true
|| whitelist[_deployer]
);
} }
} }
...@@ -12,7 +12,6 @@ import { getContractArtifact } from './contract-artifacts' ...@@ -12,7 +12,6 @@ import { getContractArtifact } from './contract-artifacts'
export interface RollupDeployConfig { export interface RollupDeployConfig {
whitelistConfig: { whitelistConfig: {
owner: string | Signer owner: string | Signer
allowArbitraryContractDeployment: boolean
} }
gasPriceOracleConfig: { gasPriceOracleConfig: {
owner: string | Signer owner: string | Signer
...@@ -32,9 +31,6 @@ export interface RollupDeployConfig { ...@@ -32,9 +31,6 @@ export interface RollupDeployConfig {
export const makeStateDump = async (cfg: RollupDeployConfig): Promise<any> => { export const makeStateDump = async (cfg: RollupDeployConfig): Promise<any> => {
const variables = { const variables = {
OVM_DeployerWhitelist: { OVM_DeployerWhitelist: {
initialized: true,
allowArbitraryDeployment:
cfg.whitelistConfig.allowArbitraryContractDeployment,
owner: cfg.whitelistConfig.owner, owner: cfg.whitelistConfig.owner,
}, },
OVM_GasPriceOracle: { OVM_GasPriceOracle: {
......
...@@ -60,18 +60,12 @@ task('whitelist') ...@@ -60,18 +60,12 @@ task('whitelist')
const addr = await signer.getAddress() const addr = await signer.getAddress()
console.log(`Using signer: ${addr}`) console.log(`Using signer: ${addr}`)
let owner = await deployerWhitelist.owner() const owner = await deployerWhitelist.owner()
console.log(`OVM_DeployerWhitelist owner: ${owner}`)
if (owner === '0x0000000000000000000000000000000000000000') { if (owner === '0x0000000000000000000000000000000000000000') {
console.log(`Initializing whitelist`) console.log(`Whitelist is disabled. Exiting early.`)
const response = await deployerWhitelist.initialize(addr, false, { return
gasPrice: args.transactionGasPrice, } else {
}) console.log(`OVM_DeployerWhitelist owner: ${owner}`)
const receipt = await response.wait()
console.log(`Initialized whitelist: ${receipt.transactionHash}`)
owner = await deployerWhitelist.owner()
} }
if (addr !== owner) { if (addr !== owner) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment