github.com/status-im/status-go@v1.1.0/protocol/transaction_validator_test.go (about) 1 package protocol 2 3 import ( 4 "context" 5 "crypto/ecdsa" 6 "encoding/hex" 7 "strings" 8 "testing" 9 10 "math/big" 11 12 "github.com/stretchr/testify/suite" 13 14 coretypes "github.com/status-im/status-go/eth-node/core/types" 15 "github.com/status-im/status-go/eth-node/crypto" 16 "github.com/status-im/status-go/eth-node/types" 17 "github.com/status-im/status-go/protocol/common" 18 "github.com/status-im/status-go/protocol/tt" 19 ) 20 21 func padArray(bb []byte, size int) []byte { 22 l := len(bb) 23 if l == size { 24 return bb 25 } 26 if l > size { 27 return bb[l-size:] 28 } 29 tmp := make([]byte, size) 30 copy(tmp[size-l:], bb) 31 return tmp 32 } 33 34 type TransactionValidatorSuite struct { 35 suite.Suite 36 } 37 38 func TestTransactionValidatorSuite(t *testing.T) { 39 suite.Run(t, new(TransactionValidatorSuite)) 40 } 41 42 func buildSignature(walletKey *ecdsa.PrivateKey, chatKey *ecdsa.PublicKey, hash string) ([]byte, error) { 43 hashBytes, err := hex.DecodeString(hash[2:]) 44 if err != nil { 45 return nil, err 46 } 47 chatKeyBytes := crypto.FromECDSAPub(chatKey) 48 signatureMaterial := append(chatKeyBytes, hashBytes...) 49 signatureMaterial = crypto.TextHash(signatureMaterial) 50 signature, err := crypto.Sign(signatureMaterial, walletKey) 51 if err != nil { 52 return nil, err 53 } 54 signature[64] += 27 55 return signature, nil 56 } 57 58 func buildData(fn string, to types.Address, value *big.Int) []byte { 59 var data []byte 60 addressBytes := make([]byte, 32) 61 62 fnBytes, _ := hex.DecodeString(fn) 63 copy(addressBytes[12:], to.Bytes()) 64 valueBytes := padArray(value.Bytes(), 32) 65 66 data = append(data, fnBytes...) 67 data = append(data, addressBytes...) 68 data = append(data, valueBytes...) 69 return data 70 } 71 72 func (s *TransactionValidatorSuite) TestValidateTransactions() { 73 notTransferFunction := "a9059cbd" 74 75 senderKey, err := crypto.GenerateKey() 76 s.Require().NoError(err) 77 78 senderWalletKey, err := crypto.GenerateKey() 79 s.Require().NoError(err) 80 81 myWalletKey1, err := crypto.GenerateKey() 82 s.Require().NoError(err) 83 myWalletKey2, err := crypto.GenerateKey() 84 s.Require().NoError(err) 85 86 senderAddress := crypto.PubkeyToAddress(senderWalletKey.PublicKey) 87 myAddress1 := crypto.PubkeyToAddress(myWalletKey1.PublicKey) 88 myAddress2 := crypto.PubkeyToAddress(myWalletKey2.PublicKey) 89 90 db, err := openTestDB() 91 s.Require().NoError(err) 92 p := &sqlitePersistence{db: db} 93 94 logger := tt.MustCreateTestLogger() 95 validator := NewTransactionValidator([]types.Address{myAddress1, myAddress2}, p, nil, logger) 96 97 contractString := "0x744d70fdbe2ba4cf95131626614a1763df805b9e" 98 contractAddress := types.HexToAddress(contractString) 99 100 defaultTransactionHash := "0x53edbe74408c2eeed4e5493b3aac0c006d8a14b140975f4306dd35f5e1d245bc" 101 testCases := []struct { 102 Name string 103 Valid bool 104 AccordingToSpec bool 105 Error bool 106 Transaction coretypes.Message 107 OverrideSignatureChatKey *ecdsa.PublicKey 108 OverrideTransactionHash string 109 Parameters *common.CommandParameters 110 WalletKey *ecdsa.PrivateKey 111 From *ecdsa.PublicKey 112 }{ 113 { 114 Name: "valid eth transfer to any address", 115 Valid: true, 116 AccordingToSpec: true, 117 Transaction: coretypes.NewMessage( 118 senderAddress, 119 &myAddress1, 120 1, 121 big.NewInt(int64(23)), 122 0, 123 nil, 124 nil, 125 false, 126 ), 127 Parameters: &common.CommandParameters{ 128 Value: "23", 129 }, 130 WalletKey: senderWalletKey, 131 From: &senderKey.PublicKey, 132 }, 133 { 134 Name: "valid eth transfer to specific address", 135 Valid: true, 136 AccordingToSpec: true, 137 Transaction: coretypes.NewMessage( 138 senderAddress, 139 &myAddress1, 140 1, 141 big.NewInt(int64(23)), 142 0, 143 nil, 144 nil, 145 false, 146 ), 147 Parameters: &common.CommandParameters{ 148 Value: "23", 149 Address: strings.ToLower(myAddress1.Hex()), 150 }, 151 WalletKey: senderWalletKey, 152 From: &senderKey.PublicKey, 153 }, 154 { 155 Name: "invalid eth transfer, not includes pk of the chat in signature", 156 Transaction: coretypes.NewMessage( 157 senderAddress, 158 &myAddress1, 159 1, 160 big.NewInt(int64(23)), 161 0, 162 nil, 163 nil, 164 false, 165 ), 166 Parameters: &common.CommandParameters{ 167 Value: "23", 168 Address: strings.ToLower(myAddress1.Hex()), 169 }, 170 WalletKey: senderWalletKey, 171 OverrideSignatureChatKey: &senderWalletKey.PublicKey, 172 From: &senderKey.PublicKey, 173 }, 174 { 175 Name: "invalid eth transfer, not signed with the wallet key", 176 Transaction: coretypes.NewMessage( 177 senderAddress, 178 &myAddress1, 179 1, 180 big.NewInt(int64(23)), 181 0, 182 nil, 183 nil, 184 false, 185 ), 186 Parameters: &common.CommandParameters{ 187 Value: "23", 188 Address: strings.ToLower(myAddress1.Hex()), 189 }, 190 WalletKey: senderKey, 191 From: &senderKey.PublicKey, 192 }, 193 { 194 Name: "invalid eth transfer, wrong signature transaction hash", 195 Transaction: coretypes.NewMessage( 196 senderAddress, 197 &myAddress1, 198 1, 199 big.NewInt(int64(23)), 200 0, 201 nil, 202 nil, 203 false, 204 ), 205 OverrideTransactionHash: "0xdd9202df5e2f3611b5b6b716aef2a3543cc0bdd7506f50926e0869b83c8383b9", 206 Parameters: &common.CommandParameters{ 207 Value: "23", 208 }, 209 WalletKey: senderWalletKey, 210 From: &senderKey.PublicKey, 211 }, 212 213 { 214 Name: "invalid eth transfer, we own the wallet but not as specified", 215 Transaction: coretypes.NewMessage( 216 senderAddress, 217 &myAddress1, 218 1, 219 big.NewInt(int64(23)), 220 0, 221 nil, 222 nil, 223 false, 224 ), 225 Parameters: &common.CommandParameters{ 226 Value: "23", 227 Address: strings.ToLower(myAddress2.Hex()), 228 }, 229 WalletKey: senderWalletKey, 230 From: &senderKey.PublicKey, 231 }, 232 { 233 Name: "invalid eth transfer, not our wallet", 234 Transaction: coretypes.NewMessage( 235 senderAddress, 236 &senderAddress, 237 1, 238 big.NewInt(int64(23)), 239 0, 240 nil, 241 nil, 242 false, 243 ), 244 Parameters: &common.CommandParameters{ 245 Value: "23", 246 }, 247 WalletKey: senderWalletKey, 248 From: &senderKey.PublicKey, 249 }, 250 { 251 Name: "valid eth transfer, but not according to spec, wrong amount", 252 Valid: true, 253 Transaction: coretypes.NewMessage( 254 senderAddress, 255 &myAddress1, 256 1, 257 big.NewInt(int64(20)), 258 0, 259 nil, 260 nil, 261 false, 262 ), 263 Parameters: &common.CommandParameters{ 264 Value: "23", 265 }, 266 WalletKey: senderWalletKey, 267 From: &senderKey.PublicKey, 268 }, 269 { 270 Name: "valid token transfer to any address", 271 Valid: true, 272 AccordingToSpec: true, 273 Transaction: coretypes.NewMessage( 274 senderAddress, 275 &contractAddress, 276 1, 277 big.NewInt(int64(0)), 278 0, 279 nil, 280 buildData(transferFunction, myAddress1, big.NewInt(int64(23))), 281 false, 282 ), 283 Parameters: &common.CommandParameters{ 284 Contract: contractString, 285 Value: "23", 286 }, 287 WalletKey: senderWalletKey, 288 From: &senderKey.PublicKey, 289 }, 290 { 291 Name: "valid token transfer to a specific address", 292 Valid: true, 293 AccordingToSpec: true, 294 Transaction: coretypes.NewMessage( 295 senderAddress, 296 &contractAddress, 297 1, 298 big.NewInt(int64(0)), 299 0, 300 nil, 301 buildData(transferFunction, myAddress1, big.NewInt(int64(23))), 302 false, 303 ), 304 Parameters: &common.CommandParameters{ 305 Contract: contractString, 306 Address: strings.ToLower(myAddress1.Hex()), 307 Value: "23", 308 }, 309 WalletKey: senderWalletKey, 310 From: &senderKey.PublicKey, 311 }, 312 { 313 Name: "valid token transfer, not according to spec because of amount", 314 Valid: true, 315 AccordingToSpec: false, 316 Transaction: coretypes.NewMessage( 317 senderAddress, 318 &contractAddress, 319 1, 320 big.NewInt(int64(0)), 321 0, 322 nil, 323 buildData(transferFunction, myAddress1, big.NewInt(int64(13))), 324 false, 325 ), 326 Parameters: &common.CommandParameters{ 327 Contract: contractString, 328 Value: "23", 329 }, 330 WalletKey: senderWalletKey, 331 From: &senderKey.PublicKey, 332 }, 333 { 334 Name: "invalid token transfer, wrong contract", 335 Transaction: coretypes.NewMessage( 336 senderAddress, 337 &senderAddress, 338 1, 339 big.NewInt(int64(0)), 340 0, 341 nil, 342 buildData(transferFunction, myAddress1, big.NewInt(int64(23))), 343 false, 344 ), 345 Parameters: &common.CommandParameters{ 346 Contract: contractString, 347 Address: strings.ToLower(myAddress1.Hex()), 348 Value: "23", 349 }, 350 WalletKey: senderWalletKey, 351 From: &senderKey.PublicKey, 352 }, 353 354 { 355 Name: "invalid token transfer, not an address I own", 356 Transaction: coretypes.NewMessage( 357 senderAddress, 358 &contractAddress, 359 1, 360 big.NewInt(int64(0)), 361 0, 362 nil, 363 buildData(transferFunction, myAddress1, big.NewInt(int64(23))), 364 false, 365 ), 366 Parameters: &common.CommandParameters{ 367 Contract: contractString, 368 Address: strings.ToLower(senderAddress.Hex()), 369 Value: "23", 370 }, 371 WalletKey: senderWalletKey, 372 From: &senderKey.PublicKey, 373 }, 374 375 { 376 Name: "invalid token transfer, not the specified address", 377 Transaction: coretypes.NewMessage( 378 senderAddress, 379 &contractAddress, 380 1, 381 big.NewInt(int64(0)), 382 0, 383 nil, 384 buildData(transferFunction, myAddress2, big.NewInt(int64(23))), 385 false, 386 ), 387 Parameters: &common.CommandParameters{ 388 Contract: contractString, 389 Address: strings.ToLower(myAddress1.Hex()), 390 Value: "23", 391 }, 392 WalletKey: senderWalletKey, 393 From: &senderKey.PublicKey, 394 }, 395 { 396 Name: "invalid token transfer, wrong fn", 397 Transaction: coretypes.NewMessage( 398 senderAddress, 399 &contractAddress, 400 1, 401 big.NewInt(int64(0)), 402 0, 403 nil, 404 buildData(notTransferFunction, myAddress1, big.NewInt(int64(23))), 405 false, 406 ), 407 Parameters: &common.CommandParameters{ 408 Contract: contractString, 409 Value: "23", 410 }, 411 WalletKey: senderWalletKey, 412 From: &senderKey.PublicKey, 413 }, 414 } 415 416 for _, tc := range testCases { 417 s.Run(tc.Name, func() { 418 tc.Parameters.TransactionHash = defaultTransactionHash 419 signatureTransactionHash := defaultTransactionHash 420 signatureChatKey := tc.From 421 if tc.OverrideTransactionHash != "" { 422 signatureTransactionHash = tc.OverrideTransactionHash 423 } 424 if tc.OverrideSignatureChatKey != nil { 425 signatureChatKey = tc.OverrideSignatureChatKey 426 } 427 signature, err := buildSignature(tc.WalletKey, signatureChatKey, signatureTransactionHash) 428 s.Require().NoError(err) 429 tc.Parameters.Signature = signature 430 431 response, err := validator.validateTransaction(context.Background(), tc.Transaction, tc.Parameters, tc.From) 432 if tc.Error { 433 s.Error(err) 434 return 435 } 436 s.Require().NoError(err) 437 s.Equal(tc.AccordingToSpec, response.AccordingToSpec) 438 s.Equal(tc.Valid, response.Valid) 439 }) 440 } 441 442 }