status-go/protocol/transaction_validator_test.go

443 lines
10 KiB
Go

package protocol
import (
"context"
"crypto/ecdsa"
"encoding/hex"
"strings"
"testing"
"math/big"
"github.com/stretchr/testify/suite"
coretypes "github.com/status-im/status-go/eth-node/core/types"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/tt"
)
func padArray(bb []byte, size int) []byte {
l := len(bb)
if l == size {
return bb
}
if l > size {
return bb[l-size:]
}
tmp := make([]byte, size)
copy(tmp[size-l:], bb)
return tmp
}
type TransactionValidatorSuite struct {
suite.Suite
}
func TestTransactionValidatorSuite(t *testing.T) {
suite.Run(t, new(TransactionValidatorSuite))
}
func buildSignature(walletKey *ecdsa.PrivateKey, chatKey *ecdsa.PublicKey, hash string) ([]byte, error) {
hashBytes, err := hex.DecodeString(hash[2:])
if err != nil {
return nil, err
}
chatKeyBytes := crypto.FromECDSAPub(chatKey)
signatureMaterial := append(chatKeyBytes, hashBytes...)
signatureMaterial = crypto.TextHash(signatureMaterial)
signature, err := crypto.Sign(signatureMaterial, walletKey)
if err != nil {
return nil, err
}
signature[64] += 27
return signature, nil
}
func buildData(fn string, to types.Address, value *big.Int) []byte {
var data []byte
addressBytes := make([]byte, 32)
fnBytes, _ := hex.DecodeString(fn)
copy(addressBytes[12:], to.Bytes())
valueBytes := padArray(value.Bytes(), 32)
data = append(data, fnBytes...)
data = append(data, addressBytes...)
data = append(data, valueBytes...)
return data
}
func (s *TransactionValidatorSuite) TestValidateTransactions() {
notTransferFunction := "a9059cbd"
senderKey, err := crypto.GenerateKey()
s.Require().NoError(err)
senderWalletKey, err := crypto.GenerateKey()
s.Require().NoError(err)
myWalletKey1, err := crypto.GenerateKey()
s.Require().NoError(err)
myWalletKey2, err := crypto.GenerateKey()
s.Require().NoError(err)
senderAddress := crypto.PubkeyToAddress(senderWalletKey.PublicKey)
myAddress1 := crypto.PubkeyToAddress(myWalletKey1.PublicKey)
myAddress2 := crypto.PubkeyToAddress(myWalletKey2.PublicKey)
db, err := openTestDB()
s.Require().NoError(err)
p := &sqlitePersistence{db: db}
logger := tt.MustCreateTestLogger()
validator := NewTransactionValidator([]types.Address{myAddress1, myAddress2}, p, nil, logger)
contractString := "0x744d70fdbe2ba4cf95131626614a1763df805b9e"
contractAddress := types.HexToAddress(contractString)
defaultTransactionHash := "0x53edbe74408c2eeed4e5493b3aac0c006d8a14b140975f4306dd35f5e1d245bc"
testCases := []struct {
Name string
Valid bool
AccordingToSpec bool
Error bool
Transaction coretypes.Message
OverrideSignatureChatKey *ecdsa.PublicKey
OverrideTransactionHash string
Parameters *common.CommandParameters
WalletKey *ecdsa.PrivateKey
From *ecdsa.PublicKey
}{
{
Name: "valid eth transfer to any address",
Valid: true,
AccordingToSpec: true,
Transaction: coretypes.NewMessage(
senderAddress,
&myAddress1,
1,
big.NewInt(int64(23)),
0,
nil,
nil,
false,
),
Parameters: &common.CommandParameters{
Value: "23",
},
WalletKey: senderWalletKey,
From: &senderKey.PublicKey,
},
{
Name: "valid eth transfer to specific address",
Valid: true,
AccordingToSpec: true,
Transaction: coretypes.NewMessage(
senderAddress,
&myAddress1,
1,
big.NewInt(int64(23)),
0,
nil,
nil,
false,
),
Parameters: &common.CommandParameters{
Value: "23",
Address: strings.ToLower(myAddress1.Hex()),
},
WalletKey: senderWalletKey,
From: &senderKey.PublicKey,
},
{
Name: "invalid eth transfer, not includes pk of the chat in signature",
Transaction: coretypes.NewMessage(
senderAddress,
&myAddress1,
1,
big.NewInt(int64(23)),
0,
nil,
nil,
false,
),
Parameters: &common.CommandParameters{
Value: "23",
Address: strings.ToLower(myAddress1.Hex()),
},
WalletKey: senderWalletKey,
OverrideSignatureChatKey: &senderWalletKey.PublicKey,
From: &senderKey.PublicKey,
},
{
Name: "invalid eth transfer, not signed with the wallet key",
Transaction: coretypes.NewMessage(
senderAddress,
&myAddress1,
1,
big.NewInt(int64(23)),
0,
nil,
nil,
false,
),
Parameters: &common.CommandParameters{
Value: "23",
Address: strings.ToLower(myAddress1.Hex()),
},
WalletKey: senderKey,
From: &senderKey.PublicKey,
},
{
Name: "invalid eth transfer, wrong signature transaction hash",
Transaction: coretypes.NewMessage(
senderAddress,
&myAddress1,
1,
big.NewInt(int64(23)),
0,
nil,
nil,
false,
),
OverrideTransactionHash: "0xdd9202df5e2f3611b5b6b716aef2a3543cc0bdd7506f50926e0869b83c8383b9",
Parameters: &common.CommandParameters{
Value: "23",
},
WalletKey: senderWalletKey,
From: &senderKey.PublicKey,
},
{
Name: "invalid eth transfer, we own the wallet but not as specified",
Transaction: coretypes.NewMessage(
senderAddress,
&myAddress1,
1,
big.NewInt(int64(23)),
0,
nil,
nil,
false,
),
Parameters: &common.CommandParameters{
Value: "23",
Address: strings.ToLower(myAddress2.Hex()),
},
WalletKey: senderWalletKey,
From: &senderKey.PublicKey,
},
{
Name: "invalid eth transfer, not our wallet",
Transaction: coretypes.NewMessage(
senderAddress,
&senderAddress,
1,
big.NewInt(int64(23)),
0,
nil,
nil,
false,
),
Parameters: &common.CommandParameters{
Value: "23",
},
WalletKey: senderWalletKey,
From: &senderKey.PublicKey,
},
{
Name: "valid eth transfer, but not according to spec, wrong amount",
Valid: true,
Transaction: coretypes.NewMessage(
senderAddress,
&myAddress1,
1,
big.NewInt(int64(20)),
0,
nil,
nil,
false,
),
Parameters: &common.CommandParameters{
Value: "23",
},
WalletKey: senderWalletKey,
From: &senderKey.PublicKey,
},
{
Name: "valid token transfer to any address",
Valid: true,
AccordingToSpec: true,
Transaction: coretypes.NewMessage(
senderAddress,
&contractAddress,
1,
big.NewInt(int64(0)),
0,
nil,
buildData(transferFunction, myAddress1, big.NewInt(int64(23))),
false,
),
Parameters: &common.CommandParameters{
Contract: contractString,
Value: "23",
},
WalletKey: senderWalletKey,
From: &senderKey.PublicKey,
},
{
Name: "valid token transfer to a specific address",
Valid: true,
AccordingToSpec: true,
Transaction: coretypes.NewMessage(
senderAddress,
&contractAddress,
1,
big.NewInt(int64(0)),
0,
nil,
buildData(transferFunction, myAddress1, big.NewInt(int64(23))),
false,
),
Parameters: &common.CommandParameters{
Contract: contractString,
Address: strings.ToLower(myAddress1.Hex()),
Value: "23",
},
WalletKey: senderWalletKey,
From: &senderKey.PublicKey,
},
{
Name: "valid token transfer, not according to spec because of amount",
Valid: true,
AccordingToSpec: false,
Transaction: coretypes.NewMessage(
senderAddress,
&contractAddress,
1,
big.NewInt(int64(0)),
0,
nil,
buildData(transferFunction, myAddress1, big.NewInt(int64(13))),
false,
),
Parameters: &common.CommandParameters{
Contract: contractString,
Value: "23",
},
WalletKey: senderWalletKey,
From: &senderKey.PublicKey,
},
{
Name: "invalid token transfer, wrong contract",
Transaction: coretypes.NewMessage(
senderAddress,
&senderAddress,
1,
big.NewInt(int64(0)),
0,
nil,
buildData(transferFunction, myAddress1, big.NewInt(int64(23))),
false,
),
Parameters: &common.CommandParameters{
Contract: contractString,
Address: strings.ToLower(myAddress1.Hex()),
Value: "23",
},
WalletKey: senderWalletKey,
From: &senderKey.PublicKey,
},
{
Name: "invalid token transfer, not an address I own",
Transaction: coretypes.NewMessage(
senderAddress,
&contractAddress,
1,
big.NewInt(int64(0)),
0,
nil,
buildData(transferFunction, myAddress1, big.NewInt(int64(23))),
false,
),
Parameters: &common.CommandParameters{
Contract: contractString,
Address: strings.ToLower(senderAddress.Hex()),
Value: "23",
},
WalletKey: senderWalletKey,
From: &senderKey.PublicKey,
},
{
Name: "invalid token transfer, not the specified address",
Transaction: coretypes.NewMessage(
senderAddress,
&contractAddress,
1,
big.NewInt(int64(0)),
0,
nil,
buildData(transferFunction, myAddress2, big.NewInt(int64(23))),
false,
),
Parameters: &common.CommandParameters{
Contract: contractString,
Address: strings.ToLower(myAddress1.Hex()),
Value: "23",
},
WalletKey: senderWalletKey,
From: &senderKey.PublicKey,
},
{
Name: "invalid token transfer, wrong fn",
Transaction: coretypes.NewMessage(
senderAddress,
&contractAddress,
1,
big.NewInt(int64(0)),
0,
nil,
buildData(notTransferFunction, myAddress1, big.NewInt(int64(23))),
false,
),
Parameters: &common.CommandParameters{
Contract: contractString,
Value: "23",
},
WalletKey: senderWalletKey,
From: &senderKey.PublicKey,
},
}
for _, tc := range testCases {
s.Run(tc.Name, func() {
tc.Parameters.TransactionHash = defaultTransactionHash
signatureTransactionHash := defaultTransactionHash
signatureChatKey := tc.From
if tc.OverrideTransactionHash != "" {
signatureTransactionHash = tc.OverrideTransactionHash
}
if tc.OverrideSignatureChatKey != nil {
signatureChatKey = tc.OverrideSignatureChatKey
}
signature, err := buildSignature(tc.WalletKey, signatureChatKey, signatureTransactionHash)
s.Require().NoError(err)
tc.Parameters.Signature = signature
response, err := validator.validateTransaction(context.Background(), tc.Transaction, tc.Parameters, tc.From)
if tc.Error {
s.Error(err)
return
}
s.Require().NoError(err)
s.Equal(tc.AccordingToSpec, response.AccordingToSpec)
s.Equal(tc.Valid, response.Valid)
})
}
}