diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go index c2be8309e1..d15d452f89 100644 --- a/accounts/abi/bind/bind.go +++ b/accounts/abi/bind/bind.go @@ -51,7 +51,7 @@ const ( readAllowListFuncKey = "readAllowList" ) -type BindHook func(lang Lang, types []string, contracts map[string]*tmplContract, structs map[string]*tmplStruct) (data interface{}, templateSource string, err error) +type BindHook func(lang Lang, pkg string, types []string, contracts map[string]*tmplContract, structs map[string]*tmplStruct) (data interface{}, templateSource string, err error) // Lang is a target programming language selector to generate bindings for. type Lang int @@ -295,7 +295,7 @@ func bindHelper(types []string, abis []string, bytecodes []string, fsigs []map[s // Generate the contract template data according to hook if bindHook != nil { var err error - data, templateSource, err = bindHook(lang, types, contracts, structs) + data, templateSource, err = bindHook(lang, pkg, types, contracts, structs) if err != nil { return "", err } diff --git a/accounts/abi/bind/precompile_bind.go b/accounts/abi/bind/precompile_bind.go index 3b33478b43..d31d9d0fef 100644 --- a/accounts/abi/bind/precompile_bind.go +++ b/accounts/abi/bind/precompile_bind.go @@ -35,9 +35,23 @@ import ( "fmt" ) -func PrecompileBind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string, abifilename string) (string, error) { - // create hook - var createPrecompileFunc BindHook = func(lang Lang, types []string, contracts map[string]*tmplContract, structs map[string]*tmplStruct) (interface{}, string, error) { +// PrecompileBind generates a Go binding for a precompiled contract. It returns config binding and contract binding. +func PrecompileBind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string, abifilename string) (string, string, error) { + // create hooks + configHook := createPrecompileHook(abifilename, tmplSourcePrecompileConfigGo) + contractHook := createPrecompileHook(abifilename, tmplSourcePrecompileContractGo) + + configBind, err := bindHelper(types, abis, bytecodes, fsigs, pkg, lang, libs, aliases, configHook) + if err != nil { + return "", "", err + } + contractBind, err := bindHelper(types, abis, bytecodes, fsigs, pkg, lang, libs, aliases, contractHook) + + return configBind, contractBind, err +} + +func createPrecompileHook(abifilename string, template string) BindHook { + var bindHook BindHook = func(lang Lang, pkg string, types []string, contracts map[string]*tmplContract, structs map[string]*tmplStruct) (interface{}, string, error) { // verify first if lang != LangGo { return nil, "", errors.New("only GoLang binding for precompiled contracts is supported yet") @@ -82,11 +96,11 @@ func PrecompileBind(types []string, abis []string, bytecodes []string, fsigs []m data := &tmplPrecompileData{ Contract: precompileContract, Structs: structs, + Package: pkg, } - return data, tmplSourcePrecompileGo, nil + return data, template, nil } - - return bindHelper(types, abis, bytecodes, fsigs, pkg, lang, libs, aliases, createPrecompileFunc) + return bindHook } func allowListEnabled(funcs map[string]*tmplMethod) bool { diff --git a/accounts/abi/bind/precompile_bind_test.go b/accounts/abi/bind/precompile_bind_test.go index 8ac85fb349..23f1503142 100644 --- a/accounts/abi/bind/precompile_bind_test.go +++ b/accounts/abi/bind/precompile_bind_test.go @@ -96,7 +96,7 @@ func golangBindingsFailure(t *testing.T) { for i, tt := range bindFailedTests { t.Run(tt.name, func(t *testing.T) { // Generate the binding - _, err := PrecompileBind([]string{tt.name}, tt.abi, tt.bytecode, tt.fsigs, "bindtest", LangGo, tt.libs, tt.aliases, "") + _, _, err := PrecompileBind([]string{tt.name}, tt.abi, tt.bytecode, tt.fsigs, "bindtest", LangGo, tt.libs, tt.aliases, "") if err == nil { t.Fatalf("test %d: no error occurred but was expected", i) } diff --git a/accounts/abi/bind/precompile_config_template.go b/accounts/abi/bind/precompile_config_template.go new file mode 100644 index 0000000000..5b4e9f6fd7 --- /dev/null +++ b/accounts/abi/bind/precompile_config_template.go @@ -0,0 +1,158 @@ +// (c) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +package bind + +// tmplSourcePrecompileConfigGo is the Go precompiled config source template. +const tmplSourcePrecompileConfigGo = ` +// Code generated +// 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 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. +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 +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/ +10- Create e2e test for your solidity test in tests/e2e/solidity/suites.go +11- Run your e2e precompile Solidity tests with 'E2E=true ./scripts/run.sh' + +*/ + +package {{.Package}} + +import ( + "encoding/json" + "math/big" + + "github.com/ava-labs/subnet-evm/precompile" + + "github.com/ethereum/go-ethereum/common" +) + +// CUSTOM CODE STARTS HERE +// Reference imports to suppress errors from unused imports. This code and any unnecessary imports can be removed. +var ( + _ = big.NewInt + _ = json.Unmarshal +) + +{{$contract := .Contract}} +var ( + _ precompile.StatefulPrecompileConfig = &{{.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 + {{- end}} + precompile.UpgradeableConfig +} + +{{$structs := .Structs}} +{{range $structs}} + // {{.Name}} is an auto generated low-level Go binding around an user-defined struct. + type {{.Name}} struct { + {{range $field := .Fields}} + {{$field.Name}} {{$field.Type}}{{end}} + } +{{- end}} + +{{- range .Contract.Funcs}} +{{ if len .Normalized.Inputs | lt 1}} +type {{capitalise .Normalized.Name}}Input struct{ +{{range .Normalized.Inputs}} {{capitalise .Name}} {{bindtype .Type $structs}}; {{end}} +} +{{- end}} +{{ if len .Normalized.Outputs | lt 1}} +type {{capitalise .Normalized.Name}}Output struct{ +{{range .Normalized.Outputs}} {{capitalise .Name}} {{bindtype .Type $structs}}; {{end}} +} +{{- end}} +{{- end}} + +// New{{.Contract.Type}}Config returns a config for a network upgrade at [blockTimestamp] that enables +// {{.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}} + UpgradeableConfig: precompile.UpgradeableConfig{BlockTimestamp: blockTimestamp}, + } +} + +// NewDisable{{.Contract.Type}}Config returns config for a network upgrade at [blockTimestamp] +// that disables {{.Contract.Type}}. +func NewDisable{{.Contract.Type}}Config(blockTimestamp *big.Int) *{{.Contract.Type}}Config { + return &{{.Contract.Type}}Config{ + UpgradeableConfig: precompile.UpgradeableConfig{ + BlockTimestamp: blockTimestamp, + Disable: true, + }, + } +} + +// Verify tries to verify {{.Contract.Type}}Config and returns an error accordingly. +func (c *{{.Contract.Type}}Config) Verify() error { + {{if .Contract.AllowList}} + // Verify AllowList first + if err := c.AllowListConfig.Verify(); err != nil { + return err + } + {{end}} + // CUSTOM CODE STARTS HERE + // Add your own custom verify code for {{.Contract.Type}}Config here + // and return an error accordingly + return nil +} + +// Equal returns true if [s] is a [*{{.Contract.Type}}Config] and it has been configured identical to [c]. +func (c *{{.Contract.Type}}Config) Equal(s precompile.StatefulPrecompileConfig) bool { + // typecast before comparison + other, ok := (s).(*{{.Contract.Type}}Config) + if !ok { + return false + } + // CUSTOM CODE STARTS HERE + // modify this boolean accordingly with your custom {{.Contract.Type}}Config, to check if [other] and the current [c] are equal + // if {{.Contract.Type}}Config contains only UpgradeableConfig {{if .Contract.AllowList}} and AllowListConfig {{end}} you can skip modifying it. + equals := c.UpgradeableConfig.Equal(&other.UpgradeableConfig) {{if .Contract.AllowList}} && c.AllowListConfig.Equal(&other.AllowListConfig) {{end}} + 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}} + // CUSTOM CODE STARTS HERE + return nil +} + +// Contract returns the singleton stateful precompiled contract to be used for {{.Contract.Type}}. +func (c *{{.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) +} +` diff --git a/accounts/abi/bind/precompile_template.go b/accounts/abi/bind/precompile_contract_template.go similarity index 69% rename from accounts/abi/bind/precompile_template.go rename to accounts/abi/bind/precompile_contract_template.go index 596d96c351..6dd7967b3e 100644 --- a/accounts/abi/bind/precompile_template.go +++ b/accounts/abi/bind/precompile_contract_template.go @@ -4,6 +4,7 @@ package bind // tmplPrecompileData is the data structure required to fill the binding template. type tmplPrecompileData struct { + Package string Contract *tmplPrecompileContract // The contract to generate into this file Structs map[string]*tmplStruct // Contract struct type definitions } @@ -16,8 +17,8 @@ type tmplPrecompileContract struct { ABIFilename string // Path to the ABI file } -// tmplSourcePrecompileGo is the Go precompiled source template. -const tmplSourcePrecompileGo = ` +// tmplSourcePrecompileContractGo is the Go precompiled contract source template. +const tmplSourcePrecompileContractGo = ` // Code generated // This file is a generated precompile contract with stubbed abstract functions. // The file is generated by a template. Please inspect every code and comment in this file before use. @@ -30,20 +31,21 @@ const tmplSourcePrecompileGo = ` /* 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 here +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. 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 solidity interface and test contract to contract-examples/contracts -7- Write solidity tests for your precompile in contract-examples/test -8- Create your genesis with your precompile enabled in tests/e2e/genesis/ -9- Create e2e test for your solidity test in tests/e2e/solidity/suites.go -10- Run your e2e precompile Solidity tests with 'E2E=true ./scripts/run.sh' +6- Add your config unit test in {generatedpkg}/config_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/ +10- Create e2e test for your solidity test in tests/e2e/solidity/suites.go +11- Run your e2e precompile Solidity tests with 'E2E=true ./scripts/run.sh' */ -package precompile +package {{.Package}} import ( "encoding/json" @@ -53,6 +55,7 @@ import ( "strings" "github.com/ava-labs/subnet-evm/accounts/abi" + "github.com/ava-labs/subnet-evm/precompile" "github.com/ava-labs/subnet-evm/vmerrs" _ "embed" @@ -82,8 +85,6 @@ var ( {{$contract := .Contract}} // Singleton StatefulPrecompiledContract and signatures. var ( - _ StatefulPrecompileConfig = &{{.Contract.Type}}Config{} - {{- range .Contract.Funcs}} {{- if not .Original.IsConstant | and $contract.AllowList}} @@ -105,22 +106,13 @@ var ( {{- end}} {{.Contract.Type}}ABI abi.ABI // will be initialized by init function - {{.Contract.Type}}Precompile StatefulPrecompiledContract // 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") ) -// {{.Contract.Type}}Config implements the StatefulPrecompileConfig -// interface while adding in the {{.Contract.Type}} specific precompile address. -type {{.Contract.Type}}Config struct { - {{- if .Contract.AllowList}} - AllowListConfig - {{- end}} - UpgradeableConfig -} - {{$structs := .Structs}} {{range $structs}} // {{.Name}} is an auto generated low-level Go binding around an user-defined struct. @@ -156,82 +148,10 @@ func init() { } } -// New{{.Contract.Type}}Config returns a config for a network upgrade at [blockTimestamp] that enables -// {{.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: AllowListConfig{AllowListAdmins: admins},{{end}} - UpgradeableConfig: UpgradeableConfig{BlockTimestamp: blockTimestamp}, - } -} - -// NewDisable{{.Contract.Type}}Config returns config for a network upgrade at [blockTimestamp] -// that disables {{.Contract.Type}}. -func NewDisable{{.Contract.Type}}Config(blockTimestamp *big.Int) *{{.Contract.Type}}Config { - return &{{.Contract.Type}}Config{ - UpgradeableConfig: UpgradeableConfig{ - BlockTimestamp: blockTimestamp, - Disable: true, - }, - } -} - -// Equal returns true if [s] is a [*{{.Contract.Type}}Config] and it has been configured identical to [c]. -func (c *{{.Contract.Type}}Config) Equal(s StatefulPrecompileConfig) bool { - // typecast before comparison - other, ok := (s).(*{{.Contract.Type}}Config) - if !ok { - return false - } - // CUSTOM CODE STARTS HERE - // modify this boolean accordingly with your custom {{.Contract.Type}}Config, to check if [other] and the current [c] are equal - // if {{.Contract.Type}}Config contains only UpgradeableConfig {{if .Contract.AllowList}} and AllowListConfig {{end}} you can skip modifying it. - equals := c.UpgradeableConfig.Equal(&other.UpgradeableConfig) {{if .Contract.AllowList}} && c.AllowListConfig.Equal(&other.AllowListConfig) {{end}} - return equals -} - -// String returns a string representation of the {{.Contract.Type}}Config. -func (c *{{.Contract.Type}}Config) String() string { - bytes, _ := json.Marshal(c) - return string(bytes) -} - -// 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(_ ChainConfig, state StateDB, _ BlockContext) error { - {{if .Contract.AllowList}}c.AllowListConfig.Configure(state, {{.Contract.Type}}Address){{end}} - // CUSTOM CODE STARTS HERE - return nil -} - -// Contract returns the singleton stateful precompiled contract to be used for {{.Contract.Type}}. -func (c *{{.Contract.Type}}Config) Contract() StatefulPrecompiledContract { - return {{.Contract.Type}}Precompile -} - -// Verify tries to verify {{.Contract.Type}}Config and returns an error accordingly. -func (c *{{.Contract.Type}}Config) Verify() error { - {{if .Contract.AllowList}} - // Verify AllowList first - if err := c.AllowListConfig.Verify(); err != nil { - return err - } - {{end}} - // CUSTOM CODE STARTS HERE - // Add your own custom verify code for {{.Contract.Type}}Config here - // and return an error accordingly - return nil -} - {{if .Contract.AllowList}} // Get{{.Contract.Type}}AllowListStatus returns the role of [address] for the {{.Contract.Type}} list. -func Get{{.Contract.Type}}AllowListStatus(stateDB StateDB, address common.Address) AllowListRole { - return getAllowListStatus(stateDB, {{.Contract.Type}}Address, address) +func Get{{.Contract.Type}}AllowListStatus(stateDB precompile.StateDB, address common.Address) precompile.AllowListRole { + return precompile.GetAllowListStatus(stateDB, {{.Contract.Type}}Address, address) } // Set{{.Contract.Type}}AllowListStatus sets the permissions of [address] to [role] for the @@ -240,8 +160,8 @@ func Get{{.Contract.Type}}AllowListStatus(stateDB StateDB, address common.Addres // 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 StateDB, address common.Address, role AllowListRole) { - setAllowListRole(stateDB, {{.Contract.Type}}Address, address, role) +func Set{{.Contract.Type}}AllowListStatus(stateDB precompile.StateDB, address common.Address, role precompile.AllowListRole) { + precompile.SetAllowListRole(stateDB, {{.Contract.Type}}Address, address, role) } {{end}} @@ -309,8 +229,8 @@ func Pack{{$method.Normalized.Name}}Output ({{decapitalise $output.Name}} {{bind } {{end}} -func {{decapitalise .Normalized.Name}}(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { - if remainingGas, err = deductGas(suppliedGas, {{.Normalized.Name}}GasCost); err != nil { +func {{decapitalise .Normalized.Name}}(accessibleState precompile.PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { + if remainingGas, err = precompile.DeductGas(suppliedGas, {{.Normalized.Name}}GasCost); err != nil { return nil, 0, err } @@ -338,7 +258,7 @@ func {{decapitalise .Normalized.Name}}(accessibleState PrecompileAccessibleState // 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 := getAllowListStatus(stateDB, {{$contract.Type}}Address, caller) + callerStatus := precompile.GetAllowListStatus(stateDB, {{$contract.Type}}Address, caller) if !callerStatus.IsEnabled() { return nil, remainingGas, fmt.Errorf("%w: %s", ErrCannot{{.Normalized.Name}}, caller) } @@ -374,8 +294,8 @@ func {{decapitalise .Normalized.Name}}(accessibleState PrecompileAccessibleState {{- with .Contract.Fallback}} // {{decapitalise $contract.Type}}Fallback executed if a function identifier does not match any of the available functions in a smart contract. // This function cannot take an input or return an output. -func {{decapitalise $contract.Type}}Fallback (accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, _ []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { - if remainingGas, err = deductGas(suppliedGas, {{$contract.Type}}FallbackGasCost); err != nil { +func {{decapitalise $contract.Type}}Fallback (accessibleState precompile.PrecompileAccessibleState, caller common.Address, addr common.Address, _ []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { + if remainingGas, err = precompile.DeductGas(suppliedGas, {{$contract.Type}}FallbackGasCost); err != nil { return nil, 0, err } @@ -389,7 +309,7 @@ func {{decapitalise $contract.Type}}Fallback (accessibleState PrecompileAccessib // 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 := getAllowListStatus(stateDB, {{$contract.Type}}Address, caller) + callerStatus := precompile.GetAllowListStatus(stateDB, {{$contract.Type}}Address, caller) if !callerStatus.IsEnabled() { return nil, remainingGas, fmt.Errorf("%w: %s", Err{{$contract.Type}}CannotFallback, caller) } @@ -410,13 +330,13 @@ func {{decapitalise $contract.Type}}Fallback (accessibleState PrecompileAccessib // 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) (StatefulPrecompiledContract, error) { - var functions []*statefulPrecompileFunction +func create{{.Contract.Type}}Precompile(precompileAddr common.Address) (precompile.StatefulPrecompiledContract, error) { + var functions []*precompile.StatefulPrecompileFunction {{- if .Contract.AllowList}} - functions = append(functions, createAllowListFunctions(precompileAddr)...) + functions = append(functions, precompile.CreateAllowListFunctions(precompileAddr)...) {{- end}} - abiFunctionMap := map[string]RunStatefulPrecompileFunc{ + abiFunctionMap := map[string]precompile.RunStatefulPrecompileFunc{ {{- range .Contract.Funcs}} "{{.Original.Name}}": {{decapitalise .Normalized.Name}}, {{- end}} @@ -427,15 +347,15 @@ func create{{.Contract.Type}}Precompile(precompileAddr common.Address) (Stateful if !ok { return nil, fmt.Errorf("given method (%s) does not exist in the ABI", name) } - functions = append(functions, newStatefulPrecompileFunction(method.ID, function)) + functions = append(functions, precompile.NewStatefulPrecompileFunction(method.ID, function)) } {{- if .Contract.Fallback}} // Construct the contract with the fallback function. - return NewStatefulPrecompileContract({{decapitalise $contract.Type}}Fallback, functions) + return precompile.NewStatefulPrecompileContract({{decapitalise $contract.Type}}Fallback, functions) {{- else}} // Construct the contract with no fallback function. - return NewStatefulPrecompileContract(nil, functions) + return precompile.NewStatefulPrecompileContract(nil, functions) {{- end}} } ` diff --git a/cmd/precompilegen/main.go b/cmd/precompilegen/main.go index 454a560bc5..0a60ed187f 100644 --- a/cmd/precompilegen/main.go +++ b/cmd/precompilegen/main.go @@ -56,15 +56,15 @@ var ( } typeFlag = &cli.StringFlag{ Name: "type", - Usage: "Struct name for the precompile (default = ABI name)", + Usage: "Struct name for the precompile (default = {ABI name})", } pkgFlag = &cli.StringFlag{ Name: "pkg", - Usage: "Package name to generate the precompile into (default = precompile)", + Usage: "Package name to generate the precompile into (default = {type})", } outFlag = &cli.StringFlag{ Name: "out", - Usage: "Output file for the generated precompile (default = STDOUT)", + Usage: "Output folder for the generated precompile files, - for STDOUT (default = ./{pkg})", } ) @@ -81,13 +81,12 @@ func init() { } func precompilegen(c *cli.Context) error { - if !c.IsSet(outFlag.Name) && !c.IsSet(typeFlag.Name) { + outFlagStr := c.String(outFlag.Name) + isOutStdout := outFlagStr == "-" + + if isOutStdout && !c.IsSet(typeFlag.Name) { utils.Fatalf("type (--type) should be set explicitly for STDOUT ") } - pkg := pkgFlag.Name - if pkg == "" { - pkg = "precompile" - } lang := bind.LangGo // If the entire solidity code was specified, build and bind based on that var ( @@ -106,6 +105,7 @@ func precompilegen(c *cli.Context) error { abi []byte err error ) + input := c.String(abiFlag.Name) if input == "-" { abi, err = io.ReadAll(os.Stdin) @@ -116,7 +116,9 @@ func precompilegen(c *cli.Context) error { utils.Fatalf("Failed to read input ABI: %v", err) } abis = append(abis, string(abi)) + bins = append(bins, "") + kind := c.String(typeFlag.Name) if kind == "" { fn := filepath.Base(input) @@ -124,30 +126,51 @@ func precompilegen(c *cli.Context) error { kind = strings.TrimSpace(kind) } types = append(types, kind) - outFlagSet := c.IsSet(outFlag.Name) - outFlag := c.String(outFlag.Name) + + pkg := c.String(pkgFlag.Name) + if pkg == "" { + pkg = strings.ToLower(kind) + } + + if outFlagStr == "" { + outFlagStr = filepath.Join("./", pkg) + } + abifilename := "" abipath := "" // we should not generate the abi file if output is set to stdout - if outFlagSet { + if !isOutStdout { // get file name from the output path - pathNoExt := strings.TrimSuffix(outFlag, filepath.Ext(outFlag)) - abipath = pathNoExt + ".abi" - abifilename = filepath.Base(abipath) + abifilename = "contract.abi" + abipath = filepath.Join(outFlagStr, abifilename) } // Generate the contract precompile - code, err := bind.PrecompileBind(types, abis, bins, sigs, pkg, lang, libs, aliases, abifilename) + 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) } // Either flush it out to a file or display on the standard output - if !outFlagSet { - fmt.Printf("%s\n", code) + if isOutStdout { + fmt.Print("-----Config Code-----\n") + fmt.Printf("%s\n", configCode) + fmt.Print("-----Contract Code-----\n") + fmt.Printf("%s\n", contractCode) return nil } - if err := os.WriteFile(outFlag, []byte(code), 0o600); err != nil { + if _, err := os.Stat(outFlagStr); os.IsNotExist(err) { + os.MkdirAll(outFlagStr, 0700) // Create your file + } + 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) + } + + 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) } @@ -155,7 +178,7 @@ func precompilegen(c *cli.Context) error { utils.Fatalf("Failed to write ABI: %v", err) } - fmt.Println("Precompile Generation was a success!") + fmt.Println("Precompile files generated successfully at: ", outFlagStr) return nil } diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index f17527c82e..5b0b943269 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -40,6 +40,8 @@ import ( "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/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/event" ) @@ -366,7 +368,7 @@ func (bc *BlockChain) GetFeeConfigAt(parent *types.Header) (commontype.FeeConfig return commontype.EmptyFeeConfig, nil, err } - storedFeeConfig := precompile.GetStoredFeeConfig(stateDB) + storedFeeConfig := feemanager.GetStoredFeeConfig(stateDB) // this should not return an invalid fee config since it's assumed that // StoreFeeConfig returns an error when an invalid fee config is attempted to be stored. // However an external stateDB call can modify the contract state. @@ -374,7 +376,7 @@ func (bc *BlockChain) GetFeeConfigAt(parent *types.Header) (commontype.FeeConfig if err := storedFeeConfig.Verify(); err != nil { return commontype.EmptyFeeConfig, nil, err } - lastChangedAt := precompile.GetFeeConfigLastChangedAt(stateDB) + lastChangedAt := feemanager.GetFeeConfigLastChangedAt(stateDB) cacheable := &cacheableFeeConfig{feeConfig: storedFeeConfig, lastChangedAt: lastChangedAt} // add it to the cache bc.feeConfigCache.Add(parent.Root, cacheable) @@ -413,7 +415,7 @@ func (bc *BlockChain) GetCoinbaseAt(parent *types.Header) (common.Address, bool, if err != nil { return common.Address{}, false, err } - rewardAddress, feeRecipients := precompile.GetStoredRewardAddress(stateDB) + rewardAddress, feeRecipients := rewardmanager.GetStoredRewardAddress(stateDB) cacheable := &cacheableCoinbaseConfig{coinbaseAddress: rewardAddress, allowFeeRecipients: feeRecipients} bc.coinbaseConfigCache.Add(parent.Root, cacheable) diff --git a/core/genesis_test.go b/core/genesis_test.go index 920e5337b6..c9d84ced6c 100644 --- a/core/genesis_test.go +++ b/core/genesis_test.go @@ -39,6 +39,7 @@ import ( "github.com/ava-labs/subnet-evm/ethdb" "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/precompile" + "github.com/ava-labs/subnet-evm/precompile/deployerallowlist" "github.com/davecgh/go-spew/spew" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/assert" @@ -190,11 +191,11 @@ func TestStatefulPrecompilesConfigure(t *testing.T) { "allow list enabled in genesis": { getConfig: func() *params.ChainConfig { config := *params.TestChainConfig - config.ContractDeployerAllowListConfig = precompile.NewContractDeployerAllowListConfig(big.NewInt(0), []common.Address{addr}, nil) + config.ContractDeployerAllowListConfig = deployerallowlist.NewContractDeployerAllowListConfig(big.NewInt(0), []common.Address{addr}, nil) return &config }, assertState: func(t *testing.T, sdb *state.StateDB) { - assert.Equal(t, precompile.AllowListAdmin, precompile.GetContractDeployerAllowListStatus(sdb, addr), "unexpected allow list status for modified address") + assert.Equal(t, precompile.AllowListAdmin, deployerallowlist.GetContractDeployerAllowListStatus(sdb, addr), "unexpected allow list status for modified address") assert.Equal(t, uint64(1), sdb.GetNonce(precompile.ContractDeployerAllowListAddress)) }, }, @@ -265,7 +266,7 @@ func TestPrecompileActivationAfterHeaderBlock(t *testing.T) { require.Greater(block.Time(), bc.lastAccepted.Time()) activatedGenesis := customg - contractDeployerConfig := precompile.NewContractDeployerAllowListConfig(big.NewInt(51), nil, nil) + contractDeployerConfig := deployerallowlist.NewContractDeployerAllowListConfig(big.NewInt(51), nil, nil) activatedGenesis.Config.UpgradeConfig.PrecompileUpgrades = []params.PrecompileUpgrade{ { // Enable ContractDeployerAllowList at timestamp 50 diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 6bf05057d9..0c5ce0f50b 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -36,7 +36,7 @@ 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/txallowlist" "github.com/ava-labs/subnet-evm/trie" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -316,7 +316,7 @@ func TestBadTxAllowListBlock(t *testing.T) { SubnetEVMTimestamp: big.NewInt(0), }, PrecompileUpgrade: params.PrecompileUpgrade{ - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(0), nil, nil), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(0), nil, nil), }, } signer = types.LatestSigner(config) diff --git a/core/state_transition.go b/core/state_transition.go index acd81569e2..5fcfa642b5 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -37,6 +37,7 @@ import ( "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/txallowlist" "github.com/ava-labs/subnet-evm/vmerrs" "github.com/ethereum/go-ethereum/common" ) @@ -250,9 +251,9 @@ func (st *StateTransition) preCheck() error { // Check that the sender is on the tx allow list if enabled if st.evm.ChainConfig().IsPrecompileEnabled(precompile.TxAllowListAddress, st.evm.Context.Time) { - txAllowListRole := precompile.GetTxAllowListStatus(st.state, st.msg.From()) + txAllowListRole := txallowlist.GetTxAllowListStatus(st.state, st.msg.From()) if !txAllowListRole.IsEnabled() { - return fmt.Errorf("%w: %s", precompile.ErrSenderAddressNotAllowListed, st.msg.From()) + return fmt.Errorf("%w: %s", txallowlist.ErrSenderAddressNotAllowListed, st.msg.From()) } } } diff --git a/core/stateful_precompile_test.go b/core/stateful_precompile_test.go index 702c8715b4..75cd50f7ff 100644 --- a/core/stateful_precompile_test.go +++ b/core/stateful_precompile_test.go @@ -14,12 +14,19 @@ import ( "github.com/ava-labs/subnet-evm/core/state" "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/precompile" + "github.com/ava-labs/subnet-evm/precompile/deployerallowlist" + "github.com/ava-labs/subnet-evm/precompile/feemanager" + "github.com/ava-labs/subnet-evm/precompile/nativeminter" + "github.com/ava-labs/subnet-evm/precompile/rewardmanager" + "github.com/ava-labs/subnet-evm/precompile/txallowlist" "github.com/ava-labs/subnet-evm/vmerrs" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/stretchr/testify/require" ) +// TODO: move this to precompile package once cross-import is resolved + var ( _ precompile.BlockContext = &mockBlockContext{} _ precompile.PrecompileAccessibleState = &mockAccessibleState{} @@ -95,7 +102,7 @@ func TestContractDeployerAllowListRun(t *testing.T) { readOnly: false, expectedRes: []byte{}, assertState: func(t *testing.T, state *state.StateDB) { - res := precompile.GetContractDeployerAllowListStatus(state, noRoleAddr) + res := deployerallowlist.GetContractDeployerAllowListStatus(state, noRoleAddr) require.Equal(t, precompile.AllowListAdmin, res) }, }, @@ -111,7 +118,7 @@ func TestContractDeployerAllowListRun(t *testing.T) { readOnly: false, expectedRes: []byte{}, assertState: func(t *testing.T, state *state.StateDB) { - res := precompile.GetContractDeployerAllowListStatus(state, noRoleAddr) + res := deployerallowlist.GetContractDeployerAllowListStatus(state, noRoleAddr) require.Equal(t, precompile.AllowListEnabled, res) }, }, @@ -127,7 +134,7 @@ func TestContractDeployerAllowListRun(t *testing.T) { readOnly: false, expectedRes: []byte{}, assertState: func(t *testing.T, state *state.StateDB) { - res := precompile.GetContractDeployerAllowListStatus(state, adminAddr) + res := deployerallowlist.GetContractDeployerAllowListStatus(state, adminAddr) require.Equal(t, precompile.AllowListNoRole, res) }, }, @@ -237,13 +244,13 @@ func TestContractDeployerAllowListRun(t *testing.T) { require.NoError(t, err) // Set up the state so that each address has the expected permissions at the start. - precompile.SetContractDeployerAllowListStatus(state, adminAddr, precompile.AllowListAdmin) - precompile.SetContractDeployerAllowListStatus(state, noRoleAddr, precompile.AllowListNoRole) - require.Equal(t, precompile.AllowListAdmin, precompile.GetContractDeployerAllowListStatus(state, adminAddr)) - require.Equal(t, precompile.AllowListNoRole, precompile.GetContractDeployerAllowListStatus(state, noRoleAddr)) + deployerallowlist.SetContractDeployerAllowListStatus(state, adminAddr, precompile.AllowListAdmin) + deployerallowlist.SetContractDeployerAllowListStatus(state, noRoleAddr, precompile.AllowListNoRole) + require.Equal(t, precompile.AllowListAdmin, deployerallowlist.GetContractDeployerAllowListStatus(state, adminAddr)) + require.Equal(t, precompile.AllowListNoRole, deployerallowlist.GetContractDeployerAllowListStatus(state, noRoleAddr)) blockContext := &mockBlockContext{blockNumber: common.Big0} - ret, remainingGas, err := precompile.ContractDeployerAllowListPrecompile.Run(&mockAccessibleState{state: state, blockContext: blockContext, snowContext: snow.DefaultContextTest()}, test.caller, precompile.ContractDeployerAllowListAddress, test.input(), test.suppliedGas, test.readOnly) + ret, remainingGas, err := deployerallowlist.ContractDeployerAllowListPrecompile.Run(&mockAccessibleState{state: state, blockContext: blockContext, snowContext: snow.DefaultContextTest()}, test.caller, precompile.ContractDeployerAllowListAddress, test.input(), test.suppliedGas, test.readOnly) if len(test.expectedErr) != 0 { require.ErrorContains(t, err, test.expectedErr) } else { @@ -290,7 +297,7 @@ func TestTxAllowListRun(t *testing.T) { readOnly: false, expectedRes: []byte{}, assertState: func(t *testing.T, state *state.StateDB) { - res := precompile.GetTxAllowListStatus(state, noRoleAddr) + res := txallowlist.GetTxAllowListStatus(state, noRoleAddr) require.Equal(t, precompile.AllowListAdmin, res) }, }, @@ -306,7 +313,7 @@ func TestTxAllowListRun(t *testing.T) { readOnly: false, expectedRes: []byte{}, assertState: func(t *testing.T, state *state.StateDB) { - res := precompile.GetTxAllowListStatus(state, noRoleAddr) + res := txallowlist.GetTxAllowListStatus(state, noRoleAddr) require.Equal(t, precompile.AllowListEnabled, res) }, }, @@ -322,7 +329,7 @@ func TestTxAllowListRun(t *testing.T) { readOnly: false, expectedRes: []byte{}, assertState: func(t *testing.T, state *state.StateDB) { - res := precompile.GetTxAllowListStatus(state, adminAddr) + res := txallowlist.GetTxAllowListStatus(state, adminAddr) require.Equal(t, precompile.AllowListNoRole, res) }, }, @@ -433,11 +440,11 @@ func TestTxAllowListRun(t *testing.T) { require.NoError(t, err) // Set up the state so that each address has the expected permissions at the start. - precompile.SetTxAllowListStatus(state, adminAddr, precompile.AllowListAdmin) - require.Equal(t, precompile.AllowListAdmin, precompile.GetTxAllowListStatus(state, adminAddr)) + txallowlist.SetTxAllowListStatus(state, adminAddr, precompile.AllowListAdmin) + require.Equal(t, precompile.AllowListAdmin, txallowlist.GetTxAllowListStatus(state, adminAddr)) blockContext := &mockBlockContext{blockNumber: common.Big0} - ret, remainingGas, err := precompile.TxAllowListPrecompile.Run(&mockAccessibleState{state: state, blockContext: blockContext, snowContext: snow.DefaultContextTest()}, test.caller, precompile.TxAllowListAddress, test.input(), test.suppliedGas, test.readOnly) + ret, remainingGas, err := txallowlist.TxAllowListPrecompile.Run(&mockAccessibleState{state: state, blockContext: blockContext, snowContext: snow.DefaultContextTest()}, test.caller, precompile.TxAllowListAddress, test.input(), test.suppliedGas, test.readOnly) if len(test.expectedErr) != 0 { require.ErrorContains(t, err, test.expectedErr) } else { @@ -460,7 +467,7 @@ func TestContractNativeMinterRun(t *testing.T) { input func() []byte suppliedGas uint64 readOnly bool - config *precompile.ContractNativeMinterConfig + config *nativeminter.ContractNativeMinterConfig expectedRes []byte expectedErr string @@ -477,24 +484,24 @@ func TestContractNativeMinterRun(t *testing.T) { "mint funds from no role fails": { caller: noRoleAddr, input: func() []byte { - input, err := precompile.PackMintInput(noRoleAddr, common.Big1) + input, err := nativeminter.PackMintInput(noRoleAddr, common.Big1) require.NoError(t, err) return input }, - suppliedGas: precompile.MintGasCost, + suppliedGas: nativeminter.MintGasCost, readOnly: false, - expectedErr: precompile.ErrCannotMint.Error(), + expectedErr: nativeminter.ErrCannotMint.Error(), }, "mint funds from enabled address": { caller: enabledAddr, input: func() []byte { - input, err := precompile.PackMintInput(enabledAddr, common.Big1) + input, err := nativeminter.PackMintInput(enabledAddr, common.Big1) require.NoError(t, err) return input }, - suppliedGas: precompile.MintGasCost, + suppliedGas: nativeminter.MintGasCost, readOnly: false, expectedRes: []byte{}, assertState: func(t *testing.T, state *state.StateDB) { @@ -510,15 +517,15 @@ func TestContractNativeMinterRun(t *testing.T) { readOnly: false, expectedRes: common.Hash(precompile.AllowListEnabled).Bytes(), assertState: func(t *testing.T, state *state.StateDB) { - require.Equal(t, precompile.AllowListEnabled, precompile.GetContractNativeMinterStatus(state, testAddr)) + require.Equal(t, precompile.AllowListEnabled, nativeminter.GetContractNativeMinterStatus(state, testAddr)) }, - config: &precompile.ContractNativeMinterConfig{ + config: &nativeminter.ContractNativeMinterConfig{ AllowListConfig: precompile.AllowListConfig{EnabledAddresses: []common.Address{testAddr}}, }, }, "initial mint funds": { caller: enabledAddr, - config: &precompile.ContractNativeMinterConfig{ + config: &nativeminter.ContractNativeMinterConfig{ InitialMint: map[common.Address]*math.HexOrDecimal256{ enabledAddr: math.NewHexOrDecimal256(2), }, @@ -536,12 +543,12 @@ func TestContractNativeMinterRun(t *testing.T) { "mint funds from admin address": { caller: adminAddr, input: func() []byte { - input, err := precompile.PackMintInput(adminAddr, common.Big1) + input, err := nativeminter.PackMintInput(adminAddr, common.Big1) require.NoError(t, err) return input }, - suppliedGas: precompile.MintGasCost, + suppliedGas: nativeminter.MintGasCost, readOnly: false, expectedRes: []byte{}, assertState: func(t *testing.T, state *state.StateDB) { @@ -551,12 +558,12 @@ func TestContractNativeMinterRun(t *testing.T) { "mint max big funds": { caller: adminAddr, input: func() []byte { - input, err := precompile.PackMintInput(adminAddr, math.MaxBig256) + input, err := nativeminter.PackMintInput(adminAddr, math.MaxBig256) require.NoError(t, err) return input }, - suppliedGas: precompile.MintGasCost, + suppliedGas: nativeminter.MintGasCost, readOnly: false, expectedRes: []byte{}, assertState: func(t *testing.T, state *state.StateDB) { @@ -566,48 +573,48 @@ func TestContractNativeMinterRun(t *testing.T) { "readOnly mint with noRole fails": { caller: noRoleAddr, input: func() []byte { - input, err := precompile.PackMintInput(adminAddr, common.Big1) + input, err := nativeminter.PackMintInput(adminAddr, common.Big1) require.NoError(t, err) return input }, - suppliedGas: precompile.MintGasCost, + suppliedGas: nativeminter.MintGasCost, readOnly: true, expectedErr: vmerrs.ErrWriteProtection.Error(), }, "readOnly mint with allow role fails": { caller: enabledAddr, input: func() []byte { - input, err := precompile.PackMintInput(enabledAddr, common.Big1) + input, err := nativeminter.PackMintInput(enabledAddr, common.Big1) require.NoError(t, err) return input }, - suppliedGas: precompile.MintGasCost, + suppliedGas: nativeminter.MintGasCost, readOnly: true, expectedErr: vmerrs.ErrWriteProtection.Error(), }, "readOnly mint with admin role fails": { caller: adminAddr, input: func() []byte { - input, err := precompile.PackMintInput(adminAddr, common.Big1) + input, err := nativeminter.PackMintInput(adminAddr, common.Big1) require.NoError(t, err) return input }, - suppliedGas: precompile.MintGasCost, + suppliedGas: nativeminter.MintGasCost, readOnly: true, expectedErr: vmerrs.ErrWriteProtection.Error(), }, "insufficient gas mint from admin": { caller: adminAddr, input: func() []byte { - input, err := precompile.PackMintInput(enabledAddr, common.Big1) + input, err := nativeminter.PackMintInput(enabledAddr, common.Big1) require.NoError(t, err) return input }, - suppliedGas: precompile.MintGasCost - 1, + suppliedGas: nativeminter.MintGasCost - 1, readOnly: false, expectedErr: vmerrs.ErrOutOfGas.Error(), }, @@ -652,7 +659,7 @@ func TestContractNativeMinterRun(t *testing.T) { readOnly: false, expectedRes: []byte{}, assertState: func(t *testing.T, state *state.StateDB) { - res := precompile.GetContractNativeMinterStatus(state, noRoleAddr) + res := nativeminter.GetContractNativeMinterStatus(state, noRoleAddr) require.Equal(t, precompile.AllowListEnabled, res) }, }, @@ -675,18 +682,18 @@ func TestContractNativeMinterRun(t *testing.T) { require.NoError(t, err) // Set up the state so that each address has the expected permissions at the start. - precompile.SetContractNativeMinterStatus(state, adminAddr, precompile.AllowListAdmin) - precompile.SetContractNativeMinterStatus(state, enabledAddr, precompile.AllowListEnabled) - precompile.SetContractNativeMinterStatus(state, noRoleAddr, precompile.AllowListNoRole) - require.Equal(t, precompile.AllowListAdmin, precompile.GetContractNativeMinterStatus(state, adminAddr)) - require.Equal(t, precompile.AllowListEnabled, precompile.GetContractNativeMinterStatus(state, enabledAddr)) - require.Equal(t, precompile.AllowListNoRole, precompile.GetContractNativeMinterStatus(state, noRoleAddr)) + nativeminter.SetContractNativeMinterStatus(state, adminAddr, precompile.AllowListAdmin) + nativeminter.SetContractNativeMinterStatus(state, enabledAddr, precompile.AllowListEnabled) + nativeminter.SetContractNativeMinterStatus(state, noRoleAddr, precompile.AllowListNoRole) + require.Equal(t, precompile.AllowListAdmin, nativeminter.GetContractNativeMinterStatus(state, adminAddr)) + require.Equal(t, precompile.AllowListEnabled, nativeminter.GetContractNativeMinterStatus(state, enabledAddr)) + require.Equal(t, precompile.AllowListNoRole, nativeminter.GetContractNativeMinterStatus(state, noRoleAddr)) blockContext := &mockBlockContext{blockNumber: common.Big0} if test.config != nil { test.config.Configure(params.TestChainConfig, state, blockContext) } - ret, remainingGas, err := precompile.ContractNativeMinterPrecompile.Run(&mockAccessibleState{state: state, blockContext: blockContext, snowContext: snow.DefaultContextTest()}, test.caller, precompile.ContractNativeMinterAddress, test.input(), test.suppliedGas, test.readOnly) + ret, remainingGas, err := nativeminter.ContractNativeMinterPrecompile.Run(&mockAccessibleState{state: state, blockContext: blockContext, snowContext: snow.DefaultContextTest()}, test.caller, precompile.ContractNativeMinterAddress, test.input(), test.suppliedGas, test.readOnly) if len(test.expectedErr) != 0 { require.ErrorContains(t, err, test.expectedErr) } else { @@ -710,7 +717,7 @@ func TestFeeConfigManagerRun(t *testing.T) { input func() []byte suppliedGas uint64 readOnly bool - config *precompile.FeeConfigManagerConfig + config *feemanager.FeeManagerConfig expectedRes []byte expectedErr string @@ -726,28 +733,28 @@ func TestFeeConfigManagerRun(t *testing.T) { "set config from no role fails": { caller: noRoleAddr, input: func() []byte { - input, err := precompile.PackSetFeeConfig(testFeeConfig) + input, err := feemanager.PackSetFeeConfig(testFeeConfig) require.NoError(t, err) return input }, - suppliedGas: precompile.SetFeeConfigGasCost, + suppliedGas: feemanager.SetFeeConfigGasCost, readOnly: false, - expectedErr: precompile.ErrCannotChangeFee.Error(), + expectedErr: feemanager.ErrCannotChangeFee.Error(), }, "set config from enabled address": { caller: enabledAddr, input: func() []byte { - input, err := precompile.PackSetFeeConfig(testFeeConfig) + input, err := feemanager.PackSetFeeConfig(testFeeConfig) require.NoError(t, err) return input }, - suppliedGas: precompile.SetFeeConfigGasCost, + suppliedGas: feemanager.SetFeeConfigGasCost, readOnly: false, expectedRes: []byte{}, assertState: func(t *testing.T, state *state.StateDB) { - feeConfig := precompile.GetStoredFeeConfig(state) + feeConfig := feemanager.GetStoredFeeConfig(state) require.Equal(t, testFeeConfig, feeConfig) }, }, @@ -756,60 +763,60 @@ func TestFeeConfigManagerRun(t *testing.T) { input: func() []byte { feeConfig := testFeeConfig feeConfig.MinBlockGasCost = new(big.Int).Mul(feeConfig.MaxBlockGasCost, common.Big2) - input, err := precompile.PackSetFeeConfig(feeConfig) + input, err := feemanager.PackSetFeeConfig(feeConfig) require.NoError(t, err) return input }, - suppliedGas: precompile.SetFeeConfigGasCost, + suppliedGas: feemanager.SetFeeConfigGasCost, readOnly: false, expectedRes: nil, - config: &precompile.FeeConfigManagerConfig{ + config: &feemanager.FeeManagerConfig{ InitialFeeConfig: &testFeeConfig, }, expectedErr: "cannot be greater than maxBlockGasCost", assertState: func(t *testing.T, state *state.StateDB) { - feeConfig := precompile.GetStoredFeeConfig(state) + feeConfig := feemanager.GetStoredFeeConfig(state) require.Equal(t, testFeeConfig, feeConfig) }, }, "set config from admin address": { caller: adminAddr, input: func() []byte { - input, err := precompile.PackSetFeeConfig(testFeeConfig) + input, err := feemanager.PackSetFeeConfig(testFeeConfig) require.NoError(t, err) return input }, - suppliedGas: precompile.SetFeeConfigGasCost, + suppliedGas: feemanager.SetFeeConfigGasCost, readOnly: false, expectedRes: []byte{}, assertState: func(t *testing.T, state *state.StateDB) { - feeConfig := precompile.GetStoredFeeConfig(state) + feeConfig := feemanager.GetStoredFeeConfig(state) require.Equal(t, testFeeConfig, feeConfig) - lastChangedAt := precompile.GetFeeConfigLastChangedAt(state) + lastChangedAt := feemanager.GetFeeConfigLastChangedAt(state) require.EqualValues(t, testBlockNumber, lastChangedAt) }, }, "get fee config from non-enabled address": { caller: noRoleAddr, preCondition: func(t *testing.T, state *state.StateDB) { - err := precompile.StoreFeeConfig(state, testFeeConfig, &mockBlockContext{blockNumber: big.NewInt(6)}) + err := feemanager.StoreFeeConfig(state, testFeeConfig, &mockBlockContext{blockNumber: big.NewInt(6)}) require.NoError(t, err) }, input: func() []byte { - return precompile.PackGetFeeConfigInput() + return feemanager.PackGetFeeConfigInput() }, - suppliedGas: precompile.GetFeeConfigGasCost, + suppliedGas: feemanager.GetFeeConfigGasCost, readOnly: true, expectedRes: func() []byte { - res, err := precompile.PackFeeConfig(testFeeConfig) + res, err := feemanager.PackFeeConfig(testFeeConfig) require.NoError(t, err) return res }(), assertState: func(t *testing.T, state *state.StateDB) { - feeConfig := precompile.GetStoredFeeConfig(state) - lastChangedAt := precompile.GetFeeConfigLastChangedAt(state) + feeConfig := feemanager.GetStoredFeeConfig(state) + lastChangedAt := feemanager.GetFeeConfigLastChangedAt(state) require.Equal(t, testFeeConfig, feeConfig) require.EqualValues(t, big.NewInt(6), lastChangedAt) }, @@ -817,21 +824,21 @@ func TestFeeConfigManagerRun(t *testing.T) { "get initial fee config": { caller: noRoleAddr, input: func() []byte { - return precompile.PackGetFeeConfigInput() + return feemanager.PackGetFeeConfigInput() }, - suppliedGas: precompile.GetFeeConfigGasCost, - config: &precompile.FeeConfigManagerConfig{ + suppliedGas: feemanager.GetFeeConfigGasCost, + config: &feemanager.FeeManagerConfig{ InitialFeeConfig: &testFeeConfig, }, readOnly: true, expectedRes: func() []byte { - res, err := precompile.PackFeeConfig(testFeeConfig) + res, err := feemanager.PackFeeConfig(testFeeConfig) require.NoError(t, err) return res }(), assertState: func(t *testing.T, state *state.StateDB) { - feeConfig := precompile.GetStoredFeeConfig(state) - lastChangedAt := precompile.GetFeeConfigLastChangedAt(state) + feeConfig := feemanager.GetStoredFeeConfig(state) + lastChangedAt := feemanager.GetFeeConfigLastChangedAt(state) require.Equal(t, testFeeConfig, feeConfig) require.EqualValues(t, testBlockNumber, lastChangedAt) }, @@ -839,18 +846,18 @@ func TestFeeConfigManagerRun(t *testing.T) { "get last changed at from non-enabled address": { caller: noRoleAddr, preCondition: func(t *testing.T, state *state.StateDB) { - err := precompile.StoreFeeConfig(state, testFeeConfig, &mockBlockContext{blockNumber: testBlockNumber}) + err := feemanager.StoreFeeConfig(state, testFeeConfig, &mockBlockContext{blockNumber: testBlockNumber}) require.NoError(t, err) }, input: func() []byte { - return precompile.PackGetLastChangedAtInput() + return feemanager.PackGetLastChangedAtInput() }, - suppliedGas: precompile.GetLastChangedAtGasCost, + suppliedGas: feemanager.GetLastChangedAtGasCost, readOnly: true, expectedRes: common.BigToHash(testBlockNumber).Bytes(), assertState: func(t *testing.T, state *state.StateDB) { - feeConfig := precompile.GetStoredFeeConfig(state) - lastChangedAt := precompile.GetFeeConfigLastChangedAt(state) + feeConfig := feemanager.GetStoredFeeConfig(state) + lastChangedAt := feemanager.GetFeeConfigLastChangedAt(state) require.Equal(t, testFeeConfig, feeConfig) require.Equal(t, testBlockNumber, lastChangedAt) }, @@ -858,48 +865,48 @@ func TestFeeConfigManagerRun(t *testing.T) { "readOnly setFeeConfig with noRole fails": { caller: noRoleAddr, input: func() []byte { - input, err := precompile.PackSetFeeConfig(testFeeConfig) + input, err := feemanager.PackSetFeeConfig(testFeeConfig) require.NoError(t, err) return input }, - suppliedGas: precompile.SetFeeConfigGasCost, + suppliedGas: feemanager.SetFeeConfigGasCost, readOnly: true, expectedErr: vmerrs.ErrWriteProtection.Error(), }, "readOnly setFeeConfig with allow role fails": { caller: enabledAddr, input: func() []byte { - input, err := precompile.PackSetFeeConfig(testFeeConfig) + input, err := feemanager.PackSetFeeConfig(testFeeConfig) require.NoError(t, err) return input }, - suppliedGas: precompile.SetFeeConfigGasCost, + suppliedGas: feemanager.SetFeeConfigGasCost, readOnly: true, expectedErr: vmerrs.ErrWriteProtection.Error(), }, "readOnly setFeeConfig with admin role fails": { caller: adminAddr, input: func() []byte { - input, err := precompile.PackSetFeeConfig(testFeeConfig) + input, err := feemanager.PackSetFeeConfig(testFeeConfig) require.NoError(t, err) return input }, - suppliedGas: precompile.SetFeeConfigGasCost, + suppliedGas: feemanager.SetFeeConfigGasCost, readOnly: true, expectedErr: vmerrs.ErrWriteProtection.Error(), }, "insufficient gas setFeeConfig from admin": { caller: adminAddr, input: func() []byte { - input, err := precompile.PackSetFeeConfig(testFeeConfig) + input, err := feemanager.PackSetFeeConfig(testFeeConfig) require.NoError(t, err) return input }, - suppliedGas: precompile.SetFeeConfigGasCost - 1, + suppliedGas: feemanager.SetFeeConfigGasCost - 1, readOnly: false, expectedErr: vmerrs.ErrOutOfGas.Error(), }, @@ -915,7 +922,7 @@ func TestFeeConfigManagerRun(t *testing.T) { readOnly: false, expectedRes: []byte{}, assertState: func(t *testing.T, state *state.StateDB) { - res := precompile.GetFeeConfigManagerStatus(state, noRoleAddr) + res := feemanager.GetFeeConfigManagerStatus(state, noRoleAddr) require.Equal(t, precompile.AllowListEnabled, res) }, }, @@ -938,9 +945,9 @@ func TestFeeConfigManagerRun(t *testing.T) { require.NoError(t, err) // Set up the state so that each address has the expected permissions at the start. - precompile.SetFeeConfigManagerStatus(state, adminAddr, precompile.AllowListAdmin) - precompile.SetFeeConfigManagerStatus(state, enabledAddr, precompile.AllowListEnabled) - precompile.SetFeeConfigManagerStatus(state, noRoleAddr, precompile.AllowListNoRole) + feemanager.SetFeeConfigManagerStatus(state, adminAddr, precompile.AllowListAdmin) + feemanager.SetFeeConfigManagerStatus(state, enabledAddr, precompile.AllowListEnabled) + feemanager.SetFeeConfigManagerStatus(state, noRoleAddr, precompile.AllowListNoRole) if test.preCondition != nil { test.preCondition(t, state) @@ -950,7 +957,7 @@ func TestFeeConfigManagerRun(t *testing.T) { if test.config != nil { test.config.Configure(params.TestChainConfig, state, blockContext) } - ret, remainingGas, err := precompile.FeeConfigManagerPrecompile.Run(&mockAccessibleState{state: state, blockContext: blockContext, snowContext: snow.DefaultContextTest()}, test.caller, precompile.FeeConfigManagerAddress, test.input(), test.suppliedGas, test.readOnly) + ret, remainingGas, err := feemanager.FeeConfigManagerPrecompile.Run(&mockAccessibleState{state: state, blockContext: blockContext, snowContext: snow.DefaultContextTest()}, test.caller, precompile.FeeConfigManagerAddress, test.input(), test.suppliedGas, test.readOnly) if len(test.expectedErr) != 0 { require.ErrorContains(t, err, test.expectedErr) } else { @@ -974,7 +981,7 @@ func TestRewardManagerRun(t *testing.T) { input func() []byte suppliedGas uint64 readOnly bool - config *precompile.RewardManagerConfig + config *rewardmanager.RewardManagerConfig expectedRes []byte expectedErr string @@ -991,68 +998,68 @@ func TestRewardManagerRun(t *testing.T) { "set allow fee recipients from no role fails": { caller: noRoleAddr, input: func() []byte { - input, err := precompile.PackAllowFeeRecipients() + input, err := rewardmanager.PackAllowFeeRecipients() require.NoError(t, err) return input }, - suppliedGas: precompile.AllowFeeRecipientsGasCost, + suppliedGas: rewardmanager.AllowFeeRecipientsGasCost, readOnly: false, - expectedErr: precompile.ErrCannotAllowFeeRecipients.Error(), + expectedErr: rewardmanager.ErrCannotAllowFeeRecipients.Error(), }, "set reward address from no role fails": { caller: noRoleAddr, input: func() []byte { - input, err := precompile.PackSetRewardAddress(testAddr) + input, err := rewardmanager.PackSetRewardAddress(testAddr) require.NoError(t, err) return input }, - suppliedGas: precompile.SetRewardAddressGasCost, + suppliedGas: rewardmanager.SetRewardAddressGasCost, readOnly: false, - expectedErr: precompile.ErrCannotSetRewardAddress.Error(), + expectedErr: rewardmanager.ErrCannotSetRewardAddress.Error(), }, "disable rewards from no role fails": { caller: noRoleAddr, input: func() []byte { - input, err := precompile.PackDisableRewards() + input, err := rewardmanager.PackDisableRewards() require.NoError(t, err) return input }, - suppliedGas: precompile.DisableRewardsGasCost, + suppliedGas: rewardmanager.DisableRewardsGasCost, readOnly: false, - expectedErr: precompile.ErrCannotDisableRewards.Error(), + expectedErr: rewardmanager.ErrCannotDisableRewards.Error(), }, "set allow fee recipients from enabled succeeds": { caller: enabledAddr, input: func() []byte { - input, err := precompile.PackAllowFeeRecipients() + input, err := rewardmanager.PackAllowFeeRecipients() require.NoError(t, err) return input }, - suppliedGas: precompile.AllowFeeRecipientsGasCost, + suppliedGas: rewardmanager.AllowFeeRecipientsGasCost, readOnly: false, expectedRes: []byte{}, assertState: func(t *testing.T, state *state.StateDB) { - _, isFeeRecipients := precompile.GetStoredRewardAddress(state) + _, isFeeRecipients := rewardmanager.GetStoredRewardAddress(state) require.True(t, isFeeRecipients) }, }, "set reward address from enabled succeeds": { caller: enabledAddr, input: func() []byte { - input, err := precompile.PackSetRewardAddress(testAddr) + input, err := rewardmanager.PackSetRewardAddress(testAddr) require.NoError(t, err) return input }, - suppliedGas: precompile.SetRewardAddressGasCost, + suppliedGas: rewardmanager.SetRewardAddressGasCost, readOnly: false, expectedRes: []byte{}, assertState: func(t *testing.T, state *state.StateDB) { - address, isFeeRecipients := precompile.GetStoredRewardAddress(state) + address, isFeeRecipients := rewardmanager.GetStoredRewardAddress(state) require.Equal(t, testAddr, address) require.False(t, isFeeRecipients) }, @@ -1060,16 +1067,16 @@ func TestRewardManagerRun(t *testing.T) { "disable rewards from enabled succeeds": { caller: enabledAddr, input: func() []byte { - input, err := precompile.PackDisableRewards() + input, err := rewardmanager.PackDisableRewards() require.NoError(t, err) return input }, - suppliedGas: precompile.DisableRewardsGasCost, + suppliedGas: rewardmanager.DisableRewardsGasCost, readOnly: false, expectedRes: []byte{}, assertState: func(t *testing.T, state *state.StateDB) { - address, isFeeRecipients := precompile.GetStoredRewardAddress(state) + address, isFeeRecipients := rewardmanager.GetStoredRewardAddress(state) require.False(t, isFeeRecipients) require.Equal(t, constants.BlackholeAddr, address) }, @@ -1077,18 +1084,18 @@ func TestRewardManagerRun(t *testing.T) { "get current reward address from no role succeeds": { caller: noRoleAddr, preCondition: func(t *testing.T, state *state.StateDB) { - precompile.StoreRewardAddress(state, testAddr) + rewardmanager.StoreRewardAddress(state, testAddr) }, input: func() []byte { - input, err := precompile.PackCurrentRewardAddress() + input, err := rewardmanager.PackCurrentRewardAddress() require.NoError(t, err) return input }, - suppliedGas: precompile.CurrentRewardAddressGasCost, + suppliedGas: rewardmanager.CurrentRewardAddressGasCost, readOnly: false, expectedRes: func() []byte { - res, err := precompile.PackCurrentRewardAddressOutput(testAddr) + res, err := rewardmanager.PackCurrentRewardAddressOutput(testAddr) require.NoError(t, err) return res }(), @@ -1096,17 +1103,17 @@ func TestRewardManagerRun(t *testing.T) { "get are fee recipients allowed from no role succeeds": { caller: noRoleAddr, preCondition: func(t *testing.T, state *state.StateDB) { - precompile.EnableAllowFeeRecipients(state) + rewardmanager.EnableAllowFeeRecipients(state) }, input: func() []byte { - input, err := precompile.PackAreFeeRecipientsAllowed() + input, err := rewardmanager.PackAreFeeRecipientsAllowed() require.NoError(t, err) return input }, - suppliedGas: precompile.AreFeeRecipientsAllowedGasCost, + suppliedGas: rewardmanager.AreFeeRecipientsAllowedGasCost, readOnly: false, expectedRes: func() []byte { - res, err := precompile.PackAreFeeRecipientsAllowedOutput(true) + res, err := rewardmanager.PackAreFeeRecipientsAllowedOutput(true) require.NoError(t, err) return res }(), @@ -1114,19 +1121,19 @@ func TestRewardManagerRun(t *testing.T) { "get initial config with address": { caller: noRoleAddr, input: func() []byte { - input, err := precompile.PackCurrentRewardAddress() + input, err := rewardmanager.PackCurrentRewardAddress() require.NoError(t, err) return input }, - suppliedGas: precompile.CurrentRewardAddressGasCost, - config: &precompile.RewardManagerConfig{ - InitialRewardConfig: &precompile.InitialRewardConfig{ + suppliedGas: rewardmanager.CurrentRewardAddressGasCost, + config: &rewardmanager.RewardManagerConfig{ + InitialRewardConfig: &rewardmanager.InitialRewardConfig{ RewardAddress: testAddr, }, }, readOnly: false, expectedRes: func() []byte { - res, err := precompile.PackCurrentRewardAddressOutput(testAddr) + res, err := rewardmanager.PackCurrentRewardAddressOutput(testAddr) require.NoError(t, err) return res }(), @@ -1134,19 +1141,19 @@ func TestRewardManagerRun(t *testing.T) { "get initial config with allow fee recipients enabled": { caller: noRoleAddr, input: func() []byte { - input, err := precompile.PackAreFeeRecipientsAllowed() + input, err := rewardmanager.PackAreFeeRecipientsAllowed() require.NoError(t, err) return input }, - suppliedGas: precompile.AreFeeRecipientsAllowedGasCost, - config: &precompile.RewardManagerConfig{ - InitialRewardConfig: &precompile.InitialRewardConfig{ + suppliedGas: rewardmanager.AreFeeRecipientsAllowedGasCost, + config: &rewardmanager.RewardManagerConfig{ + InitialRewardConfig: &rewardmanager.InitialRewardConfig{ AllowFeeRecipients: true, }, }, readOnly: false, expectedRes: func() []byte { - res, err := precompile.PackAreFeeRecipientsAllowedOutput(true) + res, err := rewardmanager.PackAreFeeRecipientsAllowedOutput(true) require.NoError(t, err) return res }(), @@ -1154,72 +1161,72 @@ func TestRewardManagerRun(t *testing.T) { "readOnly allow fee recipients with allowed role fails": { caller: enabledAddr, input: func() []byte { - input, err := precompile.PackAllowFeeRecipients() + input, err := rewardmanager.PackAllowFeeRecipients() require.NoError(t, err) return input }, - suppliedGas: precompile.AllowFeeRecipientsGasCost, + suppliedGas: rewardmanager.AllowFeeRecipientsGasCost, readOnly: true, expectedErr: vmerrs.ErrWriteProtection.Error(), }, "readOnly set reward addresss with allowed role fails": { caller: enabledAddr, input: func() []byte { - input, err := precompile.PackSetRewardAddress(testAddr) + input, err := rewardmanager.PackSetRewardAddress(testAddr) require.NoError(t, err) return input }, - suppliedGas: precompile.SetRewardAddressGasCost, + suppliedGas: rewardmanager.SetRewardAddressGasCost, readOnly: true, expectedErr: vmerrs.ErrWriteProtection.Error(), }, "insufficient gas set reward address from allowed role": { caller: enabledAddr, input: func() []byte { - input, err := precompile.PackSetRewardAddress(testAddr) + input, err := rewardmanager.PackSetRewardAddress(testAddr) require.NoError(t, err) return input }, - suppliedGas: precompile.SetRewardAddressGasCost - 1, + suppliedGas: rewardmanager.SetRewardAddressGasCost - 1, readOnly: false, expectedErr: vmerrs.ErrOutOfGas.Error(), }, "insufficient gas allow fee recipients from allowed role": { caller: enabledAddr, input: func() []byte { - input, err := precompile.PackAllowFeeRecipients() + input, err := rewardmanager.PackAllowFeeRecipients() require.NoError(t, err) return input }, - suppliedGas: precompile.AllowFeeRecipientsGasCost - 1, + suppliedGas: rewardmanager.AllowFeeRecipientsGasCost - 1, readOnly: false, expectedErr: vmerrs.ErrOutOfGas.Error(), }, "insufficient gas read current reward address from allowed role": { caller: enabledAddr, input: func() []byte { - input, err := precompile.PackCurrentRewardAddress() + input, err := rewardmanager.PackCurrentRewardAddress() require.NoError(t, err) return input }, - suppliedGas: precompile.CurrentRewardAddressGasCost - 1, + suppliedGas: rewardmanager.CurrentRewardAddressGasCost - 1, readOnly: false, expectedErr: vmerrs.ErrOutOfGas.Error(), }, "insufficient gas are fee recipients allowed from allowed role": { caller: enabledAddr, input: func() []byte { - input, err := precompile.PackAreFeeRecipientsAllowed() + input, err := rewardmanager.PackAreFeeRecipientsAllowed() require.NoError(t, err) return input }, - suppliedGas: precompile.AreFeeRecipientsAllowedGasCost - 1, + suppliedGas: rewardmanager.AreFeeRecipientsAllowedGasCost - 1, readOnly: false, expectedErr: vmerrs.ErrOutOfGas.Error(), }, @@ -1235,7 +1242,7 @@ func TestRewardManagerRun(t *testing.T) { readOnly: false, expectedRes: []byte{}, assertState: func(t *testing.T, state *state.StateDB) { - res := precompile.GetRewardManagerAllowListStatus(state, noRoleAddr) + res := rewardmanager.GetRewardManagerAllowListStatus(state, noRoleAddr) require.Equal(t, precompile.AllowListEnabled, res) }, }, @@ -1258,9 +1265,9 @@ func TestRewardManagerRun(t *testing.T) { require.NoError(t, err) // Set up the state so that each address has the expected permissions at the start. - precompile.SetRewardManagerAllowListStatus(state, adminAddr, precompile.AllowListAdmin) - precompile.SetRewardManagerAllowListStatus(state, enabledAddr, precompile.AllowListEnabled) - precompile.SetRewardManagerAllowListStatus(state, noRoleAddr, precompile.AllowListNoRole) + rewardmanager.SetRewardManagerAllowListStatus(state, adminAddr, precompile.AllowListAdmin) + rewardmanager.SetRewardManagerAllowListStatus(state, enabledAddr, precompile.AllowListEnabled) + rewardmanager.SetRewardManagerAllowListStatus(state, noRoleAddr, precompile.AllowListNoRole) if test.preCondition != nil { test.preCondition(t, state) @@ -1270,7 +1277,7 @@ func TestRewardManagerRun(t *testing.T) { if test.config != nil { test.config.Configure(params.TestChainConfig, state, blockContext) } - ret, remainingGas, err := precompile.RewardManagerPrecompile.Run(&mockAccessibleState{state: state, blockContext: blockContext, snowContext: snow.DefaultContextTest()}, test.caller, precompile.RewardManagerAddress, test.input(), test.suppliedGas, test.readOnly) + ret, remainingGas, err := rewardmanager.RewardManagerPrecompile.Run(&mockAccessibleState{state: state, blockContext: blockContext, snowContext: snow.DefaultContextTest()}, test.caller, precompile.RewardManagerAddress, test.input(), test.suppliedGas, test.readOnly) if len(test.expectedErr) != 0 { require.ErrorContains(t, err, test.expectedErr) } else { diff --git a/core/test_blockchain.go b/core/test_blockchain.go index 29b465a22f..c1254ee005 100644 --- a/core/test_blockchain.go +++ b/core/test_blockchain.go @@ -17,6 +17,8 @@ import ( "github.com/ava-labs/subnet-evm/ethdb" "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/precompile" + "github.com/ava-labs/subnet-evm/precompile/deployerallowlist" + "github.com/ava-labs/subnet-evm/precompile/feemanager" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/assert" @@ -1546,8 +1548,8 @@ func TestStatefulPrecompiles(t *testing.T, create func(db ethdb.Database, chainC genesisBalance := new(big.Int).Mul(big.NewInt(1000000), big.NewInt(params.Ether)) config := *params.TestChainConfig // Set all of the required config parameters - config.ContractDeployerAllowListConfig = precompile.NewContractDeployerAllowListConfig(big.NewInt(0), []common.Address{addr1}, nil) - config.FeeManagerConfig = precompile.NewFeeManagerConfig(big.NewInt(0), []common.Address{addr1}, nil, nil) + config.ContractDeployerAllowListConfig = deployerallowlist.NewContractDeployerAllowListConfig(big.NewInt(0), []common.Address{addr1}, nil) + config.FeeManagerConfig = feemanager.NewFeeManagerConfig(big.NewInt(0), []common.Address{addr1}, nil, nil) gspec := &Genesis{ Config: &config, Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}}, @@ -1609,22 +1611,22 @@ func TestStatefulPrecompiles(t *testing.T, create func(db ethdb.Database, chainC gen.AddTx(signedTx) }, verifyState: func(sdb *state.StateDB) error { - res := precompile.GetContractDeployerAllowListStatus(sdb, addr1) + res := deployerallowlist.GetContractDeployerAllowListStatus(sdb, addr1) if precompile.AllowListAdmin != res { return fmt.Errorf("unexpected allow list status for addr1 %s, expected %s", res, precompile.AllowListAdmin) } - res = precompile.GetContractDeployerAllowListStatus(sdb, addr2) + res = deployerallowlist.GetContractDeployerAllowListStatus(sdb, addr2) if precompile.AllowListAdmin != res { return fmt.Errorf("unexpected allow list status for addr2 %s, expected %s", res, precompile.AllowListAdmin) } return nil }, verifyGenesis: func(sdb *state.StateDB) { - res := precompile.GetContractDeployerAllowListStatus(sdb, addr1) + res := deployerallowlist.GetContractDeployerAllowListStatus(sdb, addr1) if precompile.AllowListAdmin != res { t.Fatalf("unexpected allow list status for addr1 %s, expected %s", res, precompile.AllowListAdmin) } - res = precompile.GetContractDeployerAllowListStatus(sdb, addr2) + res = deployerallowlist.GetContractDeployerAllowListStatus(sdb, addr2) if precompile.AllowListNoRole != res { t.Fatalf("unexpected allow list status for addr2 %s, expected %s", res, precompile.AllowListNoRole) } @@ -1633,7 +1635,7 @@ func TestStatefulPrecompiles(t *testing.T, create func(db ethdb.Database, chainC "fee manager set config": { addTx: func(gen *BlockGen) { feeCap := new(big.Int).Add(gen.BaseFee(), tip) - input, err := precompile.PackSetFeeConfig(testFeeConfig) + input, err := feemanager.PackSetFeeConfig(testFeeConfig) if err != nil { t.Fatal(err) } @@ -1655,10 +1657,10 @@ func TestStatefulPrecompiles(t *testing.T, create func(db ethdb.Database, chainC gen.AddTx(signedTx) }, verifyState: func(sdb *state.StateDB) error { - res := precompile.GetFeeConfigManagerStatus(sdb, addr1) + res := feemanager.GetFeeConfigManagerStatus(sdb, addr1) assert.Equal(precompile.AllowListAdmin, res) - storedConfig := precompile.GetStoredFeeConfig(sdb) + storedConfig := feemanager.GetStoredFeeConfig(sdb) assert.EqualValues(testFeeConfig, storedConfig) feeConfig, _, err := blockchain.GetFeeConfigAt(blockchain.CurrentHeader()) @@ -1667,7 +1669,7 @@ func TestStatefulPrecompiles(t *testing.T, create func(db ethdb.Database, chainC return nil }, verifyGenesis: func(sdb *state.StateDB) { - res := precompile.GetFeeConfigManagerStatus(sdb, addr1) + res := feemanager.GetFeeConfigManagerStatus(sdb, addr1) assert.Equal(precompile.AllowListAdmin, res) feeConfig, _, err := blockchain.GetFeeConfigAt(blockchain.Genesis().Header()) diff --git a/core/tx_pool.go b/core/tx_pool.go index e60e6a22ec..e6e49047b2 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -43,6 +43,7 @@ import ( "github.com/ava-labs/subnet-evm/metrics" "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/precompile" + "github.com/ava-labs/subnet-evm/precompile/txallowlist" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/prque" "github.com/ethereum/go-ethereum/event" @@ -694,9 +695,9 @@ func (pool *TxPool) checkTxState(from common.Address, tx *types.Transaction) err // If the tx allow list is enabled, return an error if the from address is not allow listed. headTimestamp := big.NewInt(int64(pool.currentHead.Time)) if pool.chainconfig.IsPrecompileEnabled(precompile.TxAllowListAddress, headTimestamp) { - txAllowListRole := precompile.GetTxAllowListStatus(pool.currentState, from) + txAllowListRole := txallowlist.GetTxAllowListStatus(pool.currentState, from) if !txAllowListRole.IsEnabled() { - return fmt.Errorf("%w: %s", precompile.ErrSenderAddressNotAllowListed, from) + return fmt.Errorf("%w: %s", txallowlist.ErrSenderAddressNotAllowListed, from) } } return nil diff --git a/core/vm/evm.go b/core/vm/evm.go index bb4b14b3e8..86849d05ad 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -36,6 +36,7 @@ import ( "github.com/ava-labs/subnet-evm/constants" "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/precompile" + "github.com/ava-labs/subnet-evm/precompile/deployerallowlist" "github.com/ava-labs/subnet-evm/vmerrs" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -508,7 +509,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, } // If the allow list is enabled, check that [evm.TxContext.Origin] has permission to deploy a contract. if evm.chainRules.IsPrecompileEnabled(precompile.ContractDeployerAllowListAddress) { - allowListRole := precompile.GetContractDeployerAllowListStatus(evm.StateDB, evm.TxContext.Origin) + allowListRole := deployerallowlist.GetContractDeployerAllowListStatus(evm.StateDB, evm.TxContext.Origin) if !allowListRole.IsEnabled() { return nil, common.Address{}, 0, fmt.Errorf("tx.origin %s is not authorized to deploy a contract", evm.TxContext.Origin) } diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index 62e770564f..0ed41c7df9 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -41,6 +41,7 @@ import ( "github.com/ava-labs/subnet-evm/ethdb" "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/rpc" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -434,13 +435,13 @@ func TestSuggestGasPriceAfterFeeConfigUpdate(t *testing.T) { // create a chain config with fee manager enabled at genesis with [addr] as the admin chainConfig := *params.TestChainConfig - chainConfig.FeeManagerConfig = precompile.NewFeeManagerConfig(big.NewInt(0), []common.Address{addr}, nil, nil) + chainConfig.FeeManagerConfig = feemanager.NewFeeManagerConfig(big.NewInt(0), []common.Address{addr}, nil, nil) // create a fee config with higher MinBaseFee and prepare it for inclusion in a tx signer := types.LatestSigner(params.TestChainConfig) highFeeConfig := chainConfig.FeeConfig highFeeConfig.MinBaseFee = big.NewInt(28_000_000_000) - data, err := precompile.PackSetFeeConfig(highFeeConfig) + data, err := feemanager.PackSetFeeConfig(highFeeConfig) require.NoError(err) // before issuing the block changing the fee into the chain, the fee estimation should diff --git a/params/precompile_config.go b/params/precompile_config.go index cd629ced55..fc2547092f 100644 --- a/params/precompile_config.go +++ b/params/precompile_config.go @@ -8,6 +8,11 @@ import ( "math/big" "github.com/ava-labs/subnet-evm/precompile" + "github.com/ava-labs/subnet-evm/precompile/deployerallowlist" + "github.com/ava-labs/subnet-evm/precompile/feemanager" + "github.com/ava-labs/subnet-evm/precompile/nativeminter" + "github.com/ava-labs/subnet-evm/precompile/rewardmanager" + "github.com/ava-labs/subnet-evm/precompile/txallowlist" "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" @@ -17,11 +22,11 @@ import ( // each of the possible stateful precompile types that can be activated // as a network upgrade. type PrecompileUpgrade struct { - ContractDeployerAllowListConfig *precompile.ContractDeployerAllowListConfig `json:"contractDeployerAllowListConfig,omitempty"` // Config for the contract deployer allow list precompile - ContractNativeMinterConfig *precompile.ContractNativeMinterConfig `json:"contractNativeMinterConfig,omitempty"` // Config for the native minter precompile - TxAllowListConfig *precompile.TxAllowListConfig `json:"txAllowListConfig,omitempty"` // Config for the tx allow list precompile - FeeManagerConfig *precompile.FeeConfigManagerConfig `json:"feeManagerConfig,omitempty"` // Config for the fee manager precompile - RewardManagerConfig *precompile.RewardManagerConfig `json:"rewardManagerConfig,omitempty"` // Config for the reward manager precompile + ContractDeployerAllowListConfig *deployerallowlist.ContractDeployerAllowListConfig `json:"contractDeployerAllowListConfig,omitempty"` // Config for the contract deployer allow list precompile + ContractNativeMinterConfig *nativeminter.ContractNativeMinterConfig `json:"contractNativeMinterConfig,omitempty"` // Config for the native minter precompile + TxAllowListConfig *txallowlist.TxAllowListConfig `json:"txAllowListConfig,omitempty"` // Config for the tx allow list precompile + FeeManagerConfig *feemanager.FeeManagerConfig `json:"feeManagerConfig,omitempty"` // Config for the fee manager precompile + RewardManagerConfig *rewardmanager.RewardManagerConfig `json:"rewardManagerConfig,omitempty"` // Config for the reward manager precompile // ADD YOUR PRECOMPILE HERE // {YourPrecompile}Config *precompile.{YourPrecompile}Config `json:"{yourPrecompile}Config,omitempty"` } @@ -171,19 +176,19 @@ func (c *ChainConfig) GetPrecompileConfig(address common.Address, blockTimestamp func (c *ChainConfig) GetActivePrecompileUpgrade(blockTimestamp *big.Int) PrecompileUpgrade { pu := PrecompileUpgrade{} if config := c.GetPrecompileConfig(precompile.ContractDeployerAllowListAddress, blockTimestamp); config != nil && !config.IsDisabled() { - pu.ContractDeployerAllowListConfig = config.(*precompile.ContractDeployerAllowListConfig) + pu.ContractDeployerAllowListConfig = config.(*deployerallowlist.ContractDeployerAllowListConfig) } if config := c.GetPrecompileConfig(precompile.ContractNativeMinterAddress, blockTimestamp); config != nil && !config.IsDisabled() { - pu.ContractNativeMinterConfig = config.(*precompile.ContractNativeMinterConfig) + pu.ContractNativeMinterConfig = config.(*nativeminter.ContractNativeMinterConfig) } if config := c.GetPrecompileConfig(precompile.TxAllowListAddress, blockTimestamp); config != nil && !config.IsDisabled() { - pu.TxAllowListConfig = config.(*precompile.TxAllowListConfig) + pu.TxAllowListConfig = config.(*txallowlist.TxAllowListConfig) } if config := c.GetPrecompileConfig(precompile.FeeConfigManagerAddress, blockTimestamp); config != nil && !config.IsDisabled() { - pu.FeeManagerConfig = config.(*precompile.FeeConfigManagerConfig) + pu.FeeManagerConfig = config.(*feemanager.FeeManagerConfig) } if config := c.GetPrecompileConfig(precompile.RewardManagerAddress, blockTimestamp); config != nil && !config.IsDisabled() { - pu.RewardManagerConfig = config.(*precompile.RewardManagerConfig) + pu.RewardManagerConfig = config.(*rewardmanager.RewardManagerConfig) } // ADD YOUR PRECOMPILE HERE diff --git a/params/precompile_config_test.go b/params/precompile_config_test.go index 3ac584aec4..4d7a114d14 100644 --- a/params/precompile_config_test.go +++ b/params/precompile_config_test.go @@ -9,6 +9,9 @@ import ( "github.com/ava-labs/subnet-evm/commontype" "github.com/ava-labs/subnet-evm/precompile" + "github.com/ava-labs/subnet-evm/precompile/deployerallowlist" + "github.com/ava-labs/subnet-evm/precompile/feemanager" + "github.com/ava-labs/subnet-evm/precompile/txallowlist" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -19,16 +22,16 @@ func TestVerifyWithChainConfig(t *testing.T) { baseConfig := *SubnetEVMDefaultChainConfig config := &baseConfig config.PrecompileUpgrade = PrecompileUpgrade{ - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(2), nil, nil), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(2), nil, nil), } config.PrecompileUpgrades = []PrecompileUpgrade{ { // disable TxAllowList at timestamp 4 - TxAllowListConfig: precompile.NewDisableTxAllowListConfig(big.NewInt(4)), + TxAllowListConfig: txallowlist.NewDisableTxAllowListConfig(big.NewInt(4)), }, { // re-enable TxAllowList at timestamp 5 - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(5), admins, nil), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(5), admins, nil), }, } @@ -41,7 +44,7 @@ func TestVerifyWithChainConfig(t *testing.T) { badConfig.PrecompileUpgrades = append( badConfig.PrecompileUpgrades, PrecompileUpgrade{ - TxAllowListConfig: precompile.NewDisableTxAllowListConfig(big.NewInt(5)), + TxAllowListConfig: txallowlist.NewDisableTxAllowListConfig(big.NewInt(5)), }, ) err = badConfig.Verify() @@ -52,7 +55,7 @@ func TestVerifyWithChainConfig(t *testing.T) { badConfig.PrecompileUpgrades = append( badConfig.PrecompileUpgrades, PrecompileUpgrade{ - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(5), admins, nil), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(5), admins, nil), }, ) err = badConfig.Verify() @@ -70,10 +73,10 @@ func TestVerifyPrecompileUpgrades(t *testing.T) { name: "enable and disable tx allow list", upgrades: []PrecompileUpgrade{ { - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(1), admins, nil), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(1), admins, nil), }, { - TxAllowListConfig: precompile.NewDisableTxAllowListConfig(big.NewInt(2)), + TxAllowListConfig: txallowlist.NewDisableTxAllowListConfig(big.NewInt(2)), }, }, expectedError: "", @@ -82,13 +85,13 @@ func TestVerifyPrecompileUpgrades(t *testing.T) { name: "invalid allow list config in tx allowlist", upgrades: []PrecompileUpgrade{ { - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(1), admins, nil), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(1), admins, nil), }, { - TxAllowListConfig: precompile.NewDisableTxAllowListConfig(big.NewInt(2)), + TxAllowListConfig: txallowlist.NewDisableTxAllowListConfig(big.NewInt(2)), }, { - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(3), admins, admins), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(3), admins, admins), }, }, expectedError: "cannot set address", @@ -97,7 +100,7 @@ func TestVerifyPrecompileUpgrades(t *testing.T) { name: "invalid initial fee manager config", upgrades: []PrecompileUpgrade{ { - FeeManagerConfig: precompile.NewFeeManagerConfig(big.NewInt(3), admins, nil, + FeeManagerConfig: feemanager.NewFeeManagerConfig(big.NewInt(3), admins, nil, &commontype.FeeConfig{ GasLimit: big.NewInt(-1), }), @@ -109,7 +112,7 @@ func TestVerifyPrecompileUpgrades(t *testing.T) { name: "invalid initial fee manager config gas limit 0", upgrades: []PrecompileUpgrade{ { - FeeManagerConfig: precompile.NewFeeManagerConfig(big.NewInt(3), admins, nil, + FeeManagerConfig: feemanager.NewFeeManagerConfig(big.NewInt(3), admins, nil, &commontype.FeeConfig{ GasLimit: big.NewInt(0), }), @@ -145,14 +148,14 @@ func TestVerifyPrecompiles(t *testing.T) { { name: "invalid allow list config in tx allowlist", upgrade: PrecompileUpgrade{ - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(3), admins, admins), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(3), admins, admins), }, expectedError: "cannot set address", }, { name: "invalid initial fee manager config", upgrade: PrecompileUpgrade{ - FeeManagerConfig: precompile.NewFeeManagerConfig(big.NewInt(3), admins, nil, + FeeManagerConfig: feemanager.NewFeeManagerConfig(big.NewInt(3), admins, nil, &commontype.FeeConfig{ GasLimit: big.NewInt(-1), }), @@ -183,10 +186,10 @@ func TestVerifyRequiresSortedTimestamps(t *testing.T) { config := &baseConfig config.PrecompileUpgrades = []PrecompileUpgrade{ { - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(2), admins, nil), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(2), admins, nil), }, { - ContractDeployerAllowListConfig: precompile.NewContractDeployerAllowListConfig(big.NewInt(1), admins, nil), + ContractDeployerAllowListConfig: deployerallowlist.NewContractDeployerAllowListConfig(big.NewInt(1), admins, nil), }, } @@ -200,7 +203,7 @@ func TestGetPrecompileConfig(t *testing.T) { baseConfig := *SubnetEVMDefaultChainConfig config := &baseConfig config.PrecompileUpgrade = PrecompileUpgrade{ - ContractDeployerAllowListConfig: precompile.NewContractDeployerAllowListConfig(big.NewInt(10), nil, nil), + ContractDeployerAllowListConfig: deployerallowlist.NewContractDeployerAllowListConfig(big.NewInt(10), nil, nil), } deployerConfig := config.GetPrecompileConfig(precompile.ContractDeployerAllowListAddress, big.NewInt(0)) diff --git a/params/upgrade_config_test.go b/params/upgrade_config_test.go index 38c93f5386..e8e5fb9750 100644 --- a/params/upgrade_config_test.go +++ b/params/upgrade_config_test.go @@ -7,7 +7,8 @@ import ( "math/big" "testing" - "github.com/ava-labs/subnet-evm/precompile" + "github.com/ava-labs/subnet-evm/precompile/deployerallowlist" + "github.com/ava-labs/subnet-evm/precompile/txallowlist" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/assert" ) @@ -15,7 +16,7 @@ import ( func TestVerifyUpgradeConfig(t *testing.T) { admins := []common.Address{{1}} chainConfig := *TestChainConfig - chainConfig.TxAllowListConfig = precompile.NewTxAllowListConfig(big.NewInt(1), admins, nil) + chainConfig.TxAllowListConfig = txallowlist.NewTxAllowListConfig(big.NewInt(1), admins, nil) type test struct { upgrades []PrecompileUpgrade @@ -27,7 +28,7 @@ func TestVerifyUpgradeConfig(t *testing.T) { expectedErrorString: "disable should be [true]", upgrades: []PrecompileUpgrade{ { - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(2), admins, nil), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(2), admins, nil), }, }, }, @@ -35,7 +36,7 @@ func TestVerifyUpgradeConfig(t *testing.T) { expectedErrorString: "config timestamp (0) <= previous timestamp (1)", upgrades: []PrecompileUpgrade{ { - TxAllowListConfig: precompile.NewDisableTxAllowListConfig(big.NewInt(0)), + TxAllowListConfig: txallowlist.NewDisableTxAllowListConfig(big.NewInt(0)), }, }, }, @@ -43,7 +44,7 @@ func TestVerifyUpgradeConfig(t *testing.T) { expectedErrorString: "config timestamp (1) <= previous timestamp (1)", upgrades: []PrecompileUpgrade{ { - TxAllowListConfig: precompile.NewDisableTxAllowListConfig(big.NewInt(1)), + TxAllowListConfig: txallowlist.NewDisableTxAllowListConfig(big.NewInt(1)), }, }, }, @@ -70,8 +71,8 @@ func TestVerifyUpgradeConfig(t *testing.T) { func TestCheckCompatibleUpgradeConfigs(t *testing.T) { admins := []common.Address{{1}} chainConfig := *TestChainConfig - chainConfig.TxAllowListConfig = precompile.NewTxAllowListConfig(big.NewInt(1), admins, nil) - chainConfig.ContractDeployerAllowListConfig = precompile.NewContractDeployerAllowListConfig(big.NewInt(10), admins, nil) + chainConfig.TxAllowListConfig = txallowlist.NewTxAllowListConfig(big.NewInt(1), admins, nil) + chainConfig.ContractDeployerAllowListConfig = deployerallowlist.NewContractDeployerAllowListConfig(big.NewInt(10), admins, nil) type test struct { configs []*UpgradeConfig @@ -86,10 +87,10 @@ func TestCheckCompatibleUpgradeConfigs(t *testing.T) { { PrecompileUpgrades: []PrecompileUpgrade{ { - TxAllowListConfig: precompile.NewDisableTxAllowListConfig(big.NewInt(6)), + TxAllowListConfig: txallowlist.NewDisableTxAllowListConfig(big.NewInt(6)), }, { - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(7), admins, nil), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(7), admins, nil), }, }, }, @@ -101,20 +102,20 @@ func TestCheckCompatibleUpgradeConfigs(t *testing.T) { { PrecompileUpgrades: []PrecompileUpgrade{ { - TxAllowListConfig: precompile.NewDisableTxAllowListConfig(big.NewInt(6)), + TxAllowListConfig: txallowlist.NewDisableTxAllowListConfig(big.NewInt(6)), }, { - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(7), admins, nil), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(7), admins, nil), }, }, }, { PrecompileUpgrades: []PrecompileUpgrade{ { - TxAllowListConfig: precompile.NewDisableTxAllowListConfig(big.NewInt(6)), + TxAllowListConfig: txallowlist.NewDisableTxAllowListConfig(big.NewInt(6)), }, { - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(8), admins, nil), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(8), admins, nil), }, }, }, @@ -127,20 +128,20 @@ func TestCheckCompatibleUpgradeConfigs(t *testing.T) { { PrecompileUpgrades: []PrecompileUpgrade{ { - TxAllowListConfig: precompile.NewDisableTxAllowListConfig(big.NewInt(6)), + TxAllowListConfig: txallowlist.NewDisableTxAllowListConfig(big.NewInt(6)), }, { - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(7), admins, nil), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(7), admins, nil), }, }, }, { PrecompileUpgrades: []PrecompileUpgrade{ { - TxAllowListConfig: precompile.NewDisableTxAllowListConfig(big.NewInt(6)), + TxAllowListConfig: txallowlist.NewDisableTxAllowListConfig(big.NewInt(6)), }, { - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(8), admins, nil), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(8), admins, nil), }, }, }, @@ -152,17 +153,17 @@ func TestCheckCompatibleUpgradeConfigs(t *testing.T) { { PrecompileUpgrades: []PrecompileUpgrade{ { - TxAllowListConfig: precompile.NewDisableTxAllowListConfig(big.NewInt(6)), + TxAllowListConfig: txallowlist.NewDisableTxAllowListConfig(big.NewInt(6)), }, { - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(7), admins, nil), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(7), admins, nil), }, }, }, { PrecompileUpgrades: []PrecompileUpgrade{ { - TxAllowListConfig: precompile.NewDisableTxAllowListConfig(big.NewInt(6)), + TxAllowListConfig: txallowlist.NewDisableTxAllowListConfig(big.NewInt(6)), }, }, }, @@ -175,17 +176,17 @@ func TestCheckCompatibleUpgradeConfigs(t *testing.T) { { PrecompileUpgrades: []PrecompileUpgrade{ { - TxAllowListConfig: precompile.NewDisableTxAllowListConfig(big.NewInt(6)), + TxAllowListConfig: txallowlist.NewDisableTxAllowListConfig(big.NewInt(6)), }, { - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(7), admins, nil), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(7), admins, nil), }, }, }, { PrecompileUpgrades: []PrecompileUpgrade{ { - TxAllowListConfig: precompile.NewDisableTxAllowListConfig(big.NewInt(6)), + TxAllowListConfig: txallowlist.NewDisableTxAllowListConfig(big.NewInt(6)), }, }, }, @@ -198,21 +199,21 @@ func TestCheckCompatibleUpgradeConfigs(t *testing.T) { { PrecompileUpgrades: []PrecompileUpgrade{ { - TxAllowListConfig: precompile.NewDisableTxAllowListConfig(big.NewInt(6)), + TxAllowListConfig: txallowlist.NewDisableTxAllowListConfig(big.NewInt(6)), }, { - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(7), admins, nil), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(7), admins, nil), }, }, }, { PrecompileUpgrades: []PrecompileUpgrade{ { - TxAllowListConfig: precompile.NewDisableTxAllowListConfig(big.NewInt(6)), + TxAllowListConfig: txallowlist.NewDisableTxAllowListConfig(big.NewInt(6)), }, { // uses a different (empty) admin list, not allowed - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(7), []common.Address{}, nil), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(7), []common.Address{}, nil), }, }, }, @@ -224,20 +225,20 @@ func TestCheckCompatibleUpgradeConfigs(t *testing.T) { { PrecompileUpgrades: []PrecompileUpgrade{ { - TxAllowListConfig: precompile.NewDisableTxAllowListConfig(big.NewInt(6)), + TxAllowListConfig: txallowlist.NewDisableTxAllowListConfig(big.NewInt(6)), }, { - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(7), admins, nil), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(7), admins, nil), }, }, }, { PrecompileUpgrades: []PrecompileUpgrade{ { - TxAllowListConfig: precompile.NewDisableTxAllowListConfig(big.NewInt(6)), + TxAllowListConfig: txallowlist.NewDisableTxAllowListConfig(big.NewInt(6)), }, { - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(7), admins, nil), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(7), admins, nil), }, }, }, diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go index baf1a1c322..986c56b38f 100644 --- a/plugin/evm/vm_test.go +++ b/plugin/evm/vm_test.go @@ -20,12 +20,17 @@ import ( "github.com/ava-labs/subnet-evm/commontype" "github.com/ava-labs/subnet-evm/metrics" "github.com/ava-labs/subnet-evm/precompile" + "github.com/ava-labs/subnet-evm/precompile/deployerallowlist" + "github.com/ava-labs/subnet-evm/precompile/feemanager" + "github.com/ava-labs/subnet-evm/precompile/rewardmanager" + "github.com/ava-labs/subnet-evm/precompile/txallowlist" "github.com/ava-labs/subnet-evm/trie" "github.com/ava-labs/subnet-evm/vmerrs" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/ava-labs/avalanchego/api/keystore" @@ -2116,7 +2121,7 @@ func TestBuildAllowListActivationBlock(t *testing.T) { if err := genesis.UnmarshalJSON([]byte(genesisJSONSubnetEVM)); err != nil { t.Fatal(err) } - genesis.Config.ContractDeployerAllowListConfig = precompile.NewContractDeployerAllowListConfig(big.NewInt(time.Now().Unix()), testEthAddrs, nil) + genesis.Config.ContractDeployerAllowListConfig = deployerallowlist.NewContractDeployerAllowListConfig(big.NewInt(time.Now().Unix()), testEthAddrs, nil) genesisJSON, err := genesis.MarshalJSON() if err != nil { @@ -2137,7 +2142,7 @@ func TestBuildAllowListActivationBlock(t *testing.T) { if err != nil { t.Fatal(err) } - role := precompile.GetContractDeployerAllowListStatus(genesisState, testEthAddrs[0]) + role := deployerallowlist.GetContractDeployerAllowListStatus(genesisState, testEthAddrs[0]) if role != precompile.AllowListNoRole { t.Fatalf("Expected allow list status to be set to no role: %s, but found: %s", precompile.AllowListNoRole, role) } @@ -2167,7 +2172,7 @@ func TestBuildAllowListActivationBlock(t *testing.T) { if err != nil { t.Fatal(err) } - role = precompile.GetContractDeployerAllowListStatus(blkState, testEthAddrs[0]) + role = deployerallowlist.GetContractDeployerAllowListStatus(blkState, testEthAddrs[0]) if role != precompile.AllowListAdmin { t.Fatalf("Expected allow list status to be set to Admin: %s, but found: %s", precompile.AllowListAdmin, role) } @@ -2180,7 +2185,7 @@ func TestTxAllowListSuccessfulTx(t *testing.T) { if err := genesis.UnmarshalJSON([]byte(genesisJSONSubnetEVM)); err != nil { t.Fatal(err) } - genesis.Config.TxAllowListConfig = precompile.NewTxAllowListConfig(big.NewInt(0), testEthAddrs[0:1], nil) + genesis.Config.TxAllowListConfig = txallowlist.NewTxAllowListConfig(big.NewInt(0), testEthAddrs[0:1], nil) genesisJSON, err := genesis.MarshalJSON() if err != nil { t.Fatal(err) @@ -2202,11 +2207,11 @@ func TestTxAllowListSuccessfulTx(t *testing.T) { } // Check that address 0 is whitelisted and address 1 is not - role := precompile.GetTxAllowListStatus(genesisState, testEthAddrs[0]) + role := txallowlist.GetTxAllowListStatus(genesisState, testEthAddrs[0]) if role != precompile.AllowListAdmin { t.Fatalf("Expected allow list status to be set to admin: %s, but found: %s", precompile.AllowListAdmin, role) } - role = precompile.GetTxAllowListStatus(genesisState, testEthAddrs[1]) + role = txallowlist.GetTxAllowListStatus(genesisState, testEthAddrs[1]) if role != precompile.AllowListNoRole { t.Fatalf("Expected allow list status to be set to no role: %s, but found: %s", precompile.AllowListNoRole, role) } @@ -2229,7 +2234,7 @@ func TestTxAllowListSuccessfulTx(t *testing.T) { } errs = vm.txPool.AddRemotesSync([]*types.Transaction{signedTx1}) - if err := errs[0]; !errors.Is(err, precompile.ErrSenderAddressNotAllowListed) { + if err := errs[0]; !errors.Is(err, txallowlist.ErrSenderAddressNotAllowListed) { t.Fatalf("expected ErrSenderAddressNotAllowListed, got: %s", err) } @@ -2256,7 +2261,7 @@ func TestTxAllowListDisablePrecompile(t *testing.T) { t.Fatal(err) } enableAllowListTimestamp := time.Unix(0, 0) // enable at genesis - genesis.Config.TxAllowListConfig = precompile.NewTxAllowListConfig(big.NewInt(enableAllowListTimestamp.Unix()), testEthAddrs[0:1], nil) + genesis.Config.TxAllowListConfig = txallowlist.NewTxAllowListConfig(big.NewInt(enableAllowListTimestamp.Unix()), testEthAddrs[0:1], nil) genesisJSON, err := genesis.MarshalJSON() if err != nil { t.Fatal(err) @@ -2297,11 +2302,11 @@ func TestTxAllowListDisablePrecompile(t *testing.T) { } // Check that address 0 is whitelisted and address 1 is not - role := precompile.GetTxAllowListStatus(genesisState, testEthAddrs[0]) + role := txallowlist.GetTxAllowListStatus(genesisState, testEthAddrs[0]) if role != precompile.AllowListAdmin { t.Fatalf("Expected allow list status to be set to admin: %s, but found: %s", precompile.AllowListAdmin, role) } - role = precompile.GetTxAllowListStatus(genesisState, testEthAddrs[1]) + role = txallowlist.GetTxAllowListStatus(genesisState, testEthAddrs[1]) if role != precompile.AllowListNoRole { t.Fatalf("Expected allow list status to be set to no role: %s, but found: %s", precompile.AllowListNoRole, role) } @@ -2324,7 +2329,7 @@ func TestTxAllowListDisablePrecompile(t *testing.T) { } errs = vm.txPool.AddRemotesSync([]*types.Transaction{signedTx1}) - if err := errs[0]; !errors.Is(err, precompile.ErrSenderAddressNotAllowListed) { + if err := errs[0]; !errors.Is(err, txallowlist.ErrSenderAddressNotAllowListed) { t.Fatalf("expected ErrSenderAddressNotAllowListed, got: %s", err) } @@ -2368,7 +2373,7 @@ func TestFeeManagerChangeFee(t *testing.T) { if err := genesis.UnmarshalJSON([]byte(genesisJSONSubnetEVM)); err != nil { t.Fatal(err) } - genesis.Config.FeeManagerConfig = precompile.NewFeeManagerConfig(big.NewInt(0), testEthAddrs[0:1], nil, nil) + genesis.Config.FeeManagerConfig = feemanager.NewFeeManagerConfig(big.NewInt(0), testEthAddrs[0:1], nil, nil) // set a lower fee config now testLowFeeConfig := commontype.FeeConfig{ @@ -2406,11 +2411,11 @@ func TestFeeManagerChangeFee(t *testing.T) { } // Check that address 0 is whitelisted and address 1 is not - role := precompile.GetFeeConfigManagerStatus(genesisState, testEthAddrs[0]) + role := feemanager.GetFeeConfigManagerStatus(genesisState, testEthAddrs[0]) if role != precompile.AllowListAdmin { t.Fatalf("Expected fee manager list status to be set to admin: %s, but found: %s", precompile.FeeConfigManagerAddress, role) } - role = precompile.GetFeeConfigManagerStatus(genesisState, testEthAddrs[1]) + role = feemanager.GetFeeConfigManagerStatus(genesisState, testEthAddrs[1]) if role != precompile.AllowListNoRole { t.Fatalf("Expected fee manager list status to be set to no role: %s, but found: %s", precompile.FeeConfigManagerAddress, role) } @@ -2424,8 +2429,8 @@ func TestFeeManagerChangeFee(t *testing.T) { testHighFeeConfig := testLowFeeConfig testHighFeeConfig.MinBaseFee = big.NewInt(28_000_000_000) - data, err := precompile.PackSetFeeConfig(testHighFeeConfig) - require.NoError(t, err) + data, err := feemanager.PackSetFeeConfig(testHighFeeConfig) + assert.NoError(t, err) tx := types.NewTx(&types.DynamicFeeTx{ ChainID: genesis.Config.ChainID, @@ -2608,7 +2613,7 @@ func TestRewardManagerPrecompileSetRewardAddress(t *testing.T) { genesis := &core.Genesis{} require.NoError(t, genesis.UnmarshalJSON([]byte(genesisJSONSubnetEVM))) - genesis.Config.RewardManagerConfig = precompile.NewRewardManagerConfig(common.Big0, testEthAddrs[0:1], nil, nil) + genesis.Config.RewardManagerConfig = rewardmanager.NewRewardManagerConfig(common.Big0, testEthAddrs[0:1], nil, nil) genesis.Config.AllowFeeRecipients = true // enable this in genesis to test if this is recognized by the reward manager genesisJSON, err := genesis.MarshalJSON() require.NoError(t, err) @@ -2649,10 +2654,10 @@ func TestRewardManagerPrecompileSetRewardAddress(t *testing.T) { vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) testAddr := common.HexToAddress("0x9999991111") - data, err := precompile.PackSetRewardAddress(testAddr) + data, err := rewardmanager.PackSetRewardAddress(testAddr) require.NoError(t, err) - gas := 21000 + 240 + precompile.SetRewardAddressGasCost // 21000 for tx, 240 for tx data + gas := 21000 + 240 + rewardmanager.SetRewardAddressGasCost // 21000 for tx, 240 for tx data tx := types.NewTransaction(uint64(0), precompile.RewardManagerAddress, big.NewInt(1), gas, big.NewInt(testMinGasPrice), data) @@ -2748,7 +2753,7 @@ func TestRewardManagerPrecompileAllowFeeRecipients(t *testing.T) { genesis := &core.Genesis{} require.NoError(t, genesis.UnmarshalJSON([]byte(genesisJSONSubnetEVM))) - genesis.Config.RewardManagerConfig = precompile.NewRewardManagerConfig(common.Big0, testEthAddrs[0:1], nil, nil) + genesis.Config.RewardManagerConfig = rewardmanager.NewRewardManagerConfig(common.Big0, testEthAddrs[0:1], nil, nil) genesis.Config.AllowFeeRecipients = false // disable this in genesis genesisJSON, err := genesis.MarshalJSON() require.NoError(t, err) @@ -2785,10 +2790,10 @@ func TestRewardManagerPrecompileAllowFeeRecipients(t *testing.T) { newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) - data, err := precompile.PackAllowFeeRecipients() + data, err := rewardmanager.PackAllowFeeRecipients() require.NoError(t, err) - gas := 21000 + 240 + precompile.AllowFeeRecipientsGasCost // 21000 for tx, 240 for tx data + gas := 21000 + 240 + rewardmanager.AllowFeeRecipientsGasCost // 21000 for tx, 240 for tx data tx := types.NewTransaction(uint64(0), precompile.RewardManagerAddress, big.NewInt(1), gas, big.NewInt(testMinGasPrice), data) diff --git a/plugin/evm/vm_upgrade_bytes_test.go b/plugin/evm/vm_upgrade_bytes_test.go index 22c78b5800..2adca151fe 100644 --- a/plugin/evm/vm_upgrade_bytes_test.go +++ b/plugin/evm/vm_upgrade_bytes_test.go @@ -18,7 +18,7 @@ import ( "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/metrics" "github.com/ava-labs/subnet-evm/params" - "github.com/ava-labs/subnet-evm/precompile" + "github.com/ava-labs/subnet-evm/precompile/txallowlist" "github.com/stretchr/testify/assert" ) @@ -28,7 +28,7 @@ func TestVMUpgradeBytesPrecompile(t *testing.T) { upgradeConfig := ¶ms.UpgradeConfig{ PrecompileUpgrades: []params.PrecompileUpgrade{ { - TxAllowListConfig: precompile.NewTxAllowListConfig(big.NewInt(enableAllowListTimestamp.Unix()), testEthAddrs[0:1], nil), + TxAllowListConfig: txallowlist.NewTxAllowListConfig(big.NewInt(enableAllowListTimestamp.Unix()), testEthAddrs[0:1], nil), }, }, } @@ -57,7 +57,7 @@ func TestVMUpgradeBytesPrecompile(t *testing.T) { t.Fatal(err) } errs = vm.txPool.AddRemotesSync([]*types.Transaction{signedTx1}) - if err := errs[0]; !errors.Is(err, precompile.ErrSenderAddressNotAllowListed) { + if err := errs[0]; !errors.Is(err, txallowlist.ErrSenderAddressNotAllowListed) { t.Fatalf("expected ErrSenderAddressNotAllowListed, got: %s", err) } @@ -71,7 +71,7 @@ func TestVMUpgradeBytesPrecompile(t *testing.T) { upgradeConfig.PrecompileUpgrades = append( upgradeConfig.PrecompileUpgrades, params.PrecompileUpgrade{ - TxAllowListConfig: precompile.NewDisableTxAllowListConfig(big.NewInt(disableAllowListTimestamp.Unix())), + TxAllowListConfig: txallowlist.NewDisableTxAllowListConfig(big.NewInt(disableAllowListTimestamp.Unix())), }, ) upgradeBytesJSON, err = json.Marshal(upgradeConfig) @@ -108,7 +108,7 @@ func TestVMUpgradeBytesPrecompile(t *testing.T) { // Submit a rejected transaction, should throw an error errs = vm.txPool.AddRemotesSync([]*types.Transaction{signedTx1}) - if err := errs[0]; !errors.Is(err, precompile.ErrSenderAddressNotAllowListed) { + if err := errs[0]; !errors.Is(err, txallowlist.ErrSenderAddressNotAllowListed) { t.Fatalf("expected ErrSenderAddressNotAllowListed, got: %s", err) } diff --git a/precompile/allow_list.go b/precompile/allow_list.go index b4f75becdb..9e1f867a51 100644 --- a/precompile/allow_list.go +++ b/precompile/allow_list.go @@ -18,8 +18,8 @@ const ( SetNoneFuncKey = "setNone" ReadAllowListFuncKey = "readAllowList" - ModifyAllowListGasCost = writeGasCostPerSlot - ReadAllowListGasCost = readGasCostPerSlot + ModifyAllowListGasCost = WriteGasCostPerSlot + ReadAllowListGasCost = ReadGasCostPerSlot ) var ( @@ -48,10 +48,10 @@ type AllowListConfig struct { // the addresses in [AllowListAdmins]. func (c *AllowListConfig) Configure(state StateDB, precompileAddr common.Address) error { for _, enabledAddr := range c.EnabledAddresses { - setAllowListRole(state, precompileAddr, enabledAddr, AllowListEnabled) + SetAllowListRole(state, precompileAddr, enabledAddr, AllowListEnabled) } for _, adminAddr := range c.AllowListAdmins { - setAllowListRole(state, precompileAddr, adminAddr, AllowListAdmin) + SetAllowListRole(state, precompileAddr, adminAddr, AllowListAdmin) } return nil } @@ -112,18 +112,18 @@ func (c *AllowListConfig) Verify() error { return nil } -// getAllowListStatus returns the allow list role of [address] for the precompile +// GetAllowListStatus returns the allow list role of [address] for the precompile // at [precompileAddr] -func getAllowListStatus(state StateDB, precompileAddr common.Address, address common.Address) AllowListRole { +func GetAllowListStatus(state StateDB, precompileAddr common.Address, address common.Address) AllowListRole { // Generate the state key for [address] addressKey := address.Hash() return AllowListRole(state.GetState(precompileAddr, addressKey)) } -// setAllowListRole sets the permissions of [address] to [role] for the precompile +// SetAllowListRole sets the permissions of [address] to [role] for the precompile // at [precompileAddr]. // assumes [role] has already been verified as valid. -func setAllowListRole(stateDB StateDB, precompileAddr, address common.Address, role AllowListRole) { +func SetAllowListRole(stateDB StateDB, precompileAddr, address common.Address, role AllowListRole) { // Generate the state key for [address] addressKey := address.Hash() // Assign [role] to the address @@ -139,7 +139,7 @@ func setAllowListRole(stateDB StateDB, precompileAddr, address common.Address, r // selector that should be encoded in the input. func PackModifyAllowList(address common.Address, role AllowListRole) ([]byte, error) { // function selector (4 bytes) + hash for address - input := make([]byte, 0, selectorLen+common.HashLength) + input := make([]byte, 0, SelectorLen+common.HashLength) switch role { case AllowListAdmin: @@ -158,7 +158,7 @@ func PackModifyAllowList(address common.Address, role AllowListRole) ([]byte, er // PackReadAllowList packs [address] into the input data to the read allow list function func PackReadAllowList(address common.Address) []byte { - input := make([]byte, 0, selectorLen+common.HashLength) + input := make([]byte, 0, SelectorLen+common.HashLength) input = append(input, readAllowListSignature...) input = append(input, address.Hash().Bytes()...) return input @@ -168,7 +168,7 @@ func PackReadAllowList(address common.Address) []byte { // This execution function is speciifc to [precompileAddr]. func createAllowListRoleSetter(precompileAddr common.Address, role AllowListRole) RunStatefulPrecompileFunc { return func(evm PrecompileAccessibleState, callerAddr, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { - if remainingGas, err = deductGas(suppliedGas, ModifyAllowListGasCost); err != nil { + if remainingGas, err = DeductGas(suppliedGas, ModifyAllowListGasCost); err != nil { return nil, 0, err } @@ -185,12 +185,12 @@ func createAllowListRoleSetter(precompileAddr common.Address, role AllowListRole stateDB := evm.GetStateDB() // Verify that the caller is in the allow list and therefore has the right to modify it - callerStatus := getAllowListStatus(stateDB, precompileAddr, callerAddr) + callerStatus := GetAllowListStatus(stateDB, precompileAddr, callerAddr) if !callerStatus.IsAdmin() { return nil, remainingGas, fmt.Errorf("%w: %s", ErrCannotModifyAllowList, callerAddr) } - setAllowListRole(stateDB, precompileAddr, modifyAddress, role) + SetAllowListRole(stateDB, precompileAddr, modifyAddress, role) // Return an empty output and the remaining gas return []byte{}, remainingGas, nil } @@ -201,7 +201,7 @@ func createAllowListRoleSetter(precompileAddr common.Address, role AllowListRole // designated role of that address func createReadAllowList(precompileAddr common.Address) RunStatefulPrecompileFunc { return func(evm PrecompileAccessibleState, callerAddr common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { - if remainingGas, err = deductGas(suppliedGas, ReadAllowListGasCost); err != nil { + if remainingGas, err = DeductGas(suppliedGas, ReadAllowListGasCost); err != nil { return nil, 0, err } @@ -210,16 +210,16 @@ func createReadAllowList(precompileAddr common.Address) RunStatefulPrecompileFun } readAddress := common.BytesToAddress(input) - role := getAllowListStatus(evm.GetStateDB(), precompileAddr, readAddress) + role := GetAllowListStatus(evm.GetStateDB(), precompileAddr, readAddress) roleBytes := common.Hash(role).Bytes() return roleBytes, remainingGas, nil } } // createAllowListPrecompile returns a StatefulPrecompiledContract with R/W control of an allow list at [precompileAddr] -func createAllowListPrecompile(precompileAddr common.Address) StatefulPrecompiledContract { +func CreateAllowListPrecompile(precompileAddr common.Address) StatefulPrecompiledContract { // Construct the contract with no fallback function. - allowListFuncs := createAllowListFunctions(precompileAddr) + allowListFuncs := CreateAllowListFunctions(precompileAddr) contract, err := NewStatefulPrecompileContract(nil, allowListFuncs) // TODO Change this to be returned as an error after refactoring this precompile // to use the new precompile template. @@ -229,11 +229,11 @@ func createAllowListPrecompile(precompileAddr common.Address) StatefulPrecompile return contract } -func createAllowListFunctions(precompileAddr common.Address) []*statefulPrecompileFunction { - setAdmin := newStatefulPrecompileFunction(setAdminSignature, createAllowListRoleSetter(precompileAddr, AllowListAdmin)) - setEnabled := newStatefulPrecompileFunction(setEnabledSignature, createAllowListRoleSetter(precompileAddr, AllowListEnabled)) - setNone := newStatefulPrecompileFunction(setNoneSignature, createAllowListRoleSetter(precompileAddr, AllowListNoRole)) - read := newStatefulPrecompileFunction(readAllowListSignature, createReadAllowList(precompileAddr)) +func CreateAllowListFunctions(precompileAddr common.Address) []*StatefulPrecompileFunction { + setAdmin := NewStatefulPrecompileFunction(setAdminSignature, createAllowListRoleSetter(precompileAddr, AllowListAdmin)) + setEnabled := NewStatefulPrecompileFunction(setEnabledSignature, createAllowListRoleSetter(precompileAddr, AllowListEnabled)) + setNone := NewStatefulPrecompileFunction(setNoneSignature, createAllowListRoleSetter(precompileAddr, AllowListNoRole)) + read := NewStatefulPrecompileFunction(readAllowListSignature, createReadAllowList(precompileAddr)) - return []*statefulPrecompileFunction{setAdmin, setEnabled, setNone, read} + return []*StatefulPrecompileFunction{setAdmin, setEnabled, setNone, read} } diff --git a/precompile/config_test.go b/precompile/config_test.go deleted file mode 100644 index a396cec543..0000000000 --- a/precompile/config_test.go +++ /dev/null @@ -1,462 +0,0 @@ -// (c) 2022 Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package precompile - -import ( - "math/big" - "testing" - - "github.com/ava-labs/subnet-evm/commontype" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" - "github.com/stretchr/testify/require" -) - -var validFeeConfig = commontype.FeeConfig{ - GasLimit: big.NewInt(8_000_000), - TargetBlockRate: 2, // in seconds - - MinBaseFee: big.NewInt(25_000_000_000), - TargetGas: big.NewInt(15_000_000), - BaseFeeChangeDenominator: big.NewInt(36), - - MinBlockGasCost: big.NewInt(0), - MaxBlockGasCost: big.NewInt(1_000_000), - BlockGasCostStep: big.NewInt(200_000), -} - -func TestVerifyPrecompileUpgrades(t *testing.T) { - admins := []common.Address{{1}} - enableds := []common.Address{{2}} - tests := []struct { - name string - config StatefulPrecompileConfig - expectedError string - }{ - { - name: "invalid allow list config in tx allowlist", - config: NewTxAllowListConfig(big.NewInt(3), admins, admins), - expectedError: "cannot set address", - }, - { - name: "nil member allow list config in tx allowlist", - config: NewTxAllowListConfig(big.NewInt(3), nil, nil), - expectedError: "", - }, - { - name: "empty member allow list config in tx allowlist", - config: NewTxAllowListConfig(big.NewInt(3), []common.Address{}, []common.Address{}), - expectedError: "", - }, - { - name: "valid allow list config in tx allowlist", - config: NewTxAllowListConfig(big.NewInt(3), admins, enableds), - expectedError: "", - }, - { - name: "invalid allow list config in deployer allowlist", - config: NewContractDeployerAllowListConfig(big.NewInt(3), admins, admins), - expectedError: "cannot set address", - }, - { - name: "invalid allow list config in native minter allowlist", - config: NewContractNativeMinterConfig(big.NewInt(3), admins, admins, nil), - expectedError: "cannot set address", - }, - { - name: "duplicate admins in config in native minter allowlist", - config: NewContractNativeMinterConfig(big.NewInt(3), append(admins, admins[0]), enableds, nil), - expectedError: "duplicate address", - }, - { - name: "duplicate enableds in config in native minter allowlist", - config: NewContractNativeMinterConfig(big.NewInt(3), admins, append(enableds, enableds[0]), nil), - expectedError: "duplicate address", - }, - { - name: "invalid allow list config in fee manager allowlist", - config: NewFeeManagerConfig(big.NewInt(3), admins, admins, nil), - expectedError: "cannot set address", - }, - { - name: "invalid initial fee manager config", - config: NewFeeManagerConfig(big.NewInt(3), admins, nil, - &commontype.FeeConfig{ - GasLimit: big.NewInt(0), - }), - expectedError: "gasLimit = 0 cannot be less than or equal to 0", - }, - { - name: "nil amount in native minter config", - config: NewContractNativeMinterConfig(big.NewInt(3), admins, nil, - map[common.Address]*math.HexOrDecimal256{ - common.HexToAddress("0x01"): math.NewHexOrDecimal256(123), - common.HexToAddress("0x02"): nil, - }), - expectedError: "initial mint cannot contain nil", - }, - { - name: "negative amount in native minter config", - config: NewContractNativeMinterConfig(big.NewInt(3), admins, nil, - map[common.Address]*math.HexOrDecimal256{ - common.HexToAddress("0x01"): math.NewHexOrDecimal256(123), - common.HexToAddress("0x02"): math.NewHexOrDecimal256(-1), - }), - expectedError: "initial mint cannot contain invalid amount", - }, - { - name: "duplicate enableds in config in reward manager allowlist", - config: NewRewardManagerConfig(big.NewInt(3), admins, append(enableds, enableds[0]), nil), - expectedError: "duplicate address", - }, - { - name: "both reward mechanisms should not be activated at the same time in reward manager", - config: NewRewardManagerConfig(big.NewInt(3), admins, enableds, &InitialRewardConfig{ - AllowFeeRecipients: true, - RewardAddress: common.HexToAddress("0x01"), - }), - expectedError: ErrCannotEnableBothRewards.Error(), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - require := require.New(t) - - err := tt.config.Verify() - if tt.expectedError == "" { - require.NoError(err) - } else { - require.ErrorContains(err, tt.expectedError) - } - }) - } -} - -func TestEqualTxAllowListConfig(t *testing.T) { - admins := []common.Address{{1}} - enableds := []common.Address{{2}} - tests := []struct { - name string - config StatefulPrecompileConfig - other StatefulPrecompileConfig - expected bool - }{ - { - name: "non-nil config and nil other", - config: NewTxAllowListConfig(big.NewInt(3), admins, enableds), - other: nil, - expected: false, - }, - { - name: "different type", - config: NewTxAllowListConfig(big.NewInt(3), admins, enableds), - other: NewContractDeployerAllowListConfig(big.NewInt(3), admins, enableds), - expected: false, - }, - { - name: "different admin", - config: NewTxAllowListConfig(big.NewInt(3), admins, enableds), - other: NewTxAllowListConfig(big.NewInt(3), []common.Address{{3}}, enableds), - expected: false, - }, - { - name: "different enabled", - config: NewTxAllowListConfig(big.NewInt(3), admins, enableds), - other: NewTxAllowListConfig(big.NewInt(3), admins, []common.Address{{3}}), - expected: false, - }, - { - name: "different timestamp", - config: NewTxAllowListConfig(big.NewInt(3), admins, enableds), - other: NewTxAllowListConfig(big.NewInt(4), admins, enableds), - expected: false, - }, - { - name: "same config", - config: NewTxAllowListConfig(big.NewInt(3), admins, enableds), - other: NewTxAllowListConfig(big.NewInt(3), admins, enableds), - expected: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - require := require.New(t) - - require.Equal(tt.expected, tt.config.Equal(tt.other)) - }) - } -} - -func TestEqualContractDeployerAllowListConfig(t *testing.T) { - admins := []common.Address{{1}} - enableds := []common.Address{{2}} - tests := []struct { - name string - config StatefulPrecompileConfig - other StatefulPrecompileConfig - expected bool - }{ - { - name: "non-nil config and nil other", - config: NewContractDeployerAllowListConfig(big.NewInt(3), admins, enableds), - other: nil, - expected: false, - }, - { - name: "different type", - config: NewContractDeployerAllowListConfig(big.NewInt(3), admins, enableds), - other: NewTxAllowListConfig(big.NewInt(3), admins, enableds), - expected: false, - }, - { - name: "different admin", - config: NewContractDeployerAllowListConfig(big.NewInt(3), admins, enableds), - other: NewContractDeployerAllowListConfig(big.NewInt(3), []common.Address{{3}}, enableds), - expected: false, - }, - { - name: "different enabled", - config: NewContractDeployerAllowListConfig(big.NewInt(3), admins, enableds), - other: NewContractDeployerAllowListConfig(big.NewInt(3), admins, []common.Address{{3}}), - expected: false, - }, - { - name: "different timestamp", - config: NewContractDeployerAllowListConfig(big.NewInt(3), admins, enableds), - other: NewContractDeployerAllowListConfig(big.NewInt(4), admins, enableds), - expected: false, - }, - { - name: "same config", - config: NewContractDeployerAllowListConfig(big.NewInt(3), admins, enableds), - other: NewContractDeployerAllowListConfig(big.NewInt(3), admins, enableds), - expected: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - require := require.New(t) - - require.Equal(tt.expected, tt.config.Equal(tt.other)) - }) - } -} - -func TestEqualContractNativeMinterConfig(t *testing.T) { - admins := []common.Address{{1}} - enableds := []common.Address{{2}} - tests := []struct { - name string - config StatefulPrecompileConfig - other StatefulPrecompileConfig - expected bool - }{ - { - name: "non-nil config and nil other", - config: NewContractNativeMinterConfig(big.NewInt(3), admins, enableds, nil), - other: nil, - expected: false, - }, - { - name: "different type", - config: NewContractNativeMinterConfig(big.NewInt(3), admins, enableds, nil), - other: NewTxAllowListConfig(big.NewInt(3), []common.Address{{1}}, []common.Address{{2}}), - expected: false, - }, - { - name: "different timestamps", - config: NewContractNativeMinterConfig(big.NewInt(3), admins, nil, nil), - other: NewContractNativeMinterConfig(big.NewInt(4), admins, nil, nil), - expected: false, - }, - { - name: "different enabled", - config: NewContractNativeMinterConfig(big.NewInt(3), admins, nil, nil), - other: NewContractNativeMinterConfig(big.NewInt(3), admins, enableds, nil), - expected: false, - }, - { - name: "different initial mint amounts", - config: NewContractNativeMinterConfig(big.NewInt(3), admins, nil, - map[common.Address]*math.HexOrDecimal256{ - common.HexToAddress("0x01"): math.NewHexOrDecimal256(1), - }), - other: NewContractNativeMinterConfig(big.NewInt(3), admins, nil, - map[common.Address]*math.HexOrDecimal256{ - common.HexToAddress("0x01"): math.NewHexOrDecimal256(2), - }), - expected: false, - }, - { - name: "different initial mint addresses", - config: NewContractNativeMinterConfig(big.NewInt(3), admins, nil, - map[common.Address]*math.HexOrDecimal256{ - common.HexToAddress("0x01"): math.NewHexOrDecimal256(1), - }), - other: NewContractNativeMinterConfig(big.NewInt(3), admins, nil, - map[common.Address]*math.HexOrDecimal256{ - common.HexToAddress("0x02"): math.NewHexOrDecimal256(1), - }), - expected: false, - }, - - { - name: "same config", - config: NewContractNativeMinterConfig(big.NewInt(3), admins, nil, - map[common.Address]*math.HexOrDecimal256{ - common.HexToAddress("0x01"): math.NewHexOrDecimal256(1), - }), - other: NewContractNativeMinterConfig(big.NewInt(3), admins, nil, - map[common.Address]*math.HexOrDecimal256{ - common.HexToAddress("0x01"): math.NewHexOrDecimal256(1), - }), - expected: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - require := require.New(t) - - require.Equal(tt.expected, tt.config.Equal(tt.other)) - }) - } -} - -func TestEqualFeeConfigManagerConfig(t *testing.T) { - admins := []common.Address{{1}} - enableds := []common.Address{{2}} - tests := []struct { - name string - config StatefulPrecompileConfig - other StatefulPrecompileConfig - expected bool - }{ - { - name: "non-nil config and nil other", - config: NewFeeManagerConfig(big.NewInt(3), admins, enableds, nil), - other: nil, - expected: false, - }, - { - name: "different type", - config: NewFeeManagerConfig(big.NewInt(3), admins, enableds, nil), - other: NewTxAllowListConfig(big.NewInt(3), []common.Address{{1}}, []common.Address{{2}}), - expected: false, - }, - { - name: "different timestamp", - config: NewFeeManagerConfig(big.NewInt(3), admins, nil, nil), - other: NewFeeManagerConfig(big.NewInt(4), admins, nil, nil), - expected: false, - }, - { - name: "different enabled", - config: NewFeeManagerConfig(big.NewInt(3), admins, nil, nil), - other: NewFeeManagerConfig(big.NewInt(3), admins, enableds, nil), - expected: false, - }, - { - name: "non-nil initial config and nil initial config", - config: NewFeeManagerConfig(big.NewInt(3), admins, nil, &validFeeConfig), - other: NewFeeManagerConfig(big.NewInt(3), admins, nil, nil), - expected: false, - }, - { - name: "different initial config", - config: NewFeeManagerConfig(big.NewInt(3), admins, nil, &validFeeConfig), - other: NewFeeManagerConfig(big.NewInt(3), admins, nil, - func() *commontype.FeeConfig { - c := validFeeConfig - c.GasLimit = big.NewInt(123) - return &c - }()), - expected: false, - }, - { - name: "same config", - config: NewFeeManagerConfig(big.NewInt(3), admins, nil, &validFeeConfig), - other: NewFeeManagerConfig(big.NewInt(3), admins, nil, &validFeeConfig), - expected: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - require := require.New(t) - - require.Equal(tt.expected, tt.config.Equal(tt.other)) - }) - } -} - -func TestEqualRewardManagerConfig(t *testing.T) { - admins := []common.Address{{1}} - enableds := []common.Address{{2}} - tests := []struct { - name string - config StatefulPrecompileConfig - other StatefulPrecompileConfig - expected bool - }{ - { - name: "non-nil config and nil other", - config: NewRewardManagerConfig(big.NewInt(3), admins, enableds, nil), - other: nil, - expected: false, - }, - { - name: "different type", - config: NewRewardManagerConfig(big.NewInt(3), admins, enableds, nil), - other: NewTxAllowListConfig(big.NewInt(3), []common.Address{{1}}, []common.Address{{2}}), - expected: false, - }, - { - name: "different timestamp", - config: NewRewardManagerConfig(big.NewInt(3), admins, nil, nil), - other: NewRewardManagerConfig(big.NewInt(4), admins, nil, nil), - expected: false, - }, - { - name: "different enabled", - config: NewRewardManagerConfig(big.NewInt(3), admins, nil, nil), - other: NewRewardManagerConfig(big.NewInt(3), admins, enableds, nil), - expected: false, - }, - { - name: "non-nil initial config and nil initial config", - config: NewRewardManagerConfig(big.NewInt(3), admins, nil, &InitialRewardConfig{ - AllowFeeRecipients: true, - }), - other: NewRewardManagerConfig(big.NewInt(3), admins, nil, nil), - expected: false, - }, - { - name: "different initial config", - config: NewRewardManagerConfig(big.NewInt(3), admins, nil, &InitialRewardConfig{ - RewardAddress: common.HexToAddress("0x01"), - }), - other: NewRewardManagerConfig(big.NewInt(3), admins, nil, - &InitialRewardConfig{ - RewardAddress: common.HexToAddress("0x02"), - }), - expected: false, - }, - { - name: "same config", - config: NewRewardManagerConfig(big.NewInt(3), admins, nil, &InitialRewardConfig{ - RewardAddress: common.HexToAddress("0x01"), - }), - other: NewRewardManagerConfig(big.NewInt(3), admins, nil, &InitialRewardConfig{ - RewardAddress: common.HexToAddress("0x01"), - }), - expected: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - require := require.New(t) - - require.Equal(tt.expected, tt.config.Equal(tt.other)) - }) - } -} diff --git a/precompile/contract.go b/precompile/contract.go index 69ed968641..596ea6dc00 100644 --- a/precompile/contract.go +++ b/precompile/contract.go @@ -5,76 +5,24 @@ package precompile import ( "fmt" - "math/big" - "github.com/ava-labs/avalanchego/snow" - "github.com/ava-labs/subnet-evm/commontype" "github.com/ethereum/go-ethereum/common" ) const ( - selectorLen = 4 + SelectorLen = 4 ) type RunStatefulPrecompileFunc func(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) -// PrecompileAccessibleState defines the interface exposed to stateful precompile contracts -type PrecompileAccessibleState interface { - GetStateDB() StateDB - GetBlockContext() BlockContext - GetSnowContext() *snow.Context - CallFromPrecompile(caller common.Address, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) -} - -// BlockContext defines an interface that provides information to a stateful precompile -// about the block that activates the upgrade. The precompile can access this information -// to initialize its state. -type BlockContext interface { - Number() *big.Int - Timestamp() *big.Int -} - -// ChainContext defines an interface that provides information to a stateful precompile -// about the chain configuration. The precompile can access this information to initialize -// its state. -type ChainConfig interface { - // GetFeeConfig returns the original FeeConfig that was set in the genesis. - GetFeeConfig() commontype.FeeConfig - // AllowedFeeRecipients returns true if fee recipients are allowed in the genesis. - AllowedFeeRecipients() bool -} - -// StateDB is the interface for accessing EVM state -type StateDB interface { - GetState(common.Address, common.Hash) common.Hash - SetState(common.Address, common.Hash, common.Hash) - - SetCode(common.Address, []byte) - - SetNonce(common.Address, uint64) - GetNonce(common.Address) uint64 - - GetBalance(common.Address) *big.Int - AddBalance(common.Address, *big.Int) - SubBalance(common.Address, *big.Int) - - CreateAccount(common.Address) - Exist(common.Address) bool - - AddLog(addr common.Address, topics []common.Hash, data []byte, blockNumber uint64) - - Suicide(common.Address) bool - Finalise(deleteEmptyObjects bool) -} - // StatefulPrecompiledContract is the interface for executing a precompiled contract type StatefulPrecompiledContract interface { // Run executes the precompiled contract. Run(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) } -// statefulPrecompileFunction defines a function implemented by a stateful precompile -type statefulPrecompileFunction struct { +// StatefulPrecompileFunction defines a function implemented by a stateful precompile +type StatefulPrecompileFunction struct { // selector is the 4 byte function selector for this function // This should be calculated from the function signature using CalculateFunctionSelector selector []byte @@ -82,9 +30,9 @@ type statefulPrecompileFunction struct { execute RunStatefulPrecompileFunc } -// newStatefulPrecompileFunction creates a stateful precompile function with the given arguments -func newStatefulPrecompileFunction(selector []byte, execute RunStatefulPrecompileFunc) *statefulPrecompileFunction { - return &statefulPrecompileFunction{ +// NewStatefulPrecompileFunction creates a stateful precompile function with the given arguments +func NewStatefulPrecompileFunction(selector []byte, execute RunStatefulPrecompileFunc) *StatefulPrecompileFunction { + return &StatefulPrecompileFunction{ selector: selector, execute: execute, } @@ -95,16 +43,16 @@ func newStatefulPrecompileFunction(selector []byte, execute RunStatefulPrecompil // Note: because we only ever read from [functions] there no lock is required to make it thread-safe. type statefulPrecompileWithFunctionSelectors struct { fallback RunStatefulPrecompileFunc - functions map[string]*statefulPrecompileFunction + functions map[string]*StatefulPrecompileFunction } // NewStatefulPrecompileContract generates new StatefulPrecompile using [functions] as the available functions and [fallback] // as an optional fallback if there is no input data. Note: the selector of [fallback] will be ignored, so it is required to be left empty. -func NewStatefulPrecompileContract(fallback RunStatefulPrecompileFunc, functions []*statefulPrecompileFunction) (StatefulPrecompiledContract, error) { +func NewStatefulPrecompileContract(fallback RunStatefulPrecompileFunc, functions []*StatefulPrecompileFunction) (StatefulPrecompiledContract, error) { // Construct the contract and populate [functions]. contract := &statefulPrecompileWithFunctionSelectors{ fallback: fallback, - functions: make(map[string]*statefulPrecompileFunction), + functions: make(map[string]*StatefulPrecompileFunction), } for _, function := range functions { _, exists := contract.functions[string(function.selector)] @@ -126,13 +74,13 @@ func (s *statefulPrecompileWithFunctionSelectors) Run(accessibleState Precompile } // Otherwise, an unexpected input size will result in an error. - if len(input) < selectorLen { + if len(input) < SelectorLen { return nil, suppliedGas, fmt.Errorf("missing function selector to precompile - input length (%d)", len(input)) } // Use the function selector to grab the correct function - selector := input[:selectorLen] - functionInput := input[selectorLen:] + selector := input[:SelectorLen] + functionInput := input[SelectorLen:] function, ok := s.functions[string(selector)] if !ok { return nil, suppliedGas, fmt.Errorf("invalid function selector %#x", selector) diff --git a/precompile/contract_native_minter.go b/precompile/contract_native_minter.go deleted file mode 100644 index 26ccaf8e68..0000000000 --- a/precompile/contract_native_minter.go +++ /dev/null @@ -1,227 +0,0 @@ -// (c) 2019-2020, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package precompile - -import ( - "encoding/json" - "errors" - "fmt" - "math/big" - - "github.com/ava-labs/subnet-evm/utils" - "github.com/ava-labs/subnet-evm/vmerrs" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" -) - -const ( - mintInputAddressSlot = iota - mintInputAmountSlot - - mintInputLen = common.HashLength + common.HashLength - - MintGasCost = 30_000 -) - -var ( - _ StatefulPrecompileConfig = &ContractNativeMinterConfig{} - // Singleton StatefulPrecompiledContract for minting native assets by permissioned callers. - ContractNativeMinterPrecompile StatefulPrecompiledContract = createNativeMinterPrecompile(ContractNativeMinterAddress) - - mintSignature = CalculateFunctionSelector("mintNativeCoin(address,uint256)") // address, amount - ErrCannotMint = errors.New("non-enabled cannot mint") -) - -// ContractNativeMinterConfig wraps [AllowListConfig] and uses it to implement the StatefulPrecompileConfig -// interface while adding in the ContractNativeMinter specific precompile address. -type ContractNativeMinterConfig struct { - AllowListConfig - UpgradeableConfig - InitialMint map[common.Address]*math.HexOrDecimal256 `json:"initialMint,omitempty"` // initial mint config to be immediately minted -} - -// NewContractNativeMinterConfig returns a config for a network upgrade at [blockTimestamp] that enables -// ContractNativeMinter with the given [admins] and [enableds] as members of the allowlist. Also mints balances according to [initialMint] when the upgrade activates. -func NewContractNativeMinterConfig(blockTimestamp *big.Int, admins []common.Address, enableds []common.Address, initialMint map[common.Address]*math.HexOrDecimal256) *ContractNativeMinterConfig { - return &ContractNativeMinterConfig{ - AllowListConfig: AllowListConfig{ - AllowListAdmins: admins, - EnabledAddresses: enableds, - }, - UpgradeableConfig: UpgradeableConfig{BlockTimestamp: blockTimestamp}, - InitialMint: initialMint, - } -} - -// NewDisableContractNativeMinterConfig returns config for a network upgrade at [blockTimestamp] -// that disables ContractNativeMinter. -func NewDisableContractNativeMinterConfig(blockTimestamp *big.Int) *ContractNativeMinterConfig { - return &ContractNativeMinterConfig{ - UpgradeableConfig: UpgradeableConfig{ - BlockTimestamp: blockTimestamp, - Disable: true, - }, - } -} - -// Address returns the address of the native minter contract. -func (c *ContractNativeMinterConfig) Address() common.Address { - return ContractNativeMinterAddress -} - -// Configure configures [state] with the desired admins based on [c]. -func (c *ContractNativeMinterConfig) Configure(_ ChainConfig, state StateDB, _ BlockContext) error { - for to, amount := range c.InitialMint { - if amount != nil { - bigIntAmount := (*big.Int)(amount) - state.AddBalance(to, bigIntAmount) - } - } - - return c.AllowListConfig.Configure(state, ContractNativeMinterAddress) -} - -// Contract returns the singleton stateful precompiled contract to be used for the native minter. -func (c *ContractNativeMinterConfig) Contract() StatefulPrecompiledContract { - return ContractNativeMinterPrecompile -} - -func (c *ContractNativeMinterConfig) Verify() error { - if err := c.AllowListConfig.Verify(); err != nil { - return err - } - // ensure that all of the initial mint values in the map are non-nil positive values - for addr, amount := range c.InitialMint { - if amount == nil { - return fmt.Errorf("initial mint cannot contain nil amount for address %s", addr) - } - bigIntAmount := (*big.Int)(amount) - if bigIntAmount.Sign() < 1 { - return fmt.Errorf("initial mint cannot contain invalid amount %v for address %s", bigIntAmount, addr) - } - } - return nil -} - -// Equal returns true if [s] is a [*ContractNativeMinterConfig] and it has been configured identical to [c]. -func (c *ContractNativeMinterConfig) Equal(s StatefulPrecompileConfig) bool { - // typecast before comparison - other, ok := (s).(*ContractNativeMinterConfig) - if !ok { - return false - } - eq := c.UpgradeableConfig.Equal(&other.UpgradeableConfig) && c.AllowListConfig.Equal(&other.AllowListConfig) - if !eq { - return false - } - - if len(c.InitialMint) != len(other.InitialMint) { - return false - } - - for address, amount := range c.InitialMint { - val, ok := other.InitialMint[address] - if !ok { - return false - } - bigIntAmount := (*big.Int)(amount) - bigIntVal := (*big.Int)(val) - if !utils.BigNumEqual(bigIntAmount, bigIntVal) { - return false - } - } - - return true -} - -// String returns a string representation of the ContractNativeMinterConfig. -func (c *ContractNativeMinterConfig) String() string { - bytes, _ := json.Marshal(c) - return string(bytes) -} - -// GetContractNativeMinterStatus returns the role of [address] for the minter list. -func GetContractNativeMinterStatus(stateDB StateDB, address common.Address) AllowListRole { - return getAllowListStatus(stateDB, ContractNativeMinterAddress, address) -} - -// SetContractNativeMinterStatus sets the permissions of [address] to [role] for the -// minter list. assumes [role] has already been verified as valid. -func SetContractNativeMinterStatus(stateDB StateDB, address common.Address, role AllowListRole) { - setAllowListRole(stateDB, ContractNativeMinterAddress, address, role) -} - -// PackMintInput packs [address] and [amount] into the appropriate arguments for minting operation. -// Assumes that [amount] can be represented by 32 bytes. -func PackMintInput(address common.Address, amount *big.Int) ([]byte, error) { - // function selector (4 bytes) + input(hash for address + hash for amount) - res := make([]byte, selectorLen+mintInputLen) - err := packOrderedHashesWithSelector(res, mintSignature, []common.Hash{ - address.Hash(), - common.BigToHash(amount), - }) - - return res, err -} - -// UnpackMintInput attempts to unpack [input] into the arguments to the mint precompile -// assumes that [input] does not include selector (omits first 4 bytes in PackMintInput) -func UnpackMintInput(input []byte) (common.Address, *big.Int, error) { - if len(input) != mintInputLen { - return common.Address{}, nil, fmt.Errorf("invalid input length for minting: %d", len(input)) - } - to := common.BytesToAddress(returnPackedHash(input, mintInputAddressSlot)) - assetAmount := new(big.Int).SetBytes(returnPackedHash(input, mintInputAmountSlot)) - return to, assetAmount, nil -} - -// mintNativeCoin checks if the caller is permissioned for minting operation. -// The execution function parses the [input] into native coin amount and receiver address. -func mintNativeCoin(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { - if remainingGas, err = deductGas(suppliedGas, MintGasCost); err != nil { - return nil, 0, err - } - - if readOnly { - return nil, remainingGas, vmerrs.ErrWriteProtection - } - - to, amount, err := UnpackMintInput(input) - if err != nil { - return nil, remainingGas, err - } - - stateDB := accessibleState.GetStateDB() - // Verify that the caller is in the allow list and therefore has the right to modify it - callerStatus := getAllowListStatus(stateDB, ContractNativeMinterAddress, caller) - if !callerStatus.IsEnabled() { - return nil, remainingGas, fmt.Errorf("%w: %s", ErrCannotMint, caller) - } - - // if there is no address in the state, create one. - if !stateDB.Exist(to) { - stateDB.CreateAccount(to) - } - - stateDB.AddBalance(to, amount) - // Return an empty output and the remaining gas - return []byte{}, remainingGas, nil -} - -// createNativeMinterPrecompile returns a StatefulPrecompiledContract with R/W control of an allow list at [precompileAddr] and a native coin minter. -func createNativeMinterPrecompile(precompileAddr common.Address) StatefulPrecompiledContract { - enabledFuncs := createAllowListFunctions(precompileAddr) - - mintFunc := newStatefulPrecompileFunction(mintSignature, mintNativeCoin) - - enabledFuncs = append(enabledFuncs, mintFunc) - // Construct the contract with no fallback function. - contract, err := NewStatefulPrecompileContract(nil, enabledFuncs) - // Change this to be returned as an error after refactoring this precompile - // to use the new precompile template. - if err != nil { - panic(err) - } - return contract -} diff --git a/precompile/contract_deployer_allow_list.go b/precompile/deployerallowlist/config.go similarity index 58% rename from precompile/contract_deployer_allow_list.go rename to precompile/deployerallowlist/config.go index 11df6ff3fe..34ce2ded8a 100644 --- a/precompile/contract_deployer_allow_list.go +++ b/precompile/deployerallowlist/config.go @@ -1,37 +1,34 @@ // (c) 2019-2020, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package precompile +package deployerallowlist import ( "encoding/json" "math/big" + "github.com/ava-labs/subnet-evm/precompile" "github.com/ethereum/go-ethereum/common" ) -var ( - _ StatefulPrecompileConfig = &ContractDeployerAllowListConfig{} - // Singleton StatefulPrecompiledContract for W/R access to the contract deployer allow list. - ContractDeployerAllowListPrecompile StatefulPrecompiledContract = createAllowListPrecompile(ContractDeployerAllowListAddress) -) +var _ precompile.StatefulPrecompileConfig = &ContractDeployerAllowListConfig{} // ContractDeployerAllowListConfig wraps [AllowListConfig] and uses it to implement the StatefulPrecompileConfig // interface while adding in the contract deployer specific precompile address. type ContractDeployerAllowListConfig struct { - AllowListConfig - UpgradeableConfig + precompile.AllowListConfig + precompile.UpgradeableConfig } // NewContractDeployerAllowListConfig returns a config for a network upgrade at [blockTimestamp] that enables // ContractDeployerAllowList with [admins] and [enableds] as members of the allowlist. func NewContractDeployerAllowListConfig(blockTimestamp *big.Int, admins []common.Address, enableds []common.Address) *ContractDeployerAllowListConfig { return &ContractDeployerAllowListConfig{ - AllowListConfig: AllowListConfig{ + AllowListConfig: precompile.AllowListConfig{ AllowListAdmins: admins, EnabledAddresses: enableds, }, - UpgradeableConfig: UpgradeableConfig{BlockTimestamp: blockTimestamp}, + UpgradeableConfig: precompile.UpgradeableConfig{BlockTimestamp: blockTimestamp}, } } @@ -39,7 +36,7 @@ func NewContractDeployerAllowListConfig(blockTimestamp *big.Int, admins []common // that disables ContractDeployerAllowList. func NewDisableContractDeployerAllowListConfig(blockTimestamp *big.Int) *ContractDeployerAllowListConfig { return &ContractDeployerAllowListConfig{ - UpgradeableConfig: UpgradeableConfig{ + UpgradeableConfig: precompile.UpgradeableConfig{ BlockTimestamp: blockTimestamp, Disable: true, }, @@ -48,21 +45,21 @@ func NewDisableContractDeployerAllowListConfig(blockTimestamp *big.Int) *Contrac // Address returns the address of the contract deployer allow list. func (c *ContractDeployerAllowListConfig) Address() common.Address { - return ContractDeployerAllowListAddress + return precompile.ContractDeployerAllowListAddress } // Configure configures [state] with the desired admins based on [c]. -func (c *ContractDeployerAllowListConfig) Configure(_ ChainConfig, state StateDB, _ BlockContext) error { - return c.AllowListConfig.Configure(state, ContractDeployerAllowListAddress) +func (c *ContractDeployerAllowListConfig) Configure(_ precompile.ChainConfig, state precompile.StateDB, _ precompile.BlockContext) error { + return c.AllowListConfig.Configure(state, precompile.ContractDeployerAllowListAddress) } // Contract returns the singleton stateful precompiled contract to be used for the allow list. -func (c *ContractDeployerAllowListConfig) Contract() StatefulPrecompiledContract { +func (c *ContractDeployerAllowListConfig) Contract() precompile.StatefulPrecompiledContract { return ContractDeployerAllowListPrecompile } // Equal returns true if [s] is a [*ContractDeployerAllowListConfig] and it has been configured identical to [c]. -func (c *ContractDeployerAllowListConfig) Equal(s StatefulPrecompileConfig) bool { +func (c *ContractDeployerAllowListConfig) Equal(s precompile.StatefulPrecompileConfig) bool { // typecast before comparison other, ok := (s).(*ContractDeployerAllowListConfig) if !ok { @@ -76,16 +73,3 @@ func (c *ContractDeployerAllowListConfig) String() string { bytes, _ := json.Marshal(c) return string(bytes) } - -// GetContractDeployerAllowListStatus returns the role of [address] for the contract deployer -// allow list. -func GetContractDeployerAllowListStatus(stateDB StateDB, address common.Address) AllowListRole { - return getAllowListStatus(stateDB, ContractDeployerAllowListAddress, address) -} - -// SetContractDeployerAllowListStatus sets the permissions of [address] to [role] for the -// contract deployer allow list. -// assumes [role] has already been verified as valid. -func SetContractDeployerAllowListStatus(stateDB StateDB, address common.Address, role AllowListRole) { - setAllowListRole(stateDB, ContractDeployerAllowListAddress, address, role) -} diff --git a/precompile/deployerallowlist/config_test.go b/precompile/deployerallowlist/config_test.go new file mode 100644 index 0000000000..2ffb0be77c --- /dev/null +++ b/precompile/deployerallowlist/config_test.go @@ -0,0 +1,95 @@ +// (c) 2019-2020, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package deployerallowlist + +import ( + "math/big" + "testing" + + "github.com/ava-labs/subnet-evm/precompile" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestVerifyContractDeployerConfig(t *testing.T) { + admins := []common.Address{{1}} + tests := []struct { + name string + config precompile.StatefulPrecompileConfig + expectedError string + }{ + { + name: "invalid allow list config in deployer allowlist", + config: NewContractDeployerAllowListConfig(big.NewInt(3), admins, admins), + expectedError: "cannot set address", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + + err := tt.config.Verify() + if tt.expectedError == "" { + require.NoError(err) + } else { + require.ErrorContains(err, tt.expectedError) + } + }) + } +} + +func TestEqualContractDeployerAllowListConfig(t *testing.T) { + admins := []common.Address{{1}} + enableds := []common.Address{{2}} + tests := []struct { + name string + config precompile.StatefulPrecompileConfig + other precompile.StatefulPrecompileConfig + expected bool + }{ + { + name: "non-nil config and nil other", + config: NewContractDeployerAllowListConfig(big.NewInt(3), admins, enableds), + other: nil, + expected: false, + }, + { + name: "different type", + config: NewContractDeployerAllowListConfig(big.NewInt(3), admins, enableds), + other: precompile.NewNoopStatefulPrecompileConfig(), + expected: false, + }, + { + name: "different admin", + config: NewContractDeployerAllowListConfig(big.NewInt(3), admins, enableds), + other: NewContractDeployerAllowListConfig(big.NewInt(3), []common.Address{{3}}, enableds), + expected: false, + }, + { + name: "different enabled", + config: NewContractDeployerAllowListConfig(big.NewInt(3), admins, enableds), + other: NewContractDeployerAllowListConfig(big.NewInt(3), admins, []common.Address{{3}}), + expected: false, + }, + { + name: "different timestamp", + config: NewContractDeployerAllowListConfig(big.NewInt(3), admins, enableds), + other: NewContractDeployerAllowListConfig(big.NewInt(4), admins, enableds), + expected: false, + }, + { + name: "same config", + config: NewContractDeployerAllowListConfig(big.NewInt(3), admins, enableds), + other: NewContractDeployerAllowListConfig(big.NewInt(3), admins, enableds), + expected: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + + require.Equal(tt.expected, tt.config.Equal(tt.other)) + }) + } +} diff --git a/precompile/deployerallowlist/contract.go b/precompile/deployerallowlist/contract.go new file mode 100644 index 0000000000..df464452b3 --- /dev/null +++ b/precompile/deployerallowlist/contract.go @@ -0,0 +1,27 @@ +// (c) 2019-2020, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package deployerallowlist + +import ( + "github.com/ava-labs/subnet-evm/precompile" + "github.com/ethereum/go-ethereum/common" +) + +var ( + // Singleton StatefulPrecompiledContract for W/R access to the contract deployer allow list. + ContractDeployerAllowListPrecompile precompile.StatefulPrecompiledContract = precompile.CreateAllowListPrecompile(precompile.ContractDeployerAllowListAddress) +) + +// GetContractDeployerAllowListStatus returns the role of [address] for the contract deployer +// allow list. +func GetContractDeployerAllowListStatus(stateDB precompile.StateDB, address common.Address) precompile.AllowListRole { + return precompile.GetAllowListStatus(stateDB, precompile.ContractDeployerAllowListAddress, address) +} + +// SetContractDeployerAllowListStatus sets the permissions of [address] to [role] for the +// contract deployer allow list. +// assumes [role] has already been verified as valid. +func SetContractDeployerAllowListStatus(stateDB precompile.StateDB, address common.Address, role precompile.AllowListRole) { + precompile.SetAllowListRole(stateDB, precompile.ContractDeployerAllowListAddress, address, role) +} diff --git a/precompile/feemanager/config.go b/precompile/feemanager/config.go new file mode 100644 index 0000000000..65c8d82b84 --- /dev/null +++ b/precompile/feemanager/config.go @@ -0,0 +1,109 @@ +// (c) 2019-2020, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package feemanager + +import ( + "encoding/json" + "fmt" + "math/big" + + "github.com/ava-labs/subnet-evm/commontype" + "github.com/ava-labs/subnet-evm/precompile" + "github.com/ethereum/go-ethereum/common" +) + +// FeeManagerConfig wraps [AllowListConfig] and uses it to implement the StatefulPrecompileConfig +// interface while adding in the FeeConfigManager specific precompile address. +type FeeManagerConfig struct { + precompile.AllowListConfig // Config for the fee config manager allow list + precompile.UpgradeableConfig + InitialFeeConfig *commontype.FeeConfig `json:"initialFeeConfig,omitempty"` // initial fee config to be immediately activated +} + +// NewFeeManagerConfig returns a config for a network upgrade at [blockTimestamp] that enables +// FeeConfigManager with the given [admins] and [enableds] as members of the allowlist with [initialConfig] as initial fee config if specified. +func NewFeeManagerConfig(blockTimestamp *big.Int, admins []common.Address, enableds []common.Address, initialConfig *commontype.FeeConfig) *FeeManagerConfig { + return &FeeManagerConfig{ + AllowListConfig: precompile.AllowListConfig{ + AllowListAdmins: admins, + EnabledAddresses: enableds, + }, + UpgradeableConfig: precompile.UpgradeableConfig{BlockTimestamp: blockTimestamp}, + InitialFeeConfig: initialConfig, + } +} + +// NewDisableFeeManagerConfig returns config for a network upgrade at [blockTimestamp] +// that disables FeeConfigManager. +func NewDisableFeeManagerConfig(blockTimestamp *big.Int) *FeeManagerConfig { + return &FeeManagerConfig{ + UpgradeableConfig: precompile.UpgradeableConfig{ + BlockTimestamp: blockTimestamp, + Disable: true, + }, + } +} + +// Address returns the address of the fee config manager contract. +func (c *FeeManagerConfig) Address() common.Address { + return precompile.FeeConfigManagerAddress +} + +// Equal returns true if [s] is a [*FeeManagerConfig] and it has been configured identical to [c]. +func (c *FeeManagerConfig) Equal(s precompile.StatefulPrecompileConfig) bool { + // typecast before comparison + other, ok := (s).(*FeeManagerConfig) + if !ok { + return false + } + eq := c.UpgradeableConfig.Equal(&other.UpgradeableConfig) && c.AllowListConfig.Equal(&other.AllowListConfig) + if !eq { + return false + } + + if c.InitialFeeConfig == nil { + return other.InitialFeeConfig == nil + } + + return c.InitialFeeConfig.Equal(other.InitialFeeConfig) +} + +// Configure configures [state] with the desired admins based on [c]. +func (c *FeeManagerConfig) Configure(chainConfig precompile.ChainConfig, state precompile.StateDB, blockContext precompile.BlockContext) error { + // Store the initial fee config into the state when the fee config manager activates. + if c.InitialFeeConfig != nil { + if err := StoreFeeConfig(state, *c.InitialFeeConfig, blockContext); err != nil { + // This should not happen since we already checked this config with Verify() + return fmt.Errorf("cannot configure given initial fee config: %w", err) + } + } else { + if err := StoreFeeConfig(state, chainConfig.GetFeeConfig(), blockContext); err != nil { + // This should not happen since we already checked the chain config in the genesis creation. + return fmt.Errorf("cannot configure fee config in chain config: %w", err) + } + } + return c.AllowListConfig.Configure(state, precompile.FeeConfigManagerAddress) +} + +// Contract returns the singleton stateful precompiled contract to be used for the fee manager. +func (c *FeeManagerConfig) Contract() precompile.StatefulPrecompiledContract { + return FeeConfigManagerPrecompile +} + +func (c *FeeManagerConfig) Verify() error { + if err := c.AllowListConfig.Verify(); err != nil { + return err + } + if c.InitialFeeConfig == nil { + return nil + } + + return c.InitialFeeConfig.Verify() +} + +// String returns a string representation of the FeeManagerConfig. +func (c *FeeManagerConfig) String() string { + bytes, _ := json.Marshal(c) + return string(bytes) +} diff --git a/precompile/feemanager/config_test.go b/precompile/feemanager/config_test.go new file mode 100644 index 0000000000..1761c3109d --- /dev/null +++ b/precompile/feemanager/config_test.go @@ -0,0 +1,128 @@ +// (c) 2022 Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package feemanager + +import ( + "math/big" + "testing" + + "github.com/ava-labs/subnet-evm/commontype" + "github.com/ava-labs/subnet-evm/precompile" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +var validFeeConfig = commontype.FeeConfig{ + GasLimit: big.NewInt(8_000_000), + TargetBlockRate: 2, // in seconds + + MinBaseFee: big.NewInt(25_000_000_000), + TargetGas: big.NewInt(15_000_000), + BaseFeeChangeDenominator: big.NewInt(36), + + MinBlockGasCost: big.NewInt(0), + MaxBlockGasCost: big.NewInt(1_000_000), + BlockGasCostStep: big.NewInt(200_000), +} + +func TestVerifyFeeManagerConfig(t *testing.T) { + admins := []common.Address{{1}} + tests := []struct { + name string + config precompile.StatefulPrecompileConfig + expectedError string + }{ + { + name: "invalid allow list config in fee manager allowlist", + config: NewFeeManagerConfig(big.NewInt(3), admins, admins, nil), + expectedError: "cannot set address", + }, + { + name: "invalid initial fee manager config", + config: NewFeeManagerConfig(big.NewInt(3), admins, nil, + &commontype.FeeConfig{ + GasLimit: big.NewInt(0), + }), + expectedError: "gasLimit = 0 cannot be less than or equal to 0", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + + err := tt.config.Verify() + if tt.expectedError == "" { + require.NoError(err) + } else { + require.ErrorContains(err, tt.expectedError) + } + }) + } +} + +func TestEqualFeeManagerConfig(t *testing.T) { + admins := []common.Address{{1}} + enableds := []common.Address{{2}} + tests := []struct { + name string + config precompile.StatefulPrecompileConfig + other precompile.StatefulPrecompileConfig + expected bool + }{ + { + name: "non-nil config and nil other", + config: NewFeeManagerConfig(big.NewInt(3), admins, enableds, nil), + other: nil, + expected: false, + }, + { + name: "different type", + config: NewFeeManagerConfig(big.NewInt(3), admins, enableds, nil), + other: precompile.NewNoopStatefulPrecompileConfig(), + expected: false, + }, + { + name: "different timestamp", + config: NewFeeManagerConfig(big.NewInt(3), admins, nil, nil), + other: NewFeeManagerConfig(big.NewInt(4), admins, nil, nil), + expected: false, + }, + { + name: "different enabled", + config: NewFeeManagerConfig(big.NewInt(3), admins, nil, nil), + other: NewFeeManagerConfig(big.NewInt(3), admins, enableds, nil), + expected: false, + }, + { + name: "non-nil initial config and nil initial config", + config: NewFeeManagerConfig(big.NewInt(3), admins, nil, &validFeeConfig), + other: NewFeeManagerConfig(big.NewInt(3), admins, nil, nil), + expected: false, + }, + { + name: "different initial config", + config: NewFeeManagerConfig(big.NewInt(3), admins, nil, &validFeeConfig), + other: NewFeeManagerConfig(big.NewInt(3), admins, nil, + func() *commontype.FeeConfig { + c := validFeeConfig + c.GasLimit = big.NewInt(123) + return &c + }()), + expected: false, + }, + { + name: "same config", + config: NewFeeManagerConfig(big.NewInt(3), admins, nil, &validFeeConfig), + other: NewFeeManagerConfig(big.NewInt(3), admins, nil, &validFeeConfig), + expected: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + + require.Equal(tt.expected, tt.config.Equal(tt.other)) + }) + } +} diff --git a/precompile/fee_config_manager.go b/precompile/feemanager/contract.go similarity index 56% rename from precompile/fee_config_manager.go rename to precompile/feemanager/contract.go index 56567d840a..0635d87734 100644 --- a/precompile/fee_config_manager.go +++ b/precompile/feemanager/contract.go @@ -1,15 +1,15 @@ // (c) 2019-2020, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package precompile +package feemanager import ( - "encoding/json" "errors" "fmt" "math/big" "github.com/ava-labs/subnet-evm/commontype" + "github.com/ava-labs/subnet-evm/precompile" "github.com/ava-labs/subnet-evm/vmerrs" "github.com/ethereum/go-ethereum/common" ) @@ -32,130 +32,35 @@ const ( // [numFeeConfigField] fields in FeeConfig struct feeConfigInputLen = common.HashLength * numFeeConfigField - SetFeeConfigGasCost = writeGasCostPerSlot * (numFeeConfigField + 1) // plus one for setting last changed at - GetFeeConfigGasCost = readGasCostPerSlot * numFeeConfigField - GetLastChangedAtGasCost = readGasCostPerSlot + SetFeeConfigGasCost = precompile.WriteGasCostPerSlot * (numFeeConfigField + 1) // plus one for setting last changed at + GetFeeConfigGasCost = precompile.ReadGasCostPerSlot * numFeeConfigField + GetLastChangedAtGasCost = precompile.ReadGasCostPerSlot ) var ( - _ StatefulPrecompileConfig = &FeeConfigManagerConfig{} + _ precompile.StatefulPrecompileConfig = &FeeManagerConfig{} // Singleton StatefulPrecompiledContract for setting fee configs by permissioned callers. - FeeConfigManagerPrecompile StatefulPrecompiledContract = createFeeConfigManagerPrecompile(FeeConfigManagerAddress) + FeeConfigManagerPrecompile precompile.StatefulPrecompiledContract = createFeeConfigManagerPrecompile(precompile.FeeConfigManagerAddress) - setFeeConfigSignature = CalculateFunctionSelector("setFeeConfig(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256)") - getFeeConfigSignature = CalculateFunctionSelector("getFeeConfig()") - getFeeConfigLastChangedAtSignature = CalculateFunctionSelector("getFeeConfigLastChangedAt()") + setFeeConfigSignature = precompile.CalculateFunctionSelector("setFeeConfig(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256)") + getFeeConfigSignature = precompile.CalculateFunctionSelector("getFeeConfig()") + getFeeConfigLastChangedAtSignature = precompile.CalculateFunctionSelector("getFeeConfigLastChangedAt()") feeConfigLastChangedAtKey = common.Hash{'l', 'c', 'a'} ErrCannotChangeFee = errors.New("non-enabled cannot change fee config") ) -// FeeConfigManagerConfig wraps [AllowListConfig] and uses it to implement the StatefulPrecompileConfig -// interface while adding in the FeeConfigManager specific precompile address. -type FeeConfigManagerConfig struct { - AllowListConfig // Config for the fee config manager allow list - UpgradeableConfig - InitialFeeConfig *commontype.FeeConfig `json:"initialFeeConfig,omitempty"` // initial fee config to be immediately activated -} - -// NewFeeManagerConfig returns a config for a network upgrade at [blockTimestamp] that enables -// FeeConfigManager with the given [admins] and [enableds] as members of the allowlist with [initialConfig] as initial fee config if specified. -func NewFeeManagerConfig(blockTimestamp *big.Int, admins []common.Address, enableds []common.Address, initialConfig *commontype.FeeConfig) *FeeConfigManagerConfig { - return &FeeConfigManagerConfig{ - AllowListConfig: AllowListConfig{ - AllowListAdmins: admins, - EnabledAddresses: enableds, - }, - UpgradeableConfig: UpgradeableConfig{BlockTimestamp: blockTimestamp}, - InitialFeeConfig: initialConfig, - } -} - -// NewDisableFeeManagerConfig returns config for a network upgrade at [blockTimestamp] -// that disables FeeConfigManager. -func NewDisableFeeManagerConfig(blockTimestamp *big.Int) *FeeConfigManagerConfig { - return &FeeConfigManagerConfig{ - UpgradeableConfig: UpgradeableConfig{ - BlockTimestamp: blockTimestamp, - Disable: true, - }, - } -} - -// Address returns the address of the fee config manager contract. -func (c *FeeConfigManagerConfig) Address() common.Address { - return FeeConfigManagerAddress -} - -// Equal returns true if [s] is a [*FeeConfigManagerConfig] and it has been configured identical to [c]. -func (c *FeeConfigManagerConfig) Equal(s StatefulPrecompileConfig) bool { - // typecast before comparison - other, ok := (s).(*FeeConfigManagerConfig) - if !ok { - return false - } - eq := c.UpgradeableConfig.Equal(&other.UpgradeableConfig) && c.AllowListConfig.Equal(&other.AllowListConfig) - if !eq { - return false - } - - if c.InitialFeeConfig == nil { - return other.InitialFeeConfig == nil - } - - return c.InitialFeeConfig.Equal(other.InitialFeeConfig) -} - -// Configure configures [state] with the desired admins based on [c]. -func (c *FeeConfigManagerConfig) Configure(chainConfig ChainConfig, state StateDB, blockContext BlockContext) error { - // Store the initial fee config into the state when the fee config manager activates. - if c.InitialFeeConfig != nil { - if err := StoreFeeConfig(state, *c.InitialFeeConfig, blockContext); err != nil { - // This should not happen since we already checked this config with Verify() - return fmt.Errorf("cannot configure given initial fee config: %w", err) - } - } else { - if err := StoreFeeConfig(state, chainConfig.GetFeeConfig(), blockContext); err != nil { - // This should not happen since we already checked the chain config in the genesis creation. - return fmt.Errorf("cannot configure fee config in chain config: %w", err) - } - } - return c.AllowListConfig.Configure(state, FeeConfigManagerAddress) -} - -// Contract returns the singleton stateful precompiled contract to be used for the fee manager. -func (c *FeeConfigManagerConfig) Contract() StatefulPrecompiledContract { - return FeeConfigManagerPrecompile -} - -func (c *FeeConfigManagerConfig) Verify() error { - if err := c.AllowListConfig.Verify(); err != nil { - return err - } - if c.InitialFeeConfig == nil { - return nil - } - - return c.InitialFeeConfig.Verify() -} - -// String returns a string representation of the FeeConfigManagerConfig. -func (c *FeeConfigManagerConfig) String() string { - bytes, _ := json.Marshal(c) - return string(bytes) -} - // GetFeeConfigManagerStatus returns the role of [address] for the fee config manager list. -func GetFeeConfigManagerStatus(stateDB StateDB, address common.Address) AllowListRole { - return getAllowListStatus(stateDB, FeeConfigManagerAddress, address) +func GetFeeConfigManagerStatus(stateDB precompile.StateDB, address common.Address) precompile.AllowListRole { + return precompile.GetAllowListStatus(stateDB, precompile.FeeConfigManagerAddress, address) } // SetFeeConfigManagerStatus sets the permissions of [address] to [role] for the // fee config manager list. assumes [role] has already been verified as valid. -func SetFeeConfigManagerStatus(stateDB StateDB, address common.Address, role AllowListRole) { - setAllowListRole(stateDB, FeeConfigManagerAddress, address, role) +func SetFeeConfigManagerStatus(stateDB precompile.StateDB, address common.Address, role precompile.AllowListRole) { + precompile.SetAllowListRole(stateDB, precompile.FeeConfigManagerAddress, address, role) } // PackGetFeeConfigInput packs the getFeeConfig signature @@ -194,12 +99,12 @@ func packFeeConfigHelper(feeConfig commontype.FeeConfig, useSelector bool) ([]by if useSelector { res := make([]byte, len(setFeeConfigSignature)+feeConfigInputLen) - err := packOrderedHashesWithSelector(res, setFeeConfigSignature, hashes) + err := precompile.PackOrderedHashesWithSelector(res, setFeeConfigSignature, hashes) return res, err } res := make([]byte, len(hashes)*common.HashLength) - err := packOrderedHashes(res, hashes) + err := precompile.PackOrderedHashes(res, hashes) return res, err } @@ -212,7 +117,7 @@ func UnpackFeeConfigInput(input []byte) (commontype.FeeConfig, error) { feeConfig := commontype.FeeConfig{} for i := minFeeConfigFieldKey; i <= numFeeConfigField; i++ { listIndex := i - 1 - packedElement := returnPackedHash(input, listIndex) + packedElement := precompile.PackedHash(input, listIndex) switch i { case gasLimitKey: feeConfig.GasLimit = new(big.Int).SetBytes(packedElement) @@ -239,10 +144,10 @@ func UnpackFeeConfigInput(input []byte) (commontype.FeeConfig, error) { } // GetStoredFeeConfig returns fee config from contract storage in given state -func GetStoredFeeConfig(stateDB StateDB) commontype.FeeConfig { +func GetStoredFeeConfig(stateDB precompile.StateDB) commontype.FeeConfig { feeConfig := commontype.FeeConfig{} for i := minFeeConfigFieldKey; i <= numFeeConfigField; i++ { - val := stateDB.GetState(FeeConfigManagerAddress, common.Hash{byte(i)}) + val := stateDB.GetState(precompile.FeeConfigManagerAddress, common.Hash{byte(i)}) switch i { case gasLimitKey: feeConfig.GasLimit = new(big.Int).Set(val.Big()) @@ -268,14 +173,14 @@ func GetStoredFeeConfig(stateDB StateDB) commontype.FeeConfig { return feeConfig } -func GetFeeConfigLastChangedAt(stateDB StateDB) *big.Int { - val := stateDB.GetState(FeeConfigManagerAddress, feeConfigLastChangedAtKey) +func GetFeeConfigLastChangedAt(stateDB precompile.StateDB) *big.Int { + val := stateDB.GetState(precompile.FeeConfigManagerAddress, feeConfigLastChangedAtKey) return val.Big() } // StoreFeeConfig stores given [feeConfig] and block number in the [blockContext] to the [stateDB]. // A validation on [feeConfig] is done before storing. -func StoreFeeConfig(stateDB StateDB, feeConfig commontype.FeeConfig, blockContext BlockContext) error { +func StoreFeeConfig(stateDB precompile.StateDB, feeConfig commontype.FeeConfig, blockContext precompile.BlockContext) error { if err := feeConfig.Verify(); err != nil { return fmt.Errorf("cannot verify fee config: %w", err) } @@ -303,22 +208,22 @@ func StoreFeeConfig(stateDB StateDB, feeConfig commontype.FeeConfig, blockContex // This should never encounter an unknown fee config key panic(fmt.Sprintf("unknown fee config key: %d", i)) } - stateDB.SetState(FeeConfigManagerAddress, common.Hash{byte(i)}, input) + stateDB.SetState(precompile.FeeConfigManagerAddress, common.Hash{byte(i)}, input) } blockNumber := blockContext.Number() if blockNumber == nil { return fmt.Errorf("blockNumber cannot be nil") } - stateDB.SetState(FeeConfigManagerAddress, feeConfigLastChangedAtKey, common.BigToHash(blockNumber)) + stateDB.SetState(precompile.FeeConfigManagerAddress, feeConfigLastChangedAtKey, common.BigToHash(blockNumber)) return nil } // setFeeConfig checks if the caller has permissions to set the fee config. // The execution function parses [input] into FeeConfig structure and sets contract storage accordingly. -func setFeeConfig(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { - if remainingGas, err = deductGas(suppliedGas, SetFeeConfigGasCost); err != nil { +func setFeeConfig(accessibleState precompile.PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { + if remainingGas, err = precompile.DeductGas(suppliedGas, SetFeeConfigGasCost); err != nil { return nil, 0, err } @@ -333,7 +238,7 @@ func setFeeConfig(accessibleState PrecompileAccessibleState, caller common.Addre stateDB := accessibleState.GetStateDB() // Verify that the caller is in the allow list and therefore has the right to modify it - callerStatus := getAllowListStatus(stateDB, FeeConfigManagerAddress, caller) + callerStatus := precompile.GetAllowListStatus(stateDB, precompile.FeeConfigManagerAddress, caller) if !callerStatus.IsEnabled() { return nil, remainingGas, fmt.Errorf("%w: %s", ErrCannotChangeFee, caller) } @@ -348,8 +253,8 @@ func setFeeConfig(accessibleState PrecompileAccessibleState, caller common.Addre // getFeeConfig returns the stored fee config as an output. // The execution function reads the contract state for the stored fee config and returns the output. -func getFeeConfig(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { - if remainingGas, err = deductGas(suppliedGas, GetFeeConfigGasCost); err != nil { +func getFeeConfig(accessibleState precompile.PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { + if remainingGas, err = precompile.DeductGas(suppliedGas, GetFeeConfigGasCost); err != nil { return nil, 0, err } @@ -366,8 +271,8 @@ func getFeeConfig(accessibleState PrecompileAccessibleState, caller common.Addre // getFeeConfigLastChangedAt returns the block number that fee config was last changed in. // The execution function reads the contract state for the stored block number and returns the output. -func getFeeConfigLastChangedAt(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { - if remainingGas, err = deductGas(suppliedGas, GetLastChangedAtGasCost); err != nil { +func getFeeConfigLastChangedAt(accessibleState precompile.PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { + if remainingGas, err = precompile.DeductGas(suppliedGas, GetLastChangedAtGasCost); err != nil { return nil, 0, err } @@ -380,16 +285,16 @@ func getFeeConfigLastChangedAt(accessibleState PrecompileAccessibleState, caller // createFeeConfigManagerPrecompile returns a StatefulPrecompiledContract // with getters and setters for the chain's fee config. Access to the getters/setters // is controlled by an allow list for [precompileAddr]. -func createFeeConfigManagerPrecompile(precompileAddr common.Address) StatefulPrecompiledContract { - feeConfigManagerFunctions := createAllowListFunctions(precompileAddr) +func createFeeConfigManagerPrecompile(precompileAddr common.Address) precompile.StatefulPrecompiledContract { + feeConfigManagerFunctions := precompile.CreateAllowListFunctions(precompileAddr) - setFeeConfigFunc := newStatefulPrecompileFunction(setFeeConfigSignature, setFeeConfig) - getFeeConfigFunc := newStatefulPrecompileFunction(getFeeConfigSignature, getFeeConfig) - getFeeConfigLastChangedAtFunc := newStatefulPrecompileFunction(getFeeConfigLastChangedAtSignature, getFeeConfigLastChangedAt) + setFeeConfigFunc := precompile.NewStatefulPrecompileFunction(setFeeConfigSignature, setFeeConfig) + getFeeConfigFunc := precompile.NewStatefulPrecompileFunction(getFeeConfigSignature, getFeeConfig) + getFeeConfigLastChangedAtFunc := precompile.NewStatefulPrecompileFunction(getFeeConfigLastChangedAtSignature, getFeeConfigLastChangedAt) feeConfigManagerFunctions = append(feeConfigManagerFunctions, setFeeConfigFunc, getFeeConfigFunc, getFeeConfigLastChangedAtFunc) // Construct the contract with no fallback function. - contract, err := NewStatefulPrecompileContract(nil, feeConfigManagerFunctions) + contract, err := precompile.NewStatefulPrecompileContract(nil, feeConfigManagerFunctions) // TODO Change this to be returned as an error after refactoring this precompile // to use the new precompile template. if err != nil { diff --git a/precompile/interface.go b/precompile/interface.go new file mode 100644 index 0000000000..82be0f749a --- /dev/null +++ b/precompile/interface.go @@ -0,0 +1,61 @@ +// (c) 2019-2020, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package precompile + +import ( + "math/big" + + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/subnet-evm/commontype" + "github.com/ethereum/go-ethereum/common" +) + +// PrecompileAccessibleState defines the interface exposed to stateful precompile contracts +type PrecompileAccessibleState interface { + GetStateDB() StateDB + GetBlockContext() BlockContext + GetSnowContext() *snow.Context + CallFromPrecompile(caller common.Address, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) +} + +// BlockContext defines an interface that provides information to a stateful precompile +// about the block that activates the upgrade. The precompile can access this information +// to initialize its state. +type BlockContext interface { + Number() *big.Int + Timestamp() *big.Int +} + +// ChainContext defines an interface that provides information to a stateful precompile +// about the chain configuration. The precompile can access this information to initialize +// its state. +type ChainConfig interface { + // GetFeeConfig returns the original FeeConfig that was set in the genesis. + GetFeeConfig() commontype.FeeConfig + // AllowedFeeRecipients returns true if fee recipients are allowed in the genesis. + AllowedFeeRecipients() bool +} + +// StateDB is the interface for accessing EVM state +type StateDB interface { + GetState(common.Address, common.Hash) common.Hash + SetState(common.Address, common.Hash, common.Hash) + + SetCode(common.Address, []byte) + + SetNonce(common.Address, uint64) + GetNonce(common.Address) uint64 + + GetBalance(common.Address) *big.Int + AddBalance(common.Address, *big.Int) + SubBalance(common.Address, *big.Int) + + CreateAccount(common.Address) + Exist(common.Address) bool + + AddLog(addr common.Address, topics []common.Hash, data []byte, blockNumber uint64) + + Suicide(common.Address) bool + Finalise(deleteEmptyObjects bool) +} diff --git a/precompile/mock_interface.go b/precompile/mock_interface.go new file mode 100644 index 0000000000..4d6ca13281 --- /dev/null +++ b/precompile/mock_interface.go @@ -0,0 +1,115 @@ +// (c) 2019-2020, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package precompile + +import ( + "math/big" + + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/subnet-evm/commontype" + "github.com/ethereum/go-ethereum/common" +) + +// TODO: replace with gomock library + +var ( + _ BlockContext = &mockBlockContext{} + _ PrecompileAccessibleState = &mockAccessibleState{} + _ ChainConfig = &mockChainConfig{} + _ StatefulPrecompileConfig = &noopStatefulPrecompileConfig{} +) + +type mockBlockContext struct { + blockNumber *big.Int + timestamp uint64 +} + +func NewMockBlockContext(blockNumber *big.Int, timestamp uint64) *mockBlockContext { + return &mockBlockContext{ + blockNumber: blockNumber, + timestamp: timestamp, + } +} + +func (mb *mockBlockContext) Number() *big.Int { return mb.blockNumber } +func (mb *mockBlockContext) Timestamp() *big.Int { return new(big.Int).SetUint64(mb.timestamp) } + +type mockAccessibleState struct { + state StateDB + blockContext *mockBlockContext + snowContext *snow.Context +} + +func NewMockAccessibleState(state StateDB, blockContext *mockBlockContext, snowContext *snow.Context) *mockAccessibleState { + return &mockAccessibleState{ + state: state, + blockContext: blockContext, + snowContext: snowContext, + } +} + +func (m *mockAccessibleState) GetStateDB() StateDB { return m.state } + +func (m *mockAccessibleState) GetBlockContext() BlockContext { return m.blockContext } + +func (m *mockAccessibleState) GetSnowContext() *snow.Context { return m.snowContext } + +func (m *mockAccessibleState) CallFromPrecompile(caller common.Address, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { + return nil, 0, nil +} + +type mockChainConfig struct { + feeConfig commontype.FeeConfig + allowedFeeRecipients bool +} + +func NewMockChainConfig(feeConfig commontype.FeeConfig, allowedFeeRecipients bool) *mockChainConfig { + return &mockChainConfig{ + feeConfig: feeConfig, + allowedFeeRecipients: allowedFeeRecipients, + } +} + +func (m *mockChainConfig) GetFeeConfig() commontype.FeeConfig { return m.feeConfig } + +func (m *mockChainConfig) AllowedFeeRecipients() bool { return m.allowedFeeRecipients } + +type noopStatefulPrecompileConfig struct { +} + +func NewNoopStatefulPrecompileConfig() *noopStatefulPrecompileConfig { + return &noopStatefulPrecompileConfig{} +} + +func (n *noopStatefulPrecompileConfig) Address() common.Address { + return common.Address{} +} + +func (n *noopStatefulPrecompileConfig) Timestamp() *big.Int { + return new(big.Int) +} + +func (n *noopStatefulPrecompileConfig) IsDisabled() bool { + return false +} + +func (n *noopStatefulPrecompileConfig) Equal(StatefulPrecompileConfig) bool { + return false +} + +func (n *noopStatefulPrecompileConfig) Verify() error { + return nil +} + +func (n *noopStatefulPrecompileConfig) Configure(ChainConfig, StateDB, BlockContext) error { + return nil +} + +func (n *noopStatefulPrecompileConfig) Contract() StatefulPrecompiledContract { + return nil +} + +func (n *noopStatefulPrecompileConfig) String() string { + return "" +} diff --git a/precompile/nativeminter/config.go b/precompile/nativeminter/config.go new file mode 100644 index 0000000000..9ba6644b6e --- /dev/null +++ b/precompile/nativeminter/config.go @@ -0,0 +1,125 @@ +// (c) 2019-2020, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package nativeminter + +import ( + "encoding/json" + "fmt" + "math/big" + + "github.com/ava-labs/subnet-evm/precompile" + "github.com/ava-labs/subnet-evm/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" +) + +var _ precompile.StatefulPrecompileConfig = &ContractNativeMinterConfig{} + +// ContractNativeMinterConfig wraps [AllowListConfig] and uses it to implement the StatefulPrecompileConfig +// interface while adding in the ContractNativeMinter specific precompile address. +type ContractNativeMinterConfig struct { + precompile.AllowListConfig + precompile.UpgradeableConfig + InitialMint map[common.Address]*math.HexOrDecimal256 `json:"initialMint,omitempty"` // initial mint config to be immediately minted +} + +// NewContractNativeMinterConfig returns a config for a network upgrade at [blockTimestamp] that enables +// ContractNativeMinter with the given [admins] and [enableds] as members of the allowlist. Also mints balances according to [initialMint] when the upgrade activates. +func NewContractNativeMinterConfig(blockTimestamp *big.Int, admins []common.Address, enableds []common.Address, initialMint map[common.Address]*math.HexOrDecimal256) *ContractNativeMinterConfig { + return &ContractNativeMinterConfig{ + AllowListConfig: precompile.AllowListConfig{ + AllowListAdmins: admins, + EnabledAddresses: enableds, + }, + UpgradeableConfig: precompile.UpgradeableConfig{BlockTimestamp: blockTimestamp}, + InitialMint: initialMint, + } +} + +// NewDisableContractNativeMinterConfig returns config for a network upgrade at [blockTimestamp] +// that disables ContractNativeMinter. +func NewDisableContractNativeMinterConfig(blockTimestamp *big.Int) *ContractNativeMinterConfig { + return &ContractNativeMinterConfig{ + UpgradeableConfig: precompile.UpgradeableConfig{ + BlockTimestamp: blockTimestamp, + Disable: true, + }, + } +} + +// Address returns the address of the native minter contract. +func (c *ContractNativeMinterConfig) Address() common.Address { + return precompile.ContractNativeMinterAddress +} + +// Configure configures [state] with the desired admins based on [c]. +func (c *ContractNativeMinterConfig) Configure(_ precompile.ChainConfig, state precompile.StateDB, _ precompile.BlockContext) error { + for to, amount := range c.InitialMint { + if amount != nil { + bigIntAmount := (*big.Int)(amount) + state.AddBalance(to, bigIntAmount) + } + } + + return c.AllowListConfig.Configure(state, precompile.ContractNativeMinterAddress) +} + +// Contract returns the singleton stateful precompiled contract to be used for the native minter. +func (c *ContractNativeMinterConfig) Contract() precompile.StatefulPrecompiledContract { + return ContractNativeMinterPrecompile +} + +func (c *ContractNativeMinterConfig) Verify() error { + if err := c.AllowListConfig.Verify(); err != nil { + return err + } + // ensure that all of the initial mint values in the map are non-nil positive values + for addr, amount := range c.InitialMint { + if amount == nil { + return fmt.Errorf("initial mint cannot contain nil amount for address %s", addr) + } + bigIntAmount := (*big.Int)(amount) + if bigIntAmount.Sign() < 1 { + return fmt.Errorf("initial mint cannot contain invalid amount %v for address %s", bigIntAmount, addr) + } + } + return nil +} + +// Equal returns true if [s] is a [*ContractNativeMinterConfig] and it has been configured identical to [c]. +func (c *ContractNativeMinterConfig) Equal(s precompile.StatefulPrecompileConfig) bool { + // typecast before comparison + other, ok := (s).(*ContractNativeMinterConfig) + if !ok { + return false + } + eq := c.UpgradeableConfig.Equal(&other.UpgradeableConfig) && c.AllowListConfig.Equal(&other.AllowListConfig) + if !eq { + return false + } + + if len(c.InitialMint) != len(other.InitialMint) { + return false + } + + for address, amount := range c.InitialMint { + val, ok := other.InitialMint[address] + if !ok { + return false + } + bigIntAmount := (*big.Int)(amount) + bigIntVal := (*big.Int)(val) + if !utils.BigNumEqual(bigIntAmount, bigIntVal) { + return false + } + } + + return true +} + +// String returns a string representation of the ContractNativeMinterConfig. +func (c *ContractNativeMinterConfig) String() string { + bytes, _ := json.Marshal(c) + return string(bytes) +} diff --git a/precompile/nativeminter/config_test.go b/precompile/nativeminter/config_test.go new file mode 100644 index 0000000000..99c87f4aad --- /dev/null +++ b/precompile/nativeminter/config_test.go @@ -0,0 +1,150 @@ +// (c) 2019-2020, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package nativeminter + +import ( + "math/big" + "testing" + + "github.com/ava-labs/subnet-evm/precompile" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/stretchr/testify/require" +) + +func TestVerifyContractNativeMinterConfig(t *testing.T) { + admins := []common.Address{{1}} + enableds := []common.Address{{2}} + tests := []struct { + name string + config precompile.StatefulPrecompileConfig + expectedError string + }{ + { + name: "invalid allow list config in native minter allowlist", + config: NewContractNativeMinterConfig(big.NewInt(3), admins, admins, nil), + expectedError: "cannot set address", + }, + { + name: "duplicate admins in config in native minter allowlist", + config: NewContractNativeMinterConfig(big.NewInt(3), append(admins, admins[0]), enableds, nil), + expectedError: "duplicate address", + }, + { + name: "duplicate enableds in config in native minter allowlist", + config: NewContractNativeMinterConfig(big.NewInt(3), admins, append(enableds, enableds[0]), nil), + expectedError: "duplicate address", + }, + { + name: "nil amount in native minter config", + config: NewContractNativeMinterConfig(big.NewInt(3), admins, nil, + map[common.Address]*math.HexOrDecimal256{ + common.HexToAddress("0x01"): math.NewHexOrDecimal256(123), + common.HexToAddress("0x02"): nil, + }), + expectedError: "initial mint cannot contain nil", + }, + { + name: "negative amount in native minter config", + config: NewContractNativeMinterConfig(big.NewInt(3), admins, nil, + map[common.Address]*math.HexOrDecimal256{ + common.HexToAddress("0x01"): math.NewHexOrDecimal256(123), + common.HexToAddress("0x02"): math.NewHexOrDecimal256(-1), + }), + expectedError: "initial mint cannot contain invalid amount", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + + err := tt.config.Verify() + if tt.expectedError == "" { + require.NoError(err) + } else { + require.ErrorContains(err, tt.expectedError) + } + }) + } +} + +func TestEqualContractNativeMinterConfig(t *testing.T) { + admins := []common.Address{{1}} + enableds := []common.Address{{2}} + tests := []struct { + name string + config precompile.StatefulPrecompileConfig + other precompile.StatefulPrecompileConfig + expected bool + }{ + { + name: "non-nil config and nil other", + config: NewContractNativeMinterConfig(big.NewInt(3), admins, enableds, nil), + other: nil, + expected: false, + }, + { + name: "different type", + config: NewContractNativeMinterConfig(big.NewInt(3), admins, enableds, nil), + other: precompile.NewNoopStatefulPrecompileConfig(), + expected: false, + }, + { + name: "different timestamps", + config: NewContractNativeMinterConfig(big.NewInt(3), admins, nil, nil), + other: NewContractNativeMinterConfig(big.NewInt(4), admins, nil, nil), + expected: false, + }, + { + name: "different enabled", + config: NewContractNativeMinterConfig(big.NewInt(3), admins, nil, nil), + other: NewContractNativeMinterConfig(big.NewInt(3), admins, enableds, nil), + expected: false, + }, + { + name: "different initial mint amounts", + config: NewContractNativeMinterConfig(big.NewInt(3), admins, nil, + map[common.Address]*math.HexOrDecimal256{ + common.HexToAddress("0x01"): math.NewHexOrDecimal256(1), + }), + other: NewContractNativeMinterConfig(big.NewInt(3), admins, nil, + map[common.Address]*math.HexOrDecimal256{ + common.HexToAddress("0x01"): math.NewHexOrDecimal256(2), + }), + expected: false, + }, + { + name: "different initial mint addresses", + config: NewContractNativeMinterConfig(big.NewInt(3), admins, nil, + map[common.Address]*math.HexOrDecimal256{ + common.HexToAddress("0x01"): math.NewHexOrDecimal256(1), + }), + other: NewContractNativeMinterConfig(big.NewInt(3), admins, nil, + map[common.Address]*math.HexOrDecimal256{ + common.HexToAddress("0x02"): math.NewHexOrDecimal256(1), + }), + expected: false, + }, + + { + name: "same config", + config: NewContractNativeMinterConfig(big.NewInt(3), admins, nil, + map[common.Address]*math.HexOrDecimal256{ + common.HexToAddress("0x01"): math.NewHexOrDecimal256(1), + }), + other: NewContractNativeMinterConfig(big.NewInt(3), admins, nil, + map[common.Address]*math.HexOrDecimal256{ + common.HexToAddress("0x01"): math.NewHexOrDecimal256(1), + }), + expected: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + + require.Equal(tt.expected, tt.config.Equal(tt.other)) + }) + } +} diff --git a/precompile/nativeminter/contract_native_minter.go b/precompile/nativeminter/contract_native_minter.go new file mode 100644 index 0000000000..aa3d19eedb --- /dev/null +++ b/precompile/nativeminter/contract_native_minter.go @@ -0,0 +1,116 @@ +// (c) 2019-2020, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package nativeminter + +import ( + "errors" + "fmt" + "math/big" + + "github.com/ava-labs/subnet-evm/precompile" + "github.com/ava-labs/subnet-evm/vmerrs" + "github.com/ethereum/go-ethereum/common" +) + +const ( + mintInputAddressSlot = iota + mintInputAmountSlot + + mintInputLen = common.HashLength + common.HashLength + + MintGasCost = 30_000 +) + +var ( + // Singleton StatefulPrecompiledContract for minting native assets by permissioned callers. + ContractNativeMinterPrecompile precompile.StatefulPrecompiledContract = createNativeMinterPrecompile(precompile.ContractNativeMinterAddress) + + mintSignature = precompile.CalculateFunctionSelector("mintNativeCoin(address,uint256)") // address, amount + ErrCannotMint = errors.New("non-enabled cannot mint") +) + +// GetContractNativeMinterStatus returns the role of [address] for the minter list. +func GetContractNativeMinterStatus(stateDB precompile.StateDB, address common.Address) precompile.AllowListRole { + return precompile.GetAllowListStatus(stateDB, precompile.ContractNativeMinterAddress, address) +} + +// SetContractNativeMinterStatus sets the permissions of [address] to [role] for the +// minter list. assumes [role] has already been verified as valid. +func SetContractNativeMinterStatus(stateDB precompile.StateDB, address common.Address, role precompile.AllowListRole) { + precompile.SetAllowListRole(stateDB, precompile.ContractNativeMinterAddress, address, role) +} + +// PackMintInput packs [address] and [amount] into the appropriate arguments for minting operation. +// Assumes that [amount] can be represented by 32 bytes. +func PackMintInput(address common.Address, amount *big.Int) ([]byte, error) { + // function selector (4 bytes) + input(hash for address + hash for amount) + res := make([]byte, precompile.SelectorLen+mintInputLen) + err := precompile.PackOrderedHashesWithSelector(res, mintSignature, []common.Hash{ + address.Hash(), + common.BigToHash(amount), + }) + + return res, err +} + +// UnpackMintInput attempts to unpack [input] into the arguments to the mint precompile +// assumes that [input] does not include selector (omits first 4 bytes in PackMintInput) +func UnpackMintInput(input []byte) (common.Address, *big.Int, error) { + if len(input) != mintInputLen { + return common.Address{}, nil, fmt.Errorf("invalid input length for minting: %d", len(input)) + } + to := common.BytesToAddress(precompile.PackedHash(input, mintInputAddressSlot)) + assetAmount := new(big.Int).SetBytes(precompile.PackedHash(input, mintInputAmountSlot)) + return to, assetAmount, nil +} + +// mintNativeCoin checks if the caller is permissioned for minting operation. +// The execution function parses the [input] into native coin amount and receiver address. +func mintNativeCoin(accessibleState precompile.PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { + if remainingGas, err = precompile.DeductGas(suppliedGas, MintGasCost); err != nil { + return nil, 0, err + } + + if readOnly { + return nil, remainingGas, vmerrs.ErrWriteProtection + } + + to, amount, err := UnpackMintInput(input) + if err != nil { + return nil, remainingGas, err + } + + stateDB := accessibleState.GetStateDB() + // Verify that the caller is in the allow list and therefore has the right to modify it + callerStatus := precompile.GetAllowListStatus(stateDB, precompile.ContractNativeMinterAddress, caller) + if !callerStatus.IsEnabled() { + return nil, remainingGas, fmt.Errorf("%w: %s", ErrCannotMint, caller) + } + + // if there is no address in the state, create one. + if !stateDB.Exist(to) { + stateDB.CreateAccount(to) + } + + stateDB.AddBalance(to, amount) + // Return an empty output and the remaining gas + return []byte{}, remainingGas, nil +} + +// createNativeMinterPrecompile returns a StatefulPrecompiledContract with R/W control of an allow list at [precompileAddr] and a native coin minter. +func createNativeMinterPrecompile(precompileAddr common.Address) precompile.StatefulPrecompiledContract { + enabledFuncs := precompile.CreateAllowListFunctions(precompileAddr) + + mintFunc := precompile.NewStatefulPrecompileFunction(mintSignature, mintNativeCoin) + + enabledFuncs = append(enabledFuncs, mintFunc) + // Construct the contract with no fallback function. + contract, err := precompile.NewStatefulPrecompileContract(nil, enabledFuncs) + // Change this to be returned as an error after refactoring this precompile + // to use the new precompile template. + if err != nil { + panic(err) + } + return contract +} diff --git a/precompile/params.go b/precompile/params.go index 965ab1df20..294ab328fa 100644 --- a/precompile/params.go +++ b/precompile/params.go @@ -11,8 +11,8 @@ import ( // Gas costs for stateful precompiles const ( - writeGasCostPerSlot = 20_000 - readGasCostPerSlot = 5_000 + WriteGasCostPerSlot = 20_000 + ReadGasCostPerSlot = 5_000 ) // Designated addresses of stateful precompiles diff --git a/precompile/rewardmanager/config.go b/precompile/rewardmanager/config.go new file mode 100644 index 0000000000..6b85f6fedb --- /dev/null +++ b/precompile/rewardmanager/config.go @@ -0,0 +1,152 @@ +// (c) 2019-2020, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +// Code generated +// This file is a generated precompile contract with stubbed abstract functions. + +package rewardmanager + +import ( + "encoding/json" + "math/big" + + "github.com/ava-labs/subnet-evm/precompile" + "github.com/ethereum/go-ethereum/common" +) + +var _ precompile.StatefulPrecompileConfig = &RewardManagerConfig{} + +type InitialRewardConfig struct { + AllowFeeRecipients bool `json:"allowFeeRecipients"` + RewardAddress common.Address `json:"rewardAddress,omitempty"` +} + +func (i *InitialRewardConfig) Verify() error { + switch { + case i.AllowFeeRecipients && i.RewardAddress != (common.Address{}): + return ErrCannotEnableBothRewards + default: + return nil + } +} + +func (c *InitialRewardConfig) Equal(other *InitialRewardConfig) bool { + if other == nil { + return false + } + + return c.AllowFeeRecipients == other.AllowFeeRecipients && c.RewardAddress == other.RewardAddress +} + +func (i *InitialRewardConfig) Configure(state precompile.StateDB) error { + // enable allow fee recipients + if i.AllowFeeRecipients { + EnableAllowFeeRecipients(state) + } else if i.RewardAddress == (common.Address{}) { + // if reward address is empty and allow fee recipients is false + // then disable rewards + DisableFeeRewards(state) + } else { + // set reward address + return StoreRewardAddress(state, i.RewardAddress) + } + return nil +} + +// RewardManagerConfig implements the StatefulPrecompileConfig +// interface while adding in the RewardManager specific precompile config. +type RewardManagerConfig struct { + precompile.AllowListConfig + precompile.UpgradeableConfig + InitialRewardConfig *InitialRewardConfig `json:"initialRewardConfig,omitempty"` +} + +// NewRewardManagerConfig returns a config for a network upgrade at [blockTimestamp] that enables +// RewardManager with the given [admins] and [enableds] as members of the allowlist with [initialConfig] as initial rewards config if specified. +func NewRewardManagerConfig(blockTimestamp *big.Int, admins []common.Address, enableds []common.Address, initialConfig *InitialRewardConfig) *RewardManagerConfig { + return &RewardManagerConfig{ + AllowListConfig: precompile.AllowListConfig{ + AllowListAdmins: admins, + EnabledAddresses: enableds, + }, + UpgradeableConfig: precompile.UpgradeableConfig{BlockTimestamp: blockTimestamp}, + InitialRewardConfig: initialConfig, + } +} + +// NewDisableRewardManagerConfig returns config for a network upgrade at [blockTimestamp] +// that disables RewardManager. +func NewDisableRewardManagerConfig(blockTimestamp *big.Int) *RewardManagerConfig { + return &RewardManagerConfig{ + UpgradeableConfig: precompile.UpgradeableConfig{ + BlockTimestamp: blockTimestamp, + Disable: true, + }, + } +} + +// Equal returns true if [s] is a [*RewardManagerConfig] and it has been configured identical to [c]. +func (c *RewardManagerConfig) Equal(s precompile.StatefulPrecompileConfig) bool { + // typecast before comparison + other, ok := (s).(*RewardManagerConfig) + if !ok { + return false + } + // modify this boolean accordingly with your custom RewardManagerConfig, to check if [other] and the current [c] are equal + // if RewardManagerConfig contains only UpgradeableConfig and precompile.AllowListConfig you can skip modifying it. + equals := c.UpgradeableConfig.Equal(&other.UpgradeableConfig) && c.AllowListConfig.Equal(&other.AllowListConfig) + if !equals { + return false + } + + if c.InitialRewardConfig == nil { + return other.InitialRewardConfig == nil + } + + return c.InitialRewardConfig.Equal(other.InitialRewardConfig) +} + +// Address returns the address of the RewardManager. Addresses reside under the precompile/params.go +// Select a non-conflicting address and set it in the params.go. +func (c *RewardManagerConfig) Address() common.Address { + return precompile.RewardManagerAddress +} + +// Configure configures [state] with the initial configuration. +func (c *RewardManagerConfig) Configure(chainConfig precompile.ChainConfig, state precompile.StateDB, _ precompile.BlockContext) error { + c.AllowListConfig.Configure(state, precompile.RewardManagerAddress) + // configure the RewardManager with the given initial configuration + if c.InitialRewardConfig != nil { + return c.InitialRewardConfig.Configure(state) + } else if chainConfig.AllowedFeeRecipients() { + // configure the RewardManager according to chainConfig + EnableAllowFeeRecipients(state) + } else { + // chainConfig does not have any reward address + // if chainConfig does not enable fee recipients + // default to disabling rewards + DisableFeeRewards(state) + } + return nil +} + +// Contract returns the singleton stateful precompiled contract to be used for RewardManager. +func (c *RewardManagerConfig) Contract() precompile.StatefulPrecompiledContract { + return RewardManagerPrecompile +} + +func (c *RewardManagerConfig) Verify() error { + if err := c.AllowListConfig.Verify(); err != nil { + return err + } + if c.InitialRewardConfig != nil { + return c.InitialRewardConfig.Verify() + } + return nil +} + +// String returns a string representation of the RewardManagerConfig. +func (c *RewardManagerConfig) String() string { + bytes, _ := json.Marshal(c) + return string(bytes) +} diff --git a/precompile/rewardmanager/config_test.go b/precompile/rewardmanager/config_test.go new file mode 100644 index 0000000000..7e5fb5c19f --- /dev/null +++ b/precompile/rewardmanager/config_test.go @@ -0,0 +1,121 @@ +// (c) 2022 Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package rewardmanager + +import ( + "math/big" + "testing" + + "github.com/ava-labs/subnet-evm/precompile" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestVerifyRewardManagerConfig(t *testing.T) { + admins := []common.Address{{1}} + enableds := []common.Address{{2}} + tests := []struct { + name string + config precompile.StatefulPrecompileConfig + expectedError string + }{ + { + name: "duplicate enableds in config in reward manager allowlist", + config: NewRewardManagerConfig(big.NewInt(3), admins, append(enableds, enableds[0]), nil), + expectedError: "duplicate address", + }, + { + name: "both reward mechanisms should not be activated at the same time in reward manager", + config: NewRewardManagerConfig(big.NewInt(3), admins, enableds, &InitialRewardConfig{ + AllowFeeRecipients: true, + RewardAddress: common.HexToAddress("0x01"), + }), + expectedError: ErrCannotEnableBothRewards.Error(), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + + err := tt.config.Verify() + if tt.expectedError == "" { + require.NoError(err) + } else { + require.ErrorContains(err, tt.expectedError) + } + }) + } +} + +func TestEqualRewardManagerConfig(t *testing.T) { + admins := []common.Address{{1}} + enableds := []common.Address{{2}} + tests := []struct { + name string + config precompile.StatefulPrecompileConfig + other precompile.StatefulPrecompileConfig + expected bool + }{ + { + name: "non-nil config and nil other", + config: NewRewardManagerConfig(big.NewInt(3), admins, enableds, nil), + other: nil, + expected: false, + }, + { + name: "different type", + config: NewRewardManagerConfig(big.NewInt(3), admins, enableds, nil), + other: precompile.NewNoopStatefulPrecompileConfig(), + expected: false, + }, + { + name: "different timestamp", + config: NewRewardManagerConfig(big.NewInt(3), admins, nil, nil), + other: NewRewardManagerConfig(big.NewInt(4), admins, nil, nil), + expected: false, + }, + { + name: "different enabled", + config: NewRewardManagerConfig(big.NewInt(3), admins, nil, nil), + other: NewRewardManagerConfig(big.NewInt(3), admins, enableds, nil), + expected: false, + }, + { + name: "non-nil initial config and nil initial config", + config: NewRewardManagerConfig(big.NewInt(3), admins, nil, &InitialRewardConfig{ + AllowFeeRecipients: true, + }), + other: NewRewardManagerConfig(big.NewInt(3), admins, nil, nil), + expected: false, + }, + { + name: "different initial config", + config: NewRewardManagerConfig(big.NewInt(3), admins, nil, &InitialRewardConfig{ + RewardAddress: common.HexToAddress("0x01"), + }), + other: NewRewardManagerConfig(big.NewInt(3), admins, nil, + &InitialRewardConfig{ + RewardAddress: common.HexToAddress("0x02"), + }), + expected: false, + }, + { + name: "same config", + config: NewRewardManagerConfig(big.NewInt(3), admins, nil, &InitialRewardConfig{ + RewardAddress: common.HexToAddress("0x01"), + }), + other: NewRewardManagerConfig(big.NewInt(3), admins, nil, &InitialRewardConfig{ + RewardAddress: common.HexToAddress("0x01"), + }), + expected: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + + require.Equal(tt.expected, tt.config.Equal(tt.other)) + }) + } +} diff --git a/precompile/reward_manager.abi b/precompile/rewardmanager/contract.abi similarity index 100% rename from precompile/reward_manager.abi rename to precompile/rewardmanager/contract.abi diff --git a/precompile/reward_manager.go b/precompile/rewardmanager/contract.go similarity index 52% rename from precompile/reward_manager.go rename to precompile/rewardmanager/contract.go index 7262efa0b8..b7b13aba94 100644 --- a/precompile/reward_manager.go +++ b/precompile/rewardmanager/contract.go @@ -4,17 +4,16 @@ // Code generated // This file is a generated precompile contract with stubbed abstract functions. -package precompile +package rewardmanager import ( - "encoding/json" "errors" "fmt" - "math/big" "strings" "github.com/ava-labs/subnet-evm/accounts/abi" "github.com/ava-labs/subnet-evm/constants" + "github.com/ava-labs/subnet-evm/precompile" "github.com/ava-labs/subnet-evm/vmerrs" _ "embed" @@ -23,17 +22,15 @@ import ( ) const ( - AllowFeeRecipientsGasCost uint64 = (writeGasCostPerSlot) + ReadAllowListGasCost // write 1 slot + read allow list - AreFeeRecipientsAllowedGasCost uint64 = readGasCostPerSlot - CurrentRewardAddressGasCost uint64 = readGasCostPerSlot - DisableRewardsGasCost uint64 = (writeGasCostPerSlot) + ReadAllowListGasCost // write 1 slot + read allow list - SetRewardAddressGasCost uint64 = (writeGasCostPerSlot) + ReadAllowListGasCost // write 1 slot + read allow list + AllowFeeRecipientsGasCost uint64 = (precompile.WriteGasCostPerSlot) + precompile.ReadAllowListGasCost // write 1 slot + read allow list + AreFeeRecipientsAllowedGasCost uint64 = precompile.ReadAllowListGasCost + CurrentRewardAddressGasCost uint64 = precompile.ReadAllowListGasCost + DisableRewardsGasCost uint64 = (precompile.WriteGasCostPerSlot) + precompile.ReadAllowListGasCost // write 1 slot + read allow list + SetRewardAddressGasCost uint64 = (precompile.WriteGasCostPerSlot) + precompile.ReadAllowListGasCost // write 1 slot + read allow list ) // Singleton StatefulPrecompiledContract and signatures. var ( - _ StatefulPrecompileConfig = &RewardManagerConfig{} - ErrCannotAllowFeeRecipients = errors.New("non-enabled cannot call allowFeeRecipients") ErrCannotAreFeeRecipientsAllowed = errors.New("non-enabled cannot call areFeeRecipientsAllowed") ErrCannotCurrentRewardAddress = errors.New("non-enabled cannot call currentRewardAddress") @@ -44,172 +41,37 @@ var ( ErrEmptyRewardAddress = errors.New("reward address cannot be empty") // RewardManagerRawABI contains the raw ABI of RewardManager contract. - //go:embed reward_manager.abi + //go:embed contract.abi RewardManagerRawABI string - RewardManagerABI abi.ABI // will be initialized by init function - RewardManagerPrecompile StatefulPrecompiledContract // will be initialized by init function + RewardManagerABI abi.ABI // will be initialized by init function + RewardManagerPrecompile precompile.StatefulPrecompiledContract // will be initialized by init function rewardAddressStorageKey = common.Hash{'r', 'a', 's', 'k'} allowFeeRecipientsAddressValue = common.Hash{'a', 'f', 'r', 'a', 'v'} ) -type InitialRewardConfig struct { - AllowFeeRecipients bool `json:"allowFeeRecipients"` - RewardAddress common.Address `json:"rewardAddress,omitempty"` -} - -func (i *InitialRewardConfig) Verify() error { - switch { - case i.AllowFeeRecipients && i.RewardAddress != (common.Address{}): - return ErrCannotEnableBothRewards - default: - return nil - } -} - -func (c *InitialRewardConfig) Equal(other *InitialRewardConfig) bool { - if other == nil { - return false - } - - return c.AllowFeeRecipients == other.AllowFeeRecipients && c.RewardAddress == other.RewardAddress -} - -func (i *InitialRewardConfig) Configure(state StateDB) error { - // enable allow fee recipients - if i.AllowFeeRecipients { - EnableAllowFeeRecipients(state) - } else if i.RewardAddress == (common.Address{}) { - // if reward address is empty and allow fee recipients is false - // then disable rewards - DisableFeeRewards(state) - } else { - // set reward address - return StoreRewardAddress(state, i.RewardAddress) - } - return nil -} - -// RewardManagerConfig implements the StatefulPrecompileConfig -// interface while adding in the RewardManager specific precompile config. -type RewardManagerConfig struct { - AllowListConfig - UpgradeableConfig - InitialRewardConfig *InitialRewardConfig `json:"initialRewardConfig,omitempty"` -} - func init() { parsed, err := abi.JSON(strings.NewReader(RewardManagerRawABI)) if err != nil { panic(err) } RewardManagerABI = parsed - RewardManagerPrecompile, err = createRewardManagerPrecompile(RewardManagerAddress) + RewardManagerPrecompile, err = createRewardManagerPrecompile(precompile.RewardManagerAddress) if err != nil { panic(err) } } -// NewRewardManagerConfig returns a config for a network upgrade at [blockTimestamp] that enables -// RewardManager with the given [admins] and [enableds] as members of the allowlist with [initialConfig] as initial rewards config if specified. -func NewRewardManagerConfig(blockTimestamp *big.Int, admins []common.Address, enableds []common.Address, initialConfig *InitialRewardConfig) *RewardManagerConfig { - return &RewardManagerConfig{ - AllowListConfig: AllowListConfig{ - AllowListAdmins: admins, - EnabledAddresses: enableds, - }, - UpgradeableConfig: UpgradeableConfig{BlockTimestamp: blockTimestamp}, - InitialRewardConfig: initialConfig, - } -} - -// NewDisableRewardManagerConfig returns config for a network upgrade at [blockTimestamp] -// that disables RewardManager. -func NewDisableRewardManagerConfig(blockTimestamp *big.Int) *RewardManagerConfig { - return &RewardManagerConfig{ - UpgradeableConfig: UpgradeableConfig{ - BlockTimestamp: blockTimestamp, - Disable: true, - }, - } -} - -// Equal returns true if [s] is a [*RewardManagerConfig] and it has been configured identical to [c]. -func (c *RewardManagerConfig) Equal(s StatefulPrecompileConfig) bool { - // typecast before comparison - other, ok := (s).(*RewardManagerConfig) - if !ok { - return false - } - // modify this boolean accordingly with your custom RewardManagerConfig, to check if [other] and the current [c] are equal - // if RewardManagerConfig contains only UpgradeableConfig and AllowListConfig you can skip modifying it. - equals := c.UpgradeableConfig.Equal(&other.UpgradeableConfig) && c.AllowListConfig.Equal(&other.AllowListConfig) - if !equals { - return false - } - - if c.InitialRewardConfig == nil { - return other.InitialRewardConfig == nil - } - - return c.InitialRewardConfig.Equal(other.InitialRewardConfig) -} - -// Address returns the address of the RewardManager. Addresses reside under the precompile/params.go -// Select a non-conflicting address and set it in the params.go. -func (c *RewardManagerConfig) Address() common.Address { - return RewardManagerAddress -} - -// Configure configures [state] with the initial configuration. -func (c *RewardManagerConfig) Configure(chainConfig ChainConfig, state StateDB, _ BlockContext) error { - c.AllowListConfig.Configure(state, RewardManagerAddress) - // configure the RewardManager with the given initial configuration - if c.InitialRewardConfig != nil { - return c.InitialRewardConfig.Configure(state) - } else if chainConfig.AllowedFeeRecipients() { - // configure the RewardManager according to chainConfig - EnableAllowFeeRecipients(state) - } else { - // chainConfig does not have any reward address - // if chainConfig does not enable fee recipients - // default to disabling rewards - DisableFeeRewards(state) - } - return nil -} - -// Contract returns the singleton stateful precompiled contract to be used for RewardManager. -func (c *RewardManagerConfig) Contract() StatefulPrecompiledContract { - return RewardManagerPrecompile -} - -func (c *RewardManagerConfig) Verify() error { - if err := c.AllowListConfig.Verify(); err != nil { - return err - } - if c.InitialRewardConfig != nil { - return c.InitialRewardConfig.Verify() - } - return nil -} - -// String returns a string representation of the RewardManagerConfig. -func (c *RewardManagerConfig) String() string { - bytes, _ := json.Marshal(c) - return string(bytes) -} - // GetRewardManagerAllowListStatus returns the role of [address] for the RewardManager list. -func GetRewardManagerAllowListStatus(stateDB StateDB, address common.Address) AllowListRole { - return getAllowListStatus(stateDB, RewardManagerAddress, address) +func GetRewardManagerAllowListStatus(stateDB precompile.StateDB, address common.Address) precompile.AllowListRole { + return precompile.GetAllowListStatus(stateDB, precompile.RewardManagerAddress, address) } // SetRewardManagerAllowListStatus sets the permissions of [address] to [role] for the // RewardManager list. Assumes [role] has already been verified as valid. -func SetRewardManagerAllowListStatus(stateDB StateDB, address common.Address, role AllowListRole) { - setAllowListRole(stateDB, RewardManagerAddress, address, role) +func SetRewardManagerAllowListStatus(stateDB precompile.StateDB, address common.Address, role precompile.AllowListRole) { + precompile.SetAllowListRole(stateDB, precompile.RewardManagerAddress, address, role) } // PackAllowFeeRecipients packs the function selector (first 4 func signature bytes). @@ -219,17 +81,17 @@ func PackAllowFeeRecipients() ([]byte, error) { } // EnableAllowFeeRecipients enables fee recipients. -func EnableAllowFeeRecipients(stateDB StateDB) { - stateDB.SetState(RewardManagerAddress, rewardAddressStorageKey, allowFeeRecipientsAddressValue) +func EnableAllowFeeRecipients(stateDB precompile.StateDB) { + stateDB.SetState(precompile.RewardManagerAddress, rewardAddressStorageKey, allowFeeRecipientsAddressValue) } // DisableRewardAddress disables rewards and burns them by sending to Blackhole Address. -func DisableFeeRewards(stateDB StateDB) { - stateDB.SetState(RewardManagerAddress, rewardAddressStorageKey, constants.BlackholeAddr.Hash()) +func DisableFeeRewards(stateDB precompile.StateDB) { + stateDB.SetState(precompile.RewardManagerAddress, rewardAddressStorageKey, constants.BlackholeAddr.Hash()) } -func allowFeeRecipients(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { - if remainingGas, err = deductGas(suppliedGas, AllowFeeRecipientsGasCost); err != nil { +func allowFeeRecipients(accessibleState precompile.PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { + if remainingGas, err = precompile.DeductGas(suppliedGas, AllowFeeRecipientsGasCost); err != nil { return nil, 0, err } if readOnly { @@ -242,7 +104,7 @@ func allowFeeRecipients(accessibleState PrecompileAccessibleState, caller common // 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 := getAllowListStatus(stateDB, RewardManagerAddress, caller) + callerStatus := precompile.GetAllowListStatus(stateDB, precompile.RewardManagerAddress, caller) if !callerStatus.IsEnabled() { return nil, remainingGas, fmt.Errorf("%w: %s", ErrCannotAllowFeeRecipients, caller) } @@ -268,8 +130,8 @@ func PackAreFeeRecipientsAllowedOutput(isAllowed bool) ([]byte, error) { return RewardManagerABI.PackOutput("areFeeRecipientsAllowed", isAllowed) } -func areFeeRecipientsAllowed(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { - if remainingGas, err = deductGas(suppliedGas, AreFeeRecipientsAllowedGasCost); err != nil { +func areFeeRecipientsAllowed(accessibleState precompile.PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { + if remainingGas, err = precompile.DeductGas(suppliedGas, AreFeeRecipientsAllowedGasCost); err != nil { return nil, 0, err } // no input provided for this function @@ -301,18 +163,18 @@ func PackCurrentRewardAddressOutput(rewardAddress common.Address) ([]byte, error // GetStoredRewardAddress returns the current value of the address stored under rewardAddressStorageKey. // Returns an empty address and true if allow fee recipients is enabled, otherwise returns current reward address and false. -func GetStoredRewardAddress(stateDB StateDB) (common.Address, bool) { - val := stateDB.GetState(RewardManagerAddress, rewardAddressStorageKey) +func GetStoredRewardAddress(stateDB precompile.StateDB) (common.Address, bool) { + val := stateDB.GetState(precompile.RewardManagerAddress, rewardAddressStorageKey) return common.BytesToAddress(val.Bytes()), val == allowFeeRecipientsAddressValue } // StoredRewardAddress stores the given [val] under rewardAddressStorageKey. -func StoreRewardAddress(stateDB StateDB, val common.Address) error { +func StoreRewardAddress(stateDB precompile.StateDB, val common.Address) error { // if input is empty, return an error if val == (common.Address{}) { return ErrEmptyRewardAddress } - stateDB.SetState(RewardManagerAddress, rewardAddressStorageKey, val.Hash()) + stateDB.SetState(precompile.RewardManagerAddress, rewardAddressStorageKey, val.Hash()) return nil } @@ -334,8 +196,8 @@ func UnpackSetRewardAddressInput(input []byte) (common.Address, error) { return unpacked, nil } -func setRewardAddress(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { - if remainingGas, err = deductGas(suppliedGas, SetRewardAddressGasCost); err != nil { +func setRewardAddress(accessibleState precompile.PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { + if remainingGas, err = precompile.DeductGas(suppliedGas, SetRewardAddressGasCost); err != nil { return nil, 0, err } if readOnly { @@ -354,7 +216,7 @@ func setRewardAddress(accessibleState PrecompileAccessibleState, caller common.A // 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 := getAllowListStatus(stateDB, RewardManagerAddress, caller) + callerStatus := precompile.GetAllowListStatus(stateDB, precompile.RewardManagerAddress, caller) if !callerStatus.IsEnabled() { return nil, remainingGas, fmt.Errorf("%w: %s", ErrCannotSetRewardAddress, caller) } @@ -370,8 +232,8 @@ func setRewardAddress(accessibleState PrecompileAccessibleState, caller common.A return packedOutput, remainingGas, nil } -func currentRewardAddress(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { - if remainingGas, err = deductGas(suppliedGas, CurrentRewardAddressGasCost); err != nil { +func currentRewardAddress(accessibleState precompile.PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { + if remainingGas, err = precompile.DeductGas(suppliedGas, CurrentRewardAddressGasCost); err != nil { return nil, 0, err } @@ -393,8 +255,8 @@ func PackDisableRewards() ([]byte, error) { return RewardManagerABI.Pack("disableRewards") } -func disableRewards(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { - if remainingGas, err = deductGas(suppliedGas, DisableRewardsGasCost); err != nil { +func disableRewards(accessibleState precompile.PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { + if remainingGas, err = precompile.DeductGas(suppliedGas, DisableRewardsGasCost); err != nil { return nil, 0, err } if readOnly { @@ -407,7 +269,7 @@ func disableRewards(accessibleState PrecompileAccessibleState, caller common.Add // 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 := getAllowListStatus(stateDB, RewardManagerAddress, caller) + callerStatus := precompile.GetAllowListStatus(stateDB, precompile.RewardManagerAddress, caller) if !callerStatus.IsEnabled() { return nil, remainingGas, fmt.Errorf("%w: %s", ErrCannotDisableRewards, caller) } @@ -422,10 +284,10 @@ func disableRewards(accessibleState PrecompileAccessibleState, caller common.Add // createRewardManagerPrecompile returns a StatefulPrecompiledContract with getters and setters for the precompile. // Access to the getters/setters is controlled by an allow list for [precompileAddr]. -func createRewardManagerPrecompile(precompileAddr common.Address) (StatefulPrecompiledContract, error) { - var functions []*statefulPrecompileFunction - functions = append(functions, createAllowListFunctions(precompileAddr)...) - abiFunctionMap := map[string]RunStatefulPrecompileFunc{ +func createRewardManagerPrecompile(precompileAddr common.Address) (precompile.StatefulPrecompiledContract, error) { + var functions []*precompile.StatefulPrecompileFunction + functions = append(functions, precompile.CreateAllowListFunctions(precompileAddr)...) + abiFunctionMap := map[string]precompile.RunStatefulPrecompileFunc{ "allowFeeRecipients": allowFeeRecipients, "areFeeRecipientsAllowed": areFeeRecipientsAllowed, "currentRewardAddress": currentRewardAddress, @@ -438,9 +300,9 @@ func createRewardManagerPrecompile(precompileAddr common.Address) (StatefulPreco if !ok { return nil, fmt.Errorf("given method (%s) does not exist in the ABI", name) } - functions = append(functions, newStatefulPrecompileFunction(method.ID, function)) + functions = append(functions, precompile.NewStatefulPrecompileFunction(method.ID, function)) } // Construct the contract with no fallback function. - return NewStatefulPrecompileContract(nil, functions) + return precompile.NewStatefulPrecompileContract(nil, functions) } diff --git a/precompile/stateful_precompile_config.go b/precompile/stateful_precompile_config.go index 6955b8d448..3a293cbb5f 100644 --- a/precompile/stateful_precompile_config.go +++ b/precompile/stateful_precompile_config.go @@ -23,6 +23,8 @@ type StatefulPrecompileConfig interface { IsDisabled() bool // Equal returns true if the provided argument configures the same precompile with the same parameters. Equal(StatefulPrecompileConfig) bool + // Verify is called on startup and an error is treated as fatal. Configure can assume the Config has passed verification. + Verify() error // Configure is called on the first block where the stateful precompile should be enabled. // This allows the stateful precompile to configure its own state via [StateDB] and [BlockContext] as necessary. // This function must be deterministic since it will impact the EVM state. If a change to the @@ -36,8 +38,6 @@ type StatefulPrecompileConfig interface { // Contract returns a thread-safe singleton that can be used as the StatefulPrecompiledContract when // this config is enabled. Contract() StatefulPrecompiledContract - // Verify is called on startup and an error is treated as fatal. Configure can assume the Config has passed verification. - Verify() error fmt.Stringer } diff --git a/precompile/tx_allow_list.go b/precompile/txallowlist/config.go similarity index 57% rename from precompile/tx_allow_list.go rename to precompile/txallowlist/config.go index 4ab39a8f04..0551835405 100644 --- a/precompile/tx_allow_list.go +++ b/precompile/txallowlist/config.go @@ -1,40 +1,36 @@ // (c) 2019-2020, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package precompile +package txallowlist import ( "encoding/json" - "errors" "math/big" + "github.com/ava-labs/subnet-evm/precompile" "github.com/ethereum/go-ethereum/common" ) var ( - _ StatefulPrecompileConfig = &TxAllowListConfig{} - // Singleton StatefulPrecompiledContract for W/R access to the contract deployer allow list. - TxAllowListPrecompile StatefulPrecompiledContract = createAllowListPrecompile(TxAllowListAddress) - - ErrSenderAddressNotAllowListed = errors.New("cannot issue transaction from non-allow listed address") + _ precompile.StatefulPrecompileConfig = &TxAllowListConfig{} ) // TxAllowListConfig wraps [AllowListConfig] and uses it to implement the StatefulPrecompileConfig // interface while adding in the TxAllowList specific precompile address. type TxAllowListConfig struct { - AllowListConfig - UpgradeableConfig + precompile.AllowListConfig + precompile.UpgradeableConfig } // NewTxAllowListConfig returns a config for a network upgrade at [blockTimestamp] that enables // TxAllowList with the given [admins] and [enableds] as members of the allowlist. func NewTxAllowListConfig(blockTimestamp *big.Int, admins []common.Address, enableds []common.Address) *TxAllowListConfig { return &TxAllowListConfig{ - AllowListConfig: AllowListConfig{ + AllowListConfig: precompile.AllowListConfig{ AllowListAdmins: admins, EnabledAddresses: enableds, }, - UpgradeableConfig: UpgradeableConfig{BlockTimestamp: blockTimestamp}, + UpgradeableConfig: precompile.UpgradeableConfig{BlockTimestamp: blockTimestamp}, } } @@ -42,7 +38,7 @@ func NewTxAllowListConfig(blockTimestamp *big.Int, admins []common.Address, enab // that disables TxAllowList. func NewDisableTxAllowListConfig(blockTimestamp *big.Int) *TxAllowListConfig { return &TxAllowListConfig{ - UpgradeableConfig: UpgradeableConfig{ + UpgradeableConfig: precompile.UpgradeableConfig{ BlockTimestamp: blockTimestamp, Disable: true, }, @@ -51,21 +47,21 @@ func NewDisableTxAllowListConfig(blockTimestamp *big.Int) *TxAllowListConfig { // Address returns the address of the contract deployer allow list. func (c *TxAllowListConfig) Address() common.Address { - return TxAllowListAddress + return precompile.TxAllowListAddress } // Configure configures [state] with the desired admins based on [c]. -func (c *TxAllowListConfig) Configure(_ ChainConfig, state StateDB, _ BlockContext) error { - return c.AllowListConfig.Configure(state, TxAllowListAddress) +func (c *TxAllowListConfig) Configure(_ precompile.ChainConfig, state precompile.StateDB, _ precompile.BlockContext) error { + return c.AllowListConfig.Configure(state, precompile.TxAllowListAddress) } // Contract returns the singleton stateful precompiled contract to be used for the allow list. -func (c *TxAllowListConfig) Contract() StatefulPrecompiledContract { +func (c *TxAllowListConfig) Contract() precompile.StatefulPrecompiledContract { return TxAllowListPrecompile } // Equal returns true if [s] is a [*TxAllowListConfig] and it has been configured identical to [c]. -func (c *TxAllowListConfig) Equal(s StatefulPrecompileConfig) bool { +func (c *TxAllowListConfig) Equal(s precompile.StatefulPrecompileConfig) bool { // typecast before comparison other, ok := (s).(*TxAllowListConfig) if !ok { @@ -79,16 +75,3 @@ func (c *TxAllowListConfig) String() string { bytes, _ := json.Marshal(c) return string(bytes) } - -// GetTxAllowListStatus returns the role of [address] for the contract deployer -// allow list. -func GetTxAllowListStatus(stateDB StateDB, address common.Address) AllowListRole { - return getAllowListStatus(stateDB, TxAllowListAddress, address) -} - -// SetTxAllowListStatus sets the permissions of [address] to [role] for the -// tx allow list. -// assumes [role] has already been verified as valid. -func SetTxAllowListStatus(stateDB StateDB, address common.Address, role AllowListRole) { - setAllowListRole(stateDB, TxAllowListAddress, address, role) -} diff --git a/precompile/txallowlist/config_test.go b/precompile/txallowlist/config_test.go new file mode 100644 index 0000000000..651bfa9ba2 --- /dev/null +++ b/precompile/txallowlist/config_test.go @@ -0,0 +1,105 @@ +// (c) 2019-2020, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package txallowlist + +import ( + "math/big" + "testing" + + "github.com/ava-labs/subnet-evm/precompile" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestVerifyTxAllowlistConfig(t *testing.T) { + admins := []common.Address{{1}} + enableds := []common.Address{{2}} + tests := []struct { + name string + config precompile.StatefulPrecompileConfig + expectedError string + }{ + { + name: "invalid allow list config in tx allowlist", + config: NewTxAllowListConfig(big.NewInt(3), admins, admins), + expectedError: "cannot set address", + }, + { + name: "nil member allow list config in tx allowlist", + config: NewTxAllowListConfig(big.NewInt(3), nil, nil), + expectedError: "", + }, + { + name: "empty member allow list config in tx allowlist", + config: NewTxAllowListConfig(big.NewInt(3), []common.Address{}, []common.Address{}), + expectedError: "", + }, + { + name: "valid allow list config in tx allowlist", + config: NewTxAllowListConfig(big.NewInt(3), admins, enableds), + expectedError: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + + err := tt.config.Verify() + if tt.expectedError == "" { + require.NoError(err) + } else { + require.ErrorContains(err, tt.expectedError) + } + }) + } +} + +func TestEqualTxAllowListConfig(t *testing.T) { + admins := []common.Address{{1}} + enableds := []common.Address{{2}} + tests := []struct { + name string + config precompile.StatefulPrecompileConfig + other precompile.StatefulPrecompileConfig + expected bool + }{ + { + name: "non-nil config and nil other", + config: NewTxAllowListConfig(big.NewInt(3), admins, enableds), + other: nil, + expected: false, + }, + { + name: "different admin", + config: NewTxAllowListConfig(big.NewInt(3), admins, enableds), + other: NewTxAllowListConfig(big.NewInt(3), []common.Address{{3}}, enableds), + expected: false, + }, + { + name: "different enabled", + config: NewTxAllowListConfig(big.NewInt(3), admins, enableds), + other: NewTxAllowListConfig(big.NewInt(3), admins, []common.Address{{3}}), + expected: false, + }, + { + name: "different timestamp", + config: NewTxAllowListConfig(big.NewInt(3), admins, enableds), + other: NewTxAllowListConfig(big.NewInt(4), admins, enableds), + expected: false, + }, + { + name: "same config", + config: NewTxAllowListConfig(big.NewInt(3), admins, enableds), + other: NewTxAllowListConfig(big.NewInt(3), admins, enableds), + expected: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + + require.Equal(tt.expected, tt.config.Equal(tt.other)) + }) + } +} diff --git a/precompile/txallowlist/contract.go b/precompile/txallowlist/contract.go new file mode 100644 index 0000000000..d1e9ed4b3c --- /dev/null +++ b/precompile/txallowlist/contract.go @@ -0,0 +1,32 @@ +// (c) 2019-2020, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package txallowlist + +import ( + "errors" + + "github.com/ava-labs/subnet-evm/precompile" + "github.com/ethereum/go-ethereum/common" +) + +var ( + _ precompile.StatefulPrecompileConfig = &TxAllowListConfig{} + // Singleton StatefulPrecompiledContract for W/R access to the contract deployer allow list. + TxAllowListPrecompile precompile.StatefulPrecompiledContract = precompile.CreateAllowListPrecompile(precompile.TxAllowListAddress) + + ErrSenderAddressNotAllowListed = errors.New("cannot issue transaction from non-allow listed address") +) + +// GetTxAllowListStatus returns the role of [address] for the contract deployer +// allow list. +func GetTxAllowListStatus(stateDB precompile.StateDB, address common.Address) precompile.AllowListRole { + return precompile.GetAllowListStatus(stateDB, precompile.TxAllowListAddress, address) +} + +// SetTxAllowListStatus sets the permissions of [address] to [role] for the +// tx allow list. +// assumes [role] has already been verified as valid. +func SetTxAllowListStatus(stateDB precompile.StateDB, address common.Address, role precompile.AllowListRole) { + precompile.SetAllowListRole(stateDB, precompile.TxAllowListAddress, address, role) +} diff --git a/precompile/utils.go b/precompile/utils.go index 6da328f9c8..dfbe4affd6 100644 --- a/precompile/utils.go +++ b/precompile/utils.go @@ -17,6 +17,7 @@ var functionSignatureRegex = regexp.MustCompile(`[\w]+\(((([\w]+)?)|((([\w]+),)+ // CalculateFunctionSelector returns the 4 byte function selector that results from [functionSignature] // Ex. the function setBalance(addr address, balance uint256) should be passed in as the string: // "setBalance(address,uint256)" +// TODO: remove this after moving to ABI based function selectors. func CalculateFunctionSelector(functionSignature string) []byte { if !functionSignatureRegex.MatchString(functionSignature) { panic(fmt.Errorf("invalid function signature: %q", functionSignature)) @@ -25,8 +26,8 @@ func CalculateFunctionSelector(functionSignature string) []byte { return hash[:4] } -// deductGas checks if [suppliedGas] is sufficient against [requiredGas] and deducts [requiredGas] from [suppliedGas]. -func deductGas(suppliedGas uint64, requiredGas uint64) (uint64, error) { +// DeductGas checks if [suppliedGas] is sufficient against [requiredGas] and deducts [requiredGas] from [suppliedGas]. +func DeductGas(suppliedGas uint64, requiredGas uint64) (uint64, error) { if suppliedGas < requiredGas { return 0, vmerrs.ErrOutOfGas } @@ -36,14 +37,14 @@ func deductGas(suppliedGas uint64, requiredGas uint64) (uint64, error) { // packOrderedHashesWithSelector packs the function selector and ordered list of hashes into [dst] // byte slice. // assumes that [dst] has sufficient room for [functionSelector] and [hashes]. -func packOrderedHashesWithSelector(dst []byte, functionSelector []byte, hashes []common.Hash) error { +func PackOrderedHashesWithSelector(dst []byte, functionSelector []byte, hashes []common.Hash) error { copy(dst[:len(functionSelector)], functionSelector) - return packOrderedHashes(dst[len(functionSelector):], hashes) + return PackOrderedHashes(dst[len(functionSelector):], hashes) } // packOrderedHashes packs the ordered list of [hashes] into the [dst] byte buffer. // assumes that [dst] has sufficient space to pack [hashes] or else this function will panic. -func packOrderedHashes(dst []byte, hashes []common.Hash) error { +func PackOrderedHashes(dst []byte, hashes []common.Hash) error { if len(dst) != len(hashes)*common.HashLength { return fmt.Errorf("destination byte buffer has insufficient length (%d) for %d hashes", len(dst), len(hashes)) } @@ -60,10 +61,10 @@ func packOrderedHashes(dst []byte, hashes []common.Hash) error { return nil } -// returnPackedHash returns packed the byte slice with common.HashLength from [packed] +// PackedHash returns packed the byte slice with common.HashLength from [packed] // at the given [index]. // Assumes that [packed] is composed entirely of packed 32 byte segments. -func returnPackedHash(packed []byte, index int) []byte { +func PackedHash(packed []byte, index int) []byte { start := common.HashLength * index end := start + common.HashLength return packed[start:end] diff --git a/tests/e2e/utils/evm_client.go b/tests/e2e/utils/evm_client.go index 65dbe1785f..6f8c0a3950 100644 --- a/tests/e2e/utils/evm_client.go +++ b/tests/e2e/utils/evm_client.go @@ -15,7 +15,7 @@ import ( "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/ethclient" "github.com/ava-labs/subnet-evm/params" - "github.com/ava-labs/subnet-evm/precompile" + "github.com/ava-labs/subnet-evm/precompile/txallowlist" "github.com/ethereum/go-ethereum/common" ) @@ -167,7 +167,7 @@ func (ec *EvmClient) TransferTx( if err := ec.ethClient.SendTransaction(ctx, signedTx); err != nil { log.Printf("failed to send transaction: %v (key address %s)", err, sender) - if strings.Contains(err.Error(), precompile.ErrSenderAddressNotAllowListed.Error()) { + if strings.Contains(err.Error(), txallowlist.ErrSenderAddressNotAllowListed.Error()) { return nil, err }