signer/fourbyte: add support for nested types in selectors (#24407)

This replaces the simple selector parser in signer/fourbyte with one that
can actually handle most types. The new parser is added in accounts/abi
to also make it useable elsewhere.
This commit is contained in:
Daniel Perez
2022-03-04 13:39:09 +01:00
committed by GitHub
parent 8fddf27a98
commit 37f9d25ba0
3 changed files with 210 additions and 32 deletions

View File

@ -20,7 +20,6 @@ import (
"bytes"
"encoding/json"
"fmt"
"regexp"
"strings"
"github.com/ethereum/go-ethereum/accounts/abi"
@ -75,42 +74,15 @@ func verifySelector(selector string, calldata []byte) (*decodedCallData, error)
return parseCallData(calldata, string(abidata))
}
// selectorRegexp is used to validate that a 4byte database selector corresponds
// to a valid ABI function declaration.
//
// Note, although uppercase letters are not part of the ABI spec, this regexp
// still accepts it as the general format is valid. It will be rejected later
// by the type checker.
var selectorRegexp = regexp.MustCompile(`^([^\)]+)\(([A-Za-z0-9,\[\]]*)\)`)
// parseSelector converts a method selector into an ABI JSON spec. The returned
// data is a valid JSON string which can be consumed by the standard abi package.
func parseSelector(unescapedSelector string) ([]byte, error) {
// Define a tiny fake ABI struct for JSON marshalling
type fakeArg struct {
Type string `json:"type"`
selector, err := abi.ParseSelector(unescapedSelector)
if err != nil {
return nil, fmt.Errorf("failed to parse selector: %v", err)
}
type fakeABI struct {
Name string `json:"name"`
Type string `json:"type"`
Inputs []fakeArg `json:"inputs"`
}
// Validate the unescapedSelector and extract it's components
groups := selectorRegexp.FindStringSubmatch(unescapedSelector)
if len(groups) != 3 {
return nil, fmt.Errorf("invalid selector %q (%v matches)", unescapedSelector, len(groups))
}
name := groups[1]
args := groups[2]
// Reassemble the fake ABI and constuct the JSON
arguments := make([]fakeArg, 0)
if len(args) > 0 {
for _, arg := range strings.Split(args, ",") {
arguments = append(arguments, fakeArg{arg})
}
}
return json.Marshal([]fakeABI{{name, "function", arguments}})
return json.Marshal([]abi.SelectorMarshaling{selector})
}
// parseCallData matches the provided call data against the ABI definition and