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 }