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

Generalized upgrades rb #434

Merged
merged 41 commits into from
Feb 16, 2023
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
af160ae
introduce precompiles as registrable modules
ceyonur Jan 6, 2023
e1dd707
add precompile specific contract tests
ceyonur Jan 6, 2023
133714c
remove print debug
ceyonur Jan 6, 2023
a8a2b71
add unmarshal tests
ceyonur Jan 6, 2023
0d5161a
remove unnecessary func
ceyonur Jan 6, 2023
b9f9a2b
fix initial disabled value
ceyonur Jan 6, 2023
5dcec79
register all modules in core/evm/contract_stateful
ceyonur Jan 10, 2023
88789ed
more refactor & test fix
ceyonur Jan 12, 2023
a55b657
sync template
ceyonur Jan 12, 2023
62540dc
fix more tests
ceyonur Jan 12, 2023
c9992f0
rename file
ceyonur Jan 12, 2023
f5d2ab3
Merge branch 'precompile-improvements-main' into generalized-upgrades-rb
ceyonur Jan 12, 2023
85b2ae2
add comment
ceyonur Jan 12, 2023
9184227
rename
ceyonur Jan 12, 2023
4fd8ef4
fix linter
ceyonur Jan 12, 2023
798bf23
use require error contains
ceyonur Jan 12, 2023
3d91382
remove whitespace
ceyonur Jan 12, 2023
8029a56
trim mock interface
ceyonur Jan 12, 2023
6039731
sort steps
ceyonur Jan 23, 2023
f7249e0
reviews
ceyonur Jan 24, 2023
f3e05af
Update precompile/stateful_precompile_module.go
ceyonur Jan 26, 2023
cbb025e
Update params/precompile_config.go
ceyonur Jan 26, 2023
f97804e
Update params/precompile_config.go
ceyonur Jan 26, 2023
e258234
fix reviews
ceyonur Jan 26, 2023
648a8b0
add new module to configs and group module functions
ceyonur Jan 26, 2023
eb20994
Merge branch 'generalized-upgrades-rb' of github.com:ava-labs/subnet-…
ceyonur Jan 26, 2023
b89a93a
generalized-upgrades-rb review (#474)
darioush Feb 2, 2023
ce83963
review fixes
ceyonur Feb 2, 2023
f6c9c44
Merge branch 'generalized-upgrades-rb' of github.com:ava-labs/subnet-…
ceyonur Feb 2, 2023
e189eb6
Update precompile/allowlist/allowlist.go
ceyonur Feb 2, 2023
d28efae
use address in map
ceyonur Feb 2, 2023
1ffb364
fix linter for embedded keys
ceyonur Feb 2, 2023
c68d4f4
Merge branch 'generalized-upgrades-rb' of github.com:ava-labs/subnet-…
ceyonur Feb 2, 2023
0d52efe
update err messages
ceyonur Feb 2, 2023
13f152b
more err update
ceyonur Feb 2, 2023
631e2a7
remove unnecessary function (#478)
ceyonur Feb 14, 2023
acf4f9e
Start work on breaking cyclic dependency (#496)
darioush Feb 14, 2023
df4aa9a
Update core/state_processor.go
ceyonur Feb 15, 2023
9f6c6c3
fix reviews
ceyonur Feb 15, 2023
292fd6e
Update precompile/contracts/txallowlist/contract_test.go
ceyonur Feb 15, 2023
8877388
Generalized upgrades rb nits0 (#512)
darioush Feb 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions accounts/abi/bind/precompile_bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,13 @@ func PrecompileBind(types []string, abis []string, bytecodes []string, fsigs []m

configBind, err := bindHelper(types, abis, bytecodes, fsigs, pkg, lang, libs, aliases, configHook)
if err != nil {
return "", "", err
return "", "", fmt.Errorf("failed to generate config binding: %w", err)
}
contractBind, err := bindHelper(types, abis, bytecodes, fsigs, pkg, lang, libs, aliases, contractHook)

return configBind, contractBind, err
if err != nil {
return "", "", fmt.Errorf("failed to generate contract binding: %w", err)
}
return configBind, contractBind, nil
}

func createPrecompileHook(abifilename string, template string) BindHook {
Expand Down
7 changes: 3 additions & 4 deletions accounts/abi/bind/precompile_bind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ package bind

import (
"testing"

"github.com/stretchr/testify/require"
)

var bindFailedTests = []struct {
Expand Down Expand Up @@ -100,10 +102,7 @@ func golangBindingsFailure(t *testing.T) {
if err == nil {
t.Fatalf("test %d: no error occurred but was expected", i)
}

if tt.errorMsg != err.Error() {
t.Fatalf("test %d: expected Err %s but got actual Err: %s", i, tt.errorMsg, err.Error())
}
require.ErrorContains(t, err, tt.errorMsg)
})
}
}
69 changes: 43 additions & 26 deletions accounts/abi/bind/precompile_config_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ const tmplSourcePrecompileConfigGo = `
// For testing take a look at other precompile tests in core/stateful_precompile_test.go and config_test.go in other precompile folders.

/* General guidelines for precompile development:
1- Read the comment and set a suitable contract address in precompile/params.go. E.g:
{{.Contract.Type}}Address = common.HexToAddress("ASUITABLEHEXADDRESS")
2- Set gas costs in contract.go
3- It is recommended to only modify code in the highlighted areas marked with "CUSTOM CODE STARTS HERE". Modifying code outside of these areas should be done with caution and with a deep understanding of how these changes may impact the EVM.
1- Read the comment and set a suitable contract address in generated contract.go. E.g:
ContractAddress = common.HexToAddress("ASUITABLEHEXADDRESS")
2- It is recommended to only modify code in the highlighted areas marked with "CUSTOM CODE STARTS HERE". Modifying code outside of these areas should be done with caution and with a deep understanding of how these changes may impact the EVM.
Typically, custom codes are required in only those areas.
4- Add your upgradable config in params/precompile_config.go
5- Add your precompile upgrade in params/config.go
6- Add your config unit test in {generatedpkg}/config_test.go
3- Set gas costs in generated contract.go
4- Register your precompile module in params/precompile_modules.go
5- Add your config unit tests under generated package config_test.go
6- Add your contract unit tests under tests/statefulprecompiles/{precompilename}_test.go
7- Add your solidity interface and test contract to contract-examples/contracts
8- Write solidity tests for your precompile in contract-examples/test
9- Create your genesis with your precompile enabled in tests/e2e/genesis/
Expand All @@ -33,24 +33,27 @@ Typically, custom codes are required in only those areas.
package {{.Package}}

import (
"encoding/json"
"math/big"

"github.com/ava-labs/subnet-evm/precompile"
{{- if .Contract.AllowList}}
"github.com/ava-labs/subnet-evm/precompile/allowlist"
{{- end}}

"github.com/ethereum/go-ethereum/common"
)

{{$contract := .Contract}}
var (
_ precompile.StatefulPrecompileConfig = &{{.Contract.Type}}Config{}
)
var _ precompile.StatefulPrecompileConfig = &{{.Contract.Type}}Config{}

// ConfigKey is the key used in json config files to specify this precompile config.
// Must be unique across all precompiles.
const ConfigKey = "{{decapitalise .Contract.Type}}Config"

// {{.Contract.Type}}Config implements the StatefulPrecompileConfig
// interface while adding in the {{.Contract.Type}} specific precompile address.
type {{.Contract.Type}}Config struct {
{{- if .Contract.AllowList}}
precompile.AllowListConfig
allowlist.AllowListConfig
{{- end}}
precompile.UpgradeableConfig
}
Expand Down Expand Up @@ -81,7 +84,7 @@ type {{capitalise .Normalized.Name}}Output struct{
// {{.Contract.Type}} {{if .Contract.AllowList}} with the given [admins] as members of the allowlist {{end}}.
func New{{.Contract.Type}}Config(blockTimestamp *big.Int{{if .Contract.AllowList}}, admins []common.Address{{end}}) *{{.Contract.Type}}Config {
return &{{.Contract.Type}}Config{
{{if .Contract.AllowList}}AllowListConfig: precompile.AllowListConfig{AllowListAdmins: admins},{{end}}
{{if .Contract.AllowList}}AllowListConfig: allowlist.AllowListConfig{AdminAddresses: admins},{{end}}
UpgradeableConfig: precompile.UpgradeableConfig{BlockTimestamp: blockTimestamp},
}
}
Expand Down Expand Up @@ -125,27 +128,41 @@ func (c *{{.Contract.Type}}Config) Equal(s precompile.StatefulPrecompileConfig)
return equals
}

// Address returns the address of the {{.Contract.Type}}. Addresses reside under the precompile/params.go
// Select a non-conflicting address and set it in the params.go.
func (c *{{.Contract.Type}}Config) Address() common.Address {
return {{.Contract.Type}}Address
}

// Configure configures [state] with the initial configuration.
func (c *{{.Contract.Type}}Config) Configure(_ precompile.ChainConfig, state precompile.StateDB, _ precompile.BlockContext) error {
{{if .Contract.AllowList}}c.AllowListConfig.Configure(state, {{.Contract.Type}}Address){{end}}
{{if .Contract.AllowList}}c.AllowListConfig.Configure(state, ContractAddress){{end}}
// CUSTOM CODE STARTS HERE
return nil
}


// Required module functions for {{.Contract.Type}}Config
// These functions mostly do not require any custom code.

// NewModule returns a new module for {{.Contract.Type}}.
func NewModule() precompile.StatefulPrecompileModule {
return &{{.Contract.Type}}Config{}
}

// Address returns the address of the {{.Contract.Type}}.
// Select a non-conflicting address and set it in generated contract.go
func ({{.Contract.Type}}Config) Address() common.Address {
return ContractAddress
}

// Contract returns the singleton stateful precompiled contract to be used for {{.Contract.Type}}.
func (c *{{.Contract.Type}}Config) Contract() precompile.StatefulPrecompiledContract {
func ({{.Contract.Type}}Config) Contract() precompile.StatefulPrecompiledContract {
return {{.Contract.Type}}Precompile
}

// String returns a string representation of the {{.Contract.Type}}Config.
func (c *{{.Contract.Type}}Config) String() string {
bytes, _ := json.Marshal(c)
return string(bytes)
// Key returns the key used in json config files to specify this precompile config.
func ({{.Contract.Type}}Config) Key() string {
return ConfigKey
}

// New returns a new {{.Contract.Type}}Config.
// This is used by the json parser to create a new instance of the {{.Contract.Type}}Config.
func ({{.Contract.Type}}Config) NewConfig() precompile.StatefulPrecompileConfig {
return new({{.Contract.Type}}Config)
}
`
58 changes: 29 additions & 29 deletions accounts/abi/bind/precompile_contract_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,23 @@ type tmplPrecompileContract struct {
// tmplSourcePrecompileContractGo is the Go precompiled contract source template.
const tmplSourcePrecompileContractGo = `
// Code generated
// This file is a generated precompile contract with stubbed abstract functions.
// This file is a generated precompile contract config with stubbed abstract functions.
// The file is generated by a template. Please inspect every code and comment in this file before use.

// There are some must-be-done changes waiting in the file. Each area requiring you to add your code is marked with CUSTOM CODE to make them easy to find and modify.
// Additionally there are other files you need to edit to activate your precompile.
// These areas are highlighted with comments "ADD YOUR PRECOMPILE HERE".
// For testing take a look at other precompile tests in core/stateful_precompile_test.go
// For testing take a look at other precompile tests in tests/statefulprecompiles/ and config_test.go in other precompile folders.

/* General guidelines for precompile development:
1- Read the comment and set a suitable contract address in precompile/params.go. E.g:
{{.Contract.Type}}Address = common.HexToAddress("ASUITABLEHEXADDRESS")
2- Set gas costs in contract.go
1- Read the comment and set a suitable contract address in generated contract.go. E.g:
ContractAddress = common.HexToAddress("ASUITABLEHEXADDRESS")
2- Set gas costs in generated contract.go
3- It is recommended to only modify code in the highlighted areas marked with "CUSTOM CODE STARTS HERE". Modifying code outside of these areas should be done with caution and with a deep understanding of how these changes may impact the EVM.
Typically, custom codes are required in only those areas.
4- Add your upgradable config in params/precompile_config.go
5- Add your precompile upgrade in params/config.go
6- Add your config unit test in {generatedpkg}/config_test.go
4- Register your precompile module in params/precompile_modules.go
5- Add your config unit tests under generated package config_test.go
6- Add your contract unit tests under tests/statefulprecompiles/{precompilename}_test.go
7- Add your solidity interface and test contract to contract-examples/contracts
8- Write solidity tests for your precompile in contract-examples/test
9- Create your genesis with your precompile enabled in tests/e2e/genesis/
Expand All @@ -48,14 +48,16 @@ Typically, custom codes are required in only those areas.
package {{.Package}}

import (
"encoding/json"
"math/big"
"errors"
"fmt"
"strings"

"github.com/ava-labs/subnet-evm/accounts/abi"
"github.com/ava-labs/subnet-evm/precompile"
{{- if .Contract.AllowList}}
"github.com/ava-labs/subnet-evm/precompile/allowlist"
{{- end}}
"github.com/ava-labs/subnet-evm/vmerrs"

_ "embed"
Expand All @@ -77,14 +79,16 @@ const (
var (
_ = errors.New
_ = big.NewInt
_ = strings.NewReader
_ = fmt.Printf
_ = json.Unmarshal
)
ceyonur marked this conversation as resolved.
Show resolved Hide resolved

{{$contract := .Contract}}
// Singleton StatefulPrecompiledContract and signatures.
var (
// ContractAddress is the defined address of the precompile contract.
// This should be unique across all precompile contracts.
// See params/precompile_modules.go for registered precompile contracts and more information.
ContractAddress = common.HexToAddress("{ASUITABLEHEXADDRESS}") // SET A SUITABLE HEX ADDRESS HERE

{{- range .Contract.Funcs}}

{{- if not .Original.IsConstant | and $contract.AllowList}}
Expand All @@ -107,10 +111,6 @@ var (
{{.Contract.Type}}ABI abi.ABI // will be initialized by init function

{{.Contract.Type}}Precompile precompile.StatefulPrecompiledContract // will be initialized by init function

// CUSTOM CODE STARTS HERE
// THIS SHOULD BE MOVED TO precompile/params.go with a suitable hex address.
{{.Contract.Type}}Address = common.HexToAddress("ASUITABLEHEXADDRESS")
)

{{$structs := .Structs}}
Expand Down Expand Up @@ -142,26 +142,26 @@ func init() {
}
{{.Contract.Type}}ABI = parsed

{{.Contract.Type}}Precompile, err = create{{.Contract.Type}}Precompile({{.Contract.Type}}Address)
{{.Contract.Type}}Precompile, err = create{{.Contract.Type}}Precompile()
if err != nil {
panic(err)
}
}

{{if .Contract.AllowList}}
// Get{{.Contract.Type}}AllowListStatus returns the role of [address] for the {{.Contract.Type}} list.
func Get{{.Contract.Type}}AllowListStatus(stateDB precompile.StateDB, address common.Address) precompile.AllowListRole {
return precompile.GetAllowListStatus(stateDB, {{.Contract.Type}}Address, address)
func Get{{.Contract.Type}}AllowListStatus(stateDB precompile.StateDB, address common.Address) allowlist.AllowListRole {
aaronbuchwald marked this conversation as resolved.
Show resolved Hide resolved
return allowlist.GetAllowListStatus(stateDB, ContractAddress, address)
}

// Set{{.Contract.Type}}AllowListStatus sets the permissions of [address] to [role] for the
// {{.Contract.Type}} list. Assumes [role] has already been verified as valid.
// This stores the [role] in the contract storage with address [{{.Contract.Type}}Address]
// This stores the [role] in the contract storage with address [ContractAddress]
// and [address] hash. It means that any reusage of the [address] key for different value
// conflicts with the same slot [role] is stored.
// Precompile implementations must use a different key than [address] for their storage.
func Set{{.Contract.Type}}AllowListStatus(stateDB precompile.StateDB, address common.Address, role precompile.AllowListRole) {
precompile.SetAllowListRole(stateDB, {{.Contract.Type}}Address, address, role)
func Set{{.Contract.Type}}AllowListStatus(stateDB precompile.StateDB, address common.Address, role allowlist.AllowListRole) {
allowlist.SetAllowListRole(stateDB, ContractAddress, address, role)
}
{{end}}

Expand Down Expand Up @@ -257,8 +257,8 @@ func {{decapitalise .Normalized.Name}}(accessibleState precompile.PrecompileAcce
// This part of the code restricts the function to be called only by enabled/admin addresses in the allow list.
// You can modify/delete this code if you don't want this function to be restricted by the allow list.
stateDB := accessibleState.GetStateDB()
// Verify that the caller is in the allow list and therefore has the right to modify it
callerStatus := precompile.GetAllowListStatus(stateDB, {{$contract.Type}}Address, caller)
// Verify that the caller is in the allow list and therefore has the right to call this function.
callerStatus := allowlist.GetAllowListStatus(stateDB, ContractAddress, caller)
if !callerStatus.IsEnabled() {
return nil, remainingGas, fmt.Errorf("%w: %s", ErrCannot{{.Normalized.Name}}, caller)
}
Expand Down Expand Up @@ -308,8 +308,8 @@ func {{decapitalise $contract.Type}}Fallback (accessibleState precompile.Precomp
// This part of the code restricts the function to be called only by enabled/admin addresses in the allow list.
// You can modify/delete this code if you don't want this function to be restricted by the allow list.
stateDB := accessibleState.GetStateDB()
// Verify that the caller is in the allow list and therefore has the right to modify it
callerStatus := precompile.GetAllowListStatus(stateDB, {{$contract.Type}}Address, caller)
// Verify that the caller is in the allow list and therefore has the right to call this function.
callerStatus := allowlist.GetAllowListStatus(stateDB, ContractAddress, caller)
if !callerStatus.IsEnabled() {
return nil, remainingGas, fmt.Errorf("%w: %s", Err{{$contract.Type}}CannotFallback, caller)
}
Expand All @@ -329,11 +329,11 @@ func {{decapitalise $contract.Type}}Fallback (accessibleState precompile.Precomp
{{- end}}

// create{{.Contract.Type}}Precompile returns a StatefulPrecompiledContract with getters and setters for the precompile.
{{if .Contract.AllowList}} // Access to the getters/setters is controlled by an allow list for [precompileAddr].{{end}}
func create{{.Contract.Type}}Precompile(precompileAddr common.Address) (precompile.StatefulPrecompiledContract, error) {
{{if .Contract.AllowList}} // Access to the getters/setters is controlled by an allow list for ContractAddress.{{end}}
func create{{.Contract.Type}}Precompile() (precompile.StatefulPrecompiledContract, error) {
var functions []*precompile.StatefulPrecompileFunction
{{- if .Contract.AllowList}}
functions = append(functions, precompile.CreateAllowListFunctions(precompileAddr)...)
functions = append(functions, allowlist.CreateAllowListFunctions(ContractAddress)...)
{{- end}}

abiFunctionMap := map[string]precompile.RunStatefulPrecompileFunc{
Expand Down
6 changes: 3 additions & 3 deletions cmd/precompilegen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func precompilegen(c *cli.Context) error {
// Generate the contract precompile
configCode, contractCode, err := bind.PrecompileBind(types, abis, bins, sigs, pkg, lang, libs, aliases, abifilename)
if err != nil {
utils.Fatalf("Failed to generate ABI precompile: %v", err)
utils.Fatalf("Failed to generate precompile: %v", err)
}

// Either flush it out to a file or display on the standard output
Expand All @@ -165,13 +165,13 @@ func precompilegen(c *cli.Context) error {
configCodeOut := filepath.Join(outFlagStr, "config.go")

if err := os.WriteFile(configCodeOut, []byte(configCode), 0o600); err != nil {
utils.Fatalf("Failed to write generated precompile: %v", err)
utils.Fatalf("Failed to write generated config code: %v", err)
}

contractCodeOut := filepath.Join(outFlagStr, "contract.go")

if err := os.WriteFile(contractCodeOut, []byte(contractCode), 0o600); err != nil {
utils.Fatalf("Failed to write generated precompile: %v", err)
utils.Fatalf("Failed to write generated contract code: %v", err)
}

if err := os.WriteFile(abipath, []byte(abis[0]), 0o600); err != nil {
Expand Down
6 changes: 3 additions & 3 deletions contract-examples/contracts/ExampleTxAllowList.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
import "./AllowList.sol";

// ExampleDeployerList shows how ContractDeployerAllowList precompile can be used in a smart contract
// ExampleTxAllowList shows how TxAllowList precompile can be used in a smart contract
// All methods of [allowList] can be directly called. There are example calls as tasks in hardhat.config.ts file.
contract ExampleTxAllowList is AllowList {
// Precompiled Allow List Contract Address
address constant DEPLOYER_LIST = 0x0200000000000000000000000000000000000002;
address constant TX_ALLOW_LIST = 0x0200000000000000000000000000000000000002;

constructor() AllowList(DEPLOYER_LIST) {}
constructor() AllowList(TX_ALLOW_LIST) {}
}
9 changes: 4 additions & 5 deletions core/blockchain_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,8 @@ import (
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/core/vm"
"github.com/ava-labs/subnet-evm/params"
"github.com/ava-labs/subnet-evm/precompile"
"github.com/ava-labs/subnet-evm/precompile/feemanager"
"github.com/ava-labs/subnet-evm/precompile/rewardmanager"
"github.com/ava-labs/subnet-evm/precompile/contracts/feemanager"
"github.com/ava-labs/subnet-evm/precompile/contracts/rewardmanager"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/event"
)
Expand Down Expand Up @@ -350,7 +349,7 @@ func (bc *BlockChain) SubscribeAcceptedTransactionEvent(ch chan<- NewTxsEvent) e
func (bc *BlockChain) GetFeeConfigAt(parent *types.Header) (commontype.FeeConfig, *big.Int, error) {
config := bc.Config()
bigTime := new(big.Int).SetUint64(parent.Time)
if !config.IsPrecompileEnabled(precompile.FeeManagerAddress, bigTime) {
if !config.IsPrecompileEnabled(feemanager.ContractAddress, bigTime) {
return config.FeeConfig, common.Big0, nil
}

Expand Down Expand Up @@ -394,7 +393,7 @@ func (bc *BlockChain) GetCoinbaseAt(parent *types.Header) (common.Address, bool,
return constants.BlackholeAddr, false, nil
}

if !config.IsPrecompileEnabled(precompile.RewardManagerAddress, bigTime) {
if !config.IsPrecompileEnabled(rewardmanager.ContractAddress, bigTime) {
if bc.chainConfig.AllowFeeRecipients {
return common.Address{}, true, nil
} else {
Expand Down
2 changes: 1 addition & 1 deletion core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
}

// Configure any stateful precompiles that should be enabled in the genesis.
err = g.Config.ConfigurePrecompiles(nil, types.NewBlockWithHeader(head), statedb)
err = ApplyPrecompileActivations(g.Config, nil, types.NewBlockWithHeader(head), statedb)
if err != nil {
panic(fmt.Sprintf("unable to configure precompiles in genesis block: %v", err))
}
Expand Down
Loading