| 
									
										
										
										
											2019-07-22 12:17:27 +03:00
										 |  |  | // Copyright 2019 The go-ethereum Authors | 
					
						
							| 
									
										
										
										
											2019-04-11 20:01:11 +03:00
										 |  |  | // This file is part of the go-ethereum library. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							|  |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package fourbyte | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"regexp" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/accounts/abi" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // decodedCallData is an internal type to represent a method call parsed according | 
					
						
							|  |  |  | // to an ABI method signature. | 
					
						
							|  |  |  | type decodedCallData struct { | 
					
						
							|  |  |  | 	signature string | 
					
						
							|  |  |  | 	name      string | 
					
						
							|  |  |  | 	inputs    []decodedArgument | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // decodedArgument is an internal type to represent an argument parsed according | 
					
						
							|  |  |  | // to an ABI method signature. | 
					
						
							|  |  |  | type decodedArgument struct { | 
					
						
							|  |  |  | 	soltype abi.Argument | 
					
						
							|  |  |  | 	value   interface{} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // String implements stringer interface, tries to use the underlying value-type | 
					
						
							|  |  |  | func (arg decodedArgument) String() string { | 
					
						
							|  |  |  | 	var value string | 
					
						
							|  |  |  | 	switch val := arg.value.(type) { | 
					
						
							|  |  |  | 	case fmt.Stringer: | 
					
						
							|  |  |  | 		value = val.String() | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		value = fmt.Sprintf("%v", val) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return fmt.Sprintf("%v: %v", arg.soltype.Type.String(), value) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // String implements stringer interface for decodedCallData | 
					
						
							|  |  |  | func (cd decodedCallData) String() string { | 
					
						
							|  |  |  | 	args := make([]string, len(cd.inputs)) | 
					
						
							|  |  |  | 	for i, arg := range cd.inputs { | 
					
						
							|  |  |  | 		args[i] = arg.String() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return fmt.Sprintf("%s(%s)", cd.name, strings.Join(args, ",")) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // verifySelector checks whether the ABI encoded data blob matches the requested | 
					
						
							|  |  |  | // function signature. | 
					
						
							|  |  |  | func verifySelector(selector string, calldata []byte) (*decodedCallData, error) { | 
					
						
							|  |  |  | 	// Parse the selector into an ABI JSON spec | 
					
						
							|  |  |  | 	abidata, err := parseSelector(selector) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Parse the call data according to the requested selector | 
					
						
							|  |  |  | 	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(selector string) ([]byte, error) { | 
					
						
							|  |  |  | 	// Define a tiny fake ABI struct for JSON marshalling | 
					
						
							|  |  |  | 	type fakeArg struct { | 
					
						
							|  |  |  | 		Type string `json:"type"` | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	type fakeABI struct { | 
					
						
							|  |  |  | 		Name   string    `json:"name"` | 
					
						
							|  |  |  | 		Type   string    `json:"type"` | 
					
						
							|  |  |  | 		Inputs []fakeArg `json:"inputs"` | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Validate the selector and extract it's components | 
					
						
							|  |  |  | 	groups := selectorRegexp.FindStringSubmatch(selector) | 
					
						
							|  |  |  | 	if len(groups) != 3 { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("invalid selector %s (%v matches)", selector, 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}}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // parseCallData matches the provided call data against the ABI definition and | 
					
						
							|  |  |  | // returns a struct containing the actual go-typed values. | 
					
						
							|  |  |  | func parseCallData(calldata []byte, abidata string) (*decodedCallData, error) { | 
					
						
							|  |  |  | 	// Validate the call data that it has the 4byte prefix and the rest divisible by 32 bytes | 
					
						
							|  |  |  | 	if len(calldata) < 4 { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("invalid call data, incomplete method signature (%d bytes < 4)", len(calldata)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sigdata := calldata[:4] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	argdata := calldata[4:] | 
					
						
							|  |  |  | 	if len(argdata)%32 != 0 { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("invalid call data; length should be a multiple of 32 bytes (was %d)", len(argdata)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Validate the called method and upack the call data accordingly | 
					
						
							|  |  |  | 	abispec, err := abi.JSON(strings.NewReader(abidata)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("invalid method signature (%s): %v", abidata, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	method, err := abispec.MethodById(sigdata) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	values, err := method.Inputs.UnpackValues(argdata) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Everything valid, assemble the call infos for the signer | 
					
						
							| 
									
										
										
										
											2019-08-02 15:20:46 +08:00
										 |  |  | 	decoded := decodedCallData{signature: method.Sig(), name: method.RawName} | 
					
						
							| 
									
										
										
										
											2019-04-11 20:01:11 +03:00
										 |  |  | 	for i := 0; i < len(method.Inputs); i++ { | 
					
						
							|  |  |  | 		decoded.inputs = append(decoded.inputs, decodedArgument{ | 
					
						
							|  |  |  | 			soltype: method.Inputs[i], | 
					
						
							|  |  |  | 			value:   values[i], | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// We're finished decoding the data. At this point, we encode the decoded data | 
					
						
							|  |  |  | 	// to see if it matches with the original data. If we didn't do that, it would | 
					
						
							|  |  |  | 	// be possible to stuff extra data into the arguments, which is not detected | 
					
						
							|  |  |  | 	// by merely decoding the data. | 
					
						
							|  |  |  | 	encoded, err := method.Inputs.PackValues(values) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !bytes.Equal(encoded, argdata) { | 
					
						
							|  |  |  | 		was := common.Bytes2Hex(encoded) | 
					
						
							|  |  |  | 		exp := common.Bytes2Hex(argdata) | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("WARNING: Supplied data is stuffed with extra data. \nWant %s\nHave %s\nfor method %v", exp, was, method.Sig()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return &decoded, nil | 
					
						
							|  |  |  | } |