github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/signer/core/abihelper.go (about)

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