code.vegaprotocol.io/vega@v0.79.0/wallet/api/admin_check_transaction_test.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package api_test
    17  
    18  import (
    19  	"context"
    20  	"errors"
    21  	"fmt"
    22  	"testing"
    23  	"time"
    24  
    25  	vgcrypto "code.vegaprotocol.io/vega/libs/crypto"
    26  	"code.vegaprotocol.io/vega/libs/jsonrpc"
    27  	vgrand "code.vegaprotocol.io/vega/libs/rand"
    28  	"code.vegaprotocol.io/vega/wallet/api"
    29  	"code.vegaprotocol.io/vega/wallet/api/mocks"
    30  	walletnode "code.vegaprotocol.io/vega/wallet/api/node"
    31  	nodemocks "code.vegaprotocol.io/vega/wallet/api/node/mocks"
    32  	"code.vegaprotocol.io/vega/wallet/api/node/types"
    33  
    34  	"github.com/golang/mock/gomock"
    35  	"github.com/stretchr/testify/assert"
    36  )
    37  
    38  func TestAdminCheckTransaction(t *testing.T) {
    39  	t.Run("Documentation matches the code", testAdminCheckTransactionSchemaCorrect)
    40  	t.Run("Checking transaction with invalid params fails", testAdminCheckingTransactionWithInvalidParamsFails)
    41  	t.Run("Checking transaction with valid params succeeds", testAdminCheckingTransactionWithValidParamsSucceeds)
    42  	t.Run("Getting internal error during wallet verification fails", testAdminCheckTransactionGettingInternalErrorDuringWalletVerificationFails)
    43  	t.Run("Checking transaction with wallet that doesn't exist fails", testAdminCheckingTransactionWithWalletThatDoesntExistFails)
    44  	t.Run("Getting internal error during wallet retrieval fails", testAdminCheckTransactionGettingInternalErrorDuringWalletRetrievalFails)
    45  	t.Run("Checking transaction with malformed transaction fails", testAdminCheckingTransactionWithMalformedTransactionFails)
    46  	t.Run("Checking transaction which is invalid fails", testAdminCheckingTransactionWithInvalidTransactionFails)
    47  }
    48  
    49  func testAdminCheckTransactionSchemaCorrect(t *testing.T) {
    50  	assertEqualSchema(t, "admin.check_transaction", api.AdminCheckTransactionParams{}, api.AdminCheckTransactionResult{})
    51  }
    52  
    53  func testAdminCheckingTransactionWithInvalidParamsFails(t *testing.T) {
    54  	tcs := []struct {
    55  		name          string
    56  		params        interface{}
    57  		expectedError error
    58  	}{
    59  		{
    60  			name:          "with nil params",
    61  			params:        nil,
    62  			expectedError: api.ErrParamsRequired,
    63  		},
    64  		{
    65  			name:          "with wrong type of params",
    66  			params:        "test",
    67  			expectedError: api.ErrParamsDoNotMatch,
    68  		},
    69  		{
    70  			name: "with empty wallet",
    71  			params: api.AdminCheckTransactionParams{
    72  				Wallet:      "",
    73  				PublicKey:   vgrand.RandomStr(5),
    74  				Transaction: testTransaction(t),
    75  				Network:     vgrand.RandomStr(5),
    76  			},
    77  			expectedError: api.ErrWalletIsRequired,
    78  		},
    79  		{
    80  			name: "with empty public key",
    81  			params: api.AdminCheckTransactionParams{
    82  				Wallet:      vgrand.RandomStr(5),
    83  				PublicKey:   "",
    84  				Transaction: testTransaction(t),
    85  				Network:     vgrand.RandomStr(5),
    86  			},
    87  			expectedError: api.ErrPublicKeyIsRequired,
    88  		},
    89  		{
    90  			name: "with empty transaction",
    91  			params: api.AdminCheckTransactionParams{
    92  				Wallet:      vgrand.RandomStr(5),
    93  				PublicKey:   vgrand.RandomStr(5),
    94  				Transaction: "",
    95  				Network:     vgrand.RandomStr(5),
    96  			},
    97  			expectedError: api.ErrTransactionIsRequired,
    98  		},
    99  		{
   100  			name: "with no network or node address",
   101  			params: api.AdminCheckTransactionParams{
   102  				Wallet:      vgrand.RandomStr(5),
   103  				PublicKey:   vgrand.RandomStr(5),
   104  				Network:     "",
   105  				Transaction: testTransaction(t),
   106  			},
   107  			expectedError: api.ErrNetworkOrNodeAddressIsRequired,
   108  		},
   109  		{
   110  			name: "with no network and node address",
   111  			params: api.AdminCheckTransactionParams{
   112  				Wallet:      vgrand.RandomStr(5),
   113  				PublicKey:   vgrand.RandomStr(5),
   114  				Network:     "some_network",
   115  				NodeAddress: "some_node_address",
   116  				Transaction: testTransaction(t),
   117  			},
   118  			expectedError: api.ErrSpecifyingNetworkAndNodeAddressIsNotSupported,
   119  		},
   120  	}
   121  
   122  	for _, tc := range tcs {
   123  		t.Run(tc.name, func(tt *testing.T) {
   124  			// given
   125  			ctx := context.Background()
   126  
   127  			// setup
   128  			handler := newAdminCheckTransactionHandler(tt, unexpectedNodeSelectorCall(tt))
   129  
   130  			// when
   131  			result, errorDetails := handler.handle(t, ctx, tc.params)
   132  
   133  			// then
   134  			assertInvalidParams(tt, errorDetails, tc.expectedError)
   135  			assert.Empty(tt, result)
   136  		})
   137  	}
   138  }
   139  
   140  func testAdminCheckingTransactionWithValidParamsSucceeds(t *testing.T) {
   141  	// given
   142  	ctx := context.Background()
   143  	network := newNetwork(t)
   144  	nodeHost := vgrand.RandomStr(5)
   145  	w, kp := walletWithKey(t)
   146  
   147  	// setup
   148  	handler := newAdminCheckTransactionHandler(t, func(hosts []string, _ uint64, _ time.Duration) (walletnode.Selector, error) {
   149  		ctrl := gomock.NewController(t)
   150  		nodeSelector := nodemocks.NewMockSelector(ctrl)
   151  		node := nodemocks.NewMockNode(ctrl)
   152  		nodeSelector.EXPECT().Node(ctx, gomock.Any()).Times(1).Return(node, nil)
   153  		node.EXPECT().LastBlock(ctx).Times(1).Return(types.LastBlock{
   154  			BlockHeight:             150,
   155  			BlockHash:               vgrand.RandomStr(64),
   156  			ProofOfWorkHashFunction: vgcrypto.Sha3,
   157  			ProofOfWorkDifficulty:   1,
   158  			ChainID:                 vgrand.RandomStr(5),
   159  		}, nil)
   160  		node.EXPECT().CheckTransaction(ctx, gomock.Any()).Times(1).Return(nil)
   161  		node.EXPECT().Host().Times(1).Return(nodeHost)
   162  		return nodeSelector, nil
   163  	})
   164  
   165  	// -- expected calls
   166  	handler.walletStore.EXPECT().WalletExists(ctx, w.Name()).Times(1).Return(true, nil)
   167  	handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, w.Name()).Times(1).Return(true, nil)
   168  	handler.walletStore.EXPECT().GetWallet(ctx, w.Name()).Times(1).Return(w, nil)
   169  	handler.networkStore.EXPECT().NetworkExists(network.Name).Times(1).Return(true, nil)
   170  	handler.networkStore.EXPECT().GetNetwork(network.Name).Times(1).Return(&network, nil)
   171  
   172  	// when
   173  	result, errorDetails := handler.handle(t, ctx, api.AdminCheckTransactionParams{
   174  		Wallet:      w.Name(),
   175  		PublicKey:   kp.PublicKey(),
   176  		Network:     network.Name,
   177  		Transaction: testTransaction(t),
   178  	})
   179  
   180  	// then
   181  	assert.Nil(t, errorDetails)
   182  	assert.NotEmpty(t, result.Transaction)
   183  }
   184  
   185  func testAdminCheckTransactionGettingInternalErrorDuringWalletVerificationFails(t *testing.T) {
   186  	// given
   187  	ctx := context.Background()
   188  	network := newNetwork(t)
   189  	walletName := vgrand.RandomStr(5)
   190  
   191  	// setup
   192  	handler := newAdminCheckTransactionHandler(t, func(hosts []string, _ uint64, _ time.Duration) (walletnode.Selector, error) {
   193  		ctrl := gomock.NewController(t)
   194  		nodeSelector := nodemocks.NewMockSelector(ctrl)
   195  		node := nodemocks.NewMockNode(ctrl)
   196  		nodeSelector.EXPECT().Node(ctx, gomock.Any()).Times(1).Return(node, nil)
   197  		node.EXPECT().LastBlock(ctx).Times(1).Return(types.LastBlock{
   198  			BlockHeight:             150,
   199  			BlockHash:               vgrand.RandomStr(64),
   200  			ProofOfWorkHashFunction: vgcrypto.Sha3,
   201  			ProofOfWorkDifficulty:   1,
   202  			ChainID:                 vgrand.RandomStr(5),
   203  		}, nil)
   204  		return nodeSelector, nil
   205  	})
   206  
   207  	// -- expected calls
   208  	handler.walletStore.EXPECT().WalletExists(ctx, walletName).Times(1).Return(false, assert.AnError)
   209  
   210  	// when
   211  	result, errorDetails := handler.handle(t, ctx, api.AdminCheckTransactionParams{
   212  		Wallet:      walletName,
   213  		PublicKey:   vgrand.RandomStr(5),
   214  		Network:     network.Name,
   215  		Transaction: testTransaction(t),
   216  	})
   217  
   218  	// then
   219  	assertInternalError(t, errorDetails, fmt.Errorf("could not verify the wallet exists: %w", assert.AnError))
   220  	assert.Empty(t, result)
   221  }
   222  
   223  func testAdminCheckingTransactionWithWalletThatDoesntExistFails(t *testing.T) {
   224  	// given
   225  	ctx := context.Background()
   226  
   227  	params := api.AdminCheckTransactionParams{
   228  		Wallet:      vgrand.RandomStr(5),
   229  		PublicKey:   vgrand.RandomStr(5),
   230  		Network:     "fairground",
   231  		Transaction: testTransaction(t),
   232  	}
   233  
   234  	// setup
   235  	handler := newAdminCheckTransactionHandler(t, unexpectedNodeSelectorCall(t))
   236  
   237  	// -- expected calls
   238  	handler.walletStore.EXPECT().WalletExists(ctx, params.Wallet).Times(1).Return(false, nil)
   239  
   240  	// when
   241  	result, errorDetails := handler.handle(t, ctx, params)
   242  
   243  	// then
   244  	assertInvalidParams(t, errorDetails, api.ErrWalletDoesNotExist)
   245  	assert.Empty(t, result)
   246  }
   247  
   248  func testAdminCheckTransactionGettingInternalErrorDuringWalletRetrievalFails(t *testing.T) {
   249  	// given
   250  	ctx := context.Background()
   251  	network := newNetwork(t)
   252  	walletName := vgrand.RandomStr(5)
   253  
   254  	// setup
   255  	handler := newAdminCheckTransactionHandler(t, func(hosts []string, _ uint64, _ time.Duration) (walletnode.Selector, error) {
   256  		ctrl := gomock.NewController(t)
   257  		nodeSelector := nodemocks.NewMockSelector(ctrl)
   258  		node := nodemocks.NewMockNode(ctrl)
   259  		nodeSelector.EXPECT().Node(ctx, gomock.Any()).Times(1).Return(node, nil)
   260  		node.EXPECT().LastBlock(ctx).Times(1).Return(types.LastBlock{
   261  			BlockHeight:             150,
   262  			BlockHash:               vgrand.RandomStr(64),
   263  			ProofOfWorkHashFunction: vgcrypto.Sha3,
   264  			ProofOfWorkDifficulty:   1,
   265  			ChainID:                 vgrand.RandomStr(5),
   266  		}, nil)
   267  		return nodeSelector, nil
   268  	})
   269  
   270  	// -- expected calls
   271  	handler.walletStore.EXPECT().WalletExists(ctx, walletName).Times(1).Return(true, nil)
   272  	handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, walletName).Times(1).Return(true, nil)
   273  	handler.walletStore.EXPECT().GetWallet(ctx, walletName).Times(1).Return(nil, assert.AnError)
   274  
   275  	// when
   276  	result, errorDetails := handler.handle(t, ctx, api.AdminCheckTransactionParams{
   277  		Wallet:      walletName,
   278  		PublicKey:   vgrand.RandomStr(5),
   279  		Network:     network.Name,
   280  		Transaction: testTransaction(t),
   281  	})
   282  
   283  	// then
   284  	assertInternalError(t, errorDetails, fmt.Errorf("could not retrieve the wallet: %w", assert.AnError))
   285  	assert.Empty(t, result)
   286  }
   287  
   288  func testAdminCheckingTransactionWithMalformedTransactionFails(t *testing.T) {
   289  	// given
   290  	ctx := context.Background()
   291  	network := vgrand.RandomStr(5)
   292  	w, kp := walletWithKey(t)
   293  
   294  	// setup
   295  	handler := newAdminCheckTransactionHandler(t, unexpectedNodeSelectorCall(t))
   296  
   297  	// -- expected calls
   298  	handler.walletStore.EXPECT().WalletExists(ctx, w.Name()).Times(1).Return(true, nil)
   299  	handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, w.Name()).Times(1).Return(true, nil)
   300  	handler.walletStore.EXPECT().GetWallet(ctx, w.Name()).Times(1).Return(w, nil)
   301  
   302  	// when
   303  	result, errorDetails := handler.handle(t, ctx, api.AdminCheckTransactionParams{
   304  		Wallet:      w.Name(),
   305  		PublicKey:   kp.PublicKey(),
   306  		Network:     network,
   307  		Transaction: map[string]int{"bob": 5},
   308  	})
   309  
   310  	// then
   311  	assertInvalidParams(t, errorDetails, errors.New("the transaction does not use a valid Vega command: unknown field \"bob\" in vega.wallet.v1.SubmitTransactionRequest"))
   312  	assert.Empty(t, result)
   313  }
   314  
   315  func testAdminCheckingTransactionWithInvalidTransactionFails(t *testing.T) {
   316  	// given
   317  	ctx := context.Background()
   318  	network := newNetwork(t)
   319  	w, kp := walletWithKey(t)
   320  
   321  	// setup
   322  	handler := newAdminCheckTransactionHandler(t, unexpectedNodeSelectorCall(t))
   323  
   324  	// -- expected calls
   325  	handler.walletStore.EXPECT().WalletExists(ctx, w.Name()).Times(1).Return(true, nil)
   326  	handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, w.Name()).Times(1).Return(true, nil)
   327  	handler.walletStore.EXPECT().GetWallet(ctx, w.Name()).Times(1).Return(w, nil)
   328  
   329  	// when
   330  	result, errorDetails := handler.handle(t, ctx, api.AdminCheckTransactionParams{
   331  		Wallet:      w.Name(),
   332  		PublicKey:   kp.PublicKey(),
   333  		Network:     network.Name,
   334  		Transaction: testMalformedTransaction(t),
   335  	})
   336  
   337  	// then
   338  	assertInvalidParams(t, errorDetails, fmt.Errorf("vote_submission.proposal_id (should be a valid Vega ID)"))
   339  	assert.Empty(t, result)
   340  }
   341  
   342  type AdminCheckTransactionHandler struct {
   343  	*api.AdminCheckTransaction
   344  	ctrl         *gomock.Controller
   345  	walletStore  *mocks.MockWalletStore
   346  	networkStore *mocks.MockNetworkStore
   347  }
   348  
   349  func (h *AdminCheckTransactionHandler) handle(t *testing.T, ctx context.Context, params jsonrpc.Params) (api.AdminCheckTransactionResult, *jsonrpc.ErrorDetails) {
   350  	t.Helper()
   351  
   352  	rawResult, err := h.Handle(ctx, params)
   353  	if rawResult != nil {
   354  		result, ok := rawResult.(api.AdminCheckTransactionResult)
   355  		if !ok {
   356  			t.Fatal("AdminUpdatePermissions handler result is not a AdminCheckTransactionResult")
   357  		}
   358  		return result, err
   359  	}
   360  	return api.AdminCheckTransactionResult{}, err
   361  }
   362  
   363  func newAdminCheckTransactionHandler(t *testing.T, nodeBuilder api.NodeSelectorBuilder) *AdminCheckTransactionHandler {
   364  	t.Helper()
   365  
   366  	ctrl := gomock.NewController(t)
   367  	walletStore := mocks.NewMockWalletStore(ctrl)
   368  	networkStore := mocks.NewMockNetworkStore(ctrl)
   369  
   370  	return &AdminCheckTransactionHandler{
   371  		AdminCheckTransaction: api.NewAdminCheckTransaction(walletStore, networkStore, nodeBuilder),
   372  		ctrl:                  ctrl,
   373  		walletStore:           walletStore,
   374  		networkStore:          networkStore,
   375  	}
   376  }