Skip to content

Commit

Permalink
Precompile Specific Packages (#420)
Browse files Browse the repository at this point in the history
* rework on panics in precompiles

* Update precompile/allow_list.go

Co-authored-by: aaronbuchwald <aaron.buchwald56@gmail.com>

* Update precompile/fee_config_manager.go

Co-authored-by: aaronbuchwald <aaron.buchwald56@gmail.com>

* Update precompile/fee_config_manager.go

Co-authored-by: aaronbuchwald <aaron.buchwald56@gmail.com>

* fix reviews

* wrap errors in ConfigurePrecompiles

* cleaner errors

* move reward manager precompile to package (WIP)

* rename files

* fix abi path

* move typecheck

* move precompiles to their own packages

* refactor precompile template

* remove test file

* upate comments

* rm test files

* new allowlist package

* Update precompile/utils.go

Co-authored-by: Darioush Jalali <darioush.jalali@avalabs.org>

* Update precompile/nativeminter/contract_native_minter.go

Co-authored-by: Darioush Jalali <darioush.jalali@avalabs.org>

* Update precompile/nativeminter/contract_native_minter.go

Co-authored-by: Darioush Jalali <darioush.jalali@avalabs.org>

* Update precompile/utils.go

Co-authored-by: Darioush Jalali <darioush.jalali@avalabs.org>

* Update precompile/nativeminter/contract_native_minter.go

Co-authored-by: Darioush Jalali <darioush.jalali@avalabs.org>

* fix nits

Co-authored-by: aaronbuchwald <aaron.buchwald56@gmail.com>
Co-authored-by: Darioush Jalali <darioush.jalali@avalabs.org>
  • Loading branch information
3 people committed Jan 6, 2023
1 parent 9e5c8c4 commit 8868a99
Show file tree
Hide file tree
Showing 47 changed files with 2,167 additions and 1,689 deletions.
4 changes: 2 additions & 2 deletions accounts/abi/bind/bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down
26 changes: 20 additions & 6 deletions accounts/abi/bind/precompile_bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion accounts/abi/bind/precompile_bind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
151 changes: 151 additions & 0 deletions accounts/abi/bind/precompile_config_template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// (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"
)
{{$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)
}
`
Loading

0 comments on commit 8868a99

Please sign in to comment.