github.com/status-im/status-go@v1.1.0/services/typeddata/hash_test.go (about) 1 package typeddata 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "math/big" 7 "testing" 8 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/require" 11 12 "github.com/ethereum/go-ethereum/accounts/abi" 13 "github.com/ethereum/go-ethereum/common" 14 "github.com/ethereum/go-ethereum/common/hexutil" 15 "github.com/ethereum/go-ethereum/crypto" 16 ) 17 18 func TestTypeString(t *testing.T) { 19 type testCase struct { 20 description string 21 typeString string 22 types Types 23 target string 24 } 25 for _, tc := range []testCase{ 26 { 27 "WithoutDeps", 28 "Person(string name,address wallet)", 29 Types{"Person": []Field{{Name: "name", Type: "string"}, {Name: "wallet", Type: "address"}}}, 30 "Person", 31 }, 32 { 33 "SingleDep", 34 "Mail(Person from,Person to)Person(string name,address wallet)", 35 Types{ 36 "Person": []Field{{Name: "name", Type: "string"}, {Name: "wallet", Type: "address"}}, 37 "Mail": []Field{{Name: "from", Type: "Person"}, {Name: "to", Type: "Person"}}, 38 }, 39 "Mail", 40 }, 41 { 42 "DepsOrdered", 43 "Z(A a,B b)A(string name)B(string name)", 44 Types{ 45 "A": []Field{{Name: "name", Type: "string"}}, 46 "B": []Field{{Name: "name", Type: "string"}}, 47 "Z": []Field{{Name: "a", Type: "A"}, {Name: "b", Type: "B"}}, 48 }, 49 "Z", 50 }, 51 { 52 "RecursiveDepsIgnored", 53 "Z(A a)A(Z z)", 54 Types{ 55 "A": []Field{{Name: "z", Type: "Z"}}, 56 "Z": []Field{{Name: "a", Type: "A"}}, 57 }, 58 "Z", 59 }, 60 } { 61 tc := tc 62 t.Run(tc.description, func(t *testing.T) { 63 require.Equal(t, tc.typeString, typeString(tc.target, tc.types)) 64 }) 65 } 66 } 67 68 func TestEncodeData(t *testing.T) { 69 type testCase struct { 70 description string 71 message map[string]json.RawMessage 72 types Types 73 target string 74 result func(testCase) common.Hash 75 } 76 77 bytes32, _ := abi.NewType("bytes32", "", nil) 78 addr, _ := abi.NewType("address", "", nil) 79 boolT, _ := abi.NewType("bool", "", nil) 80 81 for _, tc := range []testCase{ 82 { 83 "HexAddressConvertedToBytes", 84 map[string]json.RawMessage{"wallet": json.RawMessage(`"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"`)}, 85 Types{"A": []Field{{Name: "wallet", Type: "address"}}}, 86 "A", 87 func(tc testCase) common.Hash { 88 args := abi.Arguments{{Type: bytes32}, {Type: addr}} 89 typehash := typeHash(tc.target, tc.types) 90 var data common.Address 91 assert.NoError(t, json.Unmarshal(tc.message["wallet"], &data)) 92 packed, _ := args.Pack(typehash, data) 93 return crypto.Keccak256Hash(packed) 94 }, 95 }, 96 { 97 "StringHashed", 98 map[string]json.RawMessage{"name": json.RawMessage(`"AAA"`)}, 99 Types{"A": []Field{{Name: "name", Type: "string"}}}, 100 "A", 101 func(tc testCase) common.Hash { 102 args := abi.Arguments{{Type: bytes32}, {Type: bytes32}} 103 typehash := typeHash(tc.target, tc.types) 104 var data string 105 assert.NoError(t, json.Unmarshal(tc.message["name"], &data)) 106 packed, _ := args.Pack(typehash, crypto.Keccak256Hash([]byte(data))) 107 return crypto.Keccak256Hash(packed) 108 }, 109 }, 110 { 111 "BytesHashed", 112 map[string]json.RawMessage{"name": json.RawMessage(`"0x010203"`)}, // []byte{1,2,3} 113 Types{"A": []Field{{Name: "name", Type: "bytes"}}}, 114 "A", 115 func(tc testCase) common.Hash { 116 args := abi.Arguments{{Type: bytes32}, {Type: bytes32}} 117 typehash := typeHash(tc.target, tc.types) 118 var data hexutil.Bytes 119 assert.NoError(t, json.Unmarshal(tc.message["name"], &data)) 120 packed, _ := args.Pack(typehash, crypto.Keccak256Hash(data)) 121 return crypto.Keccak256Hash(packed) 122 }, 123 }, 124 { 125 "FixedBytesAsIs", 126 map[string]json.RawMessage{"name": json.RawMessage(`"0x010203"`)}, // []byte{1,2,3} 127 Types{"A": []Field{{Name: "name", Type: "bytes32"}}}, 128 "A", 129 func(tc testCase) common.Hash { 130 args := abi.Arguments{{Type: bytes32}, {Type: bytes32}} 131 typehash := typeHash(tc.target, tc.types) 132 var data hexutil.Bytes 133 assert.NoError(t, json.Unmarshal(tc.message["name"], &data)) 134 rst := [32]byte{} 135 copy(rst[:], data) 136 packed, _ := args.Pack(typehash, rst) 137 return crypto.Keccak256Hash(packed) 138 }, 139 }, 140 { 141 "BoolAsIs", 142 map[string]json.RawMessage{"flag": json.RawMessage("true")}, 143 Types{"A": []Field{{Name: "flag", Type: "bool"}}}, 144 "A", 145 func(tc testCase) common.Hash { 146 args := abi.Arguments{{Type: bytes32}, {Type: boolT}} 147 typehash := typeHash(tc.target, tc.types) 148 var data bool 149 assert.NoError(t, json.Unmarshal(tc.message["flag"], &data)) 150 packed, _ := args.Pack(typehash, data) 151 return crypto.Keccak256Hash(packed) 152 }, 153 }, 154 { 155 "Int32Uint32AsIs", 156 map[string]json.RawMessage{"I": json.RawMessage("-10"), "UI": json.RawMessage("10")}, 157 Types{"A": []Field{{Name: "I", Type: "int32"}, {Name: "UI", Type: "uint32"}}}, 158 "A", 159 func(tc testCase) common.Hash { 160 args := abi.Arguments{{Type: bytes32}, {Type: int256Type}, {Type: int256Type}} 161 typehash := typeHash(tc.target, tc.types) 162 packed, _ := args.Pack(typehash, big.NewInt(-10), big.NewInt(10)) 163 return crypto.Keccak256Hash(packed) 164 }, 165 }, 166 { 167 "SignedUnsignedIntegersBiggerThen64", 168 map[string]json.RawMessage{ 169 "i128": json.RawMessage("1"), 170 "i256": json.RawMessage("1"), 171 "ui128": json.RawMessage("1"), 172 "ui256": json.RawMessage("1"), 173 }, 174 Types{"A": []Field{ 175 {Name: "i128", Type: "int128"}, {Name: "i256", Type: "int256"}, 176 {Name: "ui128", Type: "uint128"}, {Name: "ui256", Type: "uint256"}, 177 }}, 178 "A", 179 func(tc testCase) common.Hash { 180 intBig, _ := abi.NewType("int128", "", nil) 181 uintBig, _ := abi.NewType("uint128", "", nil) 182 args := abi.Arguments{{Type: bytes32}, 183 {Type: intBig}, {Type: intBig}, {Type: uintBig}, {Type: uintBig}} 184 typehash := typeHash(tc.target, tc.types) 185 val := big.NewInt(1) 186 packed, _ := args.Pack(typehash, val, val, val, val) 187 return crypto.Keccak256Hash(packed) 188 }, 189 }, 190 { 191 "CompositeTypesAreRecursivelyEncoded", 192 map[string]json.RawMessage{"a": json.RawMessage(`{"name":"AAA"}`)}, 193 Types{"A": []Field{{Name: "name", Type: "string"}}, "Z": []Field{{Name: "a", Type: "A"}}}, 194 "Z", 195 func(tc testCase) common.Hash { 196 args := abi.Arguments{{Type: bytes32}, {Type: bytes32}} 197 zhash := typeHash(tc.target, tc.types) 198 ahash := typeHash("A", tc.types) 199 var A map[string]string 200 assert.NoError(t, json.Unmarshal(tc.message["a"], &A)) 201 apacked, _ := args.Pack(ahash, crypto.Keccak256Hash([]byte(A["name"]))) 202 packed, _ := args.Pack(zhash, crypto.Keccak256Hash(apacked)) 203 return crypto.Keccak256Hash(packed) 204 }, 205 }, 206 } { 207 tc := tc 208 t.Run(tc.description, func(t *testing.T) { 209 encoded, err := hashStruct(tc.target, tc.message, tc.types) 210 require.NoError(t, err) 211 require.Equal(t, tc.result(tc), encoded) 212 }) 213 } 214 } 215 216 func TestEncodeDataErrors(t *testing.T) { 217 type testCase struct { 218 description string 219 message map[string]json.RawMessage 220 types Types 221 target string 222 } 223 224 for _, tc := range []testCase{ 225 { 226 "FailedUnmxarshalAsAString", 227 map[string]json.RawMessage{"a": json.RawMessage("1")}, 228 Types{"A": []Field{{Name: "name", Type: "string"}}}, 229 "A", 230 }, 231 { 232 "FailedUnmarshalToHexBytesToABytes", 233 map[string]json.RawMessage{"a": {1, 2, 3}}, 234 Types{"A": []Field{{Name: "name", Type: "bytes"}}}, 235 "A", 236 }, 237 { 238 "CompositeTypeIsNotAnObject", 239 map[string]json.RawMessage{"a": json.RawMessage(`"AAA"`)}, 240 Types{"A": []Field{{Name: "name", Type: "string"}}, "Z": []Field{{Name: "a", Type: "A"}}}, 241 "Z", 242 }, 243 { 244 "CompositeTypesFailed", 245 map[string]json.RawMessage{"a": json.RawMessage(`{"name":10}`)}, 246 Types{"A": []Field{{Name: "name", Type: "string"}}, "Z": []Field{{Name: "a", Type: "A"}}}, 247 "Z", 248 }, 249 { 250 "ArraysNotSupported", 251 map[string]json.RawMessage{"a": json.RawMessage("[1,2]")}, 252 Types{"A": []Field{{Name: "name", Type: "int8[2]"}}}, 253 "A", 254 }, 255 { 256 "SlicesNotSupported", 257 map[string]json.RawMessage{"a": json.RawMessage("[1,2]")}, 258 Types{"A": []Field{{Name: "name", Type: "int[]"}}}, 259 "A", 260 }, 261 { 262 "FailedToUnmarshalInteger", 263 map[string]json.RawMessage{"a": json.RawMessage("x00x")}, 264 Types{"A": []Field{{Name: "name", Type: "uint256"}}}, 265 "A", 266 }, 267 } { 268 tc := tc 269 t.Run(tc.description, func(t *testing.T) { 270 encoded, err := hashStruct(tc.target, tc.message, tc.types) 271 require.Error(t, err) 272 require.Equal(t, common.Hash{}, encoded) 273 }) 274 } 275 } 276 277 func TestEncodeInt(t *testing.T) { 278 example := new(big.Int).Exp(big.NewInt(2), big.NewInt(255), nil) 279 for _, tc := range []struct { 280 description string 281 data []byte 282 expected *big.Int 283 err error 284 }{ 285 { 286 description: "AsString", 287 data: []byte(example.String()), 288 expected: example, 289 }, 290 { 291 description: "WrappedIntoString", 292 data: []byte(fmt.Sprintf("\"%s\"", example.String())), 293 expected: example, 294 }, 295 { 296 description: "NotAnInteger", 297 data: []byte("\"xzy\""), 298 err: errNotInteger, 299 }, 300 } { 301 t.Run(tc.description, func(t *testing.T) { 302 rst, _, err := toInt(Field{}, tc.data) 303 if tc.err == nil { 304 require.NoError(t, err) 305 require.Equal(t, tc.expected, rst) 306 } else { 307 require.EqualError(t, err, tc.err.Error()) 308 } 309 }) 310 } 311 }