github.com/status-im/status-go@v1.1.0/services/typeddata/hash.go (about)

     1  package typeddata
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"math/big"
     9  	"sort"
    10  
    11  	"github.com/ethereum/go-ethereum/accounts/abi"
    12  	"github.com/ethereum/go-ethereum/common"
    13  	"github.com/ethereum/go-ethereum/common/hexutil"
    14  	"github.com/ethereum/go-ethereum/crypto"
    15  )
    16  
    17  var (
    18  	bytes32Type, _ = abi.NewType("bytes32", "", nil)
    19  	int256Type, _  = abi.NewType("int256", "", nil)
    20  
    21  	errNotInteger = errors.New("not an integer")
    22  )
    23  
    24  // ValidateAndHash generates a hash of TypedData and verifies that chainId in the typed data matches currently selected chain.
    25  func ValidateAndHash(typed TypedData, chain *big.Int) (common.Hash, error) {
    26  	if err := typed.ValidateChainID(chain); err != nil {
    27  		return common.Hash{}, err
    28  	}
    29  
    30  	return encodeData(typed)
    31  }
    32  
    33  // deps runs breadth-first traversal starting from target and collects all
    34  // found composite dependencies types into result slice. target always will be first
    35  // in the result array. all other dependencies are sorted alphabetically.
    36  // for example: Z{c C, a A} A{c C} and the target is Z.
    37  // result would be Z, A, B, C
    38  func deps(target string, types Types) []string {
    39  	unique := map[string]struct{}{}
    40  	unique[target] = struct{}{}
    41  	visited := []string{target}
    42  	deps := []string{}
    43  	for len(visited) > 0 {
    44  		current := visited[0]
    45  		fields := types[current]
    46  		for i := range fields {
    47  			f := fields[i]
    48  			if _, defined := types[f.Type]; defined {
    49  				if _, exist := unique[f.Type]; !exist {
    50  					visited = append(visited, f.Type)
    51  					unique[f.Type] = struct{}{}
    52  				}
    53  			}
    54  		}
    55  		visited = visited[1:]
    56  		deps = append(deps, current)
    57  	}
    58  	sort.Slice(deps[1:], func(i, j int) bool {
    59  		return deps[1:][i] < deps[1:][j]
    60  	})
    61  	return deps
    62  }
    63  
    64  func typeString(target string, types Types) string {
    65  	b := new(bytes.Buffer)
    66  	for _, dep := range deps(target, types) {
    67  		b.WriteString(dep)
    68  		b.WriteString("(")
    69  		fields := types[dep]
    70  		first := true
    71  		for i := range fields {
    72  			if !first {
    73  				b.WriteString(",")
    74  			} else {
    75  				first = false
    76  			}
    77  			f := fields[i]
    78  			b.WriteString(f.Type)
    79  			b.WriteString(" ")
    80  			b.WriteString(f.Name)
    81  		}
    82  		b.WriteString(")")
    83  	}
    84  	return b.String()
    85  }
    86  
    87  func typeHash(target string, types Types) (rst common.Hash) {
    88  	return crypto.Keccak256Hash([]byte(typeString(target, types)))
    89  }
    90  
    91  func hashStruct(target string, data map[string]json.RawMessage, types Types) (rst common.Hash, err error) {
    92  	fields := types[target]
    93  	typeh := typeHash(target, types)
    94  	args := abi.Arguments{{Type: bytes32Type}}
    95  	vals := []interface{}{typeh}
    96  	for i := range fields {
    97  		f := fields[i]
    98  		val, typ, err := toABITypeAndValue(f, data, types)
    99  		if err != nil {
   100  			return rst, err
   101  		}
   102  		vals = append(vals, val)
   103  		args = append(args, abi.Argument{Name: f.Name, Type: typ})
   104  	}
   105  	packed, err := args.Pack(vals...)
   106  	if err != nil {
   107  		return rst, err
   108  	}
   109  	return crypto.Keccak256Hash(packed), nil
   110  }
   111  
   112  func toABITypeAndValue(f Field, data map[string]json.RawMessage, types Types) (val interface{}, typ abi.Type, err error) {
   113  	if f.Type == "string" {
   114  		var str string
   115  		if err = json.Unmarshal(data[f.Name], &str); err != nil {
   116  			return
   117  		}
   118  		return crypto.Keccak256Hash([]byte(str)), bytes32Type, nil
   119  	} else if f.Type == "bytes" {
   120  		var bytes hexutil.Bytes
   121  		if err = json.Unmarshal(data[f.Name], &bytes); err != nil {
   122  			return
   123  		}
   124  		return crypto.Keccak256Hash(bytes), bytes32Type, nil
   125  	} else if _, exist := types[f.Type]; exist {
   126  		var obj map[string]json.RawMessage
   127  		if err = json.Unmarshal(data[f.Name], &obj); err != nil {
   128  			return
   129  		}
   130  		val, err = hashStruct(f.Type, obj, types)
   131  		if err != nil {
   132  			return
   133  		}
   134  		return val, bytes32Type, nil
   135  	}
   136  	return atomicType(f, data)
   137  }
   138  
   139  func atomicType(f Field, data map[string]json.RawMessage) (val interface{}, typ abi.Type, err error) {
   140  	typ, err = abi.NewType(f.Type, "", nil)
   141  	if err != nil {
   142  		return
   143  	}
   144  	if typ.T == abi.SliceTy || typ.T == abi.ArrayTy || typ.T == abi.FunctionTy {
   145  		return val, typ, errors.New("arrays, slices and functions are not supported")
   146  	} else if typ.T == abi.FixedBytesTy {
   147  		return toFixedBytes(f, data[f.Name])
   148  	} else if typ.T == abi.AddressTy {
   149  		val, err = toAddress(f, data[f.Name])
   150  	} else if typ.T == abi.IntTy || typ.T == abi.UintTy {
   151  		return toInt(f, data[f.Name])
   152  	} else if typ.T == abi.BoolTy {
   153  		val, err = toBool(f, data[f.Name])
   154  	} else {
   155  		err = fmt.Errorf("type %s is not supported", f.Type)
   156  	}
   157  	return
   158  }
   159  
   160  func toFixedBytes(f Field, data json.RawMessage) (rst [32]byte, typ abi.Type, err error) {
   161  	var bytes hexutil.Bytes
   162  	if err = json.Unmarshal(data, &bytes); err != nil {
   163  		return
   164  	}
   165  	typ = bytes32Type
   166  	rst = [32]byte{}
   167  	// reduce the length to the advertised size
   168  	if len(bytes) > typ.Size {
   169  		bytes = bytes[:typ.Size]
   170  	}
   171  	copy(rst[:], bytes)
   172  	return rst, typ, nil
   173  }
   174  
   175  func toInt(f Field, data json.RawMessage) (val *big.Int, typ abi.Type, err error) {
   176  	val = new(big.Int)
   177  	if err = json.Unmarshal(data, &val); err != nil {
   178  		var buf string
   179  		err = json.Unmarshal(data, &buf)
   180  		if err != nil {
   181  			return
   182  		}
   183  		var ok bool
   184  		val, ok = val.SetString(buf, 0)
   185  		if !ok {
   186  			err = errNotInteger
   187  			return
   188  		}
   189  	}
   190  	return val, int256Type, nil
   191  }
   192  
   193  func toAddress(f Field, data json.RawMessage) (rst common.Address, err error) {
   194  	err = json.Unmarshal(data, &rst)
   195  	return
   196  }
   197  
   198  func toBool(f Field, data json.RawMessage) (rst bool, err error) {
   199  	err = json.Unmarshal(data, &rst)
   200  	return
   201  }