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 }