github.com/daeglee/go-ethereum@v0.0.0-20190504220456-cad3e8d18e9b/signer/core/abihelper.go (about)

     1  // Copyright 2018 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // go-ethereum is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package core
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	"fmt"
    23  	"io/ioutil"
    24  	"os"
    25  	"regexp"
    26  	"strings"
    27  
    28  	"github.com/ethereum/go-ethereum/accounts/abi"
    29  	"github.com/ethereum/go-ethereum/common"
    30  )
    31  
    32  type decodedArgument struct {
    33  	soltype abi.Argument
    34  	value   interface{}
    35  }
    36  type decodedCallData struct {
    37  	signature string
    38  	name      string
    39  	inputs    []decodedArgument
    40  }
    41  
    42  // String implements stringer interface, tries to use the underlying value-type
    43  func (arg decodedArgument) String() string {
    44  	var value string
    45  	switch val := arg.value.(type) {
    46  	case fmt.Stringer:
    47  		value = val.String()
    48  	default:
    49  		value = fmt.Sprintf("%v", val)
    50  	}
    51  	return fmt.Sprintf("%v: %v", arg.soltype.Type.String(), value)
    52  }
    53  
    54  // String implements stringer interface for decodedCallData
    55  func (cd decodedCallData) String() string {
    56  	args := make([]string, len(cd.inputs))
    57  	for i, arg := range cd.inputs {
    58  		args[i] = arg.String()
    59  	}
    60  	return fmt.Sprintf("%s(%s)", cd.name, strings.Join(args, ","))
    61  }
    62  
    63  // parseCallData matches the provided call data against the abi definition,
    64  // and returns a struct containing the actual go-typed values
    65  func parseCallData(calldata []byte, abidata string) (*decodedCallData, error) {
    66  
    67  	if len(calldata) < 4 {
    68  		return nil, fmt.Errorf("Invalid ABI-data, incomplete method signature of (%d bytes)", len(calldata))
    69  	}
    70  
    71  	sigdata, argdata := calldata[:4], calldata[4:]
    72  	if len(argdata)%32 != 0 {
    73  		return nil, fmt.Errorf("Not ABI-encoded data; length should be a multiple of 32 (was %d)", len(argdata))
    74  	}
    75  
    76  	abispec, err := abi.JSON(strings.NewReader(abidata))
    77  	if err != nil {
    78  		return nil, fmt.Errorf("Failed parsing JSON ABI: %v, abidata: %v", err, abidata)
    79  	}
    80  
    81  	method, err := abispec.MethodById(sigdata)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	v, err := method.Inputs.UnpackValues(argdata)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  
    91  	decoded := decodedCallData{signature: method.Sig(), name: method.Name}
    92  
    93  	for n, argument := range method.Inputs {
    94  		if err != nil {
    95  			return nil, fmt.Errorf("Failed to decode argument %d (signature %v): %v", n, method.Sig(), err)
    96  		}
    97  		decodedArg := decodedArgument{
    98  			soltype: argument,
    99  			value:   v[n],
   100  		}
   101  		decoded.inputs = append(decoded.inputs, decodedArg)
   102  	}
   103  
   104  	// We're finished decoding the data. At this point, we encode the decoded data to see if it matches with the
   105  	// original data. If we didn't do that, it would e.g. be possible to stuff extra data into the arguments, which
   106  	// is not detected by merely decoding the data.
   107  
   108  	var (
   109  		encoded []byte
   110  	)
   111  	encoded, err = method.Inputs.PackValues(v)
   112  
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  
   117  	if !bytes.Equal(encoded, argdata) {
   118  		was := common.Bytes2Hex(encoded)
   119  		exp := common.Bytes2Hex(argdata)
   120  		return nil, fmt.Errorf("WARNING: Supplied data is stuffed with extra data. \nWant %s\nHave %s\nfor method %v", exp, was, method.Sig())
   121  	}
   122  	return &decoded, nil
   123  }
   124  
   125  // MethodSelectorToAbi converts a method selector into an ABI struct. The returned data is a valid json string
   126  // which can be consumed by the standard abi package.
   127  func MethodSelectorToAbi(selector string) ([]byte, error) {
   128  
   129  	re := regexp.MustCompile(`^([^\)]+)\(([a-z0-9,\[\]]*)\)`)
   130  
   131  	type fakeArg struct {
   132  		Type string `json:"type"`
   133  	}
   134  	type fakeABI struct {
   135  		Name   string    `json:"name"`
   136  		Type   string    `json:"type"`
   137  		Inputs []fakeArg `json:"inputs"`
   138  	}
   139  	groups := re.FindStringSubmatch(selector)
   140  	if len(groups) != 3 {
   141  		return nil, fmt.Errorf("Did not match: %v (%v matches)", selector, len(groups))
   142  	}
   143  	name := groups[1]
   144  	args := groups[2]
   145  	arguments := make([]fakeArg, 0)
   146  	if len(args) > 0 {
   147  		for _, arg := range strings.Split(args, ",") {
   148  			arguments = append(arguments, fakeArg{arg})
   149  		}
   150  	}
   151  	abicheat := fakeABI{
   152  		name, "function", arguments,
   153  	}
   154  	return json.Marshal([]fakeABI{abicheat})
   155  
   156  }
   157  
   158  type AbiDb struct {
   159  	db           map[string]string
   160  	customdb     map[string]string
   161  	customdbPath string
   162  }
   163  
   164  // NewEmptyAbiDB exists for test purposes
   165  func NewEmptyAbiDB() (*AbiDb, error) {
   166  	return &AbiDb{make(map[string]string), make(map[string]string), ""}, nil
   167  }
   168  
   169  // NewAbiDBFromFile loads signature database from file, and
   170  // errors if the file is not valid json. Does no other validation of contents
   171  func NewAbiDBFromFile(path string) (*AbiDb, error) {
   172  	raw, err := ioutil.ReadFile(path)
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  	db, err := NewEmptyAbiDB()
   177  	if err != nil {
   178  		return nil, err
   179  	}
   180  	if err := json.Unmarshal(raw, &db.db); err != nil {
   181  		return nil, err
   182  	}
   183  	return db, nil
   184  }
   185  
   186  // NewAbiDBFromFiles loads both the standard signature database and a custom database. The latter will be used
   187  // to write new values into if they are submitted via the API
   188  func NewAbiDBFromFiles(standard, custom string) (*AbiDb, error) {
   189  
   190  	db := &AbiDb{make(map[string]string), make(map[string]string), custom}
   191  	db.customdbPath = custom
   192  
   193  	raw, err := ioutil.ReadFile(standard)
   194  	if err != nil {
   195  		return nil, err
   196  	}
   197  	if err := json.Unmarshal(raw, &db.db); err != nil {
   198  		return nil, err
   199  	}
   200  	// Custom file may not exist. Will be created during save, if needed
   201  	if _, err := os.Stat(custom); err == nil {
   202  		raw, err = ioutil.ReadFile(custom)
   203  		if err != nil {
   204  			return nil, err
   205  		}
   206  		if err := json.Unmarshal(raw, &db.customdb); err != nil {
   207  			return nil, err
   208  		}
   209  	}
   210  
   211  	return db, nil
   212  }
   213  
   214  // LookupMethodSelector checks the given 4byte-sequence against the known ABI methods.
   215  // OBS: This method does not validate the match, it's assumed the caller will do so
   216  func (db *AbiDb) LookupMethodSelector(id []byte) (string, error) {
   217  	if len(id) < 4 {
   218  		return "", fmt.Errorf("Expected 4-byte id, got %d", len(id))
   219  	}
   220  	sig := common.ToHex(id[:4])
   221  	if key, exists := db.db[sig]; exists {
   222  		return key, nil
   223  	}
   224  	if key, exists := db.customdb[sig]; exists {
   225  		return key, nil
   226  	}
   227  	return "", fmt.Errorf("Signature %v not found", sig)
   228  }
   229  func (db *AbiDb) Size() int {
   230  	return len(db.db)
   231  }
   232  
   233  // saveCustomAbi saves a signature ephemerally. If custom file is used, also saves to disk
   234  func (db *AbiDb) saveCustomAbi(selector, signature string) error {
   235  	db.customdb[signature] = selector
   236  	if db.customdbPath == "" {
   237  		return nil //Not an error per se, just not used
   238  	}
   239  	d, err := json.Marshal(db.customdb)
   240  	if err != nil {
   241  		return err
   242  	}
   243  	err = ioutil.WriteFile(db.customdbPath, d, 0600)
   244  	return err
   245  }
   246  
   247  // AddSignature to the database, if custom database saving is enabled.
   248  // OBS: This method does _not_ validate the correctness of the data,
   249  // it is assumed that the caller has already done so
   250  func (db *AbiDb) AddSignature(selector string, data []byte) error {
   251  	if len(data) < 4 {
   252  		return nil
   253  	}
   254  	_, err := db.LookupMethodSelector(data[:4])
   255  	if err == nil {
   256  		return nil
   257  	}
   258  	sig := common.ToHex(data[:4])
   259  	return db.saveCustomAbi(selector, sig)
   260  }