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  }