code.vegaprotocol.io/vega@v0.79.0/wallet/api/admin_import_wallet_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 "testing" 22 23 "code.vegaprotocol.io/vega/libs/jsonrpc" 24 vgrand "code.vegaprotocol.io/vega/libs/rand" 25 "code.vegaprotocol.io/vega/wallet/api" 26 "code.vegaprotocol.io/vega/wallet/api/mocks" 27 "code.vegaprotocol.io/vega/wallet/wallet" 28 29 "github.com/golang/mock/gomock" 30 "github.com/stretchr/testify/assert" 31 "github.com/stretchr/testify/require" 32 ) 33 34 func TestAdminImportWallet(t *testing.T) { 35 t.Run("Documentation matches the code", testAdminImportWalletSchemaCorrect) 36 t.Run("Importing a wallet with invalid params fails", testImportingWalletWithInvalidParamsFails) 37 t.Run("Importing a wallet with valid params succeeds", testImportingWalletWithValidParamsSucceeds) 38 t.Run("Importing a wallet that already exists fails", testImportingWalletThatAlreadyExistsFails) 39 t.Run("Getting internal error during verification does not import the wallet", testGettingInternalErrorDuringVerificationDoesNotImportWallet) 40 t.Run("Getting internal error during saving does not import the wallet", testGettingInternalErrorDuringSavingDoesNotImportWallet) 41 } 42 43 func testAdminImportWalletSchemaCorrect(t *testing.T) { 44 assertEqualSchema(t, "admin.import_wallet", api.AdminImportWalletParams{}, api.AdminImportWalletResult{}) 45 } 46 47 func testImportingWalletWithInvalidParamsFails(t *testing.T) { 48 tcs := []struct { 49 name string 50 params interface{} 51 expectedError error 52 }{ 53 { 54 name: "with nil params", 55 params: nil, 56 expectedError: api.ErrParamsRequired, 57 }, { 58 name: "with wrong type of params", 59 params: "test", 60 expectedError: api.ErrParamsDoNotMatch, 61 }, { 62 name: "with empty name", 63 params: api.AdminImportWalletParams{ 64 Wallet: "", 65 RecoveryPhrase: "swing ceiling chaos green put insane ripple desk match tip melt usual shrug turkey renew icon parade veteran lens govern path rough page render", 66 KeyDerivationVersion: 2, 67 Passphrase: vgrand.RandomStr(5), 68 }, 69 expectedError: api.ErrWalletIsRequired, 70 }, { 71 name: "with empty passphrase", 72 params: api.AdminImportWalletParams{ 73 Wallet: vgrand.RandomStr(5), 74 RecoveryPhrase: "swing ceiling chaos green put insane ripple desk match tip melt usual shrug turkey renew icon parade veteran lens govern path rough page render", 75 KeyDerivationVersion: 2, 76 Passphrase: "", 77 }, 78 expectedError: api.ErrPassphraseIsRequired, 79 }, { 80 name: "with empty recovery phrase", 81 params: api.AdminImportWalletParams{ 82 Wallet: vgrand.RandomStr(5), 83 RecoveryPhrase: "", 84 KeyDerivationVersion: 2, 85 Passphrase: vgrand.RandomStr(5), 86 }, 87 expectedError: api.ErrRecoveryPhraseIsRequired, 88 }, { 89 name: "with unset key derivation version", 90 params: api.AdminImportWalletParams{ 91 Wallet: vgrand.RandomStr(5), 92 RecoveryPhrase: "swing ceiling chaos green put insane ripple desk match tip melt usual shrug turkey renew icon parade veteran lens govern path rough page render", 93 KeyDerivationVersion: 0, 94 Passphrase: vgrand.RandomStr(5), 95 }, 96 expectedError: api.ErrWalletKeyDerivationVersionIsRequired, 97 }, { 98 name: "with unset key derivation version", 99 params: api.AdminImportWalletParams{ 100 Wallet: vgrand.RandomStr(5), 101 RecoveryPhrase: "swing ceiling chaos green put insane ripple desk match tip melt usual shrug turkey renew icon parade veteran lens govern path rough page render", 102 Passphrase: vgrand.RandomStr(5), 103 }, 104 expectedError: api.ErrWalletKeyDerivationVersionIsRequired, 105 }, 106 } 107 108 for _, tc := range tcs { 109 t.Run(tc.name, func(tt *testing.T) { 110 // given 111 ctx := context.Background() 112 113 // setup 114 handler := newImportWalletHandler(tt) 115 116 // when 117 result, errorDetails := handler.handle(t, ctx, tc.params) 118 119 // then 120 require.Empty(tt, result) 121 assertInvalidParams(tt, errorDetails, tc.expectedError) 122 }) 123 } 124 } 125 126 func testImportingWalletWithValidParamsSucceeds(t *testing.T) { 127 // given 128 ctx := context.Background() 129 passphrase := vgrand.RandomStr(5) 130 name := vgrand.RandomStr(5) 131 var importedWallet wallet.Wallet 132 133 // setup 134 handler := newImportWalletHandler(t) 135 // -- expected calls 136 handler.walletStore.EXPECT().WalletExists(ctx, name).Times(1).Return(false, nil) 137 handler.walletStore.EXPECT().CreateWallet(ctx, gomock.Any(), passphrase).Times(1).DoAndReturn(func(_ context.Context, w wallet.Wallet, passphrase string) error { 138 importedWallet = w 139 return nil 140 }) 141 142 // when 143 result, errorDetails := handler.handle(t, ctx, api.AdminImportWalletParams{ 144 Wallet: name, 145 Passphrase: passphrase, 146 RecoveryPhrase: "swing ceiling chaos green put insane ripple desk match tip melt usual shrug turkey renew icon parade veteran lens govern path rough page render", 147 KeyDerivationVersion: 2, 148 }) 149 150 // then 151 require.Nil(t, errorDetails) 152 // Verify imported wallet. 153 assert.Equal(t, name, importedWallet.Name()) 154 // Verify the first generated key. 155 assert.Len(t, importedWallet.ListKeyPairs(), 1) 156 keyPair := importedWallet.ListKeyPairs()[0] 157 assert.Equal(t, []wallet.Metadata{{Key: "name", Value: "Key 1"}}, keyPair.Metadata()) 158 // Verify the result. 159 assert.Equal(t, name, result.Wallet.Name) 160 assert.Equal(t, keyPair.PublicKey(), result.Key.PublicKey) 161 assert.Equal(t, keyPair.AlgorithmName(), result.Key.Algorithm.Name) 162 assert.Equal(t, keyPair.AlgorithmVersion(), result.Key.Algorithm.Version) 163 assert.Equal(t, keyPair.Metadata(), result.Key.Metadata) 164 } 165 166 func testImportingWalletThatAlreadyExistsFails(t *testing.T) { 167 // given 168 ctx := context.Background() 169 passphrase := vgrand.RandomStr(5) 170 name := vgrand.RandomStr(5) 171 172 // setup 173 handler := newImportWalletHandler(t) 174 // -- expected calls 175 handler.walletStore.EXPECT().WalletExists(ctx, name).Times(1).Return(true, nil) 176 177 // when 178 result, errorDetails := handler.handle(t, ctx, api.AdminImportWalletParams{ 179 Wallet: name, 180 Passphrase: passphrase, 181 RecoveryPhrase: "swing ceiling chaos green put insane ripple desk match tip melt usual shrug turkey renew icon parade veteran lens govern path rough page render", 182 KeyDerivationVersion: 2, 183 }) 184 185 // then 186 require.NotNil(t, errorDetails) 187 assert.Empty(t, result) 188 assertInvalidParams(t, errorDetails, api.ErrWalletAlreadyExists) 189 } 190 191 func testGettingInternalErrorDuringVerificationDoesNotImportWallet(t *testing.T) { 192 // given 193 ctx := context.Background() 194 passphrase := vgrand.RandomStr(5) 195 name := vgrand.RandomStr(5) 196 197 // setup 198 handler := newImportWalletHandler(t) 199 // -- expected calls 200 handler.walletStore.EXPECT().WalletExists(ctx, name).Times(1).Return(false, assert.AnError) 201 202 // when 203 result, errorDetails := handler.handle(t, ctx, api.AdminImportWalletParams{ 204 Wallet: name, 205 Passphrase: passphrase, 206 RecoveryPhrase: "swing ceiling chaos green put insane ripple desk match tip melt usual shrug turkey renew icon parade veteran lens govern path rough page render", 207 KeyDerivationVersion: 2, 208 }) 209 210 // then 211 require.NotNil(t, errorDetails) 212 assert.Empty(t, result) 213 assertInternalError(t, errorDetails, fmt.Errorf("could not verify the wallet exists: %w", assert.AnError)) 214 } 215 216 func testGettingInternalErrorDuringSavingDoesNotImportWallet(t *testing.T) { 217 // given 218 ctx := context.Background() 219 passphrase := vgrand.RandomStr(5) 220 name := vgrand.RandomStr(5) 221 222 // setup 223 handler := newImportWalletHandler(t) 224 // -- expected calls 225 handler.walletStore.EXPECT().WalletExists(ctx, name).Times(1).Return(false, nil) 226 handler.walletStore.EXPECT().CreateWallet(ctx, gomock.Any(), passphrase).Times(1).Return(assert.AnError) 227 228 // when 229 result, errorDetails := handler.handle(t, ctx, api.AdminImportWalletParams{ 230 Wallet: name, 231 Passphrase: passphrase, 232 RecoveryPhrase: "swing ceiling chaos green put insane ripple desk match tip melt usual shrug turkey renew icon parade veteran lens govern path rough page render", 233 KeyDerivationVersion: 2, 234 }) 235 236 // then 237 require.NotNil(t, errorDetails) 238 assert.Empty(t, result) 239 assertInternalError(t, errorDetails, fmt.Errorf("could not save the wallet: %w", assert.AnError)) 240 } 241 242 type importWalletHandler struct { 243 *api.AdminImportWallet 244 ctrl *gomock.Controller 245 walletStore *mocks.MockWalletStore 246 } 247 248 func (h *importWalletHandler) handle(t *testing.T, ctx context.Context, params jsonrpc.Params) (api.AdminImportWalletResult, *jsonrpc.ErrorDetails) { 249 t.Helper() 250 251 rawResult, err := h.Handle(ctx, params) 252 if rawResult != nil { 253 result, ok := rawResult.(api.AdminImportWalletResult) 254 if !ok { 255 t.Fatal("AdminImportWallet handler result is not a AdminImportWalletResult") 256 } 257 return result, err 258 } 259 return api.AdminImportWalletResult{}, err 260 } 261 262 func newImportWalletHandler(t *testing.T) *importWalletHandler { 263 t.Helper() 264 265 ctrl := gomock.NewController(t) 266 walletStore := mocks.NewMockWalletStore(ctrl) 267 268 return &importWalletHandler{ 269 AdminImportWallet: api.NewAdminImportWallet(walletStore), 270 ctrl: ctrl, 271 walletStore: walletStore, 272 } 273 }