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  }