code.vegaprotocol.io/vega@v0.79.0/wallet/api/admin_isolate_key_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  	"fmt"
    21  	"strings"
    22  	"testing"
    23  
    24  	"code.vegaprotocol.io/vega/libs/jsonrpc"
    25  	vgrand "code.vegaprotocol.io/vega/libs/rand"
    26  	"code.vegaprotocol.io/vega/wallet/api"
    27  	"code.vegaprotocol.io/vega/wallet/api/mocks"
    28  
    29  	"github.com/golang/mock/gomock"
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  )
    33  
    34  func TestAdminIsolateKey(t *testing.T) {
    35  	t.Run("Documentation matches the code", testAdminIsolateKeySchemaCorrect)
    36  	t.Run("Isolating a key with invalid params fails", testIsolatingKeyWithInvalidParamsFails)
    37  	t.Run("Isolating a key with valid params succeeds", testIsolatingKeyWithValidParamsSucceeds)
    38  	t.Run("Isolating a key from wallet that does not exists fails", testIsolatingKeyFromWalletThatDoesNotExistsFails)
    39  	t.Run("Getting internal error during wallet verification fails", testIsolatingKeyGettingInternalErrorDuringWalletVerificationFails)
    40  	t.Run("Getting internal error during wallet retrieval fails", testIsolatingKeyGettingInternalErrorDuringWalletRetrievalFails)
    41  	t.Run("Isolating a key that does not exists fails", testIsolatingKeyThatDoesNotExistsFails)
    42  	t.Run("Getting internal error during isolated wallet saving fails", testIsolatingKeyGettingInternalErrorDuringIsolatedWalletSavingFails)
    43  }
    44  
    45  func testAdminIsolateKeySchemaCorrect(t *testing.T) {
    46  	assertEqualSchema(t, "admin.isolate_key", api.AdminIsolateKeyParams{}, api.AdminIsolateKeyResult{})
    47  }
    48  
    49  func testIsolatingKeyWithInvalidParamsFails(t *testing.T) {
    50  	tcs := []struct {
    51  		name          string
    52  		params        interface{}
    53  		expectedError error
    54  	}{
    55  		{
    56  			name:          "with nil params",
    57  			params:        nil,
    58  			expectedError: api.ErrParamsRequired,
    59  		}, {
    60  			name:          "with wrong type of params",
    61  			params:        "test",
    62  			expectedError: api.ErrParamsDoNotMatch,
    63  		}, {
    64  			name: "with empty name",
    65  			params: api.AdminIsolateKeyParams{
    66  				Wallet:                   "",
    67  				PublicKey:                "b5fd9d3c4ad553cb3196303b6e6df7f484cf7f5331a572a45031239fd71ad8a0",
    68  				IsolatedWalletPassphrase: vgrand.RandomStr(5),
    69  			},
    70  			expectedError: api.ErrWalletIsRequired,
    71  		}, {
    72  			name: "with empty isolated passphrase",
    73  			params: api.AdminIsolateKeyParams{
    74  				Wallet:                   vgrand.RandomStr(5),
    75  				IsolatedWalletPassphrase: "",
    76  				PublicKey:                "b5fd9d3c4ad553cb3196303b6e6df7f484cf7f5331a572a45031239fd71ad8a0",
    77  			},
    78  			expectedError: api.ErrIsolatedWalletPassphraseIsRequired,
    79  		}, {
    80  			name: "with empty public key",
    81  			params: api.AdminIsolateKeyParams{
    82  				Wallet:                   vgrand.RandomStr(5),
    83  				PublicKey:                "",
    84  				IsolatedWalletPassphrase: vgrand.RandomStr(5),
    85  			},
    86  			expectedError: api.ErrPublicKeyIsRequired,
    87  		},
    88  	}
    89  
    90  	for _, tc := range tcs {
    91  		t.Run(tc.name, func(tt *testing.T) {
    92  			// given
    93  			ctx := context.Background()
    94  
    95  			// setup
    96  			handler := newIsolateKeyHandler(tt)
    97  
    98  			// when
    99  			result, errorDetails := handler.handle(t, ctx, tc.params)
   100  
   101  			// then
   102  			require.Empty(tt, result)
   103  			assertInvalidParams(tt, errorDetails, tc.expectedError)
   104  		})
   105  	}
   106  }
   107  
   108  func testIsolatingKeyWithValidParamsSucceeds(t *testing.T) {
   109  	// given
   110  	ctx := context.Background()
   111  	isolatedPassphrase := vgrand.RandomStr(5)
   112  	expectedWallet, firstKey := walletWithKey(t)
   113  
   114  	// setup
   115  	handler := newIsolateKeyHandler(t)
   116  	// -- expected calls
   117  	handler.walletStore.EXPECT().WalletExists(ctx, expectedWallet.Name()).Times(1).Return(true, nil)
   118  	handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, expectedWallet.Name()).Times(1).Return(true, nil)
   119  	handler.walletStore.EXPECT().GetWallet(ctx, expectedWallet.Name()).Times(1).Return(expectedWallet, nil)
   120  	handler.walletStore.EXPECT().CreateWallet(ctx, gomock.Any(), isolatedPassphrase).Times(1).Return(nil)
   121  
   122  	// when
   123  	result, errorDetails := handler.handle(t, ctx, api.AdminIsolateKeyParams{
   124  		Wallet:                   expectedWallet.Name(),
   125  		IsolatedWalletPassphrase: isolatedPassphrase,
   126  		PublicKey:                firstKey.PublicKey(),
   127  	})
   128  
   129  	// then
   130  	require.Nil(t, errorDetails)
   131  	assert.True(t, strings.HasPrefix(result.Wallet, expectedWallet.Name()))
   132  }
   133  
   134  func testIsolatingKeyFromWalletThatDoesNotExistsFails(t *testing.T) {
   135  	// given
   136  	ctx := context.Background()
   137  	isolatedPassphrase := vgrand.RandomStr(5)
   138  	name := vgrand.RandomStr(5)
   139  
   140  	// setup
   141  	handler := newIsolateKeyHandler(t)
   142  	// -- expected calls
   143  	handler.walletStore.EXPECT().WalletExists(ctx, name).Times(1).Return(false, nil)
   144  
   145  	// when
   146  	result, errorDetails := handler.handle(t, ctx, api.AdminIsolateKeyParams{
   147  		Wallet:                   name,
   148  		IsolatedWalletPassphrase: isolatedPassphrase,
   149  		PublicKey:                vgrand.RandomStr(5),
   150  	})
   151  
   152  	// then
   153  	require.NotNil(t, errorDetails)
   154  	assert.Empty(t, result)
   155  	assertInvalidParams(t, errorDetails, api.ErrWalletDoesNotExist)
   156  }
   157  
   158  func testIsolatingKeyGettingInternalErrorDuringWalletVerificationFails(t *testing.T) {
   159  	// given
   160  	ctx := context.Background()
   161  	isolatedPassphrase := vgrand.RandomStr(5)
   162  	name := vgrand.RandomStr(5)
   163  
   164  	// setup
   165  	handler := newIsolateKeyHandler(t)
   166  	// -- expected calls
   167  	handler.walletStore.EXPECT().WalletExists(ctx, name).Times(1).Return(false, assert.AnError)
   168  
   169  	// when
   170  	result, errorDetails := handler.handle(t, ctx, api.AdminIsolateKeyParams{
   171  		Wallet:                   name,
   172  		IsolatedWalletPassphrase: isolatedPassphrase,
   173  		PublicKey:                vgrand.RandomStr(5),
   174  	})
   175  
   176  	// then
   177  	require.NotNil(t, errorDetails)
   178  	assert.Empty(t, result)
   179  	assertInternalError(t, errorDetails, fmt.Errorf("could not verify the wallet exists: %w", assert.AnError))
   180  }
   181  
   182  func testIsolatingKeyGettingInternalErrorDuringWalletRetrievalFails(t *testing.T) {
   183  	// given
   184  	ctx := context.Background()
   185  	isolatedPassphrase := vgrand.RandomStr(5)
   186  	name := vgrand.RandomStr(5)
   187  
   188  	// setup
   189  	handler := newIsolateKeyHandler(t)
   190  	// -- expected calls
   191  	handler.walletStore.EXPECT().WalletExists(ctx, name).Times(1).Return(true, nil)
   192  	handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, name).Times(1).Return(true, nil)
   193  	handler.walletStore.EXPECT().GetWallet(ctx, name).Times(1).Return(nil, assert.AnError)
   194  
   195  	// when
   196  	result, errorDetails := handler.handle(t, ctx, api.AdminIsolateKeyParams{
   197  		Wallet:                   name,
   198  		IsolatedWalletPassphrase: isolatedPassphrase,
   199  		PublicKey:                vgrand.RandomStr(5),
   200  	})
   201  
   202  	// then
   203  	require.NotNil(t, errorDetails)
   204  	assert.Empty(t, result)
   205  	assertInternalError(t, errorDetails, fmt.Errorf("could not retrieve the wallet: %w", assert.AnError))
   206  }
   207  
   208  func testIsolatingKeyGettingInternalErrorDuringIsolatedWalletSavingFails(t *testing.T) {
   209  	// given
   210  	ctx := context.Background()
   211  	isolatedPassphrase := vgrand.RandomStr(5)
   212  	expectedWallet, firstKey := walletWithKey(t)
   213  
   214  	// setup
   215  	handler := newIsolateKeyHandler(t)
   216  	// -- expected calls
   217  	handler.walletStore.EXPECT().WalletExists(ctx, expectedWallet.Name()).Times(1).Return(true, nil)
   218  	handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, expectedWallet.Name()).Times(1).Return(true, nil)
   219  	handler.walletStore.EXPECT().GetWallet(ctx, expectedWallet.Name()).Times(1).Return(expectedWallet, nil)
   220  	handler.walletStore.EXPECT().CreateWallet(ctx, gomock.Any(), isolatedPassphrase).Times(1).Return(assert.AnError)
   221  
   222  	// when
   223  	result, errorDetails := handler.handle(t, ctx, api.AdminIsolateKeyParams{
   224  		Wallet:                   expectedWallet.Name(),
   225  		IsolatedWalletPassphrase: isolatedPassphrase,
   226  		PublicKey:                firstKey.PublicKey(),
   227  	})
   228  
   229  	// then
   230  	require.NotNil(t, errorDetails)
   231  	assert.Empty(t, result)
   232  	assertInternalError(t, errorDetails, fmt.Errorf("could not save the wallet with isolated key: %w", assert.AnError))
   233  }
   234  
   235  func testIsolatingKeyThatDoesNotExistsFails(t *testing.T) {
   236  	// given
   237  	ctx := context.Background()
   238  	isolatedPassphrase := vgrand.RandomStr(5)
   239  	expectedWallet, _ := walletWithKey(t)
   240  
   241  	// setup
   242  	handler := newIsolateKeyHandler(t)
   243  	// -- expected calls
   244  	handler.walletStore.EXPECT().WalletExists(ctx, expectedWallet.Name()).Times(1).Return(true, nil)
   245  	handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, expectedWallet.Name()).Times(1).Return(true, nil)
   246  	handler.walletStore.EXPECT().GetWallet(ctx, expectedWallet.Name()).Times(1).Return(expectedWallet, nil)
   247  
   248  	// when
   249  	result, errorDetails := handler.handle(t, ctx, api.AdminIsolateKeyParams{
   250  		Wallet:                   expectedWallet.Name(),
   251  		IsolatedWalletPassphrase: isolatedPassphrase,
   252  		PublicKey:                vgrand.RandomStr(5),
   253  	})
   254  
   255  	// then
   256  	require.NotNil(t, errorDetails)
   257  	assert.Empty(t, result)
   258  	assertInvalidParams(t, errorDetails, api.ErrPublicKeyDoesNotExist)
   259  }
   260  
   261  type isolateKeyHandler struct {
   262  	*api.AdminIsolateKey
   263  	ctrl        *gomock.Controller
   264  	walletStore *mocks.MockWalletStore
   265  }
   266  
   267  func (h *isolateKeyHandler) handle(t *testing.T, ctx context.Context, params jsonrpc.Params) (api.AdminIsolateKeyResult, *jsonrpc.ErrorDetails) {
   268  	t.Helper()
   269  
   270  	rawResult, err := h.Handle(ctx, params)
   271  	if rawResult != nil {
   272  		result, ok := rawResult.(api.AdminIsolateKeyResult)
   273  		if !ok {
   274  			t.Fatal("AdminIsolateKey handler result is not a AdminIsolateKeyResult")
   275  		}
   276  		return result, err
   277  	}
   278  	return api.AdminIsolateKeyResult{}, err
   279  }
   280  
   281  func newIsolateKeyHandler(t *testing.T) *isolateKeyHandler {
   282  	t.Helper()
   283  
   284  	ctrl := gomock.NewController(t)
   285  	walletStore := mocks.NewMockWalletStore(ctrl)
   286  
   287  	return &isolateKeyHandler{
   288  		AdminIsolateKey: api.NewAdminIsolateKey(walletStore),
   289  		ctrl:            ctrl,
   290  		walletStore:     walletStore,
   291  	}
   292  }